前言
全局安装Vue脚手架
安装方式:
npm install @vue/cli -g
# or
cnpm install @vue/cli -g
# or
yarn global add @vue/cli
创建项目:
vue create manager-fe
一、前端架构设计
1、环境配置&目录规范
通过vite创建项目
官方文档:cn.vitejs.dev/
vite基于ES module的开发服务器,内置一个node server,不会编译ES6的语法。以前vue2 webpack会编译ES6成ES5,本地开发环境不需要编译,浏览器本身也支持语法,vite处理scss、less转成css,vue解析成render函数。
Vite,一个基于浏览器原生ES imports的开发服务器。 利用浏览器去解析imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有Vue文件支持,还搞定了热更新,而且热更新速度不会随着模块增多而变慢。针对生成环境则可以把同一份代码用rollup打包。
创建项目(不需要全局安装,本地初始化就行,插件方式安装):
yarn create vite
安装项目所需插件
# 安装项目生产依赖
yarn add vue-router vuex element-plus axios
# 安装项目开发依赖
yarn add sass -D
element-plus文档:element-plus.org/#/zh-CN/com…
制定目录结构
dist
node_modules
public
src
api
assets
components
config
router
store
utils
views
App.vue
main.js
.gitignore
.env.dev
.env.test
.env.prod
index.html
2、路由封装
路由跳转的三种方式
- router-link
<router-link to="/login">去登录</router-link>
- 传统跳转
<template>
<el-button @click="goHome">回首页</el-button>
</template>
<script>
export default {
name:'login',
methods:{
goHome(){
this.$router.push('/welcome')
}
}
}
</script>
- Composition API跳转
<script setup>
import { useRouter } from 'vue-router'
let router = useRouter()
const goHome = ()=>{
router.push('/welcome')
}
</script>
3、环境配置
/**
* 环境配置封装
*/
const env = import.meta.env.MODE || 'prod';
const EnvConfig = {
dev:{
baseApi:'/api',
mockApi:'https://www.fastmock.site/mock/api'
},
test:{
baseApi:'//test.futurefe.com/api',
mockApi:'https://www.fastmock.site/mock/api'
},
prod:{
baseApi:'//futurefe.com/api',
mockApi:'https://www.fastmock.site/mock/api'
}
}
export default {
env,
mock:false,
namespace:'manager',
...EnvConfig[env]
}
4、request封装
/**
* axios二次封装
*/
import axios from 'axios'
import config from './../config'
import { ElMessage } from 'element-plus'
import router from './../router'
import storage from './storage'
const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求异常,请稍后重试'
// 创建axios实例对象,添加全局配置
const service = axios.create({
baseURL: config.baseApi,
timeout: 8000
})
// 请求拦截
service.interceptors.request.use((req) => {
const headers = req.headers;
const { token = "" } = storage.getItem('userInfo') || {};
if (!headers.Authorization) headers.Authorization = 'Bearer ' + token;
return req;
})
// 响应拦截
service.interceptors.response.use((res) => {
const { code, data, msg } = res.data;
if (code === 200) {
return data;
} else if (code === 500001) {
ElMessage.error(TOKEN_INVALID)
setTimeout(() => {
router.push('/login')
}, 1500)
return Promise.reject(TOKEN_INVALID)
} else {
ElMessage.error(msg || NETWORK_ERROR)
return Promise.reject(msg || NETWORK_ERROR)
}
})
/**
* 请求核心函数
* @param {*} options 请求配置
*/
function request(options) {
options.method = options.method || 'get'
if (options.method.toLowerCase() === 'get') {
options.params = options.data;
}
let isMock = config.mock;
if (typeof options.mock != 'undefined') {
isMock = options.mock;
}
if (config.env === 'prod') {
service.defaults.baseURL = config.baseApi
} else {
service.defaults.baseURL = isMock ? config.mockApi : config.baseApi
}
return service(options)
}
['get', 'post', 'put', 'delete', 'patch'].forEach((item) => {
request[item] = (url, data, options) => {
return request({
url,
data,
method: item,
...options
})
}
})
export default request;
5、Storage二次封装
/**
* Storage二次封装
* @author JackBean
*/
import config from './../config'
export default {
setItem(key, val) {
let storage = this.getStroage();
storage[key] = val;
window.localStorage.setItem(config.namespace, JSON.stringify(storage));
},
getItem(key) {
return this.getStroage()[key]
},
getStroage() {
return JSON.parse(window.localStorage.getItem(config.namespace) || "{}");
},
clearItem(key) {
let storage = this.getStroage()
delete storage[key]
window.localStorage.setItem(config.namespace, JSON.stringify(storage));
},
clearAll() {
window.localStorage.clear()
}
}
6、权限&工作流
权限&工作流知识介绍
场景:OA、HR、ERP、CRM
工作流七要素:角色(发起人、审批人)、场景(请假、出差)、节点(审批单节点、多节点)、环节(审批单环节、多环节)、必要信息(申请理由、申请时长)、通知(申请人、审批人)、操作(未审批、已驳回、已审批)
用户登录 -> 获取用户身份(管理员和普通用户) -> 调用权限列表接口 -> 递归生成菜单和按钮list -> 前端进行菜单渲染
动态菜单渲染
按钮权限控制
动态指令: v-has
理解指令:
v-on:click = "handleUser"
click 对应binding.arg ,表示指令参数
handleUser 对应binding.value ,表示指令值
app.directive('has', {
beforeMount: function (el, binding) {
// 获取按钮列表,注意按钮的key不可以重复,必须唯一
let actionList = storage.getItem('actionList');
// 获取质量的值
let value = binding.value;
// 判断值是否在按钮列表里面
let hasPermission = actionList.includes(value)
if (!hasPermission) {
// 隐藏按钮
el.style = 'display:none';
setTimeout(() => {
// 删除按钮
el.parentNode.removeChild(el);
}, 0)
}
}
})
导航守卫、权限拦截、动态路由
常用API:beforeEach()、afterEach()、getRoutes()、push()、back()、addRoute()
我们判断当前路由是否存在时,也可以使用hasRoute()
原代码:router.getRoutes().filter(route => route.path == path).length;
更改后代码: router.hasRoute(to.name)
二、后端架构
1、Koa2项目初始化
# koa2+项目名
koa2 manager-server
2、mongodb下载配置
- 执行下面命令,配置文件启动mongodb。
ln -s /Users/****/mongodb/bin/mongo /usr/local/bin/mongo
ln -s /Users/****/mongodb/bin/mongod /usr/local/bin/mongod
mongod --config /Users/****/mongodb/mongo/conf/mongo.conf
3、JWT
- 数据传输简单、高效
- jwt会生成签名,保证传输安全
- jwt具有时效性
- jwt更高效利用集群做好单点登录
原理
服务器认证后,生成一个json对象,后续通过json进行通信。
数据结构
- Header(头部)
- Payload(负载)
- Signature(签名)
使用方式
- /api?token=***
- cookie写入token
- storage写入token,请求头添加:Authorization:Bearer
流程
- 前端调动login接口
- 服务器返回带token的data
const token = jwt.sign({
data
}, 'imooc', { expiresIn: '1h' })
data.token = token;
- 前端收到登录数据,本地缓存用户信息,加载路由。
this.$store.commit("saveUserInfo", res);
await this.loadAsyncRoutes();
this.$router.push("/welcome");
- 前端下次请求其它接口,统一请求拦截的时候增加token。
// 请求拦截
service.interceptors.request.use((req) => {
const headers = req.headers;
const { token = "" } = storage.getItem('userInfo') || {};
if (!headers.Authorization) headers.Authorization = 'Bearer ' + token;
return req;
})