|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |  | <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 }) {			if(uni.getStorageSync('token')){				this.$store.commit('getUserInfo')			}
      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() {				try {					this.categoryList = (await this.$fetch('queryCategoryList', { pageSize: 1000 }))?.records?.map(item => ({ id: item.id, name: item.title, children: [] }))				} catch(err) {					this.categoryList = []				}        return
        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: []          }        })
			},      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 []				}			},			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>
 |