Vue2电商前台项目(四):完成Detail详情页模块业务

目录

一、项目开发的步骤

二、配置路由规则+滚动行为

1.配置路由规则

2.滚动行为

三、请求详情页数据并展示数据

1.api接口

2.写Vuex获取数据

3.派发actions

4.展示动态数据

四、放大镜展示数据

1.Detail父组件把图片数据传给放大镜子组件

2.解决数据未请求到时获取其属性报错的问题

五、放大镜下边的轮播图展示数据

1.添加轮播图图片

2.售卖属性

六、产品售卖属性的排他操作

七、放大镜效果实现

八、购买产品个数的加减操作

1.收集数量并控制个数>0

2.校验用户自己输入的值


一、项目开发的步骤

静态=>请求(api)=>vuex=>动态组件

二、配置路由规则+滚动行为

1.配置路由规则

我们引入静态组件之后,得在router里给detail配置路由

import Detail from '../pages/Detail'
       {
            path:'/detail',
            component:Detail,
            meta:{show:true}
        }

点击图片就进行路由跳转显示产品,所以需要params传参,就得占位

 path:'/detail/:skuId',

把装有图片的a标签换成router-link

<router-link :to="`/detail/${good.id}`">
  <img :src="good.defaultImg" />
</router-link>

2.滚动行为

现在点击之后滚轮默认在你前一页待的地方,所以我们得设置一下滚轮位置

我们先把路由信息分开写到routers.js里,index再调用看着比较方便

import routes from './routes'
export default new VueRouter({
    //配置路由
    routes
    //kv 一致省略v
    scrollBehavior(to, from, savedPosition) {
        return { y: 0 }//代表滚动条在最上方
    }
})

滚动行为scrollBehavior(to, from, savedPosition),可以设置x、y坐标

三、请求详情页数据并展示数据

1.api接口

//获取信息详情的api
export const reqGoodsInfo = (skuId) => {
   return requests({ url: `http://gmall-h5-api.atguigu.cn/api/item/${skuId}`, method: 'get'})
}

2.写Vuex获取数据

还需要一个新的仓库服务detail,新建一个文件夹detail/index.js

import { reqGoodsInfo } from "@/api"
const state = {
    goodInfo: {}
}
const mutations = {
    GETGOODINFO(state, goodInfo) {
        state.goodInfo = goodInfo
    }
}
const actions = {
    //获取产品信息
    async getGoodInfo({ commit }, skuId) {
        let result = await reqGoodsInfo(skuId)
        if (result.code == 200) {
            commit('GETGOODINFO', result.data)
        }
    }
}
const getters = {}
export default {
    state,
    actions,
    mutations,
    getters
}

小仓库建完了之后需要回到大仓库进行合并

import detail from './detail/index'
modules:{
        home,
        search,
        detail
    }

3.派发actions

用户点击图片之后触发action,所以应该是detail组件挂载完毕之后触发action

mounted() {
      //派发action
      console.log(this.$route.params)
      this.$store.dispatch('getGoodInfo',this.$route.params.skuId)
    },

4.展示动态数据

去仓库当中简化数据,这两个都是对象类型的

const getters = {
    categoryView(state){
        return state.goodInfo.categoryView||{}
        //不加||{}会有个报错,因为数据没回来的时候,{}.categoryView是undefined
        //捞回来之后才有
    },
    skuInfo(state){
        return state.goodInfo.skuInfo||{}
    }
}

捞数据:

  import {mapGetters} from 'vuex'
  computed:{
      ...mapGetters(['categoryView','skuInfo'])
    }

然后就能直接用了

<div class="conPoin">
        <span v-show="categoryView.category1Name">{{categoryView.category1Name}}</span>
        <span v-show="categoryView.category2Name">{{categoryView.category2Name}}</span>
        <span v-show="categoryView.category3Name">{{categoryView.category3Name}}</span>
</div>
<h3 class="InfoName">{{skuInfo.skuName}}</h3>
<p class="news">{{skuInfo.skuDesc}}</p>

四、放大镜展示数据

1.Detail父组件把图片数据传给放大镜子组件

上面我们实现了detail组件从仓库中拿数据,我们需要的数据有categoryView(放大镜上面的几个标题)和skuInfo(放大镜右侧手机的介绍)中的,我们要是直接捞的话得state.detail.goodInfo.category[x].category1Id,这个时候就用到getters来简化数据了,mapGetters之后再直接调就可以了。

现在的放大镜组件(Zoom)是detail 的儿子组件,现在zoom需要的数据已经被它父亲detail捞过来了,我们想用就继承过来就行,我们用到的是对象下的skuImageList数组

<!--放大镜效果-->
          <Zoom :skuImageList="skuImageList"/>
computed:{
      、、、
      skuImageList(){
        return this.skuInfo.skuImageList||[]
       //为了上面写着方便,要不然上面就是skuInfo.skuImageList
      }
    }

zoom组件继承:

<img :src="imgObj.imgUrl" />
、、、
props:['skuImageList'],
    computed:{
      imgObj(){
        return this.skuImageList[0] || {}
       //这里是为了zoom组件写着方便
      }
    }

所以组getters里写从仓库里继承来的数据的简写,组件里用仓库中的写...mapGetters

组件里面再想简化就写在computed里,自己的简化写在自己组件里

2.解决数据未请求到时获取其属性报错的问题

就是上面提到的,如果没有加||{ }或者[ ],我们在获取数据时第一时间是没有获取到的,后面的代码直接.取它后面的东西肯定是undefined,过一定的时间取到了才会显示,为了避免这样的错误不让它出现undefined,return的时候都加上||

五、放大镜下边的轮播图展示数据

1.添加轮播图图片

现在我们来写detail的另一个儿子组件,跟zoom传的东西是一样的

<!-- 小图列表 -->
          <ImageList :skuImageList="skuImageList"/>

继承之后去遍历那些小图

<div class="swiper-slide" v-for="(slide,index) in skuImageList" :key="slide.id">
   <img :src="slide.imgUrl">
</div>

2.售卖属性

售卖属性现在还在仓库里面呢,老规矩捞过来

//售卖属性
    spuSaleAttrList(state){
        return state.goodInfo.spuSaleAttrList||[]
    }
...mapGetters(['categoryView','skuInfo','spuSaleAttrList']),

然后先去vue里看detail组件里有没有数据!!

有数据了之后回到属性和属性值那里,结构一样用v-for

<dl v-for="spuSaleAttr in spuSaleAttrList" :key="spuSaleAttr.id">
      <dt class="title">{{spuSaleAttr.saleAttrName}}</dt>
      <dd changepirce="0" :class="{active:spuSaleAttrValue.isChecked==1}" v-for="spuSaleAttrValue in spuSaleAttr.spuSaleAttrValueList" :key="spuSaleAttrValue.id">
         {{spuSaleAttrValue.saleAttrValueName}}
      </dd>
      </dl>

这里结构比较复杂,一层套一层的,要看清你选的是谁,最后高亮设置为动态的,就完事

六、产品售卖属性的排他操作

点哪个哪个有高亮,得把点击的那个对象还有它所在的数组传参过来,排他就得传他还有他的兄弟们,拿到他父亲就拿到兄弟了

 @click="changeActive(spuSaleAttrValue,spuSaleAttr.spuSaleAttrValueList)"

然后开始计算,把数组里面所有的ischecked都=0,只有被点击的那个ischecked=1

methods: {
    //产品售卖属性值高亮
    changeActive(spuAttrValue,arr){
      arr.forEach(item => {
        item.isChecked=0
      });
      spuAttrValue.isChecked=1
    }
  },

七、放大镜效果实现

我们的轮播图已经引了swiper,main.js也引了swiper的样式了,然后new Swiper写轮播图,这次的数据是父组件传过来的所以数据肯定有了,但是上面有个v-for所以就用nextTick

<div class="swiper-container" ref="cur">
watch: {
    skuImageList(newValue, oldValue) {
      this.$nextTick(() => {
         new Swiper(this.$refs.cur, {
          // 如果需要前进后退按钮
          navigation: {
            nextEl: ".swiper-button-next",
            prevEl: ".swiper-button-prev",
          },
        });
      });
    },
  },

分页器啥的都不用,但是现在的轮播图每次变换都只有一张图片,我们想实现大盒子里放三张,每次点击前进后退按钮就变换三张,通过slider容器

new Swiper(this.$refs.cur, {
slidesPerView: 3, //页面同时显示几个
slidesPerGroup: 3, //一次切换几个

同时点击小图的时候还会有边框,这里我们不用hover,用js写,点谁谁加个类,来个高光

<img :src="slide.imgUrl" :class="{active:currentIndex==index}" @click="changeCurrentIndex(index)"/>
methods: {
    changeCurrentIndex(index){
      this.currentIndex=index
    }
  },

然后实现点击哪个小图zoom组件里的大图List就显示小图,他俩是兄弟关系,用全局事件

changeCurrentIndex(index){
      this.currentIndex=index
      //通知自己的兄弟当前索引值为几
      this.$bus.$emit('getIndex',this.currentIndex)
    }

来zoom里接收:

data(){
      return {
        currentIndex:0
      }
    },
    computed:{
      imgObj(){
        return this.skuImageList[this.currentIndex] || {}
      }
    },
    mounted() {
      //获取索引,它是收方用on
      this.$bus.$on('getIndex',(index)=>{
        this.currentIndex=index
      })
    },

之前返回的图片是写死的,默认第一个,现在得设置一个data默认为0,点击哪个索引号就给data

最后让遮盖层随着鼠标的移动而移动,大图也换成和遮盖图一样的图片

<!-- 鼠标移动事件 -->
    <div class="event" @mousemove="handler"></div>
    <div class="big">
      <img :src="imgObj.imgUrl" ref="big"/>
    </div>
    <!-- 遮罩层 -->
    <div class="mask" ref="mask"></div>
 methods: {
      handler(event){
        let mask=this.$refs.mask
        let big=this.$refs.big
        let left=event.offsetX-mask.offsetWidth/2
        let top=event.offsetY-mask.offsetHeight/2
        //约束范围
        if(left<=0) left=0
        if(left>=mask.offsetWidth) left=mask.offsetWidth
        if(top<=0) top=0
        if(top>=mask.offsetHeight) top=mask.offsetHeight
        mask.style.left=left+'px'
        mask.style.top=top+'px'
        //大图是mask图的二倍大小
        big.style.left=-2*left+'px'
        big.style.top=-2*top+'px'
      }
    },

八、购买产品个数的加减操作

1.收集数量并控制个数>0

回到detail组件里的购买个数。个数是响应式的而且默认值为2,根据经验写在data里

data(){
    return {
      //购买产品的个数
      skuNum:1
    }
  },

这个数据后续我们要拿到,收集数据:ref或者v-model

<input autocomplete="off" class="itxt" v-model="skuNum"/>

两个按钮加减:直接一个三元运算符控制数量>=1,妙蛙

<a href="javascript:" class="plus" @click="skuNum++">+</a>
<a href="javascript:" class="mins" @click="skuNum>1?skuNum--:skuNum=1">-</a>

2.校验用户自己输入的值

得控制用户输入的是数字而且用户输入之后得把它输入的赋值给skuNum

(1)直接给表单加type="number",确实除了数字而且是整型的其他都加不了了,结果表单右边又来了一个数字加减框

(2)本来我想直接上parseInt,但是这样它把字母在前的识别为NAN,数字在前的就取了前面的那个数字,我们的需求是只要有字母都不合格

let values=parseInt(e.target.value)
      if(values<1||values==NaN) values=1
      this.skuNum=values

(3)乘1过滤掉文字或者字母,乘出来是NaN

changeSkuNum(e) {
      let value = e.target.value * 1;
      if (value < 1 || isNaN(value)) {
        this.skuNum = 1;
      } else {
        this.skuNum = parseInt(value);
      }
    },
  • 31
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值