瀏覽代碼

✨ 商品分类:列表

YunaiV 1 年之前
父節點
當前提交
8fd8ff9f70

+ 223 - 218
pages/index/category.vue

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

+ 4 - 7
pages/index/components/first-one.vue

@@ -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>

+ 6 - 9
pages/index/components/first-two.vue

@@ -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>

+ 3 - 3
pages/index/components/second-one.vue

@@ -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>
 
 

+ 12 - 0
sheep/api/product/category.js

@@ -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;

+ 25 - 2
sheep/util/index.js

@@ -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;
+}