Files
meida_applet/pages/index/list.vue
T
2025-10-10 17:23:24 +08:00

355 lines
9.5 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.
<template>
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/list" title="商品列表" :leftIcon="''">
<!-- 筛选 -->
<su-sticky bgColor="#fff">
<view class="ss-flex">
<view style="width: calc(100% - 210rpx)">
<su-tabs
:list="categoryState.categoryList"
:current="categoryState.curCategoryTab"
lineColor="#9251EB "
@change="onCategoryTabsChange"
/>
</view>
<view
style="
width: 210rpx;
flex-shrink: 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
"
@click="
() => {
sortState.showFilter = true;
}
"
>
{{ sortState.sortList[0].name }}
<image
src="https://puton.huimeimeta.com/imgs/down-icon.png"
style="width: 36rpx; height: 36rpx"
/>
</view>
</view>
</su-sticky>
<!-- 弹窗 -->
<su-popup
:show="sortState.showFilter"
type="top"
round="10"
:space="sys_navBar + 38"
backgroundColor="#F6F6F6"
:zIndex="10"
@close="sortState.showFilter = false"
>
<view class="filter-list-box">
<view
class="filter-item"
v-for="(item, index) in sortState.sortList[sortState.curSortTab].list"
:key="item.value"
:class="[{ 'filter-item-active': index === sortState.curFilter }]"
@tap="onFilterItem(index)"
>
{{ item.label }}
</view>
</view>
</su-popup>
<view
v-if="goodsState.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 goodsState.leftGoodsList" :key="item.id">
<!-- 很多地方都引入了s-goods-column组件为了不影响其他页面这里用s-goods-column-another组件 -->
<s-goods-column-another
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-another>
</view>
</view>
<view class="goods-list-box">
<view class="right-list" v-for="item in goodsState.rightGoodsList" :key="item.id">
<s-goods-column-another
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-another>
</view>
</view>
</view>
<uni-load-more
v-if="goodsState.pagination.total > 0"
:status="goodsState.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
@tap="loadMore"
/>
<s-empty
v-if="goodsState.pagination.total === 0"
icon="/static/soldout-empty.png"
text="暂无商品"
/>
</s-layout>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { handleTree } from '@/sheep/helper/utils';
import CategoryApi from '@/sheep/api/product/category';
import SpuApi from '@/sheep/api/product/spu';
import OrderApi from '@/sheep/api/trade/order';
import sheep from '@/sheep';
import { resetPagination } from '@/sheep/helper/utils';
import _ from 'lodash-es';
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
const sys_navBar = sheep.$platform.navbar;
onLoad(async (params) => {
await getCategoryList();
getList();
});
const categoryState = reactive({
curCategoryTab: 0,
curCategory: 0,
categoryList: [],
});
// 加载商品分类
async function getCategoryList() {
const { code, data } = await CategoryApi.getCategoryList();
if (code !== 0) {
return;
}
categoryState.categoryList =
handleTree(data)[0]?.children?.map((item) => ({
...item,
sort: item.id,
order: false,
})) || [];
categoryState.categoryList.unshift({
id: 0,
name: '全部',
sort: 0,
order: false,
});
console.log(categoryState.categoryList, 'categoryState.categoryList');
}
const sortState = reactive({
curSortTab: 0,
curFilter: 0,
sortList: [
{
name: '综合推荐',
list: [
{
label: '综合推荐',
},
{
label: '价格升序',
sort: 'price',
order: true,
},
{
label: '价格降序',
sort: 'price',
order: false,
},
{
label: '销量',
sort: 'salesCount',
order: false,
},
{
label: '新品优先',
sort: 'createTime',
order: false,
},
],
},
],
showFilter: false,
curOrder: undefined,
curSort: undefined,
});
// 选择分类
function onCategoryTabsChange(e) {
if (e.index === categoryState.curCategoryTab) return;
categoryState.curCategoryTab = e.index;
categoryState.curCategory = e.id;
emptyList();
getList();
}
// 排序规则
const onFilterItem = (val) => {
// 如果点击的是当前的筛选项,则直接收起筛选项,不要加载数据
// 这里选择 tabList[0] 的原因,是目前只有它有 list
if (
sortState.curSort === sortState.sortList[0].list[val].sort &&
sortState.curOrder === sortState.sortList[0].list[val].order
) {
sortState.showFilter = false;
return;
}
sortState.showFilter = false;
// 设置筛选条件
sortState.curFilter = val;
sortState.sortList[0].name = sortState.sortList[0].list[val].label;
sortState.curSort = sortState.sortList[0].list[val].sort;
sortState.curOrder = sortState.sortList[0].list[val].order;
// 清空 + 加载数据
emptyList();
getList();
};
const goodsState = reactive({
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 6,
},
loadStatus: '',
leftGoodsList: [],
rightGoodsList: [],
});
// 加载瀑布流
let count = 0;
let leftHeight = 0;
let rightHeight = 0;
// 处理双列布局 leftGoodsList + rightGoodsList
function mountMasonry(height = 0, where = 'left') {
if (where === 'left') {
leftHeight += height;
} else {
rightHeight += height;
}
if (!goodsState.pagination.list[count]) {
return;
}
if (leftHeight <= rightHeight) {
goodsState.leftGoodsList.push(goodsState.pagination.list[count]);
} else {
goodsState.rightGoodsList.push(goodsState.pagination.list[count]);
}
count++;
}
// 清空列表
function emptyList() {
resetPagination(goodsState.pagination);
goodsState.leftGoodsList = [];
goodsState.rightGoodsList = [];
count = 0;
leftHeight = 0;
rightHeight = 0;
}
async function getList() {
goodsState.loadStatus = 'loading';
const { code, data } = await SpuApi.getSpuPage({
pageNo: goodsState.pagination.pageNo,
pageSize: goodsState.pagination.pageSize,
sortField: sortState.curSort,
sortAsc: sortState.curOrder,
categoryId: categoryState.curCategory,
keyword: '',
});
if (code !== 0) {
return;
}
// 拼接结算信息(营销)
await OrderApi.getSettlementProduct(data.list.map((item) => item.id).join(',')).then((res) => {
if (res.code !== 0) {
return;
}
appendSettlementProduct(data.list, res.data);
});
goodsState.pagination.list = _.concat(goodsState.pagination.list, data.list);
goodsState.pagination.total = data.total;
goodsState.loadStatus =
goodsState.pagination.list.length < goodsState.pagination.total ? 'more' : 'noMore';
mountMasonry();
}
// 加载更多
function loadMore() {
if (goodsState.loadStatus === 'noMore') {
return;
}
goodsState.pagination.pageNo++;
getList();
}
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
</script>
<style lang="scss" scoped>
.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);
}
}
.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;
}
}
</style>