购物车页面是电子商务网站或应用程序中的一个关键功能页面,它允许用户查看、编辑和管理他们选择加入购物车的商品。下面通过低代码可视化实现一个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>