一,我的訂單獲取數據進行展示

在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;
        width: 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="width: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 vu3 前台_分頁

 

 

 將單元格合併後

element vu3 前台_分頁_02

 

 

二,使用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>

element vu3 前台_element vu3 前台_03

 

 

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

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')

 

沒有使用路由懶加載時,是整體的組件打包

element vu3 前台_element vu3 前台_04

 

 

使用路由懶加載後,加載一個路由組件,打包一個路由組件, 0.js,是組件打包的文件,路由的懶加載組件按照順序加載組件

element vu3 前台_分頁_05

 

 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: /^1\d{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: /^1\d{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: /^1\d{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);
        }
      }
    },