Prechádzať zdrojové kódy

新增:商品分类模板(分类+商品)

萤火科技 4 rokov pred
rodič
commit
eecc0811d9

+ 10 - 0
common/enum/goods/SpecType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:商品规格类型
+ * SpecTypeEnum
+ */
+export default new Enum([
+  { key: 'SINGLE', name: '单规格', value: 10 },
+  { key: 'MULTI', name: '多规格', value: 20 }
+])

+ 3 - 0
common/enum/goods/index.js

@@ -0,0 +1,3 @@
+import SpecTypeEnum from './SpecType'
+
+export { SpecTypeEnum }

+ 2 - 1
common/enum/store/page/category/Style.js

@@ -7,5 +7,6 @@ import Enum from '../../../enum'
 export default new Enum([
   { key: 'ONE_LEVEL_BIG', name: '一级分类[大图]', value: 10 },
   { key: 'ONE_LEVEL_SMALL', name: '一级分类[小图]', value: 11 },
-  { key: 'TWO_LEVEL', name: '二级分类', value: 20 }
+  { key: 'TWO_LEVEL', name: '二级分类', value: 20 },
+  { key: 'COMMODITY', name: '一级分类+商品', value: 30 }
 ])

+ 36 - 0
components/add-cart-btn/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <view class="add-cart" @click.stop="handleAddCart">
+    <text class="icon iconfont" :class="[`icon-jiagou${btnStyle}`]"></text>
+  </view>
+</template>
+
+<script>
+  export default {
+    props: {
+      // 购物车按钮样式 1 2 3
+      btnStyle: {
+        type: Number,
+        default: 1
+      },
+    },
+    data() {
+      return {
+        value: false,
+        goodsInfo: {}
+      }
+    },
+    methods: {
+      handleAddCart() {
+        this.$emit('click')
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .add-cart {
+    font-size: 38rpx;
+    padding: 0 20rpx;
+    color: #ff2e34;
+  }
+</style>

+ 173 - 0
components/add-cart-popup/index.vue

@@ -0,0 +1,173 @@
+<template>
+  <goods-sku-popup :value="visible" @input="onChangeValue" border-radius="20" :localdata="localdata" :mode="2" :maskCloseAble="true"
+    @add-cart="addCart" @buy-now="buyNow" buyNowText="立即购买" />
+</template>
+
+<script>
+  import { setCartTotalNum } from '@/core/app'
+  import { SpecTypeEnum } from '@/common/enum/goods'
+  import * as CartApi from '@/api/cart'
+  import * as GoodsApi from '@/api/goods'
+  import GoodsSkuPopup from '@/components/goods-sku-popup'
+
+  export default {
+    components: {
+      GoodsSkuPopup
+    },
+    props: {
+      // 购物车按钮样式 1 2 3
+      btnStyle: {
+        type: Number,
+        default: 1
+      },
+    },
+    data() {
+      return {
+        // 是否显示
+        visible: false,
+        // 主商品信息
+        goods: {},
+        // SKU商品信息
+        localdata: {}
+      }
+    },
+    methods: {
+
+      // 加入购物车事件
+      async handle(goods) {
+        this.goods = goods
+        if (goods.spec_type == SpecTypeEnum.SINGLE.value) {
+          this.singleEvent()
+        }
+        if (goods.spec_type == SpecTypeEnum.MULTI.value) {
+          this.multiEvent()
+        }
+      },
+
+      // 单规格商品事件
+      singleEvent() {
+        const { goods } = this
+        this.addCart({
+          goods_id: goods.goods_id,
+          goods_sku_id: '0',
+          buy_num: 1
+        })
+      },
+
+      // 多规格商品事件
+      async multiEvent() {
+        const app = this
+        const { goods } = app
+        // 获取商品的规格信息
+        const { data: { specData } } = await GoodsApi.specData(goods.goods_id)
+        goods.skuList = specData.skuList
+        goods.specList = specData.specList
+        // 整理SKU商品信息
+        app.localdata = {
+          _id: goods.goods_id,
+          name: goods.goods_name,
+          goods_thumb: goods.goods_image,
+          sku_list: app.getSkuList(),
+          spec_list: app.getSpecList()
+        }
+        this.visible = true
+      },
+
+      // 监听组件显示隐藏
+      onChangeValue(val) {
+        this.visible = val
+      },
+
+      // 整理商品SKU列表 (多规格)
+      getSkuList() {
+        const app = this
+        const { goods: { goods_name, goods_image, skuList } } = app
+        const skuData = []
+        skuList.forEach(item => {
+          skuData.push({
+            _id: item.id,
+            goods_sku_id: item.goods_sku_id,
+            goods_id: item.goods_id,
+            goods_name: goods_name,
+            image: item.image_url ? item.image_url : goods_image,
+            price: item.goods_price * 100,
+            stock: item.stock_num,
+            spec_value_ids: item.spec_value_ids,
+            sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
+          })
+        })
+        return skuData
+      },
+
+      // 获取sku记录的规格值列表
+      getSkuNameArr(specValueIds) {
+        const app = this
+        const defaultData = ['默认']
+        const skuNameArr = []
+        if (specValueIds) {
+          specValueIds.forEach((valueId, groupIndex) => {
+            const specValueName = app.getSpecValueName(valueId, groupIndex)
+            skuNameArr.push(specValueName)
+          })
+        }
+        return skuNameArr.length ? skuNameArr : defaultData
+      },
+
+      // 获取指定的规格值名称
+      getSpecValueName(valueId, groupIndex) {
+        const app = this
+        const { goods: { specList } } = app
+        const res = specList[groupIndex].valueList.find(specValue => {
+          return specValue.spec_value_id == valueId
+        })
+        return res.spec_value
+      },
+
+      // 整理规格数据 (多规格)
+      getSpecList() {
+        const { goods: { specList } } = this
+        const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
+        const specData = []
+        specList.forEach(group => {
+          const children = []
+          group.valueList.forEach(specValue => {
+            children.push({ name: specValue.spec_value })
+          })
+          specData.push({
+            name: group.spec_name,
+            list: children
+          })
+        })
+        return specData.length ? specData : defaultData
+      },
+
+      // 加入购物车按钮
+      addCart(selectShop) {
+        const app = this
+        const { goods_id, goods_sku_id, buy_num } = selectShop
+        CartApi.add(goods_id, goods_sku_id, buy_num)
+          .then(result => {
+            // 显示成功
+            app.$toast(result.message, 1000, false)
+            // 隐藏当前弹窗
+            app.onChangeValue(false)
+            // 购物车商品总数量
+            const cartTotal = result.data.cartTotal
+            // 缓存购物车数量
+            setCartTotalNum(cartTotal)
+            // 传递给父级
+            app.$emit('addCart', cartTotal)
+          })
+      }
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .add-cart {
+    font-size: 38rpx;
+    padding: 0 20rpx;
+    color: #ff2e34;
+  }
+</style>

+ 301 - 0
pages/category/components/commodity.vue

@@ -0,0 +1,301 @@
+<template>
+  <view class="container">
+    <!-- 一级分类 -->
+    <scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
+      <text class="type-nav" :class="{ selected: curIndex == -1 }" @click="handleSelectNav(-1)">全部</text>
+      <text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index"
+        @click="handleSelectNav(index)">{{ item.name }}</text>
+    </scroll-view>
+
+    <mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" :bottombar="false" @up="upCallback">
+      <view class="cate-content">
+        <!-- 商品列表 -->
+        <view class="goods-list">
+          <view class="goods-item--container" v-for="(item, index) in goodsList.data" :key="index">
+            <view class="goods-item" @click="onTargetGoods(item.goods_id)">
+              <!-- 商品图片 -->
+              <view class="goods-item_left">
+                <image class="image" :src="item.goods_image"></image>
+              </view>
+              <view class="goods-item_right">
+                <!-- 商品标题 -->
+                <view class="goods-name">
+                  <text class="twoline-hide">{{ item.goods_name }}</text>
+                </view>
+                <!-- 商品信息 -->
+                <view class="goods-item_desc">
+                  <view class="desc_footer">
+                    <view class="item-prices oneline-hide">
+                      <text class="price_x">¥{{ item.goods_price_min }}</text>
+                      <text v-if="item.line_price_min > 0" class="price_y">¥{{ item.line_price_min }}</text>
+                    </view>
+                    <add-cart-btn v-if="setting.showAddCart" :btnStyle="setting.cartStyle" @click="handleAddCart(item)" />
+                  </view>
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+        <!-- 加入购物车组件 -->
+        <AddCartPopup ref="AddCartPopup" @addCart="onUpdateCartTabBadge" />
+      </view>
+    </mescroll-body>
+  </view>
+</template>
+
+<script>
+  import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
+  import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
+  import { getEmptyPaginateObj, getMoreListData, setCartTabBadge } from '@/core/app'
+  import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
+  import Empty from '@/components/empty'
+  import AddCartBtn from '@/components/add-cart-btn'
+  import AddCartPopup from '@/components/add-cart-popup'
+  import { rpx2px } from '@/utils/util'
+  import * as GoodsApi from '@/api/goods'
+
+  const pageSize = 15
+
+  export default {
+    components: {
+      MescrollBody,
+      Empty,
+      AddCartBtn,
+      AddCartPopup
+    },
+    mixins: [MescrollMixin],
+    props: {
+      // 分类列表
+      list: {
+        type: Array,
+        default: []
+      },
+      // 分类设置
+      setting: {
+        type: Object,
+        default: () => {}
+      },
+    },
+    data() {
+      return {
+        // 枚举类
+        PageCategoryStyleEnum,
+        // 列表高度
+        scrollHeight: 0,
+        // 一级分类:指针
+        curIndex: -1,
+        // 商品列表
+        goodsList: getEmptyPaginateObj(),
+        // 上拉加载配置
+        upOption: {
+          // 首次自动执行
+          auto: true,
+          // 每页数据的数量; 默认10
+          page: { size: pageSize },
+          // 数量要大于3条才显示无更多数据
+          noMoreSize: 3,
+          // 返回顶部
+          toTop: { right: 30, bottom: 48, zIndex: 9 }
+        }
+      }
+    },
+    created() {
+      // 设置分类列表高度
+      this.setListHeight()
+    },
+    methods: {
+
+      /**
+       * 上拉加载的回调 (页面初始化时也会执行一次)
+       * 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
+       * @param {Object} page
+       */
+      upCallback(page) {
+        const app = this
+        // 设置列表数据
+        app.getGoodsList(page.num)
+          .then(list => {
+            const curPageLen = list.data.length
+            const totalSize = list.data.total
+            app.mescroll.endBySize(curPageLen, totalSize)
+          })
+          .catch(() => app.mescroll.endErr())
+      },
+
+      /**
+       * 获取商品列表
+       * @param {Number} pageNo 页码
+       */
+      getGoodsList(pageNo = 1) {
+        const app = this
+        const categoryId = app.curIndex > -1 ? app.list[app.curIndex].category_id : 0
+        return new Promise((resolve, reject) => {
+          GoodsApi.list({ categoryId, page: pageNo }, { load: false })
+            .then(result => {
+              const newList = result.data.list
+              app.goodsList.data = getMoreListData(newList, app.goodsList, pageNo)
+              app.goodsList.last_page = newList.last_page
+              resolve(newList)
+            })
+            .catch(reject)
+        })
+      },
+
+      // 设置列表内容的高度
+      setListHeight() {
+        const { windowHeight } = uni.getSystemInfoSync()
+        this.scrollHeight = windowHeight - rpx2px(96)
+      },
+
+      // 一级分类:选中分类
+      handleSelectNav(index) {
+        this.curIndex = index
+        this.onRefreshList()
+      },
+
+      // 刷新列表数据
+      onRefreshList() {
+        this.goodsList = getEmptyPaginateObj()
+        setTimeout(() => this.mescroll.resetUpScroll(), 120)
+      },
+
+      // 跳转至商品列表页
+      onTargetGoods(goodsId) {
+        this.$navTo('pages/goods/detail', { goodsId })
+      },
+
+      // 点击加入购物车
+      handleAddCart(item) {
+        this.$refs.AddCartPopup.handle(item)
+      },
+
+      // 更新购物车角标
+      onUpdateCartTabBadge() {
+        console.log('onUpdateCartTabBadge')
+        setCartTabBadge()
+      },
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    padding-left: 173rpx;
+  }
+
+  // 分类内容
+  .cate-content {
+    z-index: 1;
+    background: #fff;
+    padding-top: 96rpx;
+  }
+
+  // 一级分类+二级分类 20
+  .cate-left {
+    width: 173rpx;
+    height: 100%;
+    background: #f8f8f8;
+    color: #444;
+
+    position: fixed;
+    top: calc(96rpx + var(--window-top));
+    left: var(--window-left);
+    bottom: var(--window-bottom);
+  }
+
+  // 左侧一级分类
+  .type-nav {
+    position: relative;
+    height: 90rpx;
+    z-index: 10;
+    display: block;
+    font-size: 26rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &.selected {
+      background: #fff;
+      color: #fa2209;
+      border-right: none;
+      font-size: 28rpx;
+    }
+  }
+
+  // 商品列表
+  .goods-list {
+    background: #fff;
+  }
+
+  .goods-item {
+    padding: 28rpx 22rpx;
+    display: flex;
+  }
+
+  .goods-item_left {
+    position: relative;
+    background: #fff;
+    margin-right: 20rpx;
+
+    .image {
+      display: block;
+      width: 180rpx;
+      height: 180rpx;
+    }
+  }
+
+  .goods-item_right {
+    position: relative;
+    flex: 1;
+
+    .goods-name {
+      display: block;
+      width: 100%;
+      min-height: 68rpx;
+      font-size: 28rpx;
+      line-height: 1.3;
+      color: #333;
+    }
+  }
+
+  .goods-item_desc {
+    margin-top: 20rpx;
+
+    .people {
+      margin-right: 14rpx;
+    }
+
+    .desc_footer {
+      width: 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      position: absolute;
+      right: 0rpx;
+      bottom: 0rpx;
+      min-height: 44rpx;
+
+      .item-status {
+        color: #ff1d1c;
+      }
+
+      .item-prices {
+        padding-right: 6rpx;
+
+        .price_x {
+          margin-right: 14rpx;
+          color: rgb(240, 60, 60);
+          font-size: 28rpx;
+        }
+
+        .price_y {
+          color: #999;
+          text-decoration: line-through;
+          font-size: 24rpx;
+        }
+      }
+
+    }
+  }
+</style>

+ 110 - 0
pages/category/components/primary.vue

@@ -0,0 +1,110 @@
+<template>
+  <view class="primary">
+    <!-- 一级分类(大图) 10 -->
+    <view v-if="list.length > 0 && display == PageCategoryStyleEnum.ONE_LEVEL_BIG.value" class="cate-content">
+      <view class="cate-wrapper cate_style__10">
+        <view class="cate-item" v-for="(item, index) in list" :key="index" @click="onTargetGoodsList(item.category_id)">
+          <image v-if="item.image" class="image" mode="widthFix" :src="item.image.preview_url"></image>
+        </view>
+      </view>
+    </view>
+    <!-- 一级分类(小图) 11 -->
+    <view v-if="list.length > 0 && display == PageCategoryStyleEnum.ONE_LEVEL_SMALL.value" class="cate-content">
+      <view class="cate-wrapper cate_style__11">
+        <view class="cate-item" v-for="(item, index) in list" :key="index" @click="onTargetGoodsList(item.category_id)">
+          <image v-if="item.image" class="image" mode="widthFix" :src="item.image.preview_url"></image>
+          <view class="cate-name">{{ item.name }}</view>
+        </view>
+      </view>
+    </view>
+    <empty v-if="!list.length" :tips="'亲,暂无商品分类' + display" />
+  </view>
+</template>
+
+<script>
+  import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
+  import Empty from '@/components/empty'
+
+  export default {
+    components: {
+      Empty
+    },
+    props: {
+      // 分类页样式
+      display: {
+        type: Number,
+        default: 10
+      },
+      // 分类列表
+      list: {
+        type: Array,
+        default: []
+      }
+    },
+    data() {
+      return {
+        // 枚举类
+        PageCategoryStyleEnum,
+      }
+    },
+
+    methods: {
+
+      // 跳转至商品列表页
+      onTargetGoodsList(categoryId) {
+        this.$navTo('pages/goods/list', { categoryId })
+      }
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  // 分类内容
+  .cate-content {
+    z-index: 1;
+    background: #fff;
+    padding-top: 96rpx;
+
+    .cate-wrapper {
+      padding: 0 20rpx 20rpx 20rpx;
+      box-sizing: border-box;
+    }
+  }
+
+  // 一级分类(大图) 10
+  .cate_style__10 .cate-item {
+    margin-bottom: 20rpx;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    .image {
+      display: block;
+      width: 100%;
+      height: auto;
+    }
+  }
+
+  // 一级分类(小图) 11
+  .cate_style__11 .cate-item {
+    float: left;
+    padding: 25rpx;
+    width: 33.3333%;
+    text-align: center;
+    box-sizing: border-box;
+
+    .image {
+      display: block;
+      width: 100%;
+      height: 33vw;
+      margin-bottom: 12rpx;
+    }
+
+    .cate-name {
+      font-size: 28rpx;
+      color: #555;
+    }
+  }
+</style>

+ 180 - 0
pages/category/components/secondary.vue

@@ -0,0 +1,180 @@
+<template>
+  <view v-if="list.length > 0" class="secondary">
+    <!-- 二级分类 20 -->
+    <view class="cate-content">
+      <!-- 左侧 一级分类 -->
+      <scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
+        <text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index"
+          @click="handleSelectNav(index)">{{ item.name }}</text>
+      </scroll-view>
+      <!-- 右侧 二级分类 -->
+      <scroll-view class="cate-right" :scroll-top="scrollTop" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
+        <view v-if="list[curIndex]" class="cate-right-cont">
+          <view class="cate-two-box">
+            <view class="cate-cont-box">
+              <view class="flex-three" v-for="(item, idx) in list[curIndex].children" :key="idx" @click="onTargetGoodsList(item.category_id)">
+                <view class="cate-img-padding">
+                  <view v-if="item.image" class="cate-img">
+                    <image class="image" mode="scaleToFill" :src="item.image.preview_url"></image>
+                  </view>
+                </view>
+                <text class="name oneline-hide">{{ item.name }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </scroll-view>
+    </view>
+    <empty v-if="!list.length" />
+  </view>
+</template>
+
+<script>
+  import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
+  import Empty from '@/components/empty'
+  import { rpx2px } from '@/utils/util'
+
+  export default {
+    components: {
+      Empty
+    },
+    props: {
+      // 分类列表
+      list: {
+        type: Array,
+        default: []
+      },
+    },
+    data() {
+      return {
+        // 枚举类
+        PageCategoryStyleEnum,
+        // 列表高度
+        scrollHeight: 0,
+        // 一级分类:指针
+        curIndex: 0,
+        // 内容区竖向滚动条位置
+        scrollTop: 0,
+      }
+    },
+    created() {
+      // 设置分类列表高度
+      this.setListHeight()
+    },
+    methods: {
+
+      // 设置列表内容的高度
+      setListHeight() {
+        const { windowHeight } = uni.getSystemInfoSync()
+        this.scrollHeight = windowHeight - rpx2px(96)
+      },
+
+      // 一级分类:选中分类
+      handleSelectNav(index) {
+        this.curIndex = index
+        this.scrollTop = 0
+      },
+
+      // 跳转至商品列表页
+      onTargetGoodsList(categoryId) {
+        this.$navTo('pages/goods/list', { categoryId })
+      }
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  // 分类内容
+  .cate-content {
+    display: flex;
+    z-index: 1;
+    background: #fff;
+    padding-top: 96rpx;
+  }
+
+  // 一级分类+二级分类 20
+  .cate-left {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    flex: 0 0 23%;
+    background: #f8f8f8;
+    color: #444;
+  }
+
+  .cate-right {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    overflow: hidden;
+
+    .cate-right-cont {
+      width: 100%;
+      display: flex;
+      flex-flow: row wrap;
+      align-content: flex-start;
+      padding-top: 15rpx;
+
+      .cate-two-box {
+        width: 100%;
+        padding: 0 10px;
+      }
+    }
+  }
+
+  // 左侧一级分类
+  .type-nav {
+    position: relative;
+    height: 90rpx;
+    z-index: 10;
+    display: block;
+    font-size: 26rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &.selected {
+      background: #fff;
+      color: #fa2209;
+      border-right: none;
+      font-size: 28rpx;
+    }
+  }
+
+  // 右侧二级分类
+  .cate-cont-box {
+    margin-bottom: 30rpx;
+    padding-bottom: 10rpx;
+    background: #fff;
+    overflow: hidden;
+
+    .name {
+      display: block;
+      padding-bottom: 30rpx;
+      text-align: center;
+      font-size: 26rpx;
+      color: #444444;
+    }
+
+    .cate-img-padding {
+      padding: 16rpx 16rpx 4rpx 16rpx;
+    }
+
+    .cate-img {
+      position: relative;
+      width: 100%;
+      padding-top: 100%;
+
+      .image {
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+        border-radius: 10rpx;
+      }
+    }
+
+  }
+</style>

+ 36 - 239
pages/category/index.vue

@@ -1,75 +1,32 @@
 <template>
   <view class="container">
     <!-- 搜索框 -->
-    <search tips="搜索商品" @event="$navTo('pages/search/index')" />
+    <search class="search" tips="搜索商品" @event="$navTo('pages/search/index')" />
 
-    <!-- 分类样式:一级分类(大图) 10 -->
-    <view class="cate-content" v-if="templet.style == 10 && list.length > 0">
-      <view class="cate-wrapper cate_style__10">
-        <scroll-view :scroll-y="true" :style="{ height: `${scrollHeight - 10}px` }">
-          <view class="cate-item" v-for="(item, index) in list" :key="index"
-            @click="onTargetGoodsList(item.category_id)">
-            <image v-if="item.image" class="image" mode="widthFix" :src="item.image.preview_url"></image>
-          </view>
-        </scroll-view>
-      </view>
-    </view>
+    <!-- 一级分类 -->
+    <primary v-if="setting.style == PageCategoryStyleEnum.ONE_LEVEL_BIG.value || setting.style == PageCategoryStyleEnum.ONE_LEVEL_SMALL.value"
+      :display="setting.style" :list="list" />
 
-    <!-- 分类样式:一级分类(小图) 11 -->
-    <view class="cate-content" v-else-if="templet.style == 11 && list.length > 0">
-      <view class="cate-wrapper cate_style__11">
-        <scroll-view class="clear" :scroll-y="true" :style="{ height: `${scrollHeight - 10}px` }">
-          <view class="cate-item" v-for="(item, index) in list" :key="index"
-            @click="onTargetGoodsList(item.category_id)">
-            <image v-if="item.image" class="image" :src="item.image.preview_url"></image>
-            <text class="f-26">{{ item.name }}</text>
-          </view>
-        </scroll-view>
-      </view>
-    </view>
+    <!-- 二级分类 -->
+    <secondary v-if="setting.style == PageCategoryStyleEnum.TWO_LEVEL.value" :list="list" />
 
-    <!-- 分类样式:二级分类 20 -->
-    <view class="cate-content dis-flex" v-else-if="templet.style == 20 && list.length > 0">
-
-      <!-- 左侧 一级分类 -->
-      <scroll-view class="cate-left f-28" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
-        <text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index"
-          @click="handleSelectNav(index)">{{ item.name }}</text>
-      </scroll-view>
-
-      <!-- 右侧 二级分类 -->
-      <scroll-view class="cate-right b-f" :scroll-top="scrollTop" :scroll-y="true"
-        :style="{ height: `${scrollHeight}px` }">
-        <view v-if="list[curIndex]">
-          <view class="cate-right-cont">
-            <view class="cate-two-box">
-              <view class="cate-cont-box">
-                <view class="flex-three" v-for="(item, idx) in list[curIndex].children" :key="idx"
-                  @click="onTargetGoodsList(item.category_id)">
-                  <view class="cate-img-padding">
-                    <view v-if="item.image" class="cate-img">
-                      <image class="image" mode="scaleToFill" :src="item.image.preview_url"></image>
-                    </view>
-                  </view>
-                  <text class="name oneline-hide">{{ item.name }}</text>
-                </view>
-              </view>
-            </view>
-          </view>
-        </view>
-      </scroll-view>
-    </view>
-    <empty v-if="!list.length" :isLoading="isLoading" />
+    <!-- 分类+商品 -->
+    <commodity v-if="setting.style == PageCategoryStyleEnum.COMMODITY.value" ref="mescrollItem" :list="list" :setting="setting" />
   </view>
 </template>
 
 <script>
+  import MescrollCompMixin from '@/components/mescroll-uni/mixins/mescroll-comp'
   import { setCartTabBadge } from '@/core/app'
   import SettingKeyEnum from '@/common/enum/setting/Key'
+  import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
   import SettingModel from '@/common/model/Setting'
   import * as CategoryApi from '@/api/category'
-  import Search from '@/components/search'
   import Empty from '@/components/empty'
+  import Search from '@/components/search'
+  import Primary from './components/primary'
+  import Secondary from './components/secondary'
+  import Commodity from './components/commodity'
 
   // 最后一次刷新时间
   let lastRefreshTime;
@@ -77,20 +34,20 @@
   export default {
     components: {
       Search,
-      Empty
+      Empty,
+      Primary,
+      Secondary,
+      Commodity
     },
+    mixins: [MescrollCompMixin],
     data() {
       return {
-        // 列表高度
-        scrollHeight: 0,
-        // 一级分类:指针
-        curIndex: 0,
-        // 内容区竖向滚动条位置
-        scrollTop: 0,
+        // 枚举类
+        PageCategoryStyleEnum,
         // 分类列表
         list: [],
         // 分类模板设置
-        templet: {},
+        setting: {},
         // 正在加载中
         isLoading: true
       }
@@ -100,11 +57,8 @@
      * 生命周期函数--监听页面加载
      */
     onLoad() {
-      const app = this
-      // 设置分类列表高度
-      app.setListHeight()
       // 加载页面数据
-      app.onRefreshPage()
+      this.onRefreshPage()
     },
 
     /**
@@ -117,7 +71,6 @@
         this.onRefreshPage()
       }
     },
-
     methods: {
 
       // 刷新页面
@@ -130,9 +83,7 @@
         setCartTabBadge()
       },
 
-      /**
-       * 获取页面数据
-       */
+      // 获取页面数据
       getPageData() {
         const app = this
         app.isLoading = true
@@ -145,7 +96,7 @@
           ])
           .then(result => {
             // 初始化分类模板设置
-            app.initTemplet(result[0])
+            app.initSetting(result[0])
             // 初始化分类列表数据
             app.initCategory(result[1])
           })
@@ -156,8 +107,8 @@
        * 初始化分类模板设置
        * @param {Object} result
        */
-      initTemplet(setting) {
-        this.templet = setting[SettingKeyEnum.PAGE_CATEGORY_TEMPLATE.value]
+      initSetting(setting) {
+        this.setting = setting[SettingKeyEnum.PAGE_CATEGORY_TEMPLATE.value]
       },
 
       /**
@@ -165,35 +116,9 @@
        * @param {Object} result
        */
       initCategory(result) {
-        const app = this
-        const data = result.data
-        app.list = data.list
-      },
-
-      /**
-       * 设置分类列表高度
-       */
-      setListHeight() {
-        const app = this
-        uni.getSystemInfo({
-          success(res) {
-            app.scrollHeight = res.windowHeight - 47
-          }
-        })
-      },
-
-      // 一级分类:选中分类
-      handleSelectNav(index) {
-        const app = this
-        app.curIndex = index
-        app.scrollTop = 0
+        this.list = result.data.list
       },
 
-      // 跳转至商品列表页
-      onTargetGoodsList(categoryId) {
-        this.$navTo('pages/goods/list', { categoryId })
-      }
-
     },
 
     /**
@@ -229,141 +154,13 @@
   }
 </style>
 <style lang="scss" scoped>
-  .cate-content {
-    background: #fff;
-  }
-
-  .cate-wrapper {
-    padding: 0 20rpx 20rpx 20rpx;
-    box-sizing: border-box;
-  }
-
-  /* 一级分类(大图) 10 */
-
-  .cate_style__10 .cate-item {
-    margin-bottom: 18rpx;
-  }
-
-  .cate_style__10 .cate-item:last-child {
-    margin-bottom: 0;
-  }
-
-  .cate_style__10 .cate-item image {
-    display: block;
-    width: 100%;
-    height: auto;
-  }
-
-  /* 一级分类(小图) 11 */
-
-  .cate_style__11 .cate-item {
-    float: left;
-    padding: 25rpx;
-    width: 33.3333%;
-    text-align: center;
-    box-sizing: border-box;
-  }
-
-  .cate_style__11 .cate-item .image {
-    display: block;
-    width: 100%;
-    height: 33vw;
-    margin-bottom: 10rpx;
-
-
-
-  }
-
-  .cate_style__11 .cate-item text {
-    display: block;
-    color: #555;
-  }
-
-  /* 二级分类 20 */
-
-  .cate-content {
-    width: 100%;
-  }
-
-  .cate-left {
-    flex-direction: column;
-    display: flex;
-    flex: 0 0 23%;
-    color: #444;
-    height: 100%;
-    background: #f8f8f8;
-  }
-
-  .cate-right {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    overflow: hidden;
-  }
-
-  .cate-right-cont {
-    width: 100%;
-    display: flex;
-    flex-flow: row wrap;
-    align-content: flex-start;
-    padding-top: 15rpx;
-  }
-
-  .type-nav {
-    position: relative;
-    height: 90rpx;
-    z-index: 10;
-    display: block;
-    font-size: 26rpx;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  .type-nav.selected {
-    background: #fff;
-    color: #fa2209;
-    border-right: none;
-    font-size: 28rpx;
-  }
-
-  .cate-cont-box {
-    margin-bottom: 30rpx;
-    padding-bottom: 10rpx;
-    background: #fff;
-    overflow: hidden;
-
-    .name {
-      display: block;
-      padding-bottom: 30rpx;
-      text-align: center;
-      font-size: 26rpx;
-      color: #444444;
-    }
-
-    .cate-img-padding {
-      padding: 16rpx 16rpx 4rpx 16rpx;
-    }
-
-    .cate-img {
-      position: relative;
-      width: 100%;
-      padding-top: 100%;
-
-      .image {
-        width: 100%;
-        height: 100%;
-        position: absolute;
-        top: 0;
-        left: 0;
-        border-radius: 10rpx;
-      }
-    }
-
-  }
-
-  .cate-two-box {
-    width: 100%;
-    padding: 0 10px;
+  // 搜索框
+  .search {
+    position: fixed;
+    top: var(--window-top);
+    left: var(--window-left);
+    right: var(--window-right);
+    z-index: 9;
+    padding-bottom: 20rpx;
   }
 </style>

+ 76 - 16
utils/iconfont.scss

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2282770 */
-  src: url('https://at.alicdn.com/t/font_2282770_2gwytlgwykc.woff2?t=1625804354129') format('woff2'),
-       url('https://at.alicdn.com/t/font_2282770_2gwytlgwykc.woff?t=1625804354129') format('woff'),
-       url('https://at.alicdn.com/t/font_2282770_2gwytlgwykc.ttf?t=1625804354129') format('truetype'),
-       url('https://at.alicdn.com/t/font_2282770_2gwytlgwykc.svg?t=1625804354129#iconfont') format('svg');
+  src: url('https://at.alicdn.com/t/font_2282770_p9nx0bv86i.woff2?t=1649761463203') format('woff2'),
+       url('https://at.alicdn.com/t/font_2282770_p9nx0bv86i.woff?t=1649761463203') format('woff'),
+       url('https://at.alicdn.com/t/font_2282770_p9nx0bv86i.ttf?t=1649761463203') format('truetype'),
+       url('https://at.alicdn.com/t/font_2282770_p9nx0bv86i.svg?t=1649761463203#iconfont') format('svg');
 }
 
 .iconfont {
@@ -13,6 +13,78 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-jiagou3:before {
+  content: "\e69c";
+}
+
+.icon-jiagou2:before {
+  content: "\e602";
+}
+
+.icon-jiagou1:before {
+  content: "\e626";
+}
+
+.icon-youhuiquan:before {
+  content: "\e672";
+}
+
+.icon-fenxiao:before {
+  content: "\e70e";
+}
+
+.icon-kanjia:before {
+  content: "\e735";
+}
+
+.icon-pintuan:before {
+  content: "\e671";
+}
+
+.icon-alipay:before {
+  content: "\e6d3";
+}
+
+.icon-balance-pay:before {
+  content: "\e6d4";
+}
+
+.icon-wechat-pay:before {
+  content: "\e6d5";
+}
+
+.icon-poster:before {
+  content: "\e61a";
+}
+
+.icon-weixin:before {
+  content: "\e60e";
+}
+
+.icon-qzone:before {
+  content: "\e642";
+}
+
+.icon-link:before {
+  content: "\e613";
+}
+
+.icon-weibo:before {
+  content: "\e62a";
+}
+
+.icon-qq:before {
+  content: "\e612";
+}
+
+.icon-timeline:before {
+  content: "\e812";
+}
+
+.icon-fenxiao1:before {
+  content: "\e606";
+}
+
 .icon-delete:before {
   content: "\e668";
 }
@@ -45,10 +117,6 @@
   content: "\e788";
 }
 
-.icon-youhuiquan:before {
-  content: "\e623";
-}
-
 .icon-shouhuodizhi:before {
   content: "\e67e";
 }
@@ -237,14 +305,6 @@
   content: "\e632";
 }
 
-.icon-wxpay:before {
-  content: "\e669";
-}
-
-.icon-qiandai:before {
-  content: "\e656";
-}
-
 .icon-shangcheng:before {
   content: "\e605";
 }