打造vuecli3+element後臺管理系統(二)呼叫介面功能完善,定義axios工具類

greenycaicai發表於2019-08-18

一個基於vuecli3和vue-admin-template改造的響應式後臺管理系統

後臺系統少不了登陸、註冊、重置密碼功能。雖說是三個頁面,但是樣式風格統一,所以只用寫一套樣式。這塊內容不多,我們來好好梳理,寫一寫。 先上效果圖

登陸、註冊、重置密碼

一、使用mockjs

運用mock來模擬後臺介面能更加便捷高效的進行前端開發,在這裡我只是簡單的模擬後臺介面,返回一些簡單的資料,更多功能可以參考mockjs官網,寫的很詳細的噢~

1.1 引入mockjs

npm install --save-dev mockjs
複製程式碼

1.2 在mainjs引入

import '@/assets/mock'
複製程式碼

1.3 定義mock檔案

在assets資料夾下建立mock資料夾,並建立index.js

import Mock from 'mockjs'
// 獲取 mock.Random 物件
const Random = Mock.Random

// mock一組資料
const loginData = () => {
  const data = {
    token: Random.string(10)
  }
  return {
    data: data,
    resultCode: 1,
    resultMessage: 'success'
  }
}
Mock.mock('/apiReplace/login', 'post', loginData)
Mock.mock('/apiReplace/loginByVin', 'post', loginData)
複製程式碼

二、Api地址的統一定義和處理

在assets目錄下新建http資料夾,用來存放請求後端介面的一些配置檔案。

後臺的介面地址需要一個檔案進行統一的定義,然後全域性宣告之後,可以在專案裡任意使用

2.1 定義介面url

根目錄新建http/apiUrl.js,定義介面url

/* 全域性定義介面url */

// host頭,這裡我們要使用代理,所以定義的字串apiReplace是用來進行反向代理時的標記字串。
const apiHost = '/apiReplace/'
// 密碼登入
const Login = `${apiHost}login`
// 簡訊登入
const LoginByVin = `${apiHost}loginByVin`

export default {
    Login,
    LoginByVin
}
複製程式碼

2.2 在mainjs引入

import Api from '@/assets/http/apiUrl'
Vue.prototype.API = Api
複製程式碼

這樣我們就能在專案裡通過this.API.xxx去獲取相應的介面url了

三、axios工具類的封裝

總所周知,使用axios呼叫後臺介面時,每次都需要寫這麼長串:

打造vuecli3+element後臺管理系統(二)呼叫介面功能完善,定義axios工具類

當業務邏輯複雜的時候,寫起來會比較繁瑣,後期維護更加不方便,每次都要定位到具體位置去一個個替換修改。所以在axios請求時再封裝一層就顯得尤為重要。

3.1 axios攔截器

我們經常需要在發起請求之前,修改請求頭或者是在介面請求成功之後進行資料的預處理,axios給我們提供了request和response的攔截器,讓我們可以在這裡頭進行一些業務操作。

在http資料夾下新建service.js檔案

  • 在檔案頭引入需要的模組並建立axios例項
import axios from 'axios'
import { Message } from 'element-ui'
import { getToken } from '@/assets/utils/token'
import router from '@/router'
import store from '@/store'

// 建立axios例項
const service = axios.create({
  baseURL: process.env.BASE_API, // api 的 base_url
  timeout: 10000 // 請求超時時間
})
複製程式碼
  • request攔截器,設定請求頭引數,如使用者標識token等
// request攔截器
service.interceptors.request.use(
  config => {
    // 在此處設定請求頭引數
    const token = getToken()
    if (token != null) {
      config.headers['Authorization'] = token
    }
    return config
  },
  error => {
    // Do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
複製程式碼
  • response 攔截器,請求介面得到相應後,需要進行一些預處理
service.interceptors.response.use(
  response => {
    return response // 返回請求成功結果,status=200
  },
  err => {
    // 請求失敗時,即status!=200
    if (err && err.response) {
      switch (err.response.status) {
        case 400:
          err.message = '錯誤請求'
          break
        case 401:
          err.message = '未授權,請重新登入'
          break
        case 403:
          err.message = '禁止訪問'
          break
        case 404:
          err.message = '請求錯誤,未找到該資源'
          break
        case 405:
          err.message = '請求方法未允許'
          break
        case 408:
          err.message = '請求超時'
          break
        case 413:
          err.message = '上傳檔案過大'
          break
        case 500:
          err.message = '伺服器端出錯'
          break
        case 501:
          err.message = '網路未實現'
          break
        case 502:
          err.message = '網路錯誤'
          break
        case 503:
          err.message = '服務不可用'
          break
        case 504:
          err.message = '網路超時'
          break
        case 505:
          err.message = 'http版本不支援該請求'
          break
        default:
          err.message = `連線錯誤,${err.response.msg}`
      }
    } else {
      err.message = '當前網路狀態不佳'
    }
    Message.closeAll()
    Message({
      message: err.message || '資料解析出錯',
      type: 'error',
      customClass: 'errorloginwidth',
      duration: '3000'
    })
    // 如果是token過期的狀況,退出登陸重定向到登陸頁
    if (err.response && err.response.status === 401) {
      store.dispatch('FedLogOut') // 前端登出,移除token
      router.replace({
        path: `/login?redirect=${window.location.href.split(/[#]/g)[1]}`
      })
    }
    return Promise.reject(err)
  }
)
複製程式碼
  • 匯出模組
export default service
複製程式碼

3.2 axios請求封裝

3.2-1 引入Qs庫來格式化資料
npm install --save-dev qs
複製程式碼
3.2-2 分別處理get和post請求,get和post請求攜帶引數的方式是不同的,所以要分開定義
3.2-3 提供請求成功和失敗後的回撥函式,以便頁面裡進行相關邏輯的書寫

直接貼上我定義的檔案:

這裡我只定義了一個基本的httpRequest方法,後期如果需要定義併發呼叫、或者其他情景下的方法,都可以自行增加。

/* 封裝axios請求 */
/* 用法示例:(*)為必須引數
  this.$request.httpRequest({
    headers: false, // 是否格式化引數
    (*)method: 'post', // 請求方式,post或get
    (*)url: this.API.ResetPassword, // 請求地址,請求地址的配置在@/api/apiUrl.js
    noLoading: true, // 是否顯示全域性Loading遮罩,預設每個請求都顯示遮罩,即預設不設定該引數。如果需要某個請求不加遮罩,就設定noLoading: true即可
    returnFullData: true, // 是否返回完整資料,例如介面返回的資料格式為{ code:0, data: [], meaasge:''},則預設請求成功之後的回撥函式的引數為data:[],如果設定returnFullData: true,則回撥引數為{ code:0, data: [], meaasge:''}
    hideErrorMsg: true, // 是否展示錯誤提示
    (*)params: {}, // 請求引數,object型別
    (*)success: (data) => { // 請求成功之後的回撥函式,data是回撥引數
      // 在這裡寫請求成功後的邏輯
    },
    error: (err) => { 請求不成功之後的回撥函式,data是回撥引數
      // 在這裡寫請求報錯後的邏輯
    }
  })
*/
import service from './service'
import { Message, Loading } from 'element-ui'
import Qs from 'qs'

function requestMethods(options) {
  return new Promise((resolve, reject) => {
    try {
      switch (options.method) {
        case 'post':
          if (options.headers) {
            resolve(
              service({
                url: options.url,
                method: 'post',
                data: options.params
              })
            )
          } else {
            resolve(
              service({
                url: options.url,
                method: 'post',
                data: Qs.stringify(options.params)
              })
            )
          }
          break
        case 'get':
          resolve(
            service({
              url: options.url,
              method: 'get',
              params: options.params
            })
          )
          break
        default: // 預設是get呼叫
          resolve(
            service({
              url: options.url,
              method: 'get',
              params: options.params
            })
          )
          break
      }
    } catch (e) {
      Message({
        message: 'HTTP請求方法出錯!',
        type: 'error',
        duration: 3 * 1000
      })
      reject('methods error!')
    }
  })
}

function httpRequest(options = {}) {
  let loading
  if (!options.noLoading) {
    // 啟用全域性loading
    loading = Loading.service({
      lock: true,
      text: '載入中...',
      spinner: 'el-icon-loading',
      background: 'rgba(0, 0, 0, 0.7)'
    })
  }

  requestMethods(options).then(response => {
    // 成功返回結果的邏輯。根據介面定義的資料返回格式 修改判斷條件
    const data = response.data
    if (data.resultCode === '1' || data.resultCode === 1) {
      // 成功
      const result = options.returnFullData ? data : data.data // 返回完整資料結構還是隻返回有效資料
      options.success(result)
    } else {
      if (!options.hideErrorMsg) {
        // 失敗
        let errorMsg = data.hasOwnProperty('resultMessage') ? data.resultMessage : '資料解析錯誤'
        switch (data.resultCode) {
          case '401':
            errorMsg = '暫無操作許可權'
            break
        }
        Message.closeAll()
        Message({
          message: errorMsg,
          type: 'error',
          customClass: 'errorloginwidth',
          duration: 3000
        })
      }
      options.error(data)
    }
    if (!options.noLoading) {
      // loading完畢
      loading.close()
    }
  }).catch(e => {
    options.error(e.response)
  })
}
export default {
  httpRequest
}
複製程式碼

3.3 在mainjs引入

import Request from '@/assets/http'
Vue.prototype.$request = Request
複製程式碼

這樣就可以在專案中通過this.$request去呼叫介面啦~ 基礎呼叫很簡單,只用醬紫:

 this.$request.httpRequest({
    url: this.API.SendSms,
    params: {},
    success: (data) => {
        // 在這裡寫成功呼叫介面後的邏輯
    }
  })
複製程式碼

那複雜的用法這裡貼個例子,完整程式碼可以去看看專案程式碼哦,地址我會貼在文章頭。

比如說這個登陸頁面,要獲取手機驗證碼:

打造vuecli3+element後臺管理系統(二)呼叫介面功能完善,定義axios工具類

點選傳送驗證碼,開始一分鐘倒數計時並呼叫傳送簡訊驗證碼的介面

<!--html部分-->
<el-form-item prop="code" class="login-input-item">
    <span class="svg-container">
        <svg-icon icon-class="password" />
    </span>
    <el-input
        v-model="loginForm.code"
        autocomplete="off"
        type="number"
        name="code"
        placeholder="驗證碼"
        maxlength="4"
        style="padding-left: 45px"
        @keyup.enter.native="handleLogin"
    />
    <span
        :style="{ cursor: isOvertime ? 'default' : 'pointer'}"
        class="code"
        @click="sendMessage">
            {{ word }}
    </span>
</el-form-item>

// js部分
sendMessage() {
  if (this.isOvertime) {
    return false // 還在倒數計時,不往下執行
  }
  const params = {
    'phone': this.loginForm.phoneNumber
  }
  if (!params.phone) {
    this.$message.closeAll()
    this.$message.error('請先輸入手機號碼')
    return
  }
  if (!isvalidPhoneNumber(params.phone)) {
    this.$message.closeAll()
    this.$message.error('手機號格式不正確')
    return
  }
  this.loading = true
  this.$request.httpRequest({
    method: 'post',
    url: this.API.SendSms,
    returnFullData: true,
    noLoading: true,
    hideErrorMsg: true,
    params: params,
    success: (data) => {
      this.loading = false
      this.$message.closeAll()
      this.$message.success('驗證碼傳送成功,請留意手機簡訊')
      const sendTimer = setInterval(() => {
        this.isOvertime = true
        this.word = `${this.time}s後重新獲取`
        this.time--
        if (this.time <= 0) {
          this.isOvertime = false
          this.time = 60
          clearInterval(sendTimer)
          this.word = '獲取驗證碼'
        }
      }, 1000)
    },
    error: (e) => {
      this.loading = false
      const errorMsg = e.hasOwnProperty('resultMessage') ? e.resultMessage : '獲取驗證碼失敗'
      this.$message({
        message: errorMsg,
        type: 'error',
        customClass: 'errorloginwidth',
        duration: 3000
      })
    }
  })
}

複製程式碼

相關文章