2026-07-08 16:17:50 +08:00

158 lines
7.0 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.

<!--
页面未来到家 - 服务目录金刚区图标点击进入
格式顶部服务标题 + 搜索栏左侧小类竖向导航选中红条高亮
右侧按小类分组的服务网格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>