vue尚品汇商城项目-day04【24.点击搜索按钮跳转后的页面商品列表、平台售卖属性动态展示(开发Search组件)】

举报
刘大猫 发表于 2024/10/08 21:00:50 2024/10/08
【摘要】 vue尚品汇商城项目-day04【24.点击搜索按钮跳转后的页面商品列表、平台售卖属性动态展示(开发Search组件)】

在这里插入图片描述
@[toc]

24.点击搜索按钮跳转后的页面商品列表、平台售卖属性动态展示(开发Search组件)

重难点说明

  1. 搜索查询条件参数理解与准备

  2. 组件动态数据显示

  3. 根据分类和关键字进行搜索

  4. 根据品牌进行搜索

  5. 根据属性进行搜索

  6. 排序搜索

  7. 自定义分页组件

惯例步骤:

  1. 准备静态组件
  2. src/api/下封装接口
  3. vuex的触发调用3连:actions、mutations、state
  4. 动态拼接展示数据

修改代码:

src/pages/Search/index.vue

<div class="goods-list">
            <ul class="yui3-g">
              <li class="yui3-u-1-5" v-for="good in goodsList" :key="good.id">
                <div class="list-wrap">
                  <div class="p-img">
                    <a href="item.html" target="_blank"><img :src="good.defaultImg" /></a>
                  </div>
                  <div class="price">
                    <strong>
                      <em>¥</em>
                      <i>{{good.price}}.00</i>
                    </strong>
                  </div>
                  <div class="attr">
                    <a target="_blank" href="item.html" title="促销信息,下单即赠送三个月CIBN视频会员卡!【小米电视新品4A 58 火爆预约中】">{{good.title}}</a>
                  </div>
                  <div class="commit">
                    <i class="command">已有<span>2000</span>人评价</i>
                  </div>
                  <div class="operate">
                    <a href="success-cart.html" target="_blank" class="sui-btn btn-bordered btn-danger">加入购物车</a>
                    <a href="javascript:void(0);" class="sui-btn btn-bordered">收藏</a>
                  </div>
                </div>
              </li>
            </ul>
</div>

import SearchSelector from './SearchSelector/SearchSelector'
import {mapGetters} from "vuex"

export default {
    name: 'Search',
    components: {
      SearchSelector
    },
    data() {
      return {
        searchParams: {
          //产品相应的id
          category1Id: "",
          category2Id: "",
          category3Id: "",
          //产品的名字
          categoryName: "",
          //搜索的关键字
          keyword: "",
          //排序:初始状态应该是综合且降序
          order: "1:desc",
          //第几页
          pageNo: 1,
          //每一页展示条数
          pageSize: 10,
          //平台属性的操作
          props: [],
          //品牌
          trademark: "",
        },
      };
    },
    //在挂载之前调用一次|可以在发请求之前将带有参数进行修改
    beforeMount() {
      //在发请求之前,把接口需要传递参数,进行整理(在给服务器发请求之前,把参数整理好,服务器就会返回查询的数据)
      Object.assign(this.searchParams, this.$route.query, this.$route.params);
    },
    mounted() {
      //在发请求之前咱们需要将searchParams里面参数进行修改带给服务器
      this.getData();
    },
    methods: {
      //把发请求的这个action封装到一个函数里面
      //将来需要再次发请求,你只需要在调用这个函数即可
      getData() {
        this.$store.dispatch("getSearchList", this.searchParams)
      }
    },
    computed:{
      //mapGetters里面的写法:传递的数组,因为getters计算是没有划分模块【home,search】
      ...mapGetters(["goodsList"]),
    },
    watch: {
      //监听路由的信息是否发生变化,如果发生变化,再次发起请求
      $route(newValue, oldValue) {
        //每一次请求完毕,应该把相应的1、2、3级分类的id置空的,让他接受下一次的相应1、2、3
        //再次发请求之前整理带给服务器参数
        Object.assign(this.searchParams, this.$route.query, this.$route.params);
        //再次发起ajax请求
        this.getData();
        //分类名字与关键字不用清理:因为每一次路由发生变化的时候,都会给他赋予新的数据
        this.searchParams.category1Id = '';
        this.searchParams.category2Id = '';
        this.searchParams.category3Id = '';
      }
    }

src/pages/Search/SearchSelector/SearchSelector.vue

<template>
  <div class="clearfix selector">
    <div class="type-wrap logo">
      <div class="fl key brand">品牌</div>
      <div class="value logos">
        <ul class="logo-list">
          <li v-for="brands in trademarkList" :key="brands.tmId">{{brands.tmName}}</li>
        </ul>
      </div>
      <div class="ext">
        <a href="javascript:void(0);" class="sui-btn">多选</a>
        <a href="javascript:void(0);">更多</a>
      </div>
    </div>
    <div class="type-wrap" v-for="attr in attrsList" :key="attr.attrId">
      <div class="fl key">{{attr.attrName}}</div>
      <div class="fl value">
        <ul class="type-list">
          <li v-for="(attrValue, index) in attr.attrValueList" :key="index">
            <a>{{attrValue}}</a>
          </li>
        </ul>
      </div>
      <div class="fl ext"></div>
    </div>
  </div>
</template>

<script>
  import {mapGetters} from "vuex";

  export default {
    name: 'SearchSelector',
    computed:{
      ...mapGetters(["attrsList", "trademarkList"])
    },
  }
</script>

src/store/search/index.js

import {getSearchList} from "@/api";
//Search模块的小仓库
//actions代表一系列动作,可以书写自己的业务逻辑,也可以处理异步
const actions = {
    //获取search模块数据
    async getSearchList(context, params={}) {
        //当前这个getSearchList这个函数在调用获取服务器数据的时候,至少传递一个参数(空对象)
        //params形参:是当用户派发action的时候,第二个参数传递过来的,至少是一个空对象
        let response = await getSearchList(params);
        if (response.code == 200) {
            context.commit("GET_SEARCH_LIST", response.data)
        }
    }
}
//mutations代表维护,操作维护的是state中的数据,且state中数据只能在mutations中处理
const mutations = {
    GET_SEARCH_LIST(state, searchList) {
        state.searchList = searchList
    },
}
//state代表仓库中的数据
const state = {
    //仓库初始状态
    searchList:{}
}
//计算属性
//项目当中getters主要的作用是:简化仓库中的数据(简化数据而生)
//可以把我们将来在组件当中需要用的数据简化一下【将来组件在获取数据的时候就方便了】
const getters = {
    //注意:这个attrsList方法名必须和属性名goodsList一致才行,之前随便命名的getAttrsList()发现无法使用
    attrsList(state) {
        //state.searchList.goodsList如果服务器数据回来了,没问题是一个数组
        //假如网络不给力|没有网state.searchList.goodsList应该返回的是undefined
        //计算新的属性的属性值至少给人家来一个数组
        return state.searchList.attrsList || [];
    },
    goodsList(state) {
        return state.searchList.goodsList || [];
    },
    trademarkList(state) {
        return state.searchList.trademarkList || [];
    },
}

//创建并暴露store
export default {
    actions,
    mutations,
    state,
    getters
}

src/api/index.js

//获取搜索模块数据 地址:/api/list  请求方式:post  参数:需要带参数
//当前这个函数需不需要接受外部传递参数
//当前这个接口(获取搜索模块的数据),给服务器传递一个默认参数【至少是一个空对象】
export const getSearchList = (params)=>requests({url:"/list", method:"post", data:params});

注意点1:之前src/api/index.js封装的方法都没有传参,而这次需要传参,传参的话就不能用之前的requests.get(‘xx’)了,而应该使用requests({url:"/list", method:“post”, data:params})类似这种形式。

注意点2:预采用requests({})这种形式需注意,如果是post请求key叫params,即requests({url:"/list", method:“post”, data:params}),如果是post传参key叫data,即requests({url:"/list", method:“post”, data})

注意点3:action内方法也是可以有参数的,比如:

无参数时:
const actions = {
    //获取search模块数据
    async getSearchList(context) {
        //当前这个getSearchList这个函数在调用获取服务器数据的时候,至少传递一个参数(空对象)
        //params形参:是当用户派发action的时候,第二个参数传递过来的,至少是一个空对象
        let response = await getSearchList();
        if (response.code == 200) {
            context.commit("GET_SEARCH_LIST", response.data)
        }
    }
}
------------------------------------------------------------------
有参数时:
const actions = {
    //获取search模块数据
    async getSearchList(context, params={}) {
        //当前这个getSearchList这个函数在调用获取服务器数据的时候,至少传递一个参数(空对象)
        //params形参:是当用户派发action的时候,第二个参数传递过来的,至少是一个空对象
        let response = await getSearchList(params);
        if (response.code == 200) {
            context.commit("GET_SEARCH_LIST", response.data)
        }
    }
}

注意点4:state中的searchList初始值不能随意写成{}或者[],而是应该看接口的返回值是对象还是数组,从而进行初始化设定,否则就会报错或者无效。

注意点5:假设现在searchList数据有了且里面有3个不同的key属性数组,现在需要生成计算属性,原来的做法是如下,然后在模板标签中使用searchList.attrsList、searchList.goodsList、searchList.trademarkList然后进行循环遍历。

 import {mapState} from "vuex"
 
 computed:{
      ...mapState({searchList: state=>state.search.searchList})
}

现在换个思路:能不能直接把searchList内的3个属性attrsList、goodsList、trademarkList生成计算属性直接使用,于是有了如下的代码但是缺点是state=>state.search.searchList还是重复次数太多了,不优雅。

 computed:{
      ...mapState({
      		attrsList: state=>state.search.searchList.attrsList,
      		goodsList: state=>state.search.searchList.goodsList,
      		trademarkList: state=>state.search.searchList.trademarkList
     })
}

那我们再换个思路,最优雅的解决方案是在定义vuex的src/store/search/index.js文件内定义getters方法,其中getters理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便,于是有了如下代码

src/store/search/index.js

//计算属性
//项目当中getters主要的作用是:简化仓库中的数据(简化数据而生)
//可以把我们将来在组件当中需要用的数据简化一下【将来组件在获取数据的时候就方便了】
const getters = {
    getAttrsList(state) {
        //state.searchList.goodsList如果服务器数据回来了,没问题是一个数组
        //假如网络不给力|没有网state.searchList.goodsList应该返回的是undefined
        //计算新的属性的属性值至少给人家来一个数组
        return state.searchList.attrsList || [];
    },
    getGoodsList(state) {
        return state.searchList.goodsList || [];
    },
    getTrademarkList(state) {
        return state.searchList.trademarkList || [];
    },
}

src/pages/Search/index.vue

import {mapGetters} from "vuex"

computed:{
      ...mapGetters(["attrsList", "goodsList", "trademarkList"])
    }

注意点6:针对计算属性返回的state.searchList.attrsList这里,要注意,如果网络不通会导致searchList是个空数组,而调用searchList.attrsList会返回underfine,所以应该避免underfine返回,因此写成eturn state.searchList.attrsList || [],即有值返回值没值返回空数组。

注意点7:

问题:src/store/search/index.js中已经定义getters返回attrsList、goodsList、trademarkList,Search组件引入mapGetters并生成…mapGetters([“goodsList”]),但是查看Search组件中goodsList值却为underfine。哪怕是SearchSelector组件中引入mapGetters并生成…mapGetters([“attrsList”,“trademarkList”])的attrsList、trademarkList,但是查看SearchSelector组件中attrsList和trademarkList值扔为underfine

src/store/search/index.js

//计算属性
//项目当中getters主要的作用是:简化仓库中的数据(简化数据而生)
//可以把我们将来在组件当中需要用的数据简化一下【将来组件在获取数据的时候就方便了】
const getters = {
    //注意:这个attrsList方法名必须和属性名goodsList一致才行,之前随便命名的getAttrsList()发现无法使用
    getAttrsList(state) {
        //state.searchList.goodsList如果服务器数据回来了,没问题是一个数组
        //假如网络不给力|没有网state.searchList.goodsList应该返回的是undefined
        //计算新的属性的属性值至少给人家来一个数组
        return state.searchList.attrsList || [];
    },
    getGoodsList(state) {
        return state.searchList.goodsList || [];
    },
    getTrademarkList(state) {
        return state.searchList.trademarkList || [];
    },
}

src/pages/Search/index.vue

computed:{
      //mapGetters里面的写法:传递的数组,因为getters计算是没有划分模块【home,search】
      ...mapGetters(["goodsList"]),
    },

在这里插入图片描述

答案:因为在src/store/search/index.js中定义getters时,getAttrsList(state)名称不对,不能随便命名,方法名必须和属性名一样才行。也就是说定义getters时有点类似计算属性,方法名就是计算属性名,所以不能随便命名否则就会不认识而出现underfine,正确代码应该长这样。

//计算属性
//项目当中getters主要的作用是:简化仓库中的数据(简化数据而生)
//可以把我们将来在组件当中需要用的数据简化一下【将来组件在获取数据的时候就方便了】
const getters = {
    //注意:这个attrsList方法名必须和属性名goodsList一致才行,之前随便命名的getAttrsList()发现无法使用
    attrsList(state) {
        //state.searchList.goodsList如果服务器数据回来了,没问题是一个数组
        //假如网络不给力|没有网state.searchList.goodsList应该返回的是undefined
        //计算新的属性的属性值至少给人家来一个数组
        return state.searchList.attrsList || [];
    },
    goodsList(state) {
        return state.searchList.goodsList || [];
    },
    trademarkList(state) {
        return state.searchList.trademarkList || [];
    },
}

注意点8:

问题:之前的设计思路是又Home组件跳转到Search组件时,定义beforeMount()方法修改搜索值,定义getData()方法,定义在mounted()中触发getData()方法,是可以实现跳转Search组件时就动态展示相应数据,但相应的问题是已经在Search组件中再进行关键字搜索或者点击三级菜单选项时就不会触发查询信息接口,即当下该方法只会触发一次查询接口,如何完善?

答案:采用监听$route的方式。因为无论是关键字搜索还是点击点击三级菜单选项,路由信息都会改变,所以只要能监听到路由信息改变再次调佣getData()方法就可以实现更新数据。

注意点9:

问题:为啥监听方法中最后还得清空searchParams对象中的category1Id、category2Id、category3Id的值?

答案:因为每次点击三级菜单选项传递的category1Id、category2Id、category3Id值不一样,这3种每次只会传其中一个值,所以在每次调用接口之后应该清空,避免脏数据。而分类名字与关键字不用清理:因为每一次路由发生变化的时候,都会给他赋予新的数据。

本人其他相关文章链接

1.vue尚品汇商城项目-day04【24.点击搜索按钮跳转后的页面商品列表、平台售卖属性动态展示(开发Search组件)】
2.vue尚品汇商城项目-day04【25.面包屑处理关键字】
3.vue尚品汇商城项目-day04【26.排序操作(难点)】
4.vue尚品汇商城项目-day04【27.分页器静态组件(难点)】
5.vue尚品汇商城项目-day04【28.详情页面Detail】
6.vue尚品汇商城项目-day04【29.加入购物车操作(难点)】

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。