2025-10-24

购物车
This commit is contained in:
2025-10-23 17:25:39 +08:00
parent 0e2dbdb9db
commit 19e4c8bf08
7 changed files with 302 additions and 51 deletions

View File

@@ -9,11 +9,16 @@ export const mallAPI = {
getCategory: () => http.get(baseUrl + '/api/category'),
getMallDetail: (id) => http.get(baseUrl + '/api/products/' + id),
getRecommended: (id) => http.get(baseUrl + `/api/products/${id}/recommended`), // 推荐商品
addCart: (data) => http.post(baseUrl +"/api/cart/add", data), // 添加购物车
createOrder: (data) => http.post(baseUrl + `/api/orders/create-from-cart`,data), // 创建订单
createOrder: (data) => http.post(baseUrl + `/api/orders/create-from-cart`, data), // 创建订单
getOrder: (orderId) => http.get(baseUrl + `/api/orders/pending-payment/${orderId}`), // 获取订单
payment: (data) => http.post(baseUrl + "/api/orders/confirm-payment", data), // 支付订单
getCouponList: (uid) => http.get(baseUrl + `/api/coupon/user/${uid}`)
getCouponList: (uid) => http.get(baseUrl + `/api/coupon/user/${uid}`), // 获取优惠券列表
// 购物车
getCartList: () => http.get(baseUrl + "/api/cart"), // 获取购物车列表
addCart: (data) => http.post(baseUrl + "/api/cart/add", data), // 添加购物车
editCartQuantity: (item) => http.put(baseUrl + `/api/cart/${item.id}`, {quantity: item.quantity}), // 修改数量
deleteCart: (id) => http.delete(baseUrl + `/api/cart/${id}`), // 删除
}
export default {

View File

@@ -138,41 +138,37 @@
}
},
{
"path" : "pages/home/pay",
"style" :
{
"navigationBarTitleText" : "确认订单",
"path": "pages/home/pay",
"style": {
"navigationBarTitleText": "确认订单",
"backgroundColor": "#E4ECFF",
"enablePullDownRefresh": true
}
},
{
"path" : "pages/my/order",
"style" :
{
"navigationBarTitleText" : "我的订单"
"path": "pages/my/order",
"style": {
"navigationBarTitleText": "我的订单"
}
},
{
"path" : "pages/home/mallSearch",
"style" :
{
"navigationBarTitleText" : "搜索商品",
"path": "pages/home/mallSearch",
"style": {
"navigationBarTitleText": "搜索商品",
"navigationStyle": "custom"
}
},
{
"path" : "pages/my/getCoupons",
"style" :
{
"navigationBarTitleText" : "领券中心"
"path": "pages/my/getCoupons",
"style": {
"navigationBarTitleText": "领券中心"
}
},
{
"path" : "pages/my/car",
"style" :
{
"navigationBarTitleText" : "购物车"
"path": "pages/my/car",
"style": {
"navigationBarTitleText": "购物车",
"navigationStyle": "custom"
}
}
],

View File

@@ -351,7 +351,7 @@
if (res.success) {
msgRef.value.show({
title: '已加入购物车',
type: 'error'
type: 'success'
})
} else {
msgRef.value.show({

View File

@@ -174,20 +174,6 @@
uni.navigateBack();
}
const instance = getCurrentInstance();
const scrollHeight = ref(0)
const loadHeight = () => {
uni.getSystemInfo({
success(res) {
let screenHeight = res.screenHeight
uni.createSelectorQuery().in(instance.proxy).select("#boxBottom").boundingClientRect((
data) => {
scrollHeight.value = screenHeight - data.height
}).exec()
}
})
}
// 收获地址
const showSelectAddress = ref(false)
const addressOptions = ref([])
@@ -487,7 +473,6 @@
})
onMounted(() => {
loadHeight()
userId.value = uni.getStorageSync("user").id
loadData()
loadCoupon()

View File

@@ -1,22 +1,283 @@
<template>
<view>
<view class="cart-container">
<u-navbar id="uNavbarId" back-text="购物车" :back-text-style="navBarTitle"
:background="{background: 'transparent' }" :border-bottom="false" back-icon-color="#000" title-color="#000">
<template v-slot:right>
<view class="right-menu">
<view @click="handleManage">
{{isManage?'退出管理':'管理'}}
</view>
</view>
</template>
</u-navbar>
<scroll-view scroll-y="true" :style="'height:'+scrollHeight+'px'">
<view class="cart-list">
<view class="cart-item" v-for="item in cartList">
<view class="left">
<u-checkbox v-model="item.isCheck" shape="circle" active-color="#305def"></u-checkbox>
<image style="height: 160rpx;width: 160rpx;" :src="getImageUrl(item.product.image_url)" mode="">
</image>
</view>
<u-row class="right">
<u-col :span="10">
<view>{{item.product.name}}</view>
<view>
<image class="icon" src="/static/icon/rongdou.png" mode=""></image>
{{item.product.rongdou_price}}
</view>
<view>
<image class="icon" src="/static/icon/jifen.png" mode=""></image>
{{item.product.points_price}}
</view>
<view>
<u-number-box bg-color="#E3ECFF" v-model="item.quantity"
@change="handleChangeQuantity(item)" :min="1"
:max="item.product.stock"></u-number-box>
</view>
</u-col>
<u-col :span="2" text-align="right">
<template v-if="!isManage">
x{{item.quantity}}
</template>
<template v-else>
<image @click="handleDeleteCart([item])" style="width: 40rpx;height: 40rpx;"
src="/static/icon/delete.png" mode="">
</image>
</template>
</u-col>
</u-row>
</view>
<view class="box-div">
</view>
</view>
</scroll-view>
<view class="box-bottom" id="boxBottom">
<u-checkbox v-model="isAllCheck" shape="circle" @change="handleSelectAll">全选</u-checkbox>
<view class="right">
合计{{rongdou}} {{point}}
<u-button class="u-m-l-10" @click="handleSubmit">{{isManage?'删除':'结算'}}</u-button>
</view>
</view>
<!-- 确认是否删除 -->
<u-modal v-model="showDel" content="是否删除选中的商品" :show-cancel-button="true" @confirm="handleDel"></u-modal>
<!-- 提示 -->
<u-toast ref="msgRef" />
</view>
</template>
<script>
export default {
data() {
return {
<script setup>
import {
computed,
onMounted,
ref,
getCurrentInstance
} from 'vue';
import {
mallAPI
} from '../../api/mall.js';
import {
getImageUrl
} from '../../util/common.js'
const msgRef = ref()
const instance = getCurrentInstance();
const scrollHeight = ref(0)
const loadHeight = () => {
uni.getSystemInfo({
success(res) {
let screenHeight = res.screenHeight
uni.createSelectorQuery().in(instance.proxy).select("#uNavbarId").boundingClientRect((
data) => {
scrollHeight.value = screenHeight - data.height
}).exec()
uni.createSelectorQuery().in(instance.proxy).select("#boxBottom").boundingClientRect((
data) => {
scrollHeight.value = scrollHeight - data.height
}).exec()
}
},
methods: {
})
}
const navBarTitle = {
fontWeight: '510',
fontStyle: 'Medium',
fontSize: '40rpx',
lineHeight: '52px'
}
const isManage = ref(false)
const handleManage = () => {
isManage.value = !isManage.value
}
const isAllCheck = computed(() => {
if (cartList.value.length == 0) return false
let tmpData = cartList.value.filter(item => item.isCheck)
calculate()
if (tmpData.length == cartList.value.length) return true
else return false
})
const handleSelectAll = () => {
if (isAllCheck.value) cartList.value.forEach(item => item.isCheck = false)
else cartList.value.forEach(item => item.isCheck = true)
calculate()
}
const handleChangeQuantity = (item) => {
mallAPI.editCartQuantity(item)
}
const showDel = ref(false)
const delItems = ref([])
const handleDeleteCart = (items) => {
delItems.value = items
showDel.value = true
}
const handleDel = async () => {
for (var i = 0; i < delItems.value.length; i++) {
await mallAPI.deleteCart(delItems.value[i].id)
}
loadData()
msgRef.value.show({
title: '删除成功',
type: 'success'
})
}
const handleSubmit = () => {
if (isManage.value) {
// 删除
delItems.value = cartList.value.filter(item => item.isCheck)
showDel.value = true
} else {
// 支付
const ids = cartList.value.map(item => item.id)
mallAPI.createOrder({
cart_item_ids: ids
}).then(response => {
if (response.success) {
// 进入支付页面
uni.navigateTo({
url: '/pages/home/pay?preOrderId=' + response.data
.preOrderId
})
} else {
msgRef.value.show({
title: '操作失败',
type: 'error'
})
}
})
}
}
const rongdou = ref(0)
const point = ref(0)
const calculate = () => {
rongdou.value = 0
point.value = 0
cartList.value.forEach(item => {
if (item.isCheck) {
rongdou.value += item.quantity * item.product.rongdou_price
point.value += item.quantity * item.product.points_price
}
})
}
const cartList = ref([])
const loadData = () => {
mallAPI.getCartList().then(res => {
cartList.value = res.data.items
cartList.value.forEach(item => item.isCheck = false)
})
}
onMounted(() => {
loadHeight()
loadData()
})
</script>
<style>
<style lang="scss" scoped>
.cart-container {
width: 100%;
height: 100vh;
background: linear-gradient(180deg, #E3E8FF 0%, #FFFFFF 100%);
background-blend-mode: lighten;
</style>
.right-menu {
display: flex;
margin-right: 24rpx;
font-family: Work Sans;
font-weight: 500;
font-style: Medium;
font-size: 32rpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
.del-text {
color: red;
}
.add-text {
color: #305DEF;
}
}
.cart-list {
.cart-item {
display: flex;
background: #E3ECFF;
padding: 20rpx;
margin-bottom: 10rpx;
.left {
display: flex;
justify-content: center;
}
.right {
flex: 1;
margin-left: 20rpx;
.icon {
width: 30rpx;
height: 30rpx;
}
}
}
.box-div {
padding: 60rpx 0;
}
}
.box-bottom {
width: 100%;
padding: 20rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
bottom: 0;
background: #F5F8FF;
.right {
display: flex;
justify-content: flex-end;
align-items: center;
}
}
}
</style>

BIN
static/icon/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -208,6 +208,10 @@ const handleError = (errorInfo) => {
})
// uToast.warning(data.message || '您的账户尚未激活,请完成支付后再使用')
}
} else if (!data.success) {
uni.redirectTo({
url: '/pages/login/login'
})
} else {
// uToast.error(data.message || '权限不足')
uni.showToast({