• vue前台(十一)


    一,我的订单获取数据进行展示

    在center订单中心组建中,创建两个子路由组件,myorder组件, 我的订单,  grouporder组件, 团购组件

     <dd>
                  <router-link to="/center/myorder">我的订单</router-link>
                </dd>
                <dd>
                  <router-link to="/center/grouporder">团购订单</router-link>
                </dd>
      </div>
            <!-- 右侧内容 -->
            <router-view></router-view>
          </div>

    配置路由

     { 
        path:'/center',
        component:Center,
        children:[
          {
            path:'myorder',
            component:Myorder
          },
          {
            path:'grouporder',
            component:Grouporder
          },
          {
            path:'',
            redirect:'myorder'
          }
        ]
    
      },

    点击路由连接router-link,自带的类,配置颜色

       //左边
          .order-left {
            float: left;
             16.67%;
    
            .router-link-active {
              color: hotpink;
              /* background-color: hotpink; */
            }

    1.封装请求获取我的订单分页信息

    //请求获取我的订单分页信息   /api/order/auth/{page}/{limit}   get
    
    export const reqMyOrder = (page,limit) => Ajax.get(`/order/auth/${page}/${limit}`)

     2.myorder组件获取数据进行展示,需要传入page和limit参数,可在data中初始化,因为myorder中有分页器,需要用到该参数

    data() {
        return {
          page: 1,
          limit: 5,
    
          myOrderInfo: {},
        };
      },
      mounted() {
        this.getMyOrder();
      },
      methods: {
        //这个MyOrder组件是一个路由组件
        //路由组件点击切换才会创建组件对象,父组件传递才有可能
        //可以选择路由传参,但是非常复杂并且不适合(数据有可能很复杂)
        //所以请求数据只能在子路由组件
        async getMyOrder(page = 1) {
          this.page = page;
          const result = await this.$API.reqMyOrder(this.page, this.limit);
          if (result.code === 200) {
            this.myOrderInfo = result.data;
          }
        },

    3.返回的响应数据

    {
        "code": 200,
        "message": "成功",
        "data": {
            "records": [
                {
                    "id": 70,
                    "consignee": "admin",
                    "consigneeTel": "15011111111",
                    "totalAmount": 29495,
                    "orderStatus": "UNPAID",
                    "userId": 2,
                    "paymentWay": "ONLINE",
                    "deliveryAddress": "北京市昌平区2",
                    "orderComment": "",
                    "outTradeNo": "ATGUIGU1584247289311481",
                    "tradeBody": "Apple iPhone 11 (A2223) 128GB手机 双卡双待 A",
                    "createTime": "2020-03-15 12:41:29",
                    "expireTime": "2020-03-16 12:41:29",
                    "processStatus": "UNPAID",
                    "trackingNo": null,
                    "parentOrderId": null,
                    "imgUrl": null,
                    "orderDetailList": [
                        {
                            "id": 81,
                            "orderId": 70,
                            "skuId": 2,
                            "skuName": "Apple iPhone 11 (A2223) 64GB 红色",
                            "imgUrl": "http://192.168.200.128:8080/xxx.jpg",
                            "orderPrice": 5499,
                            "skuNum": 1,
                            "hasStock": null
                        },
                        …
                    ],
                    "orderStatusName": "未支付",
                    "wareId": null
                },
                …
            ],
            "total": 41,
            "size": 2,
            "current": 1,
            "pages": 21
        },
        "ok": true
    }

    4.在html中填充数据

    <tr v-for="(goods, index) in order.orderDetailList" :key="goods.id">
                  <td width="60%">
                    <div class="typographic">
                      <!-- hasStock:null
                        id:4252
                        imgUrl:"http://182.92.128.115:8080/group1/M00/00/0D/rBFUDF7G-ZKADQhWAAJsvyuFaiE144.jpg"
                        orderId:1939
                        orderPrice:4500
                        skuId:118
                        skuName:"华为P40--22"
                        skuNum:2
                      -->
                      <img :src="goods.imgUrl" style="80px;height:80px" />
                      <a href="#" class="block-text">{{goods.skuName}}</a>
                      <span>x{{goods.skuNum}}</span>
                      <a href="#" class="service">售后申请</a>
                    </div>
                  </td>
                  <!--template是一个内置的标签,这个标签不会影响样式,相当于一个包裹器和div类似,但是div影响样式  -->
                  <template v-if="index === 0">
                    <td
                      :rowspan="order.orderDetailList.length"
                      width="8%"
                      class="center"
                    >{{order.consignee}}</td>
                    <td :rowspan="order.orderDetailList.length" width="13%" class="center">
                      <ul class="unstyled">
                        <li>总金额¥{{order.totalAmount}}</li>
                        <li>{{ order.paymentWay === "ONLINE" ? '在线支付': '货到付款'}}</li>
                      </ul>
                    </td>
                    <td :rowspan="order.orderDetailList.length" width="8%" class="center">
                      <a href="#" class="btn">{{order.orderStatus === "UNPAID"?"未支付":"已完成"}}</a>
                    </td>
                    <td :rowspan="order.orderDetailList.length" width="13%" class="center">
                      <ul class="unstyled">
                        <li>
                          <a href="mycomment.html" target="_blank">评价|晒单</a>
                        </li>
                      </ul>
                    </td>
                  </template>
                </tr>

    注;1.此时需要对单元格进行合并,首先,只对第一行数据展示,然后对td单元格标签中的 :rowspan属性进行行数占据,可计算order.orderDetailList.length长度

     
     将单元格合并后

    二,使用element-ui的分页器

    1.在html中使用分页器paginaton

    在入口文件main.js中导入分液器pagainaton

    import { MessageBox, Message, Pagination } from 'element-ui';
    Vue.use(Pagination)
     <!--  @size-change="changeSize" 修改每页的数量回调函数 选择了新条数,就会触发这个事件,把选择的条数传给这个事件 -->
          <!-- @current-change="getMyOrder" 修改当前页  点击了哪一页,就会触发这个事件 把点击的页码传给这个事件 -->
          <el-pagination
            background
            :current-page="page"
           
            :page-size="limit"
            layout=" prev, pager, next, jumper,->,total"
            :total="myOrderInfo.total"
            
            :pager-count="5"  
            @current-change="getMyOrder($event)"
           @size-change="changeSize" 
            
          ></el-pagination>

    2.@current-change="getMyOrder" 和  @size-change="changeSize" 两个事件函数

     methods: {
        //这个MyOrder组件是一个路由组件
        //路由组件点击切换才会创建组件对象,父组件传递才有可能
        //可以选择路由传参,但是非常复杂并且不适合(数据有可能很复杂)
        //所以请求数据只能在子路由组件
        async getMyOrder(page = 1) {  //默认当前页为第一页
          this.page = page;   //修改当前页
          const result = await this.$API.reqMyOrder(this.page, this.limit);
          if (result.code === 200) {
            this.myOrderInfo = result.data;
          }
        },
      
      //改变当前页的条数
        changeSize(size){
          this.limit = size
          this.getMyOrder()
        },

    注,对分页器的属性详解

     <el-pagination
        //当前页的颜色
        background
        //修改每页的条数回调函数 选择了新条数,就会触发这个事件,把选择的条数传给这个事件
          @size-change="handleSizeChange"
        //修改当前页  点击了哪一页,就会触发这个事件 把点击的页码传给这个事件
          @current-change="handleCurrentChange"
        //当前页
          :current-page="currentPage4" 首尾项
        //连续页码数,包括首
        :pager-count="5" 
          :page-sizes="[100, 200, 300, 400]"
        //每页显示条目个数
          :page-size="100"
        //total放在最后面
          layout=" prev, pager, next, jumper,->,total"
        //总条目数
          :total="400">
        </el-pagination>

    三,全局前置路由守卫的使用

    1.在router-index.js,  

    2.引入vuex

    import store from '@/store'
    import routes from '@/router/routes'
    const router = new VueRouter({
      routes,
      scrollBehavior (to, from, savedPosition) {
        return { x: 0, y: 0 }
      }
    })
    
    //添加全局前置路由导航守卫
    // 必须登录后才能访问的多个界面使用全局守卫(交易相关、支付相关、用户中心相关) 
    // 自动跳转前面想而没到的页面
    
    router.beforeEach((to, from, next) => {
      //to:代表路由对象,目标(想去哪)
      //from: 代表路由对象,起始(从哪来)
      //netx:是一个函数,选择放行或者不放行的意思还可以去重定向到一个新的地方  
      //next()就是放行
      //next(false)不放行
      //next(路径)重定向
    
      let targerPath = to.path
      if(targerPath.startsWith('/pay') || targerPath.startsWith('/trade') || targerPath.startsWith('/center')){
        //看看用户是否登录了
        if(store.state.user.userInfo.name){
          next()
        }else{
          //在登录的路径后面添加上之前想要去的路径
          //配合登录逻辑可以让我们去到之前想去而没有去的地方
          next('/login?redirect='+targerPath)
        }
      }else{
        next()
      }
    
    })
    
    export default router

    注; 1.如果用户需要去交易页面,订单页面,需要判断一下

    2.判断用户是否登录了,从vuex中的user.js找到用户名信息,判断是否登录了

    3.

    如果用户没有登录,点击我的订单,跳转到登录页面,然后登录后,应该直接跳转到我的订单页面
    此时需要配置一个重定向的路径,在登录路径后添加一个query参数(去哪里的路径)
    此时在登录组件需要判断一下是否有query参数,有的话,去需要去的路径,没有的话,去home路径

      //点击登录按钮,发送请求
      methods: {
        async login() {
          let { mobile, password } = this;
          if (mobile && password) {
            let userInfo = { mobile, password };
            try {
              await this.$store.dispatch("userLogin", userInfo);
              let redirectPath = this.$route.query.redirect;
              //如果存在需要去的路径
              if (redirectPath) {
                //代表是从导航守卫进来的登录逻辑
                this.$router.push(redirectPath);
              } else {
                //代表不是从导航守卫来的登录逻辑
                this.$router.push("/home");
              }
            } catch (error) {
              alert(error.message);
            }
          }
        },
      },
     四,路由独享的守卫
     
    1.

    有个bug, 如果用户登录了,进入登录页面输入http://localhost:8080/#/login, 还是会跳转到登录页面,应该到home页面
    此时需要设置路由独享守卫,没有登录时,放行,有登录时,跳转到home页面

    2.引入vuex,    import store from '@/store'

    在路由对象中 router--routers.js, 设置login路由对象中设置路由独享守卫

      {
        path:'/login',
        component:Login,
        // 用来判定底部是否隐藏
        meta:{
          isHide:true
        },
        //路由独享守卫
        beforeEnter: (to, from, next) => {
          //to:代表路由对象,目标(想去哪),此时代表login路由对象
          if(!store.state.user.userInfo.name){
            //没登录,放行
            next()
          }else{
            //登录了,跳转到home页面  
            next('/home')
          }
        }
      },

     五,组件内的守卫,一般不用,一般用路由独享守卫

    和路由独享守卫的第二种方法,组件内的守卫,在login组件中设置

    引入vuex

    import store from '@/store'
      beforeRouteEnter(to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
        // 如果内部需要用到this,那么就得用下面的那个写法
        if (!store.state.user.userInfo.name) {
          //没有登录,放行
          next();
        } else {
          //登录了,跳转到home页面
          next("/home");
        }
      },
    
      // beforeRouteEnter(to, from, next) {
      //   next((vm) => {
      //     // 通过 `vm` 访问组件实例 vm就是你之前想要的this
      //   });
      // },

     六,只有携带了skuNum和sessionStorage内部有skuInfo数据  才能看到添加购物车成功的界面,路由独享守卫

     
     {
        path:'/addCartSuccess',
        component:AddCartSuccess,
        name:"addcartsuccess",
        //只有携带了skuNum和sessionStorage内部有skuInfo数据  才能看到添加购物车成功的界面
        beforeEnter: (to, from, next) => {
          let skuNum= to.query.skuNum
          let skuInfo = JSON.parse(sessionStorage.getItem('SKUINFO'))
          //判断
          if(skuNum && skuInfo){
            //携带了skuNum 和skuInfo,放行
            next()
    
          }else{
            next('/')
    
          }
        }
      },

    只有从购物车界面/shopcart才能跳转到交易页面(创建订单)/trade

      {
        path:'/trade',
        component:Trade,
        beforeEnter: (to, from, next) => {
          if(from.path === '/shopcart'){
            next()
          }else{
            next('/')
          }
        }
      },

    只有从交易页面(创建订单)页面/trade才能跳转到支付页面/pay

    {
        path:'/pay',
        component:Pay,
        beforeEnter: (to, from, next) => {
          if(from.path === '/trade'){
            next()
          }else{
            next('/')
          }
        }
      },

    只有从支付页面/pay才能跳转到支付成功页面/paysuccess

     {
        path:'/paysuccess',
        component:PaySuccess,
        beforeEnter: (to, from, next) => {
          if(from.path === '/pay'){
            next()
          }else{
            next('/')
          }
        }
      },

     七,图片懒加载

    1. 图片懒加载特点说明
    (1)    还没有加载得到目标图片时, 先显示loading图片
    (2)    在<img>进入可视范围才加载请求目标图片
    
    2. 下载依赖
    npm install vue-lazyload
    
    3. 引入并配置loading图片
    import VueLazyload from 'vue-lazyload'
    import loading from '@/assets/images/loading.gif'
    // 在图片界面没有进入到可视范围前不加载, 在没有得到图片前先显示loading图片
    Vue.use(VueLazyload, { // 内部自定义了一个指令lazy
      loading,  // 指定未加载得到图片之前的loading图片
    })
    
    4. 对异步获取的图片实现懒加载
    <img v-lazy="goods.defaultImg" />

     1.在入口文件main.js中配置

    // 引入图片懒加载插件
    import VueLazyload from 'vue-lazyload'
    import loading from '@/assets/images/loading.gif'
    Vue.use(VueLazyload, { // 内部自定义了一个指令lazy
      loading,  // 指定未加载得到图片之前的loading图片
    })

    2.在search组件的img标签中实行v-lazy指令

     <router-link :to="`/detail/${goods.id}`" target="_blank">
                            <img v-lazy="goods.defaultImg" />
                          </router-link>

    八,路由懒加载

    路由懒加载  
        调用import函数把一次性打包的所有路由组件分开去打包
        然后访问哪一个再去加载哪一个
    
        (1)    当打包构建应用时,JS包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,
            然后当路由被访问的时候才加载对应组件,这样就更加高效了
        (2)    本质就是Vue 的异步组件在路由组件上的应用
        (3)    需要使用动态import语法, 也就是import()函数
        (4)    import('模块路径'): webpack会对被引入的模块单独打包
        (5)     当第一次访问某个路径对应的组件时,此时才会调用import函数去加载对应的js打包文件
    1. 理解
    (1)    当打包构建应用时,JS包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
    (2)    本质就是Vue 的异步组件在路由组件上的应用
    (3)    需要使用动态import语法, 也就是import()函数
    2. 编码
    // import Home from '@/pages/Home'
    // import Search from '@/pages/Search'
    // import Detail from '@/pages/Detail'
    
    /* 
    1. import('模块路径'): webpack会对被引入的模块单独打包
    2. 路由函数只在第一次请求时才执行, 也就是第一次请求访问对应路由路径时才会请求后台加载对应的js打包文件
    */
    const Home = () => import('@/pages/Home')
    const Search = () => import('@/pages/Search')
    const Detail = () => import('@/pages/Detail')

    没有使用路由懒加载时,是整体的组件打包

    使用路由懒加载后,加载一个路由组件,打包一个路由组件, 0.js,是组件打包的文件,路由的懒加载组件按照顺序加载组件

     vee-validate表单验证

    注;
    required: true,是必须的验证,

    name="phone" 是每项的输入框的名称标识,

    invalid类,非法的类名,不匹配这个验证规则,提示报错

    class="error-msg"  报错的类,局部验证

    协议选项需要打钩,需要自定义一个规则agree

    # 1. 说明
    
        vee-validate是专门用来做表单验证的vue插件
        我们当前用的是2.x的版本, 最新的3.x版本使用比较麻烦
        github地址: https://github.com/logaretm/vee-validate
        内置校验规则: https://github.com/logaretm/vee-validate/tree/v2/src/rules
        中文messages: https://github.com/logaretm/vee-validate/blob/v2/locale/zh_CN.js
    
    # 2. 使用
    
    ## 1). 引入
    
        下载: npm install -S vee-validate@2.2.15   
        引入插件:
            import Vue from 'vue'
            import VeeValidate from 'vee-validate'
            
            Vue.use(VeeValidate)
    
    ## 2). 基本使用
    
         <input v-model="mobile" name="phone" v-validate="{required: true,regex: /^1d{10}$/}" 
              :class="{invalid: errors.has('phone')}">
         <span class="error-msg">{{ errors.first('phone') }}</span>
         
         const success = await this.$validator.validateAll() // 对所有表单项进行验证
         
         问题: 提示文本默认都是英文的
    
    ## 3). 提示文本信息本地化
    
        import VeeValidate from 'vee-validate'
        import zh_CN from 'vee-validate/dist/locale/zh_CN' // 引入中文message
        
        VeeValidate.Validator.localize('zh_CN', {
          messages: {
            ...zh_CN.messages,
            is: (field) => `${field}必须与密码相同`  // 修改内置规则的message
          },
          attributes: { // 给校验的field属性名映射中文名称
            phone: '手机号',
            code: '验证码',
          }
        })
        
        完整中文message源码: https://github.com/logaretm/vee-validate/blob/v2/locale/zh_CN.js
    
    ## 4). 自定义验证规则
    
        VeeValidate.Validator.extend('agree', {
          validate: value => {
            return value
          },
          getMessage: field => field + '必须同意'
        })

     1.在src新建一个文件,validate.js,

    import Vue from 'vue'
    import VeeValidate from 'vee-validate'
    import zh_CN from 'vee-validate/dist/locale/zh_CN' // 引入中文message
    
    Vue.use(VeeValidate)
    
    
    
    VeeValidate.Validator.localize('zh_CN', {
      messages: {
        ...zh_CN.messages,
        is: (field) => `${field}必须与密码相同`  // 修改内置规则的message
      },
      attributes: { // 给校验的field属性名映射中文名称
        phone: '手机号',
        code: '验证码',
        password:'密码',
        password2:'确认密码',
        isCheck:'协议'
      }
    })
    
    
    VeeValidate.Validator.extend('agree', {
      validate: value => {
        return value
      },
      getMessage: field => field + '必须同意'
    })

    在入口文件main.js引入该模块

    // 引入表单验证插件
    import "../validate";

    在Register注册组件中,使用规则

    <div class="content">
            <label>手机号:</label>
            <input
              placeholder="请输入你的手机号"
              v-model="mobile"
              name="phone"
              v-validate="{ required: true, regex: /^1d{10}$/ }"
              :class="{ invalid: errors.has('phone') }"
            />
            <span class="error-msg">{{ errors.first("phone") }}</span>
            <!-- <input type="text" placeholder="请输入你的手机号" v-model="mobile" />
            <span class="error-msg">错误提示信息</span> -->
          </div>
    <div class="content">
            <label>验证码:</label>
            <input
              placeholder="请输入验证码"
              v-model="code"
              name="code"
              v-validate="{ required: true, regex: /^d{4}$/ }"
              :class="{ invalid: errors.has('code') }"
            />
    
            <img
              ref="code"
              src="/api/user/passport/code"
              @click="changecode"
              alt="code"
            />
    
            <span class="error-msg">{{ errors.first("code") }}</span>
            <!-- <input type="text" placeholder="请输入验证码" v-model="code" />
            <img
              ref="code"
              src="/api/user/passport/code"
              @click="changecode"
              alt="code"
            />
            <span class="error-msg">错误提示信息</span> -->
          </div>
     <div class="content">
            <label>登录密码:</label>
    
            <input
              type="text"
              placeholder="请输入你的登录密码"
              v-model="password"
              name="password"
              v-validate="{ required: true, regex: /^1d{10}$/ }"
              :class="{ invalid: errors.has('password') }"
            />
            <span class="error-msg">{{ errors.first("password") }}</span>
            <!-- <input
              type="text"
              placeholder="请输入你的登录密码"
              v-model="password"
            />
            <span class="error-msg">错误提示信息</span> -->
          </div>

     确认密码要和密码一致

       <div class="content">
            <label>确认密码:</label>
            <input
              placeholder="请输入确认密码"
              v-model="password2"
              name="password2"
              v-validate="{ required: true, is: (password) }"
              :class="{ invalid: errors.has('password2') }"
            />
            <span class="error-msg">{{ errors.first("password2") }}</span>
    
            <!-- <input type="text" placeholder="请输入确认密码" v-model="password2" />
            <span class="error-msg">错误提示信息</span>   v-model="isCheck"   type="checkbox"-->
          </div>
    <div class="controls">
            <input
            type="checkbox"
              v-model="isCheck"
              name="isCheck"
          //自定义的agree规则
    v-validate="{ agree: true }" :class="{ invalid: errors.has('isCheck') }" /> <span>同意协议并注册《尚品汇用户协议》</span> <span class="error-msg">{{ errors.first("isCheck") }}</span> <!-- <input name="m1" type="checkbox" /> <span>同意协议并注册《尚品汇用户协议》</span> <span class="error-msg">错误提示信息</span> --> </div>

    ,此时,点击注册按钮,需要统一验证

    //点击完成注册,发送请求
        async wczc() {
          const success = await this.$validator.validateAll(); // 对所有表单项进行验证
          if (success) {
            let { mobile, password, code, password2 } = this;
            try {
              // if (mobile && code && password && password === password2) {
              let userInfo = {
                mobile,
                password,
                code,
              };
    
              await this.$store.dispatch("getreqRegister", userInfo);
              alert("注册成功,跳转到登录");
              this.$router.push("/login");
              // }
            } catch (error) {
              alert(error);
            }
          }
        },
  • 相关阅读:
    HTML 标签元素的 align 属性
    JS计算时间差值
    ICPC Southeastern Europe Contest 2019 BDFIJ
    ICPC Latin American Regional Contests 2019 EGIKLM
    UCF Local Programming Contest 2017 ABCDEFGHI
    Codeforces #631 Dreamoon Likes Coloring
    Problem Palindrome
    Problem Toki’s function
    UCF “Practice” Local Contest — Aug 25, 2018 Boots Exchange 水题
    UCF “Practice” Local Contest — Aug 25, 2018 Rummy Score
  • 原文地址:https://www.cnblogs.com/fsg6/p/13492672.html
Copyright © 2020-2023  润新知