<template> <s-layout navbar="normal" :leftWidth="0" :rightWidth="0" tools="search" :defaultSearch="state.keyword" @search="onSearch"> <!-- 筛选 --> <su-sticky bgColor="#fff"> <view class="ss-flex"> <view class="ss-flex-1"> <su-tabs :list="state.tabList" :scrollable="false" @change="onTabsChange" :current="state.currentTab" /> </view> <view class="list-icon" @tap="state.iconStatus = !state.iconStatus"> <text v-if="state.iconStatus" class="sicon-goods-list" /> <text v-else class="sicon-goods-card" /> </view> </view> </su-sticky> <!-- 弹窗 --> <su-popup :show="state.showFilter" type="top" round="10" :space="sys_navBar + 38" backgroundColor="#F6F6F6" :zIndex="10" @close="state.showFilter = false"> <view class="filter-list-box"> <view class="filter-item" v-for="(item, index) in state.tabList[state.currentTab].list" :key="item.value" :class="[{ 'filter-item-active': index === state.curFilter }]" @tap="onFilterItem(index)"> {{ item.label }} </view> </view> </su-popup> <!-- 情况一:单列布局 --> <view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20"> <view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id"> <s-goods-column class="" size="lg" :data="item" :topRadius="10" :bottomRadius="10" @click="sheep.$router.go('/pages/goods/index', { id: item.id })" /> </view> </view> <!-- 情况二:双列布局 --> <view v-if="!state.iconStatus && state.pagination.total > 0" class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"> <view class="goods-list-box"> <view class="left-list" v-for="item in state.leftGoodsList" :key="item.id"> <s-goods-column class="goods-md-box" size="md" :data="item" :topRadius="10" :bottomRadius="10" @click="sheep.$router.go('/pages/goods/index', { id: item.id })" @getHeight="mountMasonry($event, 'left')" > <template v-slot:cart> <button class="ss-reset-button cart-btn" /> </template> </s-goods-column> </view> </view> <view class="goods-list-box"> <view class="right-list" v-for="item in state.rightGoodsList" :key="item.id"> <s-goods-column class="goods-md-box" size="md" :topRadius="10" :bottomRadius="10" :data="item" @click="sheep.$router.go('/pages/goods/index', { id: item.id })" @getHeight="mountMasonry($event, 'right')" > <template v-slot:cart> <button class="ss-reset-button cart-btn" /> </template> </s-goods-column> </view> </view> </view> <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ contentdown: '上拉加载更多', }" @tap="loadMore" /> <s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" /> </s-layout> </template> <script setup> import { reactive } from 'vue'; import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import sheep from '@/sheep'; import _ from 'lodash'; import { resetPagination } from '@/sheep/util'; import SpuApi from '@/sheep/api/product/spu'; const sys_navBar = sheep.$platform.navbar; const emits = defineEmits(['close', 'change']); const state = reactive({ pagination: { list: [], total: 0, pageNo: 1, pageSize: 6, }, currentSort: undefined, currentOrder: undefined, currentTab: 0, // 当前选中的 tab curFilter: 0, // 当前选中的 list 筛选项 showFilter: false, iconStatus: false, // true - 单列布局;false - 双列布局 keyword: '', categoryId: 0, tabList: [{ name: '综合推荐', list: [{ label: '综合推荐' }, { label: '价格升序', sort: 'price', order: true, }, { label: '价格降序', sort: 'price', order: false, }, ], }, { name: '销量', sort: 'salesCount', order: false }, { name: '新品优先', value: 'createTime', order: false }, ], loadStatus: '', leftGoodsList: [], // 双列布局 - 左侧商品 rightGoodsList: [], // 双列布局 - 右侧商品 }); // 加载瀑布流 let count = 0; let leftHeight = 0; let rightHeight = 0; // 处理双列布局 leftGoodsList + rightGoodsList function mountMasonry(height = 0, where = 'left') { if (!state.pagination.list[count]) { return; } if (where === 'left') { leftHeight += height; } else { rightHeight += height; } if (leftHeight <= rightHeight) { state.leftGoodsList.push(state.pagination.list[count]); } else { state.rightGoodsList.push(state.pagination.list[count]); } count++; } // 清空列表 function emptyList() { resetPagination(state.pagination); state.leftGoodsList = []; state.rightGoodsList = []; count = 0; leftHeight = 0; rightHeight = 0; } // 搜索 function onSearch(e) { state.keyword = e; emptyList(); getList(state.currentSort, state.currentOrder); } // 点击 function onTabsChange(e) { // 如果点击的是【综合推荐】,则直接展开或者收起筛选项 if (state.tabList[e.index].list) { state.currentTab = e.index; state.showFilter = !state.showFilter; return; } state.showFilter = false; // 如果点击的是【销量】或者【新品优先】,则直接切换 tab if (e.index === state.currentTab) { return; } state.currentTab = e.index; state.currentSort = e.sort; state.currentOrder = e.order; emptyList(); getList(e.sort, e.order); } // 点击 tab 的 list 筛选项 const onFilterItem = (val) => { // 如果点击的是当前的筛选项,则直接收起筛选项,不要加载数据 // 这里选择 tabList[0] 的原因,是目前只有它有 list if (state.currentSort === state.tabList[0].list[val].sort && state.currentOrder === state.tabList[0].list[val].order) { state.showFilter = false; return; } state.showFilter = false; // 设置筛选条件 state.curFilter = val; state.tabList[0].name = state.tabList[0].list[val].label; state.currentSort = state.tabList[0].list[val].sort; state.currentOrder = state.tabList[0].list[val].order; // 清空 + 加载数据 emptyList(); getList(); } async function getList() { state.loadStatus = 'loading'; const { code, data } = await SpuApi.getSpuPage({ pageNo: state.pagination.pageNo, pageSize: state.pagination.pageSize, sortField: state.currentSort, sortAsc: state.currentOrder, categoryId: state.categoryId, keyword: state.keyword, }); if (code !== 0) { return; } state.pagination.list = _.concat(state.pagination.list, data.list) state.pagination.total = data.total; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; mountMasonry(); } // 加载更多 function loadMore() { if (state.loadStatus === 'noMore') { return; } state.pagination.pageNo++; getList(state.currentSort, state.currentOrder); } onLoad((options) => { state.categoryId = options.categoryId; state.keyword = options.keyword; getList(state.currentSort, state.currentOrder); }); // 上拉加载更多 onReachBottom(() => { loadMore(); }); </script> <style lang="scss" scoped> .goods-list-box { width: 50%; box-sizing: border-box; .left-list { margin-right: 10rpx; margin-bottom: 20rpx; } .right-list { margin-left: 10rpx; margin-bottom: 20rpx; } } .goods-box { &:nth-last-of-type(1) { margin-bottom: 0 !important; } &:nth-child(2n) { margin-right: 0; } } .list-icon { width: 80rpx; .sicon-goods-card { font-size: 40rpx; } .sicon-goods-list { font-size: 40rpx; } } .goods-card { margin-left: 20rpx; } .list-filter-tabs { background-color: #fff; } .filter-list-box { padding: 28rpx 52rpx; .filter-item { font-size: 28rpx; font-weight: 500; color: #333333; line-height: normal; margin-bottom: 24rpx; &:nth-last-child(1) { margin-bottom: 0; } } .filter-item-active { color: var(--ui-BG-Main); } } .tab-item { height: 50px; position: relative; z-index: 11; .tab-title { font-size: 30rpx; } .cur-tab-title { font-weight: $font-weight-bold; } .tab-line { width: 60rpx; height: 6rpx; border-radius: 6rpx; position: absolute; left: 50%; transform: translateX(-50%); bottom: 10rpx; background-color: var(--ui-BG-Main); z-index: 12; } } </style>