完成购物车功能

This commit is contained in:
TOP糯米 2023-03-10 00:44:07 +08:00
parent 2ae6828745
commit e1b25a3d2a
12 changed files with 314 additions and 97 deletions

View File

@ -28,7 +28,13 @@
:style="{ height: index == data.length - 1 ? wrapHeight + 'px' : 'auto' }" :style="{ height: index == data.length - 1 ? wrapHeight + 'px' : 'auto' }"
> >
<cate-tmpl v-if="cateType === 'cate'" :parentId="item.id" :data="item" @clickItem="clickItem"></cate-tmpl> <cate-tmpl v-if="cateType === 'cate'" :parentId="item.id" :data="item" @clickItem="clickItem"></cate-tmpl>
<list-tmpl v-if="cateType === 'list'" :parentId="item.id" :data="item" @clickItem="clickItem"></list-tmpl> <list-tmpl
v-if="cateType === 'list'"
:parentId="item.id"
:data="item"
@clickItem="clickItem"
@changeNumber="changeNumber"
></list-tmpl>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@ -199,6 +205,12 @@ export default {
clickItem(parentId, id) { clickItem(parentId, id) {
this.$emit("clickItem", parentId, id); this.$emit("clickItem", parentId, id);
}, },
/**
* 改变数量
*/
changeNumber(e, id) {
this.$emit("changeNumber", e, id);
},
}, },
}; };
</script> </script>

View File

@ -6,7 +6,11 @@
<view class="item-container"> <view class="item-container">
<view class="thumb-box" v-for="(item, index) in data.child" :key="index"> <view class="thumb-box" v-for="(item, index) in data.child" :key="index">
<service-preview-item :data="item" @clickItem="clickItem" /> <service-preview-item :data="item" @clickItem="clickItem" />
<widget-count-modify class="component-add" /> <widget-count-modify
:init="briefCart.list[item.id] || 0"
class="component-add"
@change="changeCount($event, item.id)"
/>
</view> </view>
</view> </view>
</view> </view>
@ -15,6 +19,7 @@
<script> <script>
import WidgetCountModify from "@/components/widgets/count-modify"; import WidgetCountModify from "@/components/widgets/count-modify";
import ServicePreviewItem from "@/components/service/preview-item"; import ServicePreviewItem from "@/components/service/preview-item";
import { mapState } from "vuex";
export default { export default {
name: "component-cate-template-list", name: "component-cate-template-list",
data() { data() {
@ -30,6 +35,11 @@ export default {
default: [], default: [],
}, },
}, },
computed: {
...mapState({
briefCart: (state) => state.cart.briefCart,
}),
},
components: { components: {
WidgetCountModify, WidgetCountModify,
ServicePreviewItem, ServicePreviewItem,
@ -41,6 +51,9 @@ export default {
clickItem(id) { clickItem(id) {
this.$emit("clickItem", this.parentId, id); this.$emit("clickItem", this.parentId, id);
}, },
changeCount(e, id) {
this.$emit("changeNumber", e, id);
}
}, },
}; };
</script> </script>

View File

@ -38,6 +38,11 @@ export default {
}, },
mounted() {}, mounted() {},
destroyed() {}, destroyed() {},
watch: {
init(value) {
this.number = value;
}
},
methods: { methods: {
sub() { sub() {
if (this.number > this.min) { if (this.number > this.min) {

View File

@ -98,12 +98,25 @@ export default {
}, },
order: { order: {
cart: { cart: {
add: { count: {
url: "/order/myshoppingcarcount",
},
toCart: {
url: "/order/addshoppingcar", url: "/order/addshoppingcar",
showLoading: true, showLoading: true,
}, },
add: {
url: "/order/shoppingcarpush",
},
sub: {
url: "/order/shoppingcarsend",
},
delete: {
url: "/order/deleteshoppingcar",
},
list: { list: {
url: "/order/myshoppingcar" url: "/order/myshoppingcar",
showLoading: true,
}, },
}, },
pay: { pay: {

View File

@ -3,33 +3,81 @@ let prototype = Vue.prototype;
export default { export default {
/** /**
* 添加到购物车 * 获取总数
*/ */
add(id) { count(id, type) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
prototype.$request({ prototype.$request({
api: "order.cart.add", api: "order.cart.count",
}).then((response) => {
return resolve(response.data.count);
}).catch(e => { });
});
},
/**
* 添加到购物车
*/
toCart(id) {
return new Promise((resolve, reject) => {
prototype.$request({
api: "order.cart.toCart",
data: { data: {
id: id, id: id,
} }
}).then((response) => { }).then((response) => {
prototype.$utils.toast(response.msg); prototype.$utils.toast(response.msg);
if (response.code == 1) { if (response.code == 1) {
return resolve(response.msg); return resolve();
} }
return reject(response.msg); return reject(response.msg);
}).catch(e => { }); }).catch(e => { });
}); });
}, },
/**
* 增加/减少数量
*/
change(id, type) {
return new Promise((resolve, reject) => {
prototype.$request({
api: "order.cart." + (type == 'sub' ? 'sub' : 'add'),
data: {
id: id,
}
}).then(() => {
return resolve();
}).catch(e => { });
});
},
/**
* 删除
*/
delete(id) {
return new Promise((resolve, reject) => {
prototype.$request({
api: "order.cart.delete",
data: {
id: id,
}
}).then(() => {
return resolve();
}).catch(e => { });
});
},
/** /**
* 列表 * 列表
*/ */
list() { list(refresh) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let cacheList = uni.getStorageSync('USER_CART');
if ((typeof refresh === "undefined" || false === refresh) && cacheList) {
return resolve(cacheList);
}
prototype.$request({ prototype.$request({
api: "order.cart.list", api: "order.cart.list",
}).then(response => { }).then(response => {
if (response.code == 1) { if (response.code == 1) {
uni.setStorageSync('USER_CART', response.data);
return resolve(response.data); return resolve(response.data);
} }
return reject(response.msg); return reject(response.msg);

View File

@ -128,6 +128,9 @@ export default {
bindDateChange(e) { bindDateChange(e) {
this.datetime = e.detail.value; this.datetime = e.detail.value;
}, },
/**
* 选择地址
*/
selectAddress() { selectAddress() {
const that = this; const that = this;
this.$utils.toPage("/pages/address/address?openType=choose&id=" + that.addressId, { this.$utils.toPage("/pages/address/address?openType=choose&id=" + that.addressId, {
@ -139,6 +142,9 @@ export default {
}, },
}); });
}, },
/**
* 保险开关
*/
changeInsuranceState(state) { changeInsuranceState(state) {
if (state) { if (state) {
this.total += this.insurancePrice; this.total += this.insurancePrice;
@ -147,6 +153,9 @@ export default {
} }
this.insurance = state; this.insurance = state;
}, },
/**
* 下单支付
*/
pay() { pay() {
if (!this.isAgree) { if (!this.isAgree) {
uni.showToast({ uni.showToast({
@ -164,6 +173,7 @@ export default {
desc: this.content, desc: this.content,
}) })
.then((id) => { .then((id) => {
this.$store.dispatch("cart/updateAll");
this.$models.order.payOrder(id); this.$models.order.payOrder(id);
}) })
.catch((e) => { .catch((e) => {

View File

@ -5,7 +5,7 @@
</view> </view>
<view class="cart-container"> <view class="cart-container">
<view class="cart-group" v-for="(item, index) in list" :key="index"> <view class="cart-group" v-for="(item, index) in list" :key="index">
<view class="group-title limit-line clamp-1" @click="clickGroup(index)"> <view class="group-title limit-line clamp-1" @click="checkGroup(index)">
<view class="group-checkbox"> <view class="group-checkbox">
<widget-check-box :size="50" :checked="item.checked" /> <widget-check-box :size="50" :checked="item.checked" />
</view> </view>
@ -14,16 +14,17 @@
</view> </view>
</view> </view>
<view class="group-items"> <view class="group-items">
<view class="cart-item" v-for="(v, i) in item.list" :key="i"> <view class="cart-item" v-for="(v, i) in item.list" :key="i" @longpress="deleteCart(index, i)">
<view class="item-checkbox" @click="clickItem(index, i)"> <view class="item-checkbox" @click="checkItem(index, i)">
<widget-check-box :size="50" :checked="v.checked" /> <widget-check-box :size="50" :checked="v.checked" />
</view> </view>
<view class="item-box"> <view class="item-box">
<service-preview-item :data="v" /> <service-preview-item :data="v" />
<widget-count-modify <widget-count-modify
class="component-add" class="component-add"
:initNumber="v.number" min="1"
@change="changeNumber($event, { parentIndex: index, index: i })" :init="v.number"
@change="changeNumber($event, { parentIndex: index, itemIndex: i })"
/> />
</view> </view>
</view> </view>
@ -31,17 +32,14 @@
<view class="bottom-btn-box"> <view class="bottom-btn-box">
<view class="total-price"> <view class="total-price">
<text class="text">合计</text> <text class="text">合计</text>
<text class="price">¥ {{ item.total }}</text> <text class="price">¥ {{ utils.formatNumber(item.total, 2) }}</text>
</view> </view>
<view class="button"> <view class="button" @click="createOrder(index)">
<text>去下单</text> <text>去下单</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view class="more">
<widget-load-more :hasMore="false" />
</view>
</app-layout> </app-layout>
</template> </template>
@ -50,12 +48,12 @@ import AppLayout from "@/components/layout/layout";
import WidgetTips from "@/components/widgets/tips"; import WidgetTips from "@/components/widgets/tips";
import WidgetCheckBox from "@/components/widgets/checkbox"; import WidgetCheckBox from "@/components/widgets/checkbox";
import WidgetCountModify from "@/components/widgets/count-modify"; import WidgetCountModify from "@/components/widgets/count-modify";
import WidgetLoadMore from "@/components/widgets/loadmore";
import ServicePreviewItem from "@/components/service/preview-item"; import ServicePreviewItem from "@/components/service/preview-item";
export default { export default {
name: "order-cart", name: "order-cart",
data() { data() {
return { return {
utils: this.$utils,
list: [], list: [],
}; };
}, },
@ -64,11 +62,9 @@ export default {
WidgetTips, WidgetTips,
WidgetCheckBox, WidgetCheckBox,
WidgetCountModify, WidgetCountModify,
WidgetLoadMore,
ServicePreviewItem, ServicePreviewItem,
}, },
onLoad() { onLoad() {
let cartList = [];
this.$models.cart.list().then((list) => { this.$models.cart.list().then((list) => {
list.forEach((item) => { list.forEach((item) => {
let goods = []; let goods = [];
@ -76,92 +72,125 @@ export default {
goods.push({ goods.push({
id: v.gid, id: v.gid,
name: v.title, name: v.title,
cover: require("@/static/temp/cate/1.png"), times: v.post_hits,
cover: v.thumbnail,
price: v.money,
number: v.number, number: v.number,
checked: false,
}); });
}); });
cartList.push({ this.list.push({
name: item.cate, name: item.cate,
list: [], total: 0,
list: goods,
checked: false,
}); });
}); });
}); });
this.list = this.parseList([
{
id: 1,
name: "空调安装",
list: [
{
id: 1,
name: "格力空调安装",
cover: require("@/static/temp/cate/1.png"),
number: 1,
},
{
id: 2,
name: "美的空调安装",
cover: require("@/static/temp/cate/1.png"),
number: 1,
},
],
},
{
id: 2,
name: "管道疏通",
list: [
{
id: 3,
name: "厨房下水道",
cover: require("@/static/temp/cate/1.png"),
number: 10,
},
],
},
]);
}, },
onShow() {}, onShow() {},
onReady() {}, onReady() {},
onReachBottom() {}, onReachBottom() {},
onPullDownRefresh() {}, onPullDownRefresh() {},
methods: { methods: {
parseList(list) { checkGroup(parentIndex) {
let newList = []; let currentItem = this.list[parentIndex];
list.forEach((item, index) => { let currentState = !currentItem.checked;
list[index].checked = false; currentItem.list.forEach((item, itemIndex) => {
list[index].total = 0; this.checkItem(parentIndex, itemIndex, currentState);
list[index].list.forEach((v, i) => {
list[index].list[i].checked = false;
});
newList.push(list[index]);
}); });
return newList;
},
clickGroup(parentIndex) {
let state = !this.list[parentIndex].checked;
this.list[parentIndex].list.forEach((item, itemIndex) => {
this.checkedItem(parentIndex, itemIndex, state);
});
},
clickItem(parentIndex, itemIndex) {
this.checkedItem(parentIndex, itemIndex);
}, },
// //
checkedItem(parentIndex, itemIndex, state) { checkItem(parentIndex, itemIndex, state) {
this.list[parentIndex].list[itemIndex].checked = let currentItem = this.list[parentIndex].list[itemIndex];
typeof state === "undefined" ? !this.list[parentIndex].list[itemIndex].checked : state; let currentState = typeof state === "undefined" ? !currentItem.checked : state;
// if (currentItem.checked != currentState) {
let allChecked = true; currentItem.checked = currentState;
this.list[parentIndex].list.forEach((item, index) => {
if (!item.checked) { //
allChecked = false; let allChecked = true;
this.list[parentIndex].list.forEach((item, index) => {
if (!item.checked) {
allChecked = false;
}
});
this.list[parentIndex].checked = allChecked;
this.updateTotal(parentIndex);
}
},
/**
* 更新总价
*/
updateTotal(parentIndex) {
let currentItem = this.list[parentIndex],
total = 0;
currentItem.list.forEach((item) => {
if (item.checked) {
total += item.price * item.number;
} }
}); });
this.list[parentIndex].checked = allChecked; currentItem.total = total;
// this.$forceUpdate();
}, },
changeNumber(e, d) { /**
console.log(e); * 改变数量
console.log(d); */
changeNumber(e, idx) {
let currentItem = this.list[idx.parentIndex].list[idx.itemIndex];
if (currentItem.number != e.value) {
this.$models.cart.change(currentItem.id, e.type).then(() => {
this.$store.dispatch("cart/updateAll");
currentItem.number = e.value;
this.updateTotal(idx.parentIndex);
});
} else {
this.$utils.toast("亲,不能再" + (e.type == "sub" ? "减少" : "增加") + "了~");
}
},
/**
* 删除购物车
*/
deleteCart(parentIndex, itemIndex) {
const that = this;
uni.showModal({
title: "确定删除该商品?",
content: "数据删除后不可恢复,请谨慎操作!",
complete(res) {
if (res.confirm) {
let parentItem = that.list[parentIndex];
let currentItem = parentItem.list[itemIndex];
that.$models.cart.delete(currentItem.id).then(() => {
that.$store.dispatch("cart/updateAll");
parentItem.list.splice(itemIndex, 1);
if (parentItem.list.length > 0) {
that.updateTotal(parentIndex);
} else {
that.list.splice(parentIndex, 1);
}
});
}
},
});
},
/**
* 购物车下单
*/
createOrder(parentIndex) {
let orderdata = [];
let currentItem = this.list[parentIndex];
currentItem.list.forEach((item) => {
if (item.checked) {
orderdata.push({
id: item.id,
count: item.number,
});
}
});
if (orderdata.length > 0) {
this.$utils.toPage("/pages/order/create?orderData=" + encodeURIComponent(JSON.stringify(orderdata)));
} else {
this.$utils.toast("未选择任何商品");
}
}, },
}, },
}; };

View File

@ -25,7 +25,7 @@
<text class="iconfont icon-gouwuche"></text> <text class="iconfont icon-gouwuche"></text>
</view> </view>
<view class="cart-number"> <view class="cart-number">
<text class="text">99</text> <text class="text">{{ cartCount }}</text>
</view> </view>
</movable-view> </movable-view>
</movable-area> </movable-area>
@ -56,10 +56,12 @@ export default {
computed: { computed: {
...mapState({ ...mapState({
indexCateId: (state) => state.system.indexCateId, indexCateId: (state) => state.system.indexCateId,
cartCount: (state) => state.cart.count,
}), }),
}, },
async onLoad() { async onLoad() {
let pageConfig = getApp().globalData.pageConfig; let pageConfig = getApp().globalData.pageConfig;
this.$store.dispatch("cart/updateCount");
let x = pageConfig.windowWidth - this.$utils.rpx2px(94); let x = pageConfig.windowWidth - this.$utils.rpx2px(94);
let y = pageConfig.windowHeight - this.$utils.rpx2px(94); let y = pageConfig.windowHeight - this.$utils.rpx2px(94);

View File

@ -144,7 +144,9 @@ export default {
* 加入购物车 * 加入购物车
*/ */
addToCart() { addToCart() {
this.$models.cart.add(this.id); this.$models.cart.toCart(this.id).then(() => {
this.$store.dispatch("cart/updateAll");
});
}, },
/** /**
* 创建订单 * 创建订单

View File

@ -1,7 +1,14 @@
<template> <template>
<app-layout minHeight="unset" btnType="back" title="服务列表" headerBackgroundColor="#FFFFFF" backgroundColor="#FFFFFF"> <app-layout minHeight="unset" btnType="back" title="服务列表" headerBackgroundColor="#FFFFFF" backgroundColor="#FFFFFF">
<view class="cate"> <view class="cate">
<app-cate :offsetHeight="0" :data="data" cateType="list" @clickItem="clickItem" :activeId="currentId" /> <app-cate
:offsetHeight="0"
:data="data"
cateType="list"
@clickItem="clickItem"
@changeNumber="changeNumber"
:activeId="currentId"
/>
</view> </view>
<view class="common-bottom-components" :style="{ bottom: pageConfig.safeAreaInsets.bottom + 'px' }"> <view class="common-bottom-components" :style="{ bottom: pageConfig.safeAreaInsets.bottom + 'px' }">
<view class="cart" @click="utils.toPage('/pages/service/cart')"> <view class="cart" @click="utils.toPage('/pages/service/cart')">
@ -9,10 +16,10 @@
<text class="iconfont icon-gouwuche"></text> <text class="iconfont icon-gouwuche"></text>
</view> </view>
<view class="cart-number"> <view class="cart-number">
<text class="text">99</text> <text class="text">{{ cartCount }}</text>
</view> </view>
</view> </view>
<view class="order-btn" @click="createOrder"> <view class="order-btn" @click="utils.toPage('/pages/service/cart')">
<text class="text">去下单</text> <text class="text">去下单</text>
</view> </view>
</view> </view>
@ -22,6 +29,7 @@
<script> <script>
import AppLayout from "@/components/layout/layout"; import AppLayout from "@/components/layout/layout";
import AppCate from "@/components/cate/cate"; import AppCate from "@/components/cate/cate";
import { mapState } from "vuex";
export default { export default {
name: "service-list", name: "service-list",
data() { data() {
@ -37,8 +45,15 @@ export default {
AppLayout, AppLayout,
AppCate, AppCate,
}, },
computed: {
...mapState({
cartCount: (state) => state.cart.count,
}),
},
onLoad(e) { onLoad(e) {
this.pageConfig = getApp().globalData.pageConfig; this.pageConfig = getApp().globalData.pageConfig;
this.$store.dispatch("cart/updateCount");
this.$store.dispatch("cart/briefCart");
if (!e.id) { if (!e.id) {
this.$utils.toast("参数错误"); this.$utils.toast("参数错误");
@ -82,12 +97,32 @@ export default {
}) })
.catch((e) => {}); .catch((e) => {});
}, },
createOrder() { /**
this.$utils.toPage("/pages/order/create"); * 详情
}, */
clickItem(parentId, id) { clickItem(parentId, id) {
this.$utils.toPage("/pages/service/detail?id=" + id); this.$utils.toPage("/pages/service/detail?id=" + id);
}, },
/**
* 数量改变
*/
changeNumber(e, id) {
if (e.value > 0) {
if (e.type == "add" && e.value == 1) {
this.$models.cart.toCart(id).then(() => {
this.$store.dispatch("cart/updateAll");
});
} else {
this.$models.cart.change(id, e.type).then(() => {
this.$store.dispatch("cart/updateAll");
});
}
} else {
this.$models.cart.delete(id).then(() => {
this.$store.dispatch("cart/updateAll");
});
}
},
}, },
}; };
</script> </script>

View File

@ -4,6 +4,7 @@ import Vuex from 'vuex'
import system from "@/store/modules/system" import system from "@/store/modules/system"
import user from "@/store/modules/user" import user from "@/store/modules/user"
import service from "@/store/modules/service" import service from "@/store/modules/service"
import cart from "@/store/modules/cart"
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
@ -11,5 +12,6 @@ export default new Vuex.Store({
system, system,
user, user,
service, service,
cart,
} }
}) })

46
src/store/modules/cart.js Normal file
View File

@ -0,0 +1,46 @@
import cart from '@/core/models/cart';
export default {
namespaced: true,
state: {
count: 0,
briefCart: {},
},
getters: {},
mutations: {
count(state, data) {
state.count = data;
},
briefCart(state, data) {
state.briefCart = data;
}
},
actions: {
updateCount(context) {
cart.count().then(count => {
context.commit('count', count);
});
},
briefCart(context) {
cart.list().then((list) => {
let data = {
ids: [],
list: {},
};
list.forEach((item) => {
item.good.forEach((k) => {
data.list[k.gid] = k.number;
});
});
data.ids = Object.keys(data.list);
context.commit('briefCart', data);
});
},
updateAll(context) {
cart.list(true).then(() => {
context.dispatch('briefCart');
context.dispatch('updateCount');
});
}
}
}