vue3微信公众号商城项目实战系列(8)商品展示页面

本篇实现在首页展示商品功能,表结构如下:

表名

字段

功能
goods

goods_id (int) 商品编号

goods_name (varchar) 商品名称

photo (varchar) 商品图片

price (decimal) 价格

商品表

页面呈现效果如下:

 

第1步:在 api.js 中加入获取首页商品信息和加购物车的接口方法,如下:

 1 export function goodsRecommend(data) {
 2   return request({
 3     url: '/goods/recommend', 
 4     method: 'post',
 5     data: data
 6   })
 7 }
 8 
 9 
10 export function cartAdd(data) {
11   return request({
12     url: '/cart/add', 
13     method: 'post',
14     data: data
15   })
16 }

第2步:在 Home.vue 中修改代码如下:

布局块代码

 1 <template>
 2     <div class="home"> 
 3 
 4         <div class="goods-list">
 5             <div class="goods-item" v-for="(item,index) in recommendtionList">
 6                 <div><img class="goods-img" :src="item.photo" /></div>
 7                 <div class="goods-name"><span>{{ item.goodsName }}</span></div>
 8                 <div class="goods-add">
 9                     <span class="goods-price">¥{{ item.price }} 元</span>
10                     <template v-if="item.qty==0"> 
11                         <img src="/public/cart.png" @click="onAddCart(item.goodsId,index)" /> 
12                     </template> 
13                     <template v-else>
14                         <img src="/public/cartadd.png" />
15                     </template>                   
16                 </div>  
17             </div> 
18         </div>
19  
20         <TabBar name="home" />
21     </div>    
22 </template>

第5行中 recommendtionList 是从后端接口获取的商品信息数组,json 格式 ,使用 v-for 遍历输出。

第10行中的 item.qty 是该商品已经加入购物车的数量,如果==0 就显示购物车图标,否则显示已加入的图标,如下:

 脚本块代码

 1 <script setup>
 2 import { onMounted, reactive, toRefs } from 'vue';
 3 import {goodsRecommend, cartAdd} from '@/http/api';
 4 import TabBar from '@/components/TabBar.vue';
 5 
 6 const data = reactive({recommendtionList:[] })
 7 let {recommendtionList } = toRefs(data);
 8 
 9 onMounted(() => {
10     let formData={}; 
11     goodsRecommend(formData).then(res=>{
12         data.recommendtionList=res;
13     });
14 })
15 
16 function onAddCart(goodsId, index){
17     let formData = {goodsId:goodsId,qty:1}; 
18     cartAdd(formData).then(res=>{
19         data.recommendtionList[index].qty=1; 
20     })
21 }
22 </script>

第3行导入 api.js 中的接口方法,分别用于显示商品、加购物车。

第6~7行初始化一个商品数组对象用于接收 goodsRecommend() 接口的返回值

,toRefs( ) 函数能将 响应式对象(这里是data)的每个属性还原为单独的响应式对象,也可以不这样处理,那布局代码块中的第5行要这样写:

<div class="goods-item" v-for="(item,index) in data.recommendtionList">

即由 "recommendtionList" 改成 "data.recommendtionList" 就可以了。

 

第9~14行是在钩子函数 onMounted()中获取商品信息,钩子函数在页面加载完后执行,这样就能第一时间显示商品信息。

第16~21行实现加购物车的功能,data.recommendtionList[index].qty=1; 的作用是当用户加完购物车后

,图标能马上更换(见下图) ,因为 recommendtionList 是响应式对象,任何时候其值发生改变都会同步影响所有引用到的地方。

 

样式代码块

 1 <style>
 2 .home{
 3     padding-bottom: 60px;
 4 }
 5 .goods-list{
 6     display: flex;
 7     flex-direction: row;
 8     flex-wrap: wrap;
 9     justify-content: space-evenly; 
10     background-color: #f0f0f0;
11 }
12 .goods-item{
13     width: 176px;
14     margin-bottom: 7px;
15     padding:4px 10px; 
16     background-color: #FFF;  
17     text-align: center; 
18 }
19 .goods-img{
20     width:150px;
21     height: 150px;
22     vertical-align: middle; 
23     border:solid 1px #E0E0E0;
24     border-radius: 6px;
25  }
26  .goods-name{ 
27     margin-top: 4px;
28     text-align: left;
29  }
30  .goods-add{
31     display: flex;
32     flex-direction: row;
33     justify-content: space-between;
34     align-items: center;
35  } 
36  .goods-price{
37     font-size: 16px;
38     font-weight: bold;
39     color:orangered;
40  } 
41 
42 </style>

这里没特别的地方,根据布局需要去定义就可以了。

 

最后,我们结合上一篇中的需求来解决加购物车后显示角标的问题。

这里需要用到 vue3 的另一个重要包 pinia , 它是官方推荐用来管理状态的(vue2中的状态管理包是vuex)。

官方文档地址:https://pinia.vuejs.org/zh/

1. 在本例子中,状态管理(本质是管理数据,用状态管理这个名称含有一种动态的意味)用来管理购物车中商品的数量

,当用户在任何页面修改了购物车商品数量的时候,状态管理都需要记录下来,这样不管用户怎么切换页面,购物车的角标都能正确显示。

2. 状态管理定义的数据所有页面都能引用,相当于一个全局的数据处理器。

3.更改后的数量需要持久化,确保用户刷新的时候不会归 0。

使用步骤如下:

第1步:安装 pinia,命令: npm install pinia

第2步:在 src/stores/目录下新增 cart.js 文件,stores 目录集中存放所有状态管理的相关文件,如下图。

第3步:修改 main.js, 将 pinia 的实例挂载到 app实例上(见红色字体),如下:

 1 import { createApp } from 'vue'
 2 import App from './App.vue'
 3 
 4 import { createPinia } from 'pinia'
 5 const pinia = createPinia();
 6 
 7 import './assets/main.css'
 8 
 9 import rtx from './router/routerx.js'
10 
11 
12 createApp(App).use(pinia).use(rtx).mount('#app')

说明:4~5行创建一个pinia实例对象,第12行用use()方法挂载到app实例对象上。

第4步:定义store,cart.js中代码如下:

 1 export const useCartStore = defineStore('cart', () => {
 2     const count = ref(0);
 3     function increment() {
 4       count.value++;
 5     }
 6     function reduction(){
 7         if(count.value>1){
 8             count.value--;
 9         } 
10     }
11   
12     return { count, increment, reduction }
13   })

说明:

第1行:定义store 用 defineStore()函数(这里是组合式API的写法,和选项式API区别见如下官方文档截图),对象名称 useCartStore 可以是任意的

,但建议使用 useXxxStore 这样的命名方式,Xxx 作为 defineStore()函数的第一个参数(必须唯一)。

 

第2行:count 是保存购物车中商品数量的变量,是值类型,所以变成响应式数据需要用 ref() 函数处理(引用类型用 reactive()函数)

,使用的时候不能直接用,必须是 xxx.value 的形式 这是值类型和引用类型的差别。

第3~10行:定义2个方法处理 count , 建议用方法去操作 count ,不要直接赋值。

第12行:用 return 语句把这3个对象(1个变量2个方法)返回,外部文件引用 cart.js 后就可以使用这3个对象了。

 

第5步,使用store ,修改 Home.vue 中 <script> 脚本代码块的内容如下(见红色字体部分):

 1 <script setup>
 2 import { onMounted, reactive, toRefs } from 'vue';
 3 import {goodsRecommend, cartAdd} from '@/http/api';
 4 import TabBar from '@/components/TabBar.vue';
 5 import { useCartStore } from '@/stores/cart.js'
 6 const cartStore = useCartStore();
 7 
 8 const data = reactive({recommendtionList:[] })
 9 let {recommendtionList } = toRefs(data);
10 
11 onMounted(() => {
12     let formData={}; 
13     goodsRecommend(formData).then(res=>{
14         data.recommendtionList=res;
15     });
16 })
17 
18 function onAddCart(goodsId, index){
19     let formData = {goodsId:goodsId,qty:1}; 
20     cartAdd(formData).then(res=>{
21         data.recommendtionList[index].qty=1; 
22         cartStore.increment();
23     })
24 }
25 </script>

第5行引用store对象,第6行实例化,第22行调用 increment() 函数给将store对象的count属性加1。

最后我们到 TabBar.vue 组将中使用 store对象的count属性就可以了,TabBar.vue 代码修改如下(见红色字体部分):

 1 <template>
 2     <div class="tab-bar">
 3         <template v-for="item in arrTab">
 4             <div :class="item.tabClass" @click="onClick(item.tabName)"> 
 5                 <img class="tab-bar-item-img" :src="item.tabIcon" />
 6                 <template v-if="item.tabName=='cart' && cartStore.count">
 7                     <span>{{ item.tabText }} {{ cartStore.count }}</span>
 8                 </template>
 9                 <template v-else>
10                     <span>{{ item.tabText }}</span>
11                 </template>
12             </div> 
13         </template> 
14     </div>    
15 </template>

第6~11行是修改的部分, 用v-if 校验如果tab项=购物车而且商品数量>0时就显示角标。

<script>脚本代码块如下:

1 <script setup> 
2 import { reactive, onMounted } from 'vue';
3 import {useRouter} from 'vue-router';
4 import { useCartStore } from '@/stores/cart.js'
5 const cartStore = useCartStore();
6 
7 //一下相同部分省略
8  
9 </script>

刷新页面后点加购物车,效果如下:

角标效果(应该显示在图标右上角)用 css 的 relative 定位实现,这里就不展开了。

 

最后,用 pinia 的 pinia-plugin-persistedstate 插件持久化 store 解决 刷新页面后 count 消失的问题:

第1步: 安装插件,命令:npm install  pinia-plugin-persistedstate

第2步:将插件挂载到 pinia 实例上,修改 main.js 如下(见红色字体):

 1 import { createApp } from 'vue'
 2 import App from './App.vue'
 3 
 4 import { createPinia } from 'pinia';
 5 const pinia = createPinia();
 6 
 7 import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
 8 pinia.use(piniaPluginPersistedstate)
 9 
10 import './assets/main.css'
11 
12 import rtx from './router/routerx.js'
13 
14 
15 createApp(App).use(pinia).use(rtx).mount('#app')

第3步:在 cart.js 中 做持久化配置,代码如下(见红色代码):

 1 import { ref } from 'vue';
 2 import {defineStore} from 'pinia';
 3 
 4 export const useCartStore = defineStore('cart', () => {
 5     const count = ref(0);
 6     function increment() {
 7       count.value++;
 8     }
 9     function reduction(){
10         if(count.value>1){
11             count.value--;
12         } 
13     }
14   
15     return { count, increment, reduction }
16   },
17   { persist:true }
18 )

刷新页面,购物车的角标值已经不会清 0 了,如下图:

 

posted @ 2023-04-19 12:48  屏风马  阅读(858)  评论(0编辑  收藏  举报