1.渲染商品页面
<template>
<view>
<!-- 商品列表的标题区域 -->
<view class="cart-title">
<!-- 左侧的图标 -->
<uni-icons type="shop" size="18"></uni-icons>
<!-- 右侧的文本 -->
<text class="cart-title-text">购物车</text>
</view>
<!-- 循环渲染购物车中的商品信息 -->
<block v-for="(goods,i) in cart" :key="i">
<my-goods :goods="goods"></my-goods>
</block>
</view>
</template>
<script>
import badgeMix from '@/mixins/tabbar-badge.js'
import { mapState } from 'vuex'
export default {
mixins: [badgeMix],
computed: {
...mapState('m_cart',['cart'])
},
data() {
return {
}
}
}
</script>
2.展示购物车选中状态
mygoods.vue中配置
<view class="goods-item-left">
<radio checked color="#C00000" v-if="showRadio"></radio>
<image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
</view>
引入showRadio组件,判断是否选中
props: {
goods: {
type: Object,
default: {}
},
showRadio: {
type: Boolean,
//默认情况下,不会展示radio组件
default: false
}
},
1)动态修改
<radio :checked="goods.goods_state" color="#C00000" v-if="showRadio"></radio>
2)修改购物车商品的勾选状态
<!-- 循环渲染购物车中的商品信息 -->
<block v-for="(goods,i) in cart" :key="i">
<my-goods :goods="goods" :show-radio="true" @radio-change="radioChangeHandler"></my-goods>
</block>
<view class="goods-item-left">
<radio :checked="goods.goods_state" color="#C00000" v-if="showRadio" @click="radioClickHandler"></radio>
<image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
</view>
methods:{
//这是radio组件的点击事件处理函数
radioClickHandler(){
this.$emit('radio-change',{
goods_id: this.goods.goods_id,
goods_state: !this.goods.goods_state
})
}
}
cart.js中配置
export default{
namespaced: true,
state: ()=> ({
//购物车的数组,用来存储购物车中每个商品的信息对象
//每个商品的信息对象,都包含如下6个属性
//{goods_id,goods_name,goods_price,goods_count,goods_small_logo,goods_state}
cart: JSON.parse(uni.getStorageSync('cart') || '[]')
}),
mutations: {
addToCart(state,goods){
//根据提交的商品的id,查询购物车中是否存在这件商品
//如果不存在,则findResult为undefined;否则,为查找到的商品信息对象
const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
if(!findResult){
//如果购物车中没有这件商品,则直接push
state.cart.push(goods)
}else{
//如果购物车中有这件商品,则只更新数量即可
findResult.goods_count++
}
//通过commit方法,调用m_cart命名空间的saveToStorage方法
this.commit('m_cart/saveToStorage')
},
saveToStorage(state){
uni.setStorageSync('cart',JSON.stringify(state.cart))
},
//更新购物车中商品的勾选状态
updateGoodsState(state,goods){
const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
if(findResult){
findResult.goods_state = goods.goods_state
this.commit('m_cart/saveToStorage')
}
}
},
getters: {
total(state){
let c = 0
state.cart.forEach(x => c += x.goods_count)
return c
}
}
}
cart.vue中配置
import badgeMix from '@/mixins/tabbar-badge.js'
import { mapState, mapMutations } from 'vuex'
export default {
mixins: [badgeMix],
computed: {
...mapState('m_cart',['cart'])
},
data() {
return {
}
},
methods:{
...mapMutations('m_cart',['updateGoodsState']),
radioChangeHandler(e){
this.updateGoodsState(e)
}
}
3)渲染数字选择框
<view class="goods-item-right">
<!-- 商品的名字 -->
<view class="goods-name">{{goods.goods_name}}</view>
<view class="goods-info-box">
<view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
<!-- 数字选择框 -->
<uni-number-box :min="1" :value="goods.goods_count"></uni-number-box>
</view>
</view>
3控制选择框按需展示/隐藏
cart.vue
<!-- 循环渲染购物车中的商品信息 -->
<block v-for="(goods,i) in cart" :key="i">
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"></my-goods>
</block>
my-goods.vue
<!-- 数字选择框 -->
<uni-number-box :min="1" :value="goods.goods_count" v-if="showNum"></uni-number-box>
props: {
showNum: {
type: Boolean,
default: false
}
},
引入NumberBox组件
my-goods.vue
<!-- 数字选择框 -->
<uni-number-box :min="1" :value="goods.goods_count" v-if="showNum" @change="numChangeHandler"></uni-number-box>
//监听到了NumberBox组件数量变化的事件9
numChangeHandler(val){
this.$emit('num-change',{
goods_id: this.goods.goods_id,
goods_count: +val ///或者val-0 (这是为了确保val是一个数值)
})
}
cart.vue
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"
@num-change="numberChangeHandler"
></my-goods>
numberChangeHandler(e){
console.log(e);
}
4.修改购物车商品数量
cart.js中新增函数
//更新商品的数量
updateGoodsCount(state,goods){
const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
if(findResult){
findResult.goods_count = goods.goods_count// 持久化存储
this.commit('m_cart/saveToStorage')
}
}
cart.vue中配置
methods:{
...mapMutations('m_cart',['updateGoodsState','updateGoodsCount']),
radioChangeHandler(e){
this.updateGoodsState(e)
},
numberChangeHandler(e){
this.updateGoodsCount(e)
}
}
5.购物车滑动删除UI效果
<!-- 滑动删除效果 -->
<uni-swipe-action>
<block v-for="(goods,i) in cart" :key="i">
<uni-swipe-action-item :right-options="options">
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"
@num-change="numberChangeHandler"
></my-goods>
</uni-swipe-action-item>
</block>
</uni-swipe-action>
data() {
return {
options: [{
text: '删除',
style: {
backgroundColor: '#C00000'
}
}]
}
},
6.商品列表实现滑动删除
cart.js配置
//根据id删除对应的商品
removeGoodsById(state,goods_id){
state.cart = state.cart.filter(x => x.goods_id !== goods_id)
this.commit('m_cart/saveToStorage')
}v
cart.vue
swipeItemClickHandler(goods){
this.removeGoodsById(goods.goods_id)
}
7.收货地址
1)微信小程序中点击跳转到选择地址
manifest.json中配置
/* 快应用特有相关 */
"mp-weixin" : {
/* 小程序特有相关 */
"appid" : "",
"setting" : {
// 取消黄色警告
"urlCheck" : true,
"checkSiteMap": false
},
"usingComponents" : true,
"permissions": {
"chooseAddress": {
"desc": "你的文字描述" // 这里是对权限的描述,可以自定义
}
},
"requiredPrivateInfos":["chooseAddress"]
},
<view class="address-choose-box" v-if="JSON.stringify(address) === '{}' ">
<button type="primary" size="mini" class="btnChooseAddress" @click="chooseAddress">请选择收货地址+</button>
</view>
2)渲染收货人信息
<!-- 渲染收货信息的盒子 -->
<view class="address-info-box" v-else>
<view class="row1">
<view class="row1-left">
<view class="username">收货人: {{address.userName}}</view>
</view>
<view class="row1-right">
<view class="phone">电话: {{address.telNumber}}</view>
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
<view class="row2">
<view class="row2-left">收货地址:</view>
<view class="row2-right">{{addstr}}</view>
</view>
</view>
methods: {
async chooseAddress() {
//1调用小程序提供的chooseAddress()方法,即可使用选中收货地址的功能
//返回值是一个数组:第1项为错误对象,第2项为成功之后的收货地址对象
const [err, succ] = await uni.chooseAddress().catch(err => err)
//2.用户成功的选择了收货地址
if (err === null && succ.errMsg === 'chooseAddress:ok') {
//为data里面 的收货地址对象赋值
this.address = succconsole.log(succ);
}}
},
//计算属性
computed:{
addstr(){
if(!this.address.provinceName) {
return ''
}
return this.address.provinceName + this.address.cityName + this.address.countyName + this.address.detailInfo
}
}
3)改造收货地址方法
创建user.js
export default{
//开启命名空间
namespaced: true,
//数据
state: () =>({
address: {}
}),
//方法
mutations: {
//更改收货地址
updateAddress(state,address){
state.address = address
}
},
getters: {}
}
挂载到store.js中
import moduleUser from '@/store/user.js'Vue.use(Vuex)
const store = new Vuex.Store({
modules:{
//挂载购物车的vuex模块,模块内成员的访问路径被调整为m_cart,例如:
//购物车模块中cart 数组的访问路径是 m_cart/cart
'm_cart': moduleCart,
'm_user': moduleUser
}
})export default store
在my-address.vue调用
import { mapState, mapMutations, mapGetters } from 'vuex'
...mapMutations('m_user',['updateAddress']),
async chooseAddress() {
//1调用小程序提供的chooseAddress()方法,即可使用选中收货地址的功能
//返回值是一个数组:第1项为错误对象,第2项为成功之后的收货地址对象
const [err, succ] = await uni.chooseAddress().catch(err => err)
//2.用户成功的选择了收货地址
if (err === null && succ.errMsg === 'chooseAddress:ok') {
//为data里面 的收货地址对象赋值
// this.address = succ
this.updateAddress(succ)
}}
//计算属性
computed:{
...mapState('m_user',['address']),
addstr(){
if(!this.address.provinceName) {
return ''
}
return this.address.provinceName + this.address.cityName + this.address.countyName + this.address.detailInfo
}
}
4)将Store中的address持久化存储到本地
user.js中配置
export default{
//开启命名空间
namespaced: true,
//数据
state: () =>({
// address: {}
address: JSON.parse(uni.getStorageSync('address') || '{}')
}),
//方法
mutations: {
//更改收货地址
updateAddress(state,address){
state.address = address
this.commit('m_user/saveAddressToStorage')
},
//持久化存储 address
saveAddressToStorage(state){
uni.setStorageSync('address',JSON.stringify(state.address))
}
},
getters: {}
}
5)将addstr抽离为getters
user.js
getters: {
addstr(state){
if(!state.address.provinceName) {
return ''
}
return state.address.provinceName + state.address.cityName + state.address.countyName + state.address.detailInfo
}
}
my-address.vue
import { mapState, mapMutations, mapGetters } from 'vuex'
//计算属性
computed:{
...mapState('m_user',['address']),
...mapGetters('m_user',['addstr'])
}
6)重新选择收货地址
<!-- 渲染收货信息的盒子 -->
<view class="address-info-box" v-else @click="chooseAddress">
8.结算组件
my-settle.vue
1)渲染
<view class="my-settle-container">
<!-- 全选 -->
<label class="radio">
<radio color="#C00000" /><text></text>
</label>
<!-- 合计 -->
<view class="amount-box">
合计:<text class="amount">¥123.00</text>
</view>
<!-- 结算按钮 -->
<view class="btn-settle">结算(0)</view>
</view>
2)动态渲染已勾选商品的总数量
cart.js中配置
getters: {
total(state){
let c = 0
state.cart.forEach(x => c += x.goods_count)
return c
},
checkedCount(state){
return state.cart.filter(x => x.goods_state).reduce((total,item) => total += item.goods_count ,0)
}
}
my-settle.vue
<!-- 结算按钮 -->
<view class="btn-settle">结算({{checkedCount}})</view>import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters('m_cart',['checkedCount'])
}
}
3)动态渲染全选按钮的选中状态
my-settle.vue
computed: {
...mapGetters('m_cart',['checkedCount','total']),
isFullCheck(){
return this.total === this.checkedCount
}
4)使用数组的reduce方法改造total
cart.js
//购物车中所有商品的总数量
total(state){
// let c = 0
// state.cart.forEach(x => c += x.goods_count)
// return c
return state.cart.reduce((total,item) => total += item.goods_count,0)
},
5)实现全选和反选功能
cart.js
//更新购物车中所有商品的勾选状态
updateAllGoodsState(state,newState){
state.cart.forEach(x => x.goods_state = newState)
this.commit('m_cart/saveToStorage')
}
my-settle.vue
<label class="radio" @click="changeAllState">
import { mapGetters,mapMutations } from 'vuex'
methods:{
...mapMutations('m_cart',['updateAllGoodsState']),
changeAllState(){
this.updateAllGoodsState(!this.isFullCheck)
}
}
6)动态渲染价格
cart.js
//已勾选商品的总价格
checkedGoodsAmount(state){
return state.cart.filter(x => x.goods_state).reduce((total,item) => total += item.goods_count * item.goods_price,0).toFixed(2)
}
my-settle.vue
<!-- 合计 -->
<view class="amount-box">
合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
</view>
computed: {
...mapGetters('m_cart',['checkedCount','total','checkedGoodsAmount']),
isFullCheck(){
return this.total === this.checkedCount
}
},
7)让购物车图标正确显示物品数量
tabbar-badge.js
//计算属性
computed: {
...mapGetters('m_cart',['total'])
},
watch:{
total(){
this.setBadge()
}
},
9.渲染购物车为空时的页面
<view class="cart-container" v-if="cart.length !== 0">
<!-- 空白购物车的区域 -->
<view class="empty-cart" v-else>
<image src="/static/cart_empty@2x.png" class="empty-img"></image>
<text class="tip-text">空空如也~</text>
</view>