Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp
commit
25e4686e30
1358
pages.json
1358
pages.json
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
||||||
<view class="type-text ss-flex ss-row-center">满减:</view>
|
<view class="type-text ss-flex ss-row-center">满减:</view>
|
||||||
<view class="ss-flex-1">
|
<view class="ss-flex-1">
|
||||||
<view class="tip-content" v-for="item in state.activityInfo.rules" :key="item">
|
<view class="tip-content" v-for="item in state.activityInfo.rules" :key="item">
|
||||||
{{ formatRewardActivityRule(state.activityInfo, item) }}
|
{{ item.description }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<image class="activity-left-image" src="/static/activity-left.png" />
|
<image class="activity-left-image" src="/static/activity-left.png" />
|
||||||
|
@ -65,8 +65,9 @@
|
||||||
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 SpuApi from '@/sheep/api/product/spu';
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
|
||||||
|
import OrderApi from '@/sheep/api/trade/order';
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
activityId: 0, // 获得编号
|
activityId: 0, // 获得编号
|
||||||
|
@ -123,6 +124,13 @@
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 拼接结算信息(营销)
|
||||||
|
await OrderApi.getSettlementProduct(data.list.map((item) => item.id).join(',')).then((res) => {
|
||||||
|
if (res.code !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appendSettlementProduct(data.list, res.data);
|
||||||
|
});
|
||||||
state.pagination.list = _.concat(state.pagination.list, data.list);
|
state.pagination.list = _.concat(state.pagination.list, data.list);
|
||||||
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';
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<!-- 积分商城:商品列表 -->
|
||||||
|
<template>
|
||||||
|
<s-layout title="积分商城">
|
||||||
|
<view class="ss-p-20">
|
||||||
|
<view v-for="item in state.pagination.data" :key="item.id" class="ss-m-b-20">
|
||||||
|
<s-point-card
|
||||||
|
size="sl"
|
||||||
|
:data="item"
|
||||||
|
priceColor="#FF3000"
|
||||||
|
@tap="sheep.$router.go('/pages/goods/point', { id: item.id })"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<s-empty
|
||||||
|
v-if="state.pagination.total === 0"
|
||||||
|
icon="/static/goods-empty.png"
|
||||||
|
text="暂无积分商品"
|
||||||
|
/>
|
||||||
|
<uni-load-more
|
||||||
|
v-if="state.pagination.total > 0"
|
||||||
|
:status="state.loadStatus"
|
||||||
|
:content-text="{
|
||||||
|
contentdown: '上拉加载更多',
|
||||||
|
}"
|
||||||
|
@tap="loadmore"
|
||||||
|
/>
|
||||||
|
</s-layout>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import sheep from '@/sheep';
|
||||||
|
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
pagination: {
|
||||||
|
data: [],
|
||||||
|
current_page: 1,
|
||||||
|
total: 1,
|
||||||
|
last_page: 1,
|
||||||
|
},
|
||||||
|
loadStatus: '',
|
||||||
|
});
|
||||||
|
async function getData(page = 1, list_rows = 5) {
|
||||||
|
// TODO @puhui999:分页重写成,我们的代码风格。原先的有点复杂
|
||||||
|
state.loadStatus = 'loading';
|
||||||
|
let res = await sheep.$api.app.scoreShop.list({
|
||||||
|
list_rows,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
if (res.error === 0) {
|
||||||
|
let couponlist = _.concat(state.pagination.data, res.data.data);
|
||||||
|
state.pagination = {
|
||||||
|
...res.data,
|
||||||
|
data: couponlist,
|
||||||
|
};
|
||||||
|
if (state.pagination.current_page < state.pagination.last_page) {
|
||||||
|
state.loadStatus = 'more';
|
||||||
|
} else {
|
||||||
|
state.loadStatus = 'noMore';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 加载更多
|
||||||
|
function loadmore() {
|
||||||
|
if (state.loadStatus !== 'noMore') {
|
||||||
|
getData(state.pagination.current_page + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
onReachBottom(() => {
|
||||||
|
loadmore();
|
||||||
|
});
|
||||||
|
onLoad(() => {
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -3,13 +3,20 @@
|
||||||
<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"
|
class="page-bg"
|
||||||
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
|
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
|
||||||
></view>
|
></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-color="rgba(255,255,255,0.6)" indicator-active-color="#fff">
|
indicator-dots="true"
|
||||||
|
autoplay="true"
|
||||||
|
:circular="true"
|
||||||
|
interval="3000"
|
||||||
|
duration="1500"
|
||||||
|
indicator-color="rgba(255,255,255,0.6)"
|
||||||
|
indicator-active-color="#fff"
|
||||||
|
>
|
||||||
<block v-for="(picUrl, index) in activeTimeConfig.sliderPicUrls" :key="index">
|
<block v-for="(picUrl, index) in activeTimeConfig.sliderPicUrls" :key="index">
|
||||||
<swiper-item class="borRadius14">
|
<swiper-item class="borRadius14">
|
||||||
<image :src="picUrl" class="slide-image borRadius14" lazy-load />
|
<image :src="picUrl" class="slide-image borRadius14" lazy-load />
|
||||||
|
@ -22,17 +29,28 @@
|
||||||
<!-- 左侧图标 -->
|
<!-- 左侧图标 -->
|
||||||
<view class="time-icon">
|
<view class="time-icon">
|
||||||
<!-- TODO 芋艿:图片统一维护 -->
|
<!-- TODO 芋艿:图片统一维护 -->
|
||||||
<image class="ss-w-100 ss-h-100" src="http://mall.yudao.iocoder.cn/static/images/priceTag.png" />
|
<image
|
||||||
|
class="ss-w-100 ss-h-100"
|
||||||
|
src="http://mall.yudao.iocoder.cn/static/images/priceTag.png"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view class="time-list" :scroll-into-view="activeTimeElId" scroll-x scroll-with-animation>
|
<scroll-view
|
||||||
<view v-for="(config, index) in timeConfigList" :key="index"
|
class="time-list"
|
||||||
:class="['item', { active: activeTimeIndex === index}]"
|
:scroll-into-view="activeTimeElId"
|
||||||
:id="`timeItem${index}`"
|
scroll-x
|
||||||
@tap="handleChangeTimeConfig(index)">
|
scroll-with-animation
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-for="(config, index) in timeConfigList"
|
||||||
|
:key="index"
|
||||||
|
:class="['item', { active: activeTimeIndex === index }]"
|
||||||
|
:id="`timeItem${index}`"
|
||||||
|
@tap="handleChangeTimeConfig(index, config.id)"
|
||||||
|
>
|
||||||
<!-- 活动起始时间 -->
|
<!-- 活动起始时间 -->
|
||||||
<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>
|
||||||
|
@ -42,7 +60,10 @@
|
||||||
<!-- 活动倒计时 -->
|
<!-- 活动倒计时 -->
|
||||||
<view class="content-header ss-flex-col ss-col-center ss-row-center">
|
<view class="content-header ss-flex-col ss-col-center ss-row-center">
|
||||||
<view class="content-header-box ss-flex ss-row-center">
|
<view class="content-header-box ss-flex ss-row-center">
|
||||||
<view class="countdown-box ss-flex" v-if="activeTimeConfig?.status === TimeStatusEnum.STARTED">
|
<view
|
||||||
|
class="countdown-box ss-flex"
|
||||||
|
v-if="activeTimeConfig?.status === TimeStatusEnum.STARTED"
|
||||||
|
>
|
||||||
<view class="countdown-title ss-m-r-12">距结束</view>
|
<view class="countdown-title ss-m-r-12">距结束</view>
|
||||||
<view class="ss-flex countdown-time">
|
<view class="ss-flex countdown-time">
|
||||||
<view class="ss-flex countdown-h">{{ countDown.h }}</view>
|
<view class="ss-flex countdown-h">{{ countDown.h }}</view>
|
||||||
|
@ -70,19 +91,44 @@
|
||||||
:data="{ ...activity, price: activity.seckillPrice }"
|
:data="{ ...activity, price: activity.seckillPrice }"
|
||||||
:goodsFields="goodsFields"
|
:goodsFields="goodsFields"
|
||||||
:seckillTag="true"
|
: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="[
|
||||||
<span v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED">马上抢</span>
|
'ss-reset-button cart-btn',
|
||||||
<span v-else>已结束</span>
|
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
|
||||||
|
]"
|
||||||
|
v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START"
|
||||||
|
>
|
||||||
|
<span>未开始</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="[
|
||||||
|
'ss-reset-button cart-btn',
|
||||||
|
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
|
||||||
|
]"
|
||||||
|
@click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })"
|
||||||
|
v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED"
|
||||||
|
>
|
||||||
|
<span>马上抢</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="[
|
||||||
|
'ss-reset-button cart-btn',
|
||||||
|
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
|
||||||
|
]"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<span>已结束</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</s-goods-column>
|
</s-goods-column>
|
||||||
|
@ -100,40 +146,51 @@
|
||||||
</s-layout>
|
</s-layout>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {reactive, computed, ref, nextTick} from 'vue';
|
import { reactive, computed, ref, nextTick } 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 { 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 时间段
|
||||||
// 时间段列表
|
// 时间段列表
|
||||||
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;
|
||||||
} else if (now.isAfter(endTime)) {
|
} else if (now.isAfter(endTime)) {
|
||||||
|
@ -142,35 +199,36 @@
|
||||||
config.status = TimeStatusEnum.STARTED;
|
config.status = TimeStatusEnum.STARTED;
|
||||||
activeTimeIndex.value = index;
|
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);
|
||||||
}
|
};
|
||||||
|
|
||||||
// 滚动到指定时间段
|
// 滚动到指定时间段
|
||||||
const activeTimeElId = ref('') // 当前选中的时间段的元素ID
|
const activeTimeElId = ref(''); // 当前选中的时间段的元素ID
|
||||||
const scrollToTimeConfig = (index) => {
|
const scrollToTimeConfig = (index) => {
|
||||||
nextTick(() => activeTimeElId.value = `timeItem${index}`)
|
nextTick(() => (activeTimeElId.value = `timeItem${index}`));
|
||||||
}
|
};
|
||||||
|
|
||||||
// 切换时间段
|
// 切换时间段
|
||||||
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;
|
||||||
activityList.value = []
|
activityPageParams.configId = id;
|
||||||
|
activityList.value = [];
|
||||||
getActivityList();
|
getActivityList();
|
||||||
}
|
};
|
||||||
|
|
||||||
// 倒计时
|
// 倒计时
|
||||||
const countDown = computed(() => {
|
const countDown = computed(() => {
|
||||||
const endTime = activeTimeConfig.value?.endTime
|
const endTime = activeTimeConfig.value?.endTime;
|
||||||
if (endTime) {
|
if (endTime) {
|
||||||
return useDurationTime(`${dayjs().format('YYYY-MM-DD')} ${endTime}`);
|
return useDurationTime(`${dayjs().format('YYYY-MM-DD')} ${endTime}`);
|
||||||
}
|
}
|
||||||
|
@ -182,20 +240,22 @@
|
||||||
|
|
||||||
// 查询活动列表
|
// 查询活动列表
|
||||||
const activityPageParams = reactive({
|
const activityPageParams = reactive({
|
||||||
id: 0, // 时间段 ID
|
configId: 0, // 时间段 ID
|
||||||
pageNo: 1, // 页码
|
pageNo: 1, // 页码
|
||||||
pageSize: 5, // 每页数量
|
pageSize: 5, // 每页数量
|
||||||
})
|
});
|
||||||
const activityTotal = ref(0) // 活动总数
|
const activityTotal = ref(0); // 活动总数
|
||||||
const activityList = ref([]) // 活动列表
|
const activityList = ref([]); // 活动列表
|
||||||
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,
|
||||||
|
);
|
||||||
|
});
|
||||||
activityList.value = activityList.value.concat(...data.list);
|
activityList.value = activityList.value.concat(...data.list);
|
||||||
activityTotal.value = data.total;
|
activityTotal.value = data.total;
|
||||||
|
|
||||||
|
@ -205,7 +265,7 @@
|
||||||
// 加载更多
|
// 加载更多
|
||||||
function loadMore() {
|
function loadMore() {
|
||||||
if (loadStatus.value !== 'noMore') {
|
if (loadStatus.value !== 'noMore') {
|
||||||
activityPageParams.pageNo += 1
|
activityPageParams.pageNo += 1;
|
||||||
getActivityList();
|
getActivityList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +276,7 @@
|
||||||
|
|
||||||
// 页面初始化
|
// 页面初始化
|
||||||
onLoad(async () => {
|
onLoad(async () => {
|
||||||
await getSeckillConfigList()
|
await getSeckillConfigList();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -235,7 +295,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 +307,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 +319,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 +334,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 +368,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 +380,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 +388,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 +405,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 +420,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 +448,7 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 秒杀限量商品数
|
// 秒杀限量商品数
|
||||||
.limit {
|
.limit {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<text v-else>
|
<text v-else>
|
||||||
{{
|
{{
|
||||||
state.coupon.status === 1
|
state.coupon.status === 1
|
||||||
? '立即使用'
|
? '可使用'
|
||||||
: state.coupon.status === 2
|
: state.coupon.status === 2
|
||||||
? '已使用'
|
? '已使用'
|
||||||
: '已过期'
|
: '已过期'
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
}
|
}
|
||||||
state.id = options.id;
|
state.id = options.id;
|
||||||
|
|
||||||
const { code, data } = await OrderApi.getOrder(state.id);
|
const { code, data } = await OrderApi.getOrderDetail(state.id);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
sheep.$helper.toast('无待评价订单');
|
sheep.$helper.toast('无待评价订单');
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,10 +27,60 @@
|
||||||
dotCur="bg-mask-40"
|
dotCur="bg-mask-40"
|
||||||
:seizeHeight="750"
|
:seizeHeight="750"
|
||||||
/>
|
/>
|
||||||
|
<!-- 限时折扣/会员价的优惠信息 -->
|
||||||
|
<view
|
||||||
|
class="discount"
|
||||||
|
v-if="
|
||||||
|
state.settlementSku && state.settlementSku.id && state.settlementSku.promotionPrice
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<image class="disImg" :src="sheep.$url.static('/static/img/shop/goods/dis.png')" />
|
||||||
|
<view class="discountCont">
|
||||||
|
<view class="disContT">
|
||||||
|
<view class="disContT1">
|
||||||
|
<view class="disContT1P">
|
||||||
|
¥{{ fen2yuan(state.settlementSku.promotionPrice) }}
|
||||||
|
</view>
|
||||||
|
<view class="disContT1End">
|
||||||
|
直降¥
|
||||||
|
{{ fen2yuan(state.settlementSku.price - state.settlementSku.promotionPrice) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="disContT2" v-if="state.settlementSku.promotionType === 4">
|
||||||
|
限时折扣
|
||||||
|
</view>
|
||||||
|
<view class="disContT2" v-else-if="state.settlementSku.promotionType === 6">
|
||||||
|
会员折扣
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="disContB">
|
||||||
|
<view class="disContB1">
|
||||||
|
价格:¥{{ fen2yuan(state.settlementSku.price) }} 丨 剩余:
|
||||||
|
{{ state.settlementSku.stock }}
|
||||||
|
</view>
|
||||||
|
<view class="disContB2" v-if="state.settlementSku.promotionEndTime > 0">
|
||||||
|
距结束仅剩
|
||||||
|
<countDown
|
||||||
|
:tipText="' '"
|
||||||
|
:bgColor="bgColor"
|
||||||
|
:dayText="':'"
|
||||||
|
:hourText="':'"
|
||||||
|
:minuteText="':'"
|
||||||
|
:secondText="' '"
|
||||||
|
:datatime="state.settlementSku.promotionEndTime / 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-30 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="!state.settlementSku.promotionPrice"
|
||||||
|
>
|
||||||
<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) }}
|
||||||
|
@ -44,24 +94,39 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="discounts-box ss-flex ss-row-between ss-m-b-28">
|
<view class="discounts-box ss-flex ss-row-between ss-m-b-28">
|
||||||
<!-- 满减送/限时折扣活动的提示 -->
|
<!-- 查看优惠劵的描述 -->
|
||||||
|
<view
|
||||||
|
class="tag ss-m-r-10"
|
||||||
|
v-for="coupon in state.couponInfo.slice(0, 1)"
|
||||||
|
:key="coupon.id"
|
||||||
|
@tap="onOpenActivity"
|
||||||
|
>
|
||||||
|
[劵]满{{ fen2yuanSimple(coupon.usePrice) }}元{{
|
||||||
|
coupon.discountType === 1
|
||||||
|
? '减' + fen2yuanSimple(coupon.discountPrice) + '元'
|
||||||
|
: '打' + formatDiscountPercent(coupon.discountPercent) + '折'
|
||||||
|
}}
|
||||||
|
</view>
|
||||||
|
<!-- 查看满减送的描述 -->
|
||||||
<div class="tag-content">
|
<div class="tag-content">
|
||||||
<view class="tag-box ss-flex">
|
<view class="tag-box ss-flex">
|
||||||
|
<!-- 最多打印 3 条,所以需要扣除优惠劵已打印的 -->
|
||||||
<view
|
<view
|
||||||
|
v-for="item in getRewardActivityRuleItemDescriptions(
|
||||||
|
state.rewardActivity,
|
||||||
|
).slice(0, 3 - state.couponInfo.slice(0, 1).length)"
|
||||||
|
:key="item"
|
||||||
class="tag ss-m-r-10"
|
class="tag ss-m-r-10"
|
||||||
v-for="promos in state.activityInfo"
|
@tap="onOpenActivity"
|
||||||
:key="promos.id"
|
|
||||||
@tap="onActivity"
|
|
||||||
>
|
>
|
||||||
{{ promos.name }}
|
<text>{{ item }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 领取优惠劵的按钮 -->
|
||||||
<!-- 优惠劵 -->
|
|
||||||
<view
|
<view
|
||||||
class="get-coupon-box ss-flex ss-col-center ss-m-l-20"
|
class="get-coupon-box ss-flex ss-col-center ss-m-l-20"
|
||||||
@tap="state.showModel = true"
|
@tap="onOpenActivity"
|
||||||
v-if="state.couponInfo.length"
|
v-if="state.couponInfo.length"
|
||||||
>
|
>
|
||||||
<view class="discounts-title ss-m-r-8">领券</view>
|
<view class="discounts-title ss-m-r-8">领券</view>
|
||||||
|
@ -127,19 +192,12 @@
|
||||||
</view>
|
</view>
|
||||||
</detail-tabbar>
|
</detail-tabbar>
|
||||||
|
|
||||||
<!-- 优惠劵弹窗 -->
|
|
||||||
<s-coupon-get
|
|
||||||
v-model="state.couponInfo"
|
|
||||||
:show="state.showModel"
|
|
||||||
@close="state.showModel = false"
|
|
||||||
@get="onGet"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 满减送/限时折扣活动弹窗 -->
|
<!-- 满减送/限时折扣活动弹窗 -->
|
||||||
<s-activity-pop
|
<s-activity-pop
|
||||||
v-model="state.activityInfo"
|
v-model="state"
|
||||||
:show="state.showActivityModel"
|
:show="state.showActivityModel"
|
||||||
@close="state.showActivityModel = false"
|
@close="state.showActivityModel = false"
|
||||||
|
@get="onTakeCoupon"
|
||||||
/>
|
/>
|
||||||
</block>
|
</block>
|
||||||
</s-layout>
|
</s-layout>
|
||||||
|
@ -147,13 +205,21 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, computed } from 'vue';
|
import { reactive, computed, ref, toRaw } from 'vue';
|
||||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
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 RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
|
||||||
|
import {
|
||||||
|
formatSales,
|
||||||
|
formatGoodsSwiper,
|
||||||
|
fen2yuan,
|
||||||
|
fen2yuanSimple,
|
||||||
|
formatDiscountPercent,
|
||||||
|
getRewardActivityRuleItemDescriptions,
|
||||||
|
} 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';
|
||||||
|
@ -165,7 +231,17 @@
|
||||||
import SpuApi from '@/sheep/api/product/spu';
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
|
||||||
onPageScroll(() => {});
|
onPageScroll(() => {});
|
||||||
|
import countDown from '@/sheep/components/countDown/index.vue';
|
||||||
|
import OrderApi from '@/sheep/api/trade/order';
|
||||||
|
import activity from '@/sheep/api/promotion/activity';
|
||||||
|
|
||||||
|
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,
|
||||||
|
@ -173,16 +249,18 @@
|
||||||
goodsInfo: {}, // SPU 信息
|
goodsInfo: {}, // SPU 信息
|
||||||
showSelectSku: false, // 是否展示 SKU 选择弹窗
|
showSelectSku: false, // 是否展示 SKU 选择弹窗
|
||||||
selectedSku: {}, // 选中的 SKU
|
selectedSku: {}, // 选中的 SKU
|
||||||
|
settlementSku: {}, // 结算的 SKU:由于 selectedSku 不进行默认选中,所以初始使用结算价格最低的 SKU 作为基础展示
|
||||||
showModel: false, // 是否展示 Coupon 优惠劵的弹窗
|
showModel: false, // 是否展示 Coupon 优惠劵的弹窗
|
||||||
couponInfo: [], // 可领取的 Coupon 优惠劵的列表
|
couponInfo: [], // 可领取的 Coupon 优惠劵的列表
|
||||||
showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
|
showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
|
||||||
activityInfo: [], // 【满减送/限时折扣】可参与的 Activity 营销活动的列表 TODO 芋艿:正在接入中
|
rewardActivity: {}, // 【满减送】活动
|
||||||
activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
|
activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
|
||||||
});
|
});
|
||||||
|
|
||||||
// 规格变更
|
// 规格变更
|
||||||
function onSkuChange(e) {
|
function onSkuChange(e) {
|
||||||
state.selectedSku = e;
|
state.selectedSku = e;
|
||||||
|
state.settlementSku = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加购物车
|
// 添加购物车
|
||||||
|
@ -196,7 +274,7 @@
|
||||||
|
|
||||||
// 立即购买
|
// 立即购买
|
||||||
function onBuy(e) {
|
function onBuy(e) {
|
||||||
if (!state.selectedSku.id) {
|
if (!e.id) {
|
||||||
sheep.$helper.toast('请选择商品规格');
|
sheep.$helper.toast('请选择商品规格');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -213,13 +291,13 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 营销活动
|
// 打开营销弹窗
|
||||||
function onActivity() {
|
function onOpenActivity() {
|
||||||
state.showActivityModel = true;
|
state.showActivityModel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即领取
|
// 立即领取优惠劵
|
||||||
async function onGet(id) {
|
async function onTakeCoupon(id) {
|
||||||
const { code } = await CouponApi.takeCoupon(id);
|
const { code } = await CouponApi.takeCoupon(id);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -261,6 +339,48 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getSettlementByIds(ids) {
|
||||||
|
let { data, code } = await OrderApi.getSettlementProduct(ids);
|
||||||
|
if (code !== 0 || data.length !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = data[0];
|
||||||
|
|
||||||
|
// 补充 SKU 的价格信息
|
||||||
|
state.goodsInfo.skus.forEach((sku) => {
|
||||||
|
data.skus.forEach((item) => {
|
||||||
|
if (sku.id === item.id) {
|
||||||
|
sku.promotionType = item.promotionType;
|
||||||
|
sku.promotionPrice = item.promotionPrice;
|
||||||
|
sku.promotionId = item.promotionId;
|
||||||
|
sku.promotionEndTime = item.promotionEndTime;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选择有 promotionPrice 且最小的
|
||||||
|
state.settlementSku = state.goodsInfo.skus
|
||||||
|
.filter((sku) => sku.stock > 0 && sku.promotionPrice > 0)
|
||||||
|
.reduce((prev, curr) => (prev.promotionPrice < curr.promotionPrice ? prev : curr));
|
||||||
|
|
||||||
|
// 设置满减送活动
|
||||||
|
if (data.rewardActivity) {
|
||||||
|
state.rewardActivity = data.rewardActivity;
|
||||||
|
//获取活动时间
|
||||||
|
getActivityTime(state.rewardActivity.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取活动时间
|
||||||
|
async function getActivityTime(id) {
|
||||||
|
const { code, data } = await RewardActivityApi.getRewardActivity(id);
|
||||||
|
if (code === 0) {
|
||||||
|
// console.log('获取到的活动 数据', data)
|
||||||
|
state.rewardActivity.startTime = data.startTime;
|
||||||
|
state.rewardActivity.endTime = data.endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
// 非法参数
|
// 非法参数
|
||||||
if (!options.id) {
|
if (!options.id) {
|
||||||
|
@ -278,7 +398,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) => {
|
||||||
|
@ -293,13 +412,15 @@
|
||||||
// 2. 加载优惠劵信息
|
// 2. 加载优惠劵信息
|
||||||
getCoupon();
|
getCoupon();
|
||||||
|
|
||||||
// 3. 获得单个商品,进行中的拼团、秒杀、砍价活动信息
|
// 3. 加载营销活动信息
|
||||||
ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {
|
ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {
|
||||||
if (res.code !== 0) {
|
if (res.code !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.activityList = res.data;
|
state.activityList = res.data;
|
||||||
});
|
});
|
||||||
|
//获取结算信息
|
||||||
|
getSettlementByIds(state.goodsId);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -448,4 +569,101 @@
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 限时折扣
|
||||||
|
.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: 0.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>
|
</style>
|
||||||
|
|
|
@ -118,12 +118,14 @@
|
||||||
</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';
|
||||||
import { resetPagination } from '@/sheep/util';
|
import { resetPagination } from '@/sheep/util';
|
||||||
import SpuApi from '@/sheep/api/product/spu';
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
import OrderApi from '@/sheep/api/trade/order';
|
||||||
|
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
|
||||||
|
|
||||||
const sys_navBar = sheep.$platform.navbar;
|
const sys_navBar = sheep.$platform.navbar;
|
||||||
const emits = defineEmits(['close', 'change']);
|
const emits = defineEmits(['close', 'change']);
|
||||||
|
@ -277,6 +279,13 @@
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 拼接结算信息(营销)
|
||||||
|
await OrderApi.getSettlementProduct(data.list.map((item) => item.id).join(',')).then((res) => {
|
||||||
|
if (res.code !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appendSettlementProduct(data.list, res.data);
|
||||||
|
});
|
||||||
state.pagination.list = _.concat(state.pagination.list, data.list);
|
state.pagination.list = _.concat(state.pagination.list, data.list);
|
||||||
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';
|
||||||
|
|
|
@ -0,0 +1,525 @@
|
||||||
|
<!-- 秒杀商品详情 -->
|
||||||
|
<template>
|
||||||
|
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<detailNavbar />
|
||||||
|
<!-- 骨架屏 -->
|
||||||
|
<detailSkeleton v-if="state.skeletonLoading" />
|
||||||
|
<!-- 下架/售罄提醒 -->
|
||||||
|
<s-empty
|
||||||
|
v-else-if="state.goodsInfo === null || state.goodsInfo.activity_type !== PromotionActivityTypeEnum.POINT.type"
|
||||||
|
text="活动不存在或已结束"
|
||||||
|
icon="/static/soldout-empty.png"
|
||||||
|
showAction
|
||||||
|
actionText="再逛逛"
|
||||||
|
actionUrl="/pages/goods/list"
|
||||||
|
/>
|
||||||
|
<block v-else>
|
||||||
|
<view class="detail-swiper-selector">
|
||||||
|
<!-- 商品图轮播 -->
|
||||||
|
<su-swiper
|
||||||
|
class="ss-m-b-14"
|
||||||
|
isPreview
|
||||||
|
:list="state.goodsSwiper"
|
||||||
|
dotStyle="tag"
|
||||||
|
imageMode="widthFix"
|
||||||
|
dotCur="bg-mask-40"
|
||||||
|
:seizeHeight="750"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 价格+标题 -->
|
||||||
|
<view class="title-card ss-m-y-14 ss-m-x-20 ss-p-x-20 ss-p-y-34">
|
||||||
|
<view class="price-box ss-flex ss-row-between ss-m-b-18">
|
||||||
|
<view class="ss-flex">
|
||||||
|
<view class="price-text ss-m-r-16">
|
||||||
|
{{ getShowPriceText }}
|
||||||
|
</view>
|
||||||
|
<view class="tig ss-flex ss-col-center">
|
||||||
|
<view class="tig-icon ss-flex ss-col-center ss-row-center">
|
||||||
|
<text class="cicon-alarm"></text>
|
||||||
|
</view>
|
||||||
|
<view class="tig-title">积分价</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="ss-flex ss-row-between ss-m-b-60">
|
||||||
|
<view class="origin-price ss-flex ss-col-center" v-if="state.goodsInfo.marketPrice">
|
||||||
|
原价
|
||||||
|
<view class="origin-price-text">
|
||||||
|
{{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<!-- 功能卡片 -->
|
||||||
|
<view class="detail-cell-card detail-card ss-flex-col">
|
||||||
|
<detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
|
||||||
|
</view>
|
||||||
|
<!-- 规格与数量弹框 -->
|
||||||
|
<s-select-seckill-sku
|
||||||
|
v-model="state.goodsInfo"
|
||||||
|
:show="state.showSelectSku"
|
||||||
|
:single-limit-count="activity.singleLimitCount"
|
||||||
|
@buy="onBuy"
|
||||||
|
@change="onSkuChange"
|
||||||
|
@close="state.showSelectSku = false"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 评价 -->
|
||||||
|
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsInfo.id" />
|
||||||
|
<!-- 详情 -->
|
||||||
|
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
|
||||||
|
|
||||||
|
<!-- 详情tabbar -->
|
||||||
|
<detail-tabbar v-model="state.goodsInfo">
|
||||||
|
<view class="buy-box ss-flex ss-col-center ss-p-r-20">
|
||||||
|
<button
|
||||||
|
class="ss-reset-button origin-price-btn ss-flex-col"
|
||||||
|
v-if="state.goodsInfo.marketPrice"
|
||||||
|
@tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })"
|
||||||
|
>
|
||||||
|
<view>
|
||||||
|
<view class="btn-price">{{ fen2yuan(state.goodsInfo.marketPrice) }}</view>
|
||||||
|
<view>原价购买</view>
|
||||||
|
</view>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="ss-reset-button btn-box ss-flex-col"
|
||||||
|
@tap="state.showSelectSku = true"
|
||||||
|
:class="
|
||||||
|
state.goodsInfo.stock != 0
|
||||||
|
? 'check-btn-box'
|
||||||
|
: 'disabled-btn-box'
|
||||||
|
"
|
||||||
|
:disabled="state.goodsInfo.stock === 0"
|
||||||
|
>
|
||||||
|
<view class="price-text">
|
||||||
|
{{getShowPriceText}}
|
||||||
|
</view>
|
||||||
|
<view v-if="state.goodsInfo.stock === 0">已售罄</view>
|
||||||
|
<view v-else>立即兑换</view>
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</detail-tabbar>
|
||||||
|
</block>
|
||||||
|
</s-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, reactive, ref, unref } from 'vue';
|
||||||
|
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||||
|
import sheep from '@/sheep';
|
||||||
|
import { isEmpty } from 'lodash-es';
|
||||||
|
import { fen2yuan, formatGoodsSwiper } from '@/sheep/hooks/useGoods';
|
||||||
|
import detailNavbar from './components/detail/detail-navbar.vue';
|
||||||
|
import detailCellSku from './components/detail/detail-cell-sku.vue';
|
||||||
|
import detailTabbar from './components/detail/detail-tabbar.vue';
|
||||||
|
import detailSkeleton from './components/detail/detail-skeleton.vue';
|
||||||
|
import detailCommentCard from './components/detail/detail-comment-card.vue';
|
||||||
|
import detailContentCard from './components/detail/detail-content-card.vue';
|
||||||
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||||
|
import PointApi from '@/sheep/api/promotion/point';
|
||||||
|
|
||||||
|
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 disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
|
||||||
|
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||||
|
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
|
||||||
|
|
||||||
|
onPageScroll(() => {
|
||||||
|
});
|
||||||
|
const state = reactive({
|
||||||
|
skeletonLoading: true,
|
||||||
|
goodsInfo: {},
|
||||||
|
showSelectSku: false,
|
||||||
|
goodsSwiper: [],
|
||||||
|
selectedSku: {},
|
||||||
|
showModel: false,
|
||||||
|
total: 0,
|
||||||
|
price: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 规格变更
|
||||||
|
function onSkuChange(e) {
|
||||||
|
state.selectedSku = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即购买
|
||||||
|
function onBuy(sku) {
|
||||||
|
sheep.$router.go('/pages/order/confirm', {
|
||||||
|
data: JSON.stringify({
|
||||||
|
order_type: 'goods',
|
||||||
|
buy_type: 'point',
|
||||||
|
pointActivityId: activity.value.id,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
skuId: sku.id,
|
||||||
|
count: sku.count,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分享信息
|
||||||
|
// TODO puhui999: 下次 fix
|
||||||
|
const shareInfo = computed(() => {
|
||||||
|
if (isEmpty(unref(activity))) return {};
|
||||||
|
return sheep.$platform.share.getShareInfo(
|
||||||
|
{
|
||||||
|
title: activity.value.name,
|
||||||
|
image: sheep.$url.cdn(state.goodsInfo.picUrl),
|
||||||
|
params: {
|
||||||
|
page: '4',
|
||||||
|
query: activity.value.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'goods', // 商品海报
|
||||||
|
title: activity.value.name, // 商品标题
|
||||||
|
image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
|
||||||
|
price: state.goodsInfo.price, // 商品价格
|
||||||
|
marketPrice: state.goodsInfo.marketPrice, // 商品原价
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const activity = ref();
|
||||||
|
|
||||||
|
const getShowPriceText = computed(() => {
|
||||||
|
let priceText = `${activity.value.point}积分${!activity.value.price ? '' : `+¥${fen2yuan(activity.value.price)}`}`;
|
||||||
|
if (!isEmpty(state.selectedSku)) {
|
||||||
|
const sku = state.selectedSku;
|
||||||
|
priceText = `${sku.point}积分${!sku.pointPrice ? '' : `+¥${fen2yuan(sku.pointPrice)}`}`;
|
||||||
|
}
|
||||||
|
return priceText;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询活动
|
||||||
|
const getActivity = async (id) => {
|
||||||
|
const { data } = await PointApi.getPointActivity(id);
|
||||||
|
activity.value = data;
|
||||||
|
// 查询商品
|
||||||
|
await getSpu(data.spuId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询商品
|
||||||
|
const getSpu = async (id) => {
|
||||||
|
const { data } = await SpuApi.getSpuDetail(id);
|
||||||
|
data.activity_type = PromotionActivityTypeEnum.POINT.type;
|
||||||
|
state.goodsInfo = data;
|
||||||
|
state.goodsInfo.stock = Math.min(data.stock, activity.value.stock);
|
||||||
|
// 处理轮播图
|
||||||
|
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.sliderPicUrls);
|
||||||
|
|
||||||
|
// 价格、库存使用活动的
|
||||||
|
data.skus.forEach((sku) => {
|
||||||
|
const product = activity.value.products.find((product) => product.skuId === sku.id);
|
||||||
|
if (product) {
|
||||||
|
sku.point = product.point;
|
||||||
|
sku.pointPrice = product.price;
|
||||||
|
sku.stock = Math.min(sku.stock, product.stock);
|
||||||
|
// 设置限购数量
|
||||||
|
sku.limitCount = product.count;
|
||||||
|
} else {
|
||||||
|
// 找不到可能是没配置
|
||||||
|
sku.stock = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state.skeletonLoading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
// 非法参数
|
||||||
|
if (!options.id) {
|
||||||
|
state.goodsInfo = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询活动
|
||||||
|
getActivity(options.id);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.disabled-btn-box[disabled] {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
background-color: $white;
|
||||||
|
margin: 14rpx 20rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 价格标题卡片
|
||||||
|
.title-card {
|
||||||
|
width: 710rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
// height: 320rpx;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
background-image: v-bind(headerBg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
.price-box {
|
||||||
|
.price-text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
line-height: normal;
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin-price {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
.origin-price-text {
|
||||||
|
text-decoration: line-through;
|
||||||
|
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '¥';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tig {
|
||||||
|
border: 2rpx solid #ffffff;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
width: 126rpx;
|
||||||
|
height: 38rpx;
|
||||||
|
|
||||||
|
.tig-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
margin-left: -2rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 4rpx 0 0 4rpx;
|
||||||
|
|
||||||
|
.cicon-alarm {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #fc6e6f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tig-title {
|
||||||
|
width: 86rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: normal;
|
||||||
|
color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-time {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
.countdown-h {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 0 4rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background: rgba(#000000, 0.1);
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-num {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background: rgba(#000000, 0.1);
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.discounts-box {
|
||||||
|
.discounts-tag {
|
||||||
|
padding: 4rpx 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
color: var(--ui-BG-Main);
|
||||||
|
// background: rgba(#2aae67, 0.05);
|
||||||
|
background: var(--ui-BG-Main-tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
.discounts-title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ui-BG-Main);
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cicon-forward {
|
||||||
|
color: var(--ui-BG-Main);
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: normal;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 42rpx;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 42rpx;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 购买
|
||||||
|
.buy-box {
|
||||||
|
.check-btn-box {
|
||||||
|
width: 248rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: -36rpx;
|
||||||
|
background-image: v-bind(btnBg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: normal;
|
||||||
|
border-radius: 0px 40rpx 40rpx 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-btn-box {
|
||||||
|
width: 248rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: -36rpx;
|
||||||
|
background-image: v-bind(disabledBtnBg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
color: #999999;
|
||||||
|
line-height: normal;
|
||||||
|
border-radius: 0px 40rpx 40rpx 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-price {
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '¥';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin-price-btn {
|
||||||
|
width: 236rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: rgba(#ff5651, 0.1);
|
||||||
|
color: #ff6000;
|
||||||
|
border-radius: 40rpx 0px 0px 40rpx;
|
||||||
|
line-height: normal;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.no-original {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//秒杀卡片
|
||||||
|
.seckill-box {
|
||||||
|
background: v-bind(seckillBg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.groupon-box {
|
||||||
|
background: v-bind(grouponBg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
//活动卡片
|
||||||
|
.activity-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
|
||||||
|
.activity-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 42rpx;
|
||||||
|
|
||||||
|
.activity-icon {
|
||||||
|
width: 38rpx;
|
||||||
|
height: 38rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-go {
|
||||||
|
width: 70rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ff6000;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-box {
|
||||||
|
.title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -225,8 +225,8 @@
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
@ -284,6 +284,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;
|
||||||
|
@ -374,6 +375,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;
|
||||||
|
@ -384,6 +386,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;
|
||||||
|
@ -467,6 +470,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;
|
||||||
|
|
||||||
|
@ -484,6 +488,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@
|
||||||
state.itemId = parseInt(options.itemId);
|
state.itemId = parseInt(options.itemId);
|
||||||
|
|
||||||
// 读取订单信息
|
// 读取订单信息
|
||||||
const { code, data } = await OrderApi.getOrder(state.orderId);
|
const { code, data } = await OrderApi.getOrderDetail(state.orderId);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,8 +143,7 @@
|
||||||
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 puhui999:【折扣】后续要把优惠信息打进去 -->
|
|
||||||
<text class="item-value text-red">
|
<text class="item-value text-red">
|
||||||
-¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
|
-¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
|
||||||
</text>
|
</text>
|
||||||
|
@ -295,6 +294,7 @@
|
||||||
combinationActivityId: state.orderPayload.combinationActivityId,
|
combinationActivityId: state.orderPayload.combinationActivityId,
|
||||||
combinationHeadId: state.orderPayload.combinationHeadId,
|
combinationHeadId: state.orderPayload.combinationHeadId,
|
||||||
seckillActivityId: state.orderPayload.seckillActivityId,
|
seckillActivityId: state.orderPayload.seckillActivityId,
|
||||||
|
pointActivityId: state.orderPayload.pointActivityId,
|
||||||
});
|
});
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -325,6 +325,7 @@
|
||||||
combinationActivityId: state.orderPayload.combinationActivityId,
|
combinationActivityId: state.orderPayload.combinationActivityId,
|
||||||
combinationHeadId: state.orderPayload.combinationHeadId,
|
combinationHeadId: state.orderPayload.combinationHeadId,
|
||||||
seckillActivityId: state.orderPayload.seckillActivityId,
|
seckillActivityId: state.orderPayload.seckillActivityId,
|
||||||
|
pointActivityId: state.orderPayload.pointActivityId,
|
||||||
});
|
});
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -127,7 +127,11 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 自提核销 -->
|
<!-- 自提核销 -->
|
||||||
<PickUpVerify :order-info="state.orderInfo" :systemStore="systemStore" ref="pickUpVerifyRef"></PickUpVerify>
|
<PickUpVerify
|
||||||
|
:order-info="state.orderInfo"
|
||||||
|
:systemStore="systemStore"
|
||||||
|
ref="pickUpVerifyRef"
|
||||||
|
></PickUpVerify>
|
||||||
|
|
||||||
<!-- 订单信息 -->
|
<!-- 订单信息 -->
|
||||||
<view class="notice-box">
|
<view class="notice-box">
|
||||||
|
@ -305,7 +309,7 @@
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定要取消订单吗?',
|
content: '确定要取消订单吗?',
|
||||||
success: async function(res) {
|
success: async function (res) {
|
||||||
if (!res.confirm) {
|
if (!res.confirm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -394,11 +398,11 @@
|
||||||
let res;
|
let res;
|
||||||
if (state.comeinType === 'wechat') {
|
if (state.comeinType === 'wechat') {
|
||||||
// TODO 芋艿:微信场景下
|
// TODO 芋艿:微信场景下
|
||||||
res = await OrderApi.getOrder(id, {
|
res = await OrderApi.getOrderDetail(id, {
|
||||||
merchant_trade_no: state.merchantTradeNo,
|
merchant_trade_no: state.merchantTradeNo,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res = await OrderApi.getOrder(id);
|
res = await OrderApi.getOrderDetail(id);
|
||||||
}
|
}
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
state.orderInfo = res.data;
|
state.orderInfo = res.data;
|
||||||
|
@ -451,7 +455,7 @@
|
||||||
color: rgba(#fff, 0.9);
|
color: rgba(#fff, 0.9);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: v-bind(headerBg) no-repeat,
|
background: v-bind(headerBg) no-repeat,
|
||||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||||
background-size: 750rpx 100%;
|
background-size: 750rpx 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
</uni-swiper-dot>
|
</uni-swiper-dot>
|
||||||
<view class="log-card-msg">
|
<view class="log-card-msg">
|
||||||
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
|
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
|
||||||
<!-- <view class="ss-flex ss-m-b-8">-->
|
<!-- <view class="ss-flex ss-m-b-8">-->
|
||||||
<!-- <view>物流状态:</view>-->
|
<!-- <view>物流状态:</view>-->
|
||||||
<!-- <view class="warning-color">{{ state.info.status_text }}</view>-->
|
<!-- <view class="warning-color">{{ state.info.status_text }}</view>-->
|
||||||
<!-- </view>-->
|
<!-- </view>-->
|
||||||
<view class="ss-m-b-8">快递单号:{{ state.info.logisticsNo }}</view>
|
<view class="ss-m-b-8">快递单号:{{ state.info.logisticsNo }}</view>
|
||||||
<view>快递公司:{{ state.info.logisticsName }}</view>
|
<view>快递公司:{{ state.info.logisticsName }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
@ -35,9 +35,9 @@
|
||||||
</view>
|
</view>
|
||||||
<view class="log-content-msg">
|
<view class="log-content-msg">
|
||||||
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
|
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
|
||||||
<!-- <view class="log-msg-title ss-m-b-20">-->
|
<!-- <view class="log-msg-title ss-m-b-20">-->
|
||||||
<!-- {{ item.status_text }}-->
|
<!-- {{ item.status_text }}-->
|
||||||
<!-- </view>-->
|
<!-- </view>-->
|
||||||
<view class="log-msg-desc ss-m-b-16">{{ item.content }}</view>
|
<view class="log-msg-desc ss-m-b-16">{{ item.content }}</view>
|
||||||
<view class="log-msg-date ss-m-b-40">
|
<view class="log-msg-date ss-m-b-40">
|
||||||
{{ sheep.$helper.timeFormat(item.time, 'yyyy-mm-dd hh:MM:ss') }}
|
{{ sheep.$helper.timeFormat(item.time, 'yyyy-mm-dd hh:MM:ss') }}
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getOrderDetail(id) {
|
async function getOrderDetail(id) {
|
||||||
const { data } = await OrderApi.getOrder(id)
|
const { data } = await OrderApi.getOrderDetail(id);
|
||||||
state.info = data;
|
state.info = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,15 @@
|
||||||
<view class="num">{{ orderInfo.pickUpVerifyCode }}</view>
|
<view class="num">{{ orderInfo.pickUpVerifyCode }}</view>
|
||||||
<view class="rules">
|
<view class="rules">
|
||||||
<!-- TODO puhui999: 需要后端放回:使用 receiveTime 即可 -->
|
<!-- TODO puhui999: 需要后端放回:使用 receiveTime 即可 -->
|
||||||
<!-- <view class="item">-->
|
<view class="item">
|
||||||
<!-- <view class="rulesTitle flex flex-wrap align-center">-->
|
<view class="rulesTitle flex flex-wrap align-center">
|
||||||
<!-- 核销时间-->
|
核销时间
|
||||||
<!-- </view>-->
|
</view>
|
||||||
<!-- <view class="info">-->
|
<view class="info">
|
||||||
<!-- 每日:-->
|
每日:
|
||||||
<!-- <text class="time">2020-2-+52</text>-->
|
<text class="time">2020-2-+52</text>
|
||||||
<!-- </view>-->
|
</view>
|
||||||
<!-- </view>-->
|
</view>
|
||||||
<view class="item">
|
<view class="item">
|
||||||
<view class="rulesTitle flex flex-wrap align-center">
|
<view class="rulesTitle flex flex-wrap align-center">
|
||||||
<text class="iconfont icon-shuoming1"></text>
|
<text class="iconfont icon-shuoming1"></text>
|
||||||
|
@ -138,7 +138,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
// TODO puhui999: 样式需要调整有 bug
|
|
||||||
.borRadius14 {
|
.borRadius14 {
|
||||||
border-radius: 14rpx !important;
|
border-radius: 14rpx !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
|
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
|
||||||
import PayOrderApi from '@/sheep/api/pay/order';
|
import PayOrderApi from '@/sheep/api/pay/order';
|
||||||
import PayChannelApi from '@/sheep/api/pay/channel';
|
import PayChannelApi from '@/sheep/api/pay/channel';
|
||||||
import { getPayMethods } from '@/sheep/platform/pay';
|
import { getPayMethods, goPayResult } from '@/sheep/platform/pay';
|
||||||
|
|
||||||
const userWallet = computed(() => sheep.$store('user').userWallet);
|
const userWallet = computed(() => sheep.$store('user').userWallet);
|
||||||
|
|
||||||
|
@ -135,12 +135,22 @@
|
||||||
|
|
||||||
// 状态转换:payOrder.status => payStatus
|
// 状态转换:payOrder.status => payStatus
|
||||||
function checkPayStatus() {
|
function checkPayStatus() {
|
||||||
if (state.orderInfo.status === 10
|
if (state.orderInfo.status === 10 || state.orderInfo.status === 20) {
|
||||||
|| state.orderInfo.status === 20 ) { // 支付成功
|
// 支付成功
|
||||||
state.payStatus = 2;
|
state.payStatus = 2;
|
||||||
|
// 跳转回支付成功页
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '订单已支付',
|
||||||
|
showCancel: false,
|
||||||
|
success: function () {
|
||||||
|
goPayResult(state.orderInfo.id, state.orderType);
|
||||||
|
},
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state.orderInfo.status === 30) { // 支付关闭
|
if (state.orderInfo.status === 30) {
|
||||||
|
// 支付关闭
|
||||||
state.payStatus = -1;
|
state.payStatus = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -155,26 +165,26 @@
|
||||||
// 设置支付订单信息
|
// 设置支付订单信息
|
||||||
async function setOrder(id) {
|
async function setOrder(id) {
|
||||||
// 获得支付订单信息
|
// 获得支付订单信息
|
||||||
const { data, code } = await PayOrderApi.getOrder(id);
|
const { data, code } = await PayOrderApi.getOrder(id, true);
|
||||||
if (code !== 0 || !data) {
|
if (code !== 0 || !data) {
|
||||||
state.payStatus = -2;
|
state.payStatus = -2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.orderInfo = data;
|
state.orderInfo = data;
|
||||||
// 获得支付方式
|
|
||||||
await setPayMethods();
|
|
||||||
// 设置支付状态
|
// 设置支付状态
|
||||||
checkPayStatus();
|
checkPayStatus();
|
||||||
|
// 获得支付方式
|
||||||
|
await setPayMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得支付方式
|
// 获得支付方式
|
||||||
async function setPayMethods() {
|
async function setPayMethods() {
|
||||||
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
|
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
state.payMethods = getPayMethods(data)
|
state.payMethods = getPayMethods(data);
|
||||||
state.payMethods.find(item => {
|
state.payMethods.find((item) => {
|
||||||
if (item.value && !item.disabled) {
|
if (item.value && !item.disabled) {
|
||||||
state.payment = item.value;
|
state.payment = item.value;
|
||||||
return true;
|
return true;
|
||||||
|
@ -183,9 +193,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
if (sheep.$platform.name === 'WechatOfficialAccount'
|
if (
|
||||||
&& sheep.$platform.os === 'ios'
|
sheep.$platform.name === 'WechatOfficialAccount' &&
|
||||||
&& !sheep.$platform.landingPage.includes('pages/pay/index')) {
|
sheep.$platform.os === 'ios' &&
|
||||||
|
!sheep.$platform.landingPage.includes('pages/pay/index')
|
||||||
|
) {
|
||||||
location.reload();
|
location.reload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +226,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 60rpx 20rpx 40rpx;
|
padding: 60rpx 20rpx 40rpx;
|
||||||
|
|
||||||
|
|
||||||
.money-text {
|
.money-text {
|
||||||
color: $red;
|
color: $red;
|
||||||
font-size: 46rpx;
|
font-size: 46rpx;
|
||||||
|
|
|
@ -126,7 +126,10 @@
|
||||||
// #endif
|
// #endif
|
||||||
// 特殊:获得商品订单信息
|
// 特殊:获得商品订单信息
|
||||||
if (state.orderType === 'goods') {
|
if (state.orderType === 'goods') {
|
||||||
const { data, code } = await OrderApi.getOrder(state.orderInfo.merchantOrderId);
|
const { data, code } = await OrderApi.getOrderDetail(
|
||||||
|
state.orderInfo.merchantOrderId,
|
||||||
|
true,
|
||||||
|
);
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
state.tradeOrder = data;
|
state.tradeOrder = data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ import request from '@/sheep/request';
|
||||||
|
|
||||||
const PayOrderApi = {
|
const PayOrderApi = {
|
||||||
// 获得支付订单
|
// 获得支付订单
|
||||||
getOrder: (id) => {
|
getOrder: (id, sync) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/pay/order/get',
|
url: '/pay/order/get',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { id }
|
params: { id, sync },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 提交支付订单
|
// 提交支付订单
|
||||||
|
@ -14,9 +14,9 @@ const PayOrderApi = {
|
||||||
return request({
|
return request({
|
||||||
url: '/pay/order/submit',
|
url: '/pay/order/submit',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PayOrderApi;
|
export default PayOrderApi;
|
||||||
|
|
|
@ -13,6 +13,18 @@ const SpuApi = {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 获得商品结算信息
|
||||||
|
getSettlementProduct: (spuIds) => {
|
||||||
|
return request({
|
||||||
|
url: '/trade/order/settlement-product',
|
||||||
|
method: 'GET',
|
||||||
|
params: { spuIds },
|
||||||
|
custom: {
|
||||||
|
showLoading: false,
|
||||||
|
showError: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
// 获得商品 SPU 分页
|
// 获得商品 SPU 分页
|
||||||
getSpuPage: (params) => {
|
getSpuPage: (params) => {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import request from '@/sheep/request';
|
||||||
|
|
||||||
|
const PointApi = {
|
||||||
|
// 获得积分商城活动分页
|
||||||
|
getPointActivityPage: (params) => {
|
||||||
|
return request({ url: 'promotion/point-activity/page', method: 'GET', params });
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得积分商城活动列表,基于活动编号数组
|
||||||
|
getPointActivityListByIds: (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/promotion/point-activity/list-by-ids',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得积分商城活动明细
|
||||||
|
getPointActivity: (id) => {
|
||||||
|
return request({
|
||||||
|
url: 'promotion/point-activity/get-detail',
|
||||||
|
method: 'GET',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PointApi;
|
|
@ -53,6 +53,18 @@ const OrderApi = {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 获得商品结算信息
|
||||||
|
getSettlementProduct: (spuIds) => {
|
||||||
|
return request({
|
||||||
|
url: '/trade/order/settlement-product',
|
||||||
|
method: 'GET',
|
||||||
|
params: { spuIds },
|
||||||
|
custom: {
|
||||||
|
showLoading: false,
|
||||||
|
showError: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
// 创建订单
|
// 创建订单
|
||||||
createOrder: (data) => {
|
createOrder: (data) => {
|
||||||
return request({
|
return request({
|
||||||
|
@ -61,13 +73,14 @@ const OrderApi = {
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 获得订单
|
// 获得订单详细:sync 是可选参数
|
||||||
getOrder: (id) => {
|
getOrderDetail: (id, sync) => {
|
||||||
return request({
|
return request({
|
||||||
url: `/trade/order/get-detail`,
|
url: `/trade/order/get-detail`,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
id,
|
id,
|
||||||
|
sync,
|
||||||
},
|
},
|
||||||
custom: {
|
custom: {
|
||||||
showLoading: false,
|
showLoading: false,
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
<!-- TODO 霖:是不是怎么复用 s-count-down 组件 -->
|
||||||
|
<template>
|
||||||
|
<view class="time" :style="justifyLeft">
|
||||||
|
<text class="" v-if="tipText">{{ tipText }}</text>
|
||||||
|
<text
|
||||||
|
class="styleAll p6"
|
||||||
|
v-if="isDay === true"
|
||||||
|
:style="{ background: bgColor.bgColor, color: bgColor.Color }"
|
||||||
|
>{{ day }}{{ bgColor.isDay ? '天' : '' }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="timeTxt"
|
||||||
|
v-if="dayText"
|
||||||
|
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||||
|
>{{ dayText }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="styleAll"
|
||||||
|
:class="isCol ? 'timeCol' : ''"
|
||||||
|
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||||
|
>{{ hour }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="timeTxt"
|
||||||
|
v-if="hourText"
|
||||||
|
:class="isCol ? 'whit' : ''"
|
||||||
|
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||||
|
>{{ hourText }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="styleAll"
|
||||||
|
:class="isCol ? 'timeCol' : ''"
|
||||||
|
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||||
|
>{{ minute }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="timeTxt"
|
||||||
|
v-if="minuteText"
|
||||||
|
:class="isCol ? 'whit' : ''"
|
||||||
|
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||||
|
>{{ minuteText }}</text
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
class="styleAll"
|
||||||
|
:class="isCol ? 'timeCol' : ''"
|
||||||
|
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||||
|
>{{ second }}</text
|
||||||
|
>
|
||||||
|
<text class="timeTxt" v-if="secondText">{{ secondText }}</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'countDown',
|
||||||
|
props: {
|
||||||
|
justifyLeft: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
//距离开始提示文字
|
||||||
|
tipText: {
|
||||||
|
type: String,
|
||||||
|
default: '倒计时',
|
||||||
|
},
|
||||||
|
dayText: {
|
||||||
|
type: String,
|
||||||
|
default: '天',
|
||||||
|
},
|
||||||
|
hourText: {
|
||||||
|
type: String,
|
||||||
|
default: '时',
|
||||||
|
},
|
||||||
|
minuteText: {
|
||||||
|
type: String,
|
||||||
|
default: '分',
|
||||||
|
},
|
||||||
|
secondText: {
|
||||||
|
type: String,
|
||||||
|
default: '秒',
|
||||||
|
},
|
||||||
|
datatime: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
isDay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
isCol: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
bgColor: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
day: '00',
|
||||||
|
hour: '00',
|
||||||
|
minute: '00',
|
||||||
|
second: '00',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
this.show_time();
|
||||||
|
},
|
||||||
|
mounted: function () {},
|
||||||
|
methods: {
|
||||||
|
show_time: function () {
|
||||||
|
let that = this;
|
||||||
|
|
||||||
|
function runTime() {
|
||||||
|
//时间函数
|
||||||
|
let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
|
||||||
|
let day = 0,
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0;
|
||||||
|
if (intDiff > 0) {
|
||||||
|
//转换时间
|
||||||
|
if (that.isDay === true) {
|
||||||
|
day = Math.floor(intDiff / (60 * 60 * 24));
|
||||||
|
} else {
|
||||||
|
day = 0;
|
||||||
|
}
|
||||||
|
hour = Math.floor(intDiff / (60 * 60)) - day * 24;
|
||||||
|
minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
|
||||||
|
second = Math.floor(intDiff) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
|
||||||
|
if (hour <= 9) hour = '0' + hour;
|
||||||
|
if (minute <= 9) minute = '0' + minute;
|
||||||
|
if (second <= 9) second = '0' + second;
|
||||||
|
that.day = day;
|
||||||
|
that.hour = hour;
|
||||||
|
that.minute = minute;
|
||||||
|
that.second = second;
|
||||||
|
} else {
|
||||||
|
that.day = '00';
|
||||||
|
that.hour = '00';
|
||||||
|
that.minute = '00';
|
||||||
|
that.second = '00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runTime();
|
||||||
|
setInterval(runTime, 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.p6 {
|
||||||
|
padding: 0 8rpx;
|
||||||
|
}
|
||||||
|
.styleAll {
|
||||||
|
/* color: #fff; */
|
||||||
|
font-size: 24rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
line-height: 36rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
text-align: center;
|
||||||
|
/* padding: 0 6rpx; */
|
||||||
|
}
|
||||||
|
.timeTxt {
|
||||||
|
text-align: center;
|
||||||
|
/* width: 16rpx; */
|
||||||
|
height: 36rpx;
|
||||||
|
line-height: 36rpx;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.whit {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: #fc4141;
|
||||||
|
margin: 0 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeCol {
|
||||||
|
/* width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
text-align:center;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 24rpx; */
|
||||||
|
color: #e93323;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,34 +2,81 @@
|
||||||
<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>
|
||||||
|
<view v-if="state.rewardActivity && state.rewardActivity.id > 0">
|
||||||
|
<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, index) in getRewardActivityRuleGroupDescriptions(state.rewardActivity)"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="boxCont ss-flex ss-col-top ss-m-b-40"
|
||||||
|
@tap="onGoodsList(state.rewardActivity)"
|
||||||
|
>
|
||||||
|
<view class="model-content-tag ss-flex ss-row-center">{{ item.name }}</view>
|
||||||
|
<view class="model-content-title">
|
||||||
|
<view class="contBu">
|
||||||
|
{{ item.values.join(';') }}
|
||||||
|
</view>
|
||||||
|
<view class="ss-m-b-24 cotBu-txt">
|
||||||
|
{{ sheep.$helper.timeFormat(state.rewardActivity.startTime, 'yyyy.mm.dd') }}
|
||||||
|
-
|
||||||
|
{{ sheep.$helper.timeFormat(state.rewardActivity.endTime, 'yyyy.mm.dd') }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="cicon-forward" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
<view class="titleLi">可领优惠券</view>
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="model-content ss-m-t-50"
|
class="model-content"
|
||||||
scroll-y
|
scroll-y
|
||||||
:scroll-with-animation="false"
|
:scroll-with-animation="false"
|
||||||
:enable-back-to-top="true"
|
:enable-back-to-top="true"
|
||||||
|
v-if="state.couponInfo.length"
|
||||||
>
|
>
|
||||||
<view v-for="item in state.activityInfo" :key="item.id">
|
<view class="actBox" v-for="item in state.couponInfo" :key="item.id">
|
||||||
<view class="ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(item)">
|
<view class="boxCont ss-flex ss-col-top ss-m-b-40">
|
||||||
<view class="model-content-tag ss-flex ss-row-center">满减</view>
|
<view class="model-content-tag2">
|
||||||
<view class="ss-m-l-20 model-content-title ss-flex-1">
|
<view class="usePrice"> ¥{{ fen2yuan(item.discountPrice) }} </view>
|
||||||
<view class="ss-m-b-24" v-for="rule in state.activityMap[item.id]?.rules" :key="rule">
|
<view class="impose"> 满¥{{ fen2yuan(item.usePrice) }}可用 </view>
|
||||||
{{ formatRewardActivityRule(state.activityMap[item.id], rule) }}
|
</view>
|
||||||
|
<view class="model-content-title2">
|
||||||
|
<view class="contBu">
|
||||||
|
{{ item.name }}
|
||||||
|
</view>
|
||||||
|
<view class="ss-m-b-24 cotBu-txt">
|
||||||
|
{{
|
||||||
|
item.validityType == 1
|
||||||
|
? sheep.$helper.timeFormat(item.validStartTime, 'yyyy.mm.dd') -
|
||||||
|
sheep.$helper.timeFormat(item.validEndTime, 'yyyy.mm.dd')
|
||||||
|
: '领取后' + item.fixedStartTerm + '-' + item.fixedEndTerm + '天可用'
|
||||||
|
}}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="cicon-forward" />
|
<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>
|
||||||
|
<view class="nullBox" v-else> 暂无可领优惠券 </view>
|
||||||
</view>
|
</view>
|
||||||
</su-popup>
|
</su-popup>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import sheep from '@/sheep';
|
import sheep from '@/sheep';
|
||||||
import { computed, reactive, watch } from 'vue';
|
import { getRewardActivityRuleGroupDescriptions } from '@/sheep/hooks/useGoods';
|
||||||
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
|
import { computed, reactive, watch, ref } from 'vue';
|
||||||
import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
|
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -42,26 +89,14 @@
|
||||||
});
|
});
|
||||||
const emits = defineEmits(['close']);
|
const emits = defineEmits(['close']);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
activityInfo: computed(() => props.modelValue),
|
rewardActivity: computed(() => props.modelValue.rewardActivity),
|
||||||
activityMap: {}
|
couponInfo: computed(() => props.modelValue.couponInfo),
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
// 领取优惠劵
|
||||||
() => props.show,
|
const getBuy = (id) => {
|
||||||
() => {
|
emits('get', id);
|
||||||
// 展示的情况下,加载每个活动的详细信息
|
};
|
||||||
if (props.show) {
|
|
||||||
state.activityInfo?.forEach(activity => {
|
|
||||||
RewardActivityApi.getRewardActivity(activity.id).then(res => {
|
|
||||||
if (res.code !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.activityMap[activity.id] = res.data;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
function onGoodsList(e) {
|
function onGoodsList(e) {
|
||||||
sheep.$router.go('/pages/activity/index', {
|
sheep.$router.go('/pages/activity/index', {
|
||||||
|
@ -72,34 +107,149 @@
|
||||||
<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: 380rpx;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增的
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
.nullBox {
|
||||||
|
width: 100%;
|
||||||
|
height: 300rpx;
|
||||||
|
font-size: 25rpx;
|
||||||
|
line-height: 300rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
<s-groupon-block v-if="type === 'PromotionCombination'" :data="data" :styles="styles" />
|
<s-groupon-block v-if="type === 'PromotionCombination'" :data="data" :styles="styles" />
|
||||||
<!-- 营销组件:秒杀 -->
|
<!-- 营销组件:秒杀 -->
|
||||||
<s-seckill-block v-if="type === 'PromotionSeckill'" :data="data" :styles="styles" />
|
<s-seckill-block v-if="type === 'PromotionSeckill'" :data="data" :styles="styles" />
|
||||||
|
<!-- 营销组件:积分商城 -->
|
||||||
|
<s-point-block v-if="type === 'PromotionPoint'" :data="data" :styles="styles" />
|
||||||
<!-- 营销组件:小程序直播(暂时没有这个功能) -->
|
<!-- 营销组件:小程序直播(暂时没有这个功能) -->
|
||||||
<s-live-block v-if="type === 'MpLive'" :data="data" :styles="styles" />
|
<s-live-block v-if="type === 'MpLive'" :data="data" :styles="styles" />
|
||||||
<!-- 营销组件:优惠券 -->
|
<!-- 营销组件:优惠券 -->
|
||||||
|
|
|
@ -15,25 +15,11 @@
|
||||||
:scroll-with-animation="false"
|
:scroll-with-animation="false"
|
||||||
:enable-back-to-top="true"
|
:enable-back-to-top="true"
|
||||||
>
|
>
|
||||||
<view v-for="(item, index) in state.orderInfo.promo_infos" :key="index">
|
<view v-for="(item, index) in state.orderInfo.promotions" :key="index">
|
||||||
<view class="ss-flex ss-m-b-40 subtitle">
|
<!-- 不展示积分、优惠劵、会员折扣,因为它们已经单独展示了 -->
|
||||||
<view>共{{ item.goods_ids.length }}件,</view>
|
<view class="ss-flex ss-m-b-40 subtitle" v-if="[1, 2, 3, 4, 5, 6].includes(item.type)">
|
||||||
<view v-if="item.activity_type === 'full_discount'">
|
<view> {{ item.description }} </view>
|
||||||
满{{ item.discount_rule.full }}打{{ item.discount_rule.discount }}折,已减
|
|
||||||
</view>
|
|
||||||
<view v-if="item.activity_type === 'full_gift'">满赠</view>
|
|
||||||
<view v-if="item.activity_type === 'full_reduce'">
|
|
||||||
满{{ item.discount_rule.full }}减{{ item.discount_rule.discount }},已减
|
|
||||||
</view>
|
|
||||||
<view class="price-text">¥{{ item.promo_discount_money || '0.00' }}</view>
|
|
||||||
</view>
|
</view>
|
||||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
|
|
||||||
<view class="ss-flex">
|
|
||||||
<view v-for="i in item.goods_ids" :key="i">
|
|
||||||
<image class="content-img" :src="sheep.$url.cdn(getGoodsImg(i))" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
@ -44,7 +30,6 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive } from 'vue';
|
import { computed, reactive } from 'vue';
|
||||||
import sheep from '@/sheep';
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
promoInfo: {
|
promoInfo: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -67,28 +52,22 @@
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
orderInfo: computed(() => props.modelValue),
|
orderInfo: computed(() => props.modelValue),
|
||||||
});
|
});
|
||||||
const getGoodsImg = (e) => {
|
|
||||||
let goodsImg = '';
|
|
||||||
state.orderInfo.goods_list.forEach((i) => {
|
|
||||||
if (e == i.goods_id) {
|
|
||||||
goodsImg = i.goods.image;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return goodsImg;
|
|
||||||
};
|
|
||||||
</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,17 +76,20 @@
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,8 @@
|
||||||
import { computed, reactive, onMounted } from 'vue';
|
import { computed, reactive, onMounted } from 'vue';
|
||||||
import sheep from '@/sheep';
|
import sheep from '@/sheep';
|
||||||
import SpuApi from '@/sheep/api/product/spu';
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
import OrderApi from '@/sheep/api/trade/order';
|
||||||
|
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
|
||||||
|
|
||||||
// 布局类型
|
// 布局类型
|
||||||
const LayoutTypeEnum = {
|
const LayoutTypeEnum = {
|
||||||
|
@ -238,6 +240,15 @@
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 加载商品列表
|
// 加载商品列表
|
||||||
state.goodsList = await getGoodsListByIds(spuIds.join(','));
|
state.goodsList = await getGoodsListByIds(spuIds.join(','));
|
||||||
|
// 拼接结算信息(营销)
|
||||||
|
await OrderApi.getSettlementProduct(state.goodsList.map((item) => item.id).join(',')).then(
|
||||||
|
(res) => {
|
||||||
|
if (res.code !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appendSettlementProduct(state.goodsList, res.data);
|
||||||
|
},
|
||||||
|
);
|
||||||
// 只有双列布局时需要
|
// 只有双列布局时需要
|
||||||
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||||
// 分列
|
// 分列
|
||||||
|
|
|
@ -11,11 +11,7 @@
|
||||||
<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" />
|
||||||
class="xs-img-box"
|
|
||||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
|
||||||
mode="aspectFit"
|
|
||||||
></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="xs-goods-content ss-flex-col ss-row-around"
|
class="xs-goods-content ss-flex-col ss-row-around"
|
||||||
|
@ -27,13 +23,34 @@
|
||||||
>
|
>
|
||||||
{{ data.title || data.name }}
|
{{ data.title || data.name }}
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 活动信息 -->
|
||||||
|
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||||
|
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||||
|
<view
|
||||||
|
class="card2"
|
||||||
|
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view
|
<view
|
||||||
v-if="goodsFields.price?.show"
|
v-if="goodsFields.price?.show"
|
||||||
class="xs-goods-price font-OPPOSANS"
|
class="xs-goods-price font-OPPOSANS"
|
||||||
:style="[{ color: goodsFields.price.color }]"
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
>
|
>
|
||||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
<!-- 活动价格 -->
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
<text v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||||
|
{{ data.point }}积分
|
||||||
|
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${fen2yuan(data.pointPrice)}元` }}
|
||||||
|
</text>
|
||||||
|
<template v-else>
|
||||||
|
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||||
|
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||||
|
<text v-else>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
@ -60,13 +77,34 @@
|
||||||
>
|
>
|
||||||
{{ data.title || data.name }}
|
{{ data.title || data.name }}
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 活动信息 -->
|
||||||
|
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||||
|
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||||
|
<view
|
||||||
|
class="card2"
|
||||||
|
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view
|
<view
|
||||||
v-if="goodsFields.price?.show"
|
v-if="goodsFields.price?.show"
|
||||||
class="sm-goods-price font-OPPOSANS"
|
class="sm-goods-price font-OPPOSANS"
|
||||||
:style="[{ color: goodsFields.price.color }]"
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
>
|
>
|
||||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
<!-- 活动价格 -->
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
<text v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||||
|
{{ data.point }}积分
|
||||||
|
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${fen2yuan(data.pointPrice)}元` }}
|
||||||
|
</text>
|
||||||
|
<template v-else>
|
||||||
|
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||||
|
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||||
|
<text v-else>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
@ -74,13 +112,9 @@
|
||||||
<!-- md卡片:竖向,一行放两个,图上内容下 -->
|
<!-- md卡片:竖向,一行放两个,图上内容下 -->
|
||||||
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :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)" />
|
||||||
</view>
|
</view>
|
||||||
<image
|
<image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix" />
|
||||||
class="md-img-box"
|
|
||||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
|
||||||
mode="widthFix"
|
|
||||||
></image>
|
|
||||||
<view
|
<view
|
||||||
class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
|
class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
|
||||||
:id="elId"
|
:id="elId"
|
||||||
|
@ -110,16 +144,36 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</slot>
|
</slot>
|
||||||
|
<!-- 活动信息 -->
|
||||||
|
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||||
|
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||||
|
<view
|
||||||
|
class="card2"
|
||||||
|
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view class="ss-flex ss-col-bottom">
|
<view class="ss-flex ss-col-bottom">
|
||||||
<view
|
<view
|
||||||
v-if="goodsFields.price?.show"
|
v-if="goodsFields.price?.show"
|
||||||
class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
|
class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
|
||||||
:style="[{ color: goodsFields.price.color }]"
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
>
|
>
|
||||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
<!-- 活动价格 -->
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
<text v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||||
|
{{ data.point }}积分
|
||||||
|
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${fen2yuan(data.pointPrice)}元` }}
|
||||||
|
</text>
|
||||||
|
<template v-else>
|
||||||
|
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||||
|
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||||
|
<text v-else>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-if="
|
v-if="
|
||||||
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
||||||
|
@ -163,7 +217,7 @@
|
||||||
class="lg-img-box"
|
class="lg-img-box"
|
||||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
||||||
mode="aspectFill"
|
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
|
||||||
|
@ -189,21 +243,38 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</slot>
|
</slot>
|
||||||
<view class="ss-flex ss-col-bottom ss-m-t-10">
|
<!-- 活动信息 -->
|
||||||
|
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||||
|
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||||
<view
|
<view
|
||||||
v-if="goodsFields.price?.show"
|
class="card2"
|
||||||
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
|
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||||
:style="[{ color: goodsFields.price.color }]"
|
:key="item"
|
||||||
>
|
>
|
||||||
<text class="ss-font-24">{{ priceUnit }}</text>
|
{{ item }}
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
|
||||||
|
<view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
|
||||||
|
<!-- 活动价格 -->
|
||||||
|
<text v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||||
|
{{ data.point }}积分
|
||||||
|
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${fen2yuan(data.pointPrice)}元` }}
|
||||||
|
</text>
|
||||||
|
<template v-else>
|
||||||
|
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||||
|
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||||
|
<text v-else>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</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"
|
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||||
:style="[{ color: originPriceColor }]"
|
:style="[{ color: originPriceColor }]"
|
||||||
>
|
>
|
||||||
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||||
|
@ -217,22 +288,20 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<slot name="cart">
|
<slot name="cart">
|
||||||
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> 去购买 </view>
|
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> 去购买</view>
|
||||||
</slot>
|
</slot>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
|
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
|
||||||
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" :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)" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<image
|
<image
|
||||||
class="sl-img-box"
|
class="sl-img-box"
|
||||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
></image>
|
/>
|
||||||
|
|
||||||
<view class="sl-goods-content">
|
<view class="sl-goods-content">
|
||||||
<view>
|
<view>
|
||||||
<view
|
<view
|
||||||
|
@ -262,10 +331,31 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</slot>
|
</slot>
|
||||||
|
<!-- 活动信息 -->
|
||||||
|
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||||
|
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||||
|
<view
|
||||||
|
class="card2"
|
||||||
|
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</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>
|
<!-- 活动价格 -->
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
<text v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||||
|
{{ data.point }}积分
|
||||||
|
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${fen2yuan(data.pointPrice)}元` }}
|
||||||
|
</text>
|
||||||
|
<template v-else>
|
||||||
|
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||||
|
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||||
|
<text v-else>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
v-if="
|
v-if="
|
||||||
|
@ -321,14 +411,17 @@
|
||||||
* @event {Function()} click - 点击卡片
|
* @event {Function()} click - 点击卡片
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { computed, reactive, getCurrentInstance, onMounted, nextTick } from 'vue';
|
import { computed, getCurrentInstance, nextTick, onMounted } 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,
|
||||||
|
formatExchange,
|
||||||
|
formatSales,
|
||||||
|
formatStock,
|
||||||
|
getRewardActivityRuleItemDescriptions,
|
||||||
|
} from '@/sheep/hooks/useGoods';
|
||||||
import { isArray } from 'lodash-es';
|
import { isArray } from 'lodash-es';
|
||||||
|
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||||
// 数据
|
|
||||||
const state = reactive({});
|
|
||||||
|
|
||||||
// 接收参数
|
// 接收参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -337,17 +430,29 @@
|
||||||
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,6 +522,17 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 优惠文案
|
||||||
|
const discountText = computed(() => {
|
||||||
|
const promotionType = props.data.promotionType;
|
||||||
|
if (promotionType === 4) {
|
||||||
|
return '限时优惠';
|
||||||
|
} else if (promotionType === 6) {
|
||||||
|
return '会员价';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
// 组件样式
|
// 组件样式
|
||||||
const elStyles = computed(() => {
|
const elStyles = computed(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -432,10 +548,18 @@
|
||||||
const salesAndStock = computed(() => {
|
const salesAndStock = computed(() => {
|
||||||
let text = [];
|
let text = [];
|
||||||
if (props.goodsFields.salesCount?.show) {
|
if (props.goodsFields.salesCount?.show) {
|
||||||
text.push(formatSales(props.data.sales_show_type, props.data.salesCount));
|
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
|
||||||
|
text.push(formatExchange(props.data.sales_show_type, (props.data.pointTotalStock || 0) - (props.data.pointStock || 0)));
|
||||||
|
}else {
|
||||||
|
text.push(formatSales(props.data.sales_show_type, props.data.salesCount));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (props.goodsFields.stock?.show) {
|
if (props.goodsFields.stock?.show) {
|
||||||
text.push(formatStock(props.data.stock_show_type, props.data.stock));
|
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
|
||||||
|
text.push(formatStock(props.data.stock_show_type, props.data.pointTotalStock));
|
||||||
|
}else {
|
||||||
|
text.push(formatStock(props.data.stock_show_type, props.data.stock));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return text.join(' | ');
|
return text.join(' | ');
|
||||||
});
|
});
|
||||||
|
@ -454,7 +578,10 @@
|
||||||
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];
|
||||||
|
@ -763,4 +890,33 @@
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
padding: 2rpx 10rpx;
|
||||||
|
background-color: red;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card2 {
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
padding: 2rpx 10rpx;
|
||||||
|
background-color: rgb(255, 242, 241);
|
||||||
|
color: #ff2621;
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin: 5rpx 0 5rpx 5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconBox {
|
||||||
|
width: 100%;
|
||||||
|
height: fit-content;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -28,6 +28,14 @@
|
||||||
>
|
>
|
||||||
¥{{ fen2yuan(price) }}
|
¥{{ fen2yuan(price) }}
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="point && Number(price) > 0">+</view>
|
||||||
|
<view class="price-text ss-flex ss-col-center" v-if="point">
|
||||||
|
<image
|
||||||
|
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||||
|
class="point-img"
|
||||||
|
></image>
|
||||||
|
<view>{{ point }}</view>
|
||||||
|
</view>
|
||||||
<view v-if="num" class="total-text ss-flex ss-col-center">x {{ num }}</view>
|
<view v-if="num" class="total-text ss-flex ss-col-center">x {{ num }}</view>
|
||||||
<slot name="priceSuffix"></slot>
|
<slot name="priceSuffix"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
@ -88,7 +96,7 @@
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
score: {
|
point: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
@ -113,7 +121,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.score-img {
|
.point-img {
|
||||||
width: 36rpx;
|
width: 36rpx;
|
||||||
height: 36rpx;
|
height: 36rpx;
|
||||||
margin: 0 4rpx;
|
margin: 0 4rpx;
|
||||||
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
<!-- 装修商品组件:【积分商城】商品卡片 -->
|
||||||
|
<template>
|
||||||
|
<!-- 商品卡片 -->
|
||||||
|
<view>
|
||||||
|
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||||
|
<view
|
||||||
|
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
|
||||||
|
class="goods-sl-box"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="goods-box"
|
||||||
|
v-for="item in state.spuList"
|
||||||
|
:key="item.id"
|
||||||
|
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
|
||||||
|
>
|
||||||
|
<s-goods-column
|
||||||
|
class=""
|
||||||
|
size="sl"
|
||||||
|
:goodsFields="data.fields"
|
||||||
|
:tagStyle="data.badge"
|
||||||
|
:data="item"
|
||||||
|
:titleColor="data.fields.name?.color"
|
||||||
|
:subTitleColor="data.fields.introduction.color"
|
||||||
|
:topRadius="data.borderRadiusTop"
|
||||||
|
:bottomRadius="data.borderRadiusBottom"
|
||||||
|
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||||
|
>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<template v-slot:cart>
|
||||||
|
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||||
|
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</s-goods-column>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 布局2. 单列小图(左图,右内容) -->
|
||||||
|
<view
|
||||||
|
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
|
||||||
|
class="goods-lg-box"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="goods-box"
|
||||||
|
:style="[{ marginBottom: data.space + 'px' }]"
|
||||||
|
v-for="item in state.spuList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<s-goods-column
|
||||||
|
class="goods-card"
|
||||||
|
size="lg"
|
||||||
|
:goodsFields="data.fields"
|
||||||
|
:data="item"
|
||||||
|
:tagStyle="data.badge"
|
||||||
|
:titleColor="data.fields.name?.color"
|
||||||
|
:subTitleColor="data.fields.introduction.color"
|
||||||
|
:topRadius="data.borderRadiusTop"
|
||||||
|
:bottomRadius="data.borderRadiusBottom"
|
||||||
|
@tap="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||||
|
>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<template v-slot:cart>
|
||||||
|
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||||
|
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</s-goods-column>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 布局3. 双列(每一列:上图,下内容)-->
|
||||||
|
<view
|
||||||
|
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
|
||||||
|
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||||
|
>
|
||||||
|
<view class="goods-list-box">
|
||||||
|
<view
|
||||||
|
class="left-list"
|
||||||
|
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||||
|
v-for="item in state.leftSpuList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<s-goods-column
|
||||||
|
class="goods-md-box"
|
||||||
|
size="md"
|
||||||
|
:goodsFields="data.fields"
|
||||||
|
:tagStyle="data.badge"
|
||||||
|
:data="item"
|
||||||
|
:titleColor="data.fields.name?.color"
|
||||||
|
:subTitleColor="data.fields.introduction.color"
|
||||||
|
:topRadius="data.borderRadiusTop"
|
||||||
|
:bottomRadius="data.borderRadiusBottom"
|
||||||
|
:titleWidth="330 - marginLeft - marginRight"
|
||||||
|
@click="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||||
|
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||||
|
>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<template v-slot:cart>
|
||||||
|
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||||
|
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</s-goods-column>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="goods-list-box">
|
||||||
|
<view
|
||||||
|
class="right-list"
|
||||||
|
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||||
|
v-for="item in state.rightSpuList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<s-goods-column
|
||||||
|
class="goods-md-box"
|
||||||
|
size="md"
|
||||||
|
:goodsFields="data.fields"
|
||||||
|
:tagStyle="data.badge"
|
||||||
|
:data="item"
|
||||||
|
:titleColor="data.fields.name?.color"
|
||||||
|
:subTitleColor="data.fields.introduction.color"
|
||||||
|
:topRadius="data.borderRadiusTop"
|
||||||
|
:bottomRadius="data.borderRadiusBottom"
|
||||||
|
:titleWidth="330 - marginLeft - marginRight"
|
||||||
|
@click="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||||
|
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||||
|
>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<template v-slot:cart>
|
||||||
|
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||||
|
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</s-goods-column>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
/**
|
||||||
|
* 商品卡片
|
||||||
|
*/
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
|
import sheep from '@/sheep';
|
||||||
|
import SpuApi from '@/sheep/api/product/spu';
|
||||||
|
import PointApi from '@/sheep/api/promotion/point';
|
||||||
|
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||||
|
|
||||||
|
// 布局类型
|
||||||
|
const LayoutTypeEnum = {
|
||||||
|
// 单列大图
|
||||||
|
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||||
|
// 双列
|
||||||
|
TWO_COL: 'twoCol',
|
||||||
|
// 单列小图
|
||||||
|
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
spuList: [],
|
||||||
|
leftSpuList: [],
|
||||||
|
rightSpuList: [],
|
||||||
|
});
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default() {},
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
type: Object,
|
||||||
|
default() {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { layoutType, btnBuy, activityIds } = props.data || {};
|
||||||
|
const { marginLeft, marginRight } = props.styles || {};
|
||||||
|
|
||||||
|
// 购买按钮样式
|
||||||
|
const buyStyle = computed(() => {
|
||||||
|
if (btnBuy.type === 'text') {
|
||||||
|
// 文字按钮:线性渐变背景颜色
|
||||||
|
return {
|
||||||
|
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (btnBuy.type === 'img') {
|
||||||
|
// 图片按钮
|
||||||
|
return {
|
||||||
|
width: '54rpx',
|
||||||
|
height: '54rpx',
|
||||||
|
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
|
||||||
|
backgroundSize: '100% 100%',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//region 商品瀑布流布局
|
||||||
|
// 下一个要处理的商品索引
|
||||||
|
let count = 0;
|
||||||
|
// 左列的高度
|
||||||
|
let leftHeight = 0;
|
||||||
|
// 右列的高度
|
||||||
|
let rightHeight = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算商品在左列还是右列
|
||||||
|
* @param height 商品的高度
|
||||||
|
* @param where 添加到哪一列
|
||||||
|
*/
|
||||||
|
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||||
|
// 处理完
|
||||||
|
if (!state.spuList[count]) return;
|
||||||
|
// 增加列的高度
|
||||||
|
if (where === 'left') leftHeight += height;
|
||||||
|
if (where === 'right') rightHeight += height;
|
||||||
|
// 添加到矮的一列
|
||||||
|
if (leftHeight <= rightHeight) {
|
||||||
|
state.leftSpuList.push(state.spuList[count]);
|
||||||
|
} else {
|
||||||
|
state.rightSpuList.push(state.spuList[count]);
|
||||||
|
}
|
||||||
|
// 计数
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据商品编号列表,获取商品列表
|
||||||
|
* @param ids 商品编号列表
|
||||||
|
* @return {Promise<undefined>} 商品列表
|
||||||
|
*/
|
||||||
|
async function getPointActivityDetailList(ids) {
|
||||||
|
const { data } = await PointApi.getPointActivityListByIds(ids);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据商品编号,获取商品详情
|
||||||
|
* @param ids 商品编号列表
|
||||||
|
* @return {Promise<undefined>} 商品列表
|
||||||
|
*/
|
||||||
|
async function getSpuDetail(ids) {
|
||||||
|
const { data: spu } = await SpuApi.getSpuDetail(ids);
|
||||||
|
return spu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(async () => {
|
||||||
|
// 加载活动列表
|
||||||
|
const activityList = await getPointActivityDetailList(activityIds.join(','));
|
||||||
|
// 循环获取活动商品SPU详情并添加到spuList
|
||||||
|
for (const activity of activityList) {
|
||||||
|
state.spuList.push(await getSpuDetail(activity.spuId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环活动列表
|
||||||
|
activityList.forEach((activity) => {
|
||||||
|
// 查找对应的 spu 并更新价格
|
||||||
|
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
|
||||||
|
if (spu) {
|
||||||
|
spu.pointStock = activity.stock
|
||||||
|
spu.pointTotalStock = activity.totalStock
|
||||||
|
spu.point = activity.point
|
||||||
|
spu.pointPrice = activity.price
|
||||||
|
// 赋值活动ID,为了点击跳转详情页
|
||||||
|
spu.activityId = activity.id;
|
||||||
|
// 赋值活动类型
|
||||||
|
spu.activityType = PromotionActivityTypeEnum.POINT.type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 只有双列布局时需要
|
||||||
|
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||||
|
// 分列
|
||||||
|
calculateGoodsColumn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.goods-md-wrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-list-box {
|
||||||
|
width: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.left-list {
|
||||||
|
&:nth-last-child(1) {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-list {
|
||||||
|
&:nth-last-child(1) {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-box {
|
||||||
|
&:nth-last-of-type(1) {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-md-box,
|
||||||
|
.goods-sl-box,
|
||||||
|
.goods-lg-box {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.cart-btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 18rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
z-index: 11;
|
||||||
|
height: 50rpx;
|
||||||
|
line-height: 50rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
border-radius: 25rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,458 @@
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<!-- md卡片:竖向,一行放两个,图上内容下 -->
|
||||||
|
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||||
|
<image
|
||||||
|
class="md-img-box"
|
||||||
|
:src="sheep.$url.cdn(data.image)"
|
||||||
|
mode="widthFix"
|
||||||
|
@load="calculatePanelHeight"
|
||||||
|
></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"
|
||||||
|
class="md-goods-title ss-line-1"
|
||||||
|
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
|
||||||
|
>
|
||||||
|
{{ data.title }}
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.subtitle?.show"
|
||||||
|
class="md-goods-subtitle ss-m-t-16 ss-line-1"
|
||||||
|
:style="[{ color: subTitleColor }]"
|
||||||
|
>
|
||||||
|
{{ data.subtitle }}
|
||||||
|
</view>
|
||||||
|
<view class="ss-col-bottom">
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.score_price?.show"
|
||||||
|
class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10 ss-flex"
|
||||||
|
:style="[{ color: goodsFields.score_price.color }]"
|
||||||
|
>
|
||||||
|
<view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
|
||||||
|
<image
|
||||||
|
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||||
|
class="score-img"
|
||||||
|
></image>
|
||||||
|
{{ data.score }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.price?.show && data.original_price > 0"
|
||||||
|
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||||
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
|
>
|
||||||
|
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||||
|
<view class="ss-m-l-8">{{ data.original_price }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
|
||||||
|
<view class="sales-text">{{ salesAndStock }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<slot name="cart">
|
||||||
|
<view class="cart-box ss-flex ss-col-center ss-row-center">
|
||||||
|
<image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode=""></image>
|
||||||
|
</view>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
<!-- lg卡片:横向型,一行放一个,图片左内容右边 -->
|
||||||
|
<view
|
||||||
|
v-if="size === 'lg'"
|
||||||
|
class="lg-goods-card ss-flex ss-col-stretch"
|
||||||
|
:style="[elStyles]"
|
||||||
|
@tap="onClick"
|
||||||
|
>
|
||||||
|
<image class="lg-img-box" :src="sheep.$url.cdn(data.image)" 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="ss-m-r-20">
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.title?.show"
|
||||||
|
class="lg-goods-title ss-line-2"
|
||||||
|
:style="[{ color: titleColor }]"
|
||||||
|
>
|
||||||
|
{{ data.title }}
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.subtitle?.show"
|
||||||
|
class="lg-goods-subtitle ss-m-t-10 ss-line-1"
|
||||||
|
:style="[{ color: subTitleColor }]"
|
||||||
|
>
|
||||||
|
{{ data.subtitle }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<view class="ss-m-t-10">
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.score_price?.show"
|
||||||
|
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
|
||||||
|
:style="[{ color: goodsFields.score_price.color }]"
|
||||||
|
>
|
||||||
|
<view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
|
||||||
|
<image
|
||||||
|
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||||
|
class="score-img"
|
||||||
|
></image>
|
||||||
|
{{ data.score }}
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.price?.show && data.original_price > 0"
|
||||||
|
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
|
||||||
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
|
>
|
||||||
|
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||||
|
<view class="ss-m-l-8">{{ data.original_price }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
|
||||||
|
<view class="sales-text">{{ salesAndStock }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<slot name="cart"
|
||||||
|
><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
|
||||||
|
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" @tap="onClick">
|
||||||
|
<image class="sl-img-box" :src="sheep.$url.cdn(data.image)" mode="aspectFill"></image>
|
||||||
|
|
||||||
|
<view class="sl-goods-content ss-flex-col ss-row-between ss-p-b-20 ss-p-t-20">
|
||||||
|
<view class="ss-m-b-20">
|
||||||
|
<view class="sl-goods-title ss-line-1 ss-p-l-16 ss-p-r-16">
|
||||||
|
{{ data.title }}
|
||||||
|
</view>
|
||||||
|
<view v-if="data.subtitle" class="sl-goods-subtitle ss-p-l-16 ss-p-r-16 ss-m-t-16">
|
||||||
|
{{ data.subtitle }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<slot name="activity">
|
||||||
|
<view
|
||||||
|
v-if="data.promos?.length"
|
||||||
|
class="tag-box ss-flex ss-col-center ss-flex-wrap ss-p-l-16 ss-p-r-16"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="activity-tag ss-m-r-10 ss-m-t-16"
|
||||||
|
v-for="item in data.promos"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</slot>
|
||||||
|
<view class="ss-flex ss-col-bottom ss-p-l-16 ss-p-r-16 font-OPPOSANS">
|
||||||
|
<view class="sl-goods-price ss-m-r-12 ss-flex">
|
||||||
|
<view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
|
||||||
|
<image
|
||||||
|
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||||
|
class="score-img"
|
||||||
|
></image>
|
||||||
|
<view>{{ data.score ? data.score : '' }}</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="data.original_price > 0"
|
||||||
|
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||||
|
>
|
||||||
|
<text class="price-unit ss-font-20">¥</text>
|
||||||
|
<view class="ss-m-l-8">{{ data.original_price }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="ss-p-l-16 ss-p-r-16 ss-m-t-16 ss-flex ss-flex-wrap">
|
||||||
|
<view class="sales-text">{{ salesAndStock }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<slot name="cart"
|
||||||
|
><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed, getCurrentInstance } from 'vue';
|
||||||
|
import sheep from '@/sheep';
|
||||||
|
import { formatSales } from '@/sheep/hooks/useGoods';
|
||||||
|
import { formatStock } from '@/sheep/hooks/useGoods';
|
||||||
|
/**
|
||||||
|
* 订单卡片
|
||||||
|
*
|
||||||
|
* @property {String} img - 图片
|
||||||
|
* @property {String} title - 标题
|
||||||
|
* @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
|
||||||
|
* @property {String} skuText - 规格
|
||||||
|
* @property {String | Number} score - 积分
|
||||||
|
* @property {String | Number} price - 价格
|
||||||
|
* @property {String | Number} originalPrice - 单购价
|
||||||
|
* @property {String} priceColor - 价格颜色
|
||||||
|
* @property {Number | String} num - 数量
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const props = defineProps({
|
||||||
|
goodsFields: {
|
||||||
|
type: [Array, Object],
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
title: { show: true },
|
||||||
|
subtitle: { show: true },
|
||||||
|
price: { show: true },
|
||||||
|
original_price: { show: true },
|
||||||
|
sales: { show: true },
|
||||||
|
stock: { show: true },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tagStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'sl',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
topRadius: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
bottomRadius: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
titleWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
titleColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#333',
|
||||||
|
},
|
||||||
|
priceUnit: {
|
||||||
|
type: String,
|
||||||
|
default: '¥',
|
||||||
|
},
|
||||||
|
subTitleColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#999999',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 组件样式
|
||||||
|
const elStyles = computed(() => {
|
||||||
|
return {
|
||||||
|
background: props.background,
|
||||||
|
'border-top-left-radius': props.topRadius + 'px',
|
||||||
|
'border-top-right-radius': props.topRadius + 'px',
|
||||||
|
'border-bottom-left-radius': props.bottomRadius + 'px',
|
||||||
|
'border-bottom-right-radius': props.bottomRadius + 'px',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const emits = defineEmits(['click', 'getHeight']);
|
||||||
|
const onClick = () => {
|
||||||
|
emits('click');
|
||||||
|
};
|
||||||
|
// 格式化销量、库存信息
|
||||||
|
const salesAndStock = computed(() => {
|
||||||
|
let text = [];
|
||||||
|
text.push(formatSales(props.data.sales_show_type, props.data.sales));
|
||||||
|
text.push(formatStock(props.data.stock_show_type, props.data.stock));
|
||||||
|
return text.join(' | ');
|
||||||
|
});
|
||||||
|
// 获取实时卡片高度
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
|
||||||
|
function calculatePanelHeight(e) {
|
||||||
|
if (props.size === 'md') {
|
||||||
|
const view = uni.createSelectorQuery().in(proxy);
|
||||||
|
view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
|
||||||
|
view.exec((data) => {
|
||||||
|
const goodsPriceCard = data[0];
|
||||||
|
const card = {
|
||||||
|
width: goodsPriceCard.width,
|
||||||
|
height: (goodsPriceCard.width / e.detail.width) * e.detail.height + goodsPriceCard.height,
|
||||||
|
};
|
||||||
|
emits('getHeight', card.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.price-unit {
|
||||||
|
margin-right: -4px;
|
||||||
|
}
|
||||||
|
.sales-text {
|
||||||
|
display: table;
|
||||||
|
font-size: 24rpx;
|
||||||
|
transform: scale(0.8);
|
||||||
|
margin-left: -16rpx;
|
||||||
|
color: #c4c4c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// md
|
||||||
|
.md-goods-card {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: $white;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.md-img-box {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-goods-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.md-goods-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-goods-price {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: $red;
|
||||||
|
line-height: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-box {
|
||||||
|
width: 54rpx;
|
||||||
|
height: 54rpx;
|
||||||
|
background: linear-gradient(90deg, #fe8900, #ff5e00);
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 50rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.cart-icon {
|
||||||
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lg
|
||||||
|
.lg-goods-card {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: $white;
|
||||||
|
height: 280rpx;
|
||||||
|
|
||||||
|
.lg-img-box {
|
||||||
|
width: 280rpx;
|
||||||
|
height: 280rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-goods-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
|
// line-height: 36rpx;
|
||||||
|
// width: 410rpx;
|
||||||
|
}
|
||||||
|
.lg-goods-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 30rpx;
|
||||||
|
// width: 410rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-goods-price {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: $red;
|
||||||
|
line-height: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-box {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
z-index: 2;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||||
|
border-radius: 25rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
.tag-box {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sl-goods-card {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
.sl-img-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 360rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl-goods-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.sl-goods-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 30rpx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl-goods-price {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-box {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
z-index: 2;
|
||||||
|
width: 148rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background: linear-gradient(90deg, #fe8900, #ff5e00);
|
||||||
|
border-radius: 25rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.goods-origin-price {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #c4c4c4;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.score-img {
|
||||||
|
width: 36rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
margin: 0 4rpx;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -19,8 +19,11 @@
|
||||||
<view class="goods-title ss-line-2">{{ state.goodsInfo.name }}</view>
|
<view class="goods-title ss-line-2">{{ state.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="price-text">
|
<view v-if="state.goodsInfo.activity_type === PromotionActivityTypeEnum.POINT.type" class="price-text">
|
||||||
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
|
{{ getShowPriceText }}
|
||||||
|
</view>
|
||||||
|
<view v-else class="price-text">
|
||||||
|
¥{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
|
||||||
</view>
|
</view>
|
||||||
<!-- 秒杀价格标签 -->
|
<!-- 秒杀价格标签 -->
|
||||||
<view class="tig ss-flex ss-col-center">
|
<view class="tig ss-flex ss-col-center">
|
||||||
|
@ -92,12 +95,15 @@
|
||||||
import { computed, reactive, watch } from 'vue';
|
import { computed, reactive, watch } from 'vue';
|
||||||
import sheep from '@/sheep';
|
import sheep from '@/sheep';
|
||||||
import { convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
|
import { convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
|
||||||
import { min } from 'lodash-es';
|
import { isEmpty, min } from 'lodash-es';
|
||||||
|
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||||
|
|
||||||
const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
|
const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default() {},
|
default() {
|
||||||
|
},
|
||||||
},
|
},
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -114,7 +120,14 @@
|
||||||
selectedSku: {},
|
selectedSku: {},
|
||||||
currentPropertyArray: [],
|
currentPropertyArray: [],
|
||||||
});
|
});
|
||||||
|
const getShowPriceText = computed(() => {
|
||||||
|
let priceText = `¥${fen2yuan(state.goodsInfo.price)}`;
|
||||||
|
if (!isEmpty(state.selectedSku)) {
|
||||||
|
const sku = state.selectedSku;
|
||||||
|
priceText = `${sku.point}积分${!sku.pointPrice ? '' : `+¥${fen2yuan(sku.pointPrice)}`}`;
|
||||||
|
}
|
||||||
|
return priceText;
|
||||||
|
});
|
||||||
const propertyList = convertProductPropertyList(state.goodsInfo.skus);
|
const propertyList = convertProductPropertyList(state.goodsInfo.skus);
|
||||||
// SKU 列表
|
// SKU 列表
|
||||||
const skuList = computed(() => {
|
const skuList = computed(() => {
|
||||||
|
@ -344,11 +357,6 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: $red;
|
color: $red;
|
||||||
font-family: OPPOSANS;
|
font-family: OPPOSANS;
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '¥';
|
|
||||||
font-size: 24rpx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-text {
|
.stock-text {
|
||||||
|
|
|
@ -1,106 +1,139 @@
|
||||||
<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
|
||||||
</view>
|
class="sku-image"
|
||||||
<view class="header-right ss-flex-col ss-row-between ss-flex-1">
|
:src="state.selectedSku.picUrl || goodsInfo.picUrl"
|
||||||
<view class="goods-title ss-line-2">{{ goodsInfo.name }}</view>
|
mode="aspectFill"
|
||||||
<view class="header-right-bottom ss-flex ss-col-center ss-row-between">
|
/>
|
||||||
<view class="ss-flex">
|
</view>
|
||||||
<view class="price-text">
|
<view class="header-right ss-flex-col ss-row-between ss-flex-1">
|
||||||
{{ fen2yuan( state.selectedSku.price || goodsInfo.price) }}
|
<view class="goods-title ss-line-2">{{ goodsInfo.name }}</view>
|
||||||
</view>
|
<view class="header-right-bottom ss-flex ss-col-center ss-row-between">
|
||||||
</view>
|
<view class="ss-flex">
|
||||||
<view class="stock-text ss-m-l-20">
|
<view class="price-text">
|
||||||
{{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}
|
{{
|
||||||
</view>
|
fen2yuan(
|
||||||
</view>
|
state.selectedSku.promotionPrice || state.selectedSku.price || goodsInfo.price,
|
||||||
</view>
|
)
|
||||||
</view>
|
}}
|
||||||
|
<text v-if="state.selectedSku.promotionType > 0">
|
||||||
|
<text class="iconBox" v-if="state.selectedSku.promotionType === 4">
|
||||||
|
限时优惠
|
||||||
|
</text>
|
||||||
|
<text class="iconBox" v-else-if="state.selectedSku.promotionType === 6">
|
||||||
|
会员价
|
||||||
|
</text>
|
||||||
|
<text class="origin-price-text">
|
||||||
|
{{ fen2yuan(state.selectedSku.price) }}
|
||||||
|
</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="stock-text ss-m-l-20">
|
||||||
|
{{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 属性选择 -->
|
<!-- 属性选择 -->
|
||||||
<view class="modal-content ss-flex-1">
|
<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,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'disabled-btn': value.disabled === true,
|
'disabled-btn': value.disabled === true,
|
||||||
},
|
},
|
||||||
]" :key="value.id" :disabled="value.disabled === true" @tap="onSelectSku(property.id, value.id)">
|
]"
|
||||||
{{ value.name }}
|
:key="value.id"
|
||||||
</button>
|
:disabled="value.disabled === true"
|
||||||
</view>
|
@tap="onSelectSku(property.id, value.id)"
|
||||||
</view>
|
>
|
||||||
<view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40">
|
{{ value.name }}
|
||||||
<view class="label-text">购买数量</view>
|
</button>
|
||||||
<su-number-box :min="1" :max="state.selectedSku.stock" :step="1"
|
</view>
|
||||||
v-model="state.selectedSku.goods_num" @change="onNumberChange($event)" />
|
</view>
|
||||||
</view>
|
<view class="buy-num-box ss-flex ss-col-center ss-row-between ss-m-b-40">
|
||||||
</scroll-view>
|
<view class="label-text">购买数量</view>
|
||||||
</view>
|
<su-number-box
|
||||||
|
:min="1"
|
||||||
|
:max="state.selectedSku.stock"
|
||||||
|
:step="1"
|
||||||
|
v-model="state.selectedSku.goods_num"
|
||||||
|
@change="onNumberChange($event)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 操作区 -->
|
<!-- 操作区 -->
|
||||||
<view 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 class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy">立即购买</button>
|
>加入购物车</button
|
||||||
</view>
|
>
|
||||||
</view>
|
<button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="onBuy">立即购买</button>
|
||||||
</view>
|
</view>
|
||||||
</su-popup>
|
</view>
|
||||||
|
</view>
|
||||||
|
</su-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, watch } from 'vue';
|
import { computed, reactive, watch } from 'vue';
|
||||||
import sheep from '@/sheep';
|
import sheep from '@/sheep';
|
||||||
import { formatStock, convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
|
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: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
|
currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 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, // 立即执行
|
{
|
||||||
deep: true, // 深度监听
|
immediate: true, // 立即执行
|
||||||
},
|
deep: true, // 深度监听
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// 输入框改变数量
|
// 输入框改变数量
|
||||||
function onNumberChange(e) {
|
function onNumberChange(e) {
|
||||||
|
@ -110,21 +143,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 +167,298 @@
|
||||||
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 &&
|
||||||
isChecked = false;
|
state.currentPropertyArray[propertyId] === valueId
|
||||||
state.currentPropertyArray.splice(propertyId, 1, '');
|
) {
|
||||||
} else {
|
// 点击已被选中的,删除并填充 ''
|
||||||
// 选中
|
isChecked = false;
|
||||||
state.currentPropertyArray[propertyId] = valueId;
|
state.currentPropertyArray.splice(propertyId, 1, '');
|
||||||
}
|
} else {
|
||||||
|
// 选中
|
||||||
|
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 芋艿:待讨论的优化点:1)单规格,要不要默认选中;2)默认要不要选中第一个规格
|
// TODO 芋艿:待讨论的优化点:1)单规格,要不要默认选中;2)默认要不要选中第一个规格
|
||||||
</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>
|
|
@ -11,7 +11,7 @@ import { formatDate } from '@/sheep/util';
|
||||||
*/
|
*/
|
||||||
export function formatSales(type, num) {
|
export function formatSales(type, num) {
|
||||||
let prefix = type !== 'exact' && num < 10 ? '销量' : '已售';
|
let prefix = type !== 'exact' && num < 10 ? '销量' : '已售';
|
||||||
return formatNum(prefix, type, num)
|
return formatNum(prefix, type, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,10 +21,9 @@ export function formatSales(type, num) {
|
||||||
* @return {string} 格式化后的销量字符串
|
* @return {string} 格式化后的销量字符串
|
||||||
*/
|
*/
|
||||||
export function formatExchange(type, num) {
|
export function formatExchange(type, num) {
|
||||||
return formatNum('已兑换', type, num)
|
return formatNum('已兑换', type, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化库存
|
* 格式化库存
|
||||||
* @param {'exact' | any} type 格式类型:exact=精确值,其它=大致数量
|
* @param {'exact' | any} type 格式类型:exact=精确值,其它=大致数量
|
||||||
|
@ -32,7 +31,7 @@ export function formatExchange(type, num) {
|
||||||
* @return {string} 格式化后的销量字符串
|
* @return {string} 格式化后的销量字符串
|
||||||
*/
|
*/
|
||||||
export function formatStock(type, num) {
|
export function formatStock(type, num) {
|
||||||
return formatNum('库存', type, num)
|
return formatNum('库存', type, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +42,7 @@ export function formatStock(type, num) {
|
||||||
* @return {string} 格式化后的销量字符串
|
* @return {string} 格式化后的销量字符串
|
||||||
*/
|
*/
|
||||||
export function formatNum(prefix, type, num) {
|
export function formatNum(prefix, type, num) {
|
||||||
num = (num || 0);
|
num = num || 0;
|
||||||
// 情况一:精确数值
|
// 情况一:精确数值
|
||||||
if (type === 'exact') {
|
if (type === 'exact') {
|
||||||
return prefix + num;
|
return prefix + num;
|
||||||
|
@ -67,7 +66,7 @@ export function formatPrice(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 视频格式后缀列表
|
// 视频格式后缀列表
|
||||||
const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
|
const VIDEO_SUFFIX_LIST = ['.avi', '.mp4'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接
|
* 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接
|
||||||
|
@ -76,12 +75,19 @@ const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
|
||||||
* @return {{src: string, type: 'video' | 'image' }[]} 转换后的链接列表
|
* @return {{src: string, type: 'video' | 'image' }[]} 转换后的链接列表
|
||||||
*/
|
*/
|
||||||
export function formatGoodsSwiper(urlList) {
|
export function formatGoodsSwiper(urlList) {
|
||||||
return urlList?.filter(url => url).map((url, key) => {
|
return (
|
||||||
const isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
|
urlList
|
||||||
const type = isVideo ? 'video' : 'image'
|
?.filter((url) => url)
|
||||||
const src = $url.cdn(url);
|
.map((url, key) => {
|
||||||
return { type, src }
|
const isVideo = VIDEO_SUFFIX_LIST.some((suffix) => url.includes(suffix));
|
||||||
}) || [];
|
const type = isVideo ? 'video' : 'image';
|
||||||
|
const src = $url.cdn(url);
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
src,
|
||||||
|
};
|
||||||
|
}) || []
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,9 +100,7 @@ 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 === 30 && !order.commentStatus)) {
|
||||||
|| order.status === 20
|
|
||||||
|| (order.status === 30 && !order.commentStatus)) {
|
|
||||||
return 'warning-color';
|
return 'warning-color';
|
||||||
}
|
}
|
||||||
if (order.status === 30 && order.commentStatus) {
|
if (order.status === 30 && order.commentStatus) {
|
||||||
|
@ -139,7 +143,7 @@ export function formatOrderStatus(order) {
|
||||||
*/
|
*/
|
||||||
export function formatOrderStatusDescription(order) {
|
export function formatOrderStatusDescription(order) {
|
||||||
if (order.status === 0) {
|
if (order.status === 0) {
|
||||||
return `请在 ${ formatDate(order.payExpireTime) } 前完成支付`;
|
return `请在 ${formatDate(order.payExpireTime)} 前完成支付`;
|
||||||
}
|
}
|
||||||
if (order.status === 10) {
|
if (order.status === 10) {
|
||||||
return '商家未发货,请耐心等待';
|
return '商家未发货,请耐心等待';
|
||||||
|
@ -162,24 +166,30 @@ export function formatOrderStatusDescription(order) {
|
||||||
* @param order 订单
|
* @param order 订单
|
||||||
*/
|
*/
|
||||||
export function handleOrderButtons(order) {
|
export function handleOrderButtons(order) {
|
||||||
order.buttons = []
|
order.buttons = [];
|
||||||
if (order.type === 3) { // 查看拼团
|
if (order.type === 3) {
|
||||||
|
// 查看拼团
|
||||||
order.buttons.push('combination');
|
order.buttons.push('combination');
|
||||||
}
|
}
|
||||||
if (order.status === 20) { // 确认收货
|
if (order.status === 20) {
|
||||||
|
// 确认收货
|
||||||
order.buttons.push('confirm');
|
order.buttons.push('confirm');
|
||||||
}
|
}
|
||||||
if (order.logisticsId > 0) { // 查看物流
|
if (order.logisticsId > 0) {
|
||||||
|
// 查看物流
|
||||||
order.buttons.push('express');
|
order.buttons.push('express');
|
||||||
}
|
}
|
||||||
if (order.status === 0) { // 取消订单 / 发起支付
|
if (order.status === 0) {
|
||||||
|
// 取消订单 / 发起支付
|
||||||
order.buttons.push('cancel');
|
order.buttons.push('cancel');
|
||||||
order.buttons.push('pay');
|
order.buttons.push('pay');
|
||||||
}
|
}
|
||||||
if (order.status === 30 && !order.commentStatus) { // 发起评价
|
if (order.status === 30 && !order.commentStatus) {
|
||||||
|
// 发起评价
|
||||||
order.buttons.push('comment');
|
order.buttons.push('comment');
|
||||||
}
|
}
|
||||||
if (order.status === 40) { // 删除订单
|
if (order.status === 40) {
|
||||||
|
// 删除订单
|
||||||
order.buttons.push('delete');
|
order.buttons.push('delete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,10 +267,12 @@ export function formatAfterSaleStatusDescription(afterSale) {
|
||||||
*/
|
*/
|
||||||
export function handleAfterSaleButtons(afterSale) {
|
export function handleAfterSaleButtons(afterSale) {
|
||||||
afterSale.buttons = [];
|
afterSale.buttons = [];
|
||||||
if ([10, 20, 30].includes(afterSale.status)) { // 取消订单
|
if ([10, 20, 30].includes(afterSale.status)) {
|
||||||
|
// 取消订单
|
||||||
afterSale.buttons.push('cancel');
|
afterSale.buttons.push('cancel');
|
||||||
}
|
}
|
||||||
if (afterSale.status === 20) { // 退货信息
|
if (afterSale.status === 20) {
|
||||||
|
// 退货信息
|
||||||
afterSale.buttons.push('delivery');
|
afterSale.buttons.push('delivery');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +336,28 @@ function getDayjsTime(time) {
|
||||||
* @returns {string} 元,例如说 1.00 元
|
* @returns {string} 元,例如说 1.00 元
|
||||||
*/
|
*/
|
||||||
export function fen2yuan(price) {
|
export function fen2yuan(price) {
|
||||||
return (price / 100.0).toFixed(2)
|
return (price / 100.0).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将分转成元
|
||||||
|
*
|
||||||
|
* 如果没有小数点,则不展示小数点部分
|
||||||
|
*
|
||||||
|
* @param price 分,例如说 100 分
|
||||||
|
* @returns {string} 元,例如说 1 元
|
||||||
|
*/
|
||||||
|
export function fen2yuanSimple(price) {
|
||||||
|
return fen2yuan(price).replace(/\.?0+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将折扣百分比转化为“打x者”的 x 部分
|
||||||
|
*
|
||||||
|
* @param discountPercent
|
||||||
|
*/
|
||||||
|
export function formatDiscountPercent(discountPercent) {
|
||||||
|
return (discountPercent / 10.0).toFixed(1).replace(/\.?0+$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -345,45 +378,122 @@ export function convertProductPropertyList(skus) {
|
||||||
let result = [];
|
let result = [];
|
||||||
for (const sku of skus) {
|
for (const sku of skus) {
|
||||||
if (!sku.properties) {
|
if (!sku.properties) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
for (const property of sku.properties) {
|
for (const property of sku.properties) {
|
||||||
// ① 先处理属性
|
// ① 先处理属性
|
||||||
let resultProperty = result.find(item => item.id === property.propertyId)
|
let resultProperty = result.find((item) => item.id === property.propertyId);
|
||||||
if (!resultProperty) {
|
if (!resultProperty) {
|
||||||
resultProperty = {
|
resultProperty = {
|
||||||
id: property.propertyId,
|
id: property.propertyId,
|
||||||
name: property.propertyName,
|
name: property.propertyName,
|
||||||
values: []
|
values: [],
|
||||||
}
|
};
|
||||||
result.push(resultProperty)
|
result.push(resultProperty);
|
||||||
}
|
}
|
||||||
// ② 再处理属性值
|
// ② 再处理属性值
|
||||||
let resultValue = resultProperty.values.find(item => item.id === property.valueId)
|
let resultValue = resultProperty.values.find((item) => item.id === property.valueId);
|
||||||
if (!resultValue) {
|
if (!resultValue) {
|
||||||
resultProperty.values.push({
|
resultProperty.values.push({
|
||||||
id: property.valueId,
|
id: property.valueId,
|
||||||
name: property.valueName
|
name: property.valueName,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function appendSettlementProduct(spus, settlementInfos) {
|
||||||
* 格式化满减送活动的规则
|
if (!settlementInfos || settlementInfos.length === 0) {
|
||||||
*
|
return;
|
||||||
* @param activity 活动信息
|
|
||||||
* @param rule 优惠规格
|
|
||||||
* @returns {string} 规格字符串
|
|
||||||
*/
|
|
||||||
export function formatRewardActivityRule(activity, rule) {
|
|
||||||
if (activity.conditionType === 10) {
|
|
||||||
return `满 ${fen2yuan(rule.limit)} 元减 ${fen2yuan(rule.discountPrice)} 元`;
|
|
||||||
}
|
}
|
||||||
if (activity.conditionType === 20) {
|
for (const spu of spus) {
|
||||||
return `满 ${rule.limit} 件减 ${fen2yuan(rule.discountPrice)} 元`;
|
const settlementInfo = settlementInfos.find((info) => info.spuId === spu.id);
|
||||||
|
if (!settlementInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 选择价格最小的 SKU 设置到 SPU 上
|
||||||
|
const settlementSku = settlementInfo.skus
|
||||||
|
.filter((sku) => sku.promotionPrice > 0)
|
||||||
|
.reduce((prev, curr) => (prev.promotionPrice < curr.promotionPrice ? prev : curr));
|
||||||
|
if (settlementSku) {
|
||||||
|
spu.promotionType = settlementSku.promotionType;
|
||||||
|
spu.promotionPrice = settlementSku.promotionPrice;
|
||||||
|
}
|
||||||
|
// 设置【满减送】活动
|
||||||
|
if (settlementInfo.rewardActivity) {
|
||||||
|
spu.rewardActivity = settlementInfo.rewardActivity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return '';
|
}
|
||||||
|
|
||||||
|
// 获得满减送活动的规则描述(group)
|
||||||
|
export function getRewardActivityRuleGroupDescriptions(activity) {
|
||||||
|
if (!activity || !activity.rules || activity.rules.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result = [
|
||||||
|
{ name: '满减', values: [] },
|
||||||
|
{ name: '赠品', values: [] },
|
||||||
|
{ name: '包邮', values: [] },
|
||||||
|
];
|
||||||
|
activity.rules.forEach((rule) => {
|
||||||
|
const conditionTypeStr =
|
||||||
|
activity.conditionType === 10 ? `满 ${fen2yuanSimple(rule.limit)} 元` : `满 ${rule.limit} 件`;
|
||||||
|
// 满减
|
||||||
|
if (rule.limit) {
|
||||||
|
result[0].values.push(`${conditionTypeStr} 减 ${fen2yuanSimple(rule.discountPrice)} 元`);
|
||||||
|
}
|
||||||
|
// 赠品
|
||||||
|
if (rule.point || (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0)) {
|
||||||
|
let tips = [];
|
||||||
|
if (rule.point) {
|
||||||
|
tips.push(`送 ${rule.point} 积分`);
|
||||||
|
}
|
||||||
|
if (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0) {
|
||||||
|
tips.push(`送 ${rule.giveCouponTemplateCounts.length} 张优惠券`);
|
||||||
|
}
|
||||||
|
result[1].values.push(`${conditionTypeStr} ${tips.join('、')}`);
|
||||||
|
}
|
||||||
|
// 包邮
|
||||||
|
if (rule.freeDelivery) {
|
||||||
|
result[2].values.push(`${conditionTypeStr} 包邮`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 移除 values 为空的元素
|
||||||
|
result.forEach((item) => {
|
||||||
|
if (item.values.length === 0) {
|
||||||
|
result.splice(result.indexOf(item), 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得满减送活动的规则描述(item)
|
||||||
|
export function getRewardActivityRuleItemDescriptions(activity) {
|
||||||
|
if (!activity || !activity.rules || activity.rules.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result = [];
|
||||||
|
activity.rules.forEach((rule) => {
|
||||||
|
const conditionTypeStr =
|
||||||
|
activity.conditionType === 10 ? `满${fen2yuanSimple(rule.limit)}元` : `满${rule.limit}件`;
|
||||||
|
// 满减
|
||||||
|
if (rule.limit) {
|
||||||
|
result.push(`${conditionTypeStr}减${fen2yuanSimple(rule.discountPrice)}元`);
|
||||||
|
}
|
||||||
|
// 赠品
|
||||||
|
if (rule.point) {
|
||||||
|
result.push(`${conditionTypeStr}送${rule.point}积分`);
|
||||||
|
}
|
||||||
|
if (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0) {
|
||||||
|
result.push(`${conditionTypeStr}送${rule.giveCouponTemplateCounts.length}张优惠券`);
|
||||||
|
}
|
||||||
|
// 包邮
|
||||||
|
if (rule.freeDelivery) {
|
||||||
|
result.push(`${conditionTypeStr}包邮`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
mock: () => {
|
mock: () => {
|
||||||
this.mockPay();
|
this.mockPay();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
WechatMiniProgram: {
|
WechatMiniProgram: {
|
||||||
wechat: () => {
|
wechat: () => {
|
||||||
|
@ -49,7 +49,7 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
mock: () => {
|
mock: () => {
|
||||||
this.mockPay();
|
this.mockPay();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
App: {
|
App: {
|
||||||
wechat: () => {
|
wechat: () => {
|
||||||
|
@ -63,7 +63,7 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
mock: () => {
|
mock: () => {
|
||||||
this.mockPay();
|
this.mockPay();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
H5: {
|
H5: {
|
||||||
wechat: () => {
|
wechat: () => {
|
||||||
|
@ -77,7 +77,7 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
mock: () => {
|
mock: () => {
|
||||||
this.mockPay();
|
this.mockPay();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return payAction[sheep.$platform.name][this.payment]();
|
return payAction[sheep.$platform.name][this.payment]();
|
||||||
|
@ -89,7 +89,7 @@ export default class SheepPay {
|
||||||
let data = {
|
let data = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
channelCode: channel,
|
channelCode: channel,
|
||||||
channelExtras: {}
|
channelExtras: {},
|
||||||
};
|
};
|
||||||
// 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
|
// 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
|
||||||
if (['wx_pub', 'wx_lite'].includes(channel)) {
|
if (['wx_pub', 'wx_lite'].includes(channel)) {
|
||||||
|
@ -108,8 +108,11 @@ export default class SheepPay {
|
||||||
// 失败时
|
// 失败时
|
||||||
if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
|
if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
|
||||||
// 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
|
// 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
|
||||||
if (res.msg.indexOf('无效的openid') >= 0 // 获取的 openid 不正确时,或者随便输入了个 openid
|
if (
|
||||||
|| res.msg.indexOf('下单账号与支付账号不一致') >= 0) { // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
|
res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid
|
||||||
|
res.msg.indexOf('下单账号与支付账号不一致') >= 0
|
||||||
|
) {
|
||||||
|
// https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
|
||||||
this.bindWeixin();
|
this.bindWeixin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,30 +136,34 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
fail: (error) => {
|
fail: (error) => {
|
||||||
if (error.errMsg.indexOf('chooseWXPay:没有此SDK或暂不支持此SDK模拟') >= 0) {
|
if (error.errMsg.indexOf('chooseWXPay:没有此SDK或暂不支持此SDK模拟') >= 0) {
|
||||||
sheep.$helper.toast('发起微信支付失败,原因:可能是微信开发者工具不支持,建议使用微信打开网页后支付');
|
sheep.$helper.toast(
|
||||||
return
|
'发起微信支付失败,原因:可能是微信开发者工具不支持,建议使用微信打开网页后支付',
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.payResult('fail');
|
this.payResult('fail');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 浏览器微信 H5 支付 TODO 芋艿:待接入
|
// 浏览器微信 H5 支付 TODO 芋艿:待接入(注意:H5 支付是给普通浏览器,不是微信公众号的支付,绝大多数人用不到,可以忽略)
|
||||||
async wechatWapPay() {
|
async wechatWapPay() {
|
||||||
const { error, data } = await this.prepay();
|
const { error, data } = await this.prepay();
|
||||||
if (error === 0) {
|
if (error === 0) {
|
||||||
const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${this.payment}&orderType=${this.orderType}`;
|
const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${
|
||||||
|
this.payment
|
||||||
|
}&orderType=${this.orderType}`;
|
||||||
location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
|
location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支付链接 TODO 芋艿:待接入
|
// 支付链接(支付宝 wap 支付)
|
||||||
async redirectPay() {
|
async redirectPay() {
|
||||||
let { error, data } = await this.prepay();
|
let { code, data } = await this.prepay('alipay_wap');
|
||||||
if (error === 0) {
|
if (code !== 0) {
|
||||||
const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${this.payment}&orderType=${this.orderType}`;
|
return;
|
||||||
location.href = data.pay_data + encodeURIComponent(redirect_url);
|
|
||||||
}
|
}
|
||||||
|
location.href = data.displayContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #endif
|
// #endif
|
||||||
|
@ -202,26 +209,26 @@ export default class SheepPay {
|
||||||
code === 0 && this.payResult('success');
|
code === 0 && this.payResult('success');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支付宝复制链接支付 TODO 芋艿:待接入
|
// 支付宝复制链接支付(通过支付宝 wap 支付实现)
|
||||||
async copyPayLink() {
|
async copyPayLink() {
|
||||||
let that = this;
|
let { code, data } = await this.prepay('alipay_wap');
|
||||||
let { error, data } = await this.prepay();
|
if (code !== 0) {
|
||||||
if (error === 0) {
|
return;
|
||||||
// 引入showModal 点击确认 复制链接;
|
|
||||||
uni.showModal({
|
|
||||||
title: '支付宝支付',
|
|
||||||
content: '复制链接到外部浏览器',
|
|
||||||
confirmText: '复制链接',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
sheep.$helper.copyText(data.pay_data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// 引入 showModal 点击确认:复制链接;
|
||||||
|
uni.showModal({
|
||||||
|
title: '支付宝支付',
|
||||||
|
content: '复制链接到外部浏览器',
|
||||||
|
confirmText: '复制链接',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
sheep.$helper.copyText(data.displayContent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支付宝支付 TODO 芋艿:待接入
|
// 支付宝支付(App) TODO 芋艿:待接入【暂时没打包 app,所以没接入,一般人用不到】
|
||||||
async alipay() {
|
async alipay() {
|
||||||
let that = this;
|
let that = this;
|
||||||
const { error, data } = await this.prepay();
|
const { error, data } = await this.prepay();
|
||||||
|
@ -243,7 +250,7 @@ export default class SheepPay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 微信支付 TODO 芋艿:待接入
|
// 微信支付(App) TODO 芋艿:待接入:待接入【暂时没打包 app,所以没接入,一般人用不到】
|
||||||
async wechatAppPay() {
|
async wechatAppPay() {
|
||||||
let that = this;
|
let that = this;
|
||||||
let { error, data } = await this.prepay();
|
let { error, data } = await this.prepay();
|
||||||
|
@ -263,11 +270,7 @@ export default class SheepPay {
|
||||||
|
|
||||||
// 支付结果跳转,success:成功,fail:失败
|
// 支付结果跳转,success:成功,fail:失败
|
||||||
payResult(resultType) {
|
payResult(resultType) {
|
||||||
sheep.$router.redirect('/pages/pay/result', {
|
goPayResult(this.id, this.orderType, resultType);
|
||||||
id: this.id,
|
|
||||||
orderType: this.orderType,
|
|
||||||
payState: resultType
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 引导绑定微信
|
// 引导绑定微信
|
||||||
|
@ -282,7 +285,6 @@ export default class SheepPay {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPayMethods(channels) {
|
export function getPayMethods(channels) {
|
||||||
|
@ -316,23 +318,28 @@ export function getPayMethods(channels) {
|
||||||
title: '模拟支付',
|
title: '模拟支付',
|
||||||
value: 'mock',
|
value: 'mock',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const platform = sheep.$platform.name
|
const platform = sheep.$platform.name;
|
||||||
|
|
||||||
// 1. 处理【微信支付】
|
// 1. 处理【微信支付】
|
||||||
const wechatMethod = payMethods[0];
|
const wechatMethod = payMethods[0];
|
||||||
if ((platform === 'WechatOfficialAccount' && channels.includes('wx_pub'))
|
if (
|
||||||
|| (platform === 'WechatMiniProgram' && channels.includes('wx_lite'))
|
(platform === 'WechatOfficialAccount' && channels.includes('wx_pub')) ||
|
||||||
|| (platform === 'App' && channels.includes('wx_app'))) {
|
(platform === 'WechatMiniProgram' && channels.includes('wx_lite')) ||
|
||||||
|
(platform === 'App' && channels.includes('wx_app'))
|
||||||
|
) {
|
||||||
wechatMethod.disabled = false;
|
wechatMethod.disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 处理【支付宝支付】
|
// 2. 处理【支付宝支付】
|
||||||
const alipayMethod = payMethods[1];
|
const alipayMethod = payMethods[1];
|
||||||
if ((platform === 'WechatOfficialAccount' && channels.includes('alipay_wap'))
|
if (
|
||||||
|| platform === 'WechatMiniProgram' && channels.includes('alipay_wap')
|
(platform === 'H5' && channels.includes('alipay_wap')) ||
|
||||||
|| platform === 'App' && channels.includes('alipay_app')) {
|
(platform === 'WechatOfficialAccount' && channels.includes('alipay_wap')) ||
|
||||||
|
(platform === 'WechatMiniProgram' && channels.includes('alipay_wap')) ||
|
||||||
|
(platform === 'App' && channels.includes('alipay_app'))
|
||||||
|
) {
|
||||||
alipayMethod.disabled = false;
|
alipayMethod.disabled = false;
|
||||||
}
|
}
|
||||||
// 3. 处理【余额支付】
|
// 3. 处理【余额支付】
|
||||||
|
@ -348,3 +355,12 @@ export function getPayMethods(channels) {
|
||||||
}
|
}
|
||||||
return payMethods;
|
return payMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 支付结果跳转,success:成功,fail:失败
|
||||||
|
export function goPayResult(id, orderType, resultType) {
|
||||||
|
sheep.$router.redirect('/pages/pay/result', {
|
||||||
|
id,
|
||||||
|
orderType,
|
||||||
|
payState: resultType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -33,78 +33,100 @@ export const getTerminal = () => {
|
||||||
|
|
||||||
// ========== MALL - 营销模块 ==========
|
// ========== MALL - 营销模块 ==========
|
||||||
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优惠类型枚举
|
* 优惠类型枚举
|
||||||
*/
|
*/
|
||||||
export const PromotionDiscountTypeEnum = {
|
export const PromotionDiscountTypeEnum = {
|
||||||
PRICE: {
|
PRICE: {
|
||||||
type: 1,
|
type: 1,
|
||||||
name: '满减'
|
name: '满减',
|
||||||
},
|
},
|
||||||
PERCENT: {
|
PERCENT: {
|
||||||
type: 2,
|
type: 2,
|
||||||
name: '折扣'
|
name: '折扣',
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优惠劵模板的有限期类型的枚举
|
* 优惠劵模板的有限期类型的枚举
|
||||||
*/
|
*/
|
||||||
export const CouponTemplateValidityTypeEnum = {
|
export const CouponTemplateValidityTypeEnum = {
|
||||||
DATE: {
|
DATE: {
|
||||||
type: 1,
|
type: 1,
|
||||||
name: '固定日期可用'
|
name: '固定日期可用',
|
||||||
},
|
},
|
||||||
TERM: {
|
TERM: {
|
||||||
type: 2,
|
type: 2,
|
||||||
name: '领取之后可用'
|
name: '领取之后可用',
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 营销的商品范围枚举
|
* 营销的商品范围枚举
|
||||||
*/
|
*/
|
||||||
export const PromotionProductScopeEnum = {
|
export const PromotionProductScopeEnum = {
|
||||||
ALL: {
|
ALL: {
|
||||||
scope: 1,
|
scope: 1,
|
||||||
name: '通用劵'
|
name: '通用劵',
|
||||||
},
|
},
|
||||||
SPU: {
|
SPU: {
|
||||||
scope: 2,
|
scope: 2,
|
||||||
name: '商品劵'
|
name: '商品劵',
|
||||||
},
|
},
|
||||||
CATEGORY: {
|
CATEGORY: {
|
||||||
scope: 3,
|
scope: 3,
|
||||||
name: '品类劵'
|
name: '品类劵',
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// 时间段的状态枚举
|
// 时间段的状态枚举
|
||||||
export const TimeStatusEnum = {
|
export const TimeStatusEnum = {
|
||||||
WAIT_START: '即将开始',
|
WAIT_START: '即将开始',
|
||||||
STARTED: '进行中',
|
STARTED: '进行中',
|
||||||
END: '已结束',
|
END: '已结束',
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信小程序的订阅模版
|
* 微信小程序的订阅模版
|
||||||
*/
|
*/
|
||||||
export const WxaSubscribeTemplate = {
|
export const WxaSubscribeTemplate = {
|
||||||
TRADE_ORDER_DELIVERY: "订单发货通知",
|
TRADE_ORDER_DELIVERY: '订单发货通知',
|
||||||
PROMOTION_COMBINATION_SUCCESS: "拼团结果通知",
|
PROMOTION_COMBINATION_SUCCESS: '拼团结果通知',
|
||||||
PAY_WALLET_RECHARGER_SUCCESS: "充值成功通知",
|
PAY_WALLET_RECHARGER_SUCCESS: '充值成功通知',
|
||||||
}
|
};
|
||||||
|
export const PromotionActivityTypeEnum = {
|
||||||
|
NORMAL: {
|
||||||
|
type: 0,
|
||||||
|
name: '普通',
|
||||||
|
},
|
||||||
|
SECKILL: {
|
||||||
|
type: 1,
|
||||||
|
name: '秒杀',
|
||||||
|
},
|
||||||
|
BARGAIN: {
|
||||||
|
type: 2,
|
||||||
|
name: '砍价',
|
||||||
|
},
|
||||||
|
COMBINATION: {
|
||||||
|
type: 3,
|
||||||
|
name: '拼团',
|
||||||
|
},
|
||||||
|
POINT: {
|
||||||
|
type: 4,
|
||||||
|
name: '积分商城',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const getTimeStatusEnum = (startTime, endTime) => {
|
export const getTimeStatusEnum = (startTime, endTime) => {
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
if (now.isBefore(startTime)) {
|
if (now.isBefore(startTime)) {
|
||||||
return TimeStatusEnum.WAIT_START;
|
return TimeStatusEnum.WAIT_START;
|
||||||
} else if (now.isAfter(endTime)) {
|
} else if (now.isAfter(endTime)) {
|
||||||
return TimeStatusEnum.END;
|
return TimeStatusEnum.END;
|
||||||
} else {
|
} else {
|
||||||
return TimeStatusEnum.STARTED;
|
return TimeStatusEnum.STARTED;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
Loading…
Reference in New Issue