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

pull/101/head
痴货 2024-09-15 17:32:29 +08:00
parent f7ec1badf7
commit aa571f25f9
16 changed files with 1995 additions and 940 deletions

View File

@ -19,13 +19,9 @@
<view class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"> <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="goods-list-box">
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id"> <view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
<s-goods-column <s-goods-column class="goods-md-box" size="md" :data="item"
class="goods-md-box" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
size="md" @getHeight="mountMasonry($event, 'left')">
:data="item"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'left')"
>
<template v-slot:cart> <template v-slot:cart>
<button class="ss-reset-button cart-btn"> </button> <button class="ss-reset-button cart-btn"> </button>
</template> </template>
@ -34,13 +30,9 @@
</view> </view>
<view class="goods-list-box"> <view class="goods-list-box">
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id"> <view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
<s-goods-column <s-goods-column class="goods-md-box" size="md" :data="item"
class="goods-md-box" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
size="md" @getHeight="mountMasonry($event, 'right')">
:data="item"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'right')"
>
<template v-slot:cart> <template v-slot:cart>
<button class="ss-reset-button cart-btn" /> <button class="ss-reset-button cart-btn" />
</template> </template>
@ -49,23 +41,27 @@
</view> </view>
</view> </view>
<uni-load-more <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" }" @tap="loadMore" />
@tap="loadMore"
/>
</s-layout> </s-layout>
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import {
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; reactive,
toRaw,
ref
} from 'vue';
import {
onLoad,
onReachBottom
} from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash-es'; import _ from 'lodash-es';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity'; 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'; import SpuApi from '@/sheep/api/product/spu';
const state = reactive({ const state = reactive({
@ -106,32 +102,138 @@
// //
async function getList() { async function getList() {
// state.loadStatus = 'loading';
// //
const params = {}; const params = {}
if (state.activityInfo.productScope === 2) { 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) { } else if (state.activityInfo.productScope === 3) {
params.categoryIds = state.activityInfo.productSpuIds.join(','); 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.loadStatus = 'loading';
const { code, data } = await SpuApi.getSpuPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
...params,
});
if (code !== 0) {
return;
}
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(); 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) { async function getActivity(id) {
const { code, data } = await RewardActivityApi.getRewardActivity(id); const {
code,
data
} = await RewardActivityApi.getRewardActivity(id);
if (code === 0) { if (code === 0) {
state.activityInfo = data; state.activityInfo = data;
} }
@ -154,28 +256,32 @@
onLoad(async (options) => { onLoad(async (options) => {
state.activityId = options.activityId; state.activityId = options.activityId;
await getActivity(state.activityId); await getActivity(state.activityId);
await getList(state.activityId); await getList();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.goods-list-box { .goods-list-box {
width: 50%; width: 50%;
box-sizing: border-box; box-sizing: border-box;
.left-list { .left-list {
margin-right: 10rpx; margin-right: 10rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.right-list { .right-list {
margin-left: 10rpx; margin-left: 10rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
} }
.tip-box { .tip-box {
background: #fff0e7; background: #fff0e7;
padding: 20rpx; padding: 20rpx;
width: 100%; width: 100%;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
.activity-left-image { .activity-left-image {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -183,6 +289,7 @@
width: 58rpx; width: 58rpx;
height: 36rpx; height: 36rpx;
} }
.activity-right-image { .activity-right-image {
position: absolute; position: absolute;
top: 0; top: 0;
@ -190,12 +297,14 @@
width: 72rpx; width: 72rpx;
height: 50rpx; height: 50rpx;
} }
.type-text { .type-text {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
color: #ff6000; color: #ff6000;
line-height: 42rpx; line-height: 42rpx;
} }
.tip-content { .tip-content {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
@ -203,4 +312,4 @@
line-height: 42rpx; line-height: 42rpx;
} }
} }
</style> </style>

View File

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

View File

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

View File

@ -7,30 +7,47 @@
<!-- 骨架屏 --> <!-- 骨架屏 -->
<detailSkeleton v-if="state.skeletonLoading" /> <detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 --> <!-- 下架/售罄提醒 -->
<s-empty <s-empty v-else-if="state.goodsInfo === null" text="商品不存在或已下架" icon="/static/soldout-empty.png" showAction
v-else-if="state.goodsInfo === null" actionText="再逛逛" actionUrl="/pages/goods/list" />
text="商品不存在或已下架"
icon="/static/soldout-empty.png"
showAction
actionText="再逛逛"
actionUrl="/pages/goods/list"
/>
<block v-else> <block v-else>
<view class="detail-swiper-selector"> <view class="detail-swiper-selector">
<!-- 商品轮播图 --> <!-- 商品轮播图 -->
<su-swiper <su-swiper class="ss-m-b-14" isPreview :list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
class="ss-m-b-14" otStyle="tag" imageMode="widthFix" dotCur="bg-mask-40" :seizeHeight="750" />
isPreview <!-- 限时折扣 -->
:list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)" <view class="discount" v-if="setShow">
otStyle="tag" <image class="disImg" src="../../static/images/dis.png"></image>
imageMode="widthFix" <view class="discountCont">
dotCur="bg-mask-40" <view class="disContT">
:seizeHeight="750" <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="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-box ss-flex ss-col-bottom">
<view class="price-text ss-m-r-16"> <view class="price-text ss-m-r-16">
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }} {{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
@ -47,23 +64,16 @@
<!-- 满减送/限时折扣活动的提示 --> <!-- 满减送/限时折扣活动的提示 -->
<div class="tag-content"> <div class="tag-content">
<view class="tag-box ss-flex"> <view class="tag-box ss-flex">
<view <view class="tag ss-m-r-10" v-for="promos in state.activityInfo" :key="promos.id"
class="tag ss-m-r-10" @tap="onActivity">
v-for="promos in state.activityInfo"
:key="promos.id"
@tap="onActivity"
>
{{ promos.name }} {{ promos.name }}
</view> </view>
</view> </view>
</div> </div>
<!-- 优惠劵 --> <!-- 优惠劵@tap="state.showModel = true" -->
<view <view class="get-coupon-box ss-flex ss-col-center ss-m-l-20" @tap="onActivity"
class="get-coupon-box ss-flex ss-col-center ss-m-l-20" v-if="state.couponInfo.length">
@tap="state.showModel = true"
v-if="state.couponInfo.length"
>
<view class="discounts-title ss-m-r-8">领券</view> <view class="discounts-title ss-m-r-8">领券</view>
<text class="cicon-forward"></text> <text class="cicon-forward"></text>
</view> </view>
@ -74,51 +84,30 @@
<!-- 功能卡片 --> <!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col"> <view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku <detail-cell-sku v-model="state.selectedSku.goods_sku_text" :sku="state.selectedSku"
v-model="state.selectedSku.goods_sku_text" @tap="state.showSelectSku = true" />
:sku="state.selectedSku"
@tap="state.showSelectSku = true"
/>
</view> </view>
<!-- 规格与数量弹框 --> <!-- 规格与数量弹框 -->
<s-select-sku <s-select-sku :goodsInfo="state.goodsInfo" :show="state.showSelectSku" @addCart="onAddCart"
:goodsInfo="state.goodsInfo" @buy="onBuy" @change="onSkuChange" @close="state.showSelectSku = false" />
:show="state.showSelectSku"
@addCart="onAddCart"
@buy="onBuy"
@change="onSkuChange"
@close="state.showSelectSku = false"
/>
</view> </view>
<!-- 评价 --> <!-- 评价 -->
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" /> <detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
<!-- 详情 --> <!-- 详情 -->
<detail-content-card <detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
class="detail-content-selector"
:content="state.goodsInfo.description"
/>
<!-- 活动跳转拼团/秒杀/砍价活动 --> <!-- 活动跳转拼团/秒杀/砍价活动 -->
<detail-activity-tip <detail-activity-tip v-if="state.activityList.length > 0" :activity-list="state.activityList" />
v-if="state.activityList.length > 0"
:activity-list="state.activityList"
/>
<!-- 详情 tabbar --> <!-- 详情 tabbar -->
<detail-tabbar v-model="state.goodsInfo"> <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"> <view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
<button <button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
class="ss-reset-button add-btn ui-Shadow-Main"
@tap="state.showSelectSku = true"
>
加入购物车 加入购物车
</button> </button>
<button <button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
class="ss-reset-button buy-btn ui-Shadow-Main"
@tap="state.showSelectSku = true"
>
立即购买 立即购买
</button> </button>
</view> </view>
@ -128,32 +117,38 @@
</detail-tabbar> </detail-tabbar>
<!-- 优惠劵弹窗 --> <!-- 优惠劵弹窗 -->
<s-coupon-get <!-- <s-coupon-get v-model="state.couponInfo" :show="state.showModel" @close="state.showModel = false"
v-model="state.couponInfo" @get="onGet" /> -->
:show="state.showModel"
@close="state.showModel = false"
@get="onGet"
/>
<!-- 满减送/限时折扣活动弹窗 --> <!-- 满减送/限时折扣活动弹窗 -->
<s-activity-pop <s-activity-pop v-model="state" :show="state.showActivityModel"
v-model="state.activityInfo" @close="state.showActivityModel = false" @get="onGet" />
:show="state.showActivityModel"
@close="state.showActivityModel = false"
/>
</block> </block>
</s-layout> </s-layout>
</view> </view>
</template> </template>
<script setup> <script setup>
import { reactive, computed } from 'vue'; import {
import { onLoad, onPageScroll } from '@dcloudio/uni-app'; reactive,
computed,
ref
} from 'vue';
import {
onLoad,
onPageScroll
} from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
import ActivityApi from '@/sheep/api/promotion/activity'; import ActivityApi from '@/sheep/api/promotion/activity';
import FavoriteApi from '@/sheep/api/product/favorite'; 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 detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue'; import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue'; import detailTabbar from './components/detail/detail-tabbar.vue';
@ -161,11 +156,22 @@
import detailCommentCard from './components/detail/detail-comment-card.vue'; import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue'; import detailContentCard from './components/detail/detail-content-card.vue';
import detailActivityTip from './components/detail/detail-activity-tip.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'; 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 isLogin = computed(() => sheep.$store('user').isLogin);
const state = reactive({ const state = reactive({
goodsId: 0, goodsId: 0,
@ -182,7 +188,13 @@
// //
function onSkuChange(e) { 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) { function onBuy(e) {
if (!state.selectedSku.id) { if (!e.id) {
sheep.$helper.toast('请选择商品规格'); sheep.$helper.toast('请选择商品规格');
return; return;
} }
sheep.$router.go('/pages/order/confirm', { sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({ data: JSON.stringify({
items: [ items: [{
{ skuId: e.id,
skuId: e.id, count: e.goods_num,
count: e.goods_num, categoryId: state.goodsInfo.categoryId
}, }]
],
// TODO 2
deliveryType: 1,
pointStatus: false,
}), }),
}); });
} }
@ -222,7 +230,9 @@
// //
async function onGet(id) { async function onGet(id) {
const { code } = await CouponApi.takeCoupon(id); const {
code
} = await CouponApi.takeCoupon(id);
if (code !== 0) { if (code !== 0) {
return; return;
} }
@ -237,33 +247,66 @@
// TODO // TODO
const shareInfo = computed(() => { const shareInfo = computed(() => {
if (isEmpty(state.goodsInfo)) return {}; if (isEmpty(state.goodsInfo)) return {};
return sheep.$platform.share.getShareInfo( return sheep.$platform.share.getShareInfo({
{ title: state.goodsInfo.name,
title: state.goodsInfo.name, image: sheep.$url.cdn(state.goodsInfo.picUrl),
image: sheep.$url.cdn(state.goodsInfo.picUrl), desc: state.goodsInfo.introduction,
desc: state.goodsInfo.introduction, params: {
params: { page: '2',
page: '2', query: state.goodsInfo.id,
query: state.goodsInfo.id,
},
}, },
{ }, {
type: 'goods', // type: 'goods', //
title: state.goodsInfo.name, // title: state.goodsInfo.name, //
image: sheep.$url.cdn(state.goodsInfo.picUrl), // image: sheep.$url.cdn(state.goodsInfo.picUrl), //
price: fen2yuan(state.goodsInfo.price), // price: fen2yuan(state.goodsInfo.price), //
original_price: fen2yuan(state.goodsInfo.marketPrice), // original_price: fen2yuan(state.goodsInfo.marketPrice), //
}, });
);
}); });
async function getCoupon() { 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) { if (code === 0) {
state.couponInfo = data; 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; // truesome
}
return false; //
});
});
//
state.goodsInfo.skus.forEach(item => {
if (item.id == setList.skuId) {
setList.stock = item.stock
}
})
return setList
}
onLoad((options) => { onLoad((options) => {
// //
if (!options.id) { if (!options.id) {
@ -281,7 +324,6 @@
// //
state.skeletonLoading = false; state.skeletonLoading = false;
state.goodsInfo = res.data; state.goodsInfo = res.data;
// //
if (isLogin.value) { if (isLogin.value) {
FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => { FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
@ -301,19 +343,19 @@
if (res.code !== 0) { if (res.code !== 0) {
return; return;
} }
res.data.forEach((activity) => { let activData = handList(res.data)
if ([1, 2, 3].includes(activity.type)) { activData.forEach(activity => {
// // if ([1, 2, 3].includes(activity.type)) { // //
state.activityList.push(activity); state.activityList.push(activity);
} else if (activity.type === 5) { } else if (activity.type === 5) { //
//
state.activityInfo.push(activity); state.activityInfo.push(activity);
} else { } else { // TODO
// TODO // console.log('');
console.log('待实现!优先级不高');
} }
}); })
}); });
//
getSettlementByIds(state.goodsId)
}); });
</script> </script>
@ -462,4 +504,101 @@
color: #333333; 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>

View File

@ -118,7 +118,7 @@
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import { reactive,ref } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash-es'; import _ from 'lodash-es';
@ -277,7 +277,15 @@
if (code !== 0) { if (code !== 0) {
return; 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.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
mountMasonry(); mountMasonry();
@ -292,6 +300,55 @@
getList(state.currentSort, state.currentOrder); 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) => { onLoad((options) => {
state.categoryId = options.categoryId; state.categoryId = options.categoryId;
state.keyword = options.keyword; state.keyword = options.keyword;

View File

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

View File

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

View File

@ -1,36 +1,18 @@
<template> <template>
<s-layout title="确认订单"> <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"> <AddressSelection v-model="addressState" @change="getOrderInfo()" />
<s-address-item :item="state.addressInfo" :hasBorderBottom="false">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</s-address-item>
</view>
<!-- 商品信息 --> <!-- 商品信息 -->
<view class="order-card-box ss-m-b-14"> <view class="order-card-box ss-m-b-14">
<s-goods-item <s-goods-item v-for="item in state.orderInfo.items" :key="item.skuId" :img="item.picUrl"
v-for="item in state.orderInfo.items" :title="item.spuName" :skuText="item.properties.map((property) => property.valueName).join(' ')"
:key="item.skuId" :price="item.price" :num="item.count" marginBottom="10" />
: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="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="item-title">订单备注</view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<uni-easyinput <uni-easyinput maxlength="20" placeholder="建议留言前先与商家沟通" v-model="state.orderPayload.remark"
maxlength="20" :inputBorder="false" :clearable="false" />
placeholder="建议留言前先与商家沟通"
v-model="state.orderPayload.remark"
:inputBorder="false"
:clearable="false"
/>
</view> </view>
</view> </view>
</view> </view>
@ -46,67 +28,71 @@
</text> </text>
</view> </view>
</view> </view>
<!-- TODO 芋艿接入积分 --> <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.type === 0">
<view <view class="item-title">积分抵扣</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="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<image {{ state.pointStatus ? '剩余积分' : '当前积分' }}
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')" <image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img" />
class="score-img" <text class="item-value ss-m-r-24">
/> {{ state.pointStatus ? state.orderInfo.totalPoint - state.orderInfo.usePoint : (state.orderInfo.totalPoint || 0) }}
<text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text> </text>
<checkbox-group @change="changeIntegral">
<checkbox :checked='state.pointStatus'
:disabled="!state.orderInfo.totalPoint || state.orderInfo.totalPoint <= 0" />
</checkbox-group>
</view> </view>
</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="item-title">运费</view>
<view class="ss-flex ss-col-center"> <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) }} +{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
</text> </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>
</view> </view>
<!-- 优惠劵只有 type = 0 普通订单非拼团秒杀砍价才可以使用优惠劵 --> <!-- 优惠劵只有 type = 0 普通订单非拼团秒杀砍价才可以使用优惠劵 -->
<view <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.type === 0">
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.type === 0"
>
<view class="item-title">优惠券</view> <view class="item-title">优惠券</view>
<view class="ss-flex ss-col-center" @tap="state.showCoupon = true"> <view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
<text class="item-value text-red" v-if="state.orderPayload.couponId > 0"> <text class="item-value text-red" v-if="state.orderPayload.couponId > 0">
-{{ fen2yuan(state.orderInfo.price.couponPrice) }} -{{ fen2yuan(state.orderInfo.price.couponPrice) }}
</text> </text>
<text <text class="item-value" :class="couponNumber > 0 ? 'text-red' : 'text-disabled'" v-else>
class="item-value"
:class="state.couponInfo.length > 0 ? 'text-red' : 'text-disabled'"
v-else
>
{{ {{
state.couponInfo.length > 0 ? state.couponInfo.length + ' 张可用' : '暂无可用优惠券' couponNumber > 0 ? couponNumber + ' 张可用' : '暂无可用优惠券'
}} }}
</text> </text>
<text class="_icon-forward item-icon" /> <text class="_icon-forward item-icon" />
</view> </view>
</view> </view>
<view <view class="order-item ss-flex ss-col-center ss-row-between"
class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.price.discountPrice > 0">
v-if="state.orderInfo.price.discountPrice > 0"
>
<view class="item-title">活动优惠</view> <view class="item-title">活动优惠</view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center" @tap="state.showDiscount = true">
<!-- @tap="state.showDiscount = true" TODO 芋艿后续要把优惠信息打进去 -->
<text class="item-value text-red"> <text class="item-value text-red">
-{{ fen2yuan(state.orderInfo.price.discountPrice) }} -{{ fen2yuan(state.orderInfo.price.discountPrice) }}
</text> </text>
<text class="_icon-forward item-icon" /> <text class="_icon-forward item-icon" />
</view> </view>
</view> </view>
<view <view class="order-item ss-flex ss-col-center ss-row-between" v-if="state.orderInfo.price.vipPrice > 0">
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="item-title">会员优惠</view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<text class="item-value text-red"> <text class="item-value text-red">
@ -120,24 +106,16 @@
{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }} {{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}
</view> </view>
<view>合计</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>
</view> </view>
<!-- 选择优惠券弹框 --> <!-- 选择优惠券弹框 -->
<s-coupon-select <s-coupon-select v-model="state.couponInfo" :show="state.showCoupon" @confirm="onSelectCoupon"
v-model="state.couponInfo" @close="state.showCoupon = false" />
:show="state.showCoupon"
@confirm="onSelectCoupon"
@close="state.showCoupon = false"
/>
<!-- 满额折扣弹框 TODO 芋艿后续要把优惠信息打进去 --> <!-- 满额折扣弹框 TODO 芋艿后续要把优惠信息打进去 -->
<s-discount-list <s-discount-list v-model="state.orderInfo" :show="state.showDiscount" @close="state.showDiscount = false" />
v-model="state.orderInfo"
:show="state.showDiscount"
@close="state.showDiscount = false"
/>
<!-- 底部 --> <!-- 底部 -->
<su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200"> <su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
@ -147,10 +125,7 @@
{{ fen2yuan(state.orderInfo.price.payPrice) }} {{ fen2yuan(state.orderInfo.price.payPrice) }}
</view> </view>
</view> </view>
<button <button class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main" @tap="onConfirm">
class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main"
@tap="onConfirm"
>
提交订单 提交订单
</button> </button>
</view> </view>
@ -159,14 +134,20 @@
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import {
import { onLoad } from '@dcloudio/uni-app'; reactive,
ref
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import AddressSelection from '@/pages/order/addressSelection.vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash-es';
import OrderApi from '@/sheep/api/trade/order'; import OrderApi from '@/sheep/api/trade/order';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import {
import { WxaSubscribeTemplate } from '@/sheep/util/const'; fen2yuan
} from '@/sheep/hooks/useGoods';
const state = reactive({ const state = reactive({
orderPayload: {}, orderPayload: {},
@ -174,27 +155,30 @@
items: [], // items: [], //
price: {}, // price: {}, //
}, },
addressInfo: {}, //
showCoupon: false, // showCoupon: false, //
couponInfo: [], // couponInfo: [], //
showDiscount: false, // showDiscount: false, //
// ========== ==========
pointStatus: false, //使
}); });
// const addressState = ref({
function onSelectAddress() { addressInfo: {}, //
uni.$once('SELECT_ADDRESS', (e) => { deliveryType: 1, // 1 - 2 -
changeConsignee(e.addressInfo); isPickUp: true, // TODO puhui999:
}); pickUpInfo: {}, //
sheep.$router.go('/pages/user/address/list'); receiverName: '', //
} receiverMobile: '', //
});
// & // ========== ==========
async function changeConsignee(addressInfo = {}) { /**
if (!isEmpty(addressInfo)) { * 使用积分抵扣
state.addressInfo = addressInfo; */
} const changeIntegral = async () => {
state.pointStatus = !state.pointStatus;
await getOrderInfo(); await getOrderInfo();
} };
// //
async function onSelectCoupon(couponId) { async function onSelectCoupon(couponId) {
@ -205,22 +189,46 @@
// //
function onConfirm() { function onConfirm() {
if (!state.addressInfo.id) { if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
sheep.$helper.toast('请选择收货地址'); sheep.$helper.toast('请选择收货地址');
return; 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(); submitOrder();
} }
// & // &
async function submitOrder() { async function submitOrder() {
const { code, data } = await OrderApi.createOrder({ const {
code,
data
} = await OrderApi.createOrder({
items: state.orderPayload.items, items: state.orderPayload.items,
couponId: state.orderPayload.couponId, couponId: state.orderPayload.couponId,
remark: state.orderPayload.remark, remark: state.orderPayload.remark,
addressId: state.addressInfo.id, deliveryType: addressState.value.deliveryType,
deliveryType: 1, // TODO addressId: addressState.value.addressInfo.id, //
pointStatus: false, // TODO pickUpStoreId: addressState.value.pickUpInfo.id, //
receiverName: addressState.value.receiverName, //
receiverMobile: addressState.value.receiverMobile, //
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId, combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId, combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId, seckillActivityId: state.orderPayload.seckillActivityId,
@ -242,29 +250,44 @@
// & // &
async function getOrderInfo() { async function getOrderInfo() {
// //
const { data, code } = await OrderApi.settlementOrder({ const {
data,
code
} = await OrderApi.settlementOrder({
items: state.orderPayload.items, items: state.orderPayload.items,
couponId: state.orderPayload.couponId, couponId: state.orderPayload.couponId,
addressId: state.addressInfo.id, deliveryType: addressState.value.deliveryType,
deliveryType: 1, // TODO addressId: addressState.value.addressInfo.id, //
pointStatus: false, // TODO pickUpStoreId: addressState.value.pickUpInfo.id, //
receiverName: addressState.value.receiverName, //
receiverMobile: addressState.value.receiverMobile, //
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId, combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId, combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId, seckillActivityId: state.orderPayload.seckillActivityId,
}); });
if (code !== 0) { if (code !== 0) {
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 1500)
return; return;
} }
state.orderInfo = data; state.orderInfo = data;
// //
if (state.orderInfo.address) { if (state.orderInfo.address) {
state.addressInfo = state.orderInfo.address; addressState.value.addressInfo = state.orderInfo.address;
} }
} }
// //
let couponNumber = ref(0)
async function getCoupons() { async function getCoupons() {
const { code, data } = await CouponApi.getMatchCouponList( const {
code,
data
} = await CouponApi.getMatchCouponList(
state.orderInfo.price.payPrice, state.orderInfo.price.payPrice,
state.orderInfo.items.map((item) => item.spuId), state.orderInfo.items.map((item) => item.spuId),
state.orderPayload.items.map((item) => item.skuId), state.orderPayload.items.map((item) => item.skuId),
@ -272,6 +295,7 @@
); );
if (code === 0) { if (code === 0) {
state.couponInfo = data; state.couponInfo = data;
couponNumber.value = state.couponInfo.filter(item => item.match).length;
} }
} }
@ -405,4 +429,4 @@
font-size: 36rpx; font-size: 36rpx;
color: #999999; color: #999999;
} }
</style> </style>

View File

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

View File

@ -2,22 +2,100 @@
<template> <template>
<su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose> <su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose>
<view class="model-box"> <view class="model-box">
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">营销活动</view> <view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠</view>
<scroll-view <view v-if="state.activityMap[state.activityInfo[0]?.id]?.reduc">
class="model-content ss-m-t-50" <view class="titleLi">促销</view>
scroll-y <scroll-view class="model-content" scroll-y :scroll-with-animation="false" :enable-back-to-top="true">
:scroll-with-animation="false" <view class="actBox">
:enable-back-to-top="true" <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 v-for="item in state.activityInfo" :key="item.id"> <view class="model-content-title">
<view class="ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(item)"> <view class="contBu">
<view class="model-content-tag ss-flex ss-row-center">满减</view> <text v-for="(item,index) in state.activityMap[state.activityInfo[0]?.id]?.reduc"
<view class="ss-m-l-20 model-content-title ss-flex-1"> :key="index">{{fen2yuan(item.discountPrice)}}元减{{fen2yuan(item.limit)}};</text>
<view class="ss-m-b-24" v-for="rule in state.activityMap[item.id]?.rules" :key="rule"> </view>
{{ formatRewardActivityRule(state.activityMap[item.id], rule) }} <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> </view>
<text class="cicon-forward" /> <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>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@ -26,14 +104,21 @@
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { computed, reactive, watch } from 'vue'; import {
computed,
reactive,
watch
} from 'vue';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity'; import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
import { formatRewardActivityRule } from '@/sheep/hooks/useGoods'; import {
fen2yuan,
formatDateRange,
handActitList
} from '@/sheep/hooks/useGoods';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default() {}, default () {},
}, },
show: { show: {
type: Boolean, type: Boolean,
@ -42,10 +127,10 @@
}); });
const emits = defineEmits(['close']); const emits = defineEmits(['close']);
const state = reactive({ const state = reactive({
activityInfo: computed(() => props.modelValue), activityInfo: computed(() => props.modelValue.activityInfo),
activityMap: {} activityMap: {},
couponInfo: computed(() => props.modelValue.couponInfo)
}); });
watch( watch(
() => props.show, () => props.show,
() => { () => {
@ -56,12 +141,16 @@
if (res.code !== 0) { if (res.code !== 0) {
return; return;
} }
state.activityMap[activity.id] = res.data; state.activityMap[activity.id] = handActitList(res.data.rules);
}) })
}); });
} }
}, },
); );
//
const getBuy = (id) => {
emits('get', id);
};
function onGoodsList(e) { function onGoodsList(e) {
sheep.$router.go('/pages/activity/index', { sheep.$router.go('/pages/activity/index', {
@ -72,34 +161,141 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.model-box { .model-box {
height: 60vh; height: 60vh;
.title { .title {
justify-content: center;
font-size: 36rpx; font-size: 36rpx;
height: 80rpx; height: 80rpx;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
} }
} }
.model-content { .model-content {
height: fit-content;
max-height: 350rpx;
padding: 0 20rpx; padding: 0 20rpx;
box-sizing: border-box; box-sizing: border-box;
margin-top: 20rpx;
.model-content-tag { .model-content-tag {
background: rgba(#ff6911, 0.1); // background: rgba(#ff6911, 0.1);
font-size: 24rpx; font-size: 35rpx;
font-weight: 500; font-weight: 500;
color: #ff6911; color: #ff6911;
line-height: 42rpx; line-height: 150rpx;
width: 68rpx; width: 200rpx;
height: 32rpx; height: 150rpx;
border-radius: 5rpx; text-align: center;
// border-radius: 5rpx;
} }
.model-content-title { .model-content-title {
width: 450rpx;
height: 150rpx;
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
overflow: hidden;
} }
.cicon-forward { .cicon-forward {
font-size: 28rpx; font-size: 28rpx;
color: #999999; 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>

View File

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

View File

@ -135,7 +135,7 @@
/** /**
* 商品卡片 * 商品卡片
*/ */
import { computed, reactive, onMounted } from 'vue'; import { computed, reactive, onMounted, ref } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
@ -227,10 +227,61 @@
return data; 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 () => { 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){ if (layoutType === LayoutTypeEnum.TWO_COL){
// //

View File

@ -2,38 +2,29 @@
<template> <template>
<view class="ss-goods-wrap"> <view class="ss-goods-wrap">
<!-- xs卡片横向紧凑型一行放两个图片左内容右边 --> <!-- xs卡片横向紧凑型一行放两个图片左内容右边 -->
<view <view v-if="size === 'xs'" class="xs-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @tap="onClick">
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"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image <image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit"></image>
class="xs-img-box" <view v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
:src="sheep.$url.cdn(data.image || data.picUrl)" class="xs-goods-content ss-flex-col ss-row-around">
mode="aspectFit" <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="xs-goods-title ss-line-1"
></image> :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
<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 }} {{ data.title || data.name }}
</view> </view>
<view <!-- 这里是新加的会员价和限时优惠 -->
v-if="goodsFields.price?.show" <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
class="xs-goods-price font-OPPOSANS" <view class="card" v-if="iconShow">{{iconShow}}</view>
:style="[{ color: goodsFields.price.color }]" <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> <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> </view>
</view> </view>
@ -43,30 +34,26 @@
<view v-if="tagStyle.show" class="tag-icon-box"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image <image class="sm-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
class="sm-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view <view v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show" class="sm-goods-content" :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
class="sm-goods-content" <view v-if="goodsFields.title?.show || goodsFields.name?.show"
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]" class="sm-goods-title ss-line-1 ss-m-b-16">
>
<view
v-if="goodsFields.title?.show || goodsFields.name?.show"
class="sm-goods-title ss-line-1 ss-m-b-16"
>
{{ data.title || data.name }} {{ data.title || data.name }}
</view> </view>
<view <!-- 这里是新加的会员价和限时优惠 -->
v-if="goodsFields.price?.show" <view class="iconBox" v-if="data.discountPrice || data.vipPrice || data.reward">
class="sm-goods-price font-OPPOSANS" <view class="card" v-if="iconShow">{{iconShow}}</view>
:style="[{ color: goodsFields.price.color }]" <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> <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> </view>
</view> </view>
@ -76,58 +63,43 @@
<view v-if="tagStyle.show" class="tag-icon-box"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image <image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix"></image>
class="md-img-box" <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">
:src="sheep.$url.cdn(data.image || data.picUrl)" <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="md-goods-title ss-line-1"
mode="widthFix" :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]">
></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 }} {{ data.title || data.name }}
</view> </view>
<view <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show" class="md-goods-subtitle ss-m-t-16 ss-line-1"
class="md-goods-subtitle ss-m-t-16 ss-line-1" :style="[{ color: subTitleColor, background: subTitleBackground }]">
:style="[{ color: subTitleColor, background: subTitleBackground }]"
>
{{ data.subtitle || data.introduction }} {{ data.subtitle || data.introduction }}
</view> </view>
<slot name="activity"> <slot name="activity">
<view v-if="data.promos?.length" class="tag-box ss-flex-wrap ss-flex ss-col-center"> <view v-if="data.promos?.length" class="tag-box ss-flex-wrap ss-flex ss-col-center">
<view <view class="activity-tag ss-m-r-10 ss-m-t-16" v-for="item in data.promos" :key="item.id">
class="activity-tag ss-m-r-10 ss-m-t-16"
v-for="item in data.promos"
:key="item.id"
>
{{ item.title }} {{ item.title }}
</view> </view>
</view> </view>
</slot> </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 class="ss-flex ss-col-bottom">
<view <view v-if="goodsFields.price?.show" class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
v-if="goodsFields.price?.show" :style="[{ color: goodsFields.price.color }]">
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> <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 <view v-if="
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) && (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0) (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> <text class="price-unit ss-font-20">{{ priceUnit }}</text>
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view> <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
</view> </view>
@ -146,12 +118,7 @@
</view> </view>
<!-- lg卡片横向型一行放一个图片左内容右边 --> <!-- lg卡片横向型一行放一个图片左内容右边 -->
<view <view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @tap="onClick">
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"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
@ -159,25 +126,16 @@
<view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center"> <view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center">
<view class="tag-icon">拼团</view> <view class="tag-icon">拼团</view>
</view> </view>
<image <image class="lg-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></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 class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
<view> <view>
<view <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="lg-goods-title ss-line-2"
v-if="goodsFields.title?.show || goodsFields.name?.show" :style="[{ color: titleColor }]">
class="lg-goods-title ss-line-2"
:style="[{ color: titleColor }]"
>
{{ data.title || data.name }} {{ data.title || data.name }}
</view> </view>
<view <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show" class="lg-goods-subtitle ss-m-t-10 ss-line-1"
class="lg-goods-subtitle ss-m-t-10 ss-line-1" :style="[{ color: subTitleColor, background: subTitleBackground }]">
:style="[{ color: subTitleColor, background: subTitleBackground }]"
>
{{ data.subtitle || data.introduction }} {{ data.subtitle || data.introduction }}
</view> </view>
</view> </view>
@ -189,25 +147,27 @@
</view> </view>
</view> </view>
</slot> </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 class="ss-flex ss-col-bottom ss-m-t-10">
<view <view v-if="goodsFields.price?.show"
v-if="goodsFields.price?.show" class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS" :style="[{ color: goodsFields.price.color }]">
:style="[{ color: goodsFields.price.color }]"
>
<text class="ss-font-24">{{ priceUnit }}</text> <text class="ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }} {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</view> </view>
<view <view v-if="
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) && (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0) (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> <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> </view>
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap"> <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> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image <image class="sl-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
class="sl-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view class="sl-goods-content"> <view class="sl-goods-content">
<view> <view>
<view <view v-if="goodsFields.title?.show || goodsFields.name?.show" class="sl-goods-title ss-line-1"
v-if="goodsFields.title?.show || goodsFields.name?.show" :style="[{ color: titleColor }]">
class="sl-goods-title ss-line-1"
:style="[{ color: titleColor }]"
>
{{ data.title || data.name }} {{ data.title || data.name }}
</view> </view>
<view <view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show" class="sl-goods-subtitle ss-m-t-16"
class="sl-goods-subtitle ss-m-t-16" :style="[{ color: subTitleColor, background: subTitleBackground }]">
:style="[{ color: subTitleColor, background: subTitleBackground }]"
>
{{ data.subtitle || data.introduction }} {{ data.subtitle || data.introduction }}
</view> </view>
</view> </view>
<view> <view>
<slot name="activity"> <slot name="activity">
<view v-if="data.promos?.length" class="tag-box ss-flex ss-col-center ss-flex-wrap"> <view v-if="data.promos?.length" class="tag-box ss-flex ss-col-center ss-flex-wrap">
<view <view class="activity-tag ss-m-r-10 ss-m-t-16" v-for="item in data.promos" :key="item.id">
class="activity-tag ss-m-r-10 ss-m-t-16"
v-for="item in data.promos"
:key="item.id"
>
{{ item.title }} {{ item.title }}
</view> </view>
</view> </view>
</slot> </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 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 }]"> <view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
<text class="price-unit ss-font-24">{{ priceUnit }}</text> <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 <view v-if="
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) && (goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0) (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> <text class="price-unit ss-font-20">{{ priceUnit }}</text>
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view> <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
</view> </view>
@ -285,9 +236,9 @@
</view> </view>
</view> </view>
<slot name="cart" <slot name="cart">
><view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view></slot <view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view>
> </slot>
</view> </view>
</view> </view>
</template> </template>
@ -321,13 +272,26 @@
* @event {Function()} click - 点击卡片 * @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 sheep from '@/sheep';
import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods'; import {
import { formatStock } from '@/sheep/hooks/useGoods'; fen2yuan,
formatSales
} from '@/sheep/hooks/useGoods';
import {
formatStock
} from '@/sheep/hooks/useGoods';
import goodsCollectVue from '@/pages/user/goods-collect.vue'; import goodsCollectVue from '@/pages/user/goods-collect.vue';
import { isArray } from 'lodash-es'; import {
isArray
} from 'lodash-es';
// //
const state = reactive({}); const state = reactive({});
@ -335,20 +299,32 @@
const props = defineProps({ const props = defineProps({
goodsFields: { goodsFields: {
type: [Array, Object], type: [Array, Object],
default() { default () {
return { 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, 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(() => { const elStyles = computed(() => {
return { return {
@ -449,12 +443,18 @@
}; };
// //
const { proxy } = getCurrentInstance(); const {
proxy
} = getCurrentInstance();
const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`; const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
function getGoodsPriceCardWH() { function getGoodsPriceCardWH() {
if (props.size === 'md') { if (props.size === 'md') {
const view = uni.createSelectorQuery().in(proxy); 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) => { view.exec((data) => {
let totalHeight = 0; let totalHeight = 0;
const goodsPriceCard = data[0]; const goodsPriceCard = data[0];
@ -482,11 +482,13 @@
left: 0; left: 0;
top: 0; top: 0;
z-index: 2; z-index: 2;
.tag-icon { .tag-icon {
width: 72rpx; width: 72rpx;
height: 44rpx; height: 44rpx;
} }
} }
.seckill-tag { .seckill-tag {
position: absolute; position: absolute;
left: 0; left: 0;
@ -501,6 +503,7 @@
color: #ffffff; color: #ffffff;
line-height: 32rpx; line-height: 32rpx;
} }
.groupon-tag { .groupon-tag {
position: absolute; position: absolute;
left: 0; left: 0;
@ -515,14 +518,17 @@
color: #ffffff; color: #ffffff;
line-height: 32rpx; line-height: 32rpx;
} }
.goods-img { .goods-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.price-unit { .price-unit {
margin-right: -4px; margin-right: -4px;
} }
.sales-text { .sales-text {
display: table; display: table;
font-size: 24rpx; font-size: 24rpx;
@ -586,10 +592,12 @@
width: 100%; width: 100%;
height: 208rpx; height: 208rpx;
} }
.sm-goods-content { .sm-goods-content {
padding: 20rpx 16rpx; padding: 20rpx 16rpx;
box-sizing: border-box; box-sizing: border-box;
} }
.sm-goods-title { .sm-goods-title {
font-size: 26rpx; font-size: 26rpx;
color: #333; color: #333;
@ -619,6 +627,7 @@
color: #333; color: #333;
width: 100%; width: 100%;
} }
.md-goods-subtitle { .md-goods-subtitle {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
@ -669,6 +678,7 @@
// line-height: 36rpx; // line-height: 36rpx;
// width: 410rpx; // width: 410rpx;
} }
.lg-goods-subtitle { .lg-goods-subtitle {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
@ -695,6 +705,7 @@
font-size: 24rpx; font-size: 24rpx;
color: #ffffff; color: #ffffff;
} }
.tag-box { .tag-box {
width: 100%; width: 100%;
} }
@ -708,10 +719,12 @@
z-index: 1; z-index: 1;
width: 100%; width: 100%;
background-color: $white; background-color: $white;
.sl-goods-content { .sl-goods-content {
padding: 20rpx 20rpx; padding: 20rpx 20rpx;
box-sizing: border-box; box-sizing: border-box;
} }
.sl-img-box { .sl-img-box {
width: 100%; width: 100%;
height: 360rpx; height: 360rpx;
@ -722,6 +735,7 @@
color: #333; color: #333;
font-weight: 500; font-weight: 500;
} }
.sl-goods-subtitle { .sl-goods-subtitle {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
@ -748,4 +762,31 @@
color: #ffffff; 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>

View File

@ -1,34 +1,38 @@
<template> <template>
<!-- 规格弹窗 --> <!-- 规格弹窗 -->
<su-popup :show="show" round="10" @close="emits('close')"> <su-popup :show="show" round="10" @close="emits('close')">
<!-- SKU 信息 --> <!-- SKU 信息 -->
<view class="ss-modal-box bg-white ss-flex-col"> <view class="ss-modal-box bg-white ss-flex-col">
<view class="modal-header ss-flex ss-col-center"> <view class="modal-header ss-flex ss-col-center">
<view class="header-left ss-m-r-30"> <view class="header-left ss-m-r-30">
<image class="sku-image" :src="state.selectedSku.picUrl || goodsInfo.picUrl" mode="aspectFill" /> <image class="sku-image" :src="state.selectedSku.picUrl || goodsInfo.picUrl" mode="aspectFill" />
</view> </view>
<view class="header-right ss-flex-col ss-row-between ss-flex-1"> <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="goods-title ss-line-2">{{ goodsInfo.name }}</view>
<view class="header-right-bottom ss-flex ss-col-center ss-row-between"> <view class="header-right-bottom ss-flex ss-col-center ss-row-between">
<view class="ss-flex"> <view class="ss-flex">
<view class="price-text"> <view class="price-text">
{{ fen2yuan( state.selectedSku.price || goodsInfo.price) }} {{ fen2yuan( state.selectedSku.price || goodsInfo.price) }}
</view> <text v-if="state.selectedSku.type == 6"><text class="iconBox"></text><text
</view> class="origin-price-text">{{fen2yuan(state.selectedSku.oldPrice)}}</text></text>
<view class="stock-text ss-m-l-20"> <text v-if="state.selectedSku.type == 4"><text class="iconBox"></text><text
{{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }} class="origin-price-text">{{fen2yuan(state.selectedSku.oldPrice)}}</text></text>
</view> </view>
</view> </view>
</view> <view class="stock-text ss-m-l-20">
</view> {{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}
</view>
</view>
</view>
</view>
<!-- 属性选择 --> <!-- 属性选择 -->
<view class="modal-content ss-flex-1"> <view class="modal-content ss-flex-1">
<scroll-view scroll-y="true" class="modal-content-scroll" @touchmove.stop> <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="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="label-text ss-m-b-20">{{ property.name }}</view>
<view class="ss-flex ss-col-center ss-flex-wrap"> <view class="ss-flex ss-col-center ss-flex-wrap">
<button class="ss-reset-button spec-btn" v-for="value in property.values" :class="[ <button class="ss-reset-button spec-btn" v-for="value in property.values" :class="[
{ {
'ui-BG-Main-Gradient': state.currentPropertyArray[property.id] === value.id, 'ui-BG-Main-Gradient': state.currentPropertyArray[property.id] === value.id,
}, },
@ -36,71 +40,78 @@
'disabled-btn': value.disabled === true, 'disabled-btn': value.disabled === true,
}, },
]" :key="value.id" :disabled="value.disabled === true" @tap="onSelectSku(property.id, value.id)"> ]" :key="value.id" :disabled="value.disabled === true" @tap="onSelectSku(property.id, value.id)">
{{ value.name }} {{ value.name }}
</button> </button>
</view> </view>
</view> </view>
<view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40"> <view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40">
<view class="label-text">购买数量</view> <view class="label-text">购买数量</view>
<su-number-box :min="1" :max="state.selectedSku.stock" :step="1" <su-number-box :min="1" :max="state.selectedSku.stock" :step="1"
v-model="state.selectedSku.goods_num" @change="onNumberChange($event)" /> v-model="state.selectedSku.goods_num" @change="onNumberChange($event)" />
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<!-- 操作区 --> <!-- 操作区 -->
<view class="modal-footer border-top"> <view class="modal-footer border-top">
<view class="buy-box ss-flex ss-col-center ss-flex ss-col-center ss-row-center"> <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 add-btn ui-Shadow-Main" @tap="onAddCart"></button>
<button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy"></button> <button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy"></button>
</view> </view>
</view> </view>
</view> </view>
</su-popup> </su-popup>
</template> </template>
<script setup> <script setup>
import { computed, reactive, watch } from 'vue'; import {
import sheep from '@/sheep'; computed,
import { formatStock, convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods'; reactive,
watch
} from 'vue';
import sheep from '@/sheep';
import {
formatStock,
convertProductPropertyList,
fen2yuan
} from '@/sheep/hooks/useGoods';
const emits = defineEmits(['change', 'addCart', 'buy', 'close']); const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
const props = defineProps({ const props = defineProps({
goodsInfo: { goodsInfo: {
type: Object, type: Object,
default () {}, default () {},
}, },
show: { show: {
type: Boolean, type: Boolean,
default: false, default: false,
} }
}); });
const state = reactive({ const state = reactive({
selectedSku: {}, // SKU selectedSku: {}, // SKU
currentPropertyArray: [], // Mapkey property value value currentPropertyArray: [], // Mapkey property value value
}); });
const propertyList = convertProductPropertyList(props.goodsInfo.skus); const propertyList = convertProductPropertyList(props.goodsInfo.skus);
// SKU
// SKU const skuList = computed(() => {
const skuList = computed(() => { let skuPrices = props.goodsInfo.skus;
let skuPrices = props.goodsInfo.skus;
for (let price of skuPrices) { for (let price of skuPrices) {
price.value_id_array = price.properties.map((item) => item.valueId) price.value_id_array = price.properties.map((item) => item.valueId)
} }
return skuPrices; return skuPrices;
}); });
watch( watch(
() => state.selectedSku, () => state.selectedSku,
(newVal) => { (newVal) => {
emits('change', newVal); emits('change', newVal);
}, { }, {
immediate: true, // immediate: true, //
deep: true, // deep: true, //
}, },
); );
// //
function onNumberChange(e) { function onNumberChange(e) {
@ -110,21 +121,21 @@
} }
// //
function onAddCart() { function onAddCart() {
if (state.selectedSku.id <= 0) { if (state.selectedSku.id <= 0) {
sheep.$helper.toast('请选择规格'); sheep.$helper.toast('请选择规格');
return; return;
} }
if (state.selectedSku.stock <= 0) { if (state.selectedSku.stock <= 0) {
sheep.$helper.toast('库存不足'); sheep.$helper.toast('库存不足');
return; return;
} }
emits('addCart', state.selectedSku); emits('addCart', state.selectedSku);
} }
// //
function onBuy() { function onBuy() {
if (state.selectedSku.id <= 0) { if (state.selectedSku.id <= 0) {
sheep.$helper.toast('请选择规格'); sheep.$helper.toast('请选择规格');
return; return;
@ -134,273 +145,296 @@
return; return;
} }
emits('buy', state.selectedSku); emits('buy', state.selectedSku);
} }
// property // property
function changeDisabled(isChecked = false, propertyId = 0, valueId = 0) { function changeDisabled(isChecked = false, propertyId = 0, valueId = 0) {
let newSkus = []; // sku let newSkus = []; // sku
if (isChecked) { if (isChecked) {
// property // property
// property SKU // property SKU
for (let price of skuList.value) { for (let price of skuList.value) {
if (price.stock <= 0) { if (price.stock <= 0) {
continue; continue;
} }
if (price.value_id_array.indexOf(valueId) >= 0) { if (price.value_id_array.indexOf(valueId) >= 0) {
newSkus.push(price); newSkus.push(price);
} }
} }
} else { } else {
// property // property
// property SKU // property SKU
newSkus = getCanUseSkuList(); newSkus = getCanUseSkuList();
} }
// SKU value id // SKU value id
let noChooseValueIds = []; let noChooseValueIds = [];
for (let price of newSkus) { for (let price of newSkus) {
noChooseValueIds = noChooseValueIds.concat(price.value_id_array); noChooseValueIds = noChooseValueIds.concat(price.value_id_array);
} }
noChooseValueIds = Array.from(new Set(noChooseValueIds)); // noChooseValueIds = Array.from(new Set(noChooseValueIds)); //
if (isChecked) { if (isChecked) {
// value id // value id
let index = noChooseValueIds.indexOf(valueId); let index = noChooseValueIds.indexOf(valueId);
noChooseValueIds.splice(index, 1); noChooseValueIds.splice(index, 1);
} else { } else {
// value id // value id
state.currentPropertyArray.forEach((currentPropertyId) => { state.currentPropertyArray.forEach((currentPropertyId) => {
if (currentPropertyId.toString() !== '') { if (currentPropertyId.toString() !== '') {
return; return;
} }
// currentPropertyId // currentPropertyId
let index = noChooseValueIds.indexOf(currentPropertyId); let index = noChooseValueIds.indexOf(currentPropertyId);
if (index >= 0) { if (index >= 0) {
// currentPropertyId noChooseValueIds // currentPropertyId noChooseValueIds
noChooseValueIds.splice(index, 1); noChooseValueIds.splice(index, 1);
} }
}); });
} }
// property // property
let choosePropertyIds = []; let choosePropertyIds = [];
if (!isChecked) { if (!isChecked) {
// property // property
state.currentPropertyArray.forEach((currentPropertyId, currentValueId) => { state.currentPropertyArray.forEach((currentPropertyId, currentValueId) => {
if (currentPropertyId !== '') { if (currentPropertyId !== '') {
// currentPropertyId // currentPropertyId
choosePropertyIds.push(currentValueId); choosePropertyIds.push(currentValueId);
} }
}); });
} else { } else {
// property // property
choosePropertyIds = [propertyId]; choosePropertyIds = [propertyId];
} }
for (let propertyIndex in propertyList) { for (let propertyIndex in propertyList) {
// property property // property property
if (choosePropertyIds.indexOf(propertyList[propertyIndex]['id']) >= 0) { if (choosePropertyIds.indexOf(propertyList[propertyIndex]['id']) >= 0) {
continue; continue;
} }
// property id SKU // property id SKU
for (let valueIndex in propertyList[propertyIndex]['values']) { for (let valueIndex in propertyList[propertyIndex]['values']) {
propertyList[propertyIndex]['values'][valueIndex]['disabled'] = propertyList[propertyIndex]['values'][valueIndex]['disabled'] =
noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) < 0; // true or false noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) <
} 0; // true or false
} }
} }
}
// SKU // SKU
function getCanUseSkuList() { function getCanUseSkuList() {
let newSkus = []; let newSkus = [];
for (let sku of skuList.value) { for (let sku of skuList.value) {
if (sku.stock <= 0) { if (sku.stock <= 0) {
continue; continue;
} }
let isOk = true; let isOk = true;
state.currentPropertyArray.forEach((propertyId) => { state.currentPropertyArray.forEach((propertyId) => {
// propertyId sku // propertyId sku
if (propertyId.toString() !== '' && sku.value_id_array.indexOf(propertyId) < 0) { if (propertyId.toString() !== '' && sku.value_id_array.indexOf(propertyId) < 0) {
isOk = false; isOk = false;
} }
}); });
if (isOk) { if (isOk) {
newSkus.push(sku); newSkus.push(sku);
} }
} }
return newSkus; return newSkus;
} }
// //
function onSelectSku(propertyId, valueId) { function onSelectSku(propertyId, valueId) {
// //
let isChecked = true; // or let isChecked = true; // or
if (state.currentPropertyArray[propertyId] !== undefined && state.currentPropertyArray[propertyId] === valueId) { if (state.currentPropertyArray[propertyId] !== undefined && state.currentPropertyArray[propertyId] === valueId) {
// '' // ''
isChecked = false; isChecked = false;
state.currentPropertyArray.splice(propertyId, 1, ''); state.currentPropertyArray.splice(propertyId, 1, '');
} else { } else {
// //
state.currentPropertyArray[propertyId] = valueId; state.currentPropertyArray[propertyId] = valueId;
} }
// property // property
let choosePropertyId = []; let choosePropertyId = [];
state.currentPropertyArray.forEach((currentPropertyId) => { state.currentPropertyArray.forEach((currentPropertyId) => {
if (currentPropertyId !== '') { if (currentPropertyId !== '') {
// currentPropertyId // currentPropertyId
choosePropertyId.push(currentPropertyId); choosePropertyId.push(currentPropertyId);
} }
}); });
// property SKU // property SKU
let newSkuList = getCanUseSkuList(); let newSkuList = getCanUseSkuList();
// property // property
if (choosePropertyId.length === propertyList.length && newSkuList.length) { if (choosePropertyId.length === propertyList.length && newSkuList.length) {
newSkuList[0].goods_num = state.selectedSku.goods_num || 1; newSkuList[0].goods_num = state.selectedSku.goods_num || 1;
state.selectedSku = newSkuList[0]; state.selectedSku = newSkuList[0];
} else { } else {
state.selectedSku = {}; state.selectedSku = {};
} }
// property // property
changeDisabled(isChecked, propertyId, valueId); changeDisabled(isChecked, propertyId, valueId);
} }
changeDisabled(false); changeDisabled(false);
// TODO 12 // TODO 12
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// //
.buy-box { .buy-box {
padding: 10rpx 0; padding: 10rpx 0;
.add-btn { .add-btn {
width: 356rpx; width: 356rpx;
height: 80rpx; height: 80rpx;
border-radius: 40rpx 0 0 40rpx; border-radius: 40rpx 0 0 40rpx;
background-color: var(--ui-BG-Main-light); background-color: var(--ui-BG-Main-light);
color: var(--ui-BG-Main); color: var(--ui-BG-Main);
} }
.buy-btn { .buy-btn {
width: 356rpx; width: 356rpx;
height: 80rpx; height: 80rpx;
border-radius: 0 40rpx 40rpx 0; border-radius: 0 40rpx 40rpx 0;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: #fff; color: #fff;
} }
.score-btn { .score-btn {
width: 100%; width: 100%;
margin: 0 20rpx; margin: 0 20rpx;
height: 80rpx; height: 80rpx;
border-radius: 40rpx; border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: #fff; color: #fff;
} }
} }
.ss-modal-box { .ss-modal-box {
border-radius: 30rpx 30rpx 0 0; border-radius: 30rpx 30rpx 0 0;
max-height: 1000rpx; max-height: 1000rpx;
.modal-header { .modal-header {
position: relative; position: relative;
padding: 80rpx 20rpx 40rpx; padding: 80rpx 20rpx 40rpx;
.sku-image { .sku-image {
width: 160rpx; width: 160rpx;
height: 160rpx; height: 160rpx;
border-radius: 10rpx; border-radius: 10rpx;
} }
.header-right { .header-right {
height: 160rpx; height: 160rpx;
} }
.close-icon { .close-icon {
position: absolute; position: absolute;
top: 10rpx; top: 10rpx;
right: 20rpx; right: 20rpx;
font-size: 46rpx; font-size: 46rpx;
opacity: 0.2; opacity: 0.2;
} }
.goods-title { .goods-title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
line-height: 42rpx; line-height: 42rpx;
} }
.score-img { .score-img {
width: 36rpx; width: 36rpx;
height: 36rpx; height: 36rpx;
margin: 0 4rpx; margin: 0 4rpx;
} }
.score-text { .score-text {
font-size: 30rpx; font-size: 30rpx;
font-weight: 500; font-weight: 500;
color: $red; color: $red;
font-family: OPPOSANS; font-family: OPPOSANS;
} }
.price-text { .price-text {
font-size: 30rpx; font-size: 30rpx;
font-weight: 500; font-weight: 500;
color: $red; color: $red;
font-family: OPPOSANS; font-family: OPPOSANS;
&::before { &::before {
content: '¥'; content: '¥';
font-size: 30rpx; font-size: 30rpx;
font-weight: 500; font-weight: 500;
color: $red; color: $red;
} }
} }
.stock-text { .stock-text {
font-size: 26rpx; font-size: 26rpx;
color: #999999; color: #999999;
} }
} }
.modal-content { .modal-content {
padding: 0 20rpx; padding: 0 20rpx;
.modal-content-scroll { .modal-content-scroll {
max-height: 600rpx; max-height: 600rpx;
.label-text { .label-text {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
} }
.buy-num-box { .buy-num-box {
height: 100rpx; height: 100rpx;
} }
.spec-btn { .spec-btn {
height: 60rpx; height: 60rpx;
min-width: 100rpx; min-width: 100rpx;
padding: 0 30rpx; padding: 0 30rpx;
background: #f4f4f4; background: #f4f4f4;
border-radius: 30rpx; border-radius: 30rpx;
color: #434343; color: #434343;
font-size: 26rpx; font-size: 26rpx;
margin-right: 10rpx; margin-right: 10rpx;
margin-bottom: 10rpx; margin-bottom: 10rpx;
} }
.disabled-btn { .disabled-btn {
font-weight: 400; font-weight: 400;
color: #c6c6c6; color: #c6c6c6;
background: #f8f8f8; 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> </style>

View File

@ -1,7 +1,11 @@
import { ref } from 'vue'; import {
ref
} from 'vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import $url from '@/sheep/url'; 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 isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
const type = isVideo ? 'video' : 'image' const type = isVideo ? 'video' : 'image'
const src = $url.cdn(url); const src = $url.cdn(url);
return { type, src } return {
type,
src
}
}) || []; }) || [];
} }
@ -94,9 +101,9 @@ export function formatOrderColor(order) {
if (order.status === 0) { if (order.status === 0) {
return 'info-color'; return 'info-color';
} }
if (order.status === 10 if (order.status === 10 ||
|| order.status === 20 order.status === 20 ||
|| (order.status === 30 && !order.commentStatus)) { (order.status === 30 && !order.commentStatus)) {
return 'warning-color'; return 'warning-color';
} }
if (order.status === 30 && order.commentStatus) { if (order.status === 30 && order.commentStatus) {
@ -387,3 +394,92 @@ export function formatRewardActivityRule(activity, rule) {
} }
return ''; 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
};

BIN
static/images/dis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB