【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页

教程目录
一:《【vue create】一.使用vue creat搭建项目》


根据前文《【vue create】一.使用vue creat搭建项目》搭建好后,进行下面的操作
1.配置Esline
2.安装less
3.安装ant-design-vue(设置公共样式)
4.安装nprogress
5.配置环境变量env文件
6.配置axios
7.配置vuex状态管理器的store
8.配置Router、路由守卫以及登录页
9.配置路由守卫
10.实现登录页
11.注意点


1.配置Esline

在.eslintrc.js文件里代码替换如下

module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ['plugin:vue/strongly-recommended', '@vue/prettier'],
  rules: {
    'no-console': 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    //在rules中添加自定义规则
    //关闭组件命名规则
    'vue/multi-word-component-names': 'off',
    'generator-star-spacing': 'off',
    'no-mixed-operators': 0,
    'vue/max-attributes-per-line': [
      2,
      {
        singleline: 5,
        multiline: {
          max: 1,
          allowFirstLine: false,
        },
      },
    ],
    'vue/attribute-hyphenation': 0,
    'vue/html-self-closing': 0,
    'vue/component-name-in-template-casing': 0,
    'vue/html-closing-bracket-spacing': 0,
    'vue/singleline-html-element-content-newline': 0,
    'vue/no-unused-components': 0,
    'vue/multiline-html-element-content-newline': 0,
    'vue/no-use-v-if-with-v-for': 0,
    'vue/html-closing-bracket-newline': 0,
    'vue/no-parsing-error': 0,
    'prettier/prettier': 0,
    'vue/no-template-shadow': 0,
    'prefer-const': 0,
    'no-tabs': 0,
    'vue/max-attributes-per-line': 0,
    quotes: [2, 'single', { avoidEscape: true, allowTemplateLiterals: true }], // avoidEscape: true 允许字符串使用单引号或双引号,只要字符串包含必须以其他方式转义的引号 ;allowTemplateLiterals: true 允许字符串使用反引号,
    semi: [
      2,
      'never',
      {
        beforeStatementContinuationChars: 'never',
      },
    ], //“beforeStatementContinuationChars”: “never” 如果该语句不会因为ASI而带来风险,那么即使它的下一行语句以 [,(,/,+,或 - 开头,也不允许在语句末尾添加分号
    'no-delete-var': 2,
    'prefer-const': [
      2,
      {
        ignoreReadBeforeAssign: false,
      },
    ],
    'template-curly-spacing': 'off',
    indent: 'off',
  },
  parserOptions: {
    parser: 'babel-eslint',
  },
  overrides: [
    {
      files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
      env: {
        jest: true,
      },
    },
  ],
}



然后在终端运行如下代码安装插件

yarn add @vue/eslint-config-prettier@6.0.0  -D
yarn add eslint-plugin-prettier@3.3.1 -D
yarn add babel-eslint@10.1.0 -D
yarn add prettier@2.2.1 -D

在根目录新建.prettierrc文件,代码如下

{
    "printWidth": 120,
    "semi": false,
    "singleQuote": true,
    "prettier.spaceBeforeFunctionParen": true
}
    

【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第1张图片
vscode编辑器扩展里需要安装prettier - Code formatter插件
【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第2张图片

然后重新运行项目yarn serve后会有提示报错(只需点进报错的文件里,按Ctrl+S按键再次保存则错误解决)
【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第3张图片

【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第4张图片


2.引入less

1.在终端输入命令

//默认情况下安装less-loader不指定版本号,会导致版本过高跟less不匹配会出问题
yarn add [email protected] --save
yarn add [email protected] --save

3.安装ant-design-vue(设置公共样式)

在终端输入命令

//带@是指定版本号
yarn add [email protected]   

在src\core文件夹下新建ant-design.js文件,文件代码如下(设置为按需引入组件

// 按需加载ant-design
import Vue from 'vue'

// base library
import {
  message,
  notification,
  Affix,
  Anchor,
  AutoComplete,
  Alert,
  Avatar,
  BackTop,
  Badge,
  Breadcrumb,
  Button,
  Calendar,
  Card,
  Collapse,
  Carousel,
  Cascader,
  Checkbox,
  Col,
  DatePicker,
  Divider,
  Dropdown,
  Form,
  FormModel,
  Icon,
  Input,
  InputNumber,
  Layout,
  List,
  LocaleProvider,
  Menu,
  Mentions,
  Modal,
  Pagination,
  Popconfirm,
  Popover,
  Progress,
  Radio,
  Rate,
  Row,
  Select,
  Slider,
  Spin,
  Statistic,
  Steps,
  Switch,
  Table,
  Transfer,
  Tree,
  TreeSelect,
  Tabs,
  Tag,
  TimePicker,
  Timeline,
  Tooltip,
  Upload,
  Drawer,
  Skeleton,
  Comment,
  // ColorPicker,
  ConfigProvider,
  Empty,
  Result,
  Descriptions,
  PageHeader,
  Space,
} from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
// 全局 message 配置
message.config({
  top: `240px`,
  duration: 2,
  maxCount: 1,
})

Vue.use(Affix)
Vue.use(Anchor)
Vue.use(AutoComplete)
Vue.use(Calendar)
Vue.use(LocaleProvider)
Vue.use(Mentions)
Vue.use(Slider)
Vue.use(Statistic)
Vue.use(Transfer)
Vue.use(TreeSelect)
Vue.use(Timeline)
Vue.use(Skeleton)
Vue.use(Comment)
Vue.use(Descriptions)
Vue.use(PageHeader)

Vue.use(Cascader)
Vue.use(ConfigProvider)
Vue.use(Layout)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Button)
Vue.use(Switch)
Vue.use(Radio)
Vue.use(Checkbox)
Vue.use(Select)
Vue.use(Card)
Vue.use(Form)
Vue.use(FormModel)
Vue.use(Row)
Vue.use(Col)
Vue.use(Modal)
Vue.use(Table)
Vue.use(Tabs)
Vue.use(Icon)
Vue.use(Badge)
Vue.use(Popover)
Vue.use(Dropdown)
Vue.use(List)
Vue.use(Avatar)
Vue.use(Breadcrumb)
Vue.use(Steps)
Vue.use(Spin)
Vue.use(Menu)
Vue.use(Drawer)
Vue.use(Tooltip)
Vue.use(Alert)
Vue.use(Tag)
Vue.use(Divider)
Vue.use(DatePicker)
Vue.use(TimePicker)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Result)
Vue.use(Space)
Vue.use(Carousel)
Vue.use(BackTop)
Vue.use(Tree)
Vue.use(Pagination)
Vue.use(Popconfirm)
Vue.use(Steps)
Vue.use(Rate)
Vue.use(Empty)
Vue.use(Collapse)

Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message
Vue.prototype.$notification = notification
Vue.prototype.$info = Modal.info
Vue.prototype.$success = Modal.success
Vue.prototype.$error = Modal.error
Vue.prototype.$warning = Modal.warning

process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] NOTICE: Antd use lazy-load.')


在babel.config.js文件里加入如下代码

  plugins: [
    [
      'import',
      {
        libraryName: 'ant-design-vue',
        libraryDirectory: 'es',
        style: 'css',
      },
    ],
  ],

【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第5张图片
在终端执行命令,安装babel-plugin-import组件(按需加载组件)

yarn add [email protected]

在src\styles文件夹下新建index.less文件,代码设置如下

// 覆盖 ant-design 样式
@import './ant-design-vue.less';
// 进度条样式
@import './nprogress.less';
// 公共样式
@import './common.less';

在src\styles文件夹下新建ant-design-vue.less文件,代码设置如下

// 列表
.table-wrapper {
  height: 100%;
  .ant-table {
    .ant-table-body {
      overflow: auto !important;
    }
  }
}

.ant-table-wrapper {
  .ant-table-pagination {
    &.ant-pagination {
      float: none;
      text-align: center;
    }
  }

  .ant-table-header {
    overflow: auto !important;
  }
}

// form 布局
.c-form {
  .ant-form-item {
    display: flex;
    .ant-form-item-label {
      min-width: 80px;
    }
    .ant-form-item-control-wrapper {
      flex: 1;
    }
    &.item-block {
      display: block;
    }
  }
}

.ant-table {
  .ant-table-tbody {
    .ant-table-row-cell-break-word {
      padding-left: 0;
      padding-right: 0;
    }
  }
}

在src\styles文件夹下新建common.less文件,代码设置如下

// common
.flex {
  display: flex;
}
.flex-1 {
  flex: 1;
}
.block{
  display: block;
}
.between-center{
  justify-content: space-between;
  align-items: center;
}

.wrap{
  flex-wrap: wrap;
}

.hidden {
  overflow: hidden;
}

.no-wrap {
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}

.ellipsis-multiple {
  word-break: break-all;
  text-overflow: ellipsis;
  display: -webkit-box; 
  -webkit-box-orient: vertical; 
  // -webkit-line-clamp: 2; 
  overflow: hidden; 
}

.text-center {
  text-align: center;
}


.around-start {
  justify-content: space-around;
  align-items: flex-start;

}

.between-start {
  justify-content: space-between;
  align-items: flex-start;
}


.center-center {
  justify-content: center;
  align-items: center;
}

.around-center {
  justify-content: space-around;
  align-items: center;
}

.start-center {
  justify-content: flex-start;
  align-items: center;
}

.end-center {
  justify-content: flex-end;
  align-items: center;
}

.start-end {
  justify-content: flex-start;
  align-items: flex-end;
}

.evenly-center {
  justify-content: space-between;
  align-items: center;
  &::after,
  &::before {
    content:"";
    display: block;
  }
}

.cursor-pointer {
  cursor: pointer;
}

.not-select{
 user-select:none;
}

.fl {
  float: left;
}

.fr {
  float: right;
}

.mt-20 {
  margin-top: 20px;
}

.mt-5 {
  margin-top: 5px;
}

.mt-30 {
  margin-top: 30px;
}

.mr-1 {
  margin-right: 1px;
}
.mr-2 {
  margin-right: 2px;
}
.mr-3 {
  margin-right: 3px;
}
.mr-4 {
  margin-right: 4px;
}
.mr-5 {
  margin-right: 5px;
}
.mr-6 {
  margin-right: 6px;
}
.mr-7 {
  margin-right: 7px;
}
.mr-8 {
  margin-right: 8px;
}
.mr-9 {
  margin-right: 9px;
}
.mr-10 {
  margin-right: 10px;
}
.mr-11 {
  margin-right: 11px;
}
.mr-12 {
  margin-right: 12px;
}
.mr-13 {
  margin-right: 13px;
}
.mr-14 {
  margin-right: 14px;
}
.mr-15 {
  margin-right: 15px;
}
.mr-16 {
  margin-right: 16px;
}
.mr-17 {
  margin-right: 17px;
}
.mr-18 {
  margin-right: 18px;
}
.mr-19 {
  margin-right: 19px;
}
.mr-20 {
  margin-right: 20px;
}

.mb-1 {
  margin-bottom: 1px;
}
.mb-2 {
  margin-bottom: 2px;
}
.mb-3 {
  margin-bottom: 3px;
}
.mb-4 {
  margin-bottom: 4px;
}
.mb-5 {
  margin-bottom: 5px;
}

.mb-10 {
  margin-bottom: 10px;
}

.my-5 {
  margin-top: 5px;
  margin-bottom: 5px;
}

.ml-1 {
  margin-left: 1px;
}
.ml-2 {
  margin-left: 2px;
}
.ml-3 {
  margin-left: 3px;
}
.ml-4 {
  margin-left: 4px;
}
.ml-5 {
  margin-left: 5px;
}
.ml-6 {
  margin-left: 6px;
}
.ml-7 {
  margin-left: 7px;
}
.ml-8 {
  margin-left: 8px;
}
.ml-9 {
  margin-left: 9px;
}
.ml-10 {
  margin-left: 10px;
}
.ml-11 {
  margin-left: 11px;
}
.ml-12 {
  margin-left: 12px;
}
.ml-13 {
  margin-left: 13px;
}
.ml-14 {
  margin-left: 14px;
}
.ml-15 {
  margin-left: 15px;
}
.ml-16 {
  margin-left: 16px;
}
.ml-17 {
  margin-left: 17px;
}
.ml-18 {
  margin-left: 18px;
}


.fz-14 {
  font-size: 14px;
}

.pd-10 {
  padding: 10px;
}

.ml-53{
  margin-left:53px;
  }

在src\styles文件夹下新建nprogress.less文件,代码设置如下

#nprogress .bar {
  height: 10rpx !important;        //高度
  
  background: #d64141 !important;  //颜色
} 

然后在src\main.js文件加入如下代码

import './core/ant-design'
import './styles/index.less'


4.安装nprogress

nprogress一个第三方的进度条库,在后面router跳转的时会使用到
在控制台终端输入如下代码回车,安装nprogress插件

yarn add nprogress@0.2.0

5.配置环境变量env文件

我这里只使用开发环境和生产环境,根据实际业务可以新建多个env文件
在根目录下新建.env文件,代码如下(全局默认配置文件,不论什么环境都会加载并合并)

# 后端 api 代理路径
VUE_APP_API_BASE_URL = '/api'

VUE_APP_MINIO_PROXY_KEY=/micro-dev

在根目录下新建.env.dev文件,代码如下(开发环境下的配置文件,仅在开发环境加载)

# 环境变量
NODE_ENV=development
VUE_APP_ENV = 'dev'

# 项目目录路径
VUE_APP_PUBLIC_PATH=/

# 开发环境
 VUE_APP_PROXY_API_URL = 'http://10.88.96.35:31325'

在根目录下新建.env.prod文件,代码如下(生产环境下的配置文件,仅在生产环境加载)

# 环境变量
NODE_ENV=production
VUE_APP_ENV = 'prod'

# 项目目录路径
VUE_APP_PUBLIC_PATH=/

# 生产环境
VUE_APP_PROXY_API_URL = 'http://120.46.172.49:31400'

【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第6张图片
更改package.json文件

  "scripts": {
    "serve": "vue-cli-service serve --mode dev --open",
    "build": "vue-cli-service build --mode dev",
    "lint": "vue-cli-service lint",
    "build:prod": "vue-cli-service build --mode prod",
    "build-prod": "vue-cli-service build --mode prod",
    "build-dev": "vue-cli-service build --mode dev"
  },

【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第7张图片
配置vue.config.js文件

const vueConfig = {
  transpileDependencies: true,
  //开启esline校验
  lintOnSave: true,
  //配置api拦截规则
  devServer: {
    // development server port 8000
    port: 8000,
    https: false,
    // If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
    allowedHosts: 'all', // vue项目提示Invalid Host header 配置是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查
    proxy: {
      '/api': {
        target: process.env.VUE_APP_PROXY_API_URL,
        ws: true, // 支持websocket
        changeOrigin: true, // 是否跨域
        secure: false, // 如果是https接口,需要配置这个参数
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
}

module.exports = vueConfig



6.配置axios

在终端执行命令

yarn add axios@0.21.1

在src\utils文件夹下新建request.js文件

import axios from 'axios'
import store from '@/store'
import notification from 'ant-design-vue/es/notification'
import { getToken, setToken } from './auth'
import { checkLogin } from '@/utils/authCenter'
// 创建axios实例
const request = axios.create({
  // baseURL: process.env.VUE_APP_API_BASE_URL,
  timeout: 30000, // 请求超时时间
})

// 异常处理
const errorHanlder = (error) => {
  if (error.response) {
    const data = error.response.data
    // 获取tooken
    const token = getToken()

    if (error.response.status === 403) {
      notification.error({
        message: 'Forbidden',
        description: data.message,
      })
    }

    if (error.response.status === 401) {
      //注销登录
      checkLogin()
    }
  }
  return Promise.reject(error)
}

// requets 拦截
request.interceptors.request.use((config) => {
  const token = getToken()
  // 请求头携带token
  if (token) {
    config.headers['Authorization'] = token
  }

  return config
}, errorHanlder)

// response拦截
request.interceptors.response.use((response) => {
  // 状态码
  const { status, message } = response.data // 根据系统指定的status值
  const oldToken = store.state.user.token
  const newToken = response.config.headers['Authorization']
  if (!oldToken && newToken) {
    setToken(newToken)
    store.commit('user/SET_TOKEN', newToken)
  }
  //blob文件类型的
  if (response.request.responseType == 'blob') {
    return response
  }
  //放开status指定的状态的返回值
  if (status === 0) {
    return response.data
  } else if (status === 101) {
    return response.data
  } else {
    return response.data
  }
  // 其他状态码处理
}, errorHanlder)

export default request

安装js-cookie和jwt-decode(用于解析token以及存储数据到cookie)

yarn add js-cookie@2.2.1
yarn add jwt-decode@3.1.2

在src\utils文件夹下新建auth.js文件(用于操作token)

import Cookies from 'js-cookie'
import jwtDecode from 'jwt-decode'

let TokenKey = ''
if (process.env.VUE_APP_ENV === 'prod') {
  // 生产
  TokenKey = 'user-token'
} else {
  // 开发
  TokenKey = 'dev-user-token'
}

export function getToken() {
  return Cookies.get(TokenKey) || sessionStorage.getItem('token')
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

export function getCurrentUser() {
  const access_token = Cookies.get(TokenKey)

  if (access_token != null) {
    const decode = jwtDecode(access_token)

    return decode.username
  }
}

/**
 * 从token获取用户信息
 * @returns
 */
export function getUserInfo() {
  const access_token = Cookies.get(TokenKey)
  if (access_token != null) {
    const decode = jwtDecode(access_token)

    return decode
  }
}

在src\utils文件夹下新建authCenter.js文件,代码如下(用于解析token,跳转登录页)

import store from '@/store'
import { removeToken, setToken, getToken } from '@/utils/auth'

//跳转登录
export const checkLogin = () => {
  this.$router.push('/login')
}

//注销登录
export const logout = (appType) => {
  store.commit('user/SET_TOKEN', '') // 清除token
  store.commit('user/SET_INFO', {}) // 清除用户信息
  var token = escape(getToken())
  token = token.replace('%20', ' ')
  checkLogin()
  removeToken()
}

//解析url链接中的token值
export function getArgumentsToken() {
  if (getQueryVariable('token')) {
    var token = decodeURIComponent(getQueryVariable('token'))
    store.commit('user/SET_TOKEN', token)
    setToken(token)
    return token
  } else {
    return false
  }
}

// 获取url中数组的参数1
export function getQueryVariable(variable) {
  // 从?开始获取后面的所有数据`
  var query = window.location.search.substring(1)
  // 从字符串&开始分隔成数组split
  var vars = query.split('&')
  // 遍历该数组
  for (var i = 0; i < vars.length; i++) {
    // 从等号部分分割成字符
    var pair = vars[i].split('=')
    // 如果第一个元素等于 传进来的参的话 就输出第二个元素
    if (pair[0] == variable) {
      return pair[1]
    }
  }
  return false
}

在src\api文件夹下新建base.js文件,代码如下

/**
 * 接口域名的管理
 */
const base = {
  api: '/api',
}

export default base

在src\api文件夹下新建user.js文件,代码如下

import request from '@/utils/request'
import base from '@/api/base'

const userApi = {
  personalInfo: base.api + '/family/user/personal/info', // 查询个人信息
  loginPwd: base.api + '/family/auth/api/login/pwd', // 账号密码登录
}

/**
 * 获取用户信息
 * @returns
 */
export const getUserInfo = () => {
  return request({
    url: userApi.personalInfo,
    method: 'get',
  })
}

/**
 * 账号密码登录
 * @param {*} data
 * @returns
 */
export const login = (data) => {
  return request({
    url: userApi.loginPwd,
    method: 'post',
    data,
  })
}


7.配置vuex状态管理器的store

在src\store文件夹下的index.js,代码如下

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'

Vue.use(Vuex)

const modulesFiles = require.context(`./modules`, true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters,
})

export default store

在src\store文件夹下新建getters.js文件,代码如下(存储数据)

const getters = {
  //
  token: (state) => state.user.token,
  userInfo: (state) => state.user.info,
}

export default getters

在src\store\modules文件夹下新建user.js文件(配置存储的数据),代码如下

// 用户相关
import { getUserInfo, login } from '@/api/user'

import { getToken, setToken, removeToken } from '@/utils/auth'
import router from '@/router'

const state = {
  token: getToken(),
  info: {}, // 个人信息
}
const getters = {}

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_INFO: (state, info) => {
    state.info = info
  },
}

const actions = {
  // 登录
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password })
        .then((response) => {
          const { data, status } = response
          //status为0登录成功
          if (status === 0) {
            commit('SET_TOKEN', data)
            setToken(data)
            resolve()
          } else {
            reject(response)
          }
        })
        .catch((error) => {
          reject(error)
        })
    })
  },

  // 登出
  logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      // 调取登出接口
      commit('SET_TOKEN', '') // 清除token
      commit('SET_INFO', {}) // 清除用户信息
      removeToken()
      resolve()
    })
  },
  // 获取用户信息
  getInfo({ commit, dispatch }) {
    return new Promise((resolve, reject) => {
      // 调取接口获取用户信息
      getUserInfo()
        .then((response) => {
          const { data } = response
          const localUserList = localStorage.getItem('localUserList')
            ? JSON.parse(localStorage.getItem('localUserList'))
            : []
          const index = localUserList.findIndex((item) => item.userId === data.userId)

          if (index < 0) {
            localUserList.push(data)
            localStorage.setItem('localUserList', JSON.stringify(localUserList))
            if (localUserList.length >= 2) {
              localStorage.removeItem('localUserList')
              router.push({ path: '/home' })
            }
          }

          commit('SET_INFO', data)
          resolve(response)
        })
        .catch((error) => {
          reject(error)
        })
    })
  },
}

export default {
  namespaced: true, // 开启命名空间模块
  state,
  getters,
  mutations,
  actions,
}



8.配置Router

在src\router文件夹下index.js文件,配置代码如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import { constantRouteMap } from '@/config/router.config'

// hack router push callback
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch((err) => err)
}

Vue.use(VueRouter)

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: constantRouteMap,
})

export default router

然后新建src\config文件夹下router.config.js文件

const RouteView = {
  name: 'RouteView',
  render: (h) => h('router-view'),
}

export const asyncRouterMap = [
  {
    path: '/',
    name: 'index',
    component: () => import('@/pages/home/home.vue'),
    meta: { title: '首页' },
    redirect: '/home',
    children: [
      {
        path: '/home',
        name: 'home',
        component: () => import(/* webpackChunkName: "home" */ '@/pages/home/home.vue'),
        meta: {
          title: '首页',
          current: '/home',
        },
      },
    ],
  },
]

export const constantRouteMap = [
  {
    path: '/login',
    component: RouteView,
    meta: { title: '登录' },
    redirect: '/login',
    children: [
      {
        path: '/login',
        name: 'login',
        component: () => import(/* webpackChunkName: "user" */ '@/views/Login'),
        meta: { title: '登录' },
      },
    ],
  },
  ...asyncRouterMap,
]


在main.js文件里加入如下代码

//切换页面让滚动条回到顶部
router.afterEach((to, from, next) => {
  window.scrollTo(0, 0)
})

9.配置路由守卫

在src文件夹下新建permission.js文件

import router from './router'
import { getToken } from '@/utils/auth'
import NProgress from 'nprogress'
import store from './store'
import notification from 'ant-design-vue/es/notification'
import { checkLogin, getArgumentsToken, logout, getQueryVariable } from './utils/authCenter'
NProgress.configure({ showSpinner: false })

// 路由白名单,不需要登陆
const allowRouteList = ['home', 'login']

const loginRoutePath = '/login'
const defaultRoutePath = '/home'

router.beforeEach((to, from, next) => {
  NProgress.start()
  function checkToken() {
    if (to.path === loginRoutePath) {
      next({ path: defaultRoutePath })
      NProgress.done()
    } else {
      // user info is null

      if (Object.values(store.getters.userInfo).length === 0) {
        store
          .dispatch('user/getInfo')
          .then(() => {
            const redirect = decodeURIComponent(from.query.redirect || to.path)
            if (to.path === redirect) {
              // set the replace: true so the navigation will not leave a history record
              next({ ...to, replace: true })
            } else {
              // 跳转到目的路由
              next({ path: redirect })
            }
          })
          .catch(() => {
            notification.error({
              message: '错误',
              description: '请求用户信息失败,请重试!',
            })
            // 失败时,获取用户信息失败时,调用登出,来清空历史保留信息
            store.dispatch('user/logout').then(() => {
              logout()
            })
          })
      } else {
        next()
      }
    }
  }

  var loginStatus = getArgumentsToken()
  if (getToken()) {
    checkToken()
  } else {
    // 免登录名单

    if (allowRouteList.includes(to.name)) {
      var isToken = getQueryVariable('isToken')
      if (!loginStatus && isToken != 0) {
        checkLogin()
      } else {
        next()
      }
    } else {
      if (loginStatus) {
        checkToken()
      } else {
        checkLogin()
      }

      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})


在main.js文件加入路由守卫配置,代码如下

import './permission'

10.实现登录页

删除项目自带的AboutView.vue和HomeView.vue文件

在src\App.vue文件里,代码设置如下(设置高度自适应)

<template>
  <div id="app">
    <router-view />
  </div>
</template>
<script>
export default {
  name: 'App',
  data() {
    return {}
  },
}
</script>


<style lang="less" scoped>
html,
body,
#app {
  height: 100%;
}
</style>

在src\pages\home文件夹下新建home.vue文件
home.vue文件代码如下

<template>
  <div>
    <h3>这里是首页</h3>
    <a-button @click="toLogin">跳转登录页</a-button>
  </div>
</template>

<script>
export default {
  name: 'Home',
  methods: {
    toLogin() {
      this.$router.push('/login')
    },
  },
}
</script>

在src\utils文件夹下新建公共封装方法util.js文件,代码如下

export const timeFix = () => {
  const time = new Date()
  const hour = time.getHours()
  return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好'
}

在src\views文件夹下新建Login.vue文件

<template>
  <div class="login">
    <div class="login-content">
      <div class="login-title">账号登录</div>
      <a-form class="login-form" ref="formLogin" :form="form" @submit="handleSubmit">
        <a-form-item>
          <a-input
            size="large"
            type="text"
            placeholder="账号"
            autocomplete
            v-decorator="[
              'username',
              {
                rules: [{ required: true, message: '请输入帐户名' }, { validator: handleUsernameOrEmail }],
                validateTrigger: 'change',
              },
            ]"
          >
          </a-input>
        </a-form-item>

        <a-form-item>
          <a-input-password
            size="large"
            placeholder="密码"
            v-decorator="[
              'password',
              { rules: [{ required: true, message: '请输入密码!' }], validateTrigger: 'blur' },
            ]"
            autocomplete
          >
          </a-input-password>
        </a-form-item>

        <a-form-item style="margin-top: 30px; margin-bottom: 16px">
          <a-button
            size="large"
            type="primary"
            htmlType="submit"
            class="login-button"
            :loading="state.loginBtn"
            :disabled="state.loginBtn"
          >
            登录
          </a-button>
          <a-button size="large" type="" class="forget-button" @click="forgetPwd()"> 忘记密码?</a-button>
          <a-button size="large" type="" class="reg-button" @click="register()"> 注册 </a-button>
        </a-form-item>
      </a-form>
    </div>
  </div>
</template>

<script>
import { timeFix } from '@/utils/util'
export default {
  data() {
    return {
      form: this.$form.createForm(this),
      state: {
        time: 60,
        loginBtn: false,
        loginType: 0,
        smsSendBtn: false,
      },
      loginFlg: 0, //登录状态
    }
  },
  created() {
    this.loginFlg = localStorage.getItem('loginFlg')
  },
  methods: {
    // 注册
    register() {
      // 跳转 注册页面
    },
    // 忘记密码
    forgetPwd() {
      // 跳转 忘记密码
    },
    //账号正则校验
    handleUsernameOrEmail(rule, value, callback) {
      const { state } = this
      const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/
      if (regex.test(value)) {
        state.loginType = 0
      } else {
        state.loginType = 1
      }
      callback()
    },
    //登录按钮
    handleSubmit(e) {
      e.preventDefault()
      const {
        form: { validateFields },
        state,
      } = this
      state.loginBtn = true

      const validateFieldsKey = ['username', 'password']
      validateFields(validateFieldsKey, { force: true }, (err, values) => {
        if (!err) {
          if (this.loginFlg == 1) {
            state.loginBtn = true
          } else {
            const loginParams = {
              username: values.username,
              password: values.password,
            }
            this.$store
              .dispatch('user/login', loginParams)
              .then(() => {
                this.loginFlg = 0
                localStorage.setItem('loginFlg', 0)
                this.loginSuccess()
                state.loginBtn = false
              })
              .catch((error) => {
                //错误提示
                this.$notification.error({
                  top: '100px',
                  message: '提示',
                  description: error.message,
                })
                state.loginBtn = false
              })
          }
        } else {
          state.loginBtn = false
        }
      })
    },
    //提示
    loginSuccess() {
      this.$router.push({ path: '/' })
      // 延迟 1 秒显示欢迎信息
      setTimeout(() => {
        this.$notification.success({
          top: '100px',
          message: '欢迎',
          description: `${timeFix()},欢迎回来`,
        })
      }, 1000)
    },
  },
}
</script>

<style lang="less" scoped>
.login {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  .login-content {
    border: 1px solid #d9d9d9;
    border-radius: 12px;
    width: 457px;
    height: 420px;
    transition: all 0.3s;
    transform: translateX(0);
    .login-title {
      margin-bottom: 25px;
      font-size: 36px;
      text-align: center;
      font-weight: bold;
      color: #000;
    }
    .login-form {
      /deep/.ant-form-item {
        margin-bottom: 45px;
        .ant-form-item-control {
          width: 407px;
          margin: 0 auto;
          .ant-input {
            height: 70px;
            font-size: 26px;
            color: #000;
            background-color: transparent;
            border: 2px solid #d9d9d9;
            opacity: 1;
            border-radius: 10px;
            &:-internal-autofill-previewed,
            &:-internal-autofill-selected {
              -webkit-text-fill-color: #000 !important;
              transition: background-color 5000s ease-in-out 0s !important;
            }
          }
        }
      }
    }
    .login-button {
      margin-top: 14px;
      width: 164px;
      height: 60px;
      background-color: #3a5dea;
      border-color: #3a5dea;
      border-radius: 30px;
      font-size: 24px;
      margin-right: 15px;
    }
    .forget-button,
    .reg-button {
      border: none;
      background-color: rgba(0, 0, 0, 0);
      color: #3a5dea;
      height: 20px;
      position: relative;
      top: 10px;
    }
  }
}
</style>

登录页:
【vue create】二.配置Esline、less、nprogress、ant-design-vue、环境变量env、axios、vuex、Router、路由守卫以及登录页_第8张图片


11.注意点

1.src\api\user.js接口文件、src\store\modules\user.js数据管理文件、src\permission.js导航守卫文件、env三个文件、vue.config.js文件请根据实际的业务来更改
2.上面10步全部操作完成后,请重新yarn serve运行
3./family/auth/api/login/pwd接口返回值
失败:

{
    "status": 1,
    "message": "用户名或者密码不正确!",
    "traceId": "0e87ddcd80d5497485ebba38244e55e5.81.16784273719495701",
    "data": 1
}

成功:

{
  "status": 0,
  "message": "success",
  "traceId": "0e87ddcd80d5497485ebba38244e55e5.81.16784273719495701",
  "data": '这里是后端返回的token值'
}

4./family/user/personal/info接口入参是headers里带token,返回值

{
  "status": 0,
  "message": "success",
  "traceId": "0e87ddcd80d5497485ebba38244e55e5.81.16784273719495701",
  "data": {
    userId:'用户的id',
    userName:'用户的名称',
  }
}

你可能感兴趣的:(vue,vue.js,less,javascript,ant-design-vue,vue-router)