PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器

PC端面经后台管理项目(1)-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器处理token过期

这一次的项目是基于上一次移动端的面经项目的后台管理部分,与移动端的不同,移动端主要在于对vant组件库的熟悉和学习,pc端则是在于对element-ui的熟悉和学习,整体来说,和移动端面经项目有很多的相似之处,能够从上次的项目中借鉴经验,也能借此来加深对这套架子的熟悉。

vant 项目的定位:

  • 熟悉vant, 熟悉架子(各个目录),熟悉模块 api request storage

element 项目的定位:

  • 熟悉element => 表单组件,表单校验,表格组件
  • 巩固架子,巩固模块封装

接口文档: https://www.apifox.cn/apidoc/project-934563/api-19465917

接口根路径: http://interview-api-t.itheima.net/

本项目的技术栈 本项目技术栈基于 ES2015+、vue2、vuex3、vue-router3 、vue-cli5 、axios 和 element-ui

页面展示

登录页

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第1张图片

首页

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第2张图片

面经管理 - 列表展示

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第3张图片

面经管理 - 预览效果

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第4张图片

面经管理 - 删除功能

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第5张图片

面经管理 - 添加功能

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第6张图片

面经管理 - 修改功能

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第7张图片

创建项目

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第8张图片

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第9张图片

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第10张图片

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第11张图片

image-20220617061002551

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第12张图片

image-20220617061038189

image-20220617061103526

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第13张图片

sass/scss 语法说明

less sass stylus 都是 css 预处理器,语法上稍有差异,作用一样
都是让 css,增强能力,具备变量,函数.. 的能力

sass的语法两种语法 .sass(旧) .scss(新)
1 .sass 和 .stylus 语法很像 (了解)
  要求省略 {} 和 分号, 缩进表示嵌套
  
2 .scss 和 .less   语法很像, 都支持嵌套, 变量...
  scss 声明变量:$变量名
  less 声明变量: @变量名

##调整项目目录

默认生成的目录结构不满足我们的开发需求,所以这里需要做一些自定义改动。主要是两个工作:

  • 删除初始化的默认文件
  • 修改剩余代码内容
  • 新增调整我们需要的目录结构
  1. 删除文件
  • components/HelloWorld.vue
  • views/HomeView.vue
  • views/AboutView.vue
  • assets/logo.png
  1. 修改内容

src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: []
})

export default router

src/App.vue




store/index.js 和 main.js 不用动

  1. 新增需要目录

在 src 目录下中补充创建以下目录:

  • /api : 存储请求函数模块
  • /styles: 样式文件模块
  • /utils: 工具函数模块

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第14张图片

  1. 将项目需要的图片资源放置 assets 文件夹

引入 element-ui 组件库

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第15张图片

官方文档: https://element.eleme.io/#/zh-CN

全部引入

全部引入, 会导入所有的组件, 但是体积会变大

  • 安装
yarn add element-ui
  • main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
  • 演示
主要按钮

按需导入 (推荐)

减轻将来打包后的包的体积

  • 安装
yarn add element-ui
  • 安装babel-plugin-component
yarn add babel-plugin-component -D
  • babel.config.js中配置

    PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第16张图片

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  // 新增plugins插件节点,修改完配置文件一定重启项目
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}
  • 使用插件main.js
import { Button } from 'element-ui'
Vue.use(Button)

抽离element.js模块

  • 由于组件的导入都书写到了main.js中,导致main.js 代码冗余

    将element-ui组件的导入和注册单独抽离到utils文件夹中

  • 新建element.js

  • 项目中 完整按需导入如下:

import Vue from 'vue';
import {
  Popconfirm,
  Avatar,
  Breadcrumb,
  BreadcrumbItem,
  Pagination,
  Dialog,
  Menu,
  Input,
  Option,
  Button,
  Table,
  TableColumn,
  Form,
  FormItem,
  Icon,
  Row,
  Col,
  Card,
  Container,
  Header,
  Aside,
  Main,
  Footer,
  Link,
  Image,
  Loading,
  MessageBox,
  Message,
  Drawer,
  MenuItem
} from 'element-ui';

Vue.use(Breadcrumb);
Vue.use(BreadcrumbItem);
Vue.use(Drawer);
Vue.use(Popconfirm);
Vue.use(Avatar);
Vue.use(Pagination);
Vue.use(Dialog);
Vue.use(Menu);
Vue.use(MenuItem);
Vue.use(Input);
Vue.use(Option);
Vue.use(Button);
Vue.use(Table);
Vue.use(TableColumn);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Icon);
Vue.use(Row);
Vue.use(Col);
Vue.use(Card);
Vue.use(Container);
Vue.use(Header);
Vue.use(Aside);
Vue.use(Main);
Vue.use(Footer);
Vue.use(Link);
Vue.use(Image);

Vue.use(Loading.directive);

Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
  • 直接导入main.js中
// 直接导入vant-ui.js
import '@/utils/element.js'

导入公共样式

新建 styles/index.scss

// 修改主题色
$--color-primary: rgba(114,124,245,1);
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";

body {
  margin: 0;
  padding: 0;
  background: #fafbfe;
}

main.js 引入

import '@/styles/index.scss'

request模块 - axios封装

接口文档地址:https://www.apifox.cn/apidoc/project-934563/api-19465917

我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址等)

一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用

  1. 安装 axios
npm i axios
  1. 新建 utils/request.js 封装 axios 模块

    利用 axios.create 创建一个自定义的 axios 来使用

    http://www.axios-js.com/zh-cn/docs/#axios-create-config

/* 封装axios用于发送请求 */
import axios from 'axios'

// 创建一个新的axios实例
const request = axios.create({
  baseURL: 'http://interview-api-t.itheima.net/',
  timeout: 5000
})

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

export default request

storage模块 - 本地存储

新建 utils/storage.js

// 以前 token 令牌,如果存到了本地,每一次都写这么长,太麻烦
// localStorage.setItem(键, 值)
// localStorage.getItem(键)
// localStorage.removeItem(键)

const KEY = 'my-token-element-pc'

// 直接用按需导出,可以导出多个
// 但是按需导出,导入时必须 import { getToken } from '模块名导入'

// 获取
export const getToken = () => {
  return localStorage.getItem(KEY)
}

// 设置
export const setToken = (newToken) => {
  localStorage.setItem(KEY, newToken)
}

// 删除
export const delToken = () => {
  localStorage.removeItem(KEY)
}

路由设计配置

但凡是: 单个页面,独立展示的,都是一级路由 (登录 注册 首页架子 文章详情 …)

路由设计:

  • 登录页 (一级) login
  • 首页架子(一级) layout
    • 数据看板(二级)dashboard
    • 文章管理(二级)article

新建目录

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第17张图片

login/index.vue






layout/index.vue






dashboard/index.vue






article/index.vue






配置路由

router/index.js

import VueRouter from 'vue-router'
import Vue from 'vue'

import Layout from '@/views/layout'
import Login from '@/views/login'
import Dashboard from '@/views/dashboard'
import Article from '@/views/article'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    { path: '/login', component: Login },
    {
      path: '/',
      component: Layout,
      redirect: '/dashboard',
      children: [
        { path: 'dashboard', component: Dashboard },
        { path: 'article', component: Article }
      ]
    }
  ]
})

export default router

layout/index 配置二级路由出口






测试路径1: http://localhost:8080/#/login

测试路径2: http://localhost:8080/#/dashboard

测试路径3: http://localhost:8080/#/article

登录模块

element-ui 基本表单

说明:我们先学习 element-ui 表单组件的基本结构使用

需求:实现如图效果

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第18张图片

一般情况,这种第三方的组件,为了样式控制方便,会给组件的根元素,起一个和组件名同名的类名

控制组件的样式:

  1. 直接通过组件名 同名的 类, 进行控制样式
  2. 自己通过添加 class 类名,进行控制样式
如何给组件标签, 设置样式?
1. 给组件标签, 加<自定义类>
   添加的类, 会自动加上渲染出来的组件的根元素上
.my-card {
  width: 420px;
  margin: 0 auto;
}

2. 直接使用<组件标签名>, 作为<类名>控制样式
   组件库定义组件的规范: 声明的所有组件的根元素, 都有一个和组件名同名的类名(提供给你了)

默认,写在scoped中的样式,只会影响到当前组件模板中的元素内容

//加上scoped, 可以让样式, 只作用于当前组件模板(局部样式)
//默认scoped样式, 不会向下渗透, 影响到其他子组件的(除了根元素)
//如果希望样式, 可以向下渗透, 影响到下面的子孙后代, 就需要用到深度作用选择器(vue提供)

深度作用选择器:
::v-deep   scss
/deep/     less
.el-card {
  width: 420px;
  margin: 0 auto;
  // 原理: 一旦选择器前面有深度作用标识, 就会不会附加属性选择器的限制
  ::v-deep .el-card__header {
    background-color: #727cf5;
    text-align: center;
    color: #fff;
  }
}

深度作用选择器:向下影响到子元素的样式

::v-deep (scss)

/deep/ (less)






样式美化:






element-ui 基本校验

说明:在向后端发请求,调用接口之前,我们需要对所要传递的参数进行验证,把用户的错误扼杀在摇篮之中。

讲解内容:

  • element-ui的校验

    • el-form: model属性, rules规则

    • el-form-item: 绑定 prop 属性

    • el-input: 绑定 v-model

Form 组件提供了表单验证的功能

官方文档写的不好,一共有四个地方需要绑定:

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第19张图片

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第20张图片

  1. form组件需要 :model绑定form对象(必须), 需要通过 rules 属性传入约定的验证规则

    
export default {
  data() {
    return {
      form: {
        username: '',
        password: ''
      }
    }
  }
}
  1. 在 data 中准备 rules 规则
rules: {
  username: [
    { required: true, message: '请输入用户名', trigger: ['blur', 'change'] },
    { min: 5, max: 11, message: '长度在 5 到 11 个字符', trigger: ['blur', 'change'] }
  ]
}
  1. 将 Form-Item 的 prop 属性设置为需校验的字段名
<el-form-item label="用户名:" prop="username">
  <el-input v-model="form.username" placeholder="请输入手机号" />
el-form-item>

element-ui 正则校验

下面是常用内置的基本验证规则:其余校验规则参见 async-validator

规则 说明
required 必须的,例如校验内容是否非空
pattern 正则表达式,例如校验手机号码格式、校验邮箱格式
rules: {
  username: [
    { required: true, message: '请输入用户名', trigger: ['blur', 'change'] },
    { min: 5, max: 11, message: '长度在 5 到 11 个字符', trigger: ['blur', 'change'] }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: ['blur', 'change'] },
    { pattern: /^\w{5,11}$/, message: '请输入 5 到 10 位的密码', trigger: ['blur', 'change'] }
  ]
}

// \d 数字 0-9
// \w 字母数字下划线
// {m,n} 前面的字符,可以出现 m次 ~ n次

不要忘了配置prop

<el-form-item prop="password">

上述已经可以完成大部分需求,如果需要更复杂业务校验需求,可以自定义校验~ (项目课程:人力资源系统会进一步讲解)

提交表单校验 和 重置

每次点击按钮, 进行ajax登录前, 应该先对整个表单内容校验, 不然还是会发送很多无效的请求!!!

要通过校验了, 才发送请求!!!

作用: ref 属性配合 $refs 可以获取 dom 元素 (或者 vue组件实例)

  1. 给组件或者元素, 添加 ref 属性
<hello ref="bb">hello>
  1. 通过 this.$refs 可以获取对应的引用, 并且调用方法
this.$refs.bb.sayHi()

添加登录提交的校验

<el-form ref="form" :model="form" :rules="rules" autocomplete="off">
...
<el-button @click="login" type="primary">登 录</el-button>

methods: {
  async login () {
    try {
      const valid = await this.$refs.form.validate()
      console.log(valid)
    } catch (e) {
      console.log(e)
    }
  }
}

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第21张图片

添加重置功能

重 置

methods: {
  reset () {
    this.$refs.form.resetFields()
  }
}

封装登录api登录请求

新建 api/user.js 提供api接口函数

import request from '@/utils/request'

export const login = ({ username, password }) => {
  return request.post('/auth/login', {
    username,
    password
  })
}

发送请求获取token

methods: {
  async login () {
    try {
      const valid = await this.$refs.form.validate()
      if (valid) {
        const res = await login(this.form)
        console.log(res)
      }
    } catch (e) {
      console.log(e)
    }
  }
}

vuex user 模块 - 存token

image-20230323192434641

新建 store/modules/user.js

import { getToken, setToken } from '@/utils/storage'

export default {
  namespaced: true,
  state () {
    return {
      token: getToken()
    }
  },
  mutations: {
    setUserToken (state, payload) {
      state.token = payload
      setToken(payload)
    }
  }
}

挂载模块

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    user
  }
})

登录时调用

async login () {
  try {
    const valid = await this.$refs.form.validate()
    if (valid) {
      const res = await login(this.form)
      this.$store.commit('user/setUserToken', res.data.token)
      this.$router.push('/')
    }
  } catch (e) {
    console.log(e)
  }
},

登录访问拦截

router/index.js

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第22张图片

没有token 且 访问的不是 登录页,就直接拦截到登录

router.beforeEach((to, from, next) => {
  const { token } = store.state.user;
  if (to.path !== '/login' && !token ) return next('/login')
  next()
})

首页 layout 模块

layout 布局

api/user.js 准备api接口

export const getUser = () => {
  return request.get('/auth/currentUser')
}

layout/index.vue准备结构 (已准备)






遇到 401 错误

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第23张图片

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第24张图片

请求拦截器携带token

utils/request.js

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  const { token } = store.state.user
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

退出功能

退出操作

handleConfirm () {
  // this.$router.push('/login')
  this.$store.commit('user/logout')
  this.$router.push('/login')
}

提供mutation

import { delToken, getToken, setToken } from '@/utils/storage'

export default {
  namespaced: true,
  state () {
    return {
      token: getToken()
    }
  },
  mutations: {
    ...,
    logout (state) {
      state.token = null
      delToken()
    }
  }
}

处理token过期

响应拦截器,处理token过期

import router from '../router'

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // 对响应错误做点什么  普通错误 + 401情况
  // console.dir(error)
  if (error.response) {
    if (error.response.status === 401) {
      // 给提示,清除无效token(vuex+本地),拦到登录
      Message.error('尊敬的用户,当前登录状态已过期!')

      // 提交清除token的mutation
      store.commit('user/logout')

      // 跳转到登录
      router.push('/login')
    } else {
      // 给提示
      Message.error(error.response.data.message)
    }
  }
  return Promise.reject(error)
})

数据看板 (了解)

静态结构

dashboard/index.vue






vue中echarts的使用

装包

yarn add echarts

导入

import * as echarts from 'echarts'

添加ref

<div ref="box" class="chart-box" style="height: 500px">div>

mounted初始化

PC端面经后台管理项目-element-UI,sass/scss,axios、localstorage二次封装,响应拦截器_第25张图片

mounted () {
  const myChart = echarts.init(this.$refs.box)
  // 绘制图表
  myChart.setOption({
    title: {
      text: 'ECharts 入门示例'
    },
    tooltip: {},
    xAxis: {
      data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
    },
    yAxis: {},
    series: [
      {
        name: '销量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
      }
    ]
  })
},

你可能感兴趣的:(sass,scss,vue.js,javascript,前端)