├── vue_ts-server ├── public │ └── avatar │ │ ├── 龙二_1.jpg │ │ ├── 龙二_2.jpg │ │ ├── pikaqiu_1.jpg │ │ └── 用户64x64_16.png ├── config │ └── index.js ├── router │ ├── dashboard.js │ ├── userinfo.js │ ├── dict-items.js │ ├── dict.js │ ├── menu.js │ ├── role.js │ └── user.js ├── .gitignore ├── model │ ├── init.js │ ├── users-roles.js │ ├── roles-menus.js │ ├── dict-items.js │ ├── dict.js │ ├── menus.js │ ├── users.js │ └── roles.js ├── README.md ├── package.json ├── utils │ ├── token.js │ ├── redis.js │ └── tools.js ├── schema │ ├── userinfo.js │ ├── dict-items.js │ ├── menu.js │ ├── dict.js │ ├── role.js │ └── user.js ├── router_handler │ ├── dashboard.js │ ├── dict-items.js │ ├── dict.js │ ├── role.js │ ├── menu.js │ ├── userinfo.js │ └── user.js ├── app.js ├── vue3+ts+nodeJS后台管理系统接口文档(接口地址+参数).md └── sql │ ├── database(React项目用这个).sql │ └── database(vue3项目用这个).sql └── README.md /vue_ts-server/public/avatar/龙二_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alan-222/Vue3_Ts_NodeJs_management_system_server/HEAD/vue_ts-server/public/avatar/龙二_1.jpg -------------------------------------------------------------------------------- /vue_ts-server/public/avatar/龙二_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alan-222/Vue3_Ts_NodeJs_management_system_server/HEAD/vue_ts-server/public/avatar/龙二_2.jpg -------------------------------------------------------------------------------- /vue_ts-server/public/avatar/pikaqiu_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alan-222/Vue3_Ts_NodeJs_management_system_server/HEAD/vue_ts-server/public/avatar/pikaqiu_1.jpg -------------------------------------------------------------------------------- /vue_ts-server/public/avatar/用户64x64_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alan-222/Vue3_Ts_NodeJs_management_system_server/HEAD/vue_ts-server/public/avatar/用户64x64_16.png -------------------------------------------------------------------------------- /vue_ts-server/config/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | jwtSecretKey: 'have a lucky day!', 3 | jwtRefrechSecretKey: 'have a nick day!', 4 | secretKeyExpire: 60 * 60, // 设置token 的有效时间为1小时 5 | refreshSerectKeyExpire: 60 * 60 * 24 * 2, // 设置refreshToken的有效时间为2天 6 | post: 6379, 7 | url: '127.0.0.1', 8 | password: 123456 9 | } 10 | -------------------------------------------------------------------------------- /vue_ts-server/router/dashboard.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | // 创建路由对象 3 | const router = express.Router() 4 | const dashboardHandler = require('../router_handler/dashboard') 5 | 6 | // 获取各实体数量 7 | router.get('/getCount', dashboardHandler.getCount) 8 | // 获取模块菜单数量 9 | router.get('/getModuleCount',dashboardHandler.getModuleMenuCount) 10 | module.exports = router 11 | -------------------------------------------------------------------------------- /vue_ts-server/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vue_ts-server/model/init.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | // 建立连接 3 | const sequelize = new Sequelize( 4 | // 以下内容根据自身修改 5 | 'react_antd_admin', // 数据库名 6 | 'root', // 连接用户名 7 | '123456', // 密码 8 | { 9 | dialect: 'mysql', // 数据库类型 10 | host: '127.0.0.1', // ip 11 | port: 3306, // 端口 12 | define: { 13 | timestamps: false // 不自动创建时间 14 | }, 15 | timezone: '+08:00' // 东八时区 16 | } 17 | ) 18 | 19 | module.exports = sequelize 20 | -------------------------------------------------------------------------------- /vue_ts-server/router/userinfo.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | // 创建路由对象 3 | const router = express.Router(); 4 | const handler = require('../router_handler/userinfo'); 5 | 6 | // 获取登录用户的基本信息 7 | router.get('/userinfo', handler.getUserinfo); 8 | // 更新登录用户的基本信息 9 | router.post('/updateUserinfo', handler.updateUserInfo); 10 | // 重置密码接口 11 | router.post('/updatePwd', handler.updatepwd); 12 | // 更新头像接口 13 | router.post('/updateAvatar', handler.updateAvatar); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /vue_ts-server/router/dict-items.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | // 创建路由对象 3 | const router = express.Router(); 4 | const dictItemHandler = require('../router_handler/dict-items'); 5 | 6 | // 添加字典项 7 | router.post('/addDictItem', dictItemHandler.addDictItem); 8 | // 修改字典项 9 | router.post('/editDictItem', dictItemHandler.editDictItem); 10 | // 删除字典项 11 | router.post('/delDictItem', dictItemHandler.deleteDictItem); 12 | // 根据id获取用户信息接口 13 | router.get('/getDictItem', dictItemHandler.getOneDictItem); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /vue_ts-server/router/dict.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | // 创建路由对象 3 | const router = express.Router(); 4 | const dictHandler = require('../router_handler/dict'); 5 | 6 | // 分页获取字典列表 7 | router.get('/listDict', dictHandler.getList); 8 | // 添加字典 9 | router.post('/addDict', dictHandler.addDict); 10 | // 修改字典 11 | router.post('/editDict', dictHandler.editDict); 12 | // 删除字典 13 | router.post('/delDict', dictHandler.deleteDict); 14 | // 根据id获取字典信息接口 15 | router.get('/getDict', dictHandler.getOneDict); 16 | // 根据名称获取字典信息接口 17 | router.get('/getDictByName', dictHandler.getDictByName); 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /vue_ts-server/README.md: -------------------------------------------------------------------------------- 1 | ## 数据库 2 | 3 | ### 导入数据库文件 4 | 5 | 新建数据库并导入 sql 目录下的 **database.sql** 6 | 7 | ### 修改数据库信息 8 | 9 | 根据自身修改 model 目录下的 init.js 10 | 11 | ```js 12 | const sequelize = new Sequelize( 13 | // 以下内容根据自身修改 14 | 'react_antd_admin', // 数据库名 15 | 'root', // 连接用户名 16 | '123456', // 密码 17 | { 18 | dialect: 'mysql', // 数据库类型 19 | host: '127.0.0.1', // ip 20 | port: 3306, // 端口 21 | define: { 22 | timestamps: false // 不自动创建时间 23 | }, 24 | timezone: '+08:00' // 东八时区 25 | } 26 | ) 27 | ``` 28 | 29 | ## 项目安装 30 | 31 | ``` 32 | npm install 33 | ``` 34 | 35 | ### 启动项目 36 | 37 | ``` 38 | npm run start 39 | ``` 40 | -------------------------------------------------------------------------------- /vue_ts-server/router/menu.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | // 创建路由对象 3 | const router = express.Router() 4 | const handler = require('../router_handler/menu') 5 | 6 | // 获取菜单列表 7 | router.get('/listMenu', handler.getMenuList) 8 | // 获取菜单项 9 | router.get('/listMenuOptions', handler.getMenuOptions) 10 | // 获取菜单项(无按钮和菜单区分) 11 | router.get('/listAuthOptions', handler.getAuthOptions) 12 | // 添加菜单 13 | router.post('/addMenu', handler.addMenu) 14 | // 修改菜单 15 | router.post('/editMenu', handler.editMenu) 16 | // 删除菜单 17 | router.post('/delMenu', handler.deleteMenu) 18 | // 根据id获取菜单信息接口 19 | router.get('/getMenu', handler.getOneMenu) 20 | 21 | module.exports = router 22 | -------------------------------------------------------------------------------- /vue_ts-server/model/users-roles.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const moment = require('moment') 3 | const sequelize = require('./init') 4 | 5 | // 定义表的模型 6 | const UsresRolesModel = sequelize.define('users_roles', { 7 | user_role_id: { 8 | type: Sequelize.INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true 11 | }, 12 | role_id: { 13 | type: Sequelize.INTEGER 14 | }, 15 | user_id: { 16 | type: Sequelize.INTEGER 17 | }, 18 | create_time: { 19 | type: Sequelize.DATE, 20 | defaultValue: Sequelize.NOW, 21 | get() { 22 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss') 23 | } 24 | } 25 | }) 26 | 27 | module.exports = UsresRolesModel 28 | -------------------------------------------------------------------------------- /vue_ts-server/model/roles-menus.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const moment = require('moment'); 3 | const sequelize = require('./init'); 4 | 5 | // 定义表的模型 6 | const RolesMenusModel = sequelize.define('roles_menus', { 7 | role_menu_id: { 8 | type: Sequelize.INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true 11 | }, 12 | role_id: { 13 | type: Sequelize.INTEGER 14 | }, 15 | menu_id: { 16 | type: Sequelize.INTEGER 17 | }, 18 | create_time: { 19 | type: Sequelize.DATE, 20 | defaultValue: Sequelize.NOW, 21 | get() { 22 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss'); 23 | } 24 | } 25 | }); 26 | 27 | module.exports = RolesMenusModel; 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 所需准备 2 | 3 | #### 后端服务及接口文档 4 | 5 | 接下来要测试接口服务的话,这里就要用到之前给大家提供的node后端服务器 6 | 7 | 【[node后端服务器](https://github.com/Alan-222/Vue3_Ts_NodeJs_management_system_server)】: 8 | 9 | 1.克隆到本地后新建mysql数据库并运行sql目录下的database.sql文件 10 | 11 | 2.修改model目录下init.js中的数据库信息 12 | 13 | 2.`npm install`安装依赖 14 | 15 | 3.`npm start`启动服务 16 | 17 | 【[Redis](https://github.com/tporadowski/redis/releases)】:用于验证码的缓存 18 | 19 | 1.点击链接并安装msi后缀的版本 20 | 21 | 2.设置默认端口6379,密码123456 22 | 23 | 【[接口文档](https://github.com/Alan-222/Vue3_Ts_NodeJs_management_system_server/blob/master/vue_ts-server/vue3%2Bts%2BnodeJS%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%EF%BC%88%E6%8E%A5%E5%8F%A3%E5%9C%B0%E5%9D%80%2B%E5%8F%82%E6%95%B0%EF%BC%89.md)】:提供了接口url信息及所需请求参数 24 | -------------------------------------------------------------------------------- /vue_ts-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue_ts-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node-dev app.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcryptjs": "^2.4.3", 15 | "body-parser": "^1.20.1", 16 | "cors": "^2.8.5", 17 | "express": "^4.17.1", 18 | "express-jwt": "^5.3.3", 19 | "formidable": "^2.0.1", 20 | "joi": "^17.6.0", 21 | "jsonwebtoken": "^8.5.1", 22 | "moment": "^2.29.4", 23 | "mysql2": "^2.3.3", 24 | "node-dev": "^7.4.3", 25 | "redis": "^3.1.2", 26 | "sequelize": "^5.22.5", 27 | "svg-captcha": "^1.4.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue_ts-server/router/role.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | // 创建路由对象 3 | const router = express.Router() 4 | const roleHandler = require('../router_handler/role') 5 | 6 | // 分页获取角色列表 7 | router.get('/listRole', roleHandler.getList) 8 | // 获取所有角色 9 | router.get('/allRole', roleHandler.getAllRole) 10 | // 添加角色 11 | router.post('/addRole', roleHandler.addRole) 12 | // 获取角色资源(区分按钮、菜单) 13 | router.get('/roleResource', roleHandler.getRoleResource) 14 | // 获取角色所有资源 15 | router.get('/roleAuth', roleHandler.getRoleAuth) 16 | // 更新角色资源 17 | router.post('/updateRoleResource', roleHandler.updateRoleResource) 18 | // 修改角色 19 | router.post('/editRole', roleHandler.editRole) 20 | // 删除角色 21 | router.post('/delRole', roleHandler.deleteRole) 22 | // 根据id获取用户信息接口 23 | router.get('/getRole', roleHandler.getOneRole) 24 | 25 | module.exports = router 26 | -------------------------------------------------------------------------------- /vue_ts-server/router/user.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | // 创建路由对象 3 | const router = express.Router(); 4 | 5 | // 导入用户路由处理函数模块 6 | const userHandler = require('../router_handler/user'); 7 | 8 | // 获取用户列表 9 | router.get('/list', userHandler.getList); 10 | // 修改用户信息 11 | router.post('/editUser/:id', userHandler.editUser); 12 | // 登录 13 | router.post('/login', userHandler.login); 14 | // 添加用户接口 15 | router.post('/addUser', userHandler.addUser); 16 | // 获取图形验证码 17 | router.get('/checkCode', userHandler.getCheckCode); 18 | // 刷新token 19 | router.post('/refreshToken', userHandler.refreshToken); 20 | // 删除用户 21 | router.post('/delUser', userHandler.deleteUser); 22 | // 重置密码 23 | router.post('/editPwd', userHandler.editPassword); 24 | // 根据id获取用户信息接口 25 | router.get('/queryUserInfo/:user_id', userHandler.getUserinfoById); 26 | // 将路由对象共享出去 27 | module.exports = router; 28 | -------------------------------------------------------------------------------- /vue_ts-server/utils/token.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const tkconf = require('../config/index'); 3 | /** 4 | * token生成函数 5 | * @param {*} user 存在token中的信息 6 | * @param {*} serect 密钥 7 | * @param {*} time token存在时间 8 | * @returns 9 | */ 10 | const addToken = function (user, serect, time) { 11 | //创建token并导出 12 | 13 | const token = jwt.sign( 14 | { 15 | id: user.id, 16 | username: user.username 17 | }, 18 | serect, 19 | { expiresIn: time + 's' } 20 | ); 21 | return token; 22 | }; 23 | /** 24 | * token解析函数 25 | * @param {*} token 26 | * @returns 27 | */ 28 | const decodedToken = function (token) { 29 | const decoded = jwt.decode(token); 30 | return decoded; 31 | }; 32 | /** 33 | * 验证对应的refreshToken 34 | * @param {*} refreshToken 35 | * @returns 36 | */ 37 | const verifyToken = function verify_refreshToken(refreshToken) { 38 | return jwt.verify(refreshToken, tkconf.jwtRefrechSecretKey, (err, decode) => { 39 | return err ? err : 1; 40 | }); 41 | }; 42 | 43 | module.exports = { 44 | addToken, 45 | decodedToken, 46 | verifyToken 47 | }; 48 | -------------------------------------------------------------------------------- /vue_ts-server/utils/redis.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis'); 2 | const config = require('../config/index'); 3 | const client = redis.createClient(config.post, config.url); 4 | 5 | client.on('error', function (err) { 6 | console.log('Error ' + err); 7 | }); 8 | 9 | client.on('connect', () => { 10 | console.log('redis connect success'); 11 | }); 12 | 13 | client.auth(config.password); 14 | 15 | const redisConnect = {}; 16 | redisConnect.setKey = (key, value, expire) => { 17 | return new Promise((resolve, reject) => { 18 | client.set(key, value, (err, replay) => { 19 | if (err) { 20 | reject(err); 21 | } 22 | if (!isNaN(expire) && expire > 0) { 23 | client.expire(key, parseInt(expire)); 24 | } 25 | resolve(replay); 26 | }); 27 | }); 28 | }; 29 | 30 | redisConnect.getKey = (key) => { 31 | return new Promise((resolve, reject) => { 32 | client.get(key, (err, replay) => { 33 | if (err) { 34 | reject(err); 35 | } else { 36 | resolve(replay); 37 | } 38 | }); 39 | }); 40 | }; 41 | 42 | module.exports = redisConnect; 43 | -------------------------------------------------------------------------------- /vue_ts-server/schema/userinfo.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi') 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ) 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | 17 | // 用户名的校验规则 18 | const username = joi.string().alphanum().min(1).max(10).required() 19 | // 密码的验证规则 20 | const password = joi 21 | .string() 22 | .pattern(/^[\S]{6,12}$/) 23 | .required() 24 | .messages({ 25 | 'string.empty': '密码必填', 26 | 'any.required': '密码必填', 27 | 'string.pattern.base': '密码为6-12位字符' 28 | }) 29 | 30 | const nickname = joi.string() 31 | const email = joi.string().email() 32 | const role_ids = joi.array().items(joi.number()).required() 33 | 34 | exports.update_userinfo_schema = joi.object().keys({ 35 | username: joi.string().alphanum().min(1).max(10), 36 | nickname, 37 | email 38 | }) 39 | exports.update_password_schema = joi.object().keys({ 40 | old_password: password, 41 | password: joi.not(joi.ref('old_password')).concat(password), 42 | repassword: joi.ref('password') 43 | }) 44 | -------------------------------------------------------------------------------- /vue_ts-server/model/dict-items.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const moment = require('moment'); 3 | const sequelize = require('./init'); 4 | 5 | // 定义表的模型 6 | const DictItemModel = sequelize.define('dict_items', { 7 | id: { 8 | type: Sequelize.INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true 11 | }, 12 | dict_id: { 13 | type: Sequelize.INTEGER 14 | }, 15 | item_text: { 16 | type: Sequelize.STRING(255) 17 | }, 18 | item_value: { 19 | type: Sequelize.STRING(255) 20 | }, 21 | description: { 22 | type: Sequelize.STRING(255) 23 | }, 24 | sort_order: { 25 | type: Sequelize.INTEGER, 26 | defaultValue: 0 27 | }, 28 | status: { 29 | type: Sequelize.CHAR, 30 | defaultValue: '1' 31 | }, 32 | create_by: { 33 | type: Sequelize.STRING(32) 34 | }, 35 | update_time: { 36 | type: Sequelize.DATE, 37 | get() { 38 | return this.getDataValue('update_time') 39 | ? moment(this.getDataValue('update_time')).format('YYYY-MM-DD HH:mm:ss') 40 | : null; 41 | } 42 | }, 43 | create_time: { 44 | type: Sequelize.DATE, 45 | defaultValue: Sequelize.NOW, 46 | get() { 47 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss'); 48 | } 49 | } 50 | }); 51 | 52 | module.exports = DictItemModel; 53 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/dashboard.js: -------------------------------------------------------------------------------- 1 | const MenusModel = require('../model/menus') 2 | const RolesModel = require('../model/roles') 3 | const UsersModel = require('../model/users') 4 | 5 | // 获取各实体数量 6 | exports.getCount = async (req, res, next) => { 7 | const userCount = await UsersModel.count() 8 | const roleCount = await RolesModel.count() 9 | const menuCount = await MenusModel.count({where:{type:'M'}}) 10 | const btnCount = await MenusModel.count({where:{type:'B'}}) 11 | return res.send({ 12 | code: 0, 13 | message: '获取成功', 14 | data: [ 15 | { entity:'用户数',key:'user',value:userCount}, 16 | { entity:'角色数',key:'role',value:roleCount}, 17 | { entity:'菜单数',key:'menu',value:menuCount}, 18 | { entity:'按钮数',key:'button',value:btnCount}, 19 | ] 20 | }) 21 | } 22 | 23 | // 获取模块菜单数量 24 | exports.getModuleMenuCount=function (req,res,next) { 25 | MenusModel.getListTree(req.query).then(function (menuTree) { 26 | if(!menuTree || !menuTree.length) 27 | return res.send({ 28 | code: 1, 29 | message: '获取失败', 30 | data: null 31 | }) 32 | const countArr=menuTree.map(item=>({ 33 | type:item.title, 34 | value:item.children?item.children.length:0 35 | })) 36 | return res.send({ 37 | code: 0, 38 | message: '获取成功', 39 | data: countArr 40 | }) 41 | }) 42 | } -------------------------------------------------------------------------------- /vue_ts-server/schema/dict-items.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi'); 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ); 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | // 定义参数的校验规则 17 | const id = joi.number().integer().min(0).required(); 18 | const dict_id = joi.number().integer().min(0).required(); 19 | const item_text = joi.string().required(); 20 | const item_value = [joi.number().min(0).required(), joi.string().required()]; 21 | const description = joi.string(); 22 | const sort_order = joi.number().integer().min(0); 23 | const status = joi.string().valid('0', '1'); 24 | // 字典项id数组 25 | const dictItem_ids = [joi.array().items(joi.number()).required(), joi.number().required()]; 26 | 27 | // 添加字典项的验证规则对象 28 | exports.add_dictItem_schema = joi.object().keys({ 29 | // 对res.body对象进行验证 30 | dict_id, 31 | item_text, 32 | item_value, 33 | description, 34 | sort_order, 35 | status 36 | }); 37 | // 修改字典项的验证规则对象 38 | exports.edit_dictItem_schema = joi.object().keys({ 39 | dict_id, 40 | item_text, 41 | item_value, 42 | description, 43 | sort_order, 44 | status 45 | }); 46 | // 删除字典项的验证规则对象 47 | exports.delete_dictItem_schema = joi.object().keys({ 48 | dictItem_ids 49 | }); 50 | // 获取单字典项的验证规则对象 51 | exports.get_dictItem_schema = joi.object().keys({ 52 | // 对query参数进行验证 53 | id 54 | }); 55 | -------------------------------------------------------------------------------- /vue_ts-server/schema/menu.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi') 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ) 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | // 菜单的校验规则 17 | const title = joi.string().required() 18 | const name = joi.string().alphanum() 19 | // const component = joi 20 | // .string() 21 | // .pattern(/^\/[^\s]*$/) 22 | // .allow('Layout') 23 | const component = joi.string().allow('Layout') 24 | // const path = joi.string().pattern(/^\/[^\s]*$/); 25 | const path = joi.string() 26 | const sort = joi.number().min(0).required() 27 | // 正则匹配相对路径 28 | const redirect = joi.string().pattern(/^\/[^\s]*$/) 29 | const permission = joi.string() 30 | const menu_id = joi.number().integer().min(0).required() 31 | const parent_id = joi.number().integer().min(0).required() 32 | const hidden = joi.number().valid(0, 1) 33 | const type = joi.string().valid('M', 'B', 'C').required() 34 | 35 | // 菜单的验证规则对象 36 | exports.add_menu_schema = joi.object().keys({ 37 | parent_id, 38 | title, 39 | sort, 40 | type, 41 | name, 42 | component, 43 | path, 44 | redirect, 45 | permission, 46 | hidden 47 | }) 48 | exports.edit_menu_schema = joi.object().keys({ 49 | parent_id, 50 | title, 51 | sort, 52 | type, 53 | name, 54 | component, 55 | path, 56 | redirect, 57 | permission, 58 | hidden 59 | }) 60 | exports.delete_menu_schema = joi.object().keys({ 61 | menu_id 62 | }) 63 | exports.get_menu_schema = joi.object().keys({ 64 | menu_id 65 | }) 66 | -------------------------------------------------------------------------------- /vue_ts-server/schema/dict.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi'); 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ); 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | // 字典名的校验规则 17 | const dict_name = joi.string().min(1).max(10).required(); 18 | // 定义其它参数的校验规则 19 | const id = joi.number().integer().min(0).required(); 20 | const dict_code = joi.string().required(); 21 | const description = joi.string(); 22 | const status = joi.string().valid('0', '1'); 23 | // 字典id数组 24 | const dict_ids = [joi.array().items(joi.number()).required(), joi.number()]; 25 | // 分页参数 26 | const pageSize = joi.number().required(); 27 | const currentPage = joi.number().required(); 28 | 29 | // 添加字典的验证规则对象 30 | exports.add_dict_schema = joi.object().keys({ 31 | // 对res.body对象进行验证 32 | dict_name, 33 | dict_code, 34 | description, 35 | status 36 | }); 37 | // 获取字典列表的验证规则对象 38 | exports.get_dict_list_schema = joi.object().keys({ 39 | pageSize, 40 | currentPage, 41 | dict_name: joi.string().min(1).max(10) 42 | }); 43 | // 修改字典的验证规则对象 44 | exports.edit_dict_schema = joi.object().keys({ 45 | dict_name, 46 | dict_code, 47 | description, 48 | status 49 | }); 50 | // 删除字典的验证规则对象 51 | exports.delete_dict_schema = joi.object().keys({ 52 | dict_ids 53 | }); 54 | // 获取单字典的验证规则对象 55 | exports.get_dict_schema = joi.object().keys({ 56 | // 对query参数进行验证 57 | id 58 | }); 59 | // 用名称获取单字典的验证规则对象 60 | exports.get_dict_by_code_schema = joi.object().keys({ 61 | // 对query参数进行验证 62 | dict_code 63 | }); 64 | -------------------------------------------------------------------------------- /vue_ts-server/utils/tools.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取树形结构数据 3 | * @param data 数据 4 | * @param level 父id层级 5 | * @param idField 字段名 6 | * @param pidField 上一级字段名 7 | * @returns {null|[]} 8 | */ 9 | const getTreeData = function (data, level = null, idField = 'menu_id', pidField = 'parent_id') { 10 | const _level = [] 11 | // 第一次进来获取所有父id 12 | if (level === null) { 13 | data.forEach(function (item) { 14 | _level.push(item[pidField]) 15 | }) 16 | level = Math.min(..._level) 17 | } 18 | let prefixPath = '' 19 | const getTreeInnerData = (data, level, idField = 'menu_id', pidField = 'parent_id') => { 20 | const tree = [] 21 | data.forEach(function (item) { 22 | if (item[pidField] === level) { 23 | tree.push(item) 24 | } 25 | }) 26 | if (tree.length === 0) { 27 | return null 28 | } 29 | // 对于父id为0的进行循环,然后查出父节点为上面结果id的节点内容 30 | tree.forEach(function (item) { 31 | if (item.type !== 'B') { 32 | if (_level.includes(item[idField])) prefixPath += '/' + item.path 33 | const childData = getTreeInnerData(data, item[idField], idField, pidField) 34 | if (childData != null) { 35 | item['menuPath'] = prefixPath 36 | item['children'] = childData 37 | prefixPath = '' 38 | } else { 39 | item['menuPath'] = prefixPath + '/' + item.path 40 | } 41 | } 42 | }) 43 | return tree 44 | } 45 | return getTreeInnerData(data, level, idField, pidField) 46 | } 47 | /** 48 | * 获取两个数组差集 49 | * @param arr1 50 | * @param arr2 51 | * @returns {*[]} 52 | */ 53 | const minustArr = function (arr1 = [], arr2 = []) { 54 | return arr1.filter(function (v) { 55 | return arr2.indexOf(v) === -1 56 | }) 57 | } 58 | 59 | module.exports = { 60 | getTreeData, 61 | minustArr 62 | } 63 | -------------------------------------------------------------------------------- /vue_ts-server/schema/role.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi') 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ) 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | // 角色名的校验规则 17 | const role_name = joi.string().min(1).max(10).required().messages({ 18 | 'string.empty': '角色名必填', 19 | 'any.required': '角色名必填', 20 | 'string.max': '角色名长度不能超过10' 21 | }) 22 | // 定义 id, nickname, emial 的验证规则 23 | const role_id = joi.number().integer().min(0).required().messages({ 24 | 'any.required': '角色id必填', 25 | 'number.base': '角色id为数字', 26 | 'number.min': '角色id最少1位' 27 | }) 28 | const remark = joi.string() 29 | const status = joi.string().valid('0', '1') 30 | // 角色id数组 31 | const role_ids = [ 32 | joi.array().items(joi.number()).required(), 33 | joi.number().messages({ 34 | 'any.required': '角色id必填', 35 | 'array.base': '角色id为数组' 36 | }) 37 | ] 38 | // 分页参数 39 | const pageSize = joi.number().required() 40 | const currentPage = joi.number().required() 41 | 42 | // 添加角色的验证规则对象 43 | exports.add_role_schema = joi.object().keys({ 44 | // 对res.body对象进行验证 45 | role_name, 46 | remark, 47 | status 48 | }) 49 | // 获取角色列表的验证规则对象 50 | exports.get_role_list_schema = joi.object().keys({ 51 | pageSize, 52 | currentPage, 53 | role_name: joi.string().min(1).max(10) 54 | }) 55 | // 修改角色的验证规则对象 56 | exports.edit_role_schema = joi.object().keys({ 57 | role_name, 58 | remark, 59 | status 60 | }) 61 | // 删除角色的验证规则对象 62 | exports.delete_role_schema = joi.object().keys({ 63 | role_ids 64 | }) 65 | // 获取单角色的验证规则对象 66 | exports.get_role_schema = joi.object().keys({ 67 | // 对query参数进行验证 68 | role_id 69 | }) 70 | -------------------------------------------------------------------------------- /vue_ts-server/model/dict.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const moment = require('moment'); 3 | const sequelize = require('./init'); 4 | const DictItemModel = require('./dict-items'); 5 | // 定义表的模型 6 | const DictModel = sequelize.define('dict', { 7 | id: { 8 | type: Sequelize.INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true 11 | }, 12 | dict_name: { 13 | type: Sequelize.STRING(255) 14 | }, 15 | dict_code: { 16 | type: Sequelize.STRING(255) 17 | }, 18 | description: { 19 | type: Sequelize.STRING(255) 20 | }, 21 | status: { 22 | type: Sequelize.CHAR, 23 | defaultValue: '1' 24 | }, 25 | create_by: { 26 | type: Sequelize.STRING(32) 27 | }, 28 | update_time: { 29 | type: Sequelize.DATE, 30 | get() { 31 | return this.getDataValue('update_time') 32 | ? moment(this.getDataValue('update_time')).format('YYYY-MM-DD HH:mm:ss') 33 | : null; 34 | } 35 | }, 36 | create_time: { 37 | type: Sequelize.DATE, 38 | defaultValue: Sequelize.NOW, 39 | get() { 40 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss'); 41 | } 42 | } 43 | }); 44 | 45 | // 删除字典关联字典项都删除 46 | DictModel.delDict = async function (dict_ids) { 47 | const t = await sequelize.transaction(); 48 | try { 49 | // 先删除字典表中字典id数组的角色 50 | await DictModel.destroy({ 51 | where: { id: dict_ids } 52 | }); 53 | // 再删除字典项表中的字典项 54 | await DictItemModel.destroy({ 55 | where: { dict_id: dict_ids } 56 | }); 57 | t.commit(); 58 | return true; 59 | } catch (e) { 60 | t.rollback(); 61 | return false; 62 | } 63 | }; 64 | 65 | // 建立关联 66 | DictModel.hasMany(DictItemModel, { 67 | foreignKey: 'dict_id' 68 | }); 69 | DictItemModel.belongsTo(DictModel, { 70 | foreignKey: 'dict_id' 71 | }); 72 | module.exports = DictModel; 73 | -------------------------------------------------------------------------------- /vue_ts-server/app.js: -------------------------------------------------------------------------------- 1 | // 导入 express 模块 2 | const express = require('express') 3 | // 创建 express 的服务器实例 4 | const app = express() 5 | 6 | // 导入 cors 中间件 7 | const cors = require('cors') 8 | // 将 cors 注册为全局中间件 9 | app.use(cors()) 10 | // 导入验证规则中间件 11 | const joi = require('joi') 12 | 13 | const bodyParser = require('body-parser') 14 | app.use( 15 | bodyParser.urlencoded({ 16 | extended: true 17 | }) 18 | ) 19 | app.use(bodyParser.json()) 20 | // 开启静态资源的访问 21 | app.use('/public/avatar', express.static('./public/avatar')) 22 | // 导入配置文件 23 | const config = require('./config/index') 24 | // 解析 token 的中间件 25 | const expressJWT = require('express-jwt') 26 | // 使用 .unless({ path: [/^\/api\//] }) 指定哪些接口不需要进行 Token 的身份认证 27 | app.use( 28 | expressJWT({ secret: config.jwtSecretKey }).unless({ 29 | path: ['/user/login', '/user/checkCode', '/user/refreshToken', '/user/addUser'] 30 | }) 31 | ) 32 | // 导入并注册仪表盘模块 33 | const dashboardRouter = require('./router/dashboard') 34 | app.use('/dashboard', dashboardRouter) 35 | // 导入并注册用户路由模块 36 | const userRouter = require('./router/user') 37 | app.use('/user', userRouter) 38 | // 导入并注册用户角色模块 39 | const roleRouter = require('./router/role') 40 | app.use('/user/role', roleRouter) 41 | // 导入并注册用户菜单模块 42 | const menuRouter = require('./router/menu') 43 | app.use('/user/menu', menuRouter) 44 | // 导入用户信息路由模块 45 | const userinfoRouter = require('./router/userinfo') 46 | app.use('/user/myInfo', userinfoRouter) 47 | // 导入字典路由模块 48 | const dictRouter = require('./router/dict') 49 | app.use('/dict', dictRouter) 50 | // 导入字典项路由模块 51 | const dictItemRouter = require('./router/dict-items') 52 | app.use('/dict/item', dictItemRouter) 53 | // 定义错误级别的中间件 54 | app.use((err, req, res, next) => { 55 | // 数据验证失败 56 | if (err instanceof joi.ValidationError) return res.send({ code: 1, message: err.message }) 57 | // token解析失败 58 | if (err.name === 'UnauthorizedError') return res.send({ code: 401, message: '身份认证失败' }) 59 | // 未知错误 60 | return res.send({ code: 500, message: err.message }) 61 | }) 62 | // 调用 app.listen 方法,指定端口号并启动web服务器 63 | app.listen(9999, function () { 64 | console.log('api server running at http://127.0.0.1:9999') 65 | }) 66 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/dict-items.js: -------------------------------------------------------------------------------- 1 | const DictItemModel = require('../model/dict-items') 2 | // 导入需要的验证规则对象 3 | const { 4 | add_dictItem_schema, 5 | edit_dictItem_schema, 6 | delete_dictItem_schema, 7 | get_dictItem_schema 8 | } = require('../schema/dict-items') 9 | 10 | // 添加字典项接口 11 | exports.addDictItem = (req, res, next) => { 12 | const { value, error } = add_dictItem_schema.validate(req.body) 13 | if (error) { 14 | return next(error) 15 | } 16 | value.create_by = req.user.username 17 | DictItemModel.create(value).then(function (dictItem) { 18 | if (!dictItem) { 19 | return res.send({ 20 | code: 1, 21 | message: '创建失败', 22 | data: null 23 | }) 24 | } 25 | return res.send({ 26 | code: 0, 27 | message: '创建成功', 28 | data: dictItem 29 | }) 30 | }) 31 | } 32 | // 编辑字典项接口 33 | exports.editDictItem = (req, res, next) => { 34 | const { value, error } = edit_dictItem_schema.validate(req.body) 35 | if (error) { 36 | return next(error) 37 | } 38 | value.update_time = new Date() 39 | DictItemModel.update(value, { 40 | where: req.query 41 | }).then(function (dictItem) { 42 | if (!dictItem) { 43 | return res.send({ 44 | code: 1, 45 | message: '修改失败', 46 | data: null 47 | }) 48 | } 49 | return res.send({ 50 | code: 0, 51 | message: '修改成功', 52 | data: dictItem 53 | }) 54 | }) 55 | } 56 | // 删除字典项接口 57 | exports.deleteDictItem = (req, res, next) => { 58 | const { value, error } = delete_dictItem_schema.validate(req.body) 59 | if (error) { 60 | return next(error) 61 | } 62 | const id = value.id 63 | DictItemModel.destroy({ 64 | where: { id: id } 65 | }).then(function (dictItem) { 66 | if (!dictItem) { 67 | return res.send({ 68 | code: 1, 69 | message: '删除失败', 70 | data: null 71 | }) 72 | } 73 | return res.send({ 74 | code: 0, 75 | message: '删除成功', 76 | data: dictItem 77 | }) 78 | }) 79 | } 80 | // 获取单字典项接口 81 | exports.getOneDictItem = (req, res, next) => { 82 | const { value, error } = get_dictItem_schema.validate(req.query) 83 | if (error) { 84 | return next(error) 85 | } 86 | DictItemModel.findOne({ 87 | where: value 88 | }).then(function (dictItem) { 89 | return res.send({ 90 | code: 0, 91 | message: '获取成功', 92 | data: dictItem 93 | }) 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /vue_ts-server/schema/user.js: -------------------------------------------------------------------------------- 1 | let joi = require('joi') 2 | // 允许未设置规则的未知键 3 | joi = joi.defaults((schema) => 4 | schema.options({ 5 | allowUnknown: true 6 | }) 7 | ) 8 | /** 9 | * string() 值必须是字符串 10 | * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 11 | * min(length) 最小长度 12 | * max(length) 最大长度 13 | * required() 值是必填项,不能为 undefined 14 | * pattern(正则表达式) 值必须符合正则表达式的规则 15 | */ 16 | // 用户id的校验规则 17 | const user_id = joi.number().min(0).required() 18 | // 用户名的校验规则 19 | const username = joi.string().alphanum().min(1).max(10).required().messages({ 20 | 'string.empty': '用户名必填', 21 | 'any.required': '用户名必填', 22 | 'string.alphanum': '用户名只能包含a-zA-Z0-9', 23 | 'string.max': '用户名长度不能超过10' 24 | }) 25 | // 密码的验证规则 26 | const password = joi 27 | .string() 28 | .pattern(/^[\S]{6,12}$/) 29 | .required() 30 | .messages({ 31 | 'string.empty': '密码必填', 32 | 'any.required': '密码必填', 33 | 'string.pattern.base': '密码为6-12位字符' 34 | }) 35 | const checkCode = joi.string().alphanum().min(4).max(4).required().messages({ 36 | 'string.empty': '验证码必填', 37 | 'any.required': '验证码必填', 38 | 'string.alphanum': '验证码格式错误', 39 | 'string.max': '验证码长度不能超过4' 40 | }) 41 | const uuid = joi.number().required() 42 | const nickname = joi.string() 43 | const email = joi.string().email() 44 | const status = joi.string().valid('0', '1') 45 | const pageSize = joi.number().required() 46 | const currentPage = joi.number().required() 47 | const role_ids = joi.array().items(joi.number()).required().messages({ 48 | 'any.required': '角色必填', 49 | 'array.base': '角色id为数组' 50 | }) 51 | const user_ids = [ 52 | joi.array().items(joi.number()).required(), 53 | joi.number().messages({ 54 | 'any.required': '用户id必填' 55 | }) 56 | ] 57 | // 登录表单的验证规则对象 58 | exports.user_login_schema = joi.object().keys({ 59 | username, 60 | password, 61 | checkCode, 62 | uuid 63 | }) 64 | // 添加用户接口 65 | exports.add_user_schema = joi.object().keys({ 66 | username, 67 | password, 68 | status, 69 | role_ids 70 | }) 71 | // 获取用户列表接口 72 | exports.get_list = joi.object().keys({ 73 | pageSize, 74 | currentPage, 75 | status 76 | }) 77 | // 更新用户接口 78 | exports.update_user_schema = joi.object().keys({ 79 | username: joi.string().alphanum().min(1).max(10), 80 | status, 81 | nickname, 82 | email, 83 | role_ids 84 | }) 85 | // 重置密码 86 | exports.edit_password_schema = joi.object().keys({ 87 | user_id, 88 | // 使用 password 这个规则,验证 req.body.oldPwd 的值 89 | old_password: password, 90 | // 使用 joi.not(joi.ref('oldPwd')).concat(password) 规则,验证 req.body.newPwd 的值 91 | // 解读: 92 | // 1. joi.ref('oldPwd') 表示 newPwd 的值必须和 oldPwd 的值保持一致 93 | // 2. joi.not(joi.ref('oldPwd')) 表示 newPwd 的值不能等于 oldPwd 的值 94 | // 3. .concat() 用于合并 joi.not(joi.ref('oldPwd')) 和 password 这两条验证规则 95 | password: joi.not(joi.ref('old_password')).concat(password), 96 | repassword: joi.ref('password') 97 | }) 98 | // 根据id获取用户信息 99 | exports.get_userInfoById_schema = joi.object().keys({ 100 | user_id 101 | }) 102 | // 删除用户 103 | exports.delete_user_schema = joi.object().keys({ 104 | user_ids 105 | }) 106 | -------------------------------------------------------------------------------- /vue_ts-server/model/menus.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const moment = require('moment') 3 | const sequelize = require('./init') 4 | const tools = require('../utils/tools') 5 | const RolesMenusModel = require('./roles-menus') 6 | const { Op } = Sequelize 7 | // 定义表的模型 8 | const MenusModel = sequelize.define('menus', { 9 | menu_id: { 10 | type: Sequelize.INTEGER, 11 | primaryKey: true, 12 | autoIncrement: true 13 | }, 14 | parent_id: { 15 | type: Sequelize.INTEGER, 16 | defaultValue: 0 17 | }, 18 | title: { 19 | type: Sequelize.STRING(255), 20 | defaultValue: '' 21 | }, 22 | sort: { 23 | type: Sequelize.INTEGER, 24 | defaultValue: 0 25 | }, 26 | type: { 27 | type: Sequelize.CHAR(1), 28 | defaultValue: 'M' 29 | }, 30 | icon: { 31 | type: Sequelize.STRING(255) 32 | }, 33 | component: { 34 | type: Sequelize.STRING(255) 35 | }, 36 | path: { 37 | type: Sequelize.STRING(255) 38 | }, 39 | permission: { 40 | type: Sequelize.STRING(255) 41 | }, 42 | redirect: { 43 | type: Sequelize.STRING(255) 44 | }, 45 | hidden: { 46 | type: Sequelize.TINYINT(1), 47 | defaultValue: 0 48 | }, 49 | update_time: { 50 | type: Sequelize.DATE, 51 | get() { 52 | return this.getDataValue('update_time') 53 | ? moment(this.getDataValue('update_time')).format('YYYY-MM-DD HH:mm:ss') 54 | : null 55 | } 56 | }, 57 | create_time: { 58 | type: Sequelize.DATE, 59 | defaultValue: Sequelize.NOW, 60 | get() { 61 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss') 62 | } 63 | } 64 | }) 65 | // 获得权限的树状数据结构 66 | MenusModel.getListTree = async function (params = {}) { 67 | let menus = [] 68 | let where = {} 69 | // 查询数据库获得元数据 70 | const { title, type, path, menu_id } = params 71 | if (title) 72 | where.title = { 73 | [Op.like]: `%${title}%` 74 | } 75 | if (type) where.type = type 76 | if (path) where.path = path 77 | if (menu_id) where.menu_id = menu_id 78 | menus = await MenusModel.findAll({ 79 | where: where, 80 | order: [['sort']] 81 | }) 82 | // 将元数据转换为单纯的数据集 83 | const menusArr = menus.map(function (item) { 84 | return item.get({ plain: true }) 85 | }) 86 | // 将数据集转换为树状结构 87 | return tools.getTreeData(menusArr, null, 'menu_id') 88 | } 89 | 90 | // 删除菜单、子菜单及其角色权限表中含有此id权限的记录 91 | MenusModel.deleteMenu = async function (menu_id) { 92 | const t = await sequelize.transaction() 93 | try { 94 | let delete_ids = [] 95 | // 找到菜单表中所有为此menu_id和父id为此menu_id的记录 96 | const menus = await MenusModel.findAll({ 97 | where: { [Op.or]: [{ menu_id: menu_id }, { parent_id: menu_id }] } 98 | }) 99 | // 只保留菜单id 100 | delete_ids = menus.map((item) => { 101 | return item.menu_id 102 | }) 103 | // 删除权限表中对应记录 104 | await MenusModel.destroy({ 105 | where: { menu_id: delete_ids } 106 | }) 107 | // 删除角色权限表对应记录 108 | await RolesMenusModel.destroy({ 109 | where: { menu_id: delete_ids } 110 | }) 111 | 112 | t.commit() 113 | return true 114 | } catch (e) { 115 | t.rollback() 116 | return e.message 117 | } 118 | } 119 | // 导出菜单模型 120 | module.exports = MenusModel 121 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/dict.js: -------------------------------------------------------------------------------- 1 | const DictModel = require('../model/dict') 2 | const DictItemModel = require('../model/dict-items.js') 3 | // 导入需要的验证规则对象 4 | const { 5 | get_dict_list_schema, 6 | add_dict_schema, 7 | edit_dict_schema, 8 | delete_dict_schema, 9 | get_dict_schema, 10 | get_dict_by_code_schema 11 | } = require('../schema/dict') 12 | // 导入op模块 13 | const { Op } = require('sequelize') 14 | // 获取字典列表接口 15 | exports.getList = (req, res, next) => { 16 | const { value, error } = get_dict_list_schema.validate(req.query) 17 | if (error) { 18 | return next(error) 19 | } 20 | // 接收前端参数 21 | let { pageSize, currentPage } = value 22 | // 默认值 23 | limit = pageSize ? Number(pageSize) : 10 24 | offset = currentPage ? Number(currentPage) : 1 25 | offset = (offset - 1) * pageSize 26 | let where = {} 27 | let dict_name = value.dict_name 28 | let status = value.status 29 | if (dict_name) { 30 | where.dict_name = { [Op.like]: `%${dict_name}%` } 31 | } 32 | if (status) { 33 | where.status = { [Op.eq]: status } 34 | } 35 | DictModel.findAndCountAll({ 36 | offset: offset, 37 | limit: limit, 38 | distinct: true, 39 | include: [{ model: DictItemModel }], 40 | where: where, 41 | order: [ 42 | ['create_time', 'desc'], 43 | [DictItemModel, 'sort_order'] 44 | ] 45 | }).then(function (dicts) { 46 | return res.send({ 47 | code: 0, 48 | message: '获取成功', 49 | data: dicts 50 | }) 51 | }) 52 | } 53 | 54 | // 添加字典接口 55 | exports.addDict = (req, res, next) => { 56 | const { value, error } = add_dict_schema.validate(req.body) 57 | if (error) { 58 | return next(error) 59 | } 60 | value.create_by = req.user.username 61 | DictModel.create(value).then(function (dict) { 62 | if (!dict) { 63 | return res.send({ 64 | code: 1, 65 | message: '创建失败', 66 | data: null 67 | }) 68 | } 69 | return res.send({ 70 | code: 0, 71 | message: '创建成功', 72 | data: dict 73 | }) 74 | }) 75 | } 76 | // 编辑字典接口 77 | exports.editDict = (req, res, next) => { 78 | const { value, error } = edit_dict_schema.validate(req.body) 79 | if (error) { 80 | return next(error) 81 | } 82 | value.update_time = new Date() 83 | DictModel.update(value, { 84 | where: req.query 85 | }).then(function (dict) { 86 | if (!dict) { 87 | return res.send({ 88 | code: 1, 89 | message: '修改失败', 90 | data: null 91 | }) 92 | } 93 | return res.send({ 94 | code: 0, 95 | message: '修改成功', 96 | data: dict 97 | }) 98 | }) 99 | } 100 | // 删除字典接口 101 | exports.deleteDict = (req, res, next) => { 102 | const { value, error } = delete_dict_schema.validate(req.body) 103 | if (error) { 104 | return next(error) 105 | } 106 | const dict_ids = value.id 107 | DictModel.delDict(dict_ids || []).then(function (dict) { 108 | if (dict !== true) { 109 | return res.send({ 110 | code: 1, 111 | message: '删除失败', 112 | data: null 113 | }) 114 | } 115 | return res.send({ 116 | code: 0, 117 | message: '删除成功', 118 | data: dict 119 | }) 120 | }) 121 | } 122 | // 获取单字典接口 123 | exports.getOneDict = (req, res, next) => { 124 | const { value, error } = get_dict_schema.validate(req.query) 125 | if (error) { 126 | return next(error) 127 | } 128 | DictModel.findOne({ 129 | where: value 130 | }).then(function (dict) { 131 | return res.send({ 132 | code: 0, 133 | message: '获取成功', 134 | data: dict 135 | }) 136 | }) 137 | } 138 | // 根据名称获取字典信息 139 | exports.getDictByName = (req, res, next) => { 140 | const { value, error } = get_dict_by_code_schema.validate(req.query) 141 | if (error) { 142 | return next(error) 143 | } 144 | DictModel.findOne({ 145 | where: value, 146 | include: [{ model: DictItemModel }], 147 | order: [[DictItemModel, 'sort_order']] 148 | }).then(function (dict) { 149 | return res.send({ 150 | code: 0, 151 | message: '获取成功', 152 | data: dict.dict_items 153 | }) 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /vue_ts-server/model/users.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const moment = require('moment'); 3 | const sequelize = require('./init'); 4 | const RolesModel = require('./roles'); 5 | const UsersRolesModel = require('./users-roles'); 6 | // 引入工具方法 7 | const tools = require('../utils/tools'); 8 | // 定义表的模型 define方法第一个参数为表名,第二个参数为表字段对象 9 | const UsersModel = sequelize.define('users', { 10 | user_id: { 11 | // 数据类型 12 | type: Sequelize.INTEGER, 13 | // 主键 14 | primaryKey: true, 15 | // 自增 16 | autoIncrement: true 17 | }, 18 | username: { 19 | type: Sequelize.STRING(255) 20 | }, 21 | nickname: { 22 | type: Sequelize.STRING(255) 23 | }, 24 | email: { 25 | type: Sequelize.STRING(255) 26 | }, 27 | password: { 28 | type: Sequelize.CHAR(32) 29 | }, 30 | user_pic: { 31 | type: Sequelize.TEXT 32 | }, 33 | status: { 34 | type: Sequelize.CHAR, 35 | defaultValue: '1' 36 | }, 37 | update_time: { 38 | type: Sequelize.DATE, 39 | // 格式化日期时间戳 40 | get() { 41 | return this.getDataValue('update_time') 42 | ? moment(this.getDataValue('update_time')).format('YYYY-MM-DD HH:mm:ss') 43 | : null; 44 | } 45 | }, 46 | create_time: { 47 | type: Sequelize.DATE, 48 | defaultValue: Sequelize.NOW, 49 | get() { 50 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss'); 51 | } 52 | } 53 | }); 54 | // 添加用户的方法 55 | UsersModel.addUser = async function (data) { 56 | // 首先,我们开始一个事务并将其保存到变量中 57 | const t = await sequelize.transaction(); 58 | try { 59 | // 然后,我们进行一些调用以将此事务作为参数传递: 60 | // 添加用户 61 | const user = await UsersModel.create(data); 62 | // 遍历前端传来的用户角色id并添加到用户角色表 63 | const users_roles = data.role_ids.map(function (role_id) { 64 | return { 65 | user_id: user.user_id, 66 | role_id: role_id 67 | }; 68 | }); 69 | await UsersRolesModel.bulkCreate(users_roles); 70 | // 我们提交事务. 71 | t.commit(); 72 | return true; 73 | } catch (e) { 74 | // 如果执行到达此行,则抛出错误. 75 | // 我们回滚事务. 76 | t.rollback(); 77 | return e.message; 78 | } 79 | }; 80 | // 修改用户的方法 81 | UsersModel.updateUser = async function (user_id, data) { 82 | const t = await sequelize.transaction(); 83 | try { 84 | // 获得修改时间 85 | data.update_time = new Date(); 86 | // 先更新用户 87 | await UsersModel.update(data, { 88 | where: { 89 | user_id: user_id 90 | } 91 | }); 92 | // 再得到用户角色表中此用户的角色id 93 | const users_roles = await UsersRolesModel.findAll({ 94 | where: { user_id: user_id } 95 | }); 96 | // 将表中获得的角色id转换为数组 97 | const role_ids = users_roles.map(function (item) { 98 | return item.role_id; 99 | }); 100 | // 新加的角色加到用户角色表中 101 | const add_role_ids = tools.minustArr(data.role_ids, role_ids); 102 | const add_users_roles = add_role_ids.map(function (role_id) { 103 | return { user_id: user_id, role_id: role_id }; 104 | }); 105 | await UsersRolesModel.bulkCreate(add_users_roles); 106 | // 删除的角色从用户角色表删除 107 | const del_role_ids = tools.minustArr(role_ids, data.role_ids); 108 | if (del_role_ids && del_role_ids.length > 0) { 109 | await UsersRolesModel.destroy({ 110 | where: { 111 | user_id: user_id, 112 | role_id: del_role_ids 113 | } 114 | }); 115 | } 116 | t.commit(); 117 | return true; 118 | } catch (e) { 119 | t.rollback(); 120 | return e.message; 121 | } 122 | }; 123 | // 删除用户的方法 124 | UsersModel.delUser = async function (user_ids) { 125 | const t = await sequelize.transaction(); 126 | try { 127 | await UsersModel.destroy({ 128 | where: { user_id: user_ids } 129 | }); 130 | await UsersRolesModel.destroy({ 131 | where: { user_id: user_ids } 132 | }); 133 | t.commit(); 134 | return true; 135 | } catch (e) { 136 | t.rollback(); 137 | return false; 138 | } 139 | }; 140 | // 建立关联 141 | UsersModel.belongsToMany(RolesModel, { 142 | through: { 143 | model: UsersRolesModel 144 | }, 145 | foreignKey: 'user_id', 146 | otherKey: 'role_id' 147 | }); 148 | // 导出用户映射模型 149 | module.exports = UsersModel; 150 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/role.js: -------------------------------------------------------------------------------- 1 | const RoleModel = require('../model/roles') 2 | // 导入需要的验证规则对象 3 | const { 4 | get_role_list_schema, 5 | add_role_schema, 6 | edit_role_schema, 7 | delete_role_schema, 8 | get_role_schema 9 | } = require('../schema/role') 10 | // 导入op模块 11 | const { Op } = require('sequelize') 12 | // 获取角色列表接口 13 | exports.getList = (req, res, next) => { 14 | const { value, error } = get_role_list_schema.validate(req.query) 15 | if (error) { 16 | return next(error) 17 | } 18 | // 接收前端参数 19 | let { pageSize, currentPage } = value 20 | // 默认值 21 | limit = pageSize ? Number(pageSize) : 10 22 | offset = currentPage ? Number(currentPage) : 1 23 | offset = (offset - 1) * pageSize 24 | let where = {} 25 | const { role_name, status } = value 26 | if (role_name) where.role_name = { [Op.like]: `%${role_name}%` } 27 | 28 | if (status) where.status = { [Op.eq]: status } 29 | 30 | RoleModel.findAndCountAll({ 31 | offset: offset, 32 | limit: limit, 33 | where: where 34 | }).then(function (roles) { 35 | return res.send({ 36 | code: 0, 37 | message: '获取成功', 38 | data: roles 39 | }) 40 | }) 41 | } 42 | 43 | exports.getAllRole = (req, res) => { 44 | RoleModel.findAll({ 45 | where: { 46 | status: '1' 47 | } 48 | }).then(function (roles) { 49 | return res.send({ 50 | code: 0, 51 | message: '获取成功', 52 | data: roles 53 | }) 54 | }) 55 | } 56 | 57 | exports.getRoleResource = (req, res) => { 58 | const role_id = req.query.role_id 59 | RoleModel.getResource(role_id).then(function (resource) { 60 | if (!resource) { 61 | return res.send({ 62 | code: 1, 63 | message: '获取角色权限失败', 64 | data: null 65 | }) 66 | } 67 | return res.send({ 68 | code: 0, 69 | message: '获取角色权限成功', 70 | data: resource 71 | }) 72 | }) 73 | } 74 | 75 | exports.getRoleAuth = (req, res) => { 76 | const role_id = req.query.role_id 77 | RoleModel.getAuth(role_id).then(function (resource) { 78 | if (!resource) { 79 | return res.send({ 80 | code: 1, 81 | message: '获取角色权限失败', 82 | data: null 83 | }) 84 | } 85 | return res.send({ 86 | code: 0, 87 | message: '获取角色权限成功', 88 | data: resource 89 | }) 90 | }) 91 | } 92 | 93 | exports.updateRoleResource = (req, res) => { 94 | const role_id = req.query.role_id 95 | const data = req.body 96 | const all_ids = data.all_ids ?? data.menu_ids.concat(data.permIds) 97 | RoleModel.updateResource(role_id, all_ids).then(function (resource) { 98 | if (resource !== true) { 99 | return res.send({ 100 | code: 1, 101 | message: '修改失败', 102 | data: null 103 | }) 104 | } 105 | return res.send({ 106 | code: 0, 107 | message: '修改成功', 108 | data: resource 109 | }) 110 | }) 111 | } 112 | 113 | // 添加角色接口 114 | exports.addRole = (req, res, next) => { 115 | const { value, error } = add_role_schema.validate(req.body) 116 | if (error) { 117 | return next(error) 118 | } 119 | RoleModel.create(value).then(function (role) { 120 | if (!role) { 121 | return res.send({ 122 | code: 1, 123 | message: '创建失败', 124 | data: null 125 | }) 126 | } 127 | return res.send({ 128 | code: 0, 129 | message: '创建成功', 130 | data: role 131 | }) 132 | }) 133 | } 134 | // 编辑角色接口 135 | exports.editRole = (req, res, next) => { 136 | const { value, error } = edit_role_schema.validate(req.body) 137 | if (error) { 138 | return next(error) 139 | } 140 | value.update_time = new Date() 141 | RoleModel.update(value, { 142 | where: req.query 143 | }).then(function (role) { 144 | if (!role) { 145 | return res.send({ 146 | code: 1, 147 | message: '修改失败', 148 | data: null 149 | }) 150 | } 151 | return res.send({ 152 | code: 0, 153 | message: '修改成功', 154 | data: role 155 | }) 156 | }) 157 | } 158 | // 删除角色接口 159 | exports.deleteRole = (req, res, next) => { 160 | const { value, error } = delete_role_schema.validate(req.body) 161 | if (error) { 162 | return next(error) 163 | } 164 | const role_ids = value.role_ids 165 | if ((role_ids.length && role_ids.includes(1)) || role_ids === 1) 166 | return res.send({ 167 | code: 1, 168 | message: '超级管理员角色不可删除', 169 | data: null 170 | }) 171 | RoleModel.delRole(role_ids || []).then(function (role) { 172 | if (role !== true) { 173 | return res.send({ 174 | code: 1, 175 | message: '删除失败', 176 | data: null 177 | }) 178 | } 179 | return res.send({ 180 | code: 0, 181 | message: '删除成功', 182 | data: role 183 | }) 184 | }) 185 | } 186 | // 获取单角色接口 187 | exports.getOneRole = (req, res, next) => { 188 | const { value, error } = get_role_schema.validate(req.query) 189 | if (error) { 190 | return next(error) 191 | } 192 | RoleModel.findOne({ 193 | where: value 194 | }).then(function (role) { 195 | return res.send({ 196 | code: 0, 197 | message: '获取成功', 198 | data: role 199 | }) 200 | }) 201 | } 202 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/menu.js: -------------------------------------------------------------------------------- 1 | const MenusModel = require('../model/menus') 2 | const Sequelize = require('sequelize') 3 | const Op = Sequelize.Op 4 | // 导入需要的验证规则对象 5 | const { add_menu_schema, edit_menu_schema, delete_menu_schema, get_menu_schema } = require('../schema/menu') 6 | // 将菜单格式化为{value,label}的形式 7 | function filterRoutes(routes) { 8 | const res = [] 9 | routes.forEach((item) => { 10 | // 目录、菜单是否存在孩子 11 | if (item.children) { 12 | // 检测菜单孩子是否存在按钮 13 | if (item.children.some((item) => item.type === 'B')) { 14 | const perms = [] 15 | const children = [] 16 | // 若是按钮的用perm数组存储、是菜单的用children存储 17 | item.children.forEach((_item) => { 18 | if (_item.type === 'B') { 19 | perms.push({ 20 | value: _item.menu_id, 21 | label: _item.title, 22 | permission: _item.permission 23 | }) 24 | } else { 25 | children.push(_item) 26 | } 27 | }) 28 | const menuItem = { 29 | value: item.menu_id, 30 | label: item.title, 31 | children: children || undefined, 32 | perms: perms || undefined 33 | } 34 | // 继续递归判断菜单之下是否还有孩子 35 | if (menuItem.children && menuItem.children.length) { 36 | menuItem.children = filterRoutes(menuItem.children) 37 | } 38 | res.push(menuItem) 39 | } else { 40 | const menuItem = { 41 | value: item.menu_id, 42 | label: item.title, 43 | children: item.children || undefined 44 | } 45 | // 继续递归判断菜单之下是否还有孩子 46 | if (menuItem.children && menuItem.children.length) { 47 | menuItem.children = filterRoutes(menuItem.children) 48 | } 49 | res.push(menuItem) 50 | } 51 | } else { 52 | const menuItem = { 53 | value: item.menu_id, 54 | label: item.title 55 | } 56 | 57 | res.push(menuItem) 58 | } 59 | }) 60 | return res 61 | } 62 | function filterAuthTree(tree) { 63 | return tree.map((item) => { 64 | return { 65 | key: item.menu_id, 66 | title: item.title, 67 | children: item.children && item.children.length ? filterAuthTree(item.children) : undefined 68 | } 69 | }) 70 | } 71 | exports.getMenuList = (req, res) => { 72 | MenusModel.getListTree(req.query).then(function (menuTree) { 73 | return res.send({ 74 | code: 0, 75 | message: '获取成功', 76 | data: menuTree || [] 77 | }) 78 | }) 79 | } 80 | 81 | exports.getMenuOptions = (req, res) => { 82 | MenusModel.getListTree(req.query).then(function (menuTree) { 83 | const filterTree = filterRoutes(menuTree) 84 | return res.send({ 85 | code: 0, 86 | message: '获取成功', 87 | data: filterTree || [] 88 | }) 89 | }) 90 | } 91 | 92 | exports.getAuthOptions = (req, res) => { 93 | MenusModel.getListTree(req.query).then(function (anthTree) { 94 | const filterTree = filterAuthTree(anthTree) 95 | return res.send({ 96 | code: 0, 97 | message: '获取成功', 98 | data: filterTree || [] 99 | }) 100 | }) 101 | } 102 | 103 | exports.addMenu = (req, res, next) => { 104 | // 校验入参 105 | const { value, error } = add_menu_schema.validate(req.body) 106 | if (error) { 107 | return next(error) 108 | } 109 | // 创建数据库条目 110 | MenusModel.create(value).then(function (menu) { 111 | // 返回信息 112 | if (!menu) { 113 | return res.send({ 114 | code: 1, 115 | message: '创建失败', 116 | data: null 117 | }) 118 | } 119 | return res.send({ 120 | code: 0, 121 | message: '创建成功', 122 | data: menu.menu_id 123 | }) 124 | }) 125 | } 126 | 127 | exports.editMenu = (req, res, next) => { 128 | const { value, error } = edit_menu_schema.validate(req.body) 129 | if (error) { 130 | return next(error) 131 | } 132 | delete value.menu_id 133 | value.update_time = new Date() 134 | MenusModel.update(value, { 135 | where: { 136 | menu_id: req.query.menu_id || 0 137 | } 138 | }).then(function (menu) { 139 | if (!menu) { 140 | return res.send({ 141 | code: 1, 142 | message: '修改失败', 143 | data: null 144 | }) 145 | } 146 | return res.send({ 147 | code: 0, 148 | message: '修改成功', 149 | data: menu 150 | }) 151 | }) 152 | } 153 | 154 | exports.deleteMenu = (req, res, next) => { 155 | const { value, error } = delete_menu_schema.validate(req.body) 156 | if (error) { 157 | return next(error) 158 | } 159 | const { menu_id }=value 160 | const noDeleteMenuIds=[1,2,3,40,41,42,43,44,45,46,47,48,49,50,51] 161 | if (noDeleteMenuIds.includes(menu_id)) 162 | return res.send({ 163 | code: 1, 164 | message: '配置权限菜单不可删除', 165 | data: null 166 | }) 167 | MenusModel.deleteMenu(value.menu_id).then(function (menu) { 168 | if (menu === true) { 169 | return res.send({ 170 | code: 0, 171 | message: '删除成功', 172 | data: null 173 | }) 174 | } else { 175 | return res.send({ 176 | code: 1, 177 | message: '删除失败', 178 | data: null 179 | }) 180 | } 181 | }) 182 | // MenusModel.destroy({ 183 | // where: { 184 | // [Op.or]: [{ menu_id: value.menu_id }, { parent_id: value.menu_id }] 185 | // } 186 | // }).then(function (menu) { 187 | // return res.send({ 188 | // code: 0, 189 | // message: '删除成功', 190 | // data: menu 191 | // }); 192 | // }); 193 | } 194 | 195 | exports.getOneMenu = (req, res, next) => { 196 | const { value, error } = get_menu_schema.validate(req.query) 197 | if (error) { 198 | return next(error) 199 | } 200 | MenusModel.findOne({ 201 | where: { 202 | menu_id: value.menu_id 203 | } 204 | }).then(function (menu) { 205 | if (!menu) 206 | return res.send({ 207 | code: 1, 208 | message: '获取失败', 209 | data: null 210 | }) 211 | return res.send({ 212 | code: 0, 213 | message: '获取成功', 214 | data: menu 215 | }) 216 | }) 217 | } 218 | -------------------------------------------------------------------------------- /vue_ts-server/model/roles.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const moment = require('moment') 3 | const sequelize = require('./init') 4 | const UsersRolesModel = require('./users-roles') 5 | const MenusModel = require('./menus') 6 | const RolesMenusModel = require('./roles-menus') 7 | const tools = require('../utils/tools') 8 | // 定义表的模型 9 | const RolesModel = sequelize.define('roles', { 10 | role_id: { 11 | type: Sequelize.INTEGER, 12 | primaryKey: true, 13 | autoIncrement: true 14 | }, 15 | role_name: { 16 | type: Sequelize.STRING(255) 17 | }, 18 | remark: { 19 | type: Sequelize.STRING(255) 20 | }, 21 | status: { 22 | type: Sequelize.CHAR, 23 | defaultValue: '1' 24 | }, 25 | // menu_ids: { 26 | // type: Sequelize.TEXT, 27 | // set(val) { 28 | // this.setDataValue('menu_ids', val && val.length > 0 ? JSON.stringify(val) : JSON.stringify([])); 29 | // }, 30 | // get() { 31 | // return this.getDataValue('menu_ids') ? JSON.parse(this.getDataValue('menu_ids')) : []; 32 | // } 33 | // }, 34 | // buttons: { 35 | // type: Sequelize.TEXT, 36 | // set(val) { 37 | // this.setDataValue('buttons', val && val.length > 0 ? JSON.stringify(val) : JSON.stringify([])); 38 | // }, 39 | // get() { 40 | // return this.getDataValue('buttons') ? JSON.parse(this.getDataValue('buttons')) : []; 41 | // } 42 | // }, 43 | update_time: { 44 | type: Sequelize.DATE, 45 | get() { 46 | return this.getDataValue('update_time') 47 | ? moment(this.getDataValue('update_time')).format('YYYY-MM-DD HH:mm:ss') 48 | : null 49 | } 50 | }, 51 | create_time: { 52 | type: Sequelize.DATE, 53 | defaultValue: Sequelize.NOW, 54 | get() { 55 | return moment(this.getDataValue('create_time')).format('YYYY-MM-DD HH:mm:ss') 56 | } 57 | } 58 | }) 59 | 60 | // 删除角色的方法 61 | RolesModel.delRole = async function (role_ids) { 62 | const t = await sequelize.transaction() 63 | try { 64 | // 删除角色表中角色id数组的角色 65 | await RolesModel.destroy({ 66 | where: { role_id: role_ids } 67 | }) 68 | // 删除用户角色表中角色id数组的角色记录 69 | await UsersRolesModel.destroy({ 70 | where: { role_id: role_ids } 71 | }) 72 | // 删除角色权限表中角色id数组的角色记录 73 | await RolesMenusModel.destroy({ 74 | where: { role_id: role_ids } 75 | }) 76 | t.commit() 77 | return true 78 | } catch (e) { 79 | t.rollback() 80 | return false 81 | } 82 | } 83 | 84 | // 获取角色资源(分按钮与菜单)的方法 85 | RolesModel.getResource = async function (role_id) { 86 | const t = await sequelize.transaction() 87 | try { 88 | // 所有按钮的父id集合(重复) 89 | // let all_parent_ids = []; 90 | // 所有按钮的父id(去除重复) 91 | // let parent_ids = []; 92 | // 所有按钮的id 93 | let permIds = [] 94 | // 返回的按钮集合 按钮项格式为{menu_id:xx,btns:[xx,xx]} 95 | // const buttons = []; 96 | // 获取角色菜单表中此角色id的所有记录 97 | const roleResource = await RolesMenusModel.findAll({ 98 | where: { role_id: role_id } 99 | }) 100 | // 获得此角色id的拥有权限id 101 | const all_menu_ids = roleResource.map((resource) => { 102 | return resource.menu_id 103 | }) 104 | // 从菜单表获取此角色id拥有权限详细信息 105 | const all_menus = await MenusModel.findAll({ 106 | where: { menu_id: all_menu_ids }, 107 | attributes: ['menu_id', 'parent_id', 'type', 'permission'] 108 | }) 109 | // 获取目录及菜单的id数组 110 | const menu__arr = all_menus.filter((menu) => menu.type === 'M' || menu.type === 'C') 111 | const menu_ids = menu__arr.map((menu) => menu.menu_id) 112 | // 将获取的按钮数组转化为对应的格式 113 | const btn_arr = all_menus.filter((menu) => menu.type === 'B') 114 | btn_arr.forEach((button) => { 115 | // all_parent_ids.push(button.parent_id); 116 | permIds.push(button.menu_id) 117 | }) 118 | // parent_ids = Array.from(new Set(all_parent_ids)); 119 | // parent_ids.forEach((item) => { 120 | // buttons.push({ menu_id: item, btns: [] }); 121 | // }); 122 | // btn_arr.forEach((button) => { 123 | // parent_ids.forEach((parent) => { 124 | // if (button.parent_id === parent) { 125 | // buttons.forEach((item) => { 126 | // if (item.menu_id === parent) item.btns.push(button.permission); 127 | // }); 128 | // } 129 | // }); 130 | // }); 131 | t.commit() 132 | return { 133 | menu_ids, 134 | // buttons, 135 | permIds 136 | } 137 | } catch (e) { 138 | t.rollback() 139 | return false 140 | } 141 | } 142 | // 获取角色所有资源 143 | RolesModel.getAuth = async function (role_id) { 144 | const t = await sequelize.transaction() 145 | try { 146 | const roleResource = await RolesMenusModel.findAll({ 147 | where: { role_id: role_id } 148 | }) 149 | const roleAuth = roleResource.map((item) => item.menu_id) 150 | t.commit() 151 | return roleAuth 152 | } catch (e) { 153 | t.rollback() 154 | return false 155 | } 156 | } 157 | // 更新角色资源的方法 158 | RolesModel.updateResource = async function (role_id, menu_ids) { 159 | const t = await sequelize.transaction() 160 | try { 161 | // 先找到所有角色菜单表对应角色id的拥有权限 162 | const roles_menus = await RolesMenusModel.findAll({ 163 | where: { role_id: role_id } 164 | }) 165 | // 将表中获得的权限id转换为数组 166 | const old_menu_ids = roles_menus.map(function (item) { 167 | return item.menu_id 168 | }) 169 | // 新加的权限加到角色菜单表中 170 | const add_menu_ids = tools.minustArr(menu_ids, old_menu_ids) 171 | const add_roles_menus = add_menu_ids.map(function (menu_id) { 172 | return { role_id: role_id, menu_id: menu_id } 173 | }) 174 | await RolesMenusModel.bulkCreate(add_roles_menus) 175 | // 删除的权限从角色菜单表删除 176 | const del_menu_ids = tools.minustArr(old_menu_ids, menu_ids) 177 | if (del_menu_ids && del_menu_ids.length > 0) { 178 | await RolesMenusModel.destroy({ 179 | where: { 180 | role_id: role_id, 181 | menu_id: del_menu_ids 182 | } 183 | }) 184 | } 185 | t.commit() 186 | return true 187 | } catch (e) { 188 | t.rollback() 189 | return e.message 190 | } 191 | } 192 | // 建立关联 193 | RolesModel.belongsToMany(MenusModel, { 194 | through: { 195 | model: RolesMenusModel 196 | }, 197 | foreignKey: 'role_id', 198 | otherKey: 'menu_id' 199 | }) 200 | 201 | module.exports = RolesModel 202 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/userinfo.js: -------------------------------------------------------------------------------- 1 | // 处理图片文件中间件 2 | const formidable = require('formidable') 3 | const fs = require('fs') 4 | const path = require('path') 5 | // 导入加密模块 6 | const bcrypt = require('bcryptjs') 7 | // 导入模型 8 | const UsersModel = require('../model/users') 9 | const RolesModel = require('../model/roles') 10 | const MenusModel = require('../model/menus') 11 | const RolesMenusModel = require('../model/roles-menus') 12 | // 导入需要的验证规则对象 13 | const { update_userinfo_schema, update_password_schema } = require('../schema/userinfo') 14 | 15 | // 获取角色资源的方法 16 | const getResource = async (role_id) => { 17 | // 所有按钮的父id集合(重复) 18 | let all_parent_ids = [] 19 | // 所有按钮的父id(去除重复) 20 | let parent_ids = [] 21 | // 返回的按钮集合 按钮项格式为{menu_id:xx,btns:[xx,xx]} 22 | const buttons = [] 23 | // 获取角色菜单表中此角色id的所有记录 24 | const roleResource = await RolesMenusModel.findAll({ 25 | where: { role_id: role_id } 26 | }) 27 | // 获得此角色id的拥有权限id 28 | let all_menu_ids = roleResource.map((resource) => { 29 | return resource.menu_id 30 | }) 31 | // 将权限id数组去重 32 | all_menu_ids = Array.from(new Set(all_menu_ids)) 33 | // 从菜单表获取此角色id拥有权限详细信息 34 | const all_menus = await MenusModel.findAll({ 35 | where: { menu_id: all_menu_ids }, 36 | attributes: ['menu_id', 'parent_id', 'type', 'permission'] 37 | }) 38 | // 获取目录及菜单的id数组 39 | const menu__arr = all_menus.filter((menu) => menu.type === 'M' || menu.type === 'C') 40 | const menu_ids = menu__arr.map((menu) => menu.menu_id) 41 | // 将获取的按钮数组转化为对应的格式 42 | const btn_arr = all_menus.filter((menu) => menu.type === 'B') 43 | btn_arr.forEach((button) => { 44 | all_parent_ids.push(button.parent_id) 45 | }) 46 | parent_ids = Array.from(new Set(all_parent_ids)) 47 | parent_ids.forEach((item) => { 48 | buttons.push({ menu_id: item, btns: [] }) 49 | }) 50 | btn_arr.forEach((button) => { 51 | parent_ids.forEach((parent) => { 52 | if (button.parent_id === parent) { 53 | buttons.forEach((item) => { 54 | if (item.menu_id === parent) item.btns.push(button.permission) 55 | }) 56 | } 57 | }) 58 | }) 59 | return { 60 | menu_ids, 61 | buttons 62 | } 63 | } 64 | // 获取用户基本信息的处理函数 65 | exports.getUserinfo = async (req, res) => { 66 | // 因为加入了expressJWT中间件解析token的原因,所以在请求头传递了token通过req.user.id就可访问到登录用户的id 67 | const user_id = req.user.id 68 | // 查找已登录的用户详细信息 69 | const user_roles = await UsersModel.findOne({ 70 | attributes: { exclude: ['password'] }, 71 | include: [{ model: RolesModel, attributes: ['role_id', 'role_name', 'status'] }], 72 | where: { 73 | user_id: user_id 74 | } 75 | }) 76 | // 若无用户信息提示错误 77 | if (!user_roles) { 78 | return res.send({ 79 | code: 1, 80 | message: '帐号未分配角色', 81 | data: '' 82 | }) 83 | } 84 | let role_ids = [] 85 | let role_names = [] 86 | let buttons = [] 87 | // 获取该用户所拥有的角色 88 | user_roles.roles.forEach(function (item) { 89 | if (item.status) { 90 | role_ids.push(item.role_id) 91 | role_names.push(item.role_name) 92 | // result = await getResource(item.role_id); 93 | // menu_ids = result.menu_ids; 94 | // buttons = result.buttons; 95 | } 96 | }) 97 | // 根据角色id数组获取权限 98 | const resource = await getResource(role_ids) 99 | // 将btns合并buttons数组 100 | resource.buttons.forEach((button) => { 101 | buttons = buttons.concat(button.btns) 102 | }) 103 | // 根据菜单id数组获取菜单详细信息 104 | const menus = await MenusModel.getListTree({ menu_id: resource.menu_ids }) 105 | return res.send({ 106 | code: 0, 107 | message: '获取成功', 108 | data: { 109 | roles: role_names, 110 | user_id: user_id, 111 | name: user_roles.username, 112 | nickname: user_roles.nickname, 113 | email: user_roles.email, 114 | avatar: user_roles.user_pic, 115 | menus: menus, 116 | buttons: buttons 117 | } 118 | }) 119 | } 120 | // 更新用户基本信息的处理函数 121 | exports.updateUserInfo = (req, res, next) => { 122 | const { value, error } = update_userinfo_schema.validate(req.body) 123 | if (error) { 124 | return next(error) 125 | } 126 | const user_id = req.user.id 127 | const result = UsersModel.update(value, { 128 | where: { 129 | user_id: user_id 130 | } 131 | }) 132 | result.then(function (ret) { 133 | if (ret) { 134 | return res.send({ 135 | code: 0, 136 | message: '修改成功', 137 | data: ret 138 | }) 139 | } else { 140 | return res.send({ 141 | code: 1, 142 | message: ret, 143 | data: null 144 | }) 145 | } 146 | }) 147 | } 148 | // 重置密码接口处理函数 149 | exports.updatepwd = (req, res, next) => { 150 | const { value, error } = update_password_schema.validate(req.body) 151 | if (error) { 152 | return next(error) 153 | } 154 | if (value.password !== value.repassword) { 155 | return res.send({ 156 | code: 1, 157 | message: '两次密码输入不一致', 158 | data: null 159 | }) 160 | } 161 | const user_id = req.user.id 162 | const old_password = value.old_password 163 | UsersModel.findOne({ where: { user_id: user_id } }).then(function (user) { 164 | if (!user) { 165 | return res.send({ 166 | code: 1, 167 | message: '用户不存在', 168 | data: null 169 | }) 170 | } 171 | // 判断密码是否与数据库密码一致 172 | const compareResult = bcrypt.compareSync(old_password, user.password) 173 | if (!compareResult) { 174 | return res.send({ 175 | code: 1, 176 | message: '原密码不正确', 177 | data: null 178 | }) 179 | } 180 | const data = { 181 | password: bcrypt.hashSync(value.password, 10), 182 | update_time: new Date() 183 | } 184 | const result = UsersModel.update(data, { 185 | where: { 186 | user_id: user_id 187 | } 188 | }) 189 | result.then(function (ret) { 190 | if (ret) { 191 | return res.send({ 192 | code: 0, 193 | message: '修改成功', 194 | data: ret 195 | }) 196 | } else { 197 | return res.send({ 198 | code: 1, 199 | message: ret, 200 | data: null 201 | }) 202 | } 203 | }) 204 | }) 205 | } 206 | // 更新用户头像接口 207 | exports.updateAvatar = (req, res) => { 208 | // 获取登录用户的id 209 | let user_id = req.user.id 210 | let info = {} 211 | // 初始化处理文件对象 212 | let form = new formidable.IncomingForm() 213 | form.uploadDir = './public/avatar' // 指定解析对象(图片)存放的目录 214 | form.keepExtensions = true //保留后缀名 215 | 216 | form.parse(req, function (error, fields, files) { 217 | if (error) { 218 | info.code = 1 219 | info.message = '上传头像失败' 220 | info.data = null 221 | res.send(info) 222 | } 223 | // fields 除了图片外的信息 224 | // files 图片信息 225 | console.dir(fields) 226 | console.dir(files) 227 | const generateFilename = (originalFilename, path) => { 228 | let names = originalFilename.split('.') 229 | path = path.replace('invalid-name', '') 230 | return `${path}${names[0]}_${req.user.id}.${names[1]}` 231 | } 232 | const file = files.file ?? files.avatar 233 | // 通过fs更改文件名 234 | const newFilePath = generateFilename(file.originalFilename, file.filepath) 235 | fs.rename(file.filepath, newFilePath, (err) => { 236 | if (err) { 237 | console.log('重命名失败') 238 | console.log(err) 239 | } else { 240 | console.log(`已经保存为${generateFilename(file.newFilename, file.originalFilename, file.filepath)}`) 241 | } 242 | }) 243 | const result = UsersModel.update( 244 | { user_pic: newFilePath }, 245 | { 246 | where: { 247 | user_id: user_id 248 | } 249 | } 250 | ) 251 | result.then(function (ret) { 252 | if (ret) { 253 | return res.send({ 254 | code: 0, 255 | message: '重置头像成功', 256 | data: { 257 | srcUrl: newFilePath 258 | } 259 | }) 260 | } else { 261 | return res.send({ 262 | code: 1, 263 | message: ret, 264 | data: null 265 | }) 266 | } 267 | }) 268 | }) 269 | } 270 | -------------------------------------------------------------------------------- /vue_ts-server/vue3+ts+nodeJS后台管理系统接口文档(接口地址+参数).md: -------------------------------------------------------------------------------- 1 | #### vue3+ts+nodeJS 后台管理系统接口文档(接口地址+参数) 2 | 3 | ##### 后端服务地址 4 | 5 | http://127.0.0.1:9999 6 | 7 | ##### 公共模块 8 | 9 | 1. 每个接口 header 需要的参数值(公共、登录模块不需要) 10 | 11 | | 参数名 | 类型 | 是否必选 | 备注 | 12 | | ------ | ------ | -------- | -------- | 13 | | token | String | 是 | 接口密钥 | 14 | 15 | 2. 刷新 token 16 | **接口地址**:http://127.0.0.1:9999/user/refreshToken 17 | **接口连接方式**:POST 18 | **接口所需参数**: 19 | 20 | | 参数名 | 类型 | 是否必选 | 备注 | 21 | | ------------ | ------ | -------- | -------------- | 22 | | refreshToken | String | 是 | 刷新token | 23 | 24 | ##### 登录模块 25 | 26 | 1. 获取验证码 27 | 28 | **接口地址**:http://127.0.0.1:9999/user/checkCode 29 | **接口连接方式**:GET 30 | **接口所需参数(Query 参数)**: 31 | 32 | | 参数名 | 类型 | 是否必选 | 备注 | 33 | | ------ | ------ | -------- | -------------- | 34 | | uuid | number | 是 | 图形验证码键值 | 35 | 36 | 2. 登录 37 | **接口地址**:http://127.0.0.1:9999/user/login 38 | **接口连接方式**:POST 39 | **接口所需参数**: 40 | 41 | | 参数名 | 类型 | 是否必选 | 备注 | 42 | | --------- | ------ | -------- | -------------- | 43 | | username | String | 是 | 用户名 | 44 | | password | String | 是 | 密码 | 45 | | checkCode | String | 是 | 图形验证码 | 46 | | uuid | number | 是 | 图形验证码键值 | 47 | 48 | ##### 用户模块 49 | 50 | 1. 获取用户列表 51 | 52 | **接口地址**:http://127.0.0.1:9999/user/list 53 | **接口连接方式**:GET 54 | **接口所需参数(Query 参数)**: 55 | 56 | | 参数名 | 类型 | 是否必选 | 备注 | 57 | | ----------- | ------ | -------- | -------- | 58 | | pageSize | number | 是 | 每页数量 | 59 | | currentPage | number | 是 | 页码 | 60 | | username | String | 否 | 用户名 | 61 | | status | Char | 否 | 状态 | 62 | 63 | 2. 添加用户 64 | 65 | **接口地址**:http://127.0.0.1:9999/user/addUser 66 | **接口连接方式**:POST 67 | 68 | **接口所需参数**: 69 | 70 | | 参数名 | 类型 | 是否必选 | 备注 | 71 | | -------- | ------ | -------- | ------------ | 72 | | username | String | 是 | 用户名 | 73 | | password | String | 是 | 密码 | 74 | | role_ids | Array | 是 | 角色 id 数组 | 75 | | nickname | String | 否 | 昵称 | 76 | | email | String | 否 | 邮箱 | 77 | | status | Char | 否 | 状态 | 78 | 79 | 3. 修改用户 80 | 81 | **接口地址**:http://127.0.0.1:9999/user/editUser/:user_id 82 | **接口连接方式**:POST 83 | 84 | **接口所需参数(Query 参数)**: 85 | 86 | | 参数名 | 类型 | 是否必选 | 备注 | 87 | | ------- | ------ | -------- | ------- | 88 | | user_id | number | 是 | 用户 id | 89 | 90 | **接口所需参数**: 91 | 92 | | 参数名 | 类型 | 是否必选 | 备注 | 93 | | -------- | ------ | -------- | ------------ | 94 | | username | String | 是 | 用户名 | 95 | | role_ids | Array | 是 | 角色 id 数组 | 96 | | nickname | String | 否 | 昵称 | 97 | | email | String | 否 | 邮箱 | 98 | | status | Char | 否 | 状态 | 99 | 100 | 4. 删除用户 101 | 102 | **接口地址**:http://127.0.0.1:9999/user/delUser 103 | **接口连接方式**:POST 104 | **接口所需参数**: 105 | 106 | | 参数名 | 类型 | 是否必选 | 备注 | 107 | | ------- | ------------- | -------- | ------- | 108 | | user_id | number、Array | 是 | 用户 id | 109 | 110 | 5. 根据 id 获取用户信息 111 | 112 | **接口地址**:http://127.0.0.1:9999/user/queryUserInfo/:user_id 113 | **接口连接方式**:GET 114 | **接口所需参数(Params 参数)**: 115 | 116 | | 参数名 | 类型 | 是否必选 | 备注 | 117 | | ------- | ------ | -------- | ------- | 118 | | user_id | number | 是 | 用户 id | 119 | 120 | 6. 重置密码 121 | 122 | **接口地址**:http://127.0.0.1:9999/user/editPwd 123 | **接口连接方式**:POST 124 | **接口所需参数**: 125 | 126 | | 参数名 | 类型 | 是否必选 | 备注 | 127 | | ------------ | ------ | -------- | -------- | 128 | | user_id | number | 是 | 用户 id | 129 | | old_password | string | 是 | 旧密码 | 130 | | password | string | 是 | 新密码 | 131 | | repassword | string | 是 | 确认密码 | 132 | 133 | ##### 角色模块 134 | 135 | 1. 获取角色列表 136 | 137 | **接口地址**:http://127.0.0.1:9999/user/role/listRole 138 | **接口连接方式**:GET 139 | **接口所需参数(Query 参数)**: 140 | 141 | | 参数名 | 类型 | 是否必选 | 备注 | 142 | | ----------- | ------ | -------- | -------- | 143 | | pageSize | number | 是 | 每页数量 | 144 | | currentPage | number | 是 | 页码 | 145 | | role_name | String | 否 | 角色名 | 146 | 147 | 2. 添加角色 148 | 149 | **接口地址**:http://127.0.0.1:9999/user/role/addRole 150 | **接口连接方式**:POST 151 | 152 | **接口所需参数**: 153 | 154 | | 参数名 | 类型 | 是否必选 | 备注 | 155 | | --------- | ------ | -------- | ------ | 156 | | role_name | String | 是 | 角色名 | 157 | | remark | String | 否 | 描述 | 158 | | status | Char | 否 | 状态 | 159 | 160 | 3. 修改角色 161 | 162 | **接口地址**:http://127.0.0.1:9999/user/role/editRole 163 | **接口连接方式**:POST 164 | **接口所需参数(Query 参数)**: 165 | 166 | | 参数名 | 类型 | 是否必选 | 备注 | 167 | | ------- | ------ | -------- | ------- | 168 | | role_id | number | 是 | 角色 id | 169 | 170 | **接口所需参数**: 171 | 172 | | 参数名 | 类型 | 是否必选 | 备注 | 173 | | --------- | ------ | -------- | ------ | 174 | | role_name | String | 是 | 角色名 | 175 | | remark | String | 否 | 描述 | 176 | | status | Char | 否 | 状态 | 177 | 178 | 4. 根据 id 获取角色信息 179 | 180 | **接口地址**:http://127.0.0.1:9999/user/getRole 181 | **接口连接方式**:GET 182 | **接口所需参数(Query 参数)**: 183 | 184 | | 参数名 | 类型 | 是否必选 | 备注 | 185 | | ------- | ------ | -------- | ------- | 186 | | role_id | number | 是 | 角色 id | 187 | 188 | 5. 删除角色 189 | 190 | **接口地址**:http://127.0.0.1:9999/user/delRole 191 | **接口连接方式**:POST 192 | **接口所需参数**: 193 | 194 | | 参数名 | 类型 | 是否必选 | 备注 | 195 | | -------- | ----- | -------- | ------- | 196 | | role_ids | Array | 是 | 角色 id | 197 | 198 | 6. 获取所有角色 199 | 200 | **接口地址**:http://127.0.0.1:9999/user/allRole 201 | **接口连接方式**:GET 202 | 203 | 7. 获取角色权限 204 | 205 | **接口地址**:http://127.0.0.1:9999/user/role/roleResource 206 | **接口连接方式**:GET 207 | **接口所需参数(Query 参数)**: 208 | 209 | | 参数名 | 类型 | 是否必选 | 备注 | 210 | | ------- | ------ | -------- | ------- | 211 | | role_id | number | 是 | 角色 id | 212 | 213 | 8. 更新角色权限 214 | 215 | **接口地址**:http://127.0.0.1:9999/user/role/updateRoleResource 216 | **接口连接方式**:POST 217 | **接口所需参数(Query 参数)**: 218 | 219 | | 参数名 | 类型 | 是否必选 | 备注 | 220 | | ------- | ------ | -------- | ------- | 221 | | role_id | number | 是 | 角色 id | 222 | 223 | **接口所需参数**: 224 | 225 | | 参数名 | 类型 | 是否必选 | 备注 | 226 | | -------- | ----- | -------- | ------------ | 227 | | menu_ids | Array | 是 | 菜单 id 数组 | 228 | 229 | ##### 菜单模块 230 | 231 | 1. 获取菜单列表 232 | 233 | **接口地址**:http://127.0.0.1:9999/user/menu/listMenu 234 | **接口连接方式**:GET 235 | **接口所需参数(Query 参数)**: 236 | 237 | | 参数名 | 类型 | 是否必选 | 备注 | 238 | | ------ | ------ | -------- | -------- | 239 | | title | String | 否 | 菜单标题 | 240 | 241 | 2. 添加菜单 242 | 243 | **接口地址**:http://127.0.0.1:9999/user/menu/addMenu 244 | **接口连接方式**:POST 245 | 246 | **接口所需参数**: 247 | 248 | | 参数名 | 类型 | 是否必选 | 备注 | 249 | | ---------- | ------ | -------------- | ------------ | 250 | | parent_id | number | 是 | 权限父 id | 251 | | title | String | 是 | 权限标题 | 252 | | sort | number | 是 | 权限排序 | 253 | | type | String | 是 | 权限类别 | 254 | | name | String | 是 | 路由名 | 255 | | component | String | 否(菜单必选) | 路由文件地址 | 256 | | path | String | 否(菜单必选) | 路由地址 | 257 | | redirect | String | 否 | 重定向地址 | 258 | | permission | String | 否(按钮必选) | 权限标识 | 259 | | hidden | String | 否 | 菜单是否隐藏 | 260 | | icon | String | 否 | 菜单图标 | 261 | 262 | 3. 修改菜单 263 | 264 | **接口地址**:http://127.0.0.1:9999/user/menu/editMenu 265 | **接口连接方式**:POST 266 | 267 | **接口所需参数(Query 参数)**: 268 | 269 | | 参数名 | 类型 | 是否必选 | 备注 | 270 | | ------- | ------ | -------- | ------- | 271 | | menu_id | number | 是 | 菜单 id | 272 | 273 | **接口所需参数**: 274 | 275 | | 参数名 | 类型 | 是否必选 | 备注 | 276 | | ---------- | ------ | -------------- | ------------ | 277 | | parent_id | number | 是 | 权限父 id | 278 | | title | String | 是 | 权限标题 | 279 | | sort | number | 是 | 权限排序 | 280 | | type | String | 是 | 权限类别 | 281 | | name | String | 是 | 路由名 | 282 | | component | String | 否(菜单必选) | 路由文件地址 | 283 | | path | String | 否(菜单必选) | 路由地址 | 284 | | redirect | String | 否 | 重定向地址 | 285 | | permission | String | 否(按钮必选) | 权限标识 | 286 | | hidden | String | 否 | 菜单是否隐藏 | 287 | | icon | String | 否 | 菜单图标 | 288 | 289 | 4. 根据 id 获取菜单 290 | 291 | **接口地址**:http://127.0.0.1:9999/user/menu/getMenu 292 | **接口连接方式**:GET 293 | **接口所需参数(Query 参数)**: 294 | 295 | | 参数名 | 类型 | 是否必选 | 备注 | 296 | | ------- | ------ | -------- | ------- | 297 | | menu_id | number | 是 | 菜单 id | 298 | 299 | 5. 删除菜单 300 | 301 | **接口地址**:http://127.0.0.1:9999/user/menu/delMenu 302 | **接口连接方式**:POST 303 | **接口所需参数**: 304 | 305 | | 参数名 | 类型 | 是否必选 | 备注 | 306 | | ------- | ------ | -------- | ------- | 307 | | menu_id | number | 是 | 菜单 id | 308 | 309 | 6. 获取菜单项 310 | 311 | **接口地址**:http://127.0.0.1:9999/user/menu/listMenuOptions 312 | **接口连接方式**:GET 313 | 314 | ##### 用户信息模块(已登录用户) 315 | 316 | 1. 获取用户信息 317 | 318 | **接口地址**:http://127.0.0.1:9999/user/myInfo/userinfo 319 | **接口连接方式**:GET 320 | 321 | 2. 更新用户信息 322 | 323 | **接口地址**:http://127.0.0.1:9999/user/myInfo/updateUserinfo 324 | **接口连接方式**:POST 325 | 326 | **接口所需参数**: 327 | 328 | | 参数名 | 类型 | 是否必选 | 备注 | 329 | | -------- | ------ | -------- | ------ | 330 | | username | String | 否 | 用户名 | 331 | | nickname | String | 否 | 昵称 | 332 | | email | String | 否 | 邮箱 | 333 | 334 | 3. 更新用户密码 335 | 336 | **接口地址**:http://127.0.0.1:9999/user/myInfo/updatePwd 337 | **接口连接方式**:POST 338 | **接口所需参数**: 339 | 340 | | 参数名 | 类型 | 是否必选 | 备注 | 341 | | ------------ | ------ | -------- | -------- | 342 | | old_password | string | 是 | 旧密码 | 343 | | password | string | 是 | 新密码 | 344 | | repassword | string | 是 | 确认密码 | 345 | 346 | 4. 更新用户头像 347 | **接口地址**:http://127.0.0.1:9999/user/myInfo/updateAvatar 348 | **接口连接方式**:POST 349 | 350 | **接口请求方式**:form-data 351 | 352 | **接口所需参数**: 353 | 354 | | 参数名 | 类型 | 是否必选 | 备注 | 355 | | ------ | ------ | -------- | -------- | 356 | | file | object | 是 | 头像文件 | 357 | -------------------------------------------------------------------------------- /vue_ts-server/router_handler/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用 3 | */ 4 | // 引入用户模型 5 | const UsersModel = require('../model/users') 6 | const RolesModel = require('../model/roles') 7 | const { Op } = require('sequelize') 8 | // 引入加密模块 9 | const bcrypt = require('bcryptjs') 10 | 11 | // 1. 导入验证表单数据的中间件 12 | let joi = require('joi') 13 | // 2. 导入需要的验证规则对象 14 | const { 15 | user_login_schema, 16 | add_user_schema, 17 | get_list, 18 | update_user_schema, 19 | edit_password_schema, 20 | get_userInfoById_schema, 21 | delete_user_schema 22 | } = require('../schema/user') 23 | // 引入生成图形验证码库 24 | const svgCaptcha = require('svg-captcha') 25 | // 引入封装好的redis 26 | const redis = require('../utils/redis.js') 27 | // 引入封装好的token模块和配置信息 28 | const { addToken, decodedToken, verifyToken } = require('../utils/token') 29 | const key = require('../config/index') 30 | /** 31 | * 获取图形验证码 32 | */ 33 | exports.getCheckCode = (req, res) => { 34 | // 生成验证码,获取catcha,有{data,text}两个属性,data为svg格式图片、text为验证码 35 | const captcha = svgCaptcha.create({ 36 | size: 4, 37 | ignoreChars: '0o1lpaqd', 38 | color: true, 39 | noise: 6, 40 | background: '#aead5b', 41 | height: 32, 42 | width: 100 43 | }) 44 | // 验证码键和缓存时间 45 | const uuid = req.query.uuid 46 | const effectTime = 10 * 60 47 | // 测试 48 | // console.log(captcha); 49 | // 存入redis 50 | redis 51 | .setKey(uuid, captcha.text.toLowerCase(), effectTime) 52 | .then((result) => { 53 | if (result) { 54 | res.setHeader('Content-Type', 'image/svg+xml;charset=utf-8') 55 | res.send({ 56 | code: 0, 57 | message: '获取验证码成功', 58 | data: captcha.data 59 | }) 60 | } 61 | }) 62 | .catch((err) => { 63 | console.log(err) 64 | return res.send({ 65 | code: 1, 66 | message: '验证码获取失败', 67 | data: null 68 | }) 69 | }) 70 | } 71 | /** 72 | * 登录路由 73 | */ 74 | exports.login = async (req, res, next) => { 75 | // 验证入参,错误时抛出以捕获 76 | const { error, value } = user_login_schema.validate(req.body) 77 | if (error) { 78 | return next(error) 79 | } 80 | // 验证验证码 81 | const { username, password, checkCode, uuid } = value 82 | const captcha = await redis.getKey(uuid) 83 | if (!captcha) { 84 | return res.send({ 85 | code: 1, 86 | message: '图形验证码已过期,请点击图片刷新' 87 | }) 88 | } 89 | if (checkCode.toLowerCase() !== captcha.toLowerCase()) { 90 | return res.send({ 91 | code: 1, 92 | message: '图形验证码不正确,请重新输入' 93 | }) 94 | } 95 | // // 查询数据库用户信息是否存在密码是否正确 96 | UsersModel.findOne({ 97 | where: { 98 | username: username 99 | } 100 | }).then((result) => { 101 | if (!result) { 102 | /* 103 | * 返回体格式 104 | * code:0为成功、1为失败 105 | * message:接口信息描述 106 | * data:接口数据 107 | */ 108 | return res.send({ 109 | code: 1, 110 | message: '用户不存在', 111 | data: null 112 | }) 113 | } else if (result.status === '0') { 114 | return res.send({ 115 | code: 1, 116 | message: '帐号已停用', 117 | data: '' 118 | }) 119 | } else { 120 | const compareResult = bcrypt.compareSync(password, result.password) 121 | if (compareResult) { 122 | // 用浏览器可识别的固定格式生成token 123 | const token = 124 | 'Bearer ' + addToken({ id: result.user_id, username: result.username }, key.jwtSecretKey, key.secretKeyExpire) 125 | // 生成长时refreshToken 126 | const refreshToken = addToken( 127 | { id: result.user_id, username: result.username }, 128 | key.jwtRefrechSecretKey, 129 | key.refreshSerectKeyExpire 130 | ) 131 | return res.send({ 132 | code: 0, 133 | message: '登录成功', 134 | data: { 135 | token, 136 | refreshToken 137 | } 138 | }) 139 | } else { 140 | return res.send({ 141 | code: 1, 142 | message: '密码错误', 143 | data: '' 144 | }) 145 | } 146 | } 147 | }) 148 | } 149 | /** 150 | * 添加用户 151 | */ 152 | exports.addUser = (req, res, next) => { 153 | // 验证入参,错误时抛出以捕获 154 | const { error, value } = add_user_schema.validate(req.body) 155 | if (error) { 156 | return next(error) 157 | } 158 | // 查询是否存在相同用户名 159 | UsersModel.findAll({ 160 | where: { 161 | username: value.username 162 | } 163 | }).then((result) => { 164 | if (result && result.length) 165 | return res.send({ 166 | code: 1, 167 | message: '用户名被占用,请更换后重试!', 168 | data: null 169 | }) 170 | else { 171 | const password = value.password 172 | // 加密 173 | value.password = bcrypt.hashSync(password, 10) 174 | const result = UsersModel.addUser(value) 175 | result.then(function (ret) { 176 | if (ret === true) { 177 | return res.send({ 178 | code: 0, 179 | message: '新增成功', 180 | data: ret 181 | }) 182 | } else { 183 | return res.send({ 184 | code: 1, 185 | message: ret, 186 | data: null 187 | }) 188 | } 189 | }) 190 | } 191 | }) 192 | } 193 | 194 | /** 195 | * 刷新token 196 | */ 197 | exports.refreshToken = (req, res) => { 198 | const { refreshToken } = req.body 199 | // 验证 refreshToken 1:通过 200 | let _res = verifyToken(refreshToken) 201 | if (_res === 1) { 202 | // 对refreshToken进行解码获得id、username 203 | let { id, username } = decodedToken(refreshToken) 204 | // 续签生成新的token 205 | const token = 'Bearer ' + addToken({ id, username }, key.jwtSecretKey, key.secretKeyExpire) 206 | // 续签长时token 207 | const newRefreshToken = addToken({ id, username }, key.jwtRefrechSecretKey, key.refreshSerectKeyExpire) 208 | res.send({ 209 | code: 0, 210 | message: '获取成功', 211 | data: { 212 | token, 213 | refreshToken: newRefreshToken 214 | } 215 | }) 216 | } else { 217 | res.send({ 218 | code: 500, 219 | message: _res.message 220 | }) 221 | } 222 | } 223 | /** 224 | * 获取用户列表 225 | */ 226 | exports.getList = (req, res, next) => { 227 | const { value, error } = get_list.validate(req.query) 228 | if (error) { 229 | return next(error) 230 | } 231 | // 接收前端参数 232 | let { pageSize, currentPage } = req.query 233 | // 默认值 234 | limit = pageSize ? Number(pageSize) : 10 235 | offset = currentPage ? Number(currentPage) : 1 236 | offset = (offset - 1) * pageSize 237 | const { username, nickname, email, status } = value 238 | let where = {} 239 | if (username) where.username = { [Op.like]: `%${username}%` } 240 | if (nickname) where.nickname = nickname 241 | if (email) where.email = email 242 | if (status === '0' || status === '1') where.status = { [Op.eq]: status } 243 | 244 | UsersModel.findAndCountAll({ 245 | attributes: { exclude: ['password'] }, 246 | include: [{ model: RolesModel }], // 预先加载角色模型 247 | distinct: true, 248 | offset: offset, 249 | limit: limit, 250 | where: where 251 | }).then(function (users) { 252 | return res.send({ 253 | code: 0, 254 | message: '获取成功', 255 | data: users 256 | }) 257 | }) 258 | } 259 | /** 260 | * 修改用户 261 | */ 262 | exports.editUser = (req, res, next) => { 263 | const user_id = req.params.id 264 | const { value, error } = update_user_schema.validate(req.body) 265 | if (error) { 266 | return next(error) 267 | } 268 | UsersModel.findAll({ 269 | where: { 270 | [Op.and]: { 271 | user_id: { 272 | [Op.ne]: user_id 273 | }, 274 | username: { 275 | [Op.eq]: value.username 276 | } 277 | } 278 | } 279 | }).then((result) => { 280 | if (result && result.length) 281 | return res.send({ 282 | code: 1, 283 | message: '用户名被占用,请更换后重试!', 284 | data: null 285 | }) 286 | else { 287 | const result = UsersModel.updateUser(user_id, req.body) 288 | result.then(function (ret) { 289 | if (ret === true) { 290 | return res.send({ 291 | code: 0, 292 | message: '修改成功', 293 | data: ret 294 | }) 295 | } else { 296 | return res.send({ 297 | code: 1, 298 | message: ret, 299 | data: null 300 | }) 301 | } 302 | }) 303 | } 304 | }) 305 | } 306 | /** 307 | * 删除用户 308 | */ 309 | exports.deleteUser = (req, res, next) => { 310 | const { value, error } = delete_user_schema.validate(req.body) 311 | if (error) { 312 | return next(error) 313 | } 314 | const user_ids = value.user_ids 315 | if ((user_ids.length && user_ids.includes(1)) || user_ids === 1) 316 | return res.send({ 317 | code: 1, 318 | message: '超级管理员测试账号不可删除', 319 | data: null 320 | }) 321 | UsersModel.delUser(user_ids || []).then(function (user) { 322 | if (user !== true) { 323 | return res.send({ 324 | code: 1, 325 | message: '删除失败', 326 | data: null 327 | }) 328 | } 329 | return res.send({ 330 | code: 0, 331 | message: '删除成功', 332 | data: user 333 | }) 334 | }) 335 | } 336 | /** 337 | * 重置密码 338 | */ 339 | exports.editPassword = (req, res, next) => { 340 | const { value, error } = edit_password_schema.validate(req.body) 341 | if (error) { 342 | return next(error) 343 | } 344 | if (value.password !== value.repassword) { 345 | return res.send({ 346 | code: 1, 347 | message: '两次密码输入不一致', 348 | data: null 349 | }) 350 | } 351 | const user_id = value.user_id 352 | const old_password = value.old_password 353 | UsersModel.findOne({ where: { user_id: user_id } }).then(function (user) { 354 | if (!user) { 355 | return res.send({ 356 | code: 1, 357 | message: '用户不存在', 358 | data: null 359 | }) 360 | } 361 | // 判断密码是否与数据库密码一致 362 | const compareResult = bcrypt.compareSync(old_password, user.password) 363 | if (!compareResult) { 364 | return res.send({ 365 | code: 1, 366 | message: '原密码不正确', 367 | data: null 368 | }) 369 | } 370 | const data = { 371 | password: bcrypt.hashSync(value.password, 10), 372 | update_time: new Date() 373 | } 374 | const result = UsersModel.update(data, { 375 | where: { 376 | user_id: user_id 377 | } 378 | }) 379 | result.then(function (ret) { 380 | if (ret) { 381 | return res.send({ 382 | code: 0, 383 | message: '修改成功', 384 | data: ret 385 | }) 386 | } else { 387 | return res.send({ 388 | code: 1, 389 | message: ret, 390 | data: null 391 | }) 392 | } 393 | }) 394 | }) 395 | } 396 | /** 397 | * 根据id获取用户信息接口 398 | */ 399 | exports.getUserinfoById = (req, res, next) => { 400 | const { value, error } = get_userInfoById_schema.validate(req.params) 401 | if (error) { 402 | return next(error) 403 | } 404 | let user_id = value.user_id 405 | UsersModel.findOne({ 406 | attributes: { exclude: ['password'] }, 407 | include: [{ model: RolesModel }], // 预先加载角色模型 408 | where: { 409 | user_id: user_id 410 | } 411 | }).then((user) => { 412 | if (!user) { 413 | res.send({ 414 | code: 1, 415 | message: '用户不存在', 416 | data: null 417 | }) 418 | } else { 419 | res.send({ 420 | code: 0, 421 | message: '获取成功', 422 | data: user 423 | }) 424 | } 425 | }) 426 | } 427 | -------------------------------------------------------------------------------- /vue_ts-server/sql/database(React项目用这个).sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 80029 7 | Source Host : localhost:3306 8 | Source Schema : react-antd-admin 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80029 12 | File Encoding : 65001 13 | 14 | Date: 11/11/2023 16:11:01 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for dict_items 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `dict_items`; 24 | CREATE TABLE `dict_items` ( 25 | `id` int UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `dict_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典id', 27 | `item_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典项文本', 28 | `item_value` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '字典项键', 29 | `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', 30 | `sort_order` int NULL DEFAULT 0 COMMENT '排序', 31 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '状态(1启用 0不启用)', 32 | `create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人', 33 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 34 | `update_time` datetime NULL DEFAULT NULL, 35 | PRIMARY KEY (`id`) USING BTREE, 36 | INDEX `index_table_dict_id`(`dict_id` ASC) USING BTREE, 37 | INDEX `index_table_sort_order`(`sort_order` ASC) USING BTREE, 38 | INDEX `index_table_dict_status`(`status` ASC) USING BTREE 39 | ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 40 | 41 | -- ---------------------------- 42 | -- Records of dict_items 43 | -- ---------------------------- 44 | INSERT INTO `dict_items` VALUES (1, '1', '启用', '1', '状态可用', 1, '1', 'Alan', '2023-08-30 09:54:48', '2023-08-30 09:36:44'); 45 | INSERT INTO `dict_items` VALUES (3, '1', '禁用', '0', '状态不可用', 2, '1', 'Alan', '2023-08-30 09:56:09', '2023-08-30 09:36:47'); 46 | INSERT INTO `dict_items` VALUES (4, '2', '显示', '0', '状态标记为显示', 1, '1', 'Alan', '2023-08-30 09:56:52', '2023-08-30 14:51:40'); 47 | INSERT INTO `dict_items` VALUES (5, '2', '隐藏', '1', '状态为隐藏', 2, '1', 'Alan', '2023-08-30 09:57:08', '2023-08-30 14:51:36'); 48 | INSERT INTO `dict_items` VALUES (8, '3', '目录', 'C', '容纳菜单的目录', 1, '1', 'Alan', '2023-08-30 08:58:38', '2023-08-30 09:45:14'); 49 | INSERT INTO `dict_items` VALUES (9, '3', '菜单', 'M', '菜单', 2, '1', 'Alan', '2023-08-30 08:59:19', '2023-08-30 09:11:14'); 50 | INSERT INTO `dict_items` VALUES (10, '3', '按钮', 'B', '菜单里的按钮', 3, '1', 'Alan', '2023-08-30 08:59:37', '2023-08-30 09:11:18'); 51 | 52 | -- ---------------------------- 53 | -- Table structure for dicts 54 | -- ---------------------------- 55 | DROP TABLE IF EXISTS `dicts`; 56 | CREATE TABLE `dicts` ( 57 | `id` int UNSIGNED NOT NULL AUTO_INCREMENT, 58 | `dict_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典名称', 59 | `dict_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典编码', 60 | `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', 61 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '1' COMMENT '字典状态,0为禁用,1为启用\r\n\r\n', 62 | `create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人', 63 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 64 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新人', 65 | PRIMARY KEY (`id`) USING BTREE, 66 | UNIQUE INDEX `indextable_dict_code`(`dict_code` ASC) USING BTREE 67 | ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 68 | 69 | -- ---------------------------- 70 | -- Records of dicts 71 | -- ---------------------------- 72 | INSERT INTO `dicts` VALUES (1, '状态', 'status', '定义状态类型字典', '1', 'Alan', '2023-08-30 17:13:14', '2023-08-30 11:32:52'); 73 | INSERT INTO `dicts` VALUES (2, '显示状态', 'hidden', '定义显隐状态类型字典', '1', 'Alan', '2023-08-30 17:18:27', '2023-08-30 17:25:31'); 74 | INSERT INTO `dicts` VALUES (3, '权限类型', 'authType', '定义权限类型(菜单、按钮)等', '1', 'Alan', '2023-08-30 08:57:37', NULL); 75 | 76 | -- ---------------------------- 77 | -- Table structure for menus 78 | -- ---------------------------- 79 | DROP TABLE IF EXISTS `menus`; 80 | CREATE TABLE `menus` ( 81 | `menu_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 82 | `parent_id` int NOT NULL COMMENT '上级ID', 83 | `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '标题', 84 | `sort` int NOT NULL DEFAULT 0 COMMENT '排序', 85 | `type` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '类型(\'B\':按钮,‘M’:菜单,‘C’:目录)', 86 | `icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图标', 87 | `component` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由组件', 88 | `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由地址', 89 | `redirect` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '跳转地址', 90 | `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限标识', 91 | `hidden` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '隐藏', 92 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 93 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 94 | PRIMARY KEY (`menu_id`) USING BTREE 95 | ) ENGINE = InnoDB AUTO_INCREMENT = 56 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 96 | 97 | -- ---------------------------- 98 | -- Records of menus 99 | -- ---------------------------- 100 | INSERT INTO `menus` VALUES (1, 40, '用户管理', 1, 'M', 'user', '/System/User', 'user', NULL, NULL, '0', '2023-09-08 11:27:44', '2023-08-24 09:49:46'); 101 | INSERT INTO `menus` VALUES (2, 40, '角色管理', 2, 'M', 'peoples', '/System/Role', 'role', NULL, NULL, '0', '2023-09-08 11:28:04', '2023-08-24 09:50:23'); 102 | INSERT INTO `menus` VALUES (3, 40, '权限管理', 3, 'M', 'list', '/System/Auth', 'menu', NULL, NULL, '0', '2023-09-13 16:08:02', '2023-08-24 09:50:46'); 103 | INSERT INTO `menus` VALUES (40, 0, '系统管理', 1, 'C', 'system', 'Layout', 'system', '/system/user', NULL, '0', NULL, '2023-09-05 17:29:59'); 104 | INSERT INTO `menus` VALUES (41, 1, '用户新增', 1, 'B', NULL, NULL, NULL, NULL, 'system:user:add', '0', NULL, '2023-09-06 10:48:31'); 105 | INSERT INTO `menus` VALUES (42, 1, '用户编辑', 2, 'B', NULL, NULL, NULL, NULL, 'system:user:edit', '0', NULL, '2023-09-06 10:49:00'); 106 | INSERT INTO `menus` VALUES (43, 1, '用户删除', 3, 'B', NULL, NULL, NULL, NULL, 'system:user:del', '0', NULL, '2023-09-06 10:49:17'); 107 | INSERT INTO `menus` VALUES (44, 2, '角色新增', 1, 'B', NULL, NULL, NULL, NULL, 'system:role:add', '0', '2023-09-07 11:35:25', '2023-09-07 11:30:19'); 108 | INSERT INTO `menus` VALUES (45, 2, '角色编辑', 2, 'B', NULL, NULL, NULL, NULL, 'system:role:edit', '0', NULL, '2023-09-07 11:30:43'); 109 | INSERT INTO `menus` VALUES (46, 2, '角色删除', 3, 'B', NULL, NULL, NULL, NULL, 'system:role:del', '0', NULL, '2023-09-07 11:31:14'); 110 | INSERT INTO `menus` VALUES (47, 2, '分配角色权限', 4, 'B', NULL, NULL, NULL, NULL, 'system:role:assignAuth', '0', NULL, '2023-09-07 11:32:30'); 111 | INSERT INTO `menus` VALUES (48, 3, '权限新增', 1, 'B', NULL, NULL, NULL, NULL, 'system:auth:add', '0', NULL, '2023-09-07 11:33:05'); 112 | INSERT INTO `menus` VALUES (49, 3, '权限编辑', 2, 'B', NULL, NULL, NULL, NULL, 'system:auth:edit', '0', NULL, '2023-09-07 11:33:23'); 113 | INSERT INTO `menus` VALUES (50, 3, '权限删除', 3, 'B', NULL, NULL, NULL, NULL, 'system:auth:del', '0', NULL, '2023-09-07 11:33:41'); 114 | INSERT INTO `menus` VALUES (51, 1, '重置密码', 4, 'B', NULL, NULL, NULL, NULL, 'system:user:resetPwd', '0', NULL, '2023-09-07 15:33:38'); 115 | INSERT INTO `menus` VALUES (52, 0, '自定义多级菜单', 2, 'C', 'tree-table', 'Layout', 'custom', '/custom/test1', NULL, '0', NULL, '2023-09-08 11:16:09'); 116 | INSERT INTO `menus` VALUES (53, 52, '测试菜单一', 1, 'M', 'example', '/Custom/Test1', 'test1', NULL, NULL, '0', NULL, '2023-09-08 14:45:13'); 117 | INSERT INTO `menus` VALUES (54, 52, '测试菜单二', 1, 'C', 'example', 'Layout', 'test2', '/custom/test2/test3', NULL, '0', NULL, '2023-09-08 14:46:37'); 118 | INSERT INTO `menus` VALUES (55, 54, '测试菜单三', 1, 'M', 'example', '/Custom/Test2/Test3', 'test3', NULL, NULL, '0', NULL, '2023-09-08 14:47:19'); 119 | 120 | -- ---------------------------- 121 | -- Table structure for roles 122 | -- ---------------------------- 123 | DROP TABLE IF EXISTS `roles`; 124 | CREATE TABLE `roles` ( 125 | `role_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 126 | `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称', 127 | `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', 128 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '状态', 129 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 130 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 131 | PRIMARY KEY (`role_id`, `role_name`) USING BTREE 132 | ) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 133 | 134 | -- ---------------------------- 135 | -- Records of roles 136 | -- ---------------------------- 137 | INSERT INTO `roles` VALUES (1, '管理员', '我是一个管理员', '1', NULL, '2023-08-24 09:42:44'); 138 | INSERT INTO `roles` VALUES (2, '观察员', '我是一个观察员', '1', NULL, '2023-08-24 09:43:10'); 139 | INSERT INTO `roles` VALUES (3, '测试员', '我是一个测试员', '1', NULL, '2023-08-24 09:43:27'); 140 | INSERT INTO `roles` VALUES (19, '测试角色', '该角色仅供测试使用', '1', '2023-09-04 15:51:36', '2023-09-04 15:45:12'); 141 | 142 | -- ---------------------------- 143 | -- Table structure for roles_menus 144 | -- ---------------------------- 145 | DROP TABLE IF EXISTS `roles_menus`; 146 | CREATE TABLE `roles_menus` ( 147 | `role_menu_id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '角色菜单联合表id', 148 | `role_id` int NOT NULL COMMENT '角色id', 149 | `menu_id` int NOT NULL COMMENT '菜单id', 150 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 151 | PRIMARY KEY (`role_menu_id`) USING BTREE 152 | ) ENGINE = InnoDB AUTO_INCREMENT = 101 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; 153 | 154 | -- ---------------------------- 155 | -- Records of roles_menus 156 | -- ---------------------------- 157 | INSERT INTO `roles_menus` VALUES (1, 1, 1, '2023-08-24 09:53:09'); 158 | INSERT INTO `roles_menus` VALUES (2, 1, 2, '2023-08-24 09:53:30'); 159 | INSERT INTO `roles_menus` VALUES (3, 1, 3, '2023-08-24 09:53:40'); 160 | INSERT INTO `roles_menus` VALUES (81, 1, 40, '2023-09-05 17:38:09'); 161 | INSERT INTO `roles_menus` VALUES (82, 1, 41, '2023-09-06 16:52:29'); 162 | INSERT INTO `roles_menus` VALUES (83, 1, 42, '2023-09-06 16:52:29'); 163 | INSERT INTO `roles_menus` VALUES (84, 1, 43, '2023-09-06 16:52:29'); 164 | INSERT INTO `roles_menus` VALUES (85, 2, 40, '2023-09-06 17:33:05'); 165 | INSERT INTO `roles_menus` VALUES (86, 2, 1, '2023-09-06 17:33:05'); 166 | INSERT INTO `roles_menus` VALUES (87, 2, 2, '2023-09-06 17:33:05'); 167 | INSERT INTO `roles_menus` VALUES (88, 2, 3, '2023-09-06 17:33:05'); 168 | INSERT INTO `roles_menus` VALUES (89, 1, 44, '2023-09-07 11:34:32'); 169 | INSERT INTO `roles_menus` VALUES (90, 1, 45, '2023-09-07 11:34:32'); 170 | INSERT INTO `roles_menus` VALUES (91, 1, 46, '2023-09-07 11:34:32'); 171 | INSERT INTO `roles_menus` VALUES (92, 1, 47, '2023-09-07 11:34:32'); 172 | INSERT INTO `roles_menus` VALUES (93, 1, 48, '2023-09-07 11:34:32'); 173 | INSERT INTO `roles_menus` VALUES (94, 1, 49, '2023-09-07 11:34:32'); 174 | INSERT INTO `roles_menus` VALUES (95, 1, 50, '2023-09-07 11:34:32'); 175 | INSERT INTO `roles_menus` VALUES (96, 1, 51, '2023-09-07 15:34:00'); 176 | INSERT INTO `roles_menus` VALUES (97, 1, 52, '2023-09-08 14:47:59'); 177 | INSERT INTO `roles_menus` VALUES (98, 1, 53, '2023-09-08 14:47:59'); 178 | INSERT INTO `roles_menus` VALUES (99, 1, 54, '2023-09-08 14:47:59'); 179 | INSERT INTO `roles_menus` VALUES (100, 1, 55, '2023-09-08 14:47:59'); 180 | 181 | -- ---------------------------- 182 | -- Table structure for user_logs 183 | -- ---------------------------- 184 | DROP TABLE IF EXISTS `user_logs`; 185 | CREATE TABLE `user_logs` ( 186 | `user_log_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 187 | `user_id` int NOT NULL COMMENT '用户ID', 188 | `ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录IP', 189 | `ua` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'ua标识', 190 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间', 191 | PRIMARY KEY (`user_log_id`) USING BTREE 192 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 193 | 194 | -- ---------------------------- 195 | -- Records of user_logs 196 | -- ---------------------------- 197 | 198 | -- ---------------------------- 199 | -- Table structure for users 200 | -- ---------------------------- 201 | DROP TABLE IF EXISTS `users`; 202 | CREATE TABLE `users` ( 203 | `user_id` int NOT NULL AUTO_INCREMENT COMMENT 'id唯一字段', 204 | `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', 205 | `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户密码', 206 | `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户昵称', 207 | `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户邮箱\r\n', 208 | `user_pic` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '用户头像', 209 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '用户状态: 0为停用、1启用\r\n', 210 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 211 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 212 | PRIMARY KEY (`user_id`) USING BTREE 213 | ) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 214 | 215 | -- ---------------------------- 216 | -- Records of users 217 | -- ---------------------------- 218 | INSERT INTO `users` VALUES (1, 'Alan', '$2a$10$X8gGVZ/RN7wNGNxd0kyYW.yUSJW2XHNxA9lAAUlB4qkVKDaX41hiG', '艾伦儿', 'alan@gmail.com', 'public\\avatar\\pikaqiu_1.jpg', '1', '2023-08-24 10:15:32', '2023-11-11 14:33:08'); 219 | 220 | -- ---------------------------- 221 | -- Table structure for users_roles 222 | -- ---------------------------- 223 | DROP TABLE IF EXISTS `users_roles`; 224 | CREATE TABLE `users_roles` ( 225 | `user_role_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 226 | `role_id` int NOT NULL COMMENT '角色ID', 227 | `user_id` int NOT NULL COMMENT '用户ID', 228 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 229 | PRIMARY KEY (`user_role_id`) USING BTREE 230 | ) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 231 | 232 | -- ---------------------------- 233 | -- Records of users_roles 234 | -- ---------------------------- 235 | INSERT INTO `users_roles` VALUES (1, 1, 1, '2023-08-24 09:44:48'); 236 | INSERT INTO `users_roles` VALUES (2, 2, 1, '2023-08-24 09:44:58'); 237 | 238 | SET FOREIGN_KEY_CHECKS = 1; 239 | -------------------------------------------------------------------------------- /vue_ts-server/sql/database(vue3项目用这个).sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 80029 7 | Source Host : localhost:3306 8 | Source Schema : vue_ts-database 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80029 12 | File Encoding : 65001 13 | 14 | Date: 08/01/2024 19:47:48 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for dict_items 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `dict_items`; 24 | CREATE TABLE `dict_items` ( 25 | `id` int UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `dict_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典id', 27 | `item_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典项文本', 28 | `item_value` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '字典项键', 29 | `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', 30 | `sort_order` int NULL DEFAULT 0 COMMENT '排序', 31 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '状态(1启用 0不启用)', 32 | `create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人', 33 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 34 | `update_time` datetime NULL DEFAULT NULL, 35 | PRIMARY KEY (`id`) USING BTREE, 36 | INDEX `index_table_dict_id`(`dict_id` ASC) USING BTREE, 37 | INDEX `index_table_sort_order`(`sort_order` ASC) USING BTREE, 38 | INDEX `index_table_dict_status`(`status` ASC) USING BTREE 39 | ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 40 | 41 | -- ---------------------------- 42 | -- Records of dict_items 43 | -- ---------------------------- 44 | INSERT INTO `dict_items` VALUES (1, '1', '启用', '1', '状态可用', 1, '1', 'Alan', '2023-02-13 09:54:48', '2023-02-15 09:36:44'); 45 | INSERT INTO `dict_items` VALUES (3, '1', '禁用', '0', '状态不可用', 2, '1', 'Alan', '2023-02-13 09:56:09', '2023-02-15 09:36:47'); 46 | INSERT INTO `dict_items` VALUES (4, '2', '显示', '0', '状态标记为显示', 1, '1', 'Alan', '2023-02-13 09:56:52', '2023-02-16 14:51:40'); 47 | INSERT INTO `dict_items` VALUES (5, '2', '隐藏', '1', '状态为隐藏', 2, '1', 'Alan', '2023-02-13 09:57:08', '2023-02-16 14:51:36'); 48 | INSERT INTO `dict_items` VALUES (8, '4', '目录', 'C', '容纳菜单的目录', 1, '1', 'Alan', '2023-02-17 08:58:38', '2023-02-17 09:45:14'); 49 | INSERT INTO `dict_items` VALUES (9, '4', '菜单', 'M', '菜单', 2, '1', 'Alan', '2023-02-17 08:59:19', '2023-02-17 09:11:14'); 50 | INSERT INTO `dict_items` VALUES (10, '4', '按钮', 'B', '菜单里的按钮', 3, '1', 'Alan', '2023-02-17 08:59:37', '2023-02-17 09:11:18'); 51 | 52 | -- ---------------------------- 53 | -- Table structure for dicts 54 | -- ---------------------------- 55 | DROP TABLE IF EXISTS `dicts`; 56 | CREATE TABLE `dicts` ( 57 | `id` int UNSIGNED NOT NULL AUTO_INCREMENT, 58 | `dict_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典名称', 59 | `dict_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典编码', 60 | `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', 61 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '1' COMMENT '字典状态,0为禁用,1为启用\r\n\r\n', 62 | `create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人', 63 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 64 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新人', 65 | PRIMARY KEY (`id`) USING BTREE, 66 | UNIQUE INDEX `indextable_dict_code`(`dict_code` ASC) USING BTREE 67 | ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 68 | 69 | -- ---------------------------- 70 | -- Records of dicts 71 | -- ---------------------------- 72 | INSERT INTO `dicts` VALUES (1, '状态', 'status', '定义状态类型字典', '1', 'Alan', '2023-02-10 17:13:14', '2023-02-14 11:32:52'); 73 | INSERT INTO `dicts` VALUES (2, '显示状态', 'hidden', '定义显隐状态类型字典', '1', 'Alan', '2023-02-10 17:18:27', '2023-02-10 17:25:31'); 74 | INSERT INTO `dicts` VALUES (4, '权限类型', 'authType', '定义权限类型(菜单、按钮)等', '1', 'Alan', '2023-02-17 08:57:37', NULL); 75 | 76 | -- ---------------------------- 77 | -- Table structure for menus 78 | -- ---------------------------- 79 | DROP TABLE IF EXISTS `menus`; 80 | CREATE TABLE `menus` ( 81 | `menu_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 82 | `parent_id` int NOT NULL COMMENT '上级ID', 83 | `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '标题', 84 | `sort` int NOT NULL DEFAULT 0 COMMENT '排序', 85 | `type` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '类型', 86 | `icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图标', 87 | `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由名称', 88 | `component` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由组件', 89 | `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由地址', 90 | `redirect` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '跳转地址', 91 | `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限标识', 92 | `hidden` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '隐藏', 93 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 94 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 95 | PRIMARY KEY (`menu_id`) USING BTREE 96 | ) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 97 | 98 | -- ---------------------------- 99 | -- Records of menus 100 | -- ---------------------------- 101 | INSERT INTO `menus` VALUES (1, 0, '系统管理', 0, 'C', 'component', 'System', 'Layout', '/system', '/system/user', NULL, '0', '2023-02-17 09:44:53', '2022-12-05 14:36:30'); 102 | INSERT INTO `menus` VALUES (2, 1, '用户管理', 1, 'M', 'user', 'User', '/system/user', '/system/user', NULL, NULL, '0', '2022-12-05 15:37:43', '2022-12-05 15:19:51'); 103 | INSERT INTO `menus` VALUES (3, 1, '角色管理', 2, 'M', 'peoples', 'Role', '/system/role', '/system/role', NULL, NULL, '0', '2022-12-05 15:38:27', '2022-12-05 15:28:13'); 104 | INSERT INTO `menus` VALUES (4, 1, '菜单管理', 3, 'M', 'list', 'Menu', '/system/menu', '/system/menu', NULL, NULL, '0', '2022-12-05 15:39:15', '2022-12-05 15:29:02'); 105 | INSERT INTO `menus` VALUES (19, 0, '测试菜单', 1, 'C', 'system', 'CeshiMenu', 'Layout', '/ceshiMenu', '/ceshiMenu/ceshiMenu4', NULL, '0', '2023-02-06 17:07:09', '2022-12-06 16:45:38'); 106 | INSERT INTO `menus` VALUES (25, 19, '测试弹窗表单', 2, 'M', 'edit', 'Test2', '/ceshiMenu/ceshiMenu2', '/ceshiMenu/ceshiMenu2', NULL, NULL, '0', '2023-02-07 15:09:13', '2022-12-05 14:36:30'); 107 | INSERT INTO `menus` VALUES (26, 2, '用户新增', 1, 'B', NULL, NULL, NULL, NULL, NULL, 'system:user:add', '0', NULL, '2022-12-22 16:08:02'); 108 | INSERT INTO `menus` VALUES (27, 2, '用户编辑', 2, 'B', NULL, NULL, NULL, NULL, NULL, 'system:user:edit', '0', '2022-12-22 16:09:41', '2022-12-22 16:08:34'); 109 | INSERT INTO `menus` VALUES (28, 2, '用户查询', 3, 'B', NULL, NULL, NULL, NULL, NULL, 'system:user:query', '0', '2022-12-22 16:09:34', '2022-12-22 16:09:29'); 110 | INSERT INTO `menus` VALUES (29, 2, '用户删除', 4, 'B', NULL, NULL, NULL, NULL, NULL, 'system:user:del', '0', NULL, '2022-12-22 16:13:26'); 111 | INSERT INTO `menus` VALUES (30, 3, '角色新增', 1, 'B', NULL, NULL, NULL, NULL, NULL, 'system:role:add', '0', NULL, '2022-12-22 16:13:47'); 112 | INSERT INTO `menus` VALUES (31, 3, '角色编辑', 2, 'B', NULL, NULL, NULL, NULL, NULL, 'system:role:edit', '0', '2022-12-22 16:14:54', '2022-12-22 16:14:20'); 113 | INSERT INTO `menus` VALUES (32, 3, '角色查询', 3, 'B', NULL, NULL, NULL, NULL, NULL, 'system:role:query', '0', NULL, '2022-12-22 16:14:49'); 114 | INSERT INTO `menus` VALUES (33, 3, '角色删除', 4, 'B', NULL, NULL, NULL, NULL, NULL, 'system:role:del', '0', '2022-12-22 16:17:49', '2022-12-22 16:17:31'); 115 | INSERT INTO `menus` VALUES (34, 4, '菜单新增', 1, 'B', NULL, NULL, NULL, NULL, NULL, 'system:menu:add', '0', '2022-12-22 16:17:39', '2022-12-22 16:17:31'); 116 | INSERT INTO `menus` VALUES (35, 4, '菜单编辑', 2, 'B', NULL, NULL, NULL, NULL, NULL, 'system:menu:edit', '0', '2022-12-22 16:17:39', '2022-12-22 16:17:31'); 117 | INSERT INTO `menus` VALUES (36, 4, '菜单查询', 3, 'B', NULL, NULL, NULL, NULL, NULL, 'system:menu:query', '0', '2022-12-22 16:17:39', '2022-12-22 16:17:31'); 118 | INSERT INTO `menus` VALUES (37, 4, '菜单删除', 4, 'B', NULL, NULL, NULL, NULL, NULL, 'system:menu:del', '0', '2022-12-22 16:17:39', '2022-12-22 16:17:31'); 119 | INSERT INTO `menus` VALUES (38, 19, '测试表单', 1, 'M', 'form', 'Test1', '/ceshiMenu/ceshiMenu1', '/ceshiMenu/ceshiMenu1', NULL, NULL, '0', '2023-02-07 15:08:58', '2022-12-07 15:35:46'); 120 | INSERT INTO `menus` VALUES (39, 1, '字典管理', 4, 'M', 'dict', 'dict', '/system/dict', '/system/dict', NULL, NULL, '0', NULL, '2023-02-13 15:17:39'); 121 | 122 | -- ---------------------------- 123 | -- Table structure for roles 124 | -- ---------------------------- 125 | DROP TABLE IF EXISTS `roles`; 126 | CREATE TABLE `roles` ( 127 | `role_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 128 | `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称', 129 | `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', 130 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '状态', 131 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 132 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 133 | PRIMARY KEY (`role_id`, `role_name`) USING BTREE 134 | ) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 135 | 136 | -- ---------------------------- 137 | -- Records of roles 138 | -- ---------------------------- 139 | INSERT INTO `roles` VALUES (1, '管理员', '我是一个管理员', '1', '2023-02-17 09:44:42', '2022-12-01 10:11:05'); 140 | INSERT INTO `roles` VALUES (2, '观察员', '我是一个观察员', '1', '2022-12-06 09:17:47', '2022-12-01 10:12:58'); 141 | INSERT INTO `roles` VALUES (4, '测试员', '仅为测试员使用', '1', '2023-01-31 16:13:06', '2022-12-01 10:20:49'); 142 | INSERT INTO `roles` VALUES (8, '测试一下', '简单测试一下拉', '1', '2023-02-09 09:10:48', '2023-02-02 15:13:18'); 143 | 144 | -- ---------------------------- 145 | -- Table structure for roles_menus 146 | -- ---------------------------- 147 | DROP TABLE IF EXISTS `roles_menus`; 148 | CREATE TABLE `roles_menus` ( 149 | `role_menu_id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '角色菜单联合表id', 150 | `role_id` int NOT NULL COMMENT '角色id', 151 | `menu_id` int NOT NULL COMMENT '菜单id', 152 | `create_time` datetime NOT NULL COMMENT '创建时间', 153 | PRIMARY KEY (`role_menu_id`) USING BTREE 154 | ) ENGINE = InnoDB AUTO_INCREMENT = 81 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 155 | 156 | -- ---------------------------- 157 | -- Records of roles_menus 158 | -- ---------------------------- 159 | INSERT INTO `roles_menus` VALUES (7, 2, 19, '2022-12-06 16:47:36'); 160 | INSERT INTO `roles_menus` VALUES (13, 1, 1, '2022-12-08 11:11:57'); 161 | INSERT INTO `roles_menus` VALUES (14, 1, 2, '2022-12-08 11:11:57'); 162 | INSERT INTO `roles_menus` VALUES (15, 1, 3, '2022-12-08 11:11:57'); 163 | INSERT INTO `roles_menus` VALUES (16, 1, 4, '2022-12-08 11:11:57'); 164 | INSERT INTO `roles_menus` VALUES (17, 1, 19, '2022-12-08 11:14:26'); 165 | INSERT INTO `roles_menus` VALUES (29, 2, 1, '2022-12-20 14:35:51'); 166 | INSERT INTO `roles_menus` VALUES (30, 2, 2, '2022-12-20 14:35:51'); 167 | INSERT INTO `roles_menus` VALUES (31, 2, 3, '2022-12-20 14:35:51'); 168 | INSERT INTO `roles_menus` VALUES (32, 2, 4, '2022-12-20 14:35:51'); 169 | INSERT INTO `roles_menus` VALUES (34, 2, 25, '2022-12-22 16:28:31'); 170 | INSERT INTO `roles_menus` VALUES (35, 2, 28, '2022-12-22 16:28:31'); 171 | INSERT INTO `roles_menus` VALUES (36, 2, 32, '2022-12-22 16:28:31'); 172 | INSERT INTO `roles_menus` VALUES (37, 2, 36, '2022-12-22 16:28:31'); 173 | INSERT INTO `roles_menus` VALUES (38, 8, 1, '2023-02-02 15:20:10'); 174 | INSERT INTO `roles_menus` VALUES (44, 8, 3, '2023-02-02 15:20:22'); 175 | INSERT INTO `roles_menus` VALUES (46, 8, 2, '2023-02-02 15:20:59'); 176 | INSERT INTO `roles_menus` VALUES (47, 8, 26, '2023-02-02 15:20:59'); 177 | INSERT INTO `roles_menus` VALUES (49, 8, 28, '2023-02-02 15:53:09'); 178 | INSERT INTO `roles_menus` VALUES (50, 8, 29, '2023-02-02 15:53:09'); 179 | INSERT INTO `roles_menus` VALUES (51, 8, 30, '2023-02-02 15:53:09'); 180 | INSERT INTO `roles_menus` VALUES (52, 8, 32, '2023-02-02 15:53:09'); 181 | INSERT INTO `roles_menus` VALUES (53, 8, 33, '2023-02-02 15:53:09'); 182 | INSERT INTO `roles_menus` VALUES (57, 8, 19, '2023-02-03 10:58:11'); 183 | INSERT INTO `roles_menus` VALUES (61, 8, 31, '2023-02-03 10:58:34'); 184 | INSERT INTO `roles_menus` VALUES (64, 1, 38, '2023-02-06 17:12:10'); 185 | INSERT INTO `roles_menus` VALUES (65, 1, 25, '2023-02-06 17:12:10'); 186 | INSERT INTO `roles_menus` VALUES (66, 8, 27, '2023-02-09 09:10:53'); 187 | INSERT INTO `roles_menus` VALUES (67, 1, 26, '2023-02-09 09:31:01'); 188 | INSERT INTO `roles_menus` VALUES (68, 1, 27, '2023-02-09 09:31:01'); 189 | INSERT INTO `roles_menus` VALUES (69, 1, 28, '2023-02-09 09:31:01'); 190 | INSERT INTO `roles_menus` VALUES (70, 1, 29, '2023-02-09 09:31:01'); 191 | INSERT INTO `roles_menus` VALUES (71, 1, 30, '2023-02-09 09:31:01'); 192 | INSERT INTO `roles_menus` VALUES (72, 1, 31, '2023-02-09 09:31:01'); 193 | INSERT INTO `roles_menus` VALUES (73, 1, 32, '2023-02-09 09:31:01'); 194 | INSERT INTO `roles_menus` VALUES (74, 1, 33, '2023-02-09 09:31:01'); 195 | INSERT INTO `roles_menus` VALUES (75, 1, 34, '2023-02-09 09:31:01'); 196 | INSERT INTO `roles_menus` VALUES (76, 1, 35, '2023-02-09 09:31:01'); 197 | INSERT INTO `roles_menus` VALUES (77, 1, 36, '2023-02-09 09:31:01'); 198 | INSERT INTO `roles_menus` VALUES (78, 1, 37, '2023-02-09 09:31:01'); 199 | INSERT INTO `roles_menus` VALUES (79, 2, 38, '2023-02-09 09:31:23'); 200 | INSERT INTO `roles_menus` VALUES (80, 1, 39, '2023-02-13 15:17:58'); 201 | 202 | -- ---------------------------- 203 | -- Table structure for user_logs 204 | -- ---------------------------- 205 | DROP TABLE IF EXISTS `user_logs`; 206 | CREATE TABLE `user_logs` ( 207 | `user_log_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 208 | `user_id` int NOT NULL COMMENT '用户ID', 209 | `ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录IP', 210 | `ua` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'ua标识', 211 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间', 212 | PRIMARY KEY (`user_log_id`) USING BTREE 213 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 214 | 215 | -- ---------------------------- 216 | -- Records of user_logs 217 | -- ---------------------------- 218 | 219 | -- ---------------------------- 220 | -- Table structure for users 221 | -- ---------------------------- 222 | DROP TABLE IF EXISTS `users`; 223 | CREATE TABLE `users` ( 224 | `user_id` int NOT NULL AUTO_INCREMENT COMMENT 'id唯一字段', 225 | `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', 226 | `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户密码', 227 | `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户昵称', 228 | `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户邮箱\r\n', 229 | `user_pic` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '用户头像', 230 | `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '用户状态: 0为停用、1启用\r\n', 231 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 232 | `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', 233 | PRIMARY KEY (`user_id`) USING BTREE 234 | ) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 235 | 236 | -- ---------------------------- 237 | -- Records of users 238 | -- ---------------------------- 239 | INSERT INTO `users` VALUES (2, 'Bob', '', '鲍勃', 'Bob@qq.com', 'public\\avatar\\龙二_2.jpg', '1', '2022-11-24 09:22:01', '2023-02-17 09:44:26'); 240 | INSERT INTO `users` VALUES (7, 'Alan', '$2a$10$yeltPgYjMVbwo2/Z.frtVewGVrv2lggcu3ZuhYPzIxadAjaNUVTv2', '艾伦', 'alan@qq.com', 'public\\avatar\\龙二_2.jpg', '1', '2022-12-01 14:32:12', '2022-12-01 14:36:53'); 241 | INSERT INTO `users` VALUES (9, 'Panda', '$2a$10$j9oVibmYrB/b0XGYn9wGCuU6M1K5mWzv9ABCHETmAGRf/5vZbJ4C6', '熊猫', 'panda@gmail.com', NULL, '1', '2022-12-01 14:54:42', '2023-02-09 09:09:59'); 242 | INSERT INTO `users` VALUES (10, 'Monkey', '$2a$10$y4mCtHy/oK1LPVr3Jc5M5O8vkbehZrqRlDIJWhlMA0vnrB7dhrHnS', NULL, NULL, NULL, '1', '2022-12-19 09:58:39', '2023-02-03 15:12:13'); 243 | INSERT INTO `users` VALUES (13, 'jacky', '$2a$10$8xczY1Nv7eCIdpoNrqV82OboD0CwLValQ2oviLYfEz0tPQjYyTBoy', NULL, NULL, NULL, '0', '2023-02-09 09:10:33', NULL); 244 | 245 | -- ---------------------------- 246 | -- Table structure for users_roles 247 | -- ---------------------------- 248 | DROP TABLE IF EXISTS `users_roles`; 249 | CREATE TABLE `users_roles` ( 250 | `user_role_id` int UNSIGNED NOT NULL AUTO_INCREMENT, 251 | `role_id` int NOT NULL COMMENT '角色ID', 252 | `user_id` int NOT NULL COMMENT '用户ID', 253 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 254 | PRIMARY KEY (`user_role_id`) USING BTREE 255 | ) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; 256 | 257 | -- ---------------------------- 258 | -- Records of users_roles 259 | -- ---------------------------- 260 | INSERT INTO `users_roles` VALUES (1, 1, 7, '2022-12-01 14:32:12'); 261 | INSERT INTO `users_roles` VALUES (2, 2, 7, '2022-12-01 14:36:53'); 262 | INSERT INTO `users_roles` VALUES (5, 1, 9, '2022-12-01 14:54:42'); 263 | INSERT INTO `users_roles` VALUES (6, 1, 2, '2022-12-08 10:57:49'); 264 | INSERT INTO `users_roles` VALUES (7, 2, 2, '2022-12-08 16:09:48'); 265 | INSERT INTO `users_roles` VALUES (8, 2, 10, '2022-12-19 09:58:39'); 266 | INSERT INTO `users_roles` VALUES (12, 2, 13, '2023-02-09 09:10:33'); 267 | 268 | SET FOREIGN_KEY_CHECKS = 1; 269 | --------------------------------------------------------------------------------