【微信小程序】 实现购物车
原生实现,不使用任何框架,网上没有啥好看的购物车,而且都是抄来抄去的,我来写一个好点的作为参考吧,拿来就能用。
接口自行添加。
链接:购物车代码片段。图片太大了没办法上传到代码片段,自己找几张图片就行。
购物车功能包括:文章来源:https://www.toymoban.com/news/detail-771336.html
- 显示默认地址,或选择地址(需定向到你自己的页面去选择,这里不做演示)
- 显示店铺,点击店铺可全选该店铺的所有商品
- 显示商品,可选择商品,增加或减少数量,可左滑删除
- 左滑删除:如果有一个已经显示出删除按钮,未操作,此时滑动另一个商品,之前的显示将回归原位
- 选择商品下方显示总价并结算订单
- 选择全部购物车商品
- 显示已失效商品
效果图片
文章来源地址https://www.toymoban.com/news/detail-771336.html
cart.wxml
<!-- 选择地址 -->
<view class="choose-address">
<view class="address">配送至:<text>{{address}}</text></view>
<image class="img" src="/images/arrow_right_grey.svg"></image>
</view>
<!-- 生效商品区域 -->
<view class="cart-effect">
<block wx:for="{{cartEffectList}}" wx:key="key" wx:for-item="item">
<checkbox-group class="check-group" wx:if="{{item.merchandises.length > 0}}">
<checkbox class="check-store-all" data-store="{{item.store}}" bind:tap="checkStoreAll" checked="{{item.checked || item.merchandiseChecked}}">
<view class="store">{{item.store}}</view>
</checkbox>
<block wx:for="{{item.merchandises}}" wx:key="key" wx:for-item="merchandise">
<movable-area class="move-area">
<movable-view class="move-view" x="{{merchandise.x}}" data-store="{{item.store}}" data-id="{{merchandise.id}}" direction="horizontal" out-of-bounds="true" damping="50" inertia="true" bind:touchstart="touchStart" bind:touchmove="touchMove" bind:touchend="touchEnd" bindchange="touchChange">
<view class="info">
<checkbox class="merchandise-check" data-store="{{item.store}}" data-merchandise="{{merchandise}}" bind:tap="checkSingle" checked="{{merchandise.checked}}"></checkbox>
<view class="merchandise">
<image class="merchandise-img" src="{{merchandise.imgUrl}}"></image>
<view class="merchandise-name">{{merchandise.name}}</view>
<view class="merchandise-price">¥{{merchandise.price}}/瓶</view>
<view class="merchandise-amount">
<view class="minus" data-store="{{item.store}}" data-id="{{merchandise.id}}" bind:tap="minusAmount">-</view>
<view class="amount">{{merchandise.amount}}</view>
<view class="plus" data-store="{{item.store}}" data-id="{{merchandise.id}}" bind:tap="plusAmount">+</view>
</view>
</view>
</view>
</movable-view>
<view class="delete" data-store="{{item.store}}" data-id="{{merchandise.id}}" bind:tap="deleteMerchandise">删除</view>
</movable-area>
</block>
</checkbox-group>
</block>
</view>
<!-- 失效商品 -->
<view class="cart-lapse">
<block wx:for="{{cartLapseList}}" wx:key="key">
<checkbox-group class="check-group" bindchange="checkStoreAll">
<view class="store">已失效商品({{cartLapseList.length}})</view>
<movable-area class="move-area">
<movable-view class="move-view" x="{{item.x}}" data-id="{{item.id}}" direction="horizontal" out-of-bounds="true" damping="50" inertia="true" bind:touchstart="touchStart" bind:touchmove="touchMove" bind:touchend="touchEnd" bindchange="touchChange">
<view class="info">
<checkbox class="merchandise-check" disabled="true"></checkbox>
<view class="merchandise">
<view class="sold-out">
<image class="merchandise-img" src="{{item.imgUrl}}"></image>
<view class="status">{{item.info}}</view>
</view>
<view class="merchandise-name">{{item.name}}</view>
<view class="merchandise-price">¥{{item.price}}/瓶</view>
<view class="merchandise-amount">
<view class="minus" style="color: gray;">-</view>
<view class="amount" style="color: gray;">{{item.amount}}</view>
<view class="plus" style="color: gray;">+</view>
</view>
</view>
</view>
</movable-view>
<view class="delete" data-id="{{item.id}}" bind:tap="deleteMerchandise">删除</view>
</movable-area>
</checkbox-group>
</block>
</view>
<!-- 结算 -->
<view class="count">
<checkbox class="check-all" bind:tap="checkAll" checked="{{checkedAll}}">全选</checkbox>
<view class="grand">合计:¥{{total}}</view>
<button class="lapse {{totalCount > 0 ? 'settle-btn' : ''}}" bind:tap="settleBill" hover-class="settle-bill" disabled="{{totalCount == 0}}" loading="{{showLoading}}">结算({{totalCount}})</button>
</view>
cart.wxss
page {
padding: 16rpx;
padding-bottom: 172rpx;
box-sizing: border-box;
}
/* 选择地址 */
.choose-address {
display: flex;
align-items: center;
justify-content: flex-start;
text-align: left;
}
.choose-address .address {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.choose-address .img {
width: 32rpx;
height: 32rpx;
}
/* 选择地址 */
/* 生效商品 */
.cart-effect {
display: flex;
flex-direction: column;
}
.cart-effect .check-group {
background-color: #fbfbfd;
padding: 16rpx;
box-sizing: border-box;
border-radius: 32rpx;
margin-top: 16rpx;
z-index: 11;
}
.cart-effect .check-group .check-store-all {
display: flex;
justify-content: flex-start;
align-items: center;
}
.cart-effect .check-group .store {
font-size: 36rpx;
font-weight: bolder;
margin-left: 8rpx;
}
.cart-effect .check-group .move-area{
/* 减去删除部分的宽度 */
width: calc(100% - 128rpx);
height: 280rpx;
position: relative;
display: flex;
}
.cart-effect .check-group .move-area .move-view {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
/* 上面减了多少宽度,下面就要加多少,不然无法起到遮挡作用 */
width: calc(100% + 128rpx);
background-color: #fbfbfd;
box-sizing: border-box;
z-index: 10;
padding-right: 12rpx;
}
.cart-effect .check-group .move-area .delete {
position: absolute;
top: 0;
bottom: 0;
left: 100%;
/* 删除部分的宽度 */
width: 128rpx;
text-align: center;
z-index: 9;
display: flex;
justify-content: center;
align-items: center;
background-color: #f43838;
color: #fbfbfd;
}
.cart-effect .check-group .move-area .move-view .info {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
overflow: hidden;
box-sizing: border-box;
}
.cart-effect .check-group .move-area .move-view .info .merchandise {
display: grid;
grid-template-columns: 1fr 2fr 2fr;
grid-template-rows: 110rpx 60rpx;
grid-column-gap: 12rpx;
grid-row-gap: 12rpx;
}
.cart-effect .check-group .move-area .move-view .info .merchandise .merchandise-img {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
grid-column: 1 / 2;
grid-row: 1 / 3;
}
.cart-effect .check-group .move-area .move-view .info .merchandise .merchandise-name {
grid-column: 2 / 4;
grid-row: 1 / 2;
font-size: 32rpx;
}
.cart-effect .check-group .move-area .move-view .info .merchandise .merchandise-price {
grid-column: 2 / 3;
grid-row: 2 / 3;
font-size: 32rpx;
font-weight: 550;
}
.cart-effect .check-group .move-area .move-view .merchandise .merchandise-amount {
grid-column: 3 / 4;
grid-row: 2 / 3;
text-align: right;
display: grid;
grid-template-columns: repeat(3, 1fr);
align-items: center;
text-align: center;
line-height: 50rpx;
}
.cart-effect .check-group .move-area .move-view .merchandise .merchandise-amount .minus {
font-size: 42rpx;
background-color: #e5e5e5;
border-radius: 16rpx;
}
.cart-effect .check-group .move-area .move-view .merchandise .merchandise-amount .amount {
font-size: 36rpx;
}
.cart-effect .check-group .move-area .move-view .merchandise .merchandise-amount .plus {
background-color: #e5e5e5;
border-radius: 16rpx;
font-size: 42rpx;
}
/* 失效商品 */
.cart-lapse {
display: flex;
flex-direction: column;
}
.cart-lapse .check-group {
background-color: #fbfbfd;
padding: 16rpx;
box-sizing: border-box;
border-radius: 32rpx;
margin-top: 16rpx;
z-index: 11;
}
.cart-lapse .check-group .check-store-all {
display: flex;
justify-content: flex-start;
align-items: center;
}
.cart-lapse .check-group .store {
font-size: 36rpx;
font-weight: bolder;
}
.cart-lapse .check-group .move-area{
/* 减去删除部分的宽度 */
width: calc(100% - 128rpx);
height: 280rpx;
position: relative;
display: flex;
}
.cart-lapse .check-group .move-area .move-view {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
/* 上面减了多少宽度,下面就要加多少,不然无法起到遮挡作用 */
width: calc(100% + 128rpx);
background-color: #fbfbfd;
box-sizing: border-box;
z-index: 10;
padding-right: 12rpx;
}
.cart-lapse .check-group .move-area .delete {
position: absolute;
top: 0;
bottom: 0;
left: 100%;
/* 删除部分的宽度 */
width: 128rpx;
text-align: center;
z-index: 9;
display: flex;
justify-content: center;
align-items: center;
background-color: #f43838;
color: #fbfbfd;
}
.cart-lapse .check-group .move-area .move-view .info {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
overflow: hidden;
box-sizing: border-box;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise {
display: grid;
grid-template-columns: 1fr 2fr 2fr;
grid-template-rows: 110rpx 60rpx;
grid-column-gap: 12rpx;
grid-row-gap: 12rpx;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise .sold-out {
position: relative;
width: 180rpx;
height: 180rpx;
border-radius: 50%;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise .sold-out .merchandise-img {
width: 100%;
height: 100%;
border-radius: 50%;
grid-column: 1 / 2;
grid-row: 1 / 3;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise .sold-out .status{
width: 100%;
height: 100%;
background-color: #1a1a1a90;
position: absolute;
top: 0;
left: 0;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
color: #fbfbfb;
font-size: 36rpx;
font-weight: 500;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise .merchandise-name {
grid-column: 2 / 4;
grid-row: 1 / 2;
font-size: 32rpx;
}
.cart-lapse .check-group .move-area .move-view .info .merchandise .merchandise-price {
grid-column: 2 / 3;
grid-row: 2 / 3;
font-size: 32rpx;
font-weight: 550;
}
.cart-lapse .check-group .move-area .move-view .merchandise .merchandise-amount {
grid-column: 3 / 4;
grid-row: 2 / 3;
text-align: right;
display: grid;
grid-template-columns: repeat(3, 1fr);
align-items: center;
text-align: center;
line-height: 60rpx;
}
.cart-lapse .check-group .move-area .move-view .merchandise .merchandise-amount .minus {
font-size: 42rpx;
background-color: #e5e5e5;
border-radius: 16rpx;
}
.cart-lapse .check-group .move-area .move-view .merchandise .merchandise-amount .amount {
font-size: 36rpx;
}
.cart-lapse .check-group .move-area .move-view .merchandise .merchandise-amount .plus {
background-color: #e5e5e5;
border-radius: 16rpx;
font-size: 42rpx;
}
.count {
width: calc(100% - 32rpx);
box-sizing: border-box;
margin-top: 16rpx;
padding: 16rpx;
background-color: #fbfbfd;
border-radius: 120rpx;
position: fixed;
bottom: 42rpx;
z-index: 99;
display: flex;
justify-content: space-between;
align-items: center;
border: 1rpx solid #e5e5e5;
}
.count .check-all{
font-size: 34rpx;
font-weight: 550;
}
.count .grand {
width: 50%;
text-align: right;
font-size: 32rpx;
font-weight: 550;
color: #d1362f;
}
.count .lapse {
width: 200rpx;
height: 80rpx;
color: #fdfdfd;
border-radius: 120rpx;
margin: 0;
text-align: center;
background-color: #e5e5e5;
padding: 0;
line-height: 80rpx;
}
.count .settle-btn {
background-color: #d1362f;
}
.count .settle-bill {
background-color: #d1362f80
}
cart.js
Page({
/**
* 页面的初始数据
*/
data: {
address: "XX省XX市XX区XX街道XX号XX室",
cartEffectList: [],
// 生效商品
example: [
{
store: "龙门客栈",
checked: false,
merchandiseChecked: false,
merchandises: [
{
id: "1",
imgUrl: "/images/wine1.jpg",
name: "宫廷玉液酒",
price: 180,
amount: 1,
status: 0,
x: 0,
checked: false,
},
{
id: "2",
imgUrl: "/images/wine2.png",
name: "群英荟萃",
price: 98,
amount: 99,
status: 1,
x: 0,
checked: false,
},
{
id: "3",
imgUrl: "/images/wine3.png",
name: "二锅头",
price: 28,
amount: 1,
status: 1,
x: 0,
checked: false,
},
]
},
{
store: "八马茶叶",
checked: false,
merchandiseChecked: false,
merchandises: [
{
id: "1",
imgUrl: "/images/wine3.png",
name: "黑普洱茶饼",
price: 1800,
amount: 1,
status: 1,
x: 0,
checked: false,
},
]
},
],
// 失效商品
cartLapseList: [
{
id: "3",
imgUrl: "/images/wine2.png",
name: "威士忌",
info: "已售罄",
price: 148,
amount: 1,
x: 0,
status: -1,
checked: false,
}
],
startX: 0,
moveStore: '',
delBtnW: 128,
isLeft: 0,
total: 0,
totalCount: 0,
checkedAll: false,
billLoading: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getCartList();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 获取用户购物车
*/
getCartList() {
let cartEffectList = this.data.example;
wx.setStorageSync('cartEffectList', cartEffectList);
this.setData({
cartEffectList: cartEffectList,
});
},
/**
* 店铺全选
* @param {*} e
*/
checkStoreAll(e) {
let storeName = e.currentTarget.dataset.store;
let cartEffectList = this.data.cartEffectList;
let updatedCart = cartEffectList.map(store => {
if (store.store === storeName) {
// 切换商店的已勾选标记
store.checked = !store.checked;
// 重置商店的商品已勾选标记
store.merchandiseChecked = false;
store.merchandises = store.merchandises.map(merch => {
// 根据商店的已检查标记更新商品的已选择标记
merch.checked = store.checked;
return merch;
});
}
return store;
});
this.setData({
cartEffectList: updatedCart
});
this.grand();
},
/**
* 单个选择
* @param {*} e
*/
checkSingle(e) {
let storeName = e.currentTarget.dataset.store;
let merchandiseData = e.currentTarget.dataset.merchandise;
let cartEffectList = this.data.cartEffectList;
let updatedCart = cartEffectList.map(store => {
if (store.store === storeName) {
store.merchandises = store.merchandises.map(merch => {
if (merch.id === merchandiseData.id) {
// 更改商品的选择状态
merch.checked = !merch.checked;
// 更改店铺的选中状态
store.merchandiseChecked = merch.checked;
}
return merch;
});
}
return store;
});
this.setData({
cartEffectList: updatedCart
});
this.grand();
},
/**
* 减少数量,下限为1
* @param {*} e
*/
minusAmount(e) {
let storeName = e.currentTarget.dataset.store;
let id = e.currentTarget.dataset.id;
let cartEffectList = this.data.cartEffectList;
let updatedCart = cartEffectList.map(store => {
if (store.store === storeName) {
// 更新商店的已选择商品标记
store.merchandiseChecked = true;
store.merchandises = store.merchandises.map(merch => {
if (merch.id === id) {
// 更新商品的选择状态
merch.checked = true;
if (merch.amount > 1) {
merch.amount--;
} else {
wx.showModal({
content: '宝贝数量不能再减少了',
showCancel: false,
});
}
}
return merch;
});
}
return store;
});
this.setData({
cartEffectList: updatedCart
});
this.grand();
},
/**
* 增加数量,上限为99
* @param {*} e
*/
plusAmount(e) {
let storeName = e.currentTarget.dataset.store;
let id = e.currentTarget.dataset.id;
let cartEffectList = this.data.cartEffectList;
let updatedCart = cartEffectList.map(store => {
if (store.store === storeName) {
// 更新店铺的选中状态
store.merchandiseChecked = true;
store.merchandises = store.merchandises.map(merch => {
if (merch.id === id) {
// 更新商品的选择状态
merch.checked = true;
if (merch.amount < 99) {
merch.amount++;
} else {
wx.showModal({
content: '宝贝数量不能再增加了',
showCancel: false,
});
}
}
return merch;
});
}
return store;
});
this.setData({
cartEffectList: updatedCart
});
this.grand();
},
/**
* 删除商品
* @param {*} e
*/
deleteMerchandise(e) {
let storeName = e.currentTarget.dataset.store;
let id = e.currentTarget.dataset.id;
let cartEffectList = this.data.cartEffectList;
let cartLapseList = this.data.cartLapseList;
// 遍历每个商店
let updatedEffectCart = cartEffectList.map(store => {
if (store.store === storeName) {
// 如果是目标商店,过滤掉指定ID的商品
store.merchandises = store.merchandises.filter(merch => merch.id !== id);
}
return store;
});
cartLapseList = cartLapseList.filter(item => item.id !== id);
this.setData({
cartEffectList: updatedEffectCart,
cartLapseList: cartLapseList
});
},
/**
* 全选
*/
checkAll() {
let cartEffectList = this.data.cartEffectList;
let checkedAll = this.data.checkedAll;
// 使用map方法更新每个商店和商品的选中状态
let updatedCart = cartEffectList.map(store => {
return {
...store,
checked: !checkedAll,
merchandiseChecked: !checkedAll,
merchandises: store.merchandises.map(merch => ({
...merch,
checked: !checkedAll
}))
};
});
this.setData({
cartEffectList: updatedCart,
checkedAll: !checkedAll,
});
this.grand();
},
/**
* 合计
*/
grand() {
let cartEffectList = this.data.cartEffectList;
// 初始化总价和总数量
let total = 0;
let totalCount = 0;
cartEffectList.forEach(store => {
store.merchandises.forEach(merch => {
if (merch.checked) {
total += merch.amount * merch.price;
totalCount += merch.amount;
}
});
});
this.setData({
total: total,
totalCount: totalCount,
});
},
/**
* 结算
*/
settleBill() {
this.setData({
showLoading: true,
});
// 进行深拷贝
let cartEffectList = JSON.parse(JSON.stringify(this.data.cartEffectList));
// 筛选出未选中的商店或没有选中商品的商店
let filteredStores = cartEffectList.filter(store => {
if (store.checked) {
// 如果商店被选中,保留它
return true;
}
// 从商店中筛选出未选中的商品
store.merchandises = store.merchandises.filter(merch => merch.checked);
// 如果筛选后,商店有选中的商品,保留该商店
return store.merchandises.length > 0;
});
wx.navigateTo({
url: `/pages/index/settle-bill/settle-bill?chosenList=${JSON.stringify(filteredStores)}`,
complete: () => {
this.setData({
showLoading: false,
});
}
});
},
/**
* 开始滑动
* @param {*} e
*/
touchStart (e) {
let index = e.currentTarget.dataset.id;
let store = e.currentTarget.dataset.store;
let cartEffectList = this.data.cartEffectList;
let cartLapseList = this.data.cartLapseList;
// 复位,这样子就能保证一次显示一个删除按钮
for (let i in cartEffectList) {
for (let j in cartEffectList[i].merchandises) {
cartEffectList[i].merchandises[j].x = 0
}
}
for (let i in cartLapseList) {
cartLapseList[i].x = 0;
}
// 判断是否为多触点
if (e.touches.length == 1) {
// 记录开始触摸的位置
this.setData({
startX: e.touches[0].clientX,
cartEffectList: cartEffectList,
cartLapseList: cartLapseList
});
}
},
/**
* 开始移动
* @param {*} e
*/
touchMove (e) {
let id = e.currentTarget.dataset.id;
let store = e.currentTarget.dataset.store;
if (e.touches.length == 1) {
// 记录移动的距离
let disX = e.touches[0].clientX - this.data.startX;
// 大于0则时向右滑,复位
if (disX >= 0) {
// 向右滑
this.setData({
isLeft: 0
});
} else {
// 小于则是向左滑
this.setData({
isLeft: 1
});
}
}
},
/**
* 滑动终点
* @param {*} e
*/
touchEnd (e) {
let id = e.currentTarget.dataset.id;
let store = e.currentTarget.dataset.store;
let delw = this.data.delBtnW;
if (e.touches.length == 1) {
let endX = e.touches[0].clientX - this.data.startX;
if (endX < 0) {
this.setXmove(id, store, - delw);
} else {
this.setXmove(id, store, 0);
}
}
},
/**
* 滑动事件
* @param {*} e
*/
touchChange (e) {
let delw = this.data.delBtnW;
let store = e.currentTarget.dataset.store;
let id = e.currentTarget.dataset.id;
if (this.data.isLeft) {
if (e.detail.source == 'friction') {
if (e.detail.x < 0) {
this.setXmove(id, store, -delw);
} else {
this.setXmove(id, store, 0);
}
}
} else {
if (e.detail.source == 'friction') {
this.setXmove(id, store, 0);
}
}
},
/**
* 设置起始位置
* @param {*} id
* @param {*} store
* @param {*} x
*/
setXmove(id, store, x) {
let that = this;
if (store) {
let cartEffectList = this.data.cartEffectList;
for (let i in cartEffectList) {
if (cartEffectList[i].store == store) {
for (let j in cartEffectList[i].merchandises) {
if (cartEffectList[i].merchandises[j].id == id) {
cartEffectList[i].merchandises[j].x = x;
}
}
}
}
that.setData({
cartEffectList: cartEffectList,
});
} else {
let cartLapseList = this.data.cartLapseList;
for (let i in cartLapseList) {
if (cartLapseList[i].id == id) {
cartLapseList[i].x = x;
}
}
that.setData({
cartLapseList: cartLapseList,
});
}
}
})
checkbox样式更改
checkbox .wx-checkbox-input{
width: 40rpx;
height: 40rpx;
border-radius: 50%;
}
/* 选中后的背景样式 */
checkbox .wx-checkbox-input.wx-checkbox-input-checked{
background: #b0474c;
}
/* 选中后的勾子样式 */
checkbox .wx-checkbox-input.wx-checkbox-input-checked::before{
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
border-radius: 50%;
text-align: center;
font-size:32rpx;
color:#fbfbfd;
background: transparent;
transform:translate(-50%, -50%) scale(1);
-webkit-transform:translate(-50%, -50%) scale(1);
}
到了这里,关于【微信小程序】 实现购物车的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!