赞
踩
学习地址
零基础玩转微信小程序:黑马程序员前端微信小程序开发教程,微信小程序从基础到发布全流程_企业级商城实战(含uni-app项目多端部署)
uniapp - 黑马优购 笔记地址
知识点
1 使用vuex
1 创建 store 文件夹
2 创建 store.js
// 1. 导入 Vue 和 Vuex
// import Vue from 'vue' vue3 不需要
import Vuex from 'vuex'
// 2. 将 Vuex 安装为 Vue 的插件 vue3 不需要
// Vue.use(Vuex)
// 3. 创建 Store 的实例对象
const store = new Vuex.Store({
// TODO:挂载 store 模块
modules: {},
})
// 4. 向外共享 Store 的实例对象
export default store
在 main.js 中导入 store 实例对象并挂载到 Vue 的实例上:
// 1. 导入 store 的实例对象 import store from './store/store.js' // 省略其它代码... // #ifdef VUE3 import { createSSRApp } from 'vue' export function createApp() { const app = createSSRApp(App) // 2. 将 store 挂载到 Vue 实例上 app.use(store) return { app } } // #endif
创建购物车的 store 模块
export default { // 为当前模块开启命名空间 namespaced: true, // 模块的 state 数据 state: () => ({ // 购物车的数组,用来存储购物车中每个商品的信息对象 // 每个商品的信息对象,都包含如下 6 个属性: // { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state } cart: [], }), // 模块的 mutations 方法 mutations: {}, // 模块的 getters 属性 getters: {}, }
在 store/store.js 模块中,导入并挂载购物车的 vuex 模块,示例代码如下:
import Vue from 'vue' import Vuex from 'vuex' // 1. 导入购物车的 vuex 模块 import moduleCart from './cart.js' Vue.use(Vuex) const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: { // 2. 挂载购物车的 vuex 模块,模块内成员的访问路径被调整为 m_cart,例如: // 购物车模块中 cart 数组的访问路径是 m_cart/cart m_cart: moduleCart, }, }) export default store
在 store/store.js 模块中,导入并挂载购物车的 vuex 模块,示例代码如下:
// 1. 导入 Vue 和 Vuex // import Vue from 'vue' vue3 不需要 import Vuex from 'vuex' // 1 导入购物车的vuex模块 import moduleCart from './cart.js' // 2. 将 Vuex 安装为 Vue 的插件 vue3 不需要 // Vue.use(Vuex) // 3. 创建 Store 的实例对象 const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: { // 2. 挂载购物车的 vuex 模块,模块内成员的访问路径被调整为 m_cart,例如: // 购物车模块中 cart 数组的访问路径是 m_cart/cart m_cart: moduleCart, }, }) // 4. 向外共享 Store 的实例对象 export default store
在 goods_detail.vue 页面中,修改 标签中的代码如下:
// 从 vuex 中按需导出 mapState 辅助方法
import { mapState } from 'vuex'
export default {
computed: {
// 调用 mapState 方法,把 m_cart 模块中的 cart 数组映射到当前页面中,作为计算属性来使用
// ...mapState('模块的名称', ['要映射的数据名称1', '要映射的数据名称2'])
...mapState('m_cart', ['cart']),
},
// 省略其它代码...
}
注意:今后无论映射 mutations 方法,还是 getters 属性,还是 state 中的数据,都需要指定模块的名称,才能进行映射。
使用测试
<!-- 运费 -->
<view class="yf">快递:免运费 -- {{cart.length}}</view>
二 加入购物车
1 设置购物车添加方法
mutations: {
addToCart(state , goods){
const findResult = state.cart.find((x)=> x.goods_id === goods.goods_id)
if(!findResult){
state.cart.push(goods)
}else{
findResult.goods_count += 1
}
}
},
2 在需要的页面 导入
import { mapMutations} from 'vuex'
methods: {
...mapMutations('m_cart',['addToCart']),
}
3 加入购物车
buttonClick(e){ console.log(e) if (e.content.text === '加入购物车') { // 切换到购物车页面 const goods = { goods_id : this.goods_info.goods_id, goods_name : this.goods_info.goods_name, goods_price : this.goods_info.goods_price, goods_count:1, goods_small_logo : this.goods_info.goods_small_logo, goods_state : true } this.addToCart(goods) } }
4 计算 购物车添加的数量
total(state){
let c = 0
state.cart.forEach(goods => c+= goods.goods_count)
return c
}
5 展示 购物车的数量
import { mapGetters} from 'vuex'
computed:{
// 调用 mapState 方法,把 m_cart 模块中的 cart 数组映射到当前页面中,作为计算属性来使用
// ...mapState('模块的名称', ['要映射的数据名称1', '要映射的数据名称2'])
...mapGetters('m_cart',['total']),
},
watch:{ // 不会在首次初始完调用 // total(newVal){ // const findResult = this.options.find((item)=> item.text === '购物车') // if(findResult){ // findResult.info= newVal // } // } total:{ handler(newVal){ const findResult = this.options.find((item)=> item.text === '购物车') if(findResult){ findResult.info= newVal } }, // immediate 属性用来声明此侦听器,是否在页面初次加载完毕后立即调用 immediate : true } },
6 持久化存储购物车中的商品
saveToStorage(state){
uni.setStorageSync('cart',JSON.stringify(state.cart))
}
addToCart(state , goods){
const findResult = state.cart.find((x)=> x.goods_id === goods.goods_id)
if(!findResult){
state.cart.push(goods)
}else{
findResult.goods_count += 1
}
this.commit('m_cart/saveToStorage')
},
7 修改购物车 初始化的数据
// 模块的 state 数据
state: () => ({
// 购物车的数组,用来存储购物车中每个商品的信息对象
// 每个商品的信息对象,都包含如下 6 个属性:
// { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state }
cart: JSON.parse(uni.getStorageSync('cart') || '[]'),
}),
8 动态为 tabBar 页面设置数字徽标
import { mapGetters } from 'vuex'
computed:{
...mapGetters('m_cart',['total']),
},
onShow(){
this.setBadge()
},
methods:{
setBadge(){
uni.setTabBarBadge({
index:2,
text:this.total +''
})
}
}
9 将设置 tabBar 徽标的代码抽离为 mixins
import { mapGetters } from 'vuex' export default{ computed:{ ...mapGetters('m_cart',['total']), }, onShow(){ this.setBadge() }, methods:{ setBadge(){ uni.setTabBarBadge({ index:2, text:this.total +'' }) } } }
import badgeMix from '@/mixins/tabbar-badge.js'
export default {
mixins : [badgeMix],
....
}
源码
store.js
// 1. 导入 Vue 和 Vuex // import Vue from 'vue' vue3 不需要 import Vuex from 'vuex' // 1 导入购物车的vuex模块 import moduleCart from './cart.js' // 2. 将 Vuex 安装为 Vue 的插件 vue3 不需要 // Vue.use(Vuex) // 3. 创建 Store 的实例对象 const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: { // 2. 挂载购物车的 vuex 模块,模块内成员的访问路径被调整为 m_cart,例如: // 购物车模块中 cart 数组的访问路径是 m_cart/cart m_cart: moduleCart, }, }) // 4. 向外共享 Store 的实例对象 // export default store
cart.js
export default { // 为当前模块开启命名空间 namespaced: true, // 模块的 state 数据 state: () => ({ // 购物车的数组,用来存储购物车中每个商品的信息对象 // 每个商品的信息对象,都包含如下 6 个属性: // { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state } cart: JSON.parse(uni.getStorageSync('cart') || '[]'), }), // 模块的 mutations 方法 mutations: { addToCart(state , goods){ const findResult = state.cart.find((x)=> x.goods_id === goods.goods_id) if(!findResult){ state.cart.push(goods) }else{ findResult.goods_count += 1 } this.commit('m_cart/saveToStorage') }, saveToStorage(state){ uni.setStorageSync('cart',JSON.stringify(state.cart)) } }, // 模块的 getters 属性 getters: { total(state){ let c = 0 state.cart.forEach(goods => c+= goods.goods_count) return c } }, }
main.js
import App from './App' // 1. 导入 store 的实例对象 import store from './store/store.js' import { $http } from '@escook/request-miniprogram' uni.$http = $http $http.baseUrl = 'https://www.uinav.com' $http.beforeRequest = function(options){ uni.showLoading({ title:'数据加载中....' }) } $http.afterRequest = function(){ uni.hideLoading() } uni.$showMsg = function(title = '数据加载失败!' , duration = 1500){ uni.showToast({ title, duration, icon:"none", }) } // #ifndef VUE3 import Vue from 'vue' Vue.config.productionTip = false App.mpType = 'app' const app = new Vue({ ...App }) app.$mount() // #endif // #ifdef VUE3 import { createSSRApp } from 'vue' export function createApp() { const app = createSSRApp(App) // 2. 将 store 挂载到 Vue 实例上// app.use(store) return { app } } // #endif
goods_details.vue
<template> <view> <view class="layout" v-if="goods_info.goods_name"> <view class="topLayout"> <view class="topBannerLayout"> <swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" circular="true"> <swiper-item v-for="(item , index) in goods_info.pics" :key="index"> <image :src="item.pics_big" @click="preview(index)"></image> </swiper-item> </swiper> </view> <view class="centerInfoLayout"> <!-- 商品信息区域 --> <view class="goods-info-box"> <!-- 商品价格 --> <view class="price">¥{{goods_info.goods_price}}</view> <!-- 信息主体区域 --> <view class="goods-info-body"> <!-- 商品名称 --> <view class="goods-name">{{goods_info.goods_name}}</view> <!-- 收藏 --> <view class="favi"> <uni-icons type="star" size="18" color="gray"></uni-icons> <text>收藏</text> </view> </view> <!-- 运费 --> <view class="yf">快递:免运费 </view> </view> </view> <view class="bottomTuwenLayout"> <rich-text :nodes="goods_info.goods_introduce"></rich-text> </view> </view> <view class="goods_nav"> <uni-goods-nav :options="options" :fill="true" :button-group="buttonGroup" @click="onClick" @buttonClick="buttonClick" /> </view> </view> </view> </template> <script> // 从 vuex 中按需导出 mapState 辅助方法 import { mapState ,mapMutations ,mapGetters} from 'vuex' export default { computed:{ // 调用 mapState 方法,把 m_cart 模块中的 cart 数组映射到当前页面中,作为计算属性来使用 // ...mapState('模块的名称', ['要映射的数据名称1', '要映射的数据名称2']) ...mapState('m_cart',['cart']), ...mapGetters('m_cart',['total']), }, watch:{ // 不会在首次初始完调用 // total(newVal){ // const findResult = this.options.find((item)=> item.text === '购物车') // if(findResult){ // findResult.info= newVal // } // } total:{ handler(newVal){ const findResult = this.options.find((item)=> item.text === '购物车') if(findResult){ findResult.info= newVal } }, // immediate 属性用来声明此侦听器,是否在页面初次加载完毕后立即调用 immediate : true } }, data() { return { goods_info: {}, options: [{ icon: 'chat', text: '客服' }, { icon: 'cart', text: '购物车', info: 0 }], buttonGroup: [{ text: '加入购物车', backgroundColor: 'linear-gradient(90deg, #FFCD1E, #FF8A18)', color: '#fff' }, { text: '立即购买', backgroundColor: 'linear-gradient(90deg, #FE6035, #EF1224)', color: '#fff' } ], }; }, onLoad(options) { const goods_id = options.goods_id this.getGoodsDetail(goods_id) }, methods: { ...mapMutations('m_cart',['addToCart']), async getGoodsDetail(goods_id) { const { data: res } = await uni.$http.get('/api/public/v1/goods/detail', { goods_id }) if (res.meta.status !== 200) return uni.$showMsg() // 为 data 中的数据赋值 res.message.goods_introduce = res.message.goods_introduce.replace(/<img /g, '<img style="display:block;" ') .replace(/webp/g, 'jpg') this.goods_info = res.message console.log(this.goods_info) }, preview(index){ uni.previewImage({ // 预览时,默认显示图片的索引 current: index, // 所有图片 url 地址的数组 urls: this.goods_info.pics.map(x => x.pics_big) }) }, onClick(e){ console.log(e) if (e.content.text === '购物车') { // 切换到购物车页面 uni.switchTab({ url: '/pages/cart/cart' }) } }, buttonClick(e){ console.log(e) if (e.content.text === '加入购物车') { // 切换到购物车页面 const goods = { goods_id : this.goods_info.goods_id, goods_name : this.goods_info.goods_name, goods_price : this.goods_info.goods_price, goods_count:1, goods_small_logo : this.goods_info.goods_small_logo, goods_state : true } this.addToCart(goods) } } } } </script> <style lang="scss"> .topBannerLayout { width: 750rpx; height: 750rpx; swiper { width: 100%; height: 100%; image { width: 100%; height: 100%; } } } // 商品信息区域的样式 .goods-info-box { padding: 10px; padding-right: 0; .price { color: #c00000; font-size: 18px; margin: 10px 0; } .goods-info-body { display: flex; justify-content: space-between; .goods-name { font-size: 13px; padding-right: 10px; } // 收藏区域 .favi { width: 120px; font-size: 12px; display: flex; flex-direction: column; justify-content: center; align-items: center; border-left: 1px solid #efefef; color: gray; } } // 运费 .yf { margin: 10px 0; font-size: 12px; color: gray; } } .topLayout{ padding-bottom: 50px; } .layout{ } .goods_nav { position: fixed; bottom: 0; left: 0; width: 100%; } </style>
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。