前端vue3+elementPlus后端:koa2+MongoDB
Vue3+ElementPlus
Lo2+MongoDB
菜单按钮权限,JWT认证,审批流,常规的CRUD,模块化,组件化
技术栈
架构设计,vue3全家桶,koa2,
# Vue3重点知识介绍
创建响应式对象:ref/reactive
```bash
let name = ref('jack')
name.value = 'tom'
let user = reactive({name:'jack',age:30})
user.name = 'tom'
user.age = 28
```
钩子函数:
```bash
export default {
name:'user'
setup(){
let user = reactive({})
onMounted(()=>{
// init...
})
let computedName = computed(()=>{
return user.name;
})
return {
user,
computedName
}
}
}
```
1-2项目开发流程
1:5,1:6,1:7
何用通用后台?
通用模块和通用后台的区别?
1-5
监听对象
#2-1前端架构设计
路由封装
# 2-6环境变量封装
fastmock.site
# 2-7axios二次封装
# 2-9
```c
/**
* 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()
}
}
```
简化前端调用的方式
# 2-10
第三章-koa架构设计
```bash
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('demo:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
// app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app.callback());
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
```
# 3-3log4js日志
```bash
/**
* 日志存储
* @author JackBean
*/
const log4js = require('log4js')
const levels = {
'trace':log4js.levels.TRACE,
'debug':log4js.levels.DEBUG,
'info':log4js.levels.INFO,
'warn':log4js.levels.WARN,
'error':log4js.levels.ERROR,
'fatal':log4js.levels.FATAL,
}
log4js.configure({
appenders:{
console:{ type:'console' },
info:{
type: 'file',
filename: 'logs/all-logs.log'
},
error:{
type: 'dateFile',
filename:'logs/log',
pattern:'yyyy-MM-dd.log',
alwaysIncludePattern:true// 设置文件名称为 filename + pattern
}
},
categories:{
default:{ appenders: [ 'console' ], level: 'debug' },
info:{
appenders: [ 'info','console' ],
level: 'info'
},
error:{
appenders: [ 'error','console' ],
level: 'error'
}
}
})
/**
* 日志输出,level为debug
* @param {string} content
*/
exports.debug = (content)=>{
let logger = log4js.getLogger();
logger.level = levels.debug;
logger.debug(content);
}
/**
* 日志输出,level为info
* @param {string} content
*/
exports.info = (content)=>{
let logger = log4js.getLogger('info');
logger.level = levels.info;
logger.info(content);
}
/**
* 日志输出,level为error
* @param {string} content
*/
exports.error = (content)=>{
let logger = log4js.getLogger('error');
logger.level = levels.error;
logger.error(content);
}
```
3-5mongdb安装配置
非关系型,json
# 3-16-工具函数的封装
```bash
/**
* 通用工具函数
*/
const log4js = require('./log4j')
const jwt = require('jsonwebtoken')
const CODE = {
SUCCESS: 200,
PARAM_ERROR: 10001, // 参数错误
USER_ACCOUNT_ERROR: 20001, //账号或密码错误
USER_LOGIN_ERROR: 30001, // 用户未登录
BUSINESS_ERROR: 40001, //业务请求失败
AUTH_ERROR: 500001, // 认证失败或TOKEN过期
}
module.exports = {
/**
* 分页结构封装
* @param {number} pageNum
* @param {number} pageSize
*/
pager({ pageNum = 1, pageSize = 10 }) {
pageNum *= 1;
pageSize *= 1;
const skipIndex = (pageNum - 1) * pageSize;
return {
page: {
pageNum,
pageSize
},
skipIndex
}
},
success(data = '', msg = '', code = CODE.SUCCESS) {
log4js.debug(data);
return {
code, data, msg
}
},
fail(msg = '', code = CODE.BUSINESS_ERROR, data = '') {
log4js.debug(msg);
return {
code, data, msg
}
},
CODE,
decoded(authorization) {
if (authorization) {
let token = authorization.split(' ')[1]
return jwt.verify(token, 'imooc')
}
return '';
},
// 递归拼接树形列表
getTreeMenu(rootList, id, list) {
for (let i = 0; i < rootList.length; i++) {
let item = rootList[i]
if (String(item.parentId.slice().pop()) == String(id)) {
list.push(item._doc)
}
}
list.map(item => {
item.children = []
this.getTreeMenu(rootList, item._id, item.children)
if (item.children.length == 0) {
delete item.children;
} else if (item.children.length > 0 && item.children[0].menuType == 2) {
// 快速区分按钮和菜单,用于后期做菜单按钮权限控制
item.action = item.children;
}
})
return list;
},
formateDate(date, rule) {
let fmt = rule || 'yyyy-MM-dd hh:mm:ss'
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, date.getFullYear())
}
const o = {
// 'y+': date.getFullYear(),
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
}
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
const val = o[k] + '';
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? val : ('00' + val).substr(val.length));
}
}
return fmt;
},
}
```
通用工具的函数封装
/**
* 通用工具函数
*/
const log4js = require('./log4j')
const jwt = require('jsonwebtoken')
const CODE = {
SUCCESS: 200,
PARAM_ERROR: 10001, // 参数错误
USER_ACCOUNT_ERROR: 20001, //账号或密码错误
USER_LOGIN_ERROR: 30001, // 用户未登录
BUSINESS_ERROR: 40001, //业务请求失败
AUTH_ERROR: 500001, // 认证失败或TOKEN过期
}
module.exports = {
/**
* 分页结构封装
* @param {number} pageNum
* @param {number} pageSize
*/
pager({ pageNum = 1, pageSize = 10 }) {
pageNum *= 1;
pageSize *= 1;
const skipIndex = (pageNum - 1) * pageSize;
return {
page: {
pageNum,
pageSize
},
skipIndex
}
},
success(data = '', msg = '', code = CODE.SUCCESS) {
log4js.debug(data);
return {
code, data, msg
}
},
fail(msg = '', code = CODE.BUSINESS_ERROR, data = '') {
log4js.debug(msg);
return {
code, data, msg
}
},
CODE,
decoded(authorization) {
if (authorization) {
let token = authorization.split(' ')[1]
return jwt.verify(token, 'imooc')
}
return '';
},
// 递归拼接树形列表
getTreeMenu(rootList, id, list) {
for (let i = 0; i < rootList.length; i++) {
let item = rootList[i]
if (String(item.parentId.slice().pop()) == String(id)) {
list.push(item._doc)
}
}
list.map(item => {
item.children = []
this.getTreeMenu(rootList, item._id, item.children)
if (item.children.length == 0) {
delete item.children;
} else if (item.children.length > 0 && item.children[0].menuType == 2) {
// 快速区分按钮和菜单,用于后期做菜单按钮权限控制
item.action = item.children;
}
})
return list;
},
formateDate(date, rule) {
let fmt = rule || 'yyyy-MM-dd hh:mm:ss'
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, date.getFullYear())
}
const o = {
// 'y+': date.getFullYear(),
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
}
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
const val = o[k] + '';
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? val : ('00' + val).substr(val.length));
}
}
return fmt;
},
}