Files
hhrs/components/hhrs-filter-bar/hhrs-filter-bar.vue
T
jacobxu666andClaude Fable 5 eb01701709 feat: 列表卡片新增所属地区角标,并修正筛选条间距与遮罩压灰
- 三个列表(文旅场馆/商户折扣/权益待使用)缩略图左上角增加所属地区角标:
  缩略图外套 .card-thumb 承担尺寸与圆角裁切,角标半透明黑底白字 10px;
  districtName 为空的记录不渲染角标,避免空色块
- 新增 common/district.js:后端返回的是行政区划全称,卡片位宽只有 75px,
  按「芷江侗族自治县→芷江、中方县→中方、怀化高新技术产业开发区→怀化高新区」缩短显示
- 筛选条与列表的 12px 间隔移到头部白底之外(各页 .list-area 顶部内边距),
  此前放在头部内是白色、与白色卡片连成一片,视觉上等于没有间隔
- 遮罩由 fixed 改为从筛选条下边缘绝对定位铺开:fixed 会落进 sticky 头部的层叠上下文,
  把搜索栏和筛选条一起压灰;现改为列表变暗、头部保持原色
- 权益页搜索框加 1px #e5e5ea 描边:吸顶栏改白底后白色输入框无边框可辨

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-28 15:55:17 +08:00

216 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!-- xuchao: 列表页「所属县区 + 所属类型」筛选条。两个入口等宽平分并占满容器宽,无底色,
选中态只用品牌蓝字表达;面板第一行「全部 X」即清空该维度(所以没有独立重置按钮)。
选项与各选项条数由列表接口在响应里一并返回,已按关键词与另一维度筛算好(districts/categories 首项即「全部」)。 -->
<view class="hfb" v-if="ready">
<view class="hfb-row">
<view class="hfb-item" v-if="districtReady" :class="{ 'hfb-item--on': !!districtId }" @click="toggle('district')">
<text class="hfb-text">{{ districtText }}</text>
<view class="hfb-caret" :class="{ 'hfb-caret--up': openKey === 'district' }">
<u-icon name="arrow-down-fill" size="20" :color="districtId ? '#0088ff' : '#bbbbbb'"></u-icon>
</view>
</view>
<view class="hfb-item" v-if="categoryReady" :class="{ 'hfb-item--on': !!category }" @click="toggle('category')">
<text class="hfb-text">{{ categoryText }}</text>
<view class="hfb-caret" :class="{ 'hfb-caret--up': openKey === 'category' }">
<u-icon name="arrow-down-fill" size="20" :color="category ? '#0088ff' : '#bbbbbb'"></u-icon>
</view>
</view>
</view>
<view class="hfb-mask" v-if="openKey" @click="close"></view>
<view class="hfb-panel" v-if="openKey">
<scroll-view scroll-y class="hfb-list">
<view class="hfb-opt" v-for="o in openOptions" :key="o.value"
:class="{ 'hfb-opt--sel': current === o.value }" @click="pick(o.value)">
<text class="hfb-opt-label">{{ o.label }}</text>
<text class="hfb-opt-count">{{ o.count }}</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'hhrs-filter-bar',
props: {
// xuchao: [{value,label,count}],首项为「全部 X」(value=''),县区 value 是行政区划码
districts: { type: Array, default: () => [] },
categories: { type: Array, default: () => [] },
districtId: { type: String, default: '' },
category: { type: String, default: '' }
},
data() {
return {
openKey: '',
// xuchao: 记住见过的 value→label,避免关键词把选中项挤出选项列表后入口只能显示区划码
labelCache: {}
}
},
computed: {
// xuchao: 选项首项固定是「全部」,长度>1 才算该维度真有可选项。
// 某维度没有可选项(如商铺未录入「所在地区」)时只隐藏它自己的入口,
// 两个都没有则整条筛选不渲染,避免出现点开是空面板
districtReady() { return this.districts.length > 1 },
categoryReady() { return this.categories.length > 1 },
ready() { return this.districtReady || this.categoryReady },
openOptions() {
return this.openKey === 'district' ? this.districts : this.categories
},
current() {
return this.openKey === 'district' ? this.districtId : this.category
},
districtText() {
return this.districtId ? this.cachedLabel('district', this.districtId) : '所属县区'
},
categoryText() {
return this.category ? this.cachedLabel('category', this.category) : '所属类型'
}
},
watch: {
districts(list) { this.remember(list, 'district') },
categories(list) { this.remember(list, 'category') }
},
created() {
this.remember(this.districts, 'district')
this.remember(this.categories, 'category')
},
methods: {
remember(list, kind) {
// xuchao: 必须用 $set,Vue2 对新增对象键不做响应,入口文字会跟不上缓存
(list || []).forEach(o => {
if (o.value) this.$set(this.labelCache, kind + ':' + o.value, o.label)
})
},
cachedLabel(kind, value) {
return this.labelCache[kind + ':' + value] || value
},
toggle(key) {
// xuchao: 再点同一个入口即收起,避免面板卡在打开态
this.openKey = this.openKey === key ? '' : key
},
close() {
this.openKey = ''
},
pick(value) {
if (this.openKey === 'district') {
this.$emit('change', { districtId: value, category: this.category })
} else {
this.$emit('change', { districtId: this.districtId, category: value })
}
this.openKey = ''
}
}
}
</script>
<style scoped lang="less">
/* xuchao: 根节点贴在 sticky 头部内,面板绝对定位到筛选条下方。
只留上内边距:与列表的 12px 间隔必须落在头部的白底之外(各页 .list-area 的 padding-top),
放在这里会是白色,头部白底和白色卡片连成一片,看上去就等于没有间隔 */
.hfb {
position: relative;
padding: 24rpx 0 0;
}
.hfb-row {
display: flex;
gap: 20rpx;
}
.hfb-item {
flex: 1;
min-width: 0;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
font-size: 28rpx;
color: #333333;
}
.hfb-item--on {
color: #0088ff;
font-weight: 600;
}
.hfb-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hfb-caret {
display: flex;
transition: transform 0.18s;
}
.hfb-caret--up {
transform: rotate(180deg);
}
/* xuchao: 遮罩绝对定位在筛选条下方(不用 fixed)—— fixed 会盖进 sticky 头部的层叠上下文,
把搜索栏和筛选条一起压灰。这里从筛选条下边缘开始向下铺满一屏,列表变暗、头部保持原色 */
.hfb-mask {
position: absolute;
top: 100%;
left: -12px;
right: -12px;
height: 100vh;
background: rgba(0, 0, 0, 0.3);
z-index: 90;
}
/* xuchao: 面板左右各超出 12px(与页面头部的 12px 内边距等值,用 px 才能在各屏宽下都对齐),
使面板与手机边缘齐平 */
.hfb-panel {
position: absolute;
left: -12px;
right: -12px;
top: 100%;
z-index: 100;
background: #ffffff;
border-radius: 0 0 28rpx 28rpx;
box-shadow: 0 20rpx 48rpx rgba(0, 0, 0, 0.12);
padding: 12rpx 12rpx 16rpx;
}
.hfb-list {
max-height: 620rpx;
}
.hfb-opt {
display: flex;
align-items: center;
justify-content: space-between;
height: 84rpx;
padding: 0 24rpx;
border-radius: 16rpx;
font-size: 28rpx;
color: #333333;
}
.hfb-opt--sel {
background: #e6f2ff;
color: #0088ff;
font-weight: 600;
}
.hfb-opt-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 16rpx;
}
.hfb-opt-count {
flex-shrink: 0;
font-size: 24rpx;
color: #bbbbbb;
}
</style>