浏览代码

【功能】批量修改bug和功能

痴货 11 月之前
父节点
当前提交
aa571f25f9

+ 152 - 43
pages/activity/index.vue

@@ -19,13 +19,9 @@
     <view 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"
-            @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
-            @getHeight="mountMasonry($event, 'left')"
-          >
+          <s-goods-column class="goods-md-box" size="md" :data="item"
+                          @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"> </button>
             </template>
@@ -34,13 +30,9 @@
       </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"
-            :data="item"
-            @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
-            @getHeight="mountMasonry($event, 'right')"
-          >
+          <s-goods-column class="goods-md-box" size="md" :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>
@@ -49,23 +41,27 @@
       </view>
     </view>
 
-    <uni-load-more
-      v-if="state.pagination.total > 0"
-      :status="state.loadStatus"
-      :content-text="{
+    <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
         contentdown: '上拉加载更多',
-      }"
-      @tap="loadMore"
-    />
+      }" @tap="loadMore" />
   </s-layout>
 </template>
 <script setup>
-  import { reactive } from 'vue';
-  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+  import {
+    reactive,
+    toRaw,
+    ref
+  } from 'vue';
+  import {
+    onLoad,
+    onReachBottom
+  } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
   import _ from 'lodash-es';
   import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
-  import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
+  import {
+    formatRewardActivityRule
+  } from '@/sheep/hooks/useGoods';
   import SpuApi from '@/sheep/api/product/spu';
 
   const state = reactive({
@@ -106,32 +102,138 @@
 
   // 加载商品信息
   async function getList() {
+    // state.loadStatus = 'loading';
     // 处理拓展参数
-    const params = {};
+    const params = {}
     if (state.activityInfo.productScope === 2) {
-      params.ids = state.activityInfo.productSpuIds.join(',');
+      // const params = toRaw(state.activityInfo.productScopeValues)
+      // 请求数据
+      const {
+        code,
+        data
+      } = await SpuApi.getSpuListByIds(state.activityInfo.productScopeValues.join(','));
+      if (code !== 0) {
+        return;
+      }
+      // 使用 map 提取每个对象的 id 属性
+      const ids = data.map(item => item.id);
+      // 使用 join 方法将 id 数组连接成一个用逗号分隔的字符串
+      const idsString = ids.join(',');
+      // 获取结算信息
+      settleData.value = await getSettlementByIds(idsString)
+      // 处理获得的数据
+      const ms = enrichDataWithSkus(data, settleData.value)
+      state.pagination.list = ms;
+      // state.pagination.total = data.total;
+      // state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
     } else if (state.activityInfo.productScope === 3) {
-      params.categoryIds = state.activityInfo.productSpuIds.join(',');
-    }
-    // 请求数据
-    state.loadStatus = 'loading';
-    const { code, data } = await SpuApi.getSpuPage({
-      pageNo: state.pagination.pageNo,
-      pageSize: state.pagination.pageSize,
-      ...params,
-    });
-    if (code !== 0) {
-      return;
+      params.categoryIds = state.activityInfo.productScopeValues.join(',');
+      state.loadStatus = 'loading';
+      const {
+        code,
+        data
+      } = await SpuApi.getSpuPage({
+        pageNo: state.pagination.pageNo,
+        pageSize: state.pagination.pageSize,
+        ...params,
+      });
+      if (code !== 0) {
+        return;
+      }
+      // 使用 map 提取每个对象的 id 属性
+      const ids = data.list.map(item => item.id);
+      // 使用 join 方法将 id 数组连接成一个用逗号分隔的字符串
+      const idsString = ids.join(',');
+      // 获取结算信息
+      settleData.value = await getSettlementByIds(idsString)
+      // 处理获得的数据
+      const ms = enrichDataWithSkus(data.list, settleData.value)
+      state.pagination.list = _.concat(state.pagination.list, ms);
+      state.pagination.total = data.total;
+      state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+    } else {
+      state.loadStatus = 'loading';
+      const {
+        code,
+        data
+      } = await SpuApi.getSpuPage({
+        pageNo: state.pagination.pageNo,
+        pageSize: state.pagination.pageSize,
+      });
+      if (code !== 0) {
+        return;
+      }
+      // 使用 map 提取每个对象的 id 属性
+      const ids = data.list.map(item => item.id);
+      // 使用 join 方法将 id 数组连接成一个用逗号分隔的字符串
+      const idsString = ids.join(',');
+      // 获取结算信息
+      settleData.value = await getSettlementByIds(idsString)
+      // 处理获得的数据
+      const ms = enrichDataWithSkus(data.list, settleData.value)
+      state.pagination.list = _.concat(state.pagination.list, ms);
+      state.pagination.total = data.total;
+      state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
     }
-    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();
   }
+  //获取结算信息
+  const settleData = ref()
+  async function getSettlementByIds(ids) {
+    const {
+      data
+    } = await SpuApi.getSettlementProduct(ids);
+    return data;
+  }
+  //计算展示价格的函数
+  function enrichDataWithSkus(data, array) {
+    // 创建一个映射,以 id 为键,存储 data 数组中的对象
+    const dataMap = new Map(data.map(item => [item.id, {
+      ...item
+    }]));
+
+    // 遍历 array 数组
+    array.forEach(item => {
+      // 初始化 discountPrice 和 vipPrice 为 null
+      let discountPrice = null;
+      let vipPrice = null;
+      let foundType4 = false;
+      let foundType6 = false;
+
+      // 遍历 skus 数组,寻找 type 为 4 和 6 的首个条目
+      item.skus.forEach(sku => {
+        if (!foundType4 && sku.type === 4) {
+          discountPrice = sku.price;
+          foundType4 = true;
+        }
+        if (!foundType6 && sku.type === 6) {
+          vipPrice = sku.price;
+          foundType6 = true;
+        }
+
+        // 如果已经找到 type 为 4 和 6 的条目,则不需要继续遍历
+        if (foundType4 && foundType6) {
+          return;
+        }
+      });
+
+      // 更新 dataMap 中对应的对象
+      if (dataMap.has(item.id)) {
+        dataMap.get(item.id).discountPrice = discountPrice;
+        dataMap.get(item.id).vipPrice = vipPrice;
+        dataMap.get(item.id).reward = item.reward;
+      }
+    });
 
+    // 返回更新后的数据数组
+    return Array.from(dataMap.values());
+  }
   // 加载活动信息
   async function getActivity(id) {
-    const { code, data } = await RewardActivityApi.getRewardActivity(id);
+    const {
+      code,
+      data
+    } = await RewardActivityApi.getRewardActivity(id);
     if (code === 0) {
       state.activityInfo = data;
     }
@@ -154,28 +256,32 @@
   onLoad(async (options) => {
     state.activityId = options.activityId;
     await getActivity(state.activityId);
-    await getList(state.activityId);
+    await getList();
   });
 </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;
     }
   }
+
   .tip-box {
     background: #fff0e7;
     padding: 20rpx;
     width: 100%;
     position: relative;
     box-sizing: border-box;
+
     .activity-left-image {
       position: absolute;
       bottom: 0;
@@ -183,6 +289,7 @@
       width: 58rpx;
       height: 36rpx;
     }
+
     .activity-right-image {
       position: absolute;
       top: 0;
@@ -190,12 +297,14 @@
       width: 72rpx;
       height: 50rpx;
     }
+
     .type-text {
       font-size: 26rpx;
       font-weight: 500;
       color: #ff6000;
       line-height: 42rpx;
     }
+
     .tip-content {
       font-size: 26rpx;
       font-weight: 500;
@@ -203,4 +312,4 @@
       line-height: 42rpx;
     }
   }
-</style>
+</style>

+ 94 - 52
pages/activity/seckill/list.vue

@@ -2,10 +2,7 @@
 <template>
   <s-layout navbar="inner" :bgStyle="{ color: 'rgb(245,28,19)' }">
     <!--顶部背景图-->
-    <view
-        class="page-bg"
-        :style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
-    ></view>
+    <view class="page-bg" :style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"></view>
     <!-- 时间段轮播图 -->
     <view class="header" v-if="activeTimeConfig?.sliderPicUrls?.length > 0">
       <swiper indicator-dots="true" autoplay="true" :circular="true" interval="3000" duration="1500"
@@ -26,13 +23,12 @@
       </view>
       <scroll-view class="time-list" :scroll-into-view="activeTimeElId" scroll-x scroll-with-animation>
         <view v-for="(config, index) in timeConfigList" :key="index"
-              :class="['item', { active: activeTimeIndex === index}]"
-              :id="`timeItem${index}`"
-              @tap="handleChangeTimeConfig(index)">
+              :class="['item', { active: activeTimeIndex === index}]" :id="`timeItem${index}`"
+              @tap="handleChangeTimeConfig(index,config.id)">
           <!-- 活动起始时间 -->
           <view class="time">{{ config.startTime }}</view>
           <!-- 活动状态 -->
-          <view class="status">{{ config.status }}</view>
+          <view class="status">{{ config?.status }}</view>
         </view>
       </scroll-view>
     </view>
@@ -57,69 +53,89 @@
       </view>
 
       <!-- 活动列表 -->
-      <scroll-view
-        class="scroll-box"
-        :style="{ height: pageHeight + 'rpx' }"
-        scroll-y="true"
-        :scroll-with-animation="false"
-        :enable-back-to-top="true"
-      >
+      <scroll-view class="scroll-box" :style="{ height: pageHeight + 'rpx' }" scroll-y="true"
+                   :scroll-with-animation="false" :enable-back-to-top="true">
         <view class="goods-box ss-m-b-20" v-for="activity in activityList" :key="activity.id">
-          <s-goods-column
-            size="lg"
-            :data="{ ...activity, price: activity.seckillPrice }"
-            :goodsFields="goodsFields"
-            :seckillTag="true"
-            @click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })"
-          >
+          <s-goods-column size="lg" :data="{ ...activity, price: activity.seckillPrice }"
+                          :goodsFields="goodsFields" :seckillTag="true">
             <!-- 抢购进度 -->
             <template #activity>
-              <view class="limit">限量 <text class="ss-m-l-5">{{ activity.stock}} {{activity.unitName}}</text></view>
+              <view class="limit">限量 <text class="ss-m-l-5">{{ activity.stock}}
+                {{activity.unitName}}</text></view>
               <su-progress :percentage="activity.percent" strokeWidth="10" textInside isAnimate />
             </template>
             <!-- 抢购按钮 -->
             <template #cart>
-              <button :class="['ss-reset-button cart-btn', { disabled: activeTimeConfig.status === TimeStatusEnum.END }]">
-                <span v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START">未开始</span>
-                <span v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED">马上抢</span>
-                <span v-else>已结束</span>
+              <button
+                :class="['ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END}]"
+                v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START">
+                <span>未开始</span>
+              </button>
+              <button
+                :class="['ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END}]"
+                @click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })"
+                v-else-if='activeTimeConfig?.status === TimeStatusEnum.STARTED'>
+                <span>马上抢</span>
+              </button>
+              <button
+                :class="['ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END}]"
+                v-else>
+                <span>已结束</span>
               </button>
             </template>
           </s-goods-column>
         </view>
-        <uni-load-more
-          v-if="activityTotal > 0"
-          :status="loadStatus"
-          :content-text="{
+        <uni-load-more v-if="activityTotal > 0" :status="loadStatus" :content-text="{
             contentdown: '上拉加载更多',
-          }"
-          @tap="loadMore"
-        />
+          }" @tap="loadMore" />
       </scroll-view>
     </view>
   </s-layout>
 </template>
 <script setup>
-  import {reactive, computed, ref, nextTick} from 'vue';
-  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+  import {
+    reactive,
+    computed,
+    ref,
+    nextTick
+  } from 'vue';
+  import {
+    onLoad,
+    onReachBottom
+  } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
-  import { useDurationTime } from '@/sheep/hooks/useGoods';
+  import {
+    useDurationTime
+  } from '@/sheep/hooks/useGoods';
   import SeckillApi from "@/sheep/api/promotion/seckill";
   import dayjs from "dayjs";
-  import {TimeStatusEnum} from "@/sheep/util/const";
+  import {
+    TimeStatusEnum
+  } from "@/sheep/util/const";
 
   // 计算页面高度
-  const { safeAreaInsets, safeArea } = sheep.$platform.device;
+  const {
+    safeAreaInsets,
+    safeArea
+  } = sheep.$platform.device;
   const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
   const pageHeight = (safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sheep.$platform.navbar - 350;
   const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-header.png');
 
   // 商品控件显示的字段(不显示库存、销量。改为显示自定义的进度条)
   const goodsFields = {
-    name: { show: true },
-    introduction: { show: true },
-    price: { show: true },
-    marketPrice: { show: true },
+    name: {
+      show: true
+    },
+    introduction: {
+      show: true
+    },
+    price: {
+      show: true
+    },
+    marketPrice: {
+      show: true
+    },
   };
 
   //#region 时间段
@@ -127,25 +143,32 @@
   const timeConfigList = ref([])
   // 查询时间段
   const getSeckillConfigList = async () => {
-    const { data } = await SeckillApi.getSeckillConfigList()
+    const {
+      data
+    } = await SeckillApi.getSeckillConfigList()
     const now = dayjs();
     const today = now.format('YYYY-MM-DD')
+    const select = ref([])
     // 判断时间段的状态
     data.forEach((config, index) => {
       const startTime = dayjs(`${today} ${config.startTime}`)
       const endTime = dayjs(`${today} ${config.endTime}`)
+      select.value[index] = config.id;
       if (now.isBefore(startTime)) {
         config.status = TimeStatusEnum.WAIT_START;
+        // select.value[index] = config.id;
       } else if (now.isAfter(endTime)) {
         config.status = TimeStatusEnum.END;
+        // select.value[index] = config.id;
       } else {
         config.status = TimeStatusEnum.STARTED;
-        activeTimeIndex.value = index;
+        // select.value[index] = config.id;
+        activeTimeIndex.value = index
       }
     })
     timeConfigList.value = data
     // 默认选中进行中的活动
-    handleChangeTimeConfig(activeTimeIndex.value);
+    handleChangeTimeConfig(activeTimeIndex.value, select.value[activeTimeIndex.value]);
     // 滚动到进行中的时间段
     scrollToTimeConfig(activeTimeIndex.value)
   }
@@ -159,11 +182,12 @@
   // 切换时间段
   const activeTimeIndex = ref(0) // 当前选中的时间段的索引
   const activeTimeConfig = computed(() => timeConfigList.value[activeTimeIndex.value]) // 当前选中的时间段
-  const handleChangeTimeConfig = (index) => {
+  const handleChangeTimeConfig = (index, id) => {
     activeTimeIndex.value = index
 
     // 查询活动列表
     activityPageParams.pageNo = 1
+    activityPageParams.configId = id;
     activityList.value = []
     getActivityList();
   }
@@ -182,7 +206,7 @@
 
   // 查询活动列表
   const activityPageParams = reactive({
-    id: 0, // 时间段 ID
+    configId: 0, // 时间段 ID
     pageNo: 1, // 页码
     pageSize: 5, // 每页数量
   })
@@ -191,7 +215,9 @@
   const loadStatus = ref('') // 页面加载状态
   async function getActivityList() {
     loadStatus.value = 'loading';
-    const { data } = await SeckillApi.getSeckillActivityPage(activityPageParams)
+    const {
+      data
+    } = await SeckillApi.getSeckillActivityPage(activityPageParams)
     data.list.forEach(activity => {
       // 计算抢购进度
       activity.percent = parseInt(100 * (activity.totalStock - activity.stock) / activity.totalStock);
@@ -235,7 +261,8 @@
     margin: -276rpx auto 0 auto;
     border-radius: 14rpx;
     overflow: hidden;
-    swiper{
+
+    swiper {
       height: 330rpx !important;
       border-radius: 14rpx;
       overflow: hidden;
@@ -246,7 +273,8 @@
       height: 100%;
       border-radius: 14rpx;
       overflow: hidden;
-      img{
+
+      img {
         border-radius: 14rpx;
       }
     }
@@ -257,10 +285,12 @@
     width: 75rpx;
     height: 70rpx;
   }
+
   // 时间段列表
   .time-list {
     width: 596rpx;
     white-space: nowrap;
+
     // 时间段
     .item {
       display: inline-block;
@@ -270,17 +300,20 @@
       box-sizing: border-box;
       margin-right: 30rpx;
       width: 130rpx;
+
       // 开始时间
       .time {
         font-size: 36rpx;
         font-weight: 600;
         color: #333;
       }
+
       // 选中的时间段
       &.active {
         .time {
           color: var(--ui-BG-Main);
         }
+
         // 状态
         .status {
           height: 30rpx;
@@ -301,6 +334,7 @@
     margin: 0 20rpx 0 20rpx;
     background: #fff;
     border-radius: 20rpx 20rpx 0 0;
+
     .content-header {
       width: 100%;
       border-radius: 20rpx 20rpx 0 0;
@@ -312,6 +346,7 @@
         height: 64rpx;
         background: rgba($color: #fff, $alpha: 0.66);
         border-radius: 32px;
+
         // 场次倒计时内容
         .countdown-title {
           font-size: 28rpx;
@@ -319,10 +354,12 @@
           color: #333333;
           line-height: 28rpx;
         }
+
         // 场次倒计时
         .countdown-time {
           font-size: 28rpx;
           color: rgba(#ed3c30, 0.23);
+
           // 场次倒计时:小时部分
           .countdown-h {
             font-size: 24rpx;
@@ -334,6 +371,7 @@
             background: rgba(#ed3c30, 0.23);
             border-radius: 6rpx;
           }
+
           // 场次倒计时:分钟、秒
           .countdown-num {
             font-size: 24rpx;
@@ -348,12 +386,15 @@
         }
       }
     }
+
     // 活动列表
     .scroll-box {
       height: 900rpx;
+
       // 活动
       .goods-box {
         position: relative;
+
         // 抢购按钮
         .cart-btn {
           position: absolute;
@@ -373,6 +414,7 @@
             color: #fff;
           }
         }
+
         // 秒杀限量商品数
         .limit {
           font-size: 22rpx;
@@ -382,4 +424,4 @@
       }
     }
   }
-</style>
+</style>

+ 8 - 8
pages/coupon/detail.vue

@@ -36,10 +36,10 @@
               <text v-else>
                 {{
                   state.coupon.status === 1
-                    ? '立即使用'
+                    ? '使用'
                     : state.coupon.status === 2
-                    ? '已使用'
-                    : '已过期'
+                      ? '已使用'
+                      : '已过期'
                 }}
               </text>
             </button>
@@ -282,11 +282,11 @@
 
   .detail-wrap {
     background: linear-gradient(
-      180deg,
-      var(--ui-BG-Main),
-      var(--ui-BG-Main-gradient),
-      var(--ui-BG-Main),
-      #fff
+        180deg,
+        var(--ui-BG-Main),
+        var(--ui-BG-Main-gradient),
+        var(--ui-BG-Main),
+        #fff
     );
   }
 

+ 257 - 118
pages/goods/index.vue

@@ -7,30 +7,47 @@
       <!-- 骨架屏 -->
       <detailSkeleton v-if="state.skeletonLoading" />
       <!-- 下架/售罄提醒 -->
-      <s-empty
-        v-else-if="state.goodsInfo === null"
-        text="商品不存在或已下架"
-        icon="/static/soldout-empty.png"
-        showAction
-        actionText="再逛逛"
-        actionUrl="/pages/goods/list"
-      />
+      <s-empty v-else-if="state.goodsInfo === null" text="商品不存在或已下架" icon="/static/soldout-empty.png" showAction
+               actionText="再逛逛" actionUrl="/pages/goods/list" />
       <block v-else>
         <view class="detail-swiper-selector">
           <!-- 商品轮播图  -->
-          <su-swiper
-            class="ss-m-b-14"
-            isPreview
-            :list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
-            otStyle="tag"
-            imageMode="widthFix"
-            dotCur="bg-mask-40"
-            :seizeHeight="750"
-          />
-
+          <su-swiper class="ss-m-b-14" isPreview :list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
+                     otStyle="tag" imageMode="widthFix" dotCur="bg-mask-40" :seizeHeight="750" />
+          <!-- 限时折扣 -->
+          <view class="discount" v-if="setShow">
+            <image class="disImg" src="../../static/images/dis.png"></image>
+            <view class="discountCont">
+              <view class="disContT">
+                <view class="disContT1">
+                  <view class="disContT1P">
+                    ¥{{fen2yuan(settleData.price)}}
+                  </view>
+                  <view class="disContT1End">
+                    直降¥{{fen2yuan( state.goodsInfo.price - settleData.price)}}
+                  </view>
+                </view>
+                <view class="disContT2">
+                  限时折扣
+                </view>
+              </view>
+              <view class="disContB">
+                <view class="disContB1">
+                  价格:¥{{fen2yuan(state.goodsInfo.price)}} 丨 剩余:{{settleData.stock}}
+                </view>
+                <view class="disContB2">
+                  距结束仅剩
+                  <countDown :tipText="' '" :bgColor="bgColor" :dayText="':'" :hourText="':'"
+                             :minuteText="':'" :secondText="' '" :datatime="settleData.endTime / 1000"
+                             :isDay="false" />
+                </view>
+              </view>
+            </view>
+          </view>
+          <!-- 限时折扣 -->
           <!-- 价格+标题 -->
           <view class="title-card detail-card ss-p-y-40 ss-p-x-20">
-            <view class="ss-flex ss-row-between ss-col-center ss-m-b-26">
+            <view class="ss-flex ss-row-between ss-col-center ss-m-b-26" v-if="!setShow">
               <view class="price-box ss-flex ss-col-bottom">
                 <view class="price-text ss-m-r-16">
                   {{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
@@ -47,23 +64,16 @@
               <!-- 满减送/限时折扣活动的提示 -->
               <div class="tag-content">
                 <view class="tag-box ss-flex">
-                  <view
-                    class="tag ss-m-r-10"
-                    v-for="promos in state.activityInfo"
-                    :key="promos.id"
-                    @tap="onActivity"
-                  >
+                  <view class="tag ss-m-r-10" v-for="promos in state.activityInfo" :key="promos.id"
+                        @tap="onActivity">
                     {{ promos.name }}
                   </view>
                 </view>
               </div>
 
-              <!-- 优惠劵 -->
-              <view
-                class="get-coupon-box ss-flex ss-col-center ss-m-l-20"
-                @tap="state.showModel = true"
-                v-if="state.couponInfo.length"
-              >
+              <!-- 优惠劵@tap="state.showModel = true" -->
+              <view class="get-coupon-box ss-flex ss-col-center ss-m-l-20" @tap="onActivity"
+                    v-if="state.couponInfo.length">
                 <view class="discounts-title ss-m-r-8">领券</view>
                 <text class="cicon-forward"></text>
               </view>
@@ -74,51 +84,30 @@
 
           <!-- 功能卡片 -->
           <view class="detail-cell-card detail-card ss-flex-col">
-            <detail-cell-sku
-              v-model="state.selectedSku.goods_sku_text"
-              :sku="state.selectedSku"
-              @tap="state.showSelectSku = true"
-            />
+            <detail-cell-sku v-model="state.selectedSku.goods_sku_text" :sku="state.selectedSku"
+                             @tap="state.showSelectSku = true" />
           </view>
 
           <!-- 规格与数量弹框 -->
-          <s-select-sku
-            :goodsInfo="state.goodsInfo"
-            :show="state.showSelectSku"
-            @addCart="onAddCart"
-            @buy="onBuy"
-            @change="onSkuChange"
-            @close="state.showSelectSku = false"
-          />
+          <s-select-sku :goodsInfo="state.goodsInfo" :show="state.showSelectSku" @addCart="onAddCart"
+                        @buy="onBuy" @change="onSkuChange" @close="state.showSelectSku = false" />
         </view>
 
         <!-- 评价 -->
         <detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
         <!-- 详情 -->
-        <detail-content-card
-          class="detail-content-selector"
-          :content="state.goodsInfo.description"
-        />
+        <detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
 
         <!-- 活动跳转:拼团/秒杀/砍价活动 -->
-        <detail-activity-tip
-          v-if="state.activityList.length > 0"
-          :activity-list="state.activityList"
-        />
+        <detail-activity-tip v-if="state.activityList.length > 0" :activity-list="state.activityList" />
 
         <!-- 详情 tabbar -->
         <detail-tabbar v-model="state.goodsInfo">
           <view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
-            <button
-              class="ss-reset-button add-btn ui-Shadow-Main"
-              @tap="state.showSelectSku = true"
-            >
+            <button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
               加入购物车
             </button>
-            <button
-              class="ss-reset-button buy-btn ui-Shadow-Main"
-              @tap="state.showSelectSku = true"
-            >
+            <button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
               立即购买
             </button>
           </view>
@@ -128,32 +117,38 @@
         </detail-tabbar>
 
         <!-- 优惠劵弹窗 -->
-        <s-coupon-get
-          v-model="state.couponInfo"
-          :show="state.showModel"
-          @close="state.showModel = false"
-          @get="onGet"
-        />
+        <!-- <s-coupon-get v-model="state.couponInfo" :show="state.showModel" @close="state.showModel = false"
+          @get="onGet" /> -->
 
         <!-- 满减送/限时折扣活动弹窗 -->
-        <s-activity-pop
-          v-model="state.activityInfo"
-          :show="state.showActivityModel"
-          @close="state.showActivityModel = false"
-        />
+        <s-activity-pop v-model="state" :show="state.showActivityModel"
+                        @close="state.showActivityModel = false" @get="onGet" />
       </block>
     </s-layout>
   </view>
 </template>
 
 <script setup>
-  import { reactive, computed } from 'vue';
-  import { onLoad, onPageScroll } from '@dcloudio/uni-app';
+  import {
+    reactive,
+    computed,
+    ref
+  } from 'vue';
+  import {
+    onLoad,
+    onPageScroll
+  } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
   import CouponApi from '@/sheep/api/promotion/coupon';
   import ActivityApi from '@/sheep/api/promotion/activity';
   import FavoriteApi from '@/sheep/api/product/favorite';
-  import { formatSales, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
+  import {
+    formatSales,
+    formatGoodsSwiper,
+    fen2yuan,
+    handList,
+    handListPrice
+  } from '@/sheep/hooks/useGoods';
   import detailNavbar from './components/detail/detail-navbar.vue';
   import detailCellSku from './components/detail/detail-cell-sku.vue';
   import detailTabbar from './components/detail/detail-tabbar.vue';
@@ -161,11 +156,22 @@
   import detailCommentCard from './components/detail/detail-comment-card.vue';
   import detailContentCard from './components/detail/detail-content-card.vue';
   import detailActivityTip from './components/detail/detail-activity-tip.vue';
-  import { isEmpty } from 'lodash-es';
+  import {
+    isEmpty
+  } from 'lodash-es';
   import SpuApi from '@/sheep/api/product/spu';
 
-  onPageScroll(() => {});
-
+  onPageScroll(() => {
+  });
+  import countDown from '@/sheep/components/countDown/index.vue'
+
+  const bgColor = {
+    'bgColor': '#E93323',
+    'Color': '#fff',
+    'width': '44rpx',
+    'timeTxtwidth': '16rpx',
+    'isDay': true
+  }
   const isLogin = computed(() => sheep.$store('user').isLogin);
   const state = reactive({
     goodsId: 0,
@@ -182,7 +188,13 @@
 
   // 规格变更
   function onSkuChange(e) {
-    state.selectedSku = e;
+    if (e.type == 4) {
+      settleData.value = e
+      setShow.value = true
+    } else {
+      state.selectedSku = e;
+      setShow.value = false
+    }
   }
 
   // 添加购物车
@@ -196,21 +208,17 @@
 
   // 立即购买
   function onBuy(e) {
-    if (!state.selectedSku.id) {
+    if (!e.id) {
       sheep.$helper.toast('请选择商品规格');
       return;
     }
     sheep.$router.go('/pages/order/confirm', {
       data: JSON.stringify({
-        items: [
-          {
-            skuId: e.id,
-            count: e.goods_num,
-          },
-        ],
-        // TODO 芋艿:后续清理掉这 2 参数
-        deliveryType: 1,
-        pointStatus: false,
+        items: [{
+          skuId: e.id,
+          count: e.goods_num,
+          categoryId: state.goodsInfo.categoryId
+        }]
       }),
     });
   }
@@ -222,7 +230,9 @@
 
   // 立即领取
   async function onGet(id) {
-    const { code } = await CouponApi.takeCoupon(id);
+    const {
+      code
+    } = await CouponApi.takeCoupon(id);
     if (code !== 0) {
       return;
     }
@@ -237,33 +247,66 @@
   //  TODO 芋艿:待测试
   const shareInfo = computed(() => {
     if (isEmpty(state.goodsInfo)) return {};
-    return sheep.$platform.share.getShareInfo(
-      {
-        title: state.goodsInfo.name,
-        image: sheep.$url.cdn(state.goodsInfo.picUrl),
-        desc: state.goodsInfo.introduction,
-        params: {
-          page: '2',
-          query: state.goodsInfo.id,
-        },
+    return sheep.$platform.share.getShareInfo({
+      title: state.goodsInfo.name,
+      image: sheep.$url.cdn(state.goodsInfo.picUrl),
+      desc: state.goodsInfo.introduction,
+      params: {
+        page: '2',
+        query: state.goodsInfo.id,
       },
-      {
-        type: 'goods', // 商品海报
-        title: state.goodsInfo.name, // 商品名称
-        image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
-        price: fen2yuan(state.goodsInfo.price), // 商品价格
-        original_price: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
-      },
-    );
+    }, {
+      type: 'goods', // 商品海报
+      title: state.goodsInfo.name, // 商品名称
+      image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
+      price: fen2yuan(state.goodsInfo.price), // 商品价格
+      original_price: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
+    });
   });
 
   async function getCoupon() {
-    const { code, data } = await CouponApi.getCouponTemplateList(state.goodsId, 2, 10);
+    const {
+      code,
+      data
+    } = await CouponApi.getCouponTemplateList(state.goodsId, 2, 10);
     if (code === 0) {
       state.couponInfo = data;
     }
   }
 
+  //获取结算信息
+  const setShow = ref(false)
+  const settleData = ref()
+
+  async function getSettlementByIds(ids) {
+    const { data } = await SpuApi.getSettlementProduct(ids);
+    settleData.value = handle(data)
+    state.goodsInfo.skus = handListPrice(state.goodsInfo.skus, data[0].skus)
+  }
+
+  //判断是否有限时折扣信息
+  function handle(array) {
+    let setList = {}
+    array.some(item => {
+      return item.skus.some(items => {
+        if (items.type === 4) {
+          setShow.value = true;
+          setList = items;
+          return true; // 返回true以结束some循环
+        }
+        return false; // 继续遍历
+      });
+    });
+    // 将库存信息加入
+    state.goodsInfo.skus.forEach(item => {
+      if (item.id == setList.skuId) {
+        setList.stock = item.stock
+      }
+    })
+
+    return setList
+  }
+
   onLoad((options) => {
     // 非法参数
     if (!options.id) {
@@ -281,7 +324,6 @@
       // 加载到商品
       state.skeletonLoading = false;
       state.goodsInfo = res.data;
-
       // 加载是否收藏
       if (isLogin.value) {
         FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
@@ -301,19 +343,19 @@
       if (res.code !== 0) {
         return;
       }
-      res.data.forEach((activity) => {
-        if ([1, 2, 3].includes(activity.type)) {
-          // 情况一:拼团/秒杀/砍价
+      let activData = handList(res.data)
+      activData.forEach(activity => {
+        if ([1, 2, 3].includes(activity.type)) { // 情况一:拼团/秒杀/砍价
           state.activityList.push(activity);
-        } else if (activity.type === 5) {
-          // 情况二:满减送
+        } else if (activity.type === 5) { // 情况二:满减送
           state.activityInfo.push(activity);
-        } else {
-          // 情况三:限时折扣 TODO 芋艿
-          console.log('待实现!优先级不高');
+        } else { // 情况三:限时折扣 TODO 芋艿
+          // console.log('待实现!优先级不高');
         }
-      });
+      })
     });
+    //获取结算信息
+    getSettlementByIds(state.goodsId)
   });
 </script>
 
@@ -462,4 +504,101 @@
       color: #333333;
     }
   }
-</style>
+
+  // 限时折扣
+  .discount {
+    width: 750rpx;
+    height: 100rpx;
+    // background-color: red;
+    overflow: hidden;
+    position: relative;
+  }
+
+  .disImg {
+    width: 750rpx;
+    height: 100rpx;
+    position: absolute;
+    top: 0;
+    z-index: -1;
+  }
+
+  .discountCont {
+    width: 680rpx;
+    height: 90rpx;
+    margin: 10rpx auto 0 auto;
+    // background-color: gold;
+  }
+
+  .disContT {
+    width: 680rpx;
+    height: 50rpx;
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .disContT1 {
+    width: 400rpx;
+    height: 50rpx;
+    // background-color: green;
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+  }
+
+  .disContT2 {
+    width: 200rpx;
+    height: 50rpx;
+    line-height: 50rpx;
+    // background-color: gold;
+    font-size: 30rpx;
+    text-align: end;
+    color: white;
+    font-weight: bolder;
+    font-style: oblique 20deg;
+    letter-spacing: .1rem;
+  }
+
+  .disContT1P {
+    color: white;
+    font-weight: bold;
+    font-size: 28rpx;
+  }
+
+  .disContT1End {
+    // width: 180rpx;
+    padding: 0 10rpx;
+    height: 30rpx;
+    line-height: 28rpx;
+    text-align: center;
+    font-weight: bold;
+    background-color: white;
+    color: #ff3000;
+    font-size: 23rpx;
+    border-radius: 20rpx;
+    margin-left: 10rpx;
+  }
+
+  .disContB {
+    width: 680rpx;
+    height: 40rpx;
+    display: flex;
+    justify-content: space-between;
+    font-size: 20rpx;
+    color: white;
+    align-items: center;
+  }
+
+  .disContB1 {
+    width: 300rpx;
+    height: 40rpx;
+    line-height: 40rpx;
+  }
+
+  .disContB2 {
+    width: 300rpx;
+    height: 40rpx;
+    line-height: 40rpx;
+    display: flex;
+    justify-content: flex-end;
+  }
+</style>

+ 59 - 2
pages/goods/list.vue

@@ -118,7 +118,7 @@
 </template>
 
 <script setup>
-  import { reactive } from 'vue';
+  import { reactive,ref } from 'vue';
   import { onLoad, onReachBottom } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
   import _ from 'lodash-es';
@@ -277,7 +277,15 @@
     if (code !== 0) {
       return;
     }
-    state.pagination.list = _.concat(state.pagination.list, data.list);
+    // 使用 map 提取每个对象的 id 属性
+    const ids = data.list.map(item => item.id);
+    // 使用 join 方法将 id 数组连接成一个用逗号分隔的字符串
+    const idsString = ids.join(',');
+    // 获取结算信息
+    settleData.value = await getSettlementByIds(idsString)
+    // 处理获得的数据
+    const ms = enrichDataWithSkus(data.list,settleData.value)
+    state.pagination.list = _.concat(state.pagination.list, ms);
     state.pagination.total = data.total;
     state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
     mountMasonry();
@@ -292,6 +300,55 @@
     getList(state.currentSort, state.currentOrder);
   }
 
+  //获取结算信息
+  const settleData = ref()
+  async function getSettlementByIds(ids) {
+    const { data } = await SpuApi.getSettlementProduct(ids);
+    return data;
+  }
+
+  //计算展示价格的函数
+  function enrichDataWithSkus(data, array) {
+    // 创建一个映射,以 id 为键,存储 data 数组中的对象
+    const dataMap = new Map(data.map(item => [item.id, { ...item }]));
+
+    // 遍历 array 数组
+    array.forEach(item => {
+      // 初始化 discountPrice 和 vipPrice 为 null
+      let discountPrice = null;
+      let vipPrice = null;
+      let foundType4 = false;
+      let foundType6 = false;
+
+      // 遍历 skus 数组,寻找 type 为 4 和 6 的首个条目
+      item.skus.forEach(sku => {
+        if (!foundType4 && sku.type === 4) {
+          discountPrice = sku.price;
+          foundType4 = true;
+        }
+        if (!foundType6 && sku.type === 6) {
+          vipPrice = sku.price;
+          foundType6 = true;
+        }
+
+        // 如果已经找到 type 为 4 和 6 的条目,则不需要继续遍历
+        if (foundType4 && foundType6) {
+          return;
+        }
+      });
+
+      // 更新 dataMap 中对应的对象
+      if (dataMap.has(item.id)) {
+        dataMap.get(item.id).discountPrice = discountPrice;
+        dataMap.get(item.id).vipPrice = vipPrice;
+        dataMap.get(item.id).reward = item.reward;
+      }
+    });
+
+    // 返回更新后的数据数组
+    return Array.from(dataMap.values());
+  }
+
   onLoad((options) => {
     state.categoryId = options.categoryId;
     state.keyword = options.keyword;

+ 72 - 78
pages/goods/seckill.vue

@@ -6,26 +6,13 @@
     <!-- 骨架屏 -->
     <detailSkeleton v-if="state.skeletonLoading" />
     <!-- 下架/售罄提醒 -->
-    <s-empty
-      v-else-if="state.goodsInfo === null || state.goodsInfo.activity_type !== 'seckill'"
-      text="活动不存在或已结束"
-      icon="/static/soldout-empty.png"
-      showAction
-      actionText="再逛逛"
-      actionUrl="/pages/goods/list"
-    />
+    <s-empty v-else-if="state.goodsInfo === null || state.goodsInfo.activity_type !== 'seckill'" text="活动不存在或已结束"
+             icon="/static/soldout-empty.png" showAction actionText="再逛逛" actionUrl="/pages/goods/list" />
     <block v-else>
       <view class="detail-swiper-selector">
         <!-- 商品图轮播 -->
-        <su-swiper
-          class="ss-m-b-14"
-          isPreview
-          :list="state.goodsSwiper"
-          dotStyle="tag"
-          imageMode="widthFix"
-          dotCur="bg-mask-40"
-          :seizeHeight="750"
-        />
+        <su-swiper class="ss-m-b-14" isPreview :list="state.goodsSwiper" dotStyle="tag" imageMode="widthFix"
+                   dotCur="bg-mask-40" :seizeHeight="750" />
 
         <!-- 价格+标题 -->
         <view class="title-card ss-m-y-14 ss-m-x-20 ss-p-x-20 ss-p-y-34">
@@ -63,7 +50,7 @@
             <detail-progress :percent="state.percent" />
           </view>
 
-          <view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo?.name }}</view>
+          <view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name || '' }}</view>
           <view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
         </view>
 
@@ -72,14 +59,9 @@
           <detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
         </view>
         <!-- 规格与数量弹框 -->
-        <s-select-seckill-sku
-          v-model="state.goodsInfo"
-          :show="state.showSelectSku"
-          :single-limit-count="activity.singleLimitCount"
-          @buy="onBuy"
-          @change="onSkuChange"
-          @close="state.showSelectSku = false"
-        />
+        <s-select-seckill-sku v-model="state.goodsInfo" :show="state.showSelectSku"
+                              :single-limit-count="activity.singleLimitCount" @buy="onBuy" @change="onSkuChange"
+                              @close="state.showSelectSku = false" />
       </view>
 
       <!-- 评价 -->
@@ -91,36 +73,25 @@
       <detail-tabbar v-model="state.goodsInfo">
         <!-- TODO: 缺货中 已售罄 判断 设计-->
         <view class="buy-box ss-flex ss-col-center ss-p-r-20">
-          <button
-            class="ss-reset-button origin-price-btn ss-flex-col"
-            v-if="state.goodsInfo.marketPrice"
-            @tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })"
-          >
+          <button class="ss-reset-button origin-price-btn ss-flex-col" v-if="state.goodsInfo.marketPrice"
+                  @tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })">
             <view>
               <view class="btn-price">{{ fen2yuan(state.goodsInfo.marketPrice) }}</view>
               <view>原价购买</view>
             </view>
           </button>
           <button v-else class="ss-reset-button origin-price-btn ss-flex-col">
-            <view
-              class="no-original"
-              :class="
+            <view class="no-original" :class="
                 state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED ? '' : ''
-              "
-            >
+              ">
               秒杀价
             </view>
           </button>
-          <button
-            class="ss-reset-button btn-box ss-flex-col"
-            @tap="state.showSelectSku = true"
-            :class="
+          <button class="ss-reset-button btn-box ss-flex-col" @tap="state.showSelectSku = true" :class="
               timeStatusEnum === TimeStatusEnum.STARTED && state.goodsInfo.stock != 0
                 ? 'check-btn-box'
                 : 'disabled-btn-box'
-            "
-            :disabled="state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED"
-          >
+            " :disabled="state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED">
             <view class="btn-price">{{ fen2yuan(state.goodsInfo.price) }}</view>
             <view v-if="timeStatusEnum === TimeStatusEnum.STARTED">
               <view v-if="state.goodsInfo.stock === 0">已售罄</view>
@@ -135,11 +106,26 @@
 </template>
 
 <script setup>
-  import { reactive, computed, ref } from 'vue';
-  import { onLoad, onPageScroll } from '@dcloudio/uni-app';
+  import {
+    reactive,
+    computed,
+    ref,
+    unref
+  } from 'vue';
+  import {
+    onLoad,
+    onPageScroll
+  } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
-  import { isEmpty, min } from 'lodash-es';
-  import { useDurationTime, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
+  import {
+    isEmpty,
+    min
+  } from 'lodash-es';
+  import {
+    useDurationTime,
+    formatGoodsSwiper,
+    fen2yuan
+  } from '@/sheep/hooks/useGoods';
   import detailNavbar from './components/detail/detail-navbar.vue';
   import detailCellSku from './components/detail/detail-cell-sku.vue';
   import detailTabbar from './components/detail/detail-tabbar.vue';
@@ -149,7 +135,10 @@
   import detailProgress from './components/detail/detail-progress.vue';
   import SeckillApi from '@/sheep/api/promotion/seckill';
   import SpuApi from '@/sheep/api/product/spu';
-  import { getTimeStatusEnum, TimeStatusEnum } from '@/sheep/util/const';
+  import {
+    getTimeStatusEnum,
+    TimeStatusEnum
+  } from '@/sheep/util/const';
 
   const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-bg.png');
   const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png');
@@ -186,53 +175,53 @@
         order_type: 'goods',
         buy_type: 'seckill',
         seckillActivityId: activity.value.id,
-        items: [
-          {
-            skuId: sku.id,
-            count: sku.count,
-          },
-        ],
+        items: [{
+          skuId: sku.id,
+          count: sku.count,
+        }, ],
       }),
     });
   }
 
-  // 分享信息 TODO 芋艿:待接入
+  // 分享信息
   const shareInfo = computed(() => {
-    if (isEmpty(activity)) return {};
-    return sheep.$platform.share.getShareInfo(
-      {
-        title: activity.value.name,
-        image: sheep.$url.cdn(state.goodsInfo.picUrl),
-        params: {
-          page: '4',
-          query: activity.value.id,
-        },
+    if (isEmpty(unref(activity))) return {};
+    return sheep.$platform.share.getShareInfo({
+      title: activity.value.name,
+      image: sheep.$url.cdn(state.goodsInfo.picUrl),
+      params: {
+        page: '4',
+        query: activity.value.id,
       },
-      {
-        type: 'goods', // 商品海报
-        title: activity.value.name, // 商品标题
-        image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
-        price: state.goodsInfo.price, // 商品价格
-        marketPrice: state.goodsInfo.marketPrice, // 商品原价
-      },
-    );
+    }, {
+      type: 'goods', // 商品海报
+      title: activity.value.name, // 商品标题
+      image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
+      price: state.goodsInfo.price, // 商品价格
+      marketPrice: state.goodsInfo.marketPrice, // 商品原价
+    }, );
   });
 
   const activity = ref();
   const timeStatusEnum = ref('');
+
   // 查询活动
   const getActivity = async (id) => {
-    const { data } = await SeckillApi.getSeckillActivity(id);
+    const {
+      data
+    } = await SeckillApi.getSeckillActivity(id);
     activity.value = data;
-    timeStatusEnum.value = getTimeStatusEnum(activity.startTime, activity.endTime);
-
+    timeStatusEnum.value = getTimeStatusEnum(activity.value.startTime, activity.value.endTime);
+    state.percent = 100 - data.stock / data.totalStock * 100;
     // 查询商品
     await getSpu(data.spuId);
   };
 
+  // 查询商品
   const getSpu = async (id) => {
-    const { data } = await SpuApi.getSpuDetail(id);
-    // 模拟
+    const {
+      data
+    } = await SpuApi.getSpuDetail(id);
     data.activity_type = 'seckill';
     state.goodsInfo = data;
     // 处理轮播图
@@ -283,6 +272,7 @@
   .disabled-btn-box[disabled] {
     background-color: transparent;
   }
+
   .detail-card {
     background-color: $white;
     margin: 14rpx 20rpx;
@@ -373,6 +363,7 @@
       font-size: 26rpx;
       font-weight: 500;
       color: #ffffff;
+
       .countdown-h {
         font-size: 24rpx;
         font-family: OPPOSANS;
@@ -383,6 +374,7 @@
         background: rgba(#000000, 0.1);
         border-radius: 6rpx;
       }
+
       .countdown-num {
         font-size: 24rpx;
         font-family: OPPOSANS;
@@ -466,6 +458,7 @@
       line-height: normal;
       border-radius: 0px 40rpx 40rpx 0px;
     }
+
     .btn-price {
       font-family: OPPOSANS;
 
@@ -483,6 +476,7 @@
       line-height: normal;
       font-size: 24rpx;
       font-weight: 500;
+
       .no-original {
         font-size: 28rpx;
       }
@@ -553,4 +547,4 @@
     width: 100%;
     height: 100%;
   }
-</style>
+</style>

+ 263 - 0
pages/order/addressSelection.vue

@@ -0,0 +1,263 @@
+<!-- 下单界面,收货地址 or 自提门店的选择组件 -->
+<template>
+  <view class="allAddress" :style="state.isPickUp ? '':'padding-top:10rpx;'">
+    <view class="nav flex flex-wrap">
+      <view class="item font-color" :class="state.deliveryType === 1 ? 'on' : 'on2'"
+            @tap="switchDeliveryType(1)" v-if='state.isPickUp' />
+      <view class="item font-color" :class="state.deliveryType === 2 ? 'on' : 'on2'"
+            @tap="switchDeliveryType(2)" v-if='state.isPickUp' />
+    </view>
+    <!-- 情况一:收货地址的选择 -->
+    <view class='address flex flex-wrap flex-center ss-row-between' @tap='onSelectAddress' v-if='state.deliveryType === 1'
+          :style="state.isPickUp ? '':'border-top-left-radius: 14rpx;border-top-right-radius: 14rpx;'">
+      <view class='addressCon' v-if="state.addressInfo.name">
+        <view class='name'>{{ state.addressInfo.name }}
+          <text class='phone'>{{ state.addressInfo.mobile }}</text>
+        </view>
+        <view class="flex flex-wrap">
+          <text class='default font-color' v-if="state.addressInfo.defaultStatus">[默认]</text>
+          <text class="line2">{{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}</text>
+        </view>
+      </view>
+      <view class='addressCon' v-else>
+        <view class='setaddress'>设置收货地址</view>
+      </view>
+      <view class='iconfont'>
+        <view class="ss-rest-button">
+          <text class="_icon-forward" />
+        </view>
+      </view>
+    </view>
+    <!-- 情况二:门店的选择 -->
+    <view class='address flex flex-wrap flex-center ss-row-between' v-else @tap="onSelectAddress">
+      <view class='addressCon' v-if="state.pickUpInfo.name">
+        <view class='name'>{{ state.pickUpInfo.name }}
+          <text class='phone'>{{ state.pickUpInfo.phone }}</text>
+        </view>
+        <view class="line1"> {{ state.pickUpInfo.areaName }}{{ ', ' + state.pickUpInfo.detailAddress }}
+        </view>
+      </view>
+      <view class='addressCon' v-else>
+        <view class='setaddress'>选择自提门店</view>
+      </view>
+      <view class='iconfont'>
+        <view class="ss-rest-button">
+          <text class="_icon-forward" />
+        </view>
+      </view>
+    </view>
+    <view class='line'>
+      <image :src="sheep.$url.static('/static/images/line.png', 'local')" />
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { computed } from 'vue';
+  import sheep from '@/sheep';
+  import { isEmpty } from 'lodash-es';
+
+  const props = defineProps({
+    modelValue: {
+      type: Object,
+      default() {},
+    }
+  });
+  const emits = defineEmits(['update:modelValue','change']);
+
+  // computed 解决父子组件双向数据同步
+  const state = computed({
+    get(){
+      return new Proxy(props.modelValue, {
+        set(obj, name, val) {
+          emits('update:modelValue', {
+            ...obj,
+            [name]: val,
+          });
+          return true;
+        }
+      })
+    },
+    set(val){
+      emits('update:modelValue', val);
+    }
+  })
+
+  // 选择地址
+  function onSelectAddress() {
+    let emitName = 'SELECT_ADDRESS'
+    let addressPage = '/pages/user/address/list?type=select';
+    if (state.value.deliveryType === 2){
+      emitName = 'SELECT_PICK_UP_INFO'
+      addressPage = '/pages/user/goods_details_store/index'
+    }
+    uni.$once(emitName, (e) => {
+      changeConsignee(e.addressInfo);
+    });
+    sheep.$router.go(addressPage);
+  }
+
+  // 更改收货人地址&计算订单信息
+  async function changeConsignee(addressInfo = {}) {
+    if (!isEmpty(addressInfo)) {
+      if (state.value.deliveryType === 1){
+        state.value.addressInfo = addressInfo;
+      }
+      if (state.value.deliveryType === 2){
+        state.value.pickUpInfo = addressInfo;
+      }
+      emits('change')
+    }
+  }
+
+  // 收货方式切换
+  const switchDeliveryType = (type) =>{
+    state.value.deliveryType = type;
+    emits('change')
+  }
+</script>
+
+<style scoped lang="scss">
+  .allAddress .font-color{
+    color: #E93323!important
+  }
+  .line2{
+    width: 504rpx;
+  }
+  .textR {
+    text-align: right;
+  }
+
+  .line {
+    width: 100%;
+    height: 3rpx;
+  }
+
+  .line image {
+    width: 100%;
+    height: 100%;
+    display: block;
+  }
+
+  .address {
+    padding: 28rpx;
+    background-color: #fff;
+    box-sizing: border-box;
+  }
+
+  .address .addressCon {
+    width: 596rpx;
+    font-size: 26rpx;
+    color: #666;
+  }
+
+  .address .addressCon .name {
+    font-size: 30rpx;
+    color: #282828;
+    font-weight: bold;
+    margin-bottom: 10rpx;
+  }
+
+  .address .addressCon .name .phone {
+    margin-left: 50rpx;
+  }
+
+  .address .addressCon .default {
+    margin-right: 12rpx;
+  }
+
+  .address .addressCon .setaddress {
+    color: #333;
+    font-size: 28rpx;
+  }
+
+  .address .iconfont {
+    font-size: 35rpx;
+    color: #707070;
+  }
+
+  .allAddress {
+    width: 100%;
+    background: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: -webkit-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: -moz-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    //padding: 100rpx 30rpx 0 30rpx;
+    padding-top: 100rpx;
+    padding-bottom: 10rpx;
+  }
+
+  .allAddress .nav {
+    width: 690rpx;
+    margin: 0 auto;
+  }
+
+  .allAddress .nav .item {
+    width: 334rpx;
+  }
+
+  .allAddress .nav .item.on {
+    position: relative;
+    width: 230rpx;
+  }
+
+  .allAddress .nav .item.on::before {
+    position: absolute;
+    bottom: 0;
+    content: "快递配送";
+    font-size: 28rpx;
+    display: block;
+    height: 0;
+    width: 336rpx;
+    border-width: 0 20rpx 80rpx 0;
+    border-style: none solid solid;
+    border-color: transparent transparent #fff;
+    z-index: 2;
+    border-radius: 14rpx 36rpx 0 0;
+    text-align: center;
+    line-height: 80rpx;
+  }
+
+  .allAddress .nav .item:nth-of-type(2).on::before {
+    content: "到店自提";
+    border-width: 0 0 80rpx 20rpx;
+    border-radius: 36rpx 14rpx 0 0;
+  }
+
+  .allAddress .nav .item.on2 {
+    position: relative;
+  }
+
+  .allAddress .nav .item.on2::before {
+    position: absolute;
+    bottom: 0;
+    content: "到店自提";
+    font-size: 28rpx;
+    display: block;
+    height: 0;
+    width: 401rpx;
+    border-width: 0 0 60rpx 60rpx;
+    border-style: none solid solid;
+    border-color: transparent transparent #f7c1bd;
+    border-radius: 36rpx 14rpx 0 0;
+    text-align: center;
+    line-height: 60rpx;
+  }
+
+  .allAddress .nav .item:nth-of-type(1).on2::before {
+    content: "快递配送";
+    border-width: 0 60rpx 60rpx 0;
+    border-radius: 14rpx 36rpx 0 0;
+  }
+
+  .allAddress .address {
+    width: 690rpx;
+    max-height: 180rpx;
+    margin: 0 auto;
+  }
+
+  .allAddress .line {
+    width: 100%;
+    margin: 0 auto;
+  }
+</style>

+ 129 - 105
pages/order/confirm.vue

@@ -1,36 +1,18 @@
 <template>
   <s-layout title="确认订单">
-    <!-- TODO:这个判断先删除 v-if="state.orderInfo.need_address === 1" -->
-    <view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress">
-      <s-address-item :item="state.addressInfo" :hasBorderBottom="false">
-        <view class="ss-rest-button">
-          <text class="_icon-forward" />
-        </view>
-      </s-address-item>
-    </view>
+    <!-- 头部地址选择【配送地址】【自提地址】 -->
+    <AddressSelection v-model="addressState" @change="getOrderInfo()" />
 
     <!-- 商品信息 -->
     <view class="order-card-box ss-m-b-14">
-      <s-goods-item
-        v-for="item in state.orderInfo.items"
-        :key="item.skuId"
-        :img="item.picUrl"
-        :title="item.spuName"
-        :skuText="item.properties.map((property) => property.valueName).join(' ')"
-        :price="item.price"
-        :num="item.count"
-        marginBottom="10"
-      />
+      <s-goods-item v-for="item in state.orderInfo.items" :key="item.skuId" :img="item.picUrl"
+                    :title="item.spuName" :skuText="item.properties.map((property) => property.valueName).join(' ')"
+                    :price="item.price" :num="item.count" marginBottom="10" />
       <view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white ss-r-10">
         <view class="item-title">订单备注</view>
         <view class="ss-flex ss-col-center">
-          <uni-easyinput
-            maxlength="20"
-            placeholder="建议留言前先与商家沟通"
-            v-model="state.orderPayload.remark"
-            :inputBorder="false"
-            :clearable="false"
-          />
+          <uni-easyinput maxlength="20" placeholder="建议留言前先与商家沟通" v-model="state.orderPayload.remark"
+                         :inputBorder="false" :clearable="false" />
         </view>
       </view>
     </view>
@@ -46,67 +28,71 @@
             </text>
           </view>
         </view>
-        <!-- TODO 芋艿:接入积分 -->
-        <view
-          class="order-item ss-flex ss-col-center ss-row-between"
-          v-if="state.orderPayload.order_type === 'score'"
-        >
-          <view class="item-title">扣除积分</view>
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.type === 0">
+          <view class="item-title">积分抵扣</view>
           <view class="ss-flex ss-col-center">
-            <image
-              :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-              class="score-img"
-            />
-            <text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text>
+            {{ state.pointStatus ? '剩余积分' : '当前积分' }}
+            <image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img" />
+            <text class="item-value ss-m-r-24">
+              {{ state.pointStatus ? state.orderInfo.totalPoint - state.orderInfo.usePoint : (state.orderInfo.totalPoint || 0) }}
+            </text>
+            <checkbox-group @change="changeIntegral">
+              <checkbox :checked='state.pointStatus'
+                        :disabled="!state.orderInfo.totalPoint || state.orderInfo.totalPoint <= 0" />
+            </checkbox-group>
           </view>
         </view>
-        <view class="order-item ss-flex ss-col-center ss-row-between">
+        <!-- 快递配置时,信息的展示 -->
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 1'>
           <view class="item-title">运费</view>
           <view class="ss-flex ss-col-center">
-            <text class="item-value ss-m-r-24">
+            <text class="item-value ss-m-r-24" v-if="state.orderInfo.price.deliveryPrice > 0">
               +¥{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
             </text>
+            <view class='item-value ss-m-r-24' v-else>免运费</view>
+          </view>
+        </view>
+        <!-- 门店自提时,需要填写姓名和手机号 -->
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 2'>
+          <view class="item-title">联系人</view>
+          <view class="ss-flex ss-col-center">
+            <uni-easyinput maxlength="20" placeholder="请填写您的联系姓名" v-model="addressState.receiverName"
+                           :inputBorder="false" :clearable="false" />
+          </view>
+        </view>
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 2'>
+          <view class="item-title">联系电话</view>
+          <view class="ss-flex ss-col-center">
+            <uni-easyinput maxlength="20" placeholder="请填写您的联系电话" v-model="addressState.receiverMobile"
+                           :inputBorder="false" :clearable="false" />
           </view>
         </view>
         <!-- 优惠劵:只有 type = 0 普通订单(非拼团、秒杀、砍价),才可以使用优惠劵 -->
-        <view
-          class="order-item ss-flex ss-col-center ss-row-between"
-          v-if="state.orderInfo.type === 0"
-        >
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.type === 0">
           <view class="item-title">优惠券</view>
           <view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
             <text class="item-value text-red" v-if="state.orderPayload.couponId > 0">
               -¥{{ fen2yuan(state.orderInfo.price.couponPrice) }}
             </text>
-            <text
-              class="item-value"
-              :class="state.couponInfo.length > 0 ? 'text-red' : 'text-disabled'"
-              v-else
-            >
+            <text class="item-value" :class="couponNumber > 0 ? 'text-red' : 'text-disabled'" v-else>
               {{
-                state.couponInfo.length > 0 ? state.couponInfo.length + ' 张可用' : '暂无可用优惠券'
+                couponNumber > 0 ? couponNumber + ' 张可用' : '暂无可用优惠券'
               }}
             </text>
             <text class="_icon-forward item-icon" />
           </view>
         </view>
-        <view
-          class="order-item ss-flex ss-col-center ss-row-between"
-          v-if="state.orderInfo.price.discountPrice > 0"
-        >
+        <view class="order-item ss-flex ss-col-center ss-row-between"
+              v-if="state.orderInfo.price.discountPrice > 0">
           <view class="item-title">活动优惠</view>
-          <view class="ss-flex ss-col-center">
-            <!--                @tap="state.showDiscount = true" TODO 芋艿:后续要把优惠信息打进去 -->
+          <view class="ss-flex ss-col-center" @tap="state.showDiscount = true">
             <text class="item-value text-red">
               -¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
             </text>
             <text class="_icon-forward item-icon" />
           </view>
         </view>
-        <view
-          class="order-item ss-flex ss-col-center ss-row-between"
-          v-if="state.orderInfo.price.vipPrice > 0"
-        >
+        <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.price.vipPrice > 0">
           <view class="item-title">会员优惠</view>
           <view class="ss-flex ss-col-center">
             <text class="item-value text-red">
@@ -120,24 +106,16 @@
           共{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}件
         </view>
         <view>合计:</view>
-        <view class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }} </view>
+        <view class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}</view>
       </view>
     </view>
 
     <!-- 选择优惠券弹框 -->
-    <s-coupon-select
-      v-model="state.couponInfo"
-      :show="state.showCoupon"
-      @confirm="onSelectCoupon"
-      @close="state.showCoupon = false"
-    />
+    <s-coupon-select v-model="state.couponInfo" :show="state.showCoupon" @confirm="onSelectCoupon"
+                     @close="state.showCoupon = false" />
 
     <!-- 满额折扣弹框 TODO 芋艿:后续要把优惠信息打进去 -->
-    <s-discount-list
-      v-model="state.orderInfo"
-      :show="state.showDiscount"
-      @close="state.showDiscount = false"
-    />
+    <s-discount-list v-model="state.orderInfo" :show="state.showDiscount" @close="state.showDiscount = false" />
 
     <!-- 底部 -->
     <su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
@@ -147,10 +125,7 @@
             ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}
           </view>
         </view>
-        <button
-          class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main"
-          @tap="onConfirm"
-        >
+        <button class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main" @tap="onConfirm">
           提交订单
         </button>
       </view>
@@ -159,14 +134,20 @@
 </template>
 
 <script setup>
-  import { reactive } from 'vue';
-  import { onLoad } from '@dcloudio/uni-app';
+  import {
+    reactive,
+    ref
+  } from 'vue';
+  import {
+    onLoad
+  } from '@dcloudio/uni-app';
+  import AddressSelection from '@/pages/order/addressSelection.vue';
   import sheep from '@/sheep';
-  import { isEmpty } from 'lodash-es';
   import OrderApi from '@/sheep/api/trade/order';
   import CouponApi from '@/sheep/api/promotion/coupon';
-  import { fen2yuan } from '@/sheep/hooks/useGoods';
-  import { WxaSubscribeTemplate } from '@/sheep/util/const';
+  import {
+    fen2yuan
+  } from '@/sheep/hooks/useGoods';
 
   const state = reactive({
     orderPayload: {},
@@ -174,27 +155,30 @@
       items: [], // 商品项列表
       price: {}, // 价格信息
     },
-    addressInfo: {}, // 选择的收货地址
     showCoupon: false, // 是否展示优惠劵
     couponInfo: [], // 优惠劵列表
     showDiscount: false, // 是否展示营销活动
+    // ========== 积分 ==========
+    pointStatus: false, //是否使用积分
   });
 
-  // 选择地址
-  function onSelectAddress() {
-    uni.$once('SELECT_ADDRESS', (e) => {
-      changeConsignee(e.addressInfo);
-    });
-    sheep.$router.go('/pages/user/address/list');
-  }
+  const addressState = ref({
+    addressInfo: {}, // 选择的收货地址
+    deliveryType: 1, // 收货方式 1 - 快递配送;2 - 门店自提
+    isPickUp: true, // 门店自提是否开启 TODO puhui999: 默认开启,看看后端有开关的话接入
+    pickUpInfo: {}, // 选择的自提门店信息
+    receiverName: '', // 收件人名称
+    receiverMobile: '', // 收件人手机
+  });
 
-  // 更改收货人地址&计算订单信息
-  async function changeConsignee(addressInfo = {}) {
-    if (!isEmpty(addressInfo)) {
-      state.addressInfo = addressInfo;
-    }
+  // ========== 积分 ==========
+  /**
+   * 使用积分抵扣
+   */
+  const changeIntegral = async () => {
+    state.pointStatus = !state.pointStatus;
     await getOrderInfo();
-  }
+  };
 
   // 选择优惠券
   async function onSelectCoupon(couponId) {
@@ -205,22 +189,46 @@
 
   // 提交订单
   function onConfirm() {
-    if (!state.addressInfo.id) {
+    if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
       sheep.$helper.toast('请选择收货地址');
       return;
     }
+    if (addressState.value.deliveryType === 2) {
+      if (!addressState.value.pickUpInfo.id) {
+        sheep.$helper.toast('请选择自提门店地址');
+        return;
+      }
+      if (addressState.value.receiverName === '' || addressState.value.receiverMobile === '') {
+        sheep.$helper.toast('请填写联系人或联系人电话');
+        return;
+      }
+      if (!/^[\u4e00-\u9fa5\w]{2,16}$/.test(addressState.value.receiverName)) {
+        sheep.$helper.toast('请填写您的真实姓名');
+        return;
+      }
+      if (!/^1(3|4|5|7|8|9|6)\d{9}$/.test(addressState.value.receiverMobile)) {
+        sheep.$helper.toast('请填写正确的手机号');
+        return;
+      }
+    }
     submitOrder();
   }
 
   // 创建订单&跳转
   async function submitOrder() {
-    const { code, data } = await OrderApi.createOrder({
+    const {
+      code,
+      data
+    } = await OrderApi.createOrder({
       items: state.orderPayload.items,
       couponId: state.orderPayload.couponId,
       remark: state.orderPayload.remark,
-      addressId: state.addressInfo.id,
-      deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
-      pointStatus: false, // TODO 芋艿:需要支持【积分选择】
+      deliveryType: addressState.value.deliveryType,
+      addressId: addressState.value.addressInfo.id, // 收件地址编号
+      pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
+      receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
+      receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
+      pointStatus: state.pointStatus,
       combinationActivityId: state.orderPayload.combinationActivityId,
       combinationHeadId: state.orderPayload.combinationHeadId,
       seckillActivityId: state.orderPayload.seckillActivityId,
@@ -242,29 +250,44 @@
   // 检查库存 & 计算订单价格
   async function getOrderInfo() {
     // 计算价格
-    const { data, code } = await OrderApi.settlementOrder({
+    const {
+      data,
+      code
+    } = await OrderApi.settlementOrder({
       items: state.orderPayload.items,
       couponId: state.orderPayload.couponId,
-      addressId: state.addressInfo.id,
-      deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
-      pointStatus: false, // TODO 芋艿:需要支持【积分选择】
+      deliveryType: addressState.value.deliveryType,
+      addressId: addressState.value.addressInfo.id, // 收件地址编号
+      pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
+      receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
+      receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
+      pointStatus: state.pointStatus,
       combinationActivityId: state.orderPayload.combinationActivityId,
       combinationHeadId: state.orderPayload.combinationHeadId,
       seckillActivityId: state.orderPayload.seckillActivityId,
     });
     if (code !== 0) {
+      setTimeout(() => {
+        uni.navigateBack({
+          delta: 1
+        })
+      }, 1500)
       return;
     }
     state.orderInfo = data;
     // 设置收货地址
     if (state.orderInfo.address) {
-      state.addressInfo = state.orderInfo.address;
+      addressState.value.addressInfo = state.orderInfo.address;
     }
   }
 
   // 获取可用优惠券
+  let couponNumber = ref(0)
   async function getCoupons() {
-    const { code, data } = await CouponApi.getMatchCouponList(
+    const {
+      code,
+      data
+    } = await CouponApi.getMatchCouponList(
       state.orderInfo.price.payPrice,
       state.orderInfo.items.map((item) => item.spuId),
       state.orderPayload.items.map((item) => item.skuId),
@@ -272,6 +295,7 @@
     );
     if (code === 0) {
       state.couponInfo = data;
+      couponNumber.value = state.couponInfo.filter(item => item.match).length;
     }
   }
 
@@ -405,4 +429,4 @@
     font-size: 36rpx;
     color: #999999;
   }
-</style>
+</style>

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

@@ -13,6 +13,18 @@ const SpuApi = {
       },
     });
   },
+  // 获得商品结算信息
+  getSettlementProduct: (ids) => {
+    return request({
+      url: '/trade/order/settlementProduct',
+      method: 'GET',
+      params: { ids },
+      custom: {
+        showLoading: false,
+        showError: false,
+      },
+    });
+  },
   // 获得商品 SPU 分页
   getSpuPage: (params) => {
     return request({

+ 225 - 29
sheep/components/s-activity-pop/s-activity-pop.vue

@@ -2,22 +2,100 @@
 <template>
   <su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose>
     <view class="model-box">
-      <view class="title ss-m-t-16 ss-m-l-20 ss-flex">营销活动</view>
-      <scroll-view
-        class="model-content ss-m-t-50"
-        scroll-y
-        :scroll-with-animation="false"
-        :enable-back-to-top="true"
-      >
-        <view v-for="item in state.activityInfo" :key="item.id">
-          <view class="ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(item)">
-            <view class="model-content-tag ss-flex ss-row-center">满减</view>
-            <view class="ss-m-l-20 model-content-title ss-flex-1">
-              <view class="ss-m-b-24" v-for="rule in state.activityMap[item.id]?.rules" :key="rule">
-                {{ formatRewardActivityRule(state.activityMap[item.id], rule) }}
+      <view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠</view>
+      <view v-if="state.activityMap[state.activityInfo[0]?.id]?.reduc">
+        <view class="titleLi">促销</view>
+        <scroll-view class="model-content" scroll-y :scroll-with-animation="false" :enable-back-to-top="true">
+          <view class="actBox">
+            <view class="boxCont ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(state.activityInfo[0])">
+              <view class="model-content-tag ss-flex ss-row-center">满减</view>
+              <view class="model-content-title">
+                <view class="contBu">
+                  <text v-for="(item,index) in state.activityMap[state.activityInfo[0]?.id]?.reduc"
+                        :key="index">满{{fen2yuan(item.discountPrice)}}元减{{fen2yuan(item.limit)}}元;</text>
+                </view>
+                <view class="ss-m-b-24 cotBu-txt">
+                  {{formatDateRange(state.activityInfo[0]?.startTime,state.activityInfo[0]?.endTime)}}
+                </view>
               </view>
+              <text class="cicon-forward" />
+            </view>
+          </view>
+          <view class="actBox">
+            <view class="boxCont ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(state.activityInfo[0])">
+              <view class="model-content-tag ss-flex ss-row-center">包邮</view>
+              <view class="model-content-title">
+                <view class="contBu">
+                  <text v-for="(item,index) in state.activityMap[state.activityInfo[0]?.id]?.ship"
+                        :key="index" v-show="item.bull">满{{fen2yuan(item.discountPrice)}}元包邮;</text>
+                </view>
+                <view class="ss-m-b-24 cotBu-txt">
+                  {{formatDateRange(state.activityInfo[0]?.startTime,state.activityInfo[0]?.endTime)}}
+                </view>
+              </view>
+              <text class="cicon-forward" />
+            </view>
+          </view>
+          <view class="actBox">
+            <view class="boxCont ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(state.activityInfo[0])">
+              <view class="model-content-tag ss-flex ss-row-center">送积分</view>
+              <view class="model-content-title">
+                <view class="contBu">
+                  <text v-for="(item,index) in state.activityMap[state.activityInfo[0]?.id]?.scor"
+                        :key="index"
+                        v-show="item.bull">满{{fen2yuan(item.discountPrice)}}元送{{item.value}}积分;</text>
+                </view>
+                <view class="ss-m-b-24 cotBu-txt">
+                  {{formatDateRange(state.activityInfo[0]?.startTime,state.activityInfo[0]?.endTime)}}
+                </view>
+              </view>
+              <text class="cicon-forward" />
+            </view>
+          </view>
+          <view class="actBox">
+            <view class="boxCont ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(state.activityInfo[0])">
+              <view class="model-content-tag ss-flex ss-row-center">送优惠券</view>
+              <view class="model-content-title">
+                <view class="contBu">
+                  <text v-for="(item,index) in state.activityMap[state.activityInfo[0]?.id]?.cou"
+                        :key="index"
+                        v-show="item.bull">满{{fen2yuan(item.discountPrice)}}元送{{item.value}}张优惠券;</text>
+                </view>
+                <view class="ss-m-b-24 cotBu-txt">
+                  {{formatDateRange(state.activityInfo[0]?.startTime,state.activityInfo[0]?.endTime)}}
+                </view>
+              </view>
+              <text class="cicon-forward" />
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+      <view class="titleLi">可领优惠券</view>
+      <scroll-view class="model-content" scroll-y :scroll-with-animation="false" :enable-back-to-top="true">
+        <view class="actBox" v-for="item in state.couponInfo" :key="item.id">
+          <view class="boxCont ss-flex ss-col-top ss-m-b-40">
+            <view class="model-content-tag2">
+              <view class="usePrice">
+                ¥{{fen2yuan(item.discountPrice)}}
+              </view>
+              <view class="impose">
+                满¥{{fen2yuan(item.usePrice)}}可用
+              </view>
+            </view>
+            <view class="model-content-title2">
+              <view class="contBu">
+                {{item.name}}
+              </view>
+              <view class="ss-m-b-24 cotBu-txt">
+                {{item.validityType==1?formatDateRange(item.validStartTime,item.validEndTime) : '领取后'+item.fixedStartTerm+'-'+item.fixedEndTerm +'天可用'}}
+              </view>
+            </view>
+            <view class="coupon" @click.stop="getBuy(item.id)" v-if="item.canTake">
+              立即领取
+            </view>
+            <view class="coupon2" v-else>
+              已领取
             </view>
-            <text class="cicon-forward" />
           </view>
         </view>
       </scroll-view>
@@ -26,14 +104,21 @@
 </template>
 <script setup>
   import sheep from '@/sheep';
-  import { computed, reactive, watch } from 'vue';
+  import {
+    computed,
+    reactive,
+    watch
+  } from 'vue';
   import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
-  import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
-
+  import {
+    fen2yuan,
+    formatDateRange,
+    handActitList
+  } from '@/sheep/hooks/useGoods';
   const props = defineProps({
     modelValue: {
       type: Object,
-      default() {},
+      default () {},
     },
     show: {
       type: Boolean,
@@ -42,10 +127,10 @@
   });
   const emits = defineEmits(['close']);
   const state = reactive({
-    activityInfo: computed(() => props.modelValue),
-    activityMap: {}
+    activityInfo: computed(() => props.modelValue.activityInfo),
+    activityMap: {},
+    couponInfo: computed(() => props.modelValue.couponInfo)
   });
-
   watch(
     () => props.show,
     () => {
@@ -56,12 +141,16 @@
             if (res.code !== 0) {
               return;
             }
-            state.activityMap[activity.id] = res.data;
+            state.activityMap[activity.id] = handActitList(res.data.rules);
           })
         });
       }
     },
   );
+  // 领取优惠劵
+  const getBuy = (id) => {
+    emits('get', id);
+  };
 
   function onGoodsList(e) {
     sheep.$router.go('/pages/activity/index', {
@@ -72,34 +161,141 @@
 <style lang="scss" scoped>
   .model-box {
     height: 60vh;
+
     .title {
+      justify-content: center;
       font-size: 36rpx;
       height: 80rpx;
       font-weight: bold;
       color: #333333;
     }
   }
+
   .model-content {
+    height: fit-content;
+    max-height: 350rpx;
     padding: 0 20rpx;
     box-sizing: border-box;
+    margin-top: 20rpx;
+
     .model-content-tag {
-      background: rgba(#ff6911, 0.1);
-      font-size: 24rpx;
+      // background: rgba(#ff6911, 0.1);
+      font-size: 35rpx;
       font-weight: 500;
       color: #ff6911;
-      line-height: 42rpx;
-      width: 68rpx;
-      height: 32rpx;
-      border-radius: 5rpx;
+      line-height: 150rpx;
+      width: 200rpx;
+      height: 150rpx;
+      text-align: center;
+
+      // border-radius: 5rpx;
     }
+
     .model-content-title {
+      width: 450rpx;
+      height: 150rpx;
       font-size: 26rpx;
       font-weight: 500;
       color: #333333;
+      overflow: hidden;
     }
+
     .cicon-forward {
       font-size: 28rpx;
       color: #999999;
+      margin: 0 auto;
     }
   }
-</style>
+
+  // 新增的
+  .titleLi {
+    margin: 10rpx 0 10rpx 20rpx;
+    font-size: 26rpx;
+  }
+
+  .actBox {
+    width: 700rpx;
+    height: 150rpx;
+    background-color: #fff2f2;
+    margin: 10rpx auto;
+    border-radius: 10rpx;
+  }
+
+  .boxCont {
+    width: 700rpx;
+    height: 150rpx;
+    align-items: center;
+  }
+
+  .contBu {
+    height: 80rpx;
+    line-height: 80rpx;
+    overflow: hidden;
+    font-size: 30rpx;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    -o-text-overflow: ellipsis;
+  }
+
+  .cotBu-txt {
+    height: 70rpx;
+    line-height: 70rpx;
+    font-size: 25rpx;
+    color: #999999;
+  }
+
+  .model-content-tag2 {
+    font-size: 35rpx;
+    font-weight: 500;
+    color: #ff6911;
+    width: 200rpx;
+    height: 150rpx;
+    text-align: center;
+  }
+
+  .usePrice {
+    width: 200rpx;
+    height: 90rpx;
+    line-height: 100rpx;
+    // background-color: red;
+  }
+
+  .impose {
+    width: 200rpx;
+    height: 50rpx;
+    // line-height: 75rpx;
+    font-size: 23rpx;
+    // background-color: gold;
+  }
+
+  .model-content-title2 {
+    width: 330rpx;
+    height: 150rpx;
+    font-size: 26rpx;
+    font-weight: 500;
+    color: #333333;
+    overflow: hidden;
+  }
+
+  .coupon {
+    width: 150rpx;
+    height: 50rpx;
+    line-height: 50rpx;
+    background-color: rgb(255, 68, 68);
+    color: white;
+    border-radius: 30rpx;
+    text-align: center;
+    font-size: 25rpx;
+  }
+
+  .coupon2 {
+    width: 150rpx;
+    height: 50rpx;
+    line-height: 50rpx;
+    background-color: rgb(203, 192, 191);
+    color: white;
+    border-radius: 30rpx;
+    text-align: center;
+    font-size: 25rpx;
+  }
+</style>

+ 33 - 36
sheep/components/s-discount-list/s-discount-list.vue

@@ -1,23 +1,12 @@
 <template>
-  <su-popup
-    :show="show"
-    type="bottom"
-    round="20"
-    @close="emits('close')"
-    showClose
-    backgroundColor="#f2f2f2"
-  >
+  <su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose backgroundColor="#f2f2f2">
     <view class="model-box">
       <view class="title ss-m-t-38 ss-m-l-20 ss-m-b-40">活动优惠</view>
-      <scroll-view
-        class="model-content ss-m-l-20"
-        scroll-y
-        :scroll-with-animation="false"
-        :enable-back-to-top="true"
-      >
-        <view v-for="(item, index) in state.orderInfo.promo_infos" :key="index">
+      <scroll-view class="model-content ss-m-l-20" scroll-y :scroll-with-animation="false"
+                   :enable-back-to-top="true">
+        <view v-for="(item, index) in state.orderInfo.promotions" :key="index">
           <view class="ss-flex ss-m-b-40 subtitle">
-            <view>共{{ item.goods_ids.length }}件,</view>
+            <!-- <view>共{{ item.goods_ids.length }}件,</view>
             <view v-if="item.activity_type === 'full_discount'">
               满{{ item.discount_rule.full }}打{{ item.discount_rule.discount }}折,已减
             </view>
@@ -25,15 +14,11 @@
             <view v-if="item.activity_type === 'full_reduce'">
               满{{ item.discount_rule.full }}减{{ item.discount_rule.discount }},已减
             </view>
-            <view class="price-text">¥{{ item.promo_discount_money || '0.00' }}</view>
-          </view>
-          <scroll-view class="scroll-box" scroll-x scroll-anchoring>
-            <view class="ss-flex">
-              <view v-for="i in item.goods_ids" :key="i">
-                <image class="content-img" :src="sheep.$url.cdn(getGoodsImg(i))" />
-              </view>
+            <view class="price-text">¥{{ item.promo_discount_money || '0.00' }}</view> -->
+            <view>
+              {{item.description}}
             </view>
-          </scroll-view>
+          </view>
         </view>
       </scroll-view>
     </view>
@@ -43,8 +28,14 @@
   </su-popup>
 </template>
 <script setup>
-  import { computed, reactive } from 'vue';
+  import {
+    computed,
+    reactive
+  } from 'vue';
   import sheep from '@/sheep';
+  import {
+    fen2yuan
+  } from '@/sheep/hooks/useGoods';
   const props = defineProps({
     promoInfo: {
       type: Array,
@@ -56,7 +47,7 @@
     },
     modelValue: {
       type: Object,
-      default() {},
+      default () {},
     },
     show: {
       type: Boolean,
@@ -67,28 +58,31 @@
   const state = reactive({
     orderInfo: computed(() => props.modelValue),
   });
-  const getGoodsImg = (e) => {
-    let goodsImg = '';
-    state.orderInfo.goods_list.forEach((i) => {
-      if (e == i.goods_id) {
-        goodsImg = i.goods.image;
-      }
-    });
-    return goodsImg;
-  };
+  // const getGoodsImg = (e) => {
+  //   let goodsImg = '';
+  //   state.orderInfo.goods_list.forEach((i) => {
+  //     if (e == i.goods_id) {
+  //       goodsImg = i.goods.image;
+  //     }
+  //   });
+  //   return goodsImg;
+  // };
 </script>
 <style lang="scss" scoped>
   .model-box {
     height: 60vh;
   }
+
   .model-content {
     height: 54vh;
   }
+
   .modal-footer {
     width: 100%;
     height: 120rpx;
     background: #fff;
   }
+
   .confirm-btn {
     width: 710rpx;
     margin-left: 20rpx;
@@ -97,18 +91,21 @@
     border-radius: 40rpx;
     color: #fff;
   }
+
   .content-img {
     width: 140rpx;
     height: 140rpx;
     margin-right: 20rpx;
     margin-bottom: 20rpx;
   }
+
   .subtitle {
     font-size: 28rpx;
     font-weight: 500;
     color: #333333;
   }
+
   .price-text {
     color: #ff3000;
   }
-</style>
+</style>

+ 53 - 2
sheep/components/s-goods-card/s-goods-card.vue

@@ -135,7 +135,7 @@
   /**
    * 商品卡片
    */
-  import { computed, reactive, onMounted } from 'vue';
+  import { computed, reactive, onMounted, ref } from 'vue';
   import sheep from '@/sheep';
   import SpuApi from '@/sheep/api/product/spu';
 
@@ -227,10 +227,61 @@
     return data;
   }
 
+  //获取结算信息
+  const settleData = ref()
+  async function getSettlementByIds(ids) {
+    const { data } = await SpuApi.getSettlementProduct(ids);
+    return data;
+  }
+
+  //计算展示价格的函数
+  async function enrichDataWithSkus(data, array) {
+    // 创建一个映射,以 id 为键,存储 data 数组中的对象
+    const dataMap = new Map(data.map(item => [item.id, { ...item }]));
+
+    // 遍历 array 数组
+    array.forEach(item => {
+      // 初始化 discountPrice 和 vipPrice 为 null
+      let discountPrice = null;
+      let vipPrice = null;
+      let foundType4 = false;
+      let foundType6 = false;
+
+      // 遍历 skus 数组,寻找 type 为 4 和 6 的首个条目
+      item.skus.forEach(sku => {
+        if (!foundType4 && sku.type === 4) {
+          discountPrice = sku.price;
+          foundType4 = true;
+        }
+        if (!foundType6 && sku.type === 6) {
+          vipPrice = sku.price;
+          foundType6 = true;
+        }
+
+        // 如果已经找到 type 为 4 和 6 的条目,则不需要继续遍历
+        if (foundType4 && foundType6) {
+          return;
+        }
+      });
+
+      // 更新 dataMap 中对应的对象
+      if (dataMap.has(item.id)) {
+        dataMap.get(item.id).discountPrice = discountPrice;
+        dataMap.get(item.id).vipPrice = vipPrice;
+        dataMap.get(item.id).reward = item.reward;
+      }
+    });
+
+    // 返回更新后的数据数组
+    return Array.from(dataMap.values());
+  }
+
   // 初始化
   onMounted(async () => {
     // 加载商品列表
-    state.goodsList = await getGoodsListByIds(spuIds.join(','));
+    const ms = await getGoodsListByIds(spuIds.join(','));
+    settleData.value = await getSettlementByIds(spuIds.join(','))
+    state.goodsList = await enrichDataWithSkus(ms,settleData.value)
     // 只有双列布局时需要
     if (layoutType === LayoutTypeEnum.TWO_COL){
       // 分列

+ 203 - 162
sheep/components/s-goods-column/s-goods-column.vue

@@ -2,38 +2,29 @@
 <template>
   <view class="ss-goods-wrap">
     <!-- xs卡片:横向紧凑型,一行放两个,图片左内容右边  -->
-    <view
-      v-if="size === 'xs'"
-      class="xs-goods-card ss-flex ss-col-stretch"
-      :style="[elStyles]"
-      @tap="onClick"
-    >
+    <view v-if="size === 'xs'" class="xs-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @tap="onClick">
       <view v-if="tagStyle.show" class="tag-icon-box">
         <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
       </view>
-      <image
-        class="xs-img-box"
-        :src="sheep.$url.cdn(data.image || data.picUrl)"
-        mode="aspectFit"
-      ></image>
-      <view
-        v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
-        class="xs-goods-content ss-flex-col ss-row-around"
-      >
-        <view
-          v-if="goodsFields.title?.show || goodsFields.name?.show"
-          class="xs-goods-title ss-line-1"
-          :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
-        >
+      <image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit"></image>
+      <view v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
+            class="xs-goods-content ss-flex-col ss-row-around">
+        <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="xs-goods-title ss-line-1"
+              :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
           {{ data.title || data.name }}
         </view>
-        <view
-          v-if="goodsFields.price?.show"
-          class="xs-goods-price font-OPPOSANS"
-          :style="[{ color: goodsFields.price.color }]"
-        >
+        <!-- 这里是新加的会员价和限时优惠 -->
+        <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
+          <view class="card" v-if="iconShow">{{iconShow}}</view>
+          <view class="card2" v-if="data.reward">{{data.reward.rewardActivity}}</view>
+        </view>
+        <!-- 这里是新加的会员价和限时优惠结束 -->
+        <view v-if="goodsFields.price?.show" class="xs-goods-price font-OPPOSANS"
+              :style="[{ color: goodsFields.price.color }]">
           <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-          {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
+          <text v-if="iconShow=='限时优惠'">{{fen2yuan(data.discountPrice)}}</text>
+          <text v-else-if="iconShow=='会员价'">{{fen2yuan(data.vipPrice)}}</text>
+          <text v-else>{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}</text>
         </view>
       </view>
     </view>
@@ -43,30 +34,26 @@
       <view v-if="tagStyle.show" class="tag-icon-box">
         <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
       </view>
-      <image
-        class="sm-img-box"
-        :src="sheep.$url.cdn(data.image || data.picUrl)"
-        mode="aspectFill"
-      ></image>
-
-      <view
-        v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
-        class="sm-goods-content"
-        :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
-      >
-        <view
-          v-if="goodsFields.title?.show || goodsFields.name?.show"
-          class="sm-goods-title ss-line-1 ss-m-b-16"
-        >
+      <image class="sm-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
+
+      <view v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
+            class="sm-goods-content" :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
+        <view v-if="goodsFields.title?.show || goodsFields.name?.show"
+              class="sm-goods-title ss-line-1 ss-m-b-16">
           {{ data.title || data.name }}
         </view>
-        <view
-          v-if="goodsFields.price?.show"
-          class="sm-goods-price font-OPPOSANS"
-          :style="[{ color: goodsFields.price.color }]"
-        >
+        <!-- 这里是新加的会员价和限时优惠 -->
+        <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
+          <view class="card" v-if="iconShow">{{iconShow}}</view>
+          <view class="card2" v-if="data.reward">{{data.reward.rewardActivity}}</view>
+        </view>
+        <!-- 这里是新加的会员价和限时优惠结束 -->
+        <view v-if="goodsFields.price?.show" class="sm-goods-price font-OPPOSANS"
+              :style="[{ color: goodsFields.price.color }]">
           <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-          {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
+          <text v-if="iconShow=='限时优惠'">{{fen2yuan(data.discountPrice)}}</text>
+          <text v-else-if="iconShow=='会员价'">{{fen2yuan(data.vipPrice)}}</text>
+          <text v-else>{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}</text>
         </view>
       </view>
     </view>
@@ -76,58 +63,43 @@
       <view v-if="tagStyle.show" class="tag-icon-box">
         <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
       </view>
-      <image
-        class="md-img-box"
-        :src="sheep.$url.cdn(data.image || data.picUrl)"
-        mode="widthFix"
-      ></image>
-      <view
-        class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
-        :id="elId"
-      >
-        <view
-          v-if="goodsFields.title?.show || goodsFields.name?.show"
-          class="md-goods-title ss-line-1"
-          :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
-        >
+      <image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix"></image>
+      <view class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16" :id="elId">
+        <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="md-goods-title ss-line-1"
+              :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
           {{ data.title || data.name }}
         </view>
-        <view
-          v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
-          class="md-goods-subtitle ss-m-t-16 ss-line-1"
-          :style="[{ color: subTitleColor, background: subTitleBackground }]"
-        >
+        <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
+              class="md-goods-subtitle ss-m-t-16 ss-line-1"
+              :style="[{ color: subTitleColor, background: subTitleBackground }]">
           {{ data.subtitle || data.introduction }}
         </view>
         <slot name="activity">
           <view v-if="data.promos?.length" class="tag-box ss-flex-wrap ss-flex ss-col-center">
-            <view
-              class="activity-tag ss-m-r-10 ss-m-t-16"
-              v-for="item in data.promos"
-              :key="item.id"
-            >
+            <view class="activity-tag ss-m-r-10 ss-m-t-16" v-for="item in data.promos" :key="item.id">
               {{ item.title }}
             </view>
           </view>
         </slot>
+        <!-- 这里是新加的会员价和限时优惠 -->
+        <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
+          <view class="card" v-if="iconShow">{{iconShow}}</view>
+          <view class="card2" v-if="data.reward">{{data.reward.rewardActivity}}</view>
+        </view>
+        <!-- 这里是新加的会员价和限时优惠结束 -->
         <view class="ss-flex ss-col-bottom">
-          <view
-            v-if="goodsFields.price?.show"
-            class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
-            :style="[{ color: goodsFields.price.color }]"
-          >
+          <view v-if="goodsFields.price?.show" class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
+                :style="[{ color: goodsFields.price.color }]">
             <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-            {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
+            <text v-if="iconShow=='限时优惠'">{{fen2yuan(data.discountPrice)}}</text>
+            <text v-else-if="iconShow=='会员价'">{{fen2yuan(data.vipPrice)}}</text>
+            <text v-else>{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}</text>
           </view>
 
-          <view
-            v-if="
+          <view v-if="
               (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
               (data.original_price > 0 || data.marketPrice > 0)
-            "
-            class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
-            :style="[{ color: originPriceColor }]"
-          >
+            " class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex" :style="[{ color: originPriceColor }]">
             <text class="price-unit ss-font-20">{{ priceUnit }}</text>
             <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
           </view>
@@ -146,12 +118,7 @@
     </view>
 
     <!-- lg卡片:横向型,一行放一个,图片左内容右边  -->
-    <view
-      v-if="size === 'lg'"
-      class="lg-goods-card ss-flex ss-col-stretch"
-      :style="[elStyles]"
-      @tap="onClick"
-    >
+    <view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @tap="onClick">
       <view v-if="tagStyle.show" class="tag-icon-box">
         <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
       </view>
@@ -159,25 +126,16 @@
       <view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center">
         <view class="tag-icon">拼团</view>
       </view>
-      <image
-        class="lg-img-box"
-        :src="sheep.$url.cdn(data.image || data.picUrl)"
-        mode="aspectFill"
-      ></image>
+      <image class="lg-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
       <view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
         <view>
-          <view
-            v-if="goodsFields.title?.show || goodsFields.name?.show"
-            class="lg-goods-title ss-line-2"
-            :style="[{ color: titleColor }]"
-          >
+          <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="lg-goods-title ss-line-2"
+                :style="[{ color: titleColor }]">
             {{ data.title || data.name }}
           </view>
-          <view
-            v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
-            class="lg-goods-subtitle ss-m-t-10 ss-line-1"
-            :style="[{ color: subTitleColor, background: subTitleBackground }]"
-          >
+          <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
+                class="lg-goods-subtitle ss-m-t-10 ss-line-1"
+                :style="[{ color: subTitleColor, background: subTitleBackground }]">
             {{ data.subtitle || data.introduction }}
           </view>
         </view>
@@ -189,25 +147,27 @@
               </view>
             </view>
           </slot>
+          <!-- 这里是新加的会员价和限时优惠 -->
+          <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
+            <view class="card" v-if="iconShow">{{iconShow}}</view>
+            <view class="card2" v-if="data.reward">{{data.reward.rewardActivity}}</view>
+          </view>
+          <!-- 这里是新加的会员价和限时优惠结束 -->
           <view class="ss-flex ss-col-bottom ss-m-t-10">
-            <view
-              v-if="goodsFields.price?.show"
-              class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
-              :style="[{ color: goodsFields.price.color }]"
-            >
+            <view v-if="goodsFields.price?.show"
+                  class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
+                  :style="[{ color: goodsFields.price.color }]">
               <text class="ss-font-24">{{ priceUnit }}</text>
               {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
             </view>
-            <view
-              v-if="
+            <view v-if="
                 (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
                 (data.original_price > 0 || data.marketPrice > 0)
-              "
-              class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
-              :style="[{ color: originPriceColor }]"
-            >
+              " class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS" :style="[{ color: originPriceColor }]">
               <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-              <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
+              <text v-if="iconShow=='限时优惠'">{{fen2yuan(data.discountPrice)}}</text>
+              <text v-else-if="iconShow=='会员价'">{{fen2yuan(data.vipPrice)}}</text>
+              <text v-else>{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}</text>
             </view>
           </view>
           <view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
@@ -227,54 +187,45 @@
         <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
       </view>
 
-      <image
-        class="sl-img-box"
-        :src="sheep.$url.cdn(data.image || data.picUrl)"
-        mode="aspectFill"
-      ></image>
+      <image class="sl-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
 
       <view class="sl-goods-content">
         <view>
-          <view
-            v-if="goodsFields.title?.show || goodsFields.name?.show"
-            class="sl-goods-title ss-line-1"
-            :style="[{ color: titleColor }]"
-          >
+          <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="sl-goods-title ss-line-1"
+                :style="[{ color: titleColor }]">
             {{ data.title || data.name }}
           </view>
-          <view
-            v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
-            class="sl-goods-subtitle ss-m-t-16"
-            :style="[{ color: subTitleColor, background: subTitleBackground }]"
-          >
+          <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
+                class="sl-goods-subtitle ss-m-t-16"
+                :style="[{ color: subTitleColor, background: subTitleBackground }]">
             {{ data.subtitle || data.introduction }}
           </view>
         </view>
         <view>
           <slot name="activity">
             <view v-if="data.promos?.length" class="tag-box ss-flex ss-col-center ss-flex-wrap">
-              <view
-                class="activity-tag ss-m-r-10 ss-m-t-16"
-                v-for="item in data.promos"
-                :key="item.id"
-              >
+              <view class="activity-tag ss-m-r-10 ss-m-t-16" v-for="item in data.promos" :key="item.id">
                 {{ item.title }}
               </view>
             </view>
           </slot>
+          <!-- 这里是新加的会员价和限时优惠 -->
+          <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
+            <view class="card" v-if="iconShow">{{iconShow}}</view>
+            <view class="card2" v-if="data.reward">{{data.reward.rewardActivity}}</view>
+          </view>
+          <!-- 这里是新加的会员价和限时优惠结束 -->
           <view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
             <view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
               <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-              {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
+              <text v-if="iconShow=='限时优惠'">{{fen2yuan(data.discountPrice)}}</text>
+              <text v-else-if="iconShow=='会员价'">{{fen2yuan(data.vipPrice)}}</text>
+              <text v-else>{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}</text>
             </view>
-            <view
-              v-if="
+            <view v-if="
                 (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
                 (data.original_price > 0 || data.marketPrice > 0)
-              "
-              class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
-              :style="[{ color: originPriceColor }]"
-            >
+              " class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex" :style="[{ color: originPriceColor }]">
               <text class="price-unit ss-font-20">{{ priceUnit }}</text>
               <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
             </view>
@@ -285,9 +236,9 @@
         </view>
       </view>
 
-      <slot name="cart"
-        ><view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view></slot
-      >
+      <slot name="cart">
+        <view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view>
+      </slot>
     </view>
   </view>
 </template>
@@ -321,13 +272,26 @@
    * @event {Function()} click 										- 点击卡片
    *
    */
-  import { computed, reactive, getCurrentInstance, onMounted, nextTick } from 'vue';
+  import {
+    computed,
+    reactive,
+    getCurrentInstance,
+    onMounted,
+    nextTick,
+    ref
+  } from 'vue';
   import sheep from '@/sheep';
-  import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods';
-  import { formatStock } from '@/sheep/hooks/useGoods';
+  import {
+    fen2yuan,
+    formatSales
+  } from '@/sheep/hooks/useGoods';
+  import {
+    formatStock
+  } from '@/sheep/hooks/useGoods';
   import goodsCollectVue from '@/pages/user/goods-collect.vue';
-  import { isArray } from 'lodash-es';
-
+  import {
+    isArray
+  } from 'lodash-es';
   // 数据
   const state = reactive({});
 
@@ -335,20 +299,32 @@
   const props = defineProps({
     goodsFields: {
       type: [Array, Object],
-      default() {
+      default () {
         return {
           // 商品价格
-          price: { show: true },
+          price: {
+            show: true
+          },
           // 库存
-          stock: { show: true },
+          stock: {
+            show: true
+          },
           // 商品名称
-          name: { show: true },
+          name: {
+            show: true
+          },
           // 商品介绍
-          introduction: { show: true },
+          introduction: {
+            show: true
+          },
           // 市场价
-          marketPrice: { show: true },
+          marketPrice: {
+            show: true
+          },
           // 销量
-          salesCount: { show: true },
+          salesCount: {
+            show: true
+          },
         };
       },
     },
@@ -417,7 +393,25 @@
       default: false,
     },
   });
-
+  //判断限时优惠和会员价标签内容暂时导致页面出错,又舍不得丢,等着把新的数据整合到商品信息中,也用起来
+  const iconShow = handle()
+
+  function handle() {
+    if (props.data.discountPrice === null && props.data.vipPrice === null) {
+      // 如果两个值都为 null,则不展示任何内容
+      return '';
+    } else if (props.data.discountPrice === null) {
+      // 如果 discountPrice 为 null,展示 vipPrice
+      return '会员价';
+    } else if (props.data.vipPrice === null) {
+      // 如果 vipPrice 为 null,展示 discountPrice
+      return '限时优惠';
+    } else if (props.data.discountPrice < props.data.vipPrice) {
+      return '限时优惠';
+    } else if (props.data.discountPrice > props.data.vipPrice) {
+      return '会员价';
+    }
+  }
   // 组件样式
   const elStyles = computed(() => {
     return {
@@ -449,12 +443,18 @@
   };
 
   // 获取卡片实时高度
-  const { proxy } = getCurrentInstance();
+  const {
+    proxy
+  } = getCurrentInstance();
   const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
+
   function getGoodsPriceCardWH() {
     if (props.size === 'md') {
       const view = uni.createSelectorQuery().in(proxy);
-      view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
+      view.select(`#${elId}`).fields({
+        size: true,
+        scrollOffset: true
+      });
       view.exec((data) => {
         let totalHeight = 0;
         const goodsPriceCard = data[0];
@@ -482,11 +482,13 @@
     left: 0;
     top: 0;
     z-index: 2;
+
     .tag-icon {
       width: 72rpx;
       height: 44rpx;
     }
   }
+
   .seckill-tag {
     position: absolute;
     left: 0;
@@ -501,6 +503,7 @@
     color: #ffffff;
     line-height: 32rpx;
   }
+
   .groupon-tag {
     position: absolute;
     left: 0;
@@ -515,14 +518,17 @@
     color: #ffffff;
     line-height: 32rpx;
   }
+
   .goods-img {
     width: 100%;
     height: 100%;
     background-color: #f5f5f5;
   }
+
   .price-unit {
     margin-right: -4px;
   }
+
   .sales-text {
     display: table;
     font-size: 24rpx;
@@ -586,10 +592,12 @@
       width: 100%;
       height: 208rpx;
     }
+
     .sm-goods-content {
       padding: 20rpx 16rpx;
       box-sizing: border-box;
     }
+
     .sm-goods-title {
       font-size: 26rpx;
       color: #333;
@@ -619,6 +627,7 @@
       color: #333;
       width: 100%;
     }
+
     .md-goods-subtitle {
       font-size: 24rpx;
       font-weight: 400;
@@ -669,6 +678,7 @@
       // line-height: 36rpx;
       // width: 410rpx;
     }
+
     .lg-goods-subtitle {
       font-size: 24rpx;
       font-weight: 400;
@@ -695,6 +705,7 @@
       font-size: 24rpx;
       color: #ffffff;
     }
+
     .tag-box {
       width: 100%;
     }
@@ -708,10 +719,12 @@
     z-index: 1;
     width: 100%;
     background-color: $white;
+
     .sl-goods-content {
       padding: 20rpx 20rpx;
       box-sizing: border-box;
     }
+
     .sl-img-box {
       width: 100%;
       height: 360rpx;
@@ -722,6 +735,7 @@
       color: #333;
       font-weight: 500;
     }
+
     .sl-goods-subtitle {
       font-size: 24rpx;
       font-weight: 400;
@@ -748,4 +762,31 @@
       color: #ffffff;
     }
   }
-</style>
+
+  .card {
+    width: fit-content;
+    height: fit-content;
+    padding: 2rpx 10rpx;
+    background-color: red;
+    color: #ffffff;
+    font-size: 24rpx;
+  }
+
+  .card2 {
+    width: fit-content;
+    height: fit-content;
+    padding: 2rpx 10rpx;
+    background-color: rgb(255, 242, 241);
+    color: #ff2621;
+    font-size: 24rpx;
+    margin-left: 5rpx;
+  }
+
+  .iconBox {
+    width: 100%;
+    height: fit-content;
+    margin-top: 10rpx;
+    display: flex;
+    justify-content: flex-start;
+  }
+</style>

+ 368 - 334
sheep/components/s-select-sku/s-select-sku.vue

@@ -1,34 +1,38 @@
 <template>
-	<!-- 规格弹窗 -->
-	<su-popup :show="show" round="10" @close="emits('close')">
+  <!-- 规格弹窗 -->
+  <su-popup :show="show" round="10" @close="emits('close')">
     <!-- SKU 信息 -->
-		<view class="ss-modal-box bg-white ss-flex-col">
-			<view class="modal-header ss-flex ss-col-center">
-				<view class="header-left ss-m-r-30">
-					<image class="sku-image" :src="state.selectedSku.picUrl || goodsInfo.picUrl" mode="aspectFill" />
-				</view>
-				<view class="header-right ss-flex-col ss-row-between ss-flex-1">
-					<view class="goods-title ss-line-2">{{ goodsInfo.name }}</view>
-					<view class="header-right-bottom ss-flex ss-col-center ss-row-between">
-						<view class="ss-flex">
-							<view class="price-text">
-								{{ fen2yuan( state.selectedSku.price || goodsInfo.price) }}
-							</view>
-						</view>
-						<view class="stock-text ss-m-l-20">
-							{{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}
-						</view>
-					</view>
-				</view>
-			</view>
+    <view class="ss-modal-box bg-white ss-flex-col">
+      <view class="modal-header ss-flex ss-col-center">
+        <view class="header-left ss-m-r-30">
+          <image class="sku-image" :src="state.selectedSku.picUrl || goodsInfo.picUrl" mode="aspectFill" />
+        </view>
+        <view class="header-right ss-flex-col ss-row-between ss-flex-1">
+          <view class="goods-title ss-line-2">{{ goodsInfo.name }}</view>
+          <view class="header-right-bottom ss-flex ss-col-center ss-row-between">
+            <view class="ss-flex">
+              <view class="price-text">
+                {{ fen2yuan( state.selectedSku.price || goodsInfo.price) }}
+                <text v-if="state.selectedSku.type == 6"><text class="iconBox">会员价</text><text
+                  class="origin-price-text">{{fen2yuan(state.selectedSku.oldPrice)}}</text></text>
+                <text v-if="state.selectedSku.type == 4"><text class="iconBox">限时优惠</text><text
+                  class="origin-price-text">{{fen2yuan(state.selectedSku.oldPrice)}}</text></text>
+              </view>
+            </view>
+            <view class="stock-text ss-m-l-20">
+              {{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}
+            </view>
+          </view>
+        </view>
+      </view>
 
       <!-- 属性选择 -->
-			<view class="modal-content ss-flex-1">
-				<scroll-view scroll-y="true" class="modal-content-scroll" @touchmove.stop>
-					<view class="sku-item ss-m-b-20" v-for="property in propertyList" :key="property.id">
-						<view class="label-text ss-m-b-20">{{ property.name }}</view>
-						<view class="ss-flex ss-col-center ss-flex-wrap">
-							<button class="ss-reset-button spec-btn" v-for="value in property.values" :class="[
+      <view class="modal-content ss-flex-1">
+        <scroll-view scroll-y="true" class="modal-content-scroll" @touchmove.stop>
+          <view class="sku-item ss-m-b-20" v-for="property in propertyList" :key="property.id">
+            <view class="label-text ss-m-b-20">{{ property.name }}</view>
+            <view class="ss-flex ss-col-center ss-flex-wrap">
+              <button class="ss-reset-button spec-btn" v-for="value in property.values" :class="[
                   {
                     'ui-BG-Main-Gradient': state.currentPropertyArray[property.id] === value.id,
                   },
@@ -36,71 +40,78 @@
                     'disabled-btn': value.disabled === true,
                   },
                 ]" :key="value.id" :disabled="value.disabled === true" @tap="onSelectSku(property.id, value.id)">
-								{{ value.name }}
-							</button>
-						</view>
-					</view>
-					<view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40">
-						<view class="label-text">购买数量</view>
-						<su-number-box :min="1" :max="state.selectedSku.stock" :step="1"
+                {{ value.name }}
+              </button>
+            </view>
+          </view>
+          <view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40">
+            <view class="label-text">购买数量</view>
+            <su-number-box :min="1" :max="state.selectedSku.stock" :step="1"
                            v-model="state.selectedSku.goods_num" @change="onNumberChange($event)" />
-					</view>
-				</scroll-view>
-			</view>
+          </view>
+        </scroll-view>
+      </view>
 
       <!-- 操作区 -->
-			<view class="modal-footer border-top">
-				<view class="buy-box ss-flex ss-col-center ss-flex ss-col-center ss-row-center">
-					<button class="ss-reset-button add-btn ui-Shadow-Main" @tap="onAddCart">加入购物车</button>
-					<button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy">立即购买</button>
-				</view>
-			</view>
-		</view>
-	</su-popup>
+      <view class="modal-footer border-top">
+        <view class="buy-box ss-flex ss-col-center ss-flex ss-col-center ss-row-center">
+          <button class="ss-reset-button add-btn ui-Shadow-Main" @tap="onAddCart">加入购物车</button>
+          <button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy">立即购买</button>
+        </view>
+      </view>
+    </view>
+  </su-popup>
 </template>
 
 <script setup>
-	import { computed, reactive, watch } from 'vue';
-	import sheep from '@/sheep';
-  import { formatStock, convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
-
-	const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
-	const props = defineProps({
-		goodsInfo: {
-			type: Object,
-			default () {},
-		},
-		show: {
-			type: Boolean,
-			default: false,
-		}
-	});
-
-	const state = reactive({
-		selectedSku: {}, // 选中的 SKU
-		currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
-	});
-
-	const propertyList = convertProductPropertyList(props.goodsInfo.skus);
-
-	// SKU 列表
-	const skuList = computed(() => {
-		let skuPrices = props.goodsInfo.skus;
+  import {
+    computed,
+    reactive,
+    watch
+  } from 'vue';
+  import sheep from '@/sheep';
+  import {
+    formatStock,
+    convertProductPropertyList,
+    fen2yuan
+  } from '@/sheep/hooks/useGoods';
+
+  const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
+  const props = defineProps({
+    goodsInfo: {
+      type: Object,
+      default () {},
+    },
+    show: {
+      type: Boolean,
+      default: false,
+    }
+  });
+
+  const state = reactive({
+    selectedSku: {}, // 选中的 SKU
+    currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
+  });
+
+  const propertyList = convertProductPropertyList(props.goodsInfo.skus);
+  // SKU 列表
+  const skuList = computed(() => {
+    let skuPrices = props.goodsInfo.skus;
     for (let price of skuPrices) {
       price.value_id_array = price.properties.map((item) => item.valueId)
     }
-		return skuPrices;
-	});
-
-	watch(
-		() => state.selectedSku,
-		(newVal) => {
-			emits('change', newVal);
-		}, {
-			immediate: true, // 立即执行
-			deep: true, // 深度监听
-		},
-	);
+    return skuPrices;
+  });
+
+  watch(
+    () => state.selectedSku,
+    (newVal) => {
+      emits('change', newVal);
+    }, {
+      immediate: true, // 立即执行
+      deep: true, // 深度监听
+    },
+  );
 
   // 输入框改变数量
   function onNumberChange(e) {
@@ -110,21 +121,21 @@
   }
 
   // 加入购物车
-	function onAddCart() {
-		if (state.selectedSku.id <= 0) {
+  function onAddCart() {
+    if (state.selectedSku.id <= 0) {
       sheep.$helper.toast('请选择规格');
       return;
-		}
+    }
     if (state.selectedSku.stock <= 0) {
       sheep.$helper.toast('库存不足');
       return;
     }
 
     emits('addCart', state.selectedSku);
-	}
+  }
 
   // 立即购买
-	function onBuy() {
+  function onBuy() {
     if (state.selectedSku.id <= 0) {
       sheep.$helper.toast('请选择规格');
       return;
@@ -134,273 +145,296 @@
       return;
     }
     emits('buy', state.selectedSku);
-	}
+  }
 
-	// 改变禁用状态:计算每个 property 属性值的按钮,是否禁用
-	function changeDisabled(isChecked = false, propertyId = 0, valueId = 0) {
+  // 改变禁用状态:计算每个 property 属性值的按钮,是否禁用
+  function changeDisabled(isChecked = false, propertyId = 0, valueId = 0) {
     let newSkus = []; // 所有可以选择的 sku 数组
-		if (isChecked) {
-			// 情况一:选中 property
-			// 获得当前点击选中 property 的、所有可用 SKU
-			for (let price of skuList.value) {
-				if (price.stock <= 0) {
-					continue;
-				}
-				if (price.value_id_array.indexOf(valueId) >= 0) {
-					newSkus.push(price);
-				}
-			}
-		} else {
-			// 情况二:取消选中 property
-			// 当前所选 property 下,所有可以选择的 SKU
-			newSkus = getCanUseSkuList();
-		}
-
-		// 所有存在并且有库存未选择的 SKU 的 value 属性值 id
-		let noChooseValueIds = [];
-		for (let price of newSkus) {
-			noChooseValueIds = noChooseValueIds.concat(price.value_id_array);
-		}
-		noChooseValueIds = Array.from(new Set(noChooseValueIds)); // 去重
-
-		if (isChecked) {
-			// 去除当前选中的 value 属性值 id
-			let index = noChooseValueIds.indexOf(valueId);
-			noChooseValueIds.splice(index, 1);
-		} else {
-			// 循环去除当前已选择的 value 属性值 id
-			state.currentPropertyArray.forEach((currentPropertyId) => {
-				if (currentPropertyId.toString() !== '') {
+    if (isChecked) {
+      // 情况一:选中 property
+      // 获得当前点击选中 property 的、所有可用 SKU
+      for (let price of skuList.value) {
+        if (price.stock <= 0) {
+          continue;
+        }
+        if (price.value_id_array.indexOf(valueId) >= 0) {
+          newSkus.push(price);
+        }
+      }
+    } else {
+      // 情况二:取消选中 property
+      // 当前所选 property 下,所有可以选择的 SKU
+      newSkus = getCanUseSkuList();
+    }
+
+    // 所有存在并且有库存未选择的 SKU 的 value 属性值 id
+    let noChooseValueIds = [];
+    for (let price of newSkus) {
+      noChooseValueIds = noChooseValueIds.concat(price.value_id_array);
+    }
+    noChooseValueIds = Array.from(new Set(noChooseValueIds)); // 去重
+
+    if (isChecked) {
+      // 去除当前选中的 value 属性值 id
+      let index = noChooseValueIds.indexOf(valueId);
+      noChooseValueIds.splice(index, 1);
+    } else {
+      // 循环去除当前已选择的 value 属性值 id
+      state.currentPropertyArray.forEach((currentPropertyId) => {
+        if (currentPropertyId.toString() !== '') {
           return;
-				}
+        }
         // currentPropertyId 为空是反选 填充的
         let index = noChooseValueIds.indexOf(currentPropertyId);
         if (index >= 0) {
           // currentPropertyId 存在于 noChooseValueIds
           noChooseValueIds.splice(index, 1);
         }
-			});
-		}
+      });
+    }
 
     // 当前已选择的 property 数组
-		let choosePropertyIds = [];
-		if (!isChecked) {
-			// 当前已选择的 property
-			state.currentPropertyArray.forEach((currentPropertyId, currentValueId) => {
-				if (currentPropertyId !== '') {
-					// currentPropertyId 为空是反选 填充的
-					choosePropertyIds.push(currentValueId);
-				}
-			});
-		} else {
-			// 当前点击选择的 property
-			choosePropertyIds = [propertyId];
-		}
+    let choosePropertyIds = [];
+    if (!isChecked) {
+      // 当前已选择的 property
+      state.currentPropertyArray.forEach((currentPropertyId, currentValueId) => {
+        if (currentPropertyId !== '') {
+          // currentPropertyId 为空是反选 填充的
+          choosePropertyIds.push(currentValueId);
+        }
+      });
+    } else {
+      // 当前点击选择的 property
+      choosePropertyIds = [propertyId];
+    }
 
     for (let propertyIndex in propertyList) {
-			// 当前点击的 property、或者取消选择时候,已选中的 property 不进行处理
-			if (choosePropertyIds.indexOf(propertyList[propertyIndex]['id']) >= 0) {
-				continue;
-			}
+      // 当前点击的 property、或者取消选择时候,已选中的 property 不进行处理
+      if (choosePropertyIds.indexOf(propertyList[propertyIndex]['id']) >= 0) {
+        continue;
+      }
       // 如果当前 property id 不存在于有库存的 SKU 中,则禁用
       for (let valueIndex in propertyList[propertyIndex]['values']) {
-				propertyList[propertyIndex]['values'][valueIndex]['disabled'] =
-          noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) < 0; // true 禁用 or false 不禁用
-			}
-		}
-	}
-
-	// 当前所选属性下,获取所有有库存的 SKU 们
-	function getCanUseSkuList() {
-		let newSkus = [];
-		for (let sku of skuList.value) {
-			if (sku.stock <= 0) {
-				continue;
-			}
+        propertyList[propertyIndex]['values'][valueIndex]['disabled'] =
+          noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) <
+          0; // true 禁用 or false 不禁用
+      }
+    }
+  }
+
+  // 当前所选属性下,获取所有有库存的 SKU 们
+  function getCanUseSkuList() {
+    let newSkus = [];
+    for (let sku of skuList.value) {
+      if (sku.stock <= 0) {
+        continue;
+      }
       let isOk = true;
       state.currentPropertyArray.forEach((propertyId) => {
-				// propertyId 不为空,并且,这个 条 sku 没有被选中,则排除
-				if (propertyId.toString() !== '' && sku.value_id_array.indexOf(propertyId) < 0) {
-					isOk = false;
-				}
-			});
-			if (isOk) {
-				newSkus.push(sku);
-			}
-		}
-		return newSkus;
-	}
-
-	// 选择规格
-	function onSelectSku(propertyId, valueId) {
-		// 清空已选择
-		let isChecked = true; // 选中 or 取消选中
-		if (state.currentPropertyArray[propertyId] !== undefined && state.currentPropertyArray[propertyId] === valueId) {
-			// 点击已被选中的,删除并填充 ''
-			isChecked = false;
-			state.currentPropertyArray.splice(propertyId, 1, '');
-		} else {
-			// 选中
-			state.currentPropertyArray[propertyId] = valueId;
-		}
+        // propertyId 不为空,并且,这个 条 sku 没有被选中,则排除
+        if (propertyId.toString() !== '' && sku.value_id_array.indexOf(propertyId) < 0) {
+          isOk = false;
+        }
+      });
+      if (isOk) {
+        newSkus.push(sku);
+      }
+    }
+    return newSkus;
+  }
+
+  // 选择规格
+  function onSelectSku(propertyId, valueId) {
+    // 清空已选择
+    let isChecked = true; // 选中 or 取消选中
+    if (state.currentPropertyArray[propertyId] !== undefined && state.currentPropertyArray[propertyId] === valueId) {
+      // 点击已被选中的,删除并填充 ''
+      isChecked = false;
+      state.currentPropertyArray.splice(propertyId, 1, '');
+    } else {
+      // 选中
+      state.currentPropertyArray[propertyId] = valueId;
+    }
 
     // 选中的 property 大类
-		let choosePropertyId = [];
-		state.currentPropertyArray.forEach((currentPropertyId) => {
-			if (currentPropertyId !== '') {
-				// currentPropertyId 为空是反选 填充的
-				choosePropertyId.push(currentPropertyId);
-			}
-		});
-
-		// 当前所选 property 下,所有可以选择的 SKU 们
-		let newSkuList = getCanUseSkuList();
-
-		// 判断所有 property 大类是否选择完成
-		if (choosePropertyId.length === propertyList.length && newSkuList.length) {
-			newSkuList[0].goods_num = state.selectedSku.goods_num || 1;
-			state.selectedSku = newSkuList[0];
-		} else {
-			state.selectedSku = {};
-		}
-
-		// 改变 property 禁用状态
-		changeDisabled(isChecked, propertyId, valueId);
-	}
-
-	changeDisabled(false);
+    let choosePropertyId = [];
+    state.currentPropertyArray.forEach((currentPropertyId) => {
+      if (currentPropertyId !== '') {
+        // currentPropertyId 为空是反选 填充的
+        choosePropertyId.push(currentPropertyId);
+      }
+    });
+
+    // 当前所选 property 下,所有可以选择的 SKU 们
+    let newSkuList = getCanUseSkuList();
+
+    // 判断所有 property 大类是否选择完成
+    if (choosePropertyId.length === propertyList.length && newSkuList.length) {
+      newSkuList[0].goods_num = state.selectedSku.goods_num || 1;
+      state.selectedSku = newSkuList[0];
+    } else {
+      state.selectedSku = {};
+    }
+
+    // 改变 property 禁用状态
+    changeDisabled(isChecked, propertyId, valueId);
+  }
+
+  changeDisabled(false);
   // TODO 芋艿:待讨论的优化点:1)单规格,要不要默认选中;2)默认要不要选中第一个规格
 </script>
 
 <style lang="scss" scoped>
-	// 购买
-	.buy-box {
-		padding: 10rpx 0;
-
-		.add-btn {
-			width: 356rpx;
-			height: 80rpx;
-			border-radius: 40rpx 0 0 40rpx;
-			background-color: var(--ui-BG-Main-light);
-			color: var(--ui-BG-Main);
-		}
-
-		.buy-btn {
-			width: 356rpx;
-			height: 80rpx;
-			border-radius: 0 40rpx 40rpx 0;
-			background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-			color: #fff;
-		}
-
-		.score-btn {
-			width: 100%;
-			margin: 0 20rpx;
-			height: 80rpx;
-			border-radius: 40rpx;
-			background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-			color: #fff;
-		}
-	}
-
-	.ss-modal-box {
-		border-radius: 30rpx 30rpx 0 0;
-		max-height: 1000rpx;
-
-		.modal-header {
-			position: relative;
-			padding: 80rpx 20rpx 40rpx;
-
-			.sku-image {
-				width: 160rpx;
-				height: 160rpx;
-				border-radius: 10rpx;
-			}
-
-			.header-right {
-				height: 160rpx;
-			}
-
-			.close-icon {
-				position: absolute;
-				top: 10rpx;
-				right: 20rpx;
-				font-size: 46rpx;
-				opacity: 0.2;
-			}
-
-			.goods-title {
-				font-size: 28rpx;
-				font-weight: 500;
-				line-height: 42rpx;
-			}
-
-			.score-img {
-				width: 36rpx;
-				height: 36rpx;
-				margin: 0 4rpx;
-			}
-
-			.score-text {
-				font-size: 30rpx;
-				font-weight: 500;
-				color: $red;
-				font-family: OPPOSANS;
-			}
-
-			.price-text {
-				font-size: 30rpx;
-				font-weight: 500;
-				color: $red;
-				font-family: OPPOSANS;
-
-				&::before {
-					content: '¥';
-					font-size: 30rpx;
-					font-weight: 500;
-					color: $red;
-				}
-			}
-
-			.stock-text {
-				font-size: 26rpx;
-				color: #999999;
-			}
-		}
-
-		.modal-content {
-			padding: 0 20rpx;
-
-			.modal-content-scroll {
-				max-height: 600rpx;
-
-				.label-text {
-					font-size: 26rpx;
-					font-weight: 500;
-				}
-
-				.buy-num-box {
-					height: 100rpx;
-				}
-
-				.spec-btn {
-					height: 60rpx;
-					min-width: 100rpx;
-					padding: 0 30rpx;
-					background: #f4f4f4;
-					border-radius: 30rpx;
-					color: #434343;
-					font-size: 26rpx;
-					margin-right: 10rpx;
-					margin-bottom: 10rpx;
-				}
-
-				.disabled-btn {
-					font-weight: 400;
-					color: #c6c6c6;
-					background: #f8f8f8;
-				}
-			}
-		}
-	}
+  // 购买
+  .buy-box {
+    padding: 10rpx 0;
+
+    .add-btn {
+      width: 356rpx;
+      height: 80rpx;
+      border-radius: 40rpx 0 0 40rpx;
+      background-color: var(--ui-BG-Main-light);
+      color: var(--ui-BG-Main);
+    }
+
+    .buy-btn {
+      width: 356rpx;
+      height: 80rpx;
+      border-radius: 0 40rpx 40rpx 0;
+      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+      color: #fff;
+    }
+
+    .score-btn {
+      width: 100%;
+      margin: 0 20rpx;
+      height: 80rpx;
+      border-radius: 40rpx;
+      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+      color: #fff;
+    }
+  }
+
+  .ss-modal-box {
+    border-radius: 30rpx 30rpx 0 0;
+    max-height: 1000rpx;
+
+    .modal-header {
+      position: relative;
+      padding: 80rpx 20rpx 40rpx;
+
+      .sku-image {
+        width: 160rpx;
+        height: 160rpx;
+        border-radius: 10rpx;
+      }
+
+      .header-right {
+        height: 160rpx;
+      }
+
+      .close-icon {
+        position: absolute;
+        top: 10rpx;
+        right: 20rpx;
+        font-size: 46rpx;
+        opacity: 0.2;
+      }
+
+      .goods-title {
+        font-size: 28rpx;
+        font-weight: 500;
+        line-height: 42rpx;
+      }
+
+      .score-img {
+        width: 36rpx;
+        height: 36rpx;
+        margin: 0 4rpx;
+      }
+
+      .score-text {
+        font-size: 30rpx;
+        font-weight: 500;
+        color: $red;
+        font-family: OPPOSANS;
+      }
+
+      .price-text {
+        font-size: 30rpx;
+        font-weight: 500;
+        color: $red;
+        font-family: OPPOSANS;
+
+        &::before {
+          content: '¥';
+          font-size: 30rpx;
+          font-weight: 500;
+          color: $red;
+        }
+      }
+
+      .stock-text {
+        font-size: 26rpx;
+        color: #999999;
+      }
+    }
+
+    .modal-content {
+      padding: 0 20rpx;
+
+      .modal-content-scroll {
+        max-height: 600rpx;
+
+        .label-text {
+          font-size: 26rpx;
+          font-weight: 500;
+        }
+
+        .buy-num-box {
+          height: 100rpx;
+        }
+
+        .spec-btn {
+          height: 60rpx;
+          min-width: 100rpx;
+          padding: 0 30rpx;
+          background: #f4f4f4;
+          border-radius: 30rpx;
+          color: #434343;
+          font-size: 26rpx;
+          margin-right: 10rpx;
+          margin-bottom: 10rpx;
+        }
+
+        .disabled-btn {
+          font-weight: 400;
+          color: #c6c6c6;
+          background: #f8f8f8;
+        }
+      }
+    }
+  }
+
+  .iconBox {
+    width: fit-content;
+    height: fit-content;
+    padding: 2rpx 10rpx;
+    background-color: rgb(255, 242, 241);
+    color: #ff2621;
+    font-size: 24rpx;
+    margin-left: 5rpx;
+  }
+
+  .origin-price-text {
+    font-size: 26rpx;
+    font-weight: 400;
+    text-decoration: line-through;
+    color: $gray-c;
+    font-family: OPPOSANS;
+
+    &::before {
+      content: '¥';
+    }
+  }
 </style>

+ 102 - 6
sheep/hooks/useGoods.js

@@ -1,7 +1,11 @@
-import { ref } from 'vue';
+import {
+  ref
+} from 'vue';
 import dayjs from 'dayjs';
 import $url from '@/sheep/url';
-import { formatDate } from '@/sheep/util';
+import {
+  formatDate
+} from '@/sheep/util';
 
 /**
  * 格式化销量
@@ -80,7 +84,10 @@ export function formatGoodsSwiper(urlList) {
     const isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
     const type = isVideo ? 'video' : 'image'
     const src = $url.cdn(url);
-    return { type, src }
+    return {
+      type,
+      src
+    }
   }) || [];
 }
 
@@ -94,9 +101,9 @@ export function formatOrderColor(order) {
   if (order.status === 0) {
     return 'info-color';
   }
-  if (order.status === 10
-    || order.status === 20
-    || (order.status === 30 && !order.commentStatus)) {
+  if (order.status === 10 ||
+    order.status === 20 ||
+    (order.status === 30 && !order.commentStatus)) {
     return 'warning-color';
   }
   if (order.status === 30 && order.commentStatus) {
@@ -387,3 +394,92 @@ export function formatRewardActivityRule(activity, rule) {
   }
   return '';
 }
+// 新增将时间搓转换为开始时间-结束时间的格式
+export function formatDateRange(startTimestamp, endTimestamp) {
+  // 定义一个辅助函数来格式化时间戳为 YYYY.MM.DD 格式
+  const formatDate = (timestamp) => {
+    const date = new Date(timestamp);
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,所以需要+1
+    const day = String(date.getDate()).padStart(2, '0');
+    return `${year}.${month}.${day}`;
+  };
+
+  // 格式化开始和结束时间
+  const start = formatDate(startTimestamp);
+  const end = formatDate(endTimestamp);
+
+  // 返回格式化的日期范围
+  return `${start}-${end}`;
+}
+
+//处理活动信息
+export function handList(orders) {
+  const typeMap = {
+    '1': '秒杀活动',
+    '2': '砍价活动',
+    '3': '拼团活动',
+    '4': '限时折扣',
+    '5': '满减送',
+    '6': '会员折扣',
+    '7': '优惠券',
+    '8': '积分'
+  };
+
+  // 给每个订单对象添加 typeName 属性
+  let updatedOrders = orders.map(order => {
+    return {
+      ...order, // 展开现有的订单对象属性
+      typeName: typeMap[order.type] // 添加 typeName 属性
+    };
+  });
+  return updatedOrders
+};
+//根据skuid来修改价格并添加时间
+export function handListPrice(array,array2) {
+  // 将 array2 转换为一个以 skuId 为键的对象,以便于快速查找
+  const array2Map = array2.reduce((acc, item) => {
+    acc[item.skuId] = { price: item.price, type: item.type,endTime:item.endTime };
+    return acc;
+  }, {});
+
+  // 遍历 array 数组并更新 price 和 type
+  array.forEach(item => {
+    if (array2Map[item.id]) {
+      item.oldPrice = item.price
+      // 如果在 array2Map 中找到了对应的 skuId(即 id)
+      item.price = array2Map[item.id].price;
+      item.type = array2Map[item.id].type;
+      item.endTime = array2Map[item.id].endTime;
+    }
+  });
+
+  // 返回更新后的 array
+  return array;
+};
+
+//处理活动数据
+export function handActitList(rules) {
+  const rules2 = {
+    reduc: rules.map(item => ({
+      discountPrice: item.discountPrice,
+      limit: item.limit,
+      bull: true // 默认为 true
+    })),
+    cou: rules.map(item => ({
+      discountPrice: item.discountPrice,
+      value: item.couponCounts.reduce((acc, count) => acc + count, 0), // 计算 couponCounts 中各项之和
+      bull: item.givePoint // 对应 givePoint
+    })),
+    ship: rules.map(item => ({
+      discountPrice: item.discountPrice,
+      bull: item.freeDelivery // 对应 freeDelivery
+    })),
+    scor: rules.map(item => ({
+      discountPrice: item.discountPrice,
+      value: item.point, // 直接使用 point
+      bull: item.givePoint // 对应 givePoint
+    }))
+  };
+  return rules2
+};

二进制
static/images/dis.png