1、效果展示
2、子组件
这里我发现问题,如果不传入商品的 id ,那么我们应该怎样判断我们修改的是那个商品的数量,从而计算总价?
我突然发现向父组件传值有个这个东西
const emit = defineEmits([‘update:num’])
那么 update:num 是什么东西,我是第一次见。
原来这个是实现子父组件双向绑定值,那么我父组件直接利用
v-model:num=‘item.number’
后发现当数量发生变化时,总价也同步计算了。真的神奇。
值得注意:一定要在 num 前加 update 才有效果。
<template>
<view style="width: 240rpx;">
<view class="flex flex-aic xlb_input_number_box b_2 bgc_f8f8f8">
<view class="xlb_input_number_btn tac b_r_2" :class="numberVal <= 1 ? 'color_999' : 'color_333'" @tap="updateNum(true)">-</view>
<input @blur="inputNum" type="number" v-model="numberVal" class="xlb_input_number_ipt flex-fitem tac font_28" />
<view class="xlb_input_number_btn tac b_l_2" :class="numberVal >= max ? 'color_999' : 'color_333'" @tap="updateNum(false)">+</view>
</view>
</view>
</template>
<script setup>
import {
ref,
watchEffect
} from 'vue'
import lyzDebounce from "@/js_sdk/lyz-debounce/lyz-debounce.js"
let numberVal = ref(1)
let props = defineProps({
num: {
type: Number,
default: 1
},
max: {
type: Number,
default: 99999
}
});
watchEffect(() => {
numberVal.value = props.num
})
const inputNum = (e) => {
if(isNaN(e.detail.value)) {
numberVal.value = 1
emitFun(numberVal.value)
}else if (Number(e.detail.value) <= 0 || e.detail.value == 'e') {
numberVal.value = 1
emitFun(numberVal.value)
} else if (Number(e.detail.value) > props.max) {
numberVal.value = props.max
emitFun(numberVal.value)
} else {
emitFun(Number(e.detail.value))
}
}
const emitFun = lyzDebounce.debounce((val) => {
emit('update:num', val)
}, 200, false)
const emit = defineEmits(['update:num'])
const updateNum = (flg) => {
if (flg) {
if (numberVal.value - 1 <= 0) return
emitFun(numberVal.value -= 1)
} else {
if (props.max !== 0 && props.max < numberVal.value + 1) return
emitFun(numberVal.value += 1)
}
}
</script>
<style lang='scss' scoped>
.xlb_input_number_box {
height: 64rpx;
}
.xlb_input_number_ipt {
margin: 0 10rpx;
border: none;
}
.xlb_input_number_btn {
width: 64rpx;
height: 64rpx;
line-height: 64rpx;
}
</style>
3、父页面
<template>
<view class="shop_tool_view">
<view class="shop_tool_box flex-aic flexr-jfe boxs_bb pos_f bgc_fff">
<text>完成</text>
</view>
</view>
<view class="shop_list_view">
<view class="bgc_fff shop_list_box">
<template v-for="(item,index) in shopData" :key="index">
<view class="flex flex-aic p_t_24">
<radio style="transform:scale(0.6)" :checked="item.isChoose"
@tap="item.isChoose=!item.isChoose;choose();" color="#fe5572" />
<image class="shop_item_img m_r_16" :src="item.image" mode="aspectFill"></image>
<view class="flex-fitem">
<view class="color_333 font_26 m_b_8">{{item.title}}</view>
<view class="flex flex-aic m_b_4">
<view class="color_999 font_24 m_r_24">{{item.type}}</view>
<uni-icons type="bottom" size="14" color="#999999"></uni-icons>
</view>
<view class="flex-aic flexr-jsb">
<view class="color_ff0003">
<text class="font_28">¥</text>
<text class="font_40">{{item.price}}</text>
</view>
<lyz-input-num v-model:num='item.number'></lyz-input-num>
</view>
</view>
</view>
</template>
</view>
</view>
<view class="shop_nav_box pos_f bgc_fff flex-aic flexr-jsb boxs_bb">
<label class="flex flex-aic color_999 font_24">
<radio style="transform:scale(0.7)" color="#fe5572" @tap="allChoose" :checked="isAllChoose" />全选
</label>
<view class="flex-fitem flex-aic flexr-jfe m_r_16">
<text class="font_28 color_333">合计:</text>
<view class="color_ff7000">
<text class="font_28">¥</text>
<text class="font_40">{{allPrice}}</text>
</view>
</view>
<lyz-button borderColor="#fe5572" backgroundColor="#fe5572">
<template #title>
<view class="pos_r flex flex-aic flex-fitem" style="height: 100%;">
<text>结算</text>
<text class="pos_a btn_shop_a bgc_fff color_ff7000 font_16">{{ allNumber}}</text>
</view>
</template>
</lyz-button>
</view>
</template>
<script setup>
import {
computed,
reactive,
ref
} from 'vue';
// 全选
const choose = () => {
shopData.forEach(x => {
if (x.isChoose == false) {
isAllChoose.value = false;
}
})
}
const isAllChoose = ref(false);
const allChoose = () => {
if (!isAllChoose.value) {
shopData.forEach(x => x.isChoose = true)
isAllChoose.value = true;
} else {
shopData.forEach(x => x.isChoose = false)
isAllChoose.value = false;
}
}
// 计算价格
const allPrice = computed(() => {
let count = 0;
shopData.filter(x => x.isChoose).forEach(x => {
count += (x.price) * x.number;
})
return count.toFixed(2);
})
// 计算总数
const allNumber = computed(() => {
let count = 0;
shopData.filter(x => x.isChoose).forEach(x => {
count += x.number;
})
return count;
})
const shopData = reactive([{
id: 1,
image: '',
title: '鱼腥草折耳根嫩叶子侧耳根猪鼻孔凉拌菜农家自种新鲜',
price: 9.90,
number: 2,
type: "新鲜折耳根;1kg",
isChoose: false
},
{
id: 2,
image: '',
title: '四川豌豆尖新鲜蔬菜现摘现发豌豆苗碗豆菜火锅菜露天龙须菜5斤',
price: 19.90,
number: 1,
type: "新鲜豌豆尖;1kg",
isChoose: false
},
{
id: 3,
image: '',
title: '遵义会议水杯纪念品礼物红军长征茶杯礼品工艺收藏品摆设摆件杯子',
price: 25.00,
number: 1,
type: "红色水杯;1个",
isChoose: false
},
{
id: 4,
image: '',
title: '复古纯手工编织红军草鞋男女士麻鞋舞台表演道具夏季系带全麻凉鞋',
price: 28.00,
number: 1,
type: "手工凉鞋;1双;42.5码",
isChoose: false
}
])
</script>
<style lang="scss" scoped>
.shop_list_view {
padding: 24rpx 32rpx;
}
.shop_list_box {
padding: 0 20rpx 20rpx 0;
border-radius: 12rpx;
}
.shop_item_img {
width: 152rpx;
height: 152rpx;
border-radius: 6rpx;
}
.shop_tool_view {
width: 100%;
height: 88rpx;
}
.shop_tool_box {
height: 88rpx;
left: 0;
right: 0;
z-index: 1;
padding-right: 32rpx;
}
.shop_nav_box {
width: 100%;
height: 100rpx;
box-shadow: 0rpx -2rpx 6rpx 0rpx rgba(215, 215, 215, 0.5);
bottom: calc(100rpx);
bottom: calc(constant(safe-area-inset-bottom) + 100rpx);
bottom: calc(env(safe-area-inset-bottom) + 100rpx);
left: 0;
right: 0;
z-index: 1;
padding: 0 32rpx 0 16rpx;
}
.btn_shop_a {
padding: 0 8rpx;
height: 24rpx;
top: 10rpx;
right: -16rpx;
line-height: 26rpx;
border-radius: 24rpx;
}
</style>