158 lines
7.0 KiB
Vue
158 lines
7.0 KiB
Vue
<!--
|
||
页面:未来到家 - 服务目录(金刚区图标点击进入)
|
||
格式:顶部「服务」标题 + 搜索栏;左侧小类竖向导航(选中红条高亮),
|
||
右侧按小类分组的服务网格(3列:封面图 + 名称),点左侧定位到对应分组。
|
||
-->
|
||
<template>
|
||
<view class="sc-page">
|
||
<!-- 头部 -->
|
||
<view class="sc-hd" :style="{ paddingTop: statusBarTop + 'px' }">
|
||
<view class="sc-nav">
|
||
<view class="sc-back" @tap="goBack"><u-icon name="arrow-left" color="#1a1a1a" size="38"></u-icon></view>
|
||
<text class="sc-title">服务</text>
|
||
</view>
|
||
<view class="sc-search">
|
||
<text class="sc-city">{{ cityName }}</text>
|
||
<text class="sc-city-arrow">▾</text>
|
||
<view class="sc-search-line"></view>
|
||
<u-icon name="search" color="#9098a0" size="30"></u-icon>
|
||
<input class="sc-search-input" v-model="keyword" placeholder="搜索您需要的服务" placeholder-class="sc-ph" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主体:左侧分类导航 + 右侧分组商品 -->
|
||
<view class="sc-body">
|
||
<scroll-view class="sc-side" scroll-y>
|
||
<view class="sc-side-item" :class="{ on: activeCate === cat.id }" v-for="cat in categoryList" :key="cat.id"
|
||
@tap="selectCate(cat)">
|
||
<text>{{ cat.category_name }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 右侧:直接显示当前选中分类的商品(3列网格) -->
|
||
<scroll-view class="sc-main" scroll-y :scroll-top="mainScrollTop">
|
||
<view class="sc-sec">
|
||
<text class="sc-sec-tt">{{ activeCateName }}</text>
|
||
<view class="sc-grid">
|
||
<view class="sc-item" v-for="s in activeItems" :key="s.id" @tap="toDetail(s)">
|
||
<!-- 与详情页轮播一致:固定高度铺满格子,超出部分居中裁剪(aspectFill) -->
|
||
<image class="sc-item-img" :src="s.showImg" mode="aspectFill" />
|
||
<text class="sc-item-name">{{ s.service_name }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="sc-empty" v-if="!activeItems.length">该分类暂无服务</view>
|
||
<view class="sc-pad"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { request, picUrl, NavgateTo } from '@/utils'
|
||
import { apiArr } from '@/api/homeService'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
statusBarTop: 20,
|
||
cityName: '定位中',
|
||
keyword: '',
|
||
categoryList: [],
|
||
activeCate: 0,
|
||
mainScrollTop: 0,
|
||
groups: [] // [{ id, name, items: [service] }]
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
const sys = uni.getSystemInfoSync()
|
||
this.statusBarTop = sys.statusBarHeight || 20
|
||
const loc = uni.getStorageSync('location') || {}
|
||
this.cityName = loc.cityName || '定位中'
|
||
this.initData(options && options.cate_id ? Number(options.cate_id) : 0)
|
||
},
|
||
computed: {
|
||
activeCateName() {
|
||
const c = this.categoryList.find(x => x.id === this.activeCate)
|
||
return c ? c.category_name : ''
|
||
},
|
||
// 当前分类的商品(3列直出),支持搜索词过滤
|
||
activeItems() {
|
||
const grp = this.groups.find(g => g.id === this.activeCate)
|
||
const items = grp ? grp.items : []
|
||
const kw = this.keyword.trim()
|
||
return kw ? items.filter(s => (s.service_name || '').indexOf(kw) >= 0) : items
|
||
}
|
||
},
|
||
methods: {
|
||
async initData(cateId) {
|
||
try {
|
||
const [cateRes, listRes] = await Promise.all([
|
||
request(apiArr.categorySubList, 'POST', {}, {}, false),
|
||
// 一次拉全量服务(目录页数据量小),按分类分组
|
||
request(apiArr.serviceList, 'POST', { service_type: 0, category_id: 0, page_num: 1, page_size: 500 }, {}, false)
|
||
])
|
||
this.categoryList = (cateRes && cateRes.rows) ? cateRes.rows : []
|
||
const services = ((listRes && listRes.rows) ? listRes.rows : []).map(s => {
|
||
const cover0 = s.cover_pic ? String(s.cover_pic).split(',')[0] : ''
|
||
s.showImg = cover0 ? picUrl + cover0 : ''
|
||
return s
|
||
})
|
||
this.groups = this.categoryList.map(c => ({
|
||
id: c.id, name: c.category_name,
|
||
items: services.filter(s => s.category_id === c.id)
|
||
}))
|
||
this.activeCate = cateId || (this.categoryList[0] ? this.categoryList[0].id : 0)
|
||
} catch (e) {
|
||
console.warn('服务目录加载失败:', e)
|
||
}
|
||
},
|
||
selectCate(cat) {
|
||
this.activeCate = cat.id
|
||
// 切分类右侧回到顶部
|
||
this.mainScrollTop = this.mainScrollTop === 0 ? 0.01 : 0
|
||
},
|
||
toDetail(s) {
|
||
NavgateTo('/packages/homeService/detail/index?id=' + s.id)
|
||
},
|
||
goBack() {
|
||
uni.navigateBack({ delta: 1, fail: () => NavgateTo('/packages/homeService/index/index', { isLogin: false }) })
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
page { background: #fff; }
|
||
.sc-page { height: 100vh; display: flex; flex-direction: column; background: #fff; }
|
||
|
||
/* 头部 */
|
||
.sc-hd { flex-shrink: 0; background: #fff; padding: 0 24rpx 16rpx; }
|
||
.sc-nav { position: relative; display: flex; align-items: center; justify-content: center; height: 88rpx; }
|
||
.sc-back { position: absolute; left: 0; top: 0; height: 88rpx; display: flex; align-items: center; }
|
||
.sc-title { font-size: 36rpx; font-weight: 700; color: #1a1a1a; }
|
||
.sc-search { display: flex; align-items: center; height: 76rpx; background: #f4f5f7; border-radius: 16rpx; padding: 0 24rpx; }
|
||
.sc-city { flex-shrink: 0; font-size: 28rpx; color: #111; max-width: 160rpx; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
||
.sc-city-arrow { margin: 0 10rpx 0 6rpx; font-size: 22rpx; color: #666; }
|
||
.sc-search-line { width: 2rpx; height: 32rpx; background: #d9d9d9; margin-right: 18rpx; }
|
||
.sc-search-input { flex: 1; margin-left: 12rpx; font-size: 28rpx; color: #333; }
|
||
.sc-ph { color: #a0a0a0; }
|
||
|
||
/* 主体双栏 */
|
||
.sc-body { flex: 1; min-height: 0; display: flex; }
|
||
.sc-side { flex-shrink: 0; width: 200rpx; height: 100%; background: #f5f6f7; }
|
||
.sc-side-item { position: relative; padding: 34rpx 16rpx; font-size: 28rpx; color: #333; text-align: center; }
|
||
.sc-side-item.on { background: #fff; color: #e9272d; font-weight: 700; }
|
||
.sc-side-item.on::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 8rpx; height: 40rpx; background: #e9272d; border-radius: 0 4rpx 4rpx 0; }
|
||
|
||
.sc-main { flex: 1; min-width: 0; height: 100%; background: #fff; }
|
||
.sc-sec { padding: 24rpx 20rpx 6rpx; }
|
||
.sc-sec-tt { display: block; font-size: 32rpx; font-weight: 700; color: #1a1a1a; margin-bottom: 20rpx; }
|
||
.sc-grid { display: flex; flex-wrap: wrap; }
|
||
.sc-item { width: 33.33%; display: flex; flex-direction: column; align-items: center; margin-bottom: 28rpx; padding: 0 8rpx; box-sizing: border-box; }
|
||
.sc-item-img { width: 100%; height: 84px; border-radius: 16rpx; background: #f4f5f7; display: block; }
|
||
.sc-item-name { margin-top: 12rpx; font-size: 26rpx; color: #333; text-align: center; line-height: 1.35; }
|
||
.sc-empty { text-align: center; color: #9aa0a8; font-size: 26rpx; padding: 120rpx 0; }
|
||
.sc-pad { height: 40rpx; }
|
||
</style>
|