VUE前台项目day_05

一、分页器

(一)理解分页器

什么时候使用分页器?

当大量的数据不能同时展示到同一个页面时

做分页器需要哪些数据?

总数(total)、页码(pageNo)、页大小(pageSize)、连续页数(continues)、总页数(totalPage)=total/pageSize(注意要向上取整)

(二)分页器的静态页面

1.copy简单的分页器页面的静态组件,创建Pagination组件,记得在main.js中引入并注册Pagination组件

import Pagination from './components/Pagination'
//全局注册
Vue.component('Pagination',Pagination)

2.去Search组件指定位置,直接编写组件标签<Pagination/>

(三)实现分页器功能

  <!-- 分页器 -->
     <Pagination 
         :total="total" 
         :pageSize="searchParams.pageSize" 
         :pageNo="searchParams.pageNo" 
         :continues="5"
         :sendPageNo="changePageNo"
      />

Pagination组件中:

<template>
  <div class="pagination" v-show="total">
    <div>
      <button :disabled="pageNo === 1" @click="sendPageNo(pageNo-1)">上一页</button>
      <button v-show="startEnd.start !== 1" @click="sendPageNo(1)">1</button>
      <span v-show="startEnd.start > 2">...</span>

      <button
        v-for="(number,index) in (startEnd.end - startEnd.start + 1)"
        :key="index"
        :class="{active:(index + startEnd.start) === pageNo}"
        @click="sendPageNo(index + startEnd.start)"
      >
        {{index + startEnd.start}}
      </button>

      <span v-show="startEnd.end < totalPage - 1">...</span>
      <button v-show="startEnd.end !== totalPage" @click="sendPageNo(totalPage)">{{totalPage}}</button>
      <button :disabled="pageNo === totalPage" @click="sendPageNo(pageNo+1)">下一页</button>

      <span>共 {{total}} 条</span>
    </div>
  </div>
</template>

<script>

export default {
  name: "Pagination",
  props:["total", "pageSize", "pageNo", "continues","sendPageNo"],
  computed:{
    totalPage(){
        return Math.ceil(this.total / this.pageSize)
    },
    startEnd(){
      let start = 0
      let end = 0

      const {pageNo,continues,totalPage} = this

      // 特殊情况一
      if (continues > totalPage) {
        start = 1;
        end = totalPage
      }else{
        // 开始计算
        start = pageNo - (continues-1) / 2
        end = pageNo + (continues-1) / 2
      }

       // 特殊情况二 左边推多了
       if (start<1) {
        start = 1
        end = continues
      }

       // 特殊情况三 右边推多了
       if (end>totalPage) {
        start = totalPage - continues + 1
        end = totalPage
      }
      return {start,end}
    }
  }
};
</script>

二、商品排序

1.明确实现两种排序:综合排序、价格排序

2.依赖的是数据中的order属性,属性值格式为 1:desc

1是综合排序,2是价格排序

asc是升序(箭头往上指),desc是降序(箭头往下指)

3.思路:谁有红色背景,看order的第一位;谁有箭头,看order的第一位;箭头是上还是下,看order的第二位

三、商品详情页面

(一)静态组件

  1. ctrlCV=>Detail静态组件到页面当中;

  1. 到routes.js文件中配置路由,注意别忘了带上id!

import Detail from '@/pages/Detail'

{
  name:'detail',
  path:'/detail:id',
  component:Detail
}

(二)获取数据

  1. 先用路由配置项scrollBehavior来解决跳转Detail组件时出现的一个滚动条问题

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
scrollBehavior函数:每次路由跳转时都会执行
to:要跳到哪个路由
from:来自哪个路由
savedPosition:跳转前滚动条的位置
  1. 编写api/index.js文件,写相对应的接口函数

//该函数专门用于:获取商品详情,注意:此接口需要参数
export const reqGoodDetailInfo = (id) => ajax.get('/item/'+id)
  1. 将详细信息存入data中

  data() {
    return {
      goodNum: 1,
      info: {
        categoryView: {},
        price: {},
        skuInfo: {},
        spuSaleAttrList: [],
      },
    };
  },
  1. 引入reqGoodDetailInfo

import { reqGoodDetailInfo } from "@/api";
  1. Detail组件一挂载就获取商品详情

  1. 写一个getGoodDetailInfo方法回调,便于获取商品详情的数据

(三)放大镜效果呈现

  1. 在npm网页里搜索vue-photo-zoom-pro,在控制台输入npmivue-photo-zoom-pro@2.x,引入放大镜库。

  1. 编写zoom组件中的代码。

  1. 到父组件当中,编写代码,使其动态获取数据

<Zoom :imgurl="info.skuInfo.skuDefaultImg"/>

(四)商品图片组

  1. Detail组件给ImageList传递图片数组

  1. ImageList接收skuDefaultImg

  1. 到ImageList组件中使用轮播图展示数据(一定要将id改为ref,否则轮播效果不实现)

  1. 绑定watch中的监听事件——$nextTick来实现轮播效果,要记得引入Swiper和它的样式

  1. 切换图片

在ImageList组件中点击图片,让放大镜区域实现。在ImageList组件中利用$bus(组件之间通信)传递点击图片的url给放大镜图片。

Zoom组件中:

在Zoom组件中写个data用来存储后期的url,并用mounted来挂载,最后实现效果

beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

(五)商品购买数量

经过分析,还有和京东等电商网站的对比学习,商品购买的最小数量是1,最大数量是200,要控制在这个范围内。

  1. 正则表达式计算商品购买数量

去Detail组件中定义

data(){
  return{
    goodNum:1
  }
},

methods:{
  changeGoodNum(e){
    console.log('输出的是:',e.target.value)
 }
}

控制台输出:

创建utils/reg.js文件,以后项目当中用到的正则表达式在这里统一管理(别忘了引入)

export const goodNumReg = /^([1-9]|[1-9]\d|1\d{2}|200)$/
  1. 限制购买数量

完成changeGoodNum逻辑

在+-按钮上添加点击事件,完善业务逻辑

最终完成+-业务逻辑,记得做判断,不要低于最小值还有超过最大值

(六)添加商品到购物车

找到接口文档,写对应的函数

export const reqAddGood2Cart = (goodId,goodNum) => ajax.post(`cart/addToCart/${ goodId }/${ goodNum }`)

到Detail中找到加入购物车的位置,添加点击事件,在methodes中配置handleAddCart的回调,在此之前引入reqAddGood2Cart

<a @click="handleAddCart">加入购物车</a>

四、AddCartSuccess页面

(一)静态页面

1.直接ctrlCV写好的页面

2.到routes.js文件中配置路由

import AddCartSuccess form '@/pages/AddCartSuccess'

{
  name:'addcart_success',
  path:'/addcart_success',
  components:AddCartSuccess
}

3.回到Detail文件中写判断,完成跳转

(二)收集商品属性并呈现到页面上

1.加入购物车后,要在页面添加商品属性,呈现在页面上,这是需要给handleAddCart方法追加逻辑

handleAddCart(){
 //获取路由传过来的商品id
 const {id} = this.$route.params
 //获取组件辛苦维护的商品数据
 const {goodNum} = this
 //请求添加购物车
 const result = reqAddGood2Cart(id,goodNum)
 //判断业务逻辑是否成功
 if(result.code === 200){
   //第一步:获取商品属性
   //获取商品名称和商品图片
   const {skuName,skuDefaultImg} = this.info.skuInfo
   //获取商品单价
   const {price} = this.info
   //获取商品的数量
   const {goodNum} = this
   //获取商品的所有属性,并形成一个数组
   const arr = []
   //遍历所有可选的属性,找到用户所选择的,收集到数组当中
   this.info.spuSaleAttrList.forEach((s1) => {
     const result = s1.spuSaleAttrValueList.find(s2=>s2.isChecked === '1')
     arr.push(result.saleAttrName + ':' +result.saleAttrValueList)
  })
  // 准备一个对象,收集到所有对象,一会儿想办法传给addcart_success
  const selectedInfo = {
    name:skuName,
    url:skuDefaultImg,
    price,
    goodNum,
    arr
  }
  //第二步:将selectedGoodInfo存入sessionStorage
  sessionStorage.setItem('selectedGoodInfo',JSON.stringfy(selectGoodInfo))
  //第三步:跳转到/addcart_success,同时携带要呈现的信息
  this.$router.push('/addcart_success')
 }else{
   alert(result.message)
 }
}

2.到AddCartSuccess组件中编写代码呈现信息

利用data返回定义selectedGoodInfo

data() {
  return {
     selectedGoodInfo:JSON.parse(sessionStorage.getItem('selectedGoodInfo'))
  }
},

五、购物车ShopCart页面

(一)静态组件

  1. 还是ctrlCV静态组件到项目当中

  1. 到router/routes.js文件中配置路由,别忘了引入

import ShopCart from '@/pages/ShopCart'

{
  name:'shopcart',
  path:'/shopcart'
  components:ShopCart
}
  1. 实现”我的购物车“和“去购物车结算”两个按钮的跳转功能

<router-link to="/shopcart">我的购物车</router-link>

<router-link to="/shopcart">去购物车结算</router-link>

(二)获取购物车数据

  1. 找到接口文档,开始编写接口文件

//该函数专门用于:获取购物车列表信息
export const reqShopCartList = () => ajax.get('/cart/cartList')
  1. 用mounted进行挂载获取购物车数据

mounted:{
  //组件一挂载就获取购物车数据
  this.getShopCartList()
}
  1. 编写methods方法发送请求

methods: {
  // 获取购物车列表数据的函数
  async getShopCartList(){
    // 发请求,获取购物车数据
    let result = await reqShopCartList()
    console.log(result)
  }
},

(三)实现用户临时标识

  1. 为什么不登陆就能看到购物车,为了引导用户消费,而有的网站必须要登录才能查看,是因为网站的用户量大,逻辑复杂;

  1. 当第一次发送ajax请求时,会为其生成一个临时标识,生成之后,要在本次请求头上携带这个标识,存入local当中,避免在关闭浏览器之后,丢失数据

  1. utils中新建auth.js文件,该文件统一管理用户的身份验证,控制台npm i uuid(uuid是生成的随机字符串)

  1. ajax文件中引入getUserTempId,编写config,使得获得用户临时标识,Network里得到数据,如下图:

(四)购物车初始化列表

1.ShopCart组件一挂载就请求购物车数据,随后数据存入data,要将cartInfoList的初始值置为空。

  import {reqShopCartList} from '@/api'

  export default {
    name: 'ShopCart',
    data() {
      return {
        cartInfoList:[],
      }
    },
    methods: {
      // 获取购物车列表数据的函数
      async getShopCartList(){
        // 发请求,获取购物车数据
        let result = await reqShopCartList()
        if (result.code === 200) {
          this.cartInfoList = result.data[0].cartInfoList
        }else{
          alert(result.message)
        }
      }
    },
    mounted() {
      // 组件一挂载就调用getShopCartList,获取购物车数据
      this.getShopCartList()
    },
  }

2.利用v-for遍历,将页面数据动态化

(五)购物车勾选商品的状态

  1. 单选商品

找到接口文档,编写api接口

//该函数专门用于:获取商品单选时的状态
export const reqCheckOneCart = (skuId,isChecked) => ajax.get(`/cart/checkCart/${skuId}/${isChecked}`)

找到单选按钮,编写绑定事件handleCheckOne,并传递参数cartInfo、skuId

到methods中编写获取购物车列表单选的回调函数

记得在import中引入reqCheckOneCart,@click后面添加prevent,阻止默认行为,这里的单选行为需要联系服务器,要保持一致,要让所要实现的内容完全受到数据的控制。

  1. 全选

找到接口文档编写api接口

//该函数专门用于:获取商品全选时的状态
export const reqCheckAllCart = (idList,isChecked) => ajax.post(`/cart/batchCheckCart/${isChecked}`,idList)

找到全选按钮,编写绑定事件handleCheckAll,并传递参数cartInfoList,isAll

到methods中编写获取购物车列表全选的回调函数

利用计算属性isAll的全选状态,当单选的值都为1时,全选功能生效,这里要用到数组的every方法

computed:{
  return this.cartInfoList.every(cartInfo => cartInfo.isChecked === 1)
}

记得在import中引入reqCheckAllCart,@click后面添加prevent,阻止默认行为,这里的全选行为也需要联系服务器,要保持一致,要让所要实现的内容完全受到数据的控制。

补充:JavaScript数组中的every方法

作用:判断数组当中所有的元素是否都满足条件
方法特点:1.循环次数不等于数组长度
2.函数内部的return=>return true: 循环继续,当元素满足条件,继续判断,如果循环执 行完毕还是true,则every的返回值就是true
return false:循环结束,当前元素不满足条件,every的返回值也 是false
3.every方法的返回值=>return true:全部元素都满足条件
return false:有元素不满足条件
注意点:1.every()方法不会对空数组进行检测
2.every()方法不会改变原始数组

(六)购物车商品的总数与总金额

利用计算属性计算total和totalPrice

(七)购物车删除商品

  1. 找到接口文档编写删除商品的api接口

//该函数专门用于:获取商品被删除时的状态
export const reqDeleteOneCart = (id) => ajax.delete(`/cart/deleteCart/${id}`)
  1. 到methods里编写handleDeleteOne的方法回调,先找到按钮绑定点击事件handleDeleteOne,传递参数cartInfo.skuId

补充:findindex方法

findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
findIndex() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
注意: findIndex() 对于空数组,函数是不会执行的。
注意: findIndex() 并没有改变数组的原始值。

(八)数据为空的处理

  1. every的坑,当数组里的内容为空时,控制台报true;空数组调用every,返回值永远为真;我们设置的初始值为空,every身后写什么已经是不重要了,所以返回为真,isAll就被勾选。

  1. result.data[0]返回undefined,控制台会报错,所以需要再写得缜密一些,往外层包一个if判断。

  1. 写一个empty来展示购物车为空时的状态

实现效果如下:这样就能改善下,数组为空时,every返回true的问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值