191 lines
4.1 KiB
Vue
191 lines
4.1 KiB
Vue
<template>
|
|
<view class="category-list-page page-container">
|
|
<!-- 筛选栏 -->
|
|
<view class="filter-bar">
|
|
<scroll-view class="filter-scroll" scroll-x show-scrollbar="false">
|
|
<view class="filter-tags">
|
|
<view
|
|
v-for="(item, index) in filters"
|
|
:key="index"
|
|
class="filter-tag"
|
|
:class="{ active: currentFilter === index }"
|
|
@click="changeFilter(index)"
|
|
>
|
|
{{ item }}
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<view class="sort-btn" @click="toggleSort">
|
|
<text class="sort-icon">⚡</text>
|
|
<text class="sort-text">{{ sortText }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 服务列表 -->
|
|
<view class="services-list">
|
|
<service-card
|
|
v-for="service in filteredServices"
|
|
:key="service.id"
|
|
:service="service"
|
|
@click="goServiceDetail"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="filteredServices.length === 0" class="empty-state">
|
|
<text class="empty-icon">📦</text>
|
|
<text class="empty-text">暂无服务</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onLoad } from '@dcloudio/uni-app'
|
|
import { services, getServicesByCategory } from '../../mock'
|
|
|
|
const categoryId = ref(0)
|
|
const categoryName = ref('')
|
|
const currentFilter = ref(0)
|
|
const currentSort = ref(0)
|
|
|
|
const filters = ['全部', '代练', '陪玩', '教学']
|
|
const sortOptions = ['默认', '价格', '销量', '评分']
|
|
const sortText = computed(() => sortOptions[currentSort.value])
|
|
|
|
const allServices = ref([])
|
|
const filteredServices = computed(() => {
|
|
let result = allServices.value
|
|
|
|
// 筛选
|
|
if (currentFilter.value > 0) {
|
|
const filterMap = { 1: '代练', 2: '陪玩', 3: '教学' }
|
|
const keyword = filterMap[currentFilter.value]
|
|
result = result.filter(s => s.name.includes(keyword))
|
|
}
|
|
|
|
// 排序
|
|
if (currentSort.value === 1) {
|
|
result = [...result].sort((a, b) => a.price - b.price)
|
|
} else if (currentSort.value === 2) {
|
|
result = [...result].sort((a, b) => b.salesCount - a.salesCount)
|
|
} else if (currentSort.value === 3) {
|
|
result = [...result].sort((a, b) => b.rating - a.rating)
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
onLoad((options) => {
|
|
if (options.categoryId) {
|
|
categoryId.value = parseInt(options.categoryId)
|
|
categoryName.value = options.categoryName || ''
|
|
allServices.value = getServicesByCategory(categoryId.value)
|
|
} else {
|
|
allServices.value = services
|
|
}
|
|
|
|
uni.setNavigationBarTitle({
|
|
title: categoryName.value || '服务列表'
|
|
})
|
|
})
|
|
|
|
const changeFilter = (index) => {
|
|
currentFilter.value = index
|
|
}
|
|
|
|
const toggleSort = () => {
|
|
currentSort.value = (currentSort.value + 1) % sortOptions.length
|
|
}
|
|
|
|
const goServiceDetail = (service) => {
|
|
uni.navigateTo({
|
|
url: `/pages/service/detail?id=${service.id}`
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.category-list-page {
|
|
padding: 24rpx;
|
|
}
|
|
|
|
.filter-bar {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-bottom: 28rpx;
|
|
}
|
|
|
|
.filter-scroll {
|
|
flex: 1;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.filter-tags {
|
|
display: inline-flex;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.filter-tag {
|
|
display: inline-block;
|
|
padding: 12rpx 32rpx;
|
|
background: rgba(20, 25, 50, 0.9);
|
|
border: 1px solid rgba(0, 255, 255, 0.2);
|
|
border-radius: 50rpx;
|
|
font-size: 26rpx;
|
|
color: #a0a4c4;
|
|
transition: all 0.3s ease;
|
|
|
|
&.active {
|
|
background: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
|
color: #0a0e27;
|
|
border-color: transparent;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.sort-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
padding: 12rpx 24rpx;
|
|
background: rgba(20, 25, 50, 0.9);
|
|
border: 1px solid rgba(0, 255, 255, 0.2);
|
|
border-radius: 50rpx;
|
|
font-size: 26rpx;
|
|
color: #00ffff;
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
}
|
|
}
|
|
|
|
.sort-icon {
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.services-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 120rpx 0;
|
|
}
|
|
|
|
.empty-icon {
|
|
font-size: 120rpx;
|
|
margin-bottom: 24rpx;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 28rpx;
|
|
color: #7a7e9d;
|
|
}
|
|
</style>
|