1.收货地址组件my-address.vue
<template>
<view>
<view class="address-choose-box" v-if="JSON.stringify(address)==='{}'">
<button type="primary" size="mini" @click="chooseAddress">请选择收货地址+</button>
</view>
<!-- 收获地址信息盒子 -->
<view class="address-info-box" v-else @click="chooseAddress">
<view class="row1">
<view class="row1-left">
<view class="username">
收货人:{{address.userName}}
</view>
</view>
<view class="row1-right">
<view class="phone">
电话:{{address.telNumber}}
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
</view>
<view class="row2">
<view class="row2-left">收获地址:</view>
<view class="row2-right">
{{addstr}}
</view>
</view>
</view>
<!-- 底部边框线 -->
<image src="../../static/cart/1.png" class="address-border"></image>
</view>
</template>
<script>
import {
mapState,
mapMutations,
mapGetters
} from 'vuex';
export default {
name: "my-address",
data() {
return {
// address: {}
};
},
methods: {
...mapMutations('m_user', ['updateAddress']),
chooseAddress() {
let that=this;
uni.chooseAddress({
success(res) {
// this.address = res;
that.updateAddress(res);
}
})
}
},
computed: {
...mapState('m_user', ['address']),
...mapGetters('m_user',['addstr'])
}
}
</script>
<style lang="scss">
.address-border {
display: block;
width: 100%;
height: 5px;
}
.address-choose-box {
display: flex;
height: 90px;
justify-content: center;
align-items: center;
}
.address-info-box {
font-size: 13px;
height: 90px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 5px;
.row1 {
display: flex;
justify-content: space-between;
.row1-right {
display: flex;
justify-content: space-between;
}
}
.row2 {
display: flex;
align-items: center;
.row2-left {
white-space: nowrap;
}
}
}
</style>
2.全局数据共享store-user.js
export default {
// 开启命名空间
namespaced: true,
// 数据
state: () => ({
address: JSON.parse(uni.getStorageSync('address') || '{}'),
token:''
}),
// 方法
mutations: {
//更新收货地址
updateAddress(state, address) {
state.address = address
this.commit('m_user/saveAddressToStorage')
},
saveAddressToStorage(state) {
uni.setStorageSync('address', JSON.stringify(state.address))
}
},
getters: {
addstr(state) {
if (!state.address.provinceName) return '';
return state.address.provinceName + state.address.cityName + state.address.countyName + state.address
.detailInfo
}
}
}
3.mixins-TabBarBadge.js更新购物车角标
import {mapGetters} from 'vuex'
export default {
computed:{
...mapGetters('m_cart',['total'])
},
watch:{
//监听购物车总数更新角标
total(){
this.setBadge()
}
},
//购物车角标
onShow() {
this.setBadge()
},
methods:{
//购物车数量
setBadge(){
uni.setTabBarBadge({
index:2,
text:this.total+''
})
}
}
}
4.结算组件my-settle.vue
<template>
<view class="my-settle-container">
<!-- 全选 -->
<label class="radio" @click="changeAllState">
<radio :checked="isFullCheck" /><text>全选</text>
</label>
<!-- 合计 -->
<view class="amount-box">
合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
</view>
<!-- 结算 -->
<view class="btn-settle" @click="settlement">结算({{checkedCount}})</view>
</view>
</template>
<script>
import {
mapGetters,
mapMutations,
mapState
} from 'vuex';
export default {
name: "my-settle",
data() {
return {
};
},
computed: {
...mapGetters('m_cart', ['checkedCount', 'total','checkedGoodsAmount']),
...mapGetters('m_user', ['addstr']),
...mapState('m_user',['token']),
isFullCheck() {
return this.total === this.checkedCount
}
},
methods:{
//使用 mapMutations 辅助函数,把 m_cart 模块提供的 updateAllGoodsState 方法映射到当前组件中使用
...mapMutations('m_cart',['updateAllGoodsState']),
changeAllState(){
// 修改购物车中所有商品的选中状态
// !this.isFullcheck 表示:当前全选按钮的状态取反之后,就是最新的勾选状态
this.updateAllGoodsState(!this.isFullCheck)
},
//点击结算按钮
settlement(){
if(!this.checkedCount) return uni.$showMsg('请选择要结算的商品');
if(!this.addstr) return uni.$showMsg('请选择收货地址');
if(!this.token) return uni.$showMsg('请先登录')
}
}
}
</script>
<style lang="scss">
.my-settle-container {
position: fixed;
// bottom: 0;
left: 0;
width: 100%;
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding-left: 5px;
}
.radio {
display: flex;
align-items: center;
}
.amount-box {
.amount {
color: #c00000;
font-weight: bold;
}
}
.btn-settle {
background-color: #c00000;
height: 50px;
color: white;
line-height: 50px;
padding: 0 5px;
width: 100px;
text-align: center;
}
</style>
5.store-cart.js
export default {
//为当前模块开启命名空间
namespaced: true,
state: () => ({
// 购物车存储的数据,每个商品的数据对象
cart: JSON.parse(uni.getStorageSync('cart') || '[]')
}),
//模块mutations方法
mutations: {
addToCart(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id);
if (!findRes) {
//如果购物车没有这个商品,直接push
state.cart.push(goods);
} else {
//否则更新数量
findRes.count++;
}
this.commit('m_cart/saveToStorage')
},
//游客保存购物车到本地
saveToStorage(state) {
uni.setStorageSync('cart', JSON.stringify(state.cart))
},
//更新购物车中商品的选中状态
updateGoodsState(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id)
if (findRes) {
findRes.state = goods.state;
this.commit('m_cart/saveToStorage');
}
},
//更新商品的数量
updateGoodsCount(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id)
if (findRes) {
findRes.count = goods.count;
this.commit('m_cart/saveToStorage');
}
},
//根据id删除商品
removeGoodsById(state, id) {
state.cart = state.cart.filter(x => x.id !== id);
this.commit('m_cart/saveToStorage');
},
//更新购物车中所有商品的勾选状态
updateAllGoodsState(state, newState) {
state.cart.forEach(x => x.state = newState)
this.commit('m_cart/saveToStorage')
}
},
//模块getters属性
getters: {
//购物车中所有商品的数量
total(state) {
// let c = 0;
// state.cart.forEach(goods => c += goods.count)
// return c
return state.cart.reduce((total, item) => total += item.count, 0)
},
//购物车中已勾选的商品数量
checkedCount(state) {
// 先使用 filter 方法,从购物车中过滤器已勾选的商品
// 再使用 reduce 方法,将已勾选的商品总数量进行累加
// reduce() 的返回值就是已勾选的商品的总数量
return state.cart.filter(x => x.state).reduce((total, item) =>
total += item.count, 0)
},
// 已勾选商品的总价格
checkedGoodsAmount(state) {
// 先使用 filter 方法,从购物车中过滤器已勾选的商品
// 再使用 reduce 方法,将已勾选的商品数量 * 单价之后,进行累加
// reduce() 的返回值就是已勾选的商品的总价
// 最后调用 toFixed(2) 方法,保留两位小数
return state.cart.filter(x => x.state).reduce((total, item) => total += item.count * item.price, 0).toFixed(
2)
}
}
}
6.购物车页面cart.vue
<template>
<view class="cart-container" v-if="cart.length!==0">
<!-- 收获地址组件 -->
<my-address></my-address>
<!-- 商品的列表标题 -->
<view class="cart-title">
<!-- 左侧图表 -->
<uni-icons type="shop"></uni-icons>
<!-- 右侧文本 -->
<text class="cart-title-text">购物车</text>
</view>
<!-- 循环渲染购物车商品 -->
<!-- 滑动删除组件容器 -->
<uni-swipe-action>
<block v-for="(goods,i) in cart" :key="i">
<uni-swipe-action-item :right-options="options" @click="swipeItemClickHandler(goods)">
<my-goods :goods="goods" :showRadio="true" :showNum="true" @radio-change="radioChangeHandler"
@num-change="numChangeHandler"></my-goods>
</uni-swipe-action-item>
</block>
</uni-swipe-action>
<!-- 使用自定义结算组件 -->
<my-settle></my-settle>
</view>
<!-- 空白购物车的区域 -->
<view class="empty-cart" v-else>
<image src="/static/cart/2.png" class="empty-img"></image>
<text class="tip-text">空空如也~</text>
</view>
</template>
<script>
import badgeMix from '@/mixins/tabbar-badge.js'
import {
mapState,
mapMutations
} from 'vuex'
export default {
mixins: [badgeMix],
computed: {
...mapState('m_cart', ['cart'])
},
data() {
return {
options: [{
text: '删除',
style: {
backgroundColor: '#c00000'
}
}]
};
},
methods: {
...mapMutations('m_cart', ['updateGoodsState', 'updateGoodsCount', 'removeGoodsById']),
radioChangeHandler(e) {
this.updateGoodsState(e)
},
//商品数量的变化
numChangeHandler(e) {
this.updateGoodsCount(e)
},
swipeItemClickHandler(e) {
this.removeGoodsById(e.id)
}
}
}
</script>
<style lang="scss">
.cart-container {
padding-bottom: 50px;
}
.cart-title {
height: 40px;
display: flex;
align-items: center;
font-size: 14px;
padding-left: 5px;
border-bottom: 1px solid #efefef;
.cart-title-text {
margin-left: 10px;
}
}
.empty-cart {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 150px;
.empty-img {
width: 100px;
height: 100px;
}
.tip-text {
font-size: 12px;
color: gray;
margin-top: 15px;
}
}
</style>
7.效果示例
—1—
—2—