更新记录
1.0.0(2026-06-05)
- ✅ 商品列表展示(图片、名称、规格、单价、数量)
- ✅ 数量增减控制(支持按钮点击和输入框输入)
- ✅ 库存限制(数量不会超过库存上限)
- ✅ 单选/全选功能(支持单个商品选中和全选/反选)
- ✅ 实时价格计算(根据选中商品实时计算总价)
- ✅ 空状态提示(购物车为空时显示引导图和按钮)
- ✅ 结算跳转(点击结算将选中商品传递给订单确认页)
- ✅ 单商品模式(商品平铺展示,适用于单一商家场景)
- ✅ 多商家模式(商品按商家分组展示,支持商家级别的全选)
- ✅ 自定义样式配置(所有样式均可通过配置参数自定义)
- ✅ 底部安全区域适配(支持 tabbar 页面和非 tabbar 页面)
平台兼容性
uni-app(3.7.8)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
√ |
√ |
√ |
√ |
- |
√ |
√ |
√ |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
√ |
√ |
√ |
√ |
- |
- |
- |
- |
- |
- |
- |
其他
Shop-Cart 购物车组件
基于 uni-app 框架开发的购物车组件,支持单商品模式和多商家模式两种展示方式,提供完整的购物车功能和丰富的自定义配置选项。
特性
| 功能 |
说明 |
状态 |
| 商品列表展示 |
支持商品图片、名称、规格、单价、数量的完整展示 |
✅ |
| 数量增减控制 |
支持点击按钮增减和输入框直接输入两种方式 |
✅ |
| 库存限制 |
可设置库存上限,数量不会超过库存 |
✅ |
| 单选/全选 |
支持单个商品选中切换和全选/反选功能 |
✅ |
| 实时价格计算 |
根据选中商品实时计算总价和选中数量 |
✅ |
| 空状态提示 |
购物车为空时显示引导图和"去逛逛"按钮 |
✅ |
| 结算跳转 |
点击结算将选中商品传递给订单确认页 |
✅ |
| 商品点击 |
支持点击商品跳转到详情页 |
✅ |
| 商家点击 |
支持点击商家跳转到店铺页(仅多商家模式) |
✅ |
| 单商品模式 |
商品平铺展示,适用于单一商家场景 |
✅ |
| 多商家模式 |
商品按商家分组展示,支持商家级别的全选 |
✅ |
| 自定义样式 |
所有样式均可通过配置参数自定义 |
✅ |
| 安全区域适配 |
支持底部安全区域自动适配 |
✅ |
组件结构
shop-cart/
├── package.json # 组件元信息
├── README.md # 组件文档
├── shop-cart.vue # 主组件,控制整体逻辑和布局
├── shop-cart-item.vue # 商品项组件,展示单个商品
├── shop-cart-group.vue # 商家分组组件,多商家模式下使用
├── shop-cart-footer.vue # 底部结算栏,包含全选和结算按钮
├── shop-cart-empty.vue # 空状态组件,购物车为空时显示
└── quantity-control.vue # 数量控制器组件,独立封装复用
安装与使用
环境要求
- uni-app 框架
- Vue 2.x 或 Vue 3.x
- SCSS 支持
安装方式
-
复制组件:将 shop-cart 文件夹复制到项目的 components 目录下
-
easycom 配置:确保 pages.json 中配置了 easycom 自动引入:
{
"easycom": {
"autoscan": true,
"custom": {
"^shop-cart(.*)": "@/components/shop-cart/shop-cart$1.vue"
}
}
}
- 静态资源:将
static 目录下的图标文件复制到项目的 static 目录
使用示例
基础用法 - 单商品模式
<template>
<view class="page-container">
<shop-cart
:cart-list="cartList"
mode="single"
:config="cartConfig"
@update="handleCartUpdate"
@go-shopping="goShopping"
@checkout="handleCheckout"
@click="handleItemClick"
/>
</view>
</template>
<script>
export default {
name: 'SingleCartPage',
data() {
return {
// 购物车商品列表
cartList: [
{
id: 1,
name: 'iPhone 15 Pro Max 256GB',
spec: '原色钛金属',
price: 9999,
quantity: 1,
stock: 10,
selected: true,
image: 'https://picsum.photos/160/160?random=1'
},
{
id: 2,
name: 'MacBook Pro 14英寸',
spec: 'M3 Pro芯片 18GB内存 512GB',
price: 16999,
quantity: 2,
stock: 5,
selected: false,
image: 'https://picsum.photos/160/160?random=2'
}
],
// 配置对象
cartConfig: {
safeArea: true,
containerBg: '#f5f5f5',
empty: {
text: '购物车空空如也',
btnText: '去逛逛',
btnBg: '#1677FF',
btnColor: '#fff'
},
item: {
bg: '#fff',
borderRadius: '16rpx',
nameColor: '#333',
specColor: '#999',
priceColor: '#1677FF',
checkedIcon: '/static/icon-check-s.png',
uncheckedIcon: '/static/icon-check-n.png',
qty: {
bg: '#f5f5f5',
btnColor: '#1677FF',
valueColor: '#333'
}
},
footer: {
selectedText: '已选',
unitText: '件',
checkoutText: '结算',
bg: '#fff',
selectedCountColor: '#1677FF',
priceValueColor: '#1677FF',
checkoutBtnBg: '#1677FF',
checkoutBtnColor: '#fff',
checkoutBtnDisabledBg: '#ccc'
}
}
}
},
methods: {
/**
* 购物车数据更新回调
* @param {Array} newList - 更新后的购物车列表
*/
handleCartUpdate(newList) {
this.cartList = newList
uni.setStorageSync('cartList', JSON.stringify(this.cartList))
},
/**
* 点击"去逛逛"按钮
*/
goShopping() {
uni.switchTab({
url: '/pages/index/index'
})
},
/**
* 点击商品项,跳转到商品详情页
* @param {Object} item - 商品对象
*/
handleItemClick(item) {
uni.navigateTo({
url: `/pages/product-detail/index?id=${item.id}`
})
},
/**
* 点击结算按钮
* @param {Array} selectedItems - 选中的商品列表
*/
handleCheckout(selectedItems) {
uni.setStorageSync('selectedItems', JSON.stringify(selectedItems))
uni.navigateTo({
url: '/pages/checkout/index'
})
}
},
onLoad() {
const savedCart = uni.getStorageSync('cartList')
if (savedCart) {
this.cartList = JSON.parse(savedCart)
}
}
}
</script>
<style lang="scss">
.page-container {
min-height: 100vh;
background: #f5f5f5;
}
</style>
基础用法 - 多商家模式
<template>
<view class="page-container">
<shop-cart
:cart-list="groupCartList"
mode="group"
:config="cartConfig"
@update="handleCartUpdate"
@go-shopping="goShopping"
@checkout="handleCheckout"
@click="handleItemClick"
@group-click="handleGroupClick"
/>
</view>
</template>
<script>
export default {
name: 'GroupCartPage',
data() {
return {
// 多商家购物车列表
groupCartList: [
{
id: 'group1',
name: 'Apple Store',
shopName: '官方旗舰店',
items: [
{
id: 1,
name: 'iPhone 15 Pro',
spec: '128GB 蓝色钛金属',
price: 7999,
quantity: 1,
stock: 20,
selected: true,
image: 'https://picsum.photos/160/160?random=1'
},
{
id: 2,
name: 'AirPods Pro 2',
spec: '第二代 USB-C充电盒',
price: 1899,
quantity: 2,
stock: 50,
selected: false,
image: 'https://picsum.photos/160/160?random=2'
}
]
},
{
id: 'group2',
name: '数码配件馆',
shopName: '精选配件',
items: [
{
id: 3,
name: 'MagSafe充电器',
spec: '15W无线快充',
price: 329,
quantity: 1,
stock: 100,
selected: true,
image: 'https://picsum.photos/160/160?random=3'
}
]
}
],
cartConfig: {
safeArea: false,
containerBg: '#f8f8f8',
empty: {
text: '购物车空空如也',
btnText: '去逛逛',
btnBg: '#ff6b6b',
btnColor: '#fff'
},
item: {
bg: '#fff',
borderRadius: '12rpx',
nameColor: '#1a1a1a',
specColor: '#999',
priceColor: '#ff6b6b',
checkedIcon: '/static/icon-check-s.png',
uncheckedIcon: '/static/icon-check-n.png',
qty: {
bg: '#f0f0f0',
btnColor: '#666',
valueColor: '#333'
}
},
footer: {
selectedText: '已选',
unitText: '件',
checkoutText: '结算',
bg: '#fff',
selectedCountColor: '#ff6b6b',
priceValueColor: '#ff6b6b',
checkoutBtnBg: '#ff6b6b',
checkoutBtnColor: '#fff',
checkoutBtnDisabledBg: '#ccc'
}
}
}
},
methods: {
handleCartUpdate(newList) {
this.groupCartList = newList
uni.setStorageSync('groupCartList', JSON.stringify(this.groupCartList))
},
goShopping() {
uni.showToast({
title: '跳转到商品列表',
icon: 'none'
})
},
/**
* 点击商品项,跳转到商品详情页
* @param {Object} item - 商品对象
*/
handleItemClick(item) {
uni.navigateTo({
url: `/pages/product-detail/index?id=${item.id}`
})
},
/**
* 点击商家头部,跳转到店铺页面
* @param {Object} group - 商家分组对象
*/
handleGroupClick(group) {
uni.navigateTo({
url: `/pages/shop-detail/index?id=${group.id}`
})
},
handleCheckout(selectedItems) {
const totalPrice = selectedItems.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
uni.showModal({
title: '确认结算',
content: `共 ${selectedItems.length} 种商品,合计 ¥${totalPrice.toFixed(2)}`,
confirmText: '去支付',
success: (res) => {
if (res.confirm) {
uni.setStorageSync('selectedItems', JSON.stringify(selectedItems))
uni.navigateTo({
url: '/pages/checkout/index'
})
}
}
})
}
},
onLoad() {
const savedCart = uni.getStorageSync('groupCartList')
if (savedCart) {
this.groupCartList = JSON.parse(savedCart)
}
}
}
</script>
API 文档
Props
| 参数 |
类型 |
默认值 |
必填 |
说明 |
| cartList |
Array |
[] |
是 |
购物车商品列表,根据 mode 不同有不同的数据结构 |
| mode |
String |
'single' |
否 |
展示模式:'single' 单商品模式,'group' 多商家模式 |
| config |
Object |
{} |
否 |
配置对象,用于自定义样式和文案 |
Config 配置项详解
顶层配置
| 属性 |
类型 |
默认值 |
说明 |
| safeArea |
Boolean |
true |
是否需要底部安全区域,非 tabbar 页面设置为 true,tabbar 页面设置为 false |
| containerBg |
String |
'#f8f8f8' |
购物车容器的背景色 |
empty - 空状态配置
| 属性 |
类型 |
默认值 |
说明 |
| empty.text |
String |
'购物车空空如也' |
空状态提示文字 |
| empty.btnText |
String |
'去逛逛' |
空状态按钮文字 |
| empty.btnBg |
String |
'#1677FF' |
空状态按钮背景色 |
| empty.btnColor |
String |
'#fff' |
空状态按钮文字颜色 |
item - 商品项配置
| 属性 |
类型 |
默认值 |
说明 |
| item.bg |
String |
'#fff' |
商品项背景色 |
| item.borderRadius |
String |
'16rpx' |
商品项圆角大小 |
| item.nameColor |
String |
'#333' |
商品名称颜色 |
| item.specColor |
String |
'#999' |
商品规格颜色 |
| item.priceColor |
String |
'#1677FF' |
商品价格颜色 |
| item.checkedIcon |
String |
'/static/icon-check-s.png' |
选中状态图标路径 |
| item.uncheckedIcon |
String |
'/static/icon-check-n.png' |
未选中状态图标路径 |
item.qty - 数量控制器配置
| 属性 |
类型 |
默认值 |
说明 |
| item.qty.bg |
String |
'#f5f5f5' |
数量控制器背景色 |
| item.qty.btnColor |
String |
'#1677FF' |
增减按钮颜色 |
| item.qty.valueColor |
String |
'#333' |
数量数值颜色 |
footer - 底部结算栏配置
| 属性 |
类型 |
默认值 |
说明 |
| footer.selectedText |
String |
'已选' |
已选文字 |
| footer.unitText |
String |
'件' |
单位文字 |
| footer.checkoutText |
String |
'结算' |
结算按钮文字 |
| footer.bg |
String |
'#fff' |
底部栏背景色 |
| footer.selectedCountColor |
String |
'#1677FF' |
选中数量颜色 |
| footer.priceLabel |
String |
'合计:' |
价格标签文字 |
| footer.priceLabelColor |
String |
'#666' |
价格标签颜色 |
| footer.priceValueColor |
String |
'#1677FF' |
总价数值颜色 |
| footer.checkoutBtnBg |
String |
'#1677FF' |
结算按钮背景色 |
| footer.checkoutBtnColor |
String |
'#fff' |
结算按钮文字颜色 |
| footer.checkoutBtnDisabledBg |
String |
'#ccc' |
结算按钮禁用状态背景色 |
Events
| 事件名 |
参数 |
说明 |
| update |
cartList (Array) |
购物车数据更新时触发,携带更新后的完整购物车列表 |
| go-shopping |
- |
点击空状态的"去逛逛"按钮时触发 |
| checkout |
selectedItems (Array) |
点击结算按钮时触发,携带选中的商品列表 |
| click |
item (Object) |
点击商品项时触发,携带商品对象(用于跳转详情页) |
| group-click |
group (Object) |
点击商家头部时触发,携带分组对象(用于跳转店铺页,仅多商家模式) |
数据结构
单商品模式 cartList
[
{
id: 1, // 商品唯一ID,Number 或 String
name: '商品名称', // 商品名称,必填
spec: '规格描述', // 商品规格,如颜色、尺寸等,必填
price: 99.99, // 商品单价,Number,必填
quantity: 1, // 购买数量,Number,默认1
stock: 10, // 库存数量,Number,默认999
selected: true, // 是否选中,Boolean,默认false
image: 'https://...' // 商品图片地址,必填
}
]
多商家模式 cartList
[
{
id: 'group1', // 商家分组ID,String
name: '商家名称', // 商家名称,必填
shopName: '店铺名称', // 店铺名称,选填
items: [
{
id: 1, // 商品唯一ID
name: '商品名称', // 商品名称
spec: '规格描述', // 商品规格
price: 99.99, // 商品单价
quantity: 1, // 购买数量
stock: 10, // 库存数量
selected: true, // 是否选中
image: 'https://...' // 商品图片地址
}
]
}
]
子组件说明
1. shop-cart-item(商品项组件)
| Props: |
属性 |
类型 |
默认值 |
说明 |
| item |
Object |
- |
商品对象(必填) |
| config |
Object |
{} |
配置对象 |
| mode |
String |
'group' |
展示模式 |
| Events: |
事件名 |
参数 |
说明 |
| select |
id |
点击复选框时触发 |
| increase |
item |
数量增加时触发 |
| decrease |
item |
数量减少时触发 |
| update |
(item, val) |
数量直接输入更新时触发 |
| click |
item |
点击商品内容区域时触发(跳转详情页) |
2. shop-cart-group(商家分组组件)
| Props: |
属性 |
类型 |
默认值 |
说明 |
| group |
Object |
- |
商家分组对象(必填) |
| config |
Object |
{} |
配置对象 |
| Events: |
事件名 |
参数 |
说明 |
| update |
group |
分组内数据更新时触发 |
| click |
item |
点击商品项时触发 |
| group-click |
group |
点击商家头部时触发(跳转店铺页) |
3. shop-cart-footer(底部结算栏组件)
| Props: |
属性 |
类型 |
默认值 |
说明 |
| selectedCount |
Number |
0 |
选中的商品种类数 |
| totalPrice |
Number |
0 |
选中商品的总价 |
| isAllSelected |
Boolean |
false |
是否全选 |
| config |
Object |
{} |
配置对象 |
| safeArea |
Boolean |
true |
是否需要底部安全区域 |
| Events: |
事件名 |
参数 |
说明 |
| checkout |
- |
点击结算按钮时触发 |
| select-all |
- |
点击全选按钮时触发 |
4. shop-cart-empty(空状态组件)
| Props: |
属性 |
类型 |
默认值 |
说明 |
| config |
Object |
{} |
配置对象 |
| Events: |
事件名 |
参数 |
说明 |
| click |
- |
点击按钮时触发 |
5. quantity-control(数量控制器组件)
| Props: |
属性 |
类型 |
默认值 |
说明 |
| quantity |
Number |
1 |
当前数量 |
| stock |
Number |
999 |
库存上限 |
| config |
Object |
{} |
配置对象 |
| Events: |
事件名 |
参数 |
说明 |
| decrease |
- |
数量减少时触发 |
| increase |
- |
数量增加时触发 |
| update |
val |
数量直接输入更新时触发 |
自定义样式示例
示例1:自定义主题色
cartConfig: {
containerBg: '#f0f4f8',
item: {
priceColor: '#FF5252',
qty: {
btnColor: '#FF5252'
}
},
footer: {
selectedCountColor: '#FF5252',
priceValueColor: '#FF5252',
checkoutBtnBg: '#FF5252'
}
}
示例2:自定义图标
cartConfig: {
item: {
checkedIcon: '/static/icons/checked.png',
uncheckedIcon: '/static/icons/unchecked.png'
},
footer: {
checkedIcon: '/static/icons/checked.png',
uncheckedIcon: '/static/icons/unchecked.png'
}
}
示例3:调整尺寸
cartConfig: {
item: {
borderRadius: '8rpx',
qty: {
bg: '#fafafa',
btnColor: '#333',
valueColor: '#1a1a1a'
}
}
}
注意事项
-
图片尺寸:商品图片建议使用 160x160 像素,以获得最佳显示效果
-
安全区域适配:
- 如果购物车页面是 tabbar 页面,设置
safeArea: false
- 如果购物车页面是普通页面(非 tabbar),设置
safeArea: true
-
数据持久化:建议在 @update 事件中保存购物车数据到本地存储
-
空状态图标:默认使用网络图片,如需自定义请替换 empty.icon 配置
-
图标路径:复选框图标默认使用 /static/icon-check-s.png 和 /static/icon-check-n.png,请确保这些文件存在
-
数量限制:数量最小为 1,最大为 stock 值(默认 999)
-
点击事件:
@click 事件在单商品模式和多商家模式下都会触发
@group-click 事件仅在多商家模式下触发
更新日志
v1.1.0
新增功能:
- ✅ 商品点击事件(
@click),支持跳转商品详情页
- ✅ 商家点击事件(
@group-click),支持跳转店铺页面(仅多商家模式)
- ✅ 优化商品项布局,支持点击区域精确控制
更新文档:
v1.0.0
新增功能:
- ✅ 商品列表展示(图片、名称、规格、单价、数量)
- ✅ 数量增减控制(支持输入)
- ✅ 单选/全选功能
- ✅ 实时价格计算
- ✅ 空状态提示
- ✅ 结算跳转
- ✅ 单商品模式
- ✅ 多商家模式
- ✅ 自定义样式配置
- ✅ 底部安全区域适配
组件文件:
shop-cart.vue - 主组件
shop-cart-item.vue - 商品项组件
shop-cart-group.vue - 商家分组组件
shop-cart-footer.vue - 底部结算栏
shop-cart-empty.vue - 空状态组件
quantity-control.vue - 数量控制器
配置文件:
package.json - 组件元信息
README.md - 组件文档
License
MIT License
作者
如有问题或建议,欢迎反馈。