578 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<up-popup :show="sku_two" :round="10" @close="close" :safe-area-inset-bottom="true" mode="bottom" :lock-scroll="true" closeable>
<div class="whole">
<div class="head">
<div class="imgs" @click="preViewImg(initImg || SKU.face_img)">
<image :src="img_200 || SKU.face_img" alt="" :style="{width: imgWidth + 'rpx', height: imgWidth + 'rpx'}" />
</div>
<div class="tites">
<div class="jiage">
<text class="icon"></text>
<text class="number">{{chooseItem.price}}</text>
</div>
<div v-if="imgWidth == 200" class="szie">剩余<text>{{chooseItem.stock <= 0 ? 0 : chooseItem.stock}}</text></div>
<div class="szie">选择<text>{{skuName.join('-')}}</text></div>
</div>
</div>
<!-- 规格选项 -->
<view class="sku-list">
<view class="sku-list-item" v-for="(item, index) in specsList" :key="item.spec_id">
<view class="sku-list-item-name">{{item.spec_name }}</view>
<view class="sku-list-item-tag">
<view class="tag"
v-for="(it, i) in item.value"
:key="it.id"
@click="!it.show ? chooseSku(it, index, i) : ''"
:class="choose['v'+ (index + 1) +'_id'] == it.id ? 'choose' : it.show ? 'box' : '' ">
<image v-if="it.img" :src="it.img"></image>
<text>{{it.value}}</text>
</view>
</view>
</view>
</view>
<div class="stepper">
<div>购买数量</div>
<div>
<up-number-box
v-model="chooseItem.num"
:max="chooseItem.stock <= 0 ? 0 : chooseItem.stock"
disabledInput
bgColor="#f2f3f5"
:longPress="false"
@change="changeNum($event)">
</up-number-box>
</div>
</div>
<template v-if="!isNoStart">
<div v-if="type == 0" class="foot_two">
<div class="car" @click="hanlecart">加入购物车</div>
<div class="group" @click="hanleBuy">立即购买</div>
</div>
<div v-else class="foot_noe">
<div v-if="type == 1" class="group" @click="hanlecart">加入购物车</div>
<div v-else-if="type == 2" class="group" @click="hanleBuy">立即购买</div>
</div>
</template>
<template v-else>
<view class="remind" :class="remind ? 'disabled' : ''" @click="remindUser">
<view v-if="!remind" class="topone">预约提醒</view>
<view v-else class="topone">已提醒</view>
<view class="bot">距开始{{days}}{{hours}}{{mins}}{{seconds}}</view>
</view>
</template>
</div>
</up-popup>
</view>
</template>
<script>
import { ref, reactive, toRefs, watch } from 'vue'
import { get, post } from '@/api/request.js'
import { showToast, getUserPhone } from '@/components/common.js'
import { Style } from '@/utils/list.js'
export default {
props: {
sku_two: {
type: Boolean,
default: false
},
type: {
type: Number,
default: 0
},
source: {
type: String
},
SKU: {
type: Object,
default: () => {}
}
},
setup(props, { emit }) {
const data = reactive({
specsList: [], // 重组规格数组
chooseItem: {
price: 0
}, // 选中规格
choose: {}, // 选中规格值id
chooseIndex: [], // 选中规格值index
skuName: [], // 规格名
sku_skuInfo: '',
initImg: '', // 图片选择显示
img_200: '',
shop_goods_id: 0,
shop_goods_sku_id: 0,
num: 1,
images: [],
imgWidth: 200,
remind: false,
isNoStart: false,
days: '00',
hours: '00',
mins: '00',
seconds: '00',
downFn: null,
is_authorized: uni.getStorageSync('is_authorized'),
Color: uni.getStorageSync('theme_color'),
bgColor: Style[uni.getStorageSync('theme_index') * 1].bgColor,
priceColor: Style[uni.getStorageSync('theme_index') * 1].priceColor
})
// 规格选择-主要逻辑
function chooseSku(curObj, index, i_index) {
if (curObj.img) {
data.initImg = curObj.img_800
data.img_200 = curObj.img
}
data.choose['v' + (index + 1) + '_id'] = data.specsList[index].value[i_index].id
data.skuName[index] = data.specsList[index].value[i_index].value
data.chooseIndex.push(index)
data.chooseIndex = Array.from(new Set(data.chooseIndex))
if(data.specsList.length - Object.keys(data.choose).length == 1) {
// 如果只有一个规格类未选中,则去判断未选中的这一项是否可选
data.specsList.forEach((req, i) => {
if(data.chooseIndex.indexOf(i) == -1) {
req.value.forEach((rej, j) => {
data.sku_skuInfo.forEach((ress, m) => {
let flag = [rej.id === ress['v'+(i+1)+'_id']]
for (const key in data.choose) {
flag.push(ress[key] == data.choose[key])
}
if(flag.findIndex(target=>target===false)==-1){
if(ress.stock <= 0 || ress.price == 0){
rej.show = true
} else {
rej.show = false
}
}
})
})
}
})
} else if(data.specsList.length == Object.keys(data.choose).length) {
for (const key in data.choose) {
let i = parseInt(key.substr(1, 1))
data.specsList[i-1].value.forEach((req) => {
data.sku_skuInfo.forEach((ress, m) => {
let flag = [req.id == ress['v'+i+'_id']]
for (const k in data.choose) {
let j = parseInt(k.substr(1, 1))
if(i != j) {
flag.push(ress[k] == data.choose[k])
}
}
if(flag.findIndex(target=>target===false)==-1){
if((ress.stock <= 0 || ress.price == 0)){
req.show = true
} else {
req.show = false
}
}
})
})
}
}
}
const hanlepanduan = () => {
if (data.chooseIndex.length != data.specsList.length) {
showToast('请选择完整规格')
return false
} else if (!data.chooseItem.num || data.chooseItem.num == 0) {
showToast('请添加商品数量')
return false
}
}
const addToCart = () => {
post('/api/v1/carts', {
shop_goods_id: data.chooseItem.shop_goods_id || data.shop_goods_id,
shop_goods_sku_id: data.chooseItem.id || data.shop_goods_sku_id,
num: data.chooseItem.num || data.num,
shop_group_goods_id: 0
}).then((res) => {
showToast('已加入购物车')
emit('getnum', data.skuName.join('-'), true)
close()
}).catch((error) => {
showToast(error)
})
}
const toPay = () => {
const goods = [
{
shop_goods_id: data.chooseItem.shop_goods_id || data.shop_goods_id,
shop_goods_sku_id: data.chooseItem.id || data.shop_goods_sku_id,
num: data.chooseItem.num || data.num,
shop_group_goods_id: 0
}
]
uni.setStorageSync('shopGoods', goods)
uni.navigateTo({
url: '/pages/pay/index?type=0&source=' + props.source
})
close()
}
const hanlecart = async () => {
if (hanlepanduan() == false) {
return
} else {
addToCart()
}
}
const hanleBuy = async () => {
if (hanlepanduan() == false) {
return
} else {
toPay()
}
}
//清理数据
function close() {
data.chooseItem.num = 1
data.num = 1
emit('close', false)
}
// 预览图片
const preViewImg = (img) => {
let img_preview_suffix = uni.getStorageSync('img_preview_suffix') || ''
let imurl = img + img_preview_suffix
if(img.indexOf('.gif') >= 0 || img.indexOf('.GIF') >= 0) {
imurl = img
}
uni.previewImage({
urls: [imurl],
current: 0
})
}
// 商品数量
function changeNum(e) {
data.chooseItem.num = e.value
data.num = e.value
}
watch(() => props.SKU, (newProps) => {
data.specsList = newProps.specs
data.sku_skuInfo = newProps.skus
data.chooseItem = newProps.skus[0]
// 在仅有一个规格的时候设置状态
if(data.specsList.length == 1) {
data.specsList[0].value.forEach((req, i) => {
data.sku_skuInfo.forEach((res, j) => {
if(req.id == res.v1_id && (res.price == 0 || res.stock <= 0)){
req.show = true
}
})
})
}
data.images = []
data.specsList[0].value.forEach((it) => {
if(it.img_800){
data.images.push(it.img_800)
}
})
})
watch(() => props.sku_two, (sku_two) => {
if(sku_two && props.SKU.isNoStart == 1) {
data.isNoStart = true
data.remind = props.SKU.remind
let st = new Date(props.SKU.sold_start_time.replace(/-/g, '/'))
let nt = new Date().getTime()
if(nt - st < 0) {
setTimeIng(st - nt)
} else {
data.isNoStart = false
}
}
})
function setTimeIng(backTime) {
data.downFn = setInterval(() => {
var d = Math.floor(backTime / (1000 * 60 * 60 * 24)); //计算天数
var h = Math.floor(backTime / (1000 * 60 * 60) % 24); //计算小时数
var m = Math.floor(backTime / (1000 * 60) % 60); //计算分钟数
var s = Math.floor(backTime / 1000 % 60); //计算秒数
if (backTime < 0) {
data.days = '00'
data.hours = '00'
data.mins = '00'
data.seconds = '00'
data.isNoStart = false
clearInterval(data.downFn)
} else {
data.days = d > 9 ? d : '0' + d
data.hours = h > 9 ? h : '0' + h
data.mins = m > 9 ? m : '0' + m
data.seconds = s > 9 ? s : '0' + s
}
backTime -= 1000
}, 1000)
}
function remindUser() {
if(!data.remind){
const tmplId = 'cnyYrkMecdOwoWdXW4DQgVa8xaB9_YdzbLbLeUGWMjE' // 这里填写你的模板ID
uni.requestSubscribeMessage({
tmplIds: [tmplId],
success(res) {
if (res[tmplId] === 'accept') {
post(`/api/v1/goods/remind/${props.SKU.id}`, {type: 0}).then((res) => {
uni.showToast({
title: '开启活动提醒活动前3分钟通知你',
icon: 'none',
})
data.remind = true
emit('remind', true)
})
} else if (res[tmplId] == 'reject') {
//引导用户,手动引导用户点击按钮,去设置页开启,## Modals是自定义组件
uni.showModal({
title: '订阅消息',
content: '您当前拒绝接受消息通知,是否去开启',
confirmText: '开启授权',
confirmColor: '#345391',
cancelText: '仍然拒绝',
cancelColor: '#999999',
success(res) {
if (res.confirm) {
uni.openSetting({
success(res) {
},
fail(err) {
}
})
} else if (res.cancel) {
}
}
})
}
},
fail(res) {
}
})
}
}
// 授权手机号码
async function getPhoneNumber(e) {
if(e.detail.errMsg == 'getPhoneNumber:ok') {
await getUserPhone(e.detail).then((res) => {
data.is_authorized = 1
})
}
}
watch(() => data.choose, (newChoose) => {
// 判断选择规格值是否完整
if (Object.keys(newChoose).length != data.specsList.length) {
return
}
// 对比选中项
props.SKU.skus.forEach((sku, s_index) => {
let num = data.chooseItem.num
if (
sku.v1_id == newChoose.v1_id &&
sku.v2_id == newChoose.v2_id &&
sku.v3_id == newChoose.v3_id &&
sku.v4_id == newChoose.v4_id
) {
data.chooseItem = { ...sku }
if (num > 0) {
data.chooseItem.num = num < sku.stock ? num : sku.stock
} else if (sku.stock > 0) {
data.chooseItem.num = 1
} else {
data.chooseItem.num = 0
}
if (sku.pic_url === 'undefined') {
data.chooseItem.pic_url = props.skuInfo.face_img
}
}
})
},
{ deep: true }
)
return {
...toRefs(data),
changeNum,
close,
preViewImg,
chooseSku,
hanleBuy,
hanlecart,
remindUser,
setTimeIng,
getPhoneNumber
}
}
}
</script>
<style lang="scss" scoped>
.whole{
position: relative;
}
.sku-list-item {
margin-top: 20rpx;
}
.box {
opacity: 0.35;
}
.sku-list {
padding: 0 30rpx 30rpx 30rpx;
border-bottom: 2rpx solid #dbdbdb;
overflow-y: auto;
height: 500rpx;
box-sizing: border-box;
.sku-list-item {
.sku-list-item--name {
font-size: 26rpx;
margin-top: 20rpx;
}
.sku-list-item-tag {
display: flex;
flex-wrap: wrap;
font-size: 24rpx;
}
.tag {
display: flex;
align-items: center;
justify-content: center;
color: #000;
background-color: #f4f4f4;
margin-right: 22rpx;
margin-top: 20rpx;
padding: 10rpx 18rpx;
box-sizing: border-box;
border-radius: 6rpx;
image {
width: 35rpx;
height: 35rpx;
margin-right: 15rpx;
}
}
.choose {
background-color: v-bind('bgColor');
color: v-bind('Color');
border-radius: 8rpx;
}
}
}
.foot_noe {
padding: 20rpx;
.group {
text-align: center;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: v-bind('Color');
color: #fff;
border-radius: 41rpx;
font-size: inherit;
box-sizing: content-box;
}
}
.foot_two {
display: flex;
justify-content: space-around;
align-items: center;
padding: 0rpx 20rpx 20rpx;
box-sizing: border-box;
font-size: 28rpx;
.group {
width: 50%;
height: 70rpx;
background: v-bind('Color');
color: #fff;
border-radius: 0 70rpx 70rpx 0;
display: flex;
align-items: center;
justify-content: center;
}
.car {
width: 50%;
height: 70rpx;
border: 1px solid v-bind('Color');
color: v-bind('Color');
border-radius: 70rpx 0 0 70rpx;
background: #fff;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
}
}
.remind{
display: flex;
flex-direction: column;
height: 80rpx;
border-radius: 80rpx;
background: #32C55A;
color: #fff;
align-items: center;
justify-content: center;
width: 94%;
margin: 0 0 10rpx 3%;
&.disabled{
opacity: 0.7;
}
.topone{
font-size: 28rpx;
}
.bot{
display: flex;
align-items: center;
color: #fff;
font-size: 22rpx;
}
}
.stepper {
display: flex;
justify-content: space-between;
padding: 35rpx 20rpx;
box-sizing: border-box;
}
.head {
display: flex;
align-items: center;
padding: 30rpx;
box-sizing: border-box;
border-bottom: 1px solid #eee;
.imgs {
margin-right: 20rpx;
}
.tites {
.szie {
color: #676767;
font-size: 28rpx;
margin-top: 20rpx;
}
.jiage {
color: v-bind('priceColor');
.icon {
font-size: 28rpx;
margin-right: 10rpx;
}
.number {
font-size: 40rpx;
transition: all 0.3s;
}
}
}
}
</style>