打造vuecli3+element后台管理系统(二)调用接口功能完善,定义axios工具类

一个基于vuecli3和vue-admin-template改造的响应式后台管理系统javascript

后台系统少不了登录、注册、重置密码功能。虽然说是三个页面,可是样式风格统一,因此只用写一套样式。这块内容很少,咱来好好梳理,写一写。 先上效果图html

登录、注册、重置密码

1、使用mockjs

运用mock来模拟后台接口能更加便捷高效的进行前端开发,在这里我只是简单的模拟后台接口,返回一些简单的数据,更多功能能够参考mockjs官网,写的很详细的噢~前端

1.1 引入mockjs

npm install --save-dev mockjs
复制代码

1.2 在mainjs引入

import '@/assets/mock'
复制代码

1.3 定义mock文件

在assets文件夹下建立mock文件夹,并建立index.jsvue

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)
复制代码

2、Api地址的统必定义和处理

在assets目录下新建http文件夹,用来存放请求后端接口的一些配置文件。java

后台的接口地址须要一个文件进行统一的定义,而后全局声明以后,能够在项目里任意使用ios

2.1 定义接口url

根目录新建http/apiUrl.js,定义接口urlgit

/* 全局定义接口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了github

3、axios工具类的封装

总所周知,使用axios调用后台接口时,每次都须要写这么长串:npm

当业务逻辑复杂的时候,写起来会比较繁琐,后期维护更加不方便,每次都要定位到具体位置去一个个替换修改。因此在axios请求时再封装一层就显得尤其重要。element-ui

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) => {
        // 在这里写成功调用接口后的逻辑
    }
  })
复制代码

那复杂的用法这里贴个例子,完整代码能够去看看项目代码哦,地址我会贴在文章头。

好比说这个登录页面,要获取手机验证码:

点击发送验证码,开始一分钟倒计时并调用发送短信验证码的接口

<!--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
      })
    }
  })
}

复制代码
相关文章
相关标签/搜索