商品分类:列表

pull/26/head^2
YunaiV 2023-12-10 22:12:33 +08:00
parent e971e09593
commit 8fd8ff9f70
6 changed files with 258 additions and 224 deletions

View File

@ -1,231 +1,236 @@
<!-- 分类列表 --> <!-- 商品分类列表 -->
<template> <template>
<s-layout title="分类" tabbar="/pages/index/category" :bgStyle="{ color: '#fff' }"> <s-layout title="分类" tabbar="/pages/index/category" :bgStyle="{ color: '#fff' }">
<view class="s-category"> <view class="s-category">
<view class="three-level-wrap ss-flex ss-col-top" :style="[{ height: pageHeight + 'px' }]"> <view class="three-level-wrap ss-flex ss-col-top" :style="[{ height: pageHeight + 'px' }]">
<!-- 商品分类 --> <!-- 商品分类 -->
<scroll-view class="side-menu-wrap" scroll-y :style="[{ height: pageHeight + 'px' }]"> <scroll-view class="side-menu-wrap" scroll-y :style="[{ height: pageHeight + 'px' }]">
<view class="menu-item ss-flex" v-for="(item, index) in state.categoryList?.children" :key="item.id" <view
:class="[{ 'menu-item-active': index == state.activeMenu }]" @tap="onMenu(index)"> class="menu-item ss-flex"
<view class="menu-title ss-line-1"> v-for="(item, index) in state.categoryList"
{{ item.name }} :key="item.id"
</view> :class="[{ 'menu-item-active': index === state.activeMenu }]"
</view> @tap="onMenu(index)"
</scroll-view> >
<view class="menu-title ss-line-1">
{{ item.name }}
</view>
</view>
</scroll-view>
<!-- 商品分类 --> <!-- 商品分类 -->
<scroll-view class="goods-list-box" scroll-y :style="[{ height: pageHeight + 'px' }]" <scroll-view
v-if="state.categoryList?.children?.length"> class="goods-list-box"
<image v-if="state.categoryList.children[state.activeMenu].image" class="banner-img" scroll-y
:src="sheep.$url.cdn(state.categoryList.children[state.activeMenu].image)" mode="widthFix"> :style="[{ height: pageHeight + 'px' }]"
</image> v-if="state.categoryList?.length"
<first-one v-if="state.categoryList.style === 'first_one'" :data="state.categoryList" >
:activeMenu="state.activeMenu" :pagination="state.pagination" /> <image
<first-two v-if="state.categoryList.style === 'first_two'" :data="state.categoryList" v-if="state.categoryList[state.activeMenu].picUrl"
:activeMenu="state.activeMenu" :pagination="state.pagination" /> class="banner-img"
<second-one v-if="state.categoryList.style === 'second_one'" :data="state.categoryList" :src="sheep.$url.cdn(state.categoryList[state.activeMenu].picUrl)"
:activeMenu="state.activeMenu" :pagination="state.pagination" /> mode="widthFix"
<uni-load-more v-if=" />
(state.categoryList.style === 'first_one' || <first-one v-if="state.style === 'first_one'" :pagination="state.pagination" />
state.categoryList.style === 'first_two') && <first-two v-if="state.style === 'first_two'" :pagination="state.pagination" />
<second-one
v-if="state.style === 'second_one'"
:data="state.categoryList"
:activeMenu="state.activeMenu"
/>
<uni-load-more
v-if="
(state.style === 'first_one' || state.style === 'first_two') &&
state.pagination.total > 0 state.pagination.total > 0
" :status="state.loadStatus" :content-text="{ "
:status="state.loadStatus"
:content-text="{
contentdown: '点击查看更多', contentdown: '点击查看更多',
}" @tap="loadmore" /> }"
</scroll-view> @tap="loadMore"
</view> />
</view> </scroll-view>
</s-layout> </view>
</view>
</s-layout>
</template> </template>
<script setup> <script setup>
import secondOne from './components/second-one.vue'; import secondOne from './components/second-one.vue';
import firstOne from './components/first-one.vue'; import firstOne from './components/first-one.vue';
import firstTwo from './components/first-two.vue'; import firstTwo from './components/first-two.vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import CategoryApi from '@/sheep/api/product/category';
import { import SpuApi from '@/sheep/api/product/spu';
onLoad, import { onLoad, onReachBottom } from '@dcloudio/uni-app';
onReachBottom import { computed, reactive } from 'vue';
} from '@dcloudio/uni-app'; import _ from 'lodash';
import {
computed,
reactive
} from 'vue';
import _ from 'lodash';
import { handleTree } from '@/sheep/util'; import { handleTree } from '@/sheep/util';
const state = reactive({
const state = reactive({
style: 'second_one', // first_one - , first_two - , second_one style: 'second_one', // first_one - , first_two - , second_one
categoryList: [], // categoryList: [], //
activeMenu: '0', activeMenu: 0, // categoryList
pagination: { pagination: {
data: [], //
current_page: 1, list: [], //
total: 1, total: [], //
last_page: 1, pageNo: 1,
}, pageSize: 6,
loadStatus: '', },
}); loadStatus: '',
});
const { safeArea } = sheep.$platform.device; const { safeArea } = sheep.$platform.device;
const pageHeight = computed(() => safeArea.height - 44 - 50); const pageHeight = computed(() => safeArea.height - 44 - 50);
async function getList(options) { //
const { async function getList() {
code, const { code, data } = await CategoryApi.getCategoryList();
data if (code !== 0) {
} = await sheep.$api.category.list({ return;
id: options.id, }
}); state.categoryList = handleTree(data);
if (code === 0) { }
state.categoryList = {
children: handleTree(data),
style: 'second_one'
};
}
}
const onMenu = (val) => { //
state.activeMenu = val; const onMenu = (val) => {
if (state.categoryList.style === 'first_one' || state.categoryList.style === 'first_two') { state.activeMenu = val;
state.pagination = { if (state.style === 'first_one' || state.style === 'first_two') {
data: [], state.pagination.pageNo = 1;
current_page: 1, state.pagination.list = [];
total: 1, state.pagination.total = 0;
last_page: 1, getGoodsList();
}; }
getGoodsList(state.categoryList.children[val].id); };
}
//
// getGoodsList(state.categoryList.children[val].id);
};
async function getGoodsList(id, page = 1, list_rows = 6) { //
state.loadStatus = 'loading'; async function getGoodsList() {
const res = await sheep.$api.goods.list({ //
categoryId: id, state.loadStatus = 'loading';
pageSize: list_rows, const res = await SpuApi.getSpuPage({
pageNo: page, categoryId: state.categoryList[state.activeMenu].id,
}); pageNo: state.pagination.pageNo,
if (res.code === 0) { pageSize: state.pagination.pageSize,
let couponList = _.concat(state.pagination.data, res.data.list); });
state.pagination = { if (res.code !== 0) {
...res.data, return;
data: couponList, }
}; //
console.log(state.pagination) state.pagination.list = _.concat(state.pagination.list, res.data.list);
if (state.pagination.current_page < state.pagination.last_page) { state.pagination.total = res.data.total;
state.loadStatus = 'more'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} else { }
state.loadStatus = 'noMore';
} //
} function loadMore() {
} if (state.loadStatus === 'noMore') {
// return;
function loadmore() { }
if (state.loadStatus !== 'noMore') { state.pagination.pageNo++;
getGoodsList( getGoodsList();
state.categoryList.children[state.activeMenu].id, }
state.pagination.current_page + 1,
); onLoad(async () => {
} await getList();
} // first
onLoad(async (options) => { if (state.style === 'first_one' || state.style === 'first_two') {
await getList(options); onMenu(0);
if (state.categoryList.style === 'first_one' || state.categoryList.style === 'first_two') { }
getGoodsList(state.categoryList.children[0].id); });
}
}); onReachBottom(() => {
onReachBottom(() => { loadMore();
loadmore(); });
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.s-category { .s-category {
:deep() { :deep() {
.side-menu-wrap { .side-menu-wrap {
width: 200rpx; width: 200rpx;
height: 100%; height: 100%;
padding-left: 12rpx; padding-left: 12rpx;
background-color: #f6f6f6; background-color: #f6f6f6;
.menu-item { .menu-item {
width: 100%; width: 100%;
height: 88rpx; height: 88rpx;
position: relative; position: relative;
transition: all linear 0.2s; transition: all linear 0.2s;
.menu-title { .menu-title {
line-height: 32rpx; line-height: 32rpx;
font-size: 30rpx; font-size: 30rpx;
font-weight: 400; font-weight: 400;
color: #333; color: #333;
margin-left: 28rpx; margin-left: 28rpx;
position: relative; position: relative;
z-index: 0; z-index: 0;
&::before { &::before {
content: ''; content: '';
width: 64rpx; width: 64rpx;
height: 12rpx; height: 12rpx;
background: linear-gradient(90deg, background: linear-gradient(
var(--ui-BG-Main-gradient), 90deg,
var(--ui-BG-Main-light)) !important; var(--ui-BG-Main-gradient),
position: absolute; var(--ui-BG-Main-light)
left: -64rpx; ) !important;
bottom: 0; position: absolute;
z-index: -1; left: -64rpx;
transition: all linear 0.2s; bottom: 0;
} z-index: -1;
} transition: all linear 0.2s;
}
}
&.menu-item-active { &.menu-item-active {
background-color: #fff; background-color: #fff;
border-radius: 20rpx 0 0 20rpx; border-radius: 20rpx 0 0 20rpx;
&::before { &::before {
content: ''; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
bottom: -20rpx; bottom: -20rpx;
width: 20rpx; width: 20rpx;
height: 20rpx; height: 20rpx;
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0); background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
} }
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
top: -20rpx; top: -20rpx;
right: 0; right: 0;
width: 20rpx; width: 20rpx;
height: 20rpx; height: 20rpx;
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0); background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
} }
.menu-title { .menu-title {
font-weight: 600; font-weight: 600;
&::before { &::before {
left: 0; left: 0;
} }
} }
} }
} }
} }
.goods-list-box { .goods-list-box {
background-color: #fff; background-color: #fff;
width: calc(100vw - 100px); width: calc(100vw - 100px);
padding: 10px; padding: 10px;
} }
.banner-img { .banner-img {
width: calc(100vw - 130px); width: calc(100vw - 130px);
border-radius: 5px; border-radius: 5px;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
} }
} }
</style> </style>

View File

@ -1,23 +1,20 @@
<!-- 分类展示first-one 风格 -->
<template> <template>
<view class="ss-flex-col"> <view class="ss-flex-col">
<view class="goods-box" v-for="item in pagination.data" :key="item.id"> <view class="goods-box" v-for="item in pagination.list" :key="item.id">
<s-goods-column <s-goods-column
size="sl" size="sl"
:data="item" :data="item"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
></s-goods-column> />
</view> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
const props = defineProps({ const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
activeMenu: [Number, String],
pagination: Object, pagination: Object,
}); });
</script> </script>

View File

@ -1,15 +1,15 @@
<!-- 页面 --> <!-- 分类展示first-two 风格 -->
<template> <template>
<view> <view>
<view class="ss-flex flex-wrap"> <view class="ss-flex flex-wrap">
<view class="goods-box" v-for="item in pagination?.data" :key="item.id"> <view class="goods-box" v-for="item in pagination?.list" :key="item.id">
<view @click="sheep.$router.go('/pages/goods/index', { id: item.id })"> <view @click="sheep.$router.go('/pages/goods/index', { id: item.id })">
<view class="goods-img"> <view class="goods-img">
<image class="goods-img" :src="sheep.$url.cdn(item.image)" mode="aspectFit"></image> <image class="goods-img" :src="item.picUrl" mode="aspectFit" />
</view> </view>
<view class="goods-content"> <view class="goods-content">
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.title }}</view> <view class="goods-title ss-line-1 ss-m-b-28">{{ item.title }}</view>
<view class="goods-price">{{ item.price[0] }}</view> <view class="goods-price">{{ fen2yuan(item.price) }}</view>
</view> </view>
</view> </view>
</view> </view>
@ -19,12 +19,9 @@
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { fen2yuan } from '@/sheep/hooks/useGoods';
const props = defineProps({ const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
activeMenu: [Number, String],
pagination: Object, pagination: Object,
}); });
</script> </script>

View File

@ -4,14 +4,14 @@
<!-- 一级分类的名字 --> <!-- 一级分类的名字 -->
<view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30"> <view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30">
<view class="title-line-left" /> <view class="title-line-left" />
<view class="title-text ss-p-x-20">{{ props.data.children[activeMenu].name }}</view> <view class="title-text ss-p-x-20">{{ props.data[activeMenu].name }}</view>
<view class="title-line-right" /> <view class="title-line-right" />
</view> </view>
<!-- 二级分类的名字 --> <!-- 二级分类的名字 -->
<view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20"> <view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20">
<view <view
class="goods-item" class="goods-item"
v-for="item in props.data.children[activeMenu].children" v-for="item in props.data[activeMenu].children"
:key="item.id" :key="item.id"
@tap=" @tap="
sheep.$router.go('/pages/goods/list', { sheep.$router.go('/pages/goods/list', {
@ -36,7 +36,7 @@
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
activeMenu: [Number, String] activeMenu: [Number, String],
}); });
</script> </script>

View File

@ -0,0 +1,12 @@
import request from '@/sheep/request';
const CategoryApi = {
// 查询分类列表
getCategoryList: () => {
return request({
url: '/app-api/product/category/list',
method: 'GET'
});
}
};
export default CategoryApi;

View File

@ -53,8 +53,6 @@ export const convertToInteger = (num) => {
return Math.round(parsedNumber * 100) return Math.round(parsedNumber * 100)
} }
/** /**
* 时间日期转换 * 时间日期转换
* @param {dayjs.ConfigType} date 当前时间new Date() 格式 * @param {dayjs.ConfigType} date 当前时间new Date() 格式
@ -77,3 +75,28 @@ export function formatDate(date, format) {
} }
return dayjs(date).format(format) return dayjs(date).format(format)
} }
/**
* 构造树型结构数据
*
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
* @param {*} rootId 根Id 默认 0
*/
export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data))
// 循环所有项
const treeData = cloneData.filter(father => {
let branchArr = cloneData.filter(child => {
//返回每一项的子级数组
return father[id] === child[parentId]
});
branchArr.length > 0 ? father.children = branchArr : '';
//返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
}