Fix(购物车):[#1016]商品购买限制数量

This commit is contained in:
lifizer
2024-04-01 21:54:45 +08:00
parent 195035f7ba
commit 624c7f17a2
3 changed files with 208 additions and 118 deletions
+3 -1
View File
@@ -15,8 +15,10 @@ import {
WX_LIVE_APPID WX_LIVE_APPID
} from "@/config"; } from "@/config";
import uView from "uview-ui"; import uView from "uview-ui";
import GooSkeleton from '@/components/GooSkeleton/index.vue'
Vue.use(uView); Vue.use(uView)
Vue.component('GooSkeleton', GooSkeleton)
Vue.mixin(mixin) Vue.mixin(mixin)
// 注册全局组件 // 注册全局组件
+201 -117
View File
@@ -1,95 +1,166 @@
<template> <template>
<view class="pages-cart" v-if="ready"> <view class="pages-cart">
<u-navbar leftIcon="trash" title="购物车" fixed @leftClick="remove"/> <u-navbar
leftIcon="trash"
<view class="tc" style="margin-top: 300rpx" v-if="list.length===0"> title="购物车"
<image style="width: 420rpx;height: 418rpx" :src="$VUE_APP_RESOURCES_URL+'/20240106211138206789.png'" mode="scaleToFill"/> fixed
</view> @leftClick="remove"
/>
<view class="group-item" v-for="(item,index) in list" :key="index"> <view v-if="isLoad">
<view
<view class="flex jc-between"> v-if="list.length === 0"
<view class="flex ai-center"> class="tc"
<CheckboxIcon style="margin-right: 24rpx;" :default-state="item['state']" :mark="[index]" @change="onChange"/> style="margin-top: 300rpx"
<image class="store-cover" :src="item['merAvatar']" >
mode="scaleToFill"/> <image
<view style="color:#333333;font-size: 30rpx">{{ item['merName'] }}</view> :src="$VUE_APP_RESOURCES_URL + '/20240106211138206789.png'"
</view> style="width: 420rpx;height: 418rpx"
<view class="flex ai-center" style="color:#E92727;font-size: 24rpx" @click="navToStore(item['hotelId'])"> mode="scaleToFill"
进店逛逛 />
<image style="width: 24rpx;height: 24rpx" :src="$VUE_APP_RESOURCES_URL+'/20240109234706982937.png'" mode="scaleToFill"/>
</view>
</view> </view>
<view
<view class="goods-box"> v-for="(item, index) in list"
<view class="goods flex ai-center" v-for="(goods,gIndex) in item['carts']" :key="gIndex"> :key="index"
<CheckboxIcon style="margin-right: 24rpx;" :default-state="goods['state']" :mark="[index,gIndex]" class="group-item"
@change="onChange"/> >
<image class="goods-cover flex-0" :src="goods['productInfo']['attrInfo']['image']" mode="scaleToFill" <view class="flex jc-between">
@click="navToGoods(goods['productId'])" <view class="flex ai-center">
v-if="goods['productInfo']['attrInfo']['image']"/> <CheckboxIcon
<image class="goods-cover flex-0" :src="goods['productInfo']['image']" mode="scaleToFill" :default-state="item['state']"
@click="navToGoods(goods['productId'])" v-else/> :mark="[index]"
style="margin-right: 24rpx;"
<view style="width: 100%"> @change="onChange"
<view class="one-t" @click="navToGoods(goods['productId'])">{{ goods['productInfo']['storeName'] }}</view> />
<view class="sku ai-center" @click="navToGoods(goods['productId'])"> <image
规格{{ goods['productInfo']['attrInfo']['sku'] }} :src="item['merAvatar']"
</view> class="store-cover"
<view class="flex jc-between ai-center" style="width: 100%;margin-top: 26rpx;"> mode="scaleToFill"
<view style="color:#E92727;font-size: 32rpx;" @click="navToGoods(goods['productId'])"> />
<text class="bold" style="font-size: 24rpx"></text> <view style="color:#333333;font-size: 30rpx">{{ item['merName'] }}</view>
{{ goods['truePrice'] }} </view>
<view
class="flex ai-center"
style="color:#E92727;font-size: 24rpx"
@click="navToStore(item['hotelId'])"
>
进店逛逛
<image
:src="$VUE_APP_RESOURCES_URL + '/20240109234706982937.png'"
style="width: 24rpx;height: 24rpx"
mode="scaleToFill"
/>
</view>
</view>
<view class="good-box">
<view
v-for="(good, gIndex) in item['carts']"
:key="gIndex"
class="good flex ai-center"
>
<CheckboxIcon
:default-state="good['state']"
:mark="[index, gIndex]"
style="margin-right: 24rpx;"
@change="onChange"
/>
<image
v-if="good['productInfo']['attrInfo']['image']"
:src="good['productInfo']['attrInfo']['image']"
class="good-cover flex-0"
mode="scaleToFill"
@click="navToGood(good['productId'])"
/>
<image
v-else
:src="good['productInfo']['image']"
mode="scaleToFill"
class="good-cover flex-0"
@click="navToGood(good['productId'])"
/>
<view style="width: 100%">
<view
class="one-t"
@click="navToGood(good['productId'])"
>{{ good['productInfo']['storeName'] }}</view>
<view
class="sku ai-center"
@click="navToGood(good['productId'])"
>
规格{{ good['productInfo']['attrInfo']['sku'] }}
</view>
<view
class="flex jc-between ai-center"
style="width: 100%;margin-top: 26rpx;"
>
<view
style="color:#E92727;font-size: 32rpx;"
@click="navToGood(good['productId'])"
>
<text class="bold" style="font-size: 24rpx"></text>
{{ good['truePrice'] }}
</view>
<u-number-box
:value="good.cartNum"
:max="good['productInfo']['attrInfo']['stock']"
integer
disabledInput
iconStyle="color: #fff"
class="num-step"
@change="e => changeNumHandle(index, gIndex, good, e)"
>
<template v-slot:minus>
<view class="num-btn flex jc-center ai-center">
<u-icon name="minus" size="12" color="#FFFFFF"/>
</view>
</template>
<template v-slot:input>
<view class="num-input tc">{{ good['cartNum'] }}</view>
</template>
<template v-slot:plus>
<view class="num-btn flex jc-center ai-center">
<u-icon name="plus" size="12" color="#FFFFFF"/>
</view>
</template>
</u-number-box>
</view> </view>
<u-number-box class="num-step" v-model="goods['cartNum']" :name="goods['id']"
:max="goods['productInfo']['attrInfo']['stock']" integer disabledInput
iconStyle="color: #fff" @change="changeNum">
<template v-slot:minus>
<view class="num-btn flex jc-center ai-center">
<u-icon name="minus" size="12" color="#FFFFFF"/>
</view>
</template>
<template v-slot:input>
<view class="num-input tc">{{ goods['cartNum'] }}</view>
</template>
<template v-slot:plus>
<view class="num-btn flex jc-center ai-center">
<u-icon name="plus" size="12" color="#FFFFFF"/>
</view>
</template>
</u-number-box>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> <view class="footer-fixed flex jc-between ai-center">
<view style="margin-left: 32rpx;">
<CheckboxIcon
<view class="footer-fixed flex jc-between ai-center"> style="margin-right: 24rpx;"
<view style="margin-left: 32rpx;"> :default-state="isAll"
<CheckboxIcon style="margin-right: 24rpx;" :default-state="isAll" :mark="[-1]" @change="onChange"/> :mark="[-1]"
<text style="color:#666666;font-size: 32rpx">全选({{ selectCount }})</text> @change="onChange"
</view> />
<view class="flex ai-center"> <text style="color:#666666;font-size: 32rpx">全选({{ selectCount }})</text>
<view style="margin-right: 20rpx;color:#666666;font-size: 32rpx">合计: </view>
<text class="bold" style="color:#E92727">{{ total }}</text> <view class="flex ai-center">
<view style="margin-right: 20rpx;color:#666666;font-size: 32rpx">合计:
<text class="bold" style="color:#E92727">{{ total }}</text>
</view>
<view class="btn-submit bold flex jc-center ai-center" @click="submit">结算</view>
</view> </view>
<view class="btn-submit bold flex jc-center ai-center" @click="submit">结算</view>
</view> </view>
</view> </view>
<goo-skeleton
v-else
:show-avatar="false"
/>
</view> </view>
</template> </template>
<script> <script>
import {changeCartNum, getCartGroup, postCartDel} from "@/api/store"; import {changeCartNum, getCartGroup, postCartDel} from '@/api/store'
import CheckboxIcon from "@/components/CheckboxIcon.vue"; import CheckboxIcon from '@/components/CheckboxIcon.vue'
import {VUE_APP_RESOURCES_URL} from '../config/index'
export default { export default {
components: {CheckboxIcon}, components: {
CheckboxIcon
},
data() { data() {
return { return {
ready: false, isLoad: false,
webUrl: VUE_APP_RESOURCES_URL,
list: [], list: [],
isAll: false isAll: false
} }
@@ -98,8 +169,8 @@ export default {
total() { total() {
let price = 0 let price = 0
this.list.forEach(item => { this.list.forEach(item => {
item['carts'].forEach(goods => { item['carts'].forEach(good => {
if (goods.state) price += Math.round(goods['cartNum'] * goods['truePrice'] * 100) / 100 if (good.state) price += Math.round(good['cartNum'] * good['truePrice'] * 100) / 100
}) })
}) })
return Math.round(price * 100) / 100 return Math.round(price * 100) / 100
@@ -107,38 +178,42 @@ export default {
selectCount() { selectCount() {
let count = 0 let count = 0
this.list.forEach(item => { this.list.forEach(item => {
item['carts'].forEach(goods => { item['carts'].forEach(good => {
if (goods.state) count++ if (good.state) count++
}) })
}) })
return count return count
} }
}, },
created() {
this.getCart()
},
onShow() { onShow() {
if (!this.isLoad) {
return
}
this.getCart() this.getCart()
this.ready = true
}, },
methods: { methods: {
async getCart() { async getCart() {
const res = await getCartGroup() const res = await getCartGroup()
if (res.success) { if (res.success) {
this.isLoad = true
this.list = res.data['valid'] this.list = res.data['valid']
this.handleAll(false) this.handleAll(false)
} }
}, },
handleAll(state) { handleAll(state) {
this.isAll = state this.isAll = state
this.list = this.list.map(item => { this.list = this.list.map(item => {
item.state = state item.state = state
item.carts = item.carts.map(goods => { item.carts = item.carts.map(good => {
goods.state = state good.state = state
return goods return good
}) })
return item return item
}) })
}, },
navToStore(id) { navToStore(id) {
if (id === 0) { if (id === 0) {
uni.showToast({ uni.showToast({
@@ -148,65 +223,70 @@ export default {
}) })
return return
} }
uni.navigateTo({ uni.navigateTo({
url: '/pagesInn/inn/innHome?id=' + id url: '/pagesInn/inn/innHome?id=' + id
}) })
}, },
onChange(state, mark) { onChange(state, mark) {
if (mark.length === 1) { if (mark.length === 1) {
if (mark[0] === -1) { if (mark[0] === -1) {
this.handleAll(state) this.handleAll(state)
} else { } else {
this.list[mark[0]]['state'] = state this.list[mark[0]]['state'] = state
this.list[mark[0]]['carts'] = this.list[mark[0]]['carts'].map(goods => { this.list[mark[0]]['carts'] = this.list[mark[0]]['carts'].map(good => {
goods.state = state good.state = state
return goods return good
}) })
} }
} }
if (mark.length === 2) { if (mark.length === 2) {
this.list[mark[0]]['carts'][mark[1]]['state'] = state this.list[mark[0]]['carts'][mark[1]]['state'] = state
const length = this.list[mark[0]]['carts'].length const length = this.list[mark[0]]['carts'].length
let count = 0 let count = 0
this.list[mark[0]]['carts'] = this.list[mark[0]]['carts'].map((goods, gIndex) => { this.list[mark[0]]['carts'] = this.list[mark[0]]['carts'].map((good, gIndex) => {
if (gIndex === mark[1]) goods['state'] = state if (gIndex === mark[1]) good['state'] = state
if (goods['state']) count++ if (good['state']) count++
return goods return good
}) })
this.list[mark[0]]['state'] = count === length this.list[mark[0]]['state'] = count === length
} }
}, },
changeNumHandle(index, gIndex, good, e) {
changeNum(event) { const { type } = e
changeCartNum(event.name, event.value) const value = good.cartNum + (type === 'plus' ? 1 : -1)
const id = good.id
uni.showLoading()
changeCartNum(id, value).then(res => {
const { success } = res
if (success) {
this.$set(this.list[index].carts[gIndex], 'cartNum', value)
}
}).catch(() => {
uni.hideLoading()
}).finally(() => {
uni.hideLoading()
})
}, },
handleIds() { handleIds() {
if (this.selectCount === 0) { if (this.selectCount === 0) {
uni.showToast({ uni.showToast({
title: "请选择产品", title: '请选择产品',
icon: "none", icon: 'none',
duration: 2000 duration: 2000
}) })
return [] return []
} }
const ids = []
let ids = []
this.list.forEach(item => { this.list.forEach(item => {
item['carts'].forEach(goods => { item['carts'].forEach(good => {
if (goods['state']) ids.push(goods['id']) if (good['state']) ids.push(good['id'])
}) })
}) })
return ids return ids
}, },
remove() { remove() {
const ids = this.handleIds() const ids = this.handleIds()
if (ids.length === 0) return if (ids.length === 0) return
uni.showModal({ uni.showModal({
title: '确认删除已选商品?', title: '确认删除已选商品?',
content: '', content: '',
@@ -215,20 +295,24 @@ export default {
confirmText: '确认', confirmText: '确认',
success: async (result) => { success: async (result) => {
if (result.confirm) { if (result.confirm) {
const res = await postCartDel(ids) uni.showLoading()
if (res.success) await this.getCart() postCartDel(ids).then(res => {
const { success } = res
if (success) {
this.getCart()
}
}).finally(() => {
uni.hideLoading()
})
} }
} }
}) })
}, },
submit() { submit() {
const ids = this.handleIds() const ids = this.handleIds()
if (ids.length === 0) return if (ids.length === 0) return
const param = ids.join(',')
uni.navigateTo({ uni.navigateTo({
url: '/pages/order/OrderSubmission/index?id=' + param url: '/pages/order/OrderSubmission/index?id=' + ids.join(',')
}) })
} }
} }
@@ -261,11 +345,11 @@ view {
} }
} }
.goods-box { .good-box {
.goods { .good {
margin-top: 24rpx; margin-top: 24rpx;
.goods-cover { .good-cover {
width: 160rpx; width: 160rpx;
height: 160rpx; height: 160rpx;
margin-right: 16rpx; margin-right: 16rpx;
@@ -275,6 +275,9 @@
}, },
// 发出change事件 // 发出change事件
emitChange(value) { emitChange(value) {
const {
type
} = this
// 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更 // 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更
if (!this.asyncChange) { if (!this.asyncChange) {
this.$nextTick(() => { this.$nextTick(() => {
@@ -284,6 +287,7 @@
}) })
} }
this.$emit('change', { this.$emit('change', {
type,
value, value,
name: this.name, name: this.name,
}); });