购物车页面是电子商务网站或应用程序中的一个关键功能页面,它允许用户查看、编辑和管理他们选择加入购物车的商品。下面通过低代码可视化实现一个uniapp购物车页面,把购物车整个事件都集成进去。实现完成后可以保存为页面模板。
收货地址选择
如果尚未选择收货地址,用户可以在此选择或输入新收货地址。
API加载收货地址
api加载当前用户所有收货地址,如果没有收货地址,提示先新增地址。
如果有地址,支持显示用户收货地址。
商品列表
循环显示用户购物车的数据。
商品图片:显示商品的缩略图,帮助用户快速识别。
商品名称:商品的完整名称或描述。
价格:每个商品的单价,以及可能的折扣价或原价对比。
数量选择器:允许用户增加或减少所选商品的数量。
总价计算
商品总价:所有商品单价乘以数量的总和。。
订单总价:商品总价加上税运费(如果有)的最终金额。
查看源码
点击查看源码,也可以设计器上显示源码。
保存源码至本地
保存源码至本地,即可看见购物车功能。
<template>
<view class="container container329916">
<view class="flex flex-wrap diygw-col-24 flex-direction-column">
<view v-if="userInfo.token && userInfo.carts && userInfo.carts.length > 0 && globalData.userAddress.id" class="flex flex-wrap diygw-col-24 items-stretch flex7-clz">
<view class="flex flex-wrap diygw-col-0 flex-direction-column justify-between items-center flex6-clz">
<view class="flex flex-wrap diygw-col-24 items-center">
<text class="diygw-col-0 text5-clz"> 收货人:{{ globalData.userAddress.title }} </text>
<text class="diygw-col-0 text6-clz">
{{ globalData.userAddress.phone }}
</text>
</view>
<text class="diygw-text-line1 diygw-col-24 text30-clz"> {{ globalData.userAddress.provinceLabel }}{{ globalData.userAddress.address }} </text>
</view>
<text class="diygw-col-0 text8-clz"> </text>
<view class="flex flex-wrap diygw-col-0 items-center flex11-clz" @tap="navigateTo" data-type="page" data-url="/pages/address">
<text class="diygw-col-0"> 修改默认地址 </text>
<text class="flex icon4 diygw-col-0 diy-icon-right"></text>
</view>
</view>
<view v-if="userInfo.token && userInfo.carts && userInfo.carts.length > 0 && !globalData.userAddress.id" class="flex flex-wrap diygw-col-24 items-stretch flex13-clz" @tap="navigateTo" data-type="page" data-url="/pages/address">
<view class="flex flex-wrap diygw-col-0 flex-direction-column justify-between items-center flex14-clz">
<view class="flex flex-wrap diygw-col-24 items-center">
<text class="diygw-col-0 text12-clz"> 未找到收货地址,请先维维护 </text>
</view>
</view>
<text class="diygw-col-0 text15-clz"> </text>
<view class="flex flex-wrap diygw-col-0 items-center flex16-clz">
<text class="diygw-col-0"> 新增地址 </text>
<text class="flex icon5 diygw-col-0 diy-icon-right"></text>
</view>
</view>
<view v-for="(item, index) in userInfo.carts" :key="index" class="flex flex-wrap diygw-col-24 items-center flex5-clz">
<text v-if="item.selected == 1" @tap="navigateTo" data-type="selectOneFunction" :data-index="index" class="flex icon diygw-col-0 icon-clz diy-icon-roundcheck"></text>
<text v-else @tap="navigateTo" data-type="selectOneFunction" :data-index="index" class="flex icon1 diygw-col-0 icon1-clz diy-icon-round"></text>
<view class="flex flex-wrap diygw-col-0 items-stretch flex4-clz">
<image :src="item.img" class="image-size diygw-image diygw-col-0 image-clz" mode="scaleToFill"></image>
<view class="flex flex-wrap diygw-col-0 flex-direction-column justify-between flex2-clz">
<text class="diygw-text-line2 diygw-col-24 text-clz">
{{ item.title }}
</text>
<view class="flex flex-wrap diygw-col-24 justify-between items-center">
<text class="diygw-text-line2 diygw-col-0 text2-clz"> {{ item.price }}元 </text>
<u-form-item :borderBottom="false" class="diygw-col-0 diygw-form-item-notpadding" labelPosition="top" prop="number">
<view class="flex diygw-col-24">
<u-number-box :inputHeight="48" @change="changeItemNumber($event, index, item)" name="number" v-model="item.number" bgColor="#EBECEE" color="#323233" :min="1" :max="100" :step="1" />
</view>
</u-form-item>
</view>
</view>
</view>
</view>
</view>
<view v-if="!userInfo.carts || (userInfo.carts && userInfo.carts.length == 0)" class="flex flex-wrap diygw-col-24 flex-direction-column items-center flex10-clz">
<image src="/static/zwjl.png" class="image1-size diygw-image diygw-col-0" mode="widthFix"></image>
<text class="diygw-col-0 text7-clz"> 您的购物车是空的,快去逛逛吧 </text>
<text @tap="navigateTo" data-type="page" data-url="/pages/goods" class="diygw-col-0 text13-clz"> 去逛逛 </text>
</view>
<view class="flex flex-wrap diygw-col-24 flex-direction-column items-center diygw-bottom flex28-clz">
<view v-if="userInfo.token && userInfo.carts && userInfo.carts.length > 0" class="flex diygw-col-24 justify-between items-center flex-nowrap flex-clz">
<view class="flex flex-wrap diygw-col-0 items-center" @tap="navigateTo" data-type="selectAllFunction">
<text v-if="globalData.totalSelected == '1'" class="flex icon2 diygw-col-0 icon2-clz diy-icon-roundcheck"></text>
<text v-if="globalData.totalSelected != '1'" class="flex icon3 diygw-col-0 icon3-clz diy-icon-round"></text>
<text class="diygw-col-0"> 合计: </text>
<text class="diygw-col-0 text3-clz"> {{ globalData.totalPrice }}元 </text>
</view>
<text v-if="userInfo.token" @tap="navigateTo" data-type="orderApi" class="diygw-col-0 text4-clz"> 立即购买 </text>
<text v-else @tap="navigateTo" data-type="page" data-url="/pages/login" class="diygw-col-0 text10-clz"> 还未登录,立即登录 </text>
</view>
<view class="flex flex-wrap diygw-col-24 items-end flex17-clz">
<view class="flex flex-wrap diygw-col-6 flex-direction-column items-center flex18-clz" @tap="navigateTo" data-type="page" data-url="/pages/index" data-redirect="1">
<view class="flex flex-wrap diygw-col-0 flex-direction-column items-center">
<image src="/static/sy3.png" class="image2-size diygw-image diygw-col-0" mode="widthFix"></image>
</view>
<text class="diygw-text-line1 diygw-col-0"> 首页 </text>
</view>
<view class="flex flex-wrap diygw-col-6 flex-direction-column items-center flex20-clz" @tap="navigateTo" data-type="page" data-url="/pages/goods" data-redirect="1">
<view class="flex flex-wrap diygw-col-0 flex-direction-column items-center">
<image src="/static/fl.png" class="image8-size diygw-image diygw-col-0" mode="widthFix"></image>
</view>
<text class="diygw-text-line1 diygw-col-0"> 分类 </text>
</view>
<view class="flex flex-wrap diygw-col-6 flex-direction-column items-center flex21-clz" @tap="navigateTo" data-type="page" data-url="/pages/cart" data-redirect="1">
<view class="flex flex-wrap diygw-col-0 flex-direction-column items-center">
<text v-if="userInfo.carts && userInfo.carts.length > 0" class="diygw-text-line1 diygw-col-0 animate__animated animate__heartBeat animate__infinite text19-clz"> </text>
<image src="/static/gwcon.png" class="image5-size diygw-image diygw-col-0" mode="widthFix"></image>
</view>
<text class="diygw-text-line1 diygw-col-0"> 购物车 </text>
</view>
<view class="flex flex-wrap diygw-col-6 flex-direction-column items-center flex23-clz" @tap="navigateTo" data-type="page" data-url="/pages/articles" data-redirect="1">
<view class="flex flex-wrap diygw-col-0 flex-direction-column items-center">
<image src="/static/cp1.png" class="image3-size diygw-image diygw-col-0" mode="widthFix"></image>
</view>
<text class="diygw-text-line1 diygw-col-0"> 文章 </text>
</view>
<view class="flex flex-wrap diygw-col-6 flex-direction-column items-center flex26-clz" @tap="navigateTo" data-type="page" data-url="/pages/user" data-redirect="1">
<view class="flex flex-wrap diygw-col-0 flex-direction-column items-center">
<image src="/static/wd.png" class="image4-size diygw-image diygw-col-0" mode="widthFix"></image>
</view>
<text class="diygw-text-line1 diygw-col-0"> 我的 </text>
</view>
</view>
</view>
<view class="clearfix"></view>
</view>
</template>
<script>
export default {
data() {
return {
//用户全局信息
userInfo: {},
//页面传参
globalOption: {},
//自定义全局变量
globalData: { userAddress: {}, totalPrice: 0, totalSelected: '0' },
addressNum: 1,
address: {
rows: [
{
id: 0,
title: '',
phone: null,
isdefault: null,
remark: null,
sortnum: null,
status: '',
province: null,
address: null,
userId: 0,
createTime: '',
updateTime: '',
deleteTime: null
}
],
total: 0,
code: 0,
msg: ''
},
orderNum: 1,
order: {
code: 0,
msg: ''
},
item: {
number: 1
}
};
},
computed: {},
onShow() {
this.setCurrentPage(this);
this.initShow();
},
onLoad(option) {
this.setCurrentPage(this);
if (option) {
this.setData({
globalOption: this.getOption(option)
});
}
this.init();
},
methods: {
async init() {},
async initShow() {
//强制重新刷新页面
this.addressApi({ refresh: 1 });
await this.initCartFunction();
},
// 用户地址 API请求方法
async addressApi(param) {
let thiz = this;
param = param || {};
//请求地址及请求数据,可以在加载前执行上面增加自己的代码逻辑
let http_url = '/shop/address/list';
let http_data = {
pageNum: this.addressNum,
pageSize: 10,
isself: param.isself || '1'
};
let http_header = {};
//如果用户未登录,不加载用户数据
if (!this.userInfo.token) {
return;
}
let address = await this.$http.post(http_url, http_data, http_header, 'json');
this.address = address;
let find = address.rows.find((item) => {
return item.isdefault == 1;
});
if (find) {
this.globalData.userAddress = find;
} else if (address.rows.length > 0) {
this.globalData.userAddress = address.rows[0];
}
},
// 保存订单 API请求方法
async orderApi(param) {
let thiz = this;
param = param || {};
//如果请求要重置页面,请配置点击附加参数refresh=1 增加判断如输入框回调param不是对象
if (param.refresh || typeof param != 'object') {
this.orderNum = 1;
}
//请求地址及请求数据,可以在加载前执行上面增加自己的代码逻辑
let http_url = '/shop/order/add';
let http_data = {
pageNum: this.orderNum,
pageSize: 10
};
let http_header = {
'Content-Type': 'application/json'
};
let carts = this.userInfo.carts || [];
let body = carts.filter((item) => {
return item.selected == 1;
});
if (!this.globalData.userAddress.id) {
this.showToast('请设置收货地址');
return;
}
if (body.length == 0) {
this.showToast('请选择产品');
return;
}
let title = body
.map((item) => {
return item.title;
})
.join(';');
http_data.title = title;
http_data.total = this.globalData.totalPrice;
http_data.openid = this.userInfo.openid;
http_data.body = { carts: body, address: this.globalData.userAddress };
let order = await this.$http.post(http_url, http_data, http_header, 'json');
if (order.code != 200) {
this.showToast(order.msg);
return;
}
carts = carts.filter((item) => {
return item.selected != 1;
});
this.$session.setUserValue('carts', carts);
//跳转至订单详情页面
this.navigateTo({
type: 'page',
url: 'order/detail',
id: order.data.id
});
let datarows = order.rows;
if (http_data.pageNum == 1) {
this.order = order;
} else if (datarows) {
let rows = this.order.rows.concat(datarows);
order.rows = rows;
this.order = order;
}
if (datarows && datarows.length > 0) {
this.orderNum = this.orderNum + 1;
}
this.globalData.isshow = true;
},
// 计算总价 自定义方法
async totalPriceFunction(param) {
let thiz = this;
let total = 0;
let checked = 1;
let carts = this.userInfo.carts || [];
carts.forEach((item) => {
if (item.selected == 1) {
total = total + item.price * item.number;
} else {
checked = 0;
}
});
this.globalData.totalPrice = Number(total.toFixed(2));
this.globalData.totalSelected = checked;
this.$session.setUserValue('carts', carts);
},
// 选择全部或取消选择 自定义方法
async selectAllFunction(param) {
let thiz = this;
this.globalData.totalSelected = this.globalData.totalSelected == '1' ? '0' : '1';
//设置选中或取消
let carts = this.userInfo.carts || [];
carts.forEach((item) => {
item.selected = this.globalData.totalSelected;
});
//计算总价
this.totalPriceFunction();
},
// 选择或取消选择 自定义方法
async selectOneFunction(param) {
let thiz = this;
let index = param && (param.index || param.index == 0) ? param.index : thiz.index || '';
//选中或者取消
let carts = this.userInfo.carts || [];
carts[param.index].selected = carts[param.index].selected == '1' ? '0' : '1';
//计算总价
this.totalPriceFunction();
},
// 初始计算 自定义方法
async initCartFunction(param) {
let thiz = this;
let carts = this.userInfo.carts || [];
carts.forEach((item) => {
item.selected = 1;
});
this.userInfo.carts = carts;
//计算总价
this.totalPriceFunction();
},
changeItemNumber(evt, index, item) {
this.navigateTo({ foritem: item, forindex: index, type: 'totalPriceFunction' });
}
},
onPullDownRefresh() {
// 保存订单 API请求方法
this.orderNum = 1;
this.orderApi();
uni.stopPullDownRefresh();
},
onReachBottom() {
// 保存订单 API请求方法
this.orderApi();
}
};
</script>
<style lang="scss" scoped>
.flex7-clz {
padding-top: 20rpx;
border-bottom-left-radius: 24rpx;
padding-left: 20rpx;
padding-bottom: 20rpx;
border-top-right-radius: 24rpx;
margin-right: 20rpx;
background-color: #ffffff;
margin-left: 20rpx;
overflow: hidden;
width: calc(100% - 20rpx - 20rpx) !important;
border-top-left-radius: 24rpx;
margin-top: 20rpx;
border-bottom-right-radius: 24rpx;
margin-bottom: 20rpx;
padding-right: 20rpx;
}
.flex6-clz {
flex: 1;
}
.text5-clz {
flex: 1;
font-size: 28rpx !important;
}
.text6-clz {
font-weight: bold;
font-size: 28rpx !important;
}
.text30-clz {
margin-left: 0rpx;
color: #989898;
flex: 1;
width: calc(100% - 0rpx - 0rpx) !important;
margin-top: 10rpx;
margin-bottom: 0rpx;
margin-right: 0rpx;
}
.text8-clz {
margin-left: 16rpx;
flex-shrink: 0;
width: 2rpx !important;
margin-top: 10rpx;
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.2932422969187676) 50%, rgba(255, 255, 255, 0) 100%);
margin-bottom: 10rpx;
margin-right: 16rpx;
}
.flex11-clz {
color: #808080;
}
.icon4 {
font-size: 24rpx;
}
.flex13-clz {
padding-top: 20rpx;
border-bottom-left-radius: 24rpx;
padding-left: 20rpx;
padding-bottom: 20rpx;
border-top-right-radius: 24rpx;
margin-right: 20rpx;
background-color: #ffffff;
margin-left: 20rpx;
overflow: hidden;
width: calc(100% - 20rpx - 20rpx) !important;
border-top-left-radius: 24rpx;
margin-top: 20rpx;
border-bottom-right-radius: 24rpx;
margin-bottom: 20rpx;
padding-right: 20rpx;
}
.flex14-clz {
flex: 1;
}
.text12-clz {
color: #989898;
flex: 1;
}
.text15-clz {
margin-left: 16rpx;
flex-shrink: 0;
width: 2rpx !important;
margin-top: 10rpx;
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(135, 194, 250, 0.7932422969187676) 50%, rgba(255, 255, 255, 0) 100%);
margin-bottom: 10rpx;
margin-right: 16rpx;
}
.flex16-clz {
color: #808080;
}
.icon5 {
font-size: 24rpx;
}
.flex5-clz {
padding-top: 20rpx;
border-bottom-left-radius: 24rpx;
padding-left: 20rpx;
padding-bottom: 20rpx;
border-top-right-radius: 24rpx;
margin-right: 20rpx;
background-color: #ffffff;
margin-left: 20rpx;
overflow: hidden;
width: calc(100% - 20rpx - 20rpx) !important;
border-top-left-radius: 24rpx;
margin-top: 10rpx;
border-bottom-right-radius: 24rpx;
margin-bottom: 10rpx;
padding-right: 20rpx;
}
.icon-clz {
margin-left: 10rpx;
color: #ff2a2a;
margin-top: 0rpx;
margin-bottom: 0rpx;
margin-right: 10rpx;
}
.icon {
font-size: 48rpx;
}
.icon1-clz {
margin-left: 10rpx;
color: #ff2a2a;
margin-top: 0rpx;
margin-bottom: 0rpx;
margin-right: 10rpx;
}
.icon1 {
font-size: 48rpx;
}
.flex4-clz {
flex: 1;
}
.image-clz {
border: 2rpx solid #eee;
border-bottom-left-radius: 12rpx;
text-shadow: 1px 1px 2px #333;
overflow: hidden;
border-top-left-radius: 12rpx;
border-top-right-radius: 12rpx;
border-bottom-right-radius: 12rpx;
}
.image-size {
height: 160rpx !important;
width: 160rpx !important;
}
.flex2-clz {
padding-top: 0rpx;
flex: 1;
padding-left: 10rpx;
padding-bottom: 0rpx;
padding-right: 0rpx;
}
.text-clz {
font-weight: bold;
font-size: 28rpx !important;
}
.text2-clz {
padding-top: 6rpx;
color: #ff2a2a;
font-weight: bold;
padding-left: 0rpx;
font-size: 28rpx !important;
padding-bottom: 6rpx;
padding-right: 0rpx;
}
.flex10-clz {
padding-top: 20rpx;
padding-left: 20rpx;
padding-bottom: 20rpx;
padding-right: 20rpx;
}
.image1-size {
height: 400rpx !important;
width: 400rpx !important;
}
.text7-clz {
margin-left: 10rpx;
color: #969696;
font-size: 28rpx !important;
margin-top: 10rpx;
margin-bottom: 20rpx;
margin-right: 10rpx;
}
.text13-clz {
padding-top: 16rpx;
border-bottom-left-radius: 200rpx;
color: #ffffff;
font-weight: bold;
padding-left: 50rpx;
font-size: 28rpx !important;
padding-bottom: 16rpx;
border-top-right-radius: 200rpx;
margin-right: 10rpx;
margin-left: 10rpx;
overflow: hidden;
border-top-left-radius: 200rpx;
margin-top: 0rpx;
border-bottom-right-radius: 200rpx;
background-image: linear-gradient(to right, #fa2209, #ff6335);
margin-bottom: 0rpx;
padding-right: 50rpx;
}
.flex28-clz {
background-color: #ffffff;
border-top: 2rpx solid #e4e4e4;
border-bottom-left-radius: 0rpx;
box-shadow: 0rpx 4rpx 12rpx rgba(31, 31, 31, 0.16);
overflow: visible;
left: 0rpx;
bottom: 0rpx;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
border-bottom-right-radius: 0rpx;
}
.flex-clz {
padding-top: 16rpx;
padding-left: 16rpx;
padding-bottom: 16rpx;
border-bottom: 2rpx solid rgba(244, 242, 242, 0.66);
padding-right: 16rpx;
}
.icon2-clz {
color: #ff2a2a;
}
.icon2 {
font-size: 48rpx;
}
.icon3-clz {
color: #ff2a2a;
}
.icon3 {
font-size: 48rpx;
}
.text3-clz {
padding-top: 6rpx;
font-weight: bold;
padding-left: 0rpx;
font-size: 28rpx !important;
padding-bottom: 6rpx;
padding-right: 0rpx;
}
.text4-clz {
padding-top: 12rpx;
border-bottom-left-radius: 200rpx;
color: #ffffff;
font-weight: bold;
padding-left: 30rpx;
font-size: 28rpx !important;
padding-bottom: 12rpx;
border-top-right-radius: 200rpx;
margin-right: 10rpx;
margin-left: 10rpx;
overflow: hidden;
border-top-left-radius: 200rpx;
margin-top: 0rpx;
border-bottom-right-radius: 200rpx;
background-image: linear-gradient(to right, #fa2209, #ff6335);
margin-bottom: 0rpx;
padding-right: 30rpx;
}
.text10-clz {
padding-top: 16rpx;
border-bottom-left-radius: 200rpx;
color: #ffffff;
font-weight: bold;
padding-left: 30rpx;
font-size: 28rpx !important;
padding-bottom: 16rpx;
border-top-right-radius: 200rpx;
margin-right: 10rpx;
margin-left: 10rpx;
overflow: hidden;
border-top-left-radius: 200rpx;
margin-top: 0rpx;
border-bottom-right-radius: 200rpx;
background-image: linear-gradient(to right, #fa2209, #ff6335);
margin-bottom: 0rpx;
padding-right: 30rpx;
}
.flex17-clz {
padding-top: 16rpx;
padding-left: 16rpx;
padding-bottom: 16rpx;
padding-right: 16rpx;
}
.flex18-clz {
flex: 1;
}
.image2-size {
height: 48rpx !important;
width: 48rpx !important;
}
.flex20-clz {
flex: 1;
}
.image8-size {
height: 48rpx !important;
width: 48rpx !important;
}
.flex21-clz {
color: #fa240b;
flex: 1;
}
.text19-clz {
border: 2rpx solid #eee;
border-bottom-left-radius: 40rpx;
-webkit-animation-duration: 5000ms;
color: #ffffff;
animation-delay: 1000ms;
-webkit-animation-delay: 1000ms;
border-top-right-radius: 40rpx;
right: -8rpx;
background-color: rgba(255, 17, 17, 0.91);
animation-duration: 5000ms;
flex-shrink: 0;
overflow: hidden;
top: -8rpx;
width: 16rpx !important;
border-top-left-radius: 40rpx;
border-bottom-right-radius: 40rpx;
position: absolute;
height: 16rpx !important;
}
.image5-size {
height: 48rpx !important;
width: 48rpx !important;
}
.flex23-clz {
flex: 1;
}
.image3-size {
height: 48rpx !important;
width: 48rpx !important;
}
.flex26-clz {
flex: 1;
}
.image4-size {
height: 48rpx !important;
width: 48rpx !important;
}
.container329916 {
background-color: #f5f5f5;
}
.container {
padding-bottom: 100px;
}
</style>