<template>
|
|
<view class="page__view">
|
|
|
|
<view class="header">
|
|
<view class="filter">
|
|
<view class="filter-header">
|
|
<view class="bar">
|
|
<view>
|
|
<button :class="['btn', isFold ? 'is-fold' : '']" @click="isFold = !isFold">
|
|
<text>筛选</text>
|
|
<image class="btn-icon" :src="isFold ? '/static/image/icon-arrow-down.png' : '/static/image/icon-arrow-up-light.png'" mode="widthFix"></image>
|
|
</button>
|
|
</view>
|
|
<view class="title">分类</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="!isFold" class="filter-content">
|
|
<view class="filter-item" v-for="filter in filters" :key="filter.id">
|
|
<view class="filter-item-label">{{ `${filter.label}:` }}</view>
|
|
<view class="filter-item-content">
|
|
<template v-if="filter.key === 'price'">
|
|
<view class="flex range price">
|
|
<view class="range-item">
|
|
<uv-input
|
|
v-model="startPrice"
|
|
type="number"
|
|
inputAlign="center"
|
|
placeholder="开始价格"
|
|
placeholderStyle="color: #181818; font-size: 28rpx; font-weight: 400;"
|
|
:customStyle="{
|
|
backgroundColor: 'transparent',
|
|
padding: '0',
|
|
boxSizing: 'border-box',
|
|
fontSize: '28rpx',
|
|
border: 'none',
|
|
}"
|
|
fontSize="28rpx"
|
|
:clearable="true"
|
|
></uv-input>
|
|
</view>
|
|
<view class="split">至</view>
|
|
<view class="range-item">
|
|
<uv-input
|
|
v-model="endPrice"
|
|
type="number"
|
|
inputAlign="center"
|
|
placeholder="结束价格"
|
|
placeholderStyle="color: #181818; font-size: 28rpx; font-weight: 400;"
|
|
:customStyle="{
|
|
backgroundColor: 'transparent',
|
|
padding: '0',
|
|
boxSizing: 'border-box',
|
|
fontSize: '28rpx',
|
|
border: 'none',
|
|
}"
|
|
fontSize="28rpx"
|
|
:clearable="true"
|
|
></uv-input>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<template v-else-if="filter.key === 'time'">
|
|
<view class="flex range time">
|
|
<view class="range-item" @click="openStartDatePicker">
|
|
{{ startDate ? $dayjs(startDate).format('YYYY-MM-DD') : '开始日期' }}
|
|
<button v-if="startDate" class="btn btn-clear" @click.stop="onClearStartDate">
|
|
<uv-icon name="close-circle" color="#B5B5B5" size="28rpx"></uv-icon>
|
|
</button>
|
|
</view>
|
|
<view class="split">至</view>
|
|
<view class="range-item" @click="openEndDatePicker">
|
|
{{ endDate ? $dayjs(endDate).format('YYYY-MM-DD') : '结束日期' }}
|
|
<button v-if="endDate" class="btn btn-clear" @click.stop="onClearEndDate">
|
|
<uv-icon name="close-circle" color="#B5B5B5" size="28rpx"></uv-icon>
|
|
</button>
|
|
</view>
|
|
</view>
|
|
<uv-datetime-picker
|
|
ref="startDatePicker"
|
|
v-model="startDate"
|
|
mode="date"
|
|
title="开始日期"
|
|
confirmColor="#00A9FF"
|
|
round="32rpx"
|
|
:minDate="minTime"
|
|
@confirm="onStartDateChange"
|
|
></uv-datetime-picker>
|
|
<uv-datetime-picker
|
|
ref="endDatePicker"
|
|
v-model="endDate"
|
|
mode="date"
|
|
title="结束日期"
|
|
confirmColor="#00A9FF"
|
|
round="32rpx"
|
|
:minDate="startDate || minTime"
|
|
@confirm="onEndDateChange"
|
|
></uv-datetime-picker>
|
|
</template>
|
|
<template v-else>
|
|
<view class="option">
|
|
<view
|
|
v-for="option in filter.options"
|
|
:key="option.id"
|
|
:class="['option-item', option.id == queryParams[filter.key] ? 'is-active' : '']"
|
|
@click="onClickFilter(filter.key, option.id)"
|
|
>
|
|
{{ option.label }}
|
|
</view>
|
|
</view>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
|
|
<button class="flex btn btn-fold" @click="isFold = false">
|
|
<image class="btn-icon" src="@/static/image/icon-arrow-up.png" mode="widthFix"></image>
|
|
</button>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="sort">
|
|
<sortBar v-model="queryParams.sort" @change="onSortChange"></sortBar>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 分类商品列表 -->
|
|
<view class="main" >
|
|
|
|
<uv-vtabs
|
|
:list="categoryList"
|
|
keyName="name"
|
|
:current="current"
|
|
:chain="true"
|
|
@change="change"
|
|
|
|
barWidth="177rpx"
|
|
barBgColor="#F5F5F5"
|
|
:barItemStyle="{
|
|
color: '#1D2129',
|
|
fontSize: '28rpx',
|
|
fontWeight: 400,
|
|
}"
|
|
:barItemActiveStyle="{
|
|
color: '#00A9FF',
|
|
fontWeight: 600,
|
|
backgroundColor: '#FFFFFF',
|
|
}"
|
|
:barItemActiveLineStyle="{
|
|
background: '#00A9FF',
|
|
margin: '48rpx 4rpx',
|
|
borderRadius: '4rpx',
|
|
}"
|
|
>
|
|
<uv-vtabs-item v-for="(item, index) in categoryList" :index="index" :key="item.id">
|
|
<template v-if="item.children.length">
|
|
<view class="card" v-for="product in item.children" :key="product.id" >
|
|
<productCard :data="product" ></productCard>
|
|
</view>
|
|
</template>
|
|
<template v-else>
|
|
<uv-empty text="还没有呢"/>
|
|
</template>
|
|
</uv-vtabs-item>
|
|
</uv-vtabs>
|
|
</view>
|
|
|
|
<!-- tabbar -->
|
|
<tabber select="category" />
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
// import mixinsList from '@/mixins/list.js'
|
|
import { mapState } from 'vuex'
|
|
|
|
import tabber from '@/components/base/tabbar.vue'
|
|
import sortBar from '@/components/product/sortBar.vue'
|
|
import productCard from '@/components/product/productCard.vue'
|
|
|
|
export default {
|
|
// mixins: [mixinsList],
|
|
components: {
|
|
sortBar,
|
|
productCard,
|
|
tabber,
|
|
},
|
|
data() {
|
|
return {
|
|
current: 0,
|
|
startPrice: null,
|
|
endPrice: null,
|
|
startDate: null,
|
|
endDate: null,
|
|
minTime: new Date().getTime(),
|
|
queryParams: {
|
|
pageNo: 1,
|
|
pageSize: 1000,
|
|
// todo
|
|
sort: 'comprehensive',
|
|
},
|
|
categoryList: [],
|
|
filters: [],
|
|
isFold: true,
|
|
}
|
|
},
|
|
async onLoad({ categoryId }) {
|
|
|
|
await Promise.allSettled([this.fetchCategoryList(), this.fetchFilters()])
|
|
console.log('categoryList', this.categoryList)
|
|
console.log('filters', this.filters)
|
|
|
|
await this.initList()
|
|
|
|
if(this.categoryList.length > 0 && categoryId){
|
|
setTimeout(() => {
|
|
this.current = this.categoryList.findIndex(item => item.id === categoryId)
|
|
}, 800)
|
|
// this.$nextTick(() => {
|
|
// this.current = this.categoryList.findIndex(item => item.id === categoryId)
|
|
// })
|
|
}
|
|
},
|
|
methods: {
|
|
async fetchCategoryList() {
|
|
this.categoryList = [
|
|
{
|
|
"key": "1962345168240185345",
|
|
"title": "国际游",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345225345634305",
|
|
"title": "夏令营",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345290571255810",
|
|
"title": "周末营",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345372007862273",
|
|
"title": "周边游",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345497681793025",
|
|
"title": "定制游",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345589524467714",
|
|
"title": "周末活动",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345642188148737",
|
|
"title": "亲子活动",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962345709817106434",
|
|
"title": "主题研学",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962346300198948866",
|
|
"title": "社会实践",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962346769759670273",
|
|
"title": "研学交流",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962346834884628481",
|
|
"title": "周末研学",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962346960097185793",
|
|
"title": "假期专享",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
},
|
|
{
|
|
"key": "1962347024639135745",
|
|
"title": "本地研学",
|
|
"icon": null,
|
|
"parentId": "0",
|
|
"value": null,
|
|
"code": null,
|
|
"children": null,
|
|
"leaf": true
|
|
}
|
|
].map(item => {
|
|
const { key, title } = item
|
|
|
|
return {
|
|
id: key,
|
|
name: title,
|
|
children: []
|
|
}
|
|
})
|
|
|
|
return
|
|
try {
|
|
this.categoryList = (await this.$fetch('getCategoryList', { pageSize: 1000 }))?.records?.map(item => ({ id: item.id, name: item.name, children: [] }))
|
|
} catch(err) {
|
|
this.categoryList = []
|
|
}
|
|
},
|
|
async fetchFilters() {
|
|
|
|
this.filters = [
|
|
{
|
|
id: '001',
|
|
key: 'frontier',
|
|
label: '国境',
|
|
options: [
|
|
{
|
|
id: '00101',
|
|
label: '国内',
|
|
},
|
|
{
|
|
id: '00102',
|
|
label: '国外',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: '002',
|
|
key: 'addressId',
|
|
label: '目的地',
|
|
options: [
|
|
{
|
|
label: '全部',
|
|
},
|
|
{
|
|
id: '00201',
|
|
label: '上海',
|
|
},
|
|
{
|
|
id: '00202',
|
|
label: '北京',
|
|
},
|
|
{
|
|
id: '00203',
|
|
label: '浙江省',
|
|
},
|
|
{
|
|
id: '00204',
|
|
label: '广东省',
|
|
},
|
|
{
|
|
id: '00205',
|
|
label: '广西省',
|
|
},
|
|
{
|
|
id: '00206',
|
|
label: '云南省',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: '003',
|
|
key: 'ageId',
|
|
label: '适合年龄',
|
|
options: [
|
|
{
|
|
label: '全部',
|
|
},
|
|
{
|
|
id: '00301',
|
|
label: '6-10岁',
|
|
},
|
|
{
|
|
id: '00302',
|
|
label: '11-14岁',
|
|
},
|
|
{
|
|
id: '00303',
|
|
label: '15-16岁',
|
|
},
|
|
{
|
|
id: '00304',
|
|
label: '17-18岁',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: '004',
|
|
key: 'timeId',
|
|
label: '活动时长',
|
|
options: [
|
|
{
|
|
label: '全部',
|
|
},
|
|
{
|
|
id: '00401',
|
|
label: '1日',
|
|
},
|
|
{
|
|
id: '00402',
|
|
label: '多日',
|
|
},
|
|
{
|
|
id: '00403',
|
|
label: '寒假',
|
|
},
|
|
{
|
|
id: '00404',
|
|
label: '暑假',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: '005',
|
|
key: 'price',
|
|
label: '价格区间',
|
|
},
|
|
{
|
|
id: '006',
|
|
key: 'time',
|
|
label: '出发日期',
|
|
},
|
|
]
|
|
|
|
this.filters.forEach(item => {
|
|
const { key, options } = item
|
|
|
|
if (!options?.length || !options[0]?.id) {
|
|
return
|
|
}
|
|
|
|
this.queryParams[key] = options[0].id
|
|
})
|
|
|
|
// todo: fetch
|
|
},
|
|
async queryProductList(categoryId) {
|
|
|
|
try {
|
|
return (await this.$fetch('queryActivityList', { ...this.queryParams, categoryId }))?.records || []
|
|
} catch (err) {
|
|
return []
|
|
}
|
|
|
|
return [
|
|
{
|
|
id: '001',
|
|
image: '/static/image/temp-20.png',
|
|
title: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
{
|
|
id: '002',
|
|
image: '/static/image/temp-20.png',
|
|
title: '坝上双草原6日|乌兰布统+锡林郭勒+长城',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
{
|
|
id: '003',
|
|
image: '/static/image/temp-20.png',
|
|
title: '牛湖线探秘 | 清远牛湖线徒步,探秘天坑与大草原',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
{
|
|
id: '004',
|
|
image: '/static/image/temp-20.png',
|
|
title: '低海拔藏区草原,汉藏文化大穿越',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
{
|
|
id: '005',
|
|
image: '/static/image/temp-20.png',
|
|
title: '新丝路到敦煌7日 | 甘青轻松穿越,沙漠+草原',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
{
|
|
id: '006',
|
|
image: '/static/image/temp-20.png',
|
|
title: '呼伦贝尔6/8日|经典or环线双套餐可选',
|
|
tagList: ['国内游','7-9天','12岁+'],
|
|
priceDiscount: 688.99,
|
|
priceOrigin: 1200,
|
|
applyNum: 4168,
|
|
},
|
|
]
|
|
},
|
|
async initList() {
|
|
console.log('queryParams', this.queryParams)
|
|
const results = await Promise.allSettled(this.categoryList.map(category => { return this.queryProductList(category.id) }))
|
|
|
|
results.forEach((result, index) => {
|
|
this.categoryList[index].children = result.value || []
|
|
})
|
|
|
|
console.log('categoryList', this.categoryList)
|
|
},
|
|
change(e) {
|
|
this.current = e
|
|
},
|
|
search(){
|
|
// todo: set filter
|
|
this.initList()
|
|
},
|
|
onClickFilter(key, val) {
|
|
if (val) {
|
|
this.queryParams[key] = val
|
|
} else {
|
|
delete this.queryParams[key]
|
|
}
|
|
this.initList()
|
|
},
|
|
openStartDatePicker() {
|
|
this.$refs.startDatePicker?.[0]?.open?.();
|
|
},
|
|
onStartDateChange(e) {
|
|
const date = e.value
|
|
|
|
this.queryParams.startDate = date
|
|
|
|
const { endDate } = this.queryParams
|
|
|
|
if (endDate && this.$dayjs(date).isAfter(endDate, 'day')) {
|
|
this.endDate = null
|
|
delete this.queryParams.endDate
|
|
}
|
|
|
|
this.initList()
|
|
},
|
|
onClearStartDate() {
|
|
this.startDate = null
|
|
|
|
delete this.queryParams.startDate
|
|
|
|
this.initList()
|
|
},
|
|
openEndDatePicker() {
|
|
this.$refs.endDatePicker?.[0]?.open?.();
|
|
},
|
|
onEndDateChange(e) {
|
|
const date = e.value
|
|
|
|
this.queryParams.endDate = date
|
|
|
|
const { startDate } = this.queryParams
|
|
|
|
if (startDate && this.$dayjs(date).isBefore(startDate, 'day')) {
|
|
this.startDate = null
|
|
delete this.queryParams.startDate
|
|
}
|
|
|
|
this.initList()
|
|
},
|
|
onClearEndDate() {
|
|
this.endDate = null
|
|
|
|
delete this.queryParams.endDate
|
|
|
|
this.initList()
|
|
},
|
|
onSortChange(sort) {
|
|
console.log('onSortChange', sort)
|
|
// todo set sort
|
|
this.getData()
|
|
},
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.page__view {
|
|
height: 100vh;
|
|
background: linear-gradient(#DAF3FF, #FBFEFF 200rpx, #FBFEFF);
|
|
|
|
/deep/ .uv-popup {
|
|
z-index: 1000000 !important;
|
|
}
|
|
}
|
|
|
|
.header {
|
|
width: 100%;
|
|
padding: 0 32rpx;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.filter {
|
|
|
|
&-header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-end;
|
|
height: 176rpx;
|
|
padding-bottom: 12rpx;
|
|
box-sizing: border-box;
|
|
|
|
.bar {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
column-gap: 8rpx;
|
|
padding: 8rpx 30rpx;
|
|
font-size: 28rpx;
|
|
font-weight: 500;
|
|
color: #FFFFFF;
|
|
background: #00A9FF;
|
|
border: 2rpx solid #00A9FF;
|
|
border-radius: 64rpx;
|
|
|
|
&-icon {
|
|
width: 32rpx;
|
|
height: auto;
|
|
}
|
|
|
|
&.is-fold {
|
|
font-weight: 400;
|
|
color: #191919;
|
|
background: #D8F2FF;
|
|
border-color: #00A9FF66;
|
|
}
|
|
}
|
|
|
|
.title {
|
|
text-align: center;
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #191919;
|
|
}
|
|
}
|
|
}
|
|
|
|
&-content {
|
|
margin-top: 24rpx;
|
|
|
|
.btn {
|
|
&-fold {
|
|
margin-top: 32rpx;
|
|
width: 100%;
|
|
}
|
|
|
|
&-icon {
|
|
width: 40rpx;
|
|
height: auto;
|
|
}
|
|
}
|
|
}
|
|
|
|
&-item {
|
|
display: flex;
|
|
|
|
& + & {
|
|
margin-top: 32rpx;
|
|
}
|
|
|
|
&-label {
|
|
width: 156rpx;
|
|
min-height: 64rpx;
|
|
line-height: 64rpx;
|
|
flex: none;
|
|
}
|
|
|
|
&-content {
|
|
flex: 1;
|
|
|
|
.option {
|
|
margin-top: 6rpx;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 24rpx;
|
|
|
|
&-item {
|
|
padding: 8rpx 16rpx;
|
|
font-size: 28rpx;
|
|
color: #181818;
|
|
border-radius: 4rpx;
|
|
|
|
&.is-active {
|
|
color: #FFFFFF;
|
|
background: #00A9FF;
|
|
}
|
|
}
|
|
}
|
|
|
|
.range {
|
|
margin: 4rpx 0;
|
|
column-gap: 8rpx;
|
|
border-bottom: 2rpx solid #EEEEEE;
|
|
|
|
&-item {
|
|
width: 220rpx;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.split {
|
|
padding: 0 24rpx;
|
|
font-size: 32rpx;
|
|
line-height: 1;
|
|
color: #8B8B8B;
|
|
}
|
|
|
|
&.price {
|
|
.range-item {
|
|
padding: 4rpx 0;
|
|
}
|
|
}
|
|
|
|
&.time {
|
|
.range-item {
|
|
// width: 220rpx;
|
|
padding: 8rpx 0;
|
|
text-align: center;
|
|
font-size: 28rpx;
|
|
color: #181818;
|
|
|
|
.btn-clear {
|
|
margin: 6rpx 0;
|
|
float: right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.sort {
|
|
width: 100%;
|
|
height: 116rpx;
|
|
padding: 24rpx 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.main {
|
|
/deep/ .uv-vtabs,
|
|
/deep/ .uv-vtabs__bar,
|
|
/deep/ .uv-vtabs__content {
|
|
height: calc(100vh - 292rpx - #{$tabbar-height} - env(safe-area-inset-bottom)) !important;
|
|
}
|
|
|
|
/deep/ .uv-vtabs__bar {
|
|
background: #F6F6F6 !important;
|
|
}
|
|
|
|
/deep/ .uv-vtabs__bar-item {
|
|
padding: 48rpx 32rpx;
|
|
}
|
|
|
|
/deep/ .uv-vtabs__content {
|
|
padding: 24rpx 24rpx 0 24rpx;
|
|
box-sizing: border-box;
|
|
background: linear-gradient(#DAF3FF, #F4F4F4 250rpx, #F4F4F4);
|
|
}
|
|
}
|
|
|
|
.card {
|
|
& + & {
|
|
margin-top: 32rpx;
|
|
}
|
|
|
|
&:last-child {
|
|
padding-bottom: 24rpx;
|
|
}
|
|
}
|
|
|
|
</style>
|