You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
12 KiB
393 lines
12 KiB
4 months ago
|
<template>
|
||
|
<view :style="colorStyle">
|
||
|
<view>
|
||
|
<view class="fixed-lt w-full h-212 bg_mask z-900" :style="{ top: statusBarHeight + 'px' }">
|
||
|
<view class="px-20 h-80 flex mt-12">
|
||
|
<view class="w-56 h-56 rd-50-p111- bg-black flex-center fs-42 text--w111-fff" @tap="backPage()">
|
||
|
<text class="iconfont icon-ic_leftarrow"></text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<!-- #ifdef MP || APP-PLUS -->
|
||
|
<view :style="{'height': statusBarHeight + 'px'}"></view>
|
||
|
<view class="px-20 p-b-20">
|
||
|
<!-- #endif -->
|
||
|
<!-- #ifndef MP -->
|
||
|
<view class="p-20">
|
||
|
<!-- #endif -->
|
||
|
<view class="rd-24rpx pro_card" v-for="(item, index) in discountsData" :key="index">
|
||
|
<view class="w-full h-710 rd-t-24rpx relative">
|
||
|
<image class="w-full h-full rd-t-24rpx block" mode="aspectFill" :src="item.image"></image>
|
||
|
<view class="match-box rd-24rpx p-24 text--w111-fff">
|
||
|
<view class="fs-26 lh-36rpx w-full line1">{{item.title}}</view>
|
||
|
<view class="fs-22 lh-30rpx w-full line1 mt-12rpx">{{item.products[0].title}}</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="rd-b-24rpx bg--w111-fff pt-24 pb-32">
|
||
|
<scroll-view scroll-x="true" scroll-with-animation
|
||
|
class="white-nowrap vertical-middle w-686 ml-24" show-scrollbar="false">
|
||
|
<view class="inline-block mr-24" v-for="(pro,indexn) in item.products" :key="indexn">
|
||
|
<view class="w-222 rd-24rpx con-border relative">
|
||
|
<image class="w-full h-222 rd-t-24rpx block" :src="pro.image"
|
||
|
@tap="checkPro(pro, indexn, item)"></image>
|
||
|
<view class="bg--w111-fff py-16 mx-16">
|
||
|
<view class="fs-24 lh-34rpx w-full line1">{{ pro.title }}</view>
|
||
|
<view
|
||
|
class="w-full px-16 mt-8 h-36 bg--w111-f5f5f5 rd-20rpx px-12 flex-between-center fs-20"
|
||
|
@tap="selecAttr(pro,index,indexn)">
|
||
|
<view class="w-138 line1">{{pro.suk}}</view>
|
||
|
<text class="iconfont icon-ic_downarrow text--w111-999 fs-20"></text>
|
||
|
</view>
|
||
|
<view class="mt-16 pl-16 SemiBold font-num fs-30">¥{{pro.price}}</view>
|
||
|
</view>
|
||
|
<text class="iconfont fs-38 set-icon"
|
||
|
:class="pro.select ? 'icon-a-ic_CompleteSelect font-color' : 'icon-ic_unselect text--w111-fff'"
|
||
|
@tap="checkPro(pro, indexn, item)"></text>
|
||
|
<div class="bgIcon" v-if="pro.select"></div>
|
||
|
</view>
|
||
|
</view>
|
||
|
</scroll-view>
|
||
|
<view class="mt-32 flex-between-center px-24">
|
||
|
<view class="flex items-baseline">
|
||
|
<text class="fs-24 lh-34rpx text--w111-999">共2件,合计:</text>
|
||
|
<baseMoney :money="item.totalPrice" symbolSize="28" integerSize="44"
|
||
|
decimalSize="28" weight></baseMoney>
|
||
|
</view>
|
||
|
<view class="w-186 h-64 rd-36rpx flex-center fs-24 text--w111-fff bg-gradient1"
|
||
|
@tap="subData(item)">购买此套餐</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<base-drawer mode="bottom" :visible="visible" background-color="transparent" mask maskClosable
|
||
|
@close="closeDrawer">
|
||
|
<view class="scroll-content bg--w111-fff rd-t-40rpx">
|
||
|
<view class="w-full pt-32">
|
||
|
<view class="px-32 flex">
|
||
|
<image class="w-180 h-180 rd-16rpx" :src="attr.productSelect.image"></image>
|
||
|
<view class="pl-24">
|
||
|
<baseMoney :money="attr.productSelect.price" symbolSize="32" integerSize="48"
|
||
|
decimalSize="32" incolor="var(--primary-theme-con)" weight></baseMoney>
|
||
|
<view class="mt-20 fs-24 text--w111-999">库存:{{ attr.productSelect.stock }}</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="px-32">
|
||
|
<view class="item mt-32" v-for="(item, indexw) in attr.productAttr" :key="indexw">
|
||
|
<view class="fs-28">{{ item.attr_name }}</view>
|
||
|
<view class="flex-y-center flex-wrap">
|
||
|
<view class="sku-item" :class="item.index === itemn.attr ? 'active' : ''"
|
||
|
v-for="(itemn, indexn) in item.attr_value" @click="tapAttr(indexw, indexn)"
|
||
|
:key="indexn">
|
||
|
{{ itemn.attr }}
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="mx-20 pb-box">
|
||
|
<view class="mt-52 h-72 flex-center rd-36px bg-color fs-26 text--w111-fff" @click="closeDrawer">
|
||
|
确定</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</base-drawer>
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
/*
|
||
|
* 这是一段很重要的注释,方便您以后理解
|
||
|
* 以前的逻辑较为混乱,用一个对象去管理多个套餐多个商品的sku属性
|
||
|
* 这次修改以后,没有复用sku弹窗组件,主要是这里的逻辑可以独立处理
|
||
|
* 初始化请求到套餐列表用下标为所有商品赋值sku名称和unique码
|
||
|
* 在点击商品打开sku弹窗以后,拿到选中的sku以后,将suk和unique赋值到商品中,
|
||
|
* 同时更新页面视图,在选择套餐时就省事多了,直接穿参操作即可
|
||
|
* */
|
||
|
let sysHeight = uni.getSystemInfoSync().statusBarHeight;
|
||
|
import {
|
||
|
matchPayListApi,
|
||
|
postCartAdd
|
||
|
} from "@/api/store.js"
|
||
|
import colors from '@/mixins/color.js';
|
||
|
import productWindow from '@/components/productWindow';
|
||
|
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
|
||
|
export default {
|
||
|
data() {
|
||
|
return {
|
||
|
statusBarHeight: sysHeight,
|
||
|
id: 0,
|
||
|
params: {
|
||
|
page: 1,
|
||
|
limit: 10
|
||
|
},
|
||
|
discountsData: [],
|
||
|
selectValue: [],
|
||
|
selectAttr: [],
|
||
|
loading: false,
|
||
|
attr: {
|
||
|
cartAttr: false,
|
||
|
productAttr: [],
|
||
|
productSelect: {},
|
||
|
},
|
||
|
productValue: [], //系统属性
|
||
|
index: 0,
|
||
|
indexn: 0,
|
||
|
visible: false
|
||
|
}
|
||
|
},
|
||
|
components: {
|
||
|
baseDrawer
|
||
|
},
|
||
|
mixins: [colors],
|
||
|
onLoad(e) {
|
||
|
this.id = e.id ? e.id : 0;
|
||
|
this.getList()
|
||
|
},
|
||
|
methods: {
|
||
|
getList() {
|
||
|
if (this.loading) return;
|
||
|
this.loading = true;
|
||
|
matchPayListApi(this.id, this.params).then(res => {
|
||
|
this.loading = false;
|
||
|
if (!res.data.length) {
|
||
|
return uni.navigateBack({
|
||
|
delta: 1
|
||
|
});
|
||
|
}
|
||
|
res.data.map((item, index) => {
|
||
|
this.$set(item, 'totalPrice', 0);
|
||
|
item.products.map((item1, i) => {
|
||
|
let skuData = this.DefaultSelect(item1);
|
||
|
Object.assign(item1, skuData);
|
||
|
if(!item.type){
|
||
|
this.$set(item1, 'select', true);
|
||
|
item.totalPrice += Number(item1.price);
|
||
|
}else{
|
||
|
this.$set(item1, 'select', i == 0 ? true : false);
|
||
|
this.$set(item, 'totalPrice', Number(item.products[0].price));
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
this.discountsData = this.discountsData.concat(res.data);
|
||
|
this.params.page = this.params.page + 1;
|
||
|
}).catch(err => {
|
||
|
this.$util.Tips({
|
||
|
title: err
|
||
|
});
|
||
|
})
|
||
|
},
|
||
|
backPage() {
|
||
|
uni.navigateBack()
|
||
|
},
|
||
|
checkPro(pro, indexn, item) {
|
||
|
if(!item.type) return this.$util.Tips({
|
||
|
title: '本套餐为固定套餐,不可更改'
|
||
|
})
|
||
|
if(item.type && indexn == 0) return this.$util.Tips({
|
||
|
title: '套餐主商品不可取消'
|
||
|
})
|
||
|
pro.select = !pro.select;
|
||
|
if (pro.select) {
|
||
|
item.totalPrice = (Number(item.totalPrice) + Number(pro.price)).toFixed(2);
|
||
|
} else {
|
||
|
item.totalPrice = (Number(item.totalPrice) - Number(pro.price)).toFixed(2);
|
||
|
let i = this.selectValue.findIndex(v => v == pro.id);
|
||
|
}
|
||
|
},
|
||
|
tapAttr: function(indexw, indexn) {
|
||
|
let that = this;
|
||
|
this.$set(this.attr.productAttr[indexw], 'index', this.attr.productAttr[indexw].attr_values[indexn]);
|
||
|
let value = that.getCheckedValue().join(",");
|
||
|
let skuData = {
|
||
|
price: this.productValue[value].price,
|
||
|
unique: this.productValue[value].unique || '',
|
||
|
suk: this.productValue[value].suk || '默认',
|
||
|
};
|
||
|
this.attr.productSelect = {
|
||
|
price: this.productValue[value].price,
|
||
|
image: this.productValue[value].image,
|
||
|
stock: this.productValue[value].stock
|
||
|
};
|
||
|
this.$nextTick(() => {
|
||
|
Object.assign(this.discountsData[this.index].products[this.indexn], skuData);
|
||
|
let priceNum = 0;
|
||
|
for (let i = 0; i < this.discountsData[this.index].products.length; i++) {
|
||
|
priceNum += parseFloat(this.discountsData[this.index].products[i].price)
|
||
|
}
|
||
|
this.$set(this.discountsData[this.index],'totalPrice',priceNum);
|
||
|
|
||
|
})
|
||
|
|
||
|
},
|
||
|
//获取被选中属性;
|
||
|
getCheckedValue: function() {
|
||
|
let productAttr = this.attr.productAttr;
|
||
|
let value = [];
|
||
|
for (let i = 0; i < productAttr.length; i++) {
|
||
|
for (let j = 0; j < productAttr[i].attr_values.length; j++) {
|
||
|
if (productAttr[i].index === productAttr[i].attr_values[j]) {
|
||
|
value.push(productAttr[i].attr_values[j]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return value;
|
||
|
},
|
||
|
DefaultSelect(item) {
|
||
|
let productAttr = item.productAttr;
|
||
|
let value = [];
|
||
|
let arrPrice = []
|
||
|
for (var key in item.productValue) {
|
||
|
arrPrice.push(item.productValue[key].price)
|
||
|
}
|
||
|
let min = Math.min.apply(null, arrPrice);
|
||
|
for (var key in item.productValue) {
|
||
|
if (item.productValue[key].product_stock > 0 && item.productValue[key].price == min) {
|
||
|
value = item.productAttr.length ? key.split(',') : [];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for (let i = 0; i < item.productAttr.length; i++) {
|
||
|
this.$set(item.productAttr[i], 'index', value[i]);
|
||
|
}
|
||
|
let productSelect = item.productValue[value.join(',')];
|
||
|
let skuData = {
|
||
|
price: productSelect.price,
|
||
|
unique: productSelect.unique || '',
|
||
|
suk: productSelect.suk || '默认',
|
||
|
stock: productSelect.stock
|
||
|
};
|
||
|
return skuData
|
||
|
},
|
||
|
closeDrawer() {
|
||
|
this.visible = false;
|
||
|
},
|
||
|
selecAttr(item, index, indexn) {
|
||
|
this.index = index;
|
||
|
this.indexn = indexn;
|
||
|
this.productValue = item.productValue;
|
||
|
this.attr = {
|
||
|
productAttr: item.productAttr,
|
||
|
productSelect: {
|
||
|
price: item.price,
|
||
|
image: item.image,
|
||
|
stock: item.stock
|
||
|
},
|
||
|
};
|
||
|
this.visible = true;
|
||
|
},
|
||
|
subData(item) {
|
||
|
let reqData = {
|
||
|
new: 1,
|
||
|
discountId: item.id,
|
||
|
discountInfos: []
|
||
|
};
|
||
|
let count = 0;
|
||
|
item.products.forEach((pro, i) => {
|
||
|
if (pro.select) {
|
||
|
count++;
|
||
|
reqData.discountInfos.push({
|
||
|
id: pro.id,
|
||
|
unique: pro.unique,
|
||
|
product_id: pro.product_id
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
if (count < 2) {
|
||
|
return this.$util.Tips({
|
||
|
title: '请先选择套餐商品'
|
||
|
});
|
||
|
}
|
||
|
postCartAdd(reqData).then(res => {
|
||
|
uni.navigateTo({
|
||
|
url: '/pages/goods/order_confirm/index?new=1&noCoupon=1&cartId=' + res.data.cartId
|
||
|
.join(',')
|
||
|
});
|
||
|
}).catch(err => {
|
||
|
return this.$util.Tips({
|
||
|
title: err
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
</script>
|
||
|
<style>
|
||
|
page{
|
||
|
background-color: #000;
|
||
|
}
|
||
|
.pro_card~.pro_card {
|
||
|
margin-top: 20rpx;
|
||
|
}
|
||
|
|
||
|
.z-900 {
|
||
|
z-index: 900;
|
||
|
}
|
||
|
|
||
|
.con-border {
|
||
|
border: 1rpx solid var(--view-theme);
|
||
|
}
|
||
|
|
||
|
.SemiBold {
|
||
|
font-family: SemiBold;
|
||
|
}
|
||
|
|
||
|
.jianbian {
|
||
|
background: linear-gradient(90deg, #FF7931 0%, #E93323 100%);
|
||
|
}
|
||
|
|
||
|
.bg-black {
|
||
|
background-color: rgba(0, 0, 0, 0.4);
|
||
|
}
|
||
|
|
||
|
.set-icon {
|
||
|
position: absolute;
|
||
|
right: 8rpx;
|
||
|
top: 8rpx;
|
||
|
z-index: 9;
|
||
|
}
|
||
|
.bgIcon{
|
||
|
position: absolute;
|
||
|
right: 12rpx;
|
||
|
top: 12rpx;
|
||
|
width: 30rpx;
|
||
|
height: 30rpx;
|
||
|
background-color: #fff;
|
||
|
border-radius: 50%;
|
||
|
}
|
||
|
|
||
|
.match-box {
|
||
|
width: 424rpx;
|
||
|
background-color: rgba(0, 0, 0, 0.3);
|
||
|
position: absolute;
|
||
|
left: 24rpx;
|
||
|
bottom: 24rpx;
|
||
|
}
|
||
|
|
||
|
.sku-item {
|
||
|
height: 56rpx;
|
||
|
line-height: 56rpx;
|
||
|
border: 1px solid #F2F2F2;
|
||
|
font-size: 24rpx;
|
||
|
color: #333;
|
||
|
padding: 0 44rpx;
|
||
|
border-radius: 28rpx;
|
||
|
margin: 24rpx 0 0 16rpx;
|
||
|
background-color: #F2F2F2;
|
||
|
word-break: break-all;
|
||
|
}
|
||
|
|
||
|
.active {
|
||
|
color: var(--view-theme);
|
||
|
background: var(--view-minorColorT);
|
||
|
border-color: var(--view-theme);
|
||
|
}
|
||
|
|
||
|
.scroll-content {
|
||
|
max-height: 800rpx;
|
||
|
overflow-y: auto;
|
||
|
}
|
||
|
.pb-box{
|
||
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
|
}
|
||
|
</style>
|