# 通用后台管理系统完整版 **Repository Path**: dongdongyangysd/normal-management-system-final ## Basic Information - **Project Name**: 通用后台管理系统完整版 - **Description**: 一款通用后台管理系统,技术包括:Vue3 、Scss、Echarts、wangEditor、Element Plus、Pinia、JavaScript、Express、MySQL,使用 Vite 构建前端 Vue 应用。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-13 - **Last Updated**: 2024-08-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 通用后台管理系统 #### 介绍 技术包括:Vue3 、Scss、Echarts、wangEditor、Element Plus、Pinia、JavaScript、Express、MySQL,使用 Vite 构建前端 Vue 应用。 #### 安装教程 1. xxxx 2. xxxx 3. xxxx #### 使用说明 1. \back 下使用nodemon app.js启动后端 2. \front2\normal_management_system 下使用npm run dev启动前端 3. xxxx #### 特技 1. 安装 node 2. 安装 nodemon 3. 安装 cors 解决跨域问题 4. 安装 body-parser 进行表单验证 5. 安装 joi 对注册时的输入密码进行限制 6. 安装 brcyptjs 进行密码加密(中间件) 7. 安装 token 的中间件 jsonwebtoken 8. 安装解析 token 的中间件 express-jwt 9. 安装 axois 前后端交互(前端) 10. 安装 mitt 实现 Vue2 中 bus 的功能(前端) 11. 安装 multer 用于上传头像 12. 导入 crypto 库用于生成 uuid 13. 导入 fs 库处理文件路径 14. 安装 vite-plugin-svg-icons 和 fast-glob 可使用 icons 库(前端) 15. 安装 pinia-persistedstate-plugin 插件持久 pinia 数据(前端) 16. 安装 echarts 插件(前端) 17. 路由守卫 防止通过token直接跳转进入系统 ``````typescript import router from './router' import {useMenu} from '@/stores/menu' // 全局前置守卫 router.beforeEach( (to, from, next) => { // const menuStore = useMenu() const token =localStorage.getItem('token') if (to.name !== 'login' && !token) next({ name: 'login' }) else next() }) `````` 18. 动态路由=>登陆的时候根据用户id从后端获取路由路径,实现动态用户菜单。 后端写好各种identity对应的路由路径 ``````js // 消息管理员路由 const messageAdminRouter = [ { name: 'home', path: '/home', meta: {title: '首页'}, component: 'home/index', }, { name: 'set', path: '/set', meta: {title: '设置'}, component: 'set/index', }, { name: 'message_list', path: '/message_list', meta: {title: '消息管理'}, component: 'message/message_list/index' }, { name: 'recycle', path: '/recycle', meta: {title: '回收站'}, component: 'message/recycle/index' }, { name: 'file', path: '/file', meta: {title: '文件管理'}, component: 'file/index' }, ] // 普通用户路由 const userRouter = [ { name: 'home', path: '/home', meta: {title: '首页'}, component: 'home/index' }, { name: 'set', path: '/set', meta: {title: '设置'}, component: 'set/index' }, { name: 'product_manage_list', path: '/product_manage_list', meta: {title: '产品管理'}, component: 'product/product_manage_list/index' }, { name: 'out_product_manage_list', path: '/out_product_manage_list', meta: {title: '出库管理'}, component: 'product/out_product_manage_list/index' }, { name: 'file', path: '/file', meta: {title: '文件管理'}, component: 'file/index' }, ] //...等等 // 返回用户的路由列表,参数ID exports.returnMenuList = (req,res) =>{ const sql = 'select identity from users where id = ?' db.query(sql,req.body.id,(err,result)=>{ if (err) return res.cc(err) let menu = [] if(result[0].identity=='超级管理员'){ menu = superAdminRouter } if(result[0].identity=='用户管理员'){ menu = userAdminRouter } if(result[0].identity=='产品管理员'){ menu = productAdminRouter } if(result[0].identity=='消息管理员'){ menu = messageAdminRouter } if(result[0].identity=='用户'){ menu = userRouter } res.send(menu) }) } `````` 需要把后端转义的过程写在pinia中进行持久化,对后端获取到的字符串进行转义,把获取到的数据转译为前端框架中的router的形式,转义时候需要使用import.meta.glob实现懒加载component。 ```TypeScript import { defineStore } from 'pinia' import router from '@/router' import {ref} from "vue"; // 使用了setup写法 举例 export const useMenu = defineStore('menuInfo', () => { const menuData = ref([]) // 函数相当于传统写法的action const setRouter = (arr:any) =>{ function compilerMenu (arr:any) { if(!arr){ return } //保证在刷新时出现路由丢失的问题,存储在pinia中 menuData.value = arr arr.forEach((item)=>{ let rts = { name:item.name, path:item.path, meta:item.meta, component:item.component } if(item.children && item.children.length){ compilerMenu(item.children) } if(!item.children){ let path = loadComponent(item.component) rts.component = path; router.addRoute('menu',rts) } function loadComponent(url:string){ let Module = import.meta.glob("@/views/**/*.vue") return Module[`/src/views/${url}.vue`] } }) } compilerMenu(arr as any) } const addRouter = () =>{ setRouter(menuData.value) } return { addRouter, menuData, setRouter } }, { persist: true }) ``` 19.将分页等重复的逻辑写成hooks ``````ts import { searchUser, getAdminListLength, returnListData } from '@/api/userinfor' import {ref,reactive,watch} from "vue"; import {bus} from '@/utils/mitt' export const useTable = (identity:string) =>{ // 分页数据 const paginationData = reactive({ // 总页数 pageCount: 1, // 当前所处页数 currentPage: 1, }) // 总的管理员人数 const adminTotal = ref(0) // 搜索框的modelValue const adminAccount = ref() // 表格内容 const tableData = ref([]) // 返回管理员列表长度 const returnAdminListLength = async () => { const res = await getAdminListLength(identity) as any adminTotal.value = res.length paginationData.pageCount = Math.ceil(res.length / 10) } // 获取第一页的数据 const getFirstPageList = async () => { tableData.value = await returnListData(1, identity) as any } // 监听换页 const currentChange = async (value : number) => { paginationData.currentPage = value tableData.value = await returnListData(paginationData.currentPage, identity) as any } // 通过账号搜索管理员 const searchAdmin = async () => { tableData.value = await searchUser(adminAccount.value as number,identity) as any } // 当搜索内容清空后,返回当前页面的数据 const clearInput = async () =>{ tableData.value = await returnListData(paginationData.currentPage, identity) as any } bus.on('adminDialogOff', async (id : number) => { // 当前页数 const current = paginationData.currentPage // 1为创建管理员 if (id == 1) { await getFirstPageList() } // 2为编辑管理员 if (id == 2) { tableData.value = await returnListData(paginationData.currentPage, identity) as any } // 3为对管理员进行降职 if (id == 3) { tableData.value = await returnListData(paginationData.currentPage, identity) as any if (tableData.value.length == 0) { paginationData.currentPage = current - 1 await returnAdminListLength() } } }) watch( paginationData, ()=>{ returnAdminListLength() getFirstPageList() }, {immediate:true,deep:true} ) watch( ()=>paginationData.currentPage, ()=>{ currentChange(paginationData.currentPage) }, {immediate:true,deep:true} ) watch( adminTotal, ()=>{ returnAdminListLength() }, {immediate:true,deep:true} ) return { adminAccount, paginationData, adminTotal, tableData, currentChange, searchAdmin, clearInput } } ``````