├── src ├── assets │ ├── style │ │ ├── variables.scss │ │ ├── element │ │ │ ├── index.scss │ │ │ ├── light.scss │ │ │ └── dark.scss │ │ ├── main.scss │ │ ├── mixin.scss │ │ ├── theme.scss │ │ └── normalize.css │ └── icons │ │ ├── index.ts │ │ └── svg │ │ ├── mianxingzuojiantou.svg │ │ ├── mianxingshangjiantou.svg │ │ ├── mianxingxiajiantou.svg │ │ ├── mianxingyoujiantou.svg │ │ ├── xianxingshangjiantou.svg │ │ ├── xianxingxiajiantou.svg │ │ ├── xianxingyoujiantou.svg │ │ ├── xianxingzuojiantou.svg │ │ ├── morentouxiang.svg │ │ ├── sousuo.svg │ │ ├── excel.svg │ │ ├── nanxing.svg │ │ ├── tuichu.svg │ │ ├── fanhui.svg │ │ ├── gengduo-shu.svg │ │ ├── jingshi.svg │ │ ├── gengduo-heng.svg │ │ ├── shouye.svg │ │ ├── qingchu-mianxing.svg │ │ ├── gou.svg │ │ ├── shuaxin.svg │ │ ├── yidu.svg │ │ ├── fasong02.svg │ │ ├── shoucang-xuanzhong.svg │ │ ├── shengxu.svg │ │ ├── shezhi03.svg │ │ ├── zhongwen.svg │ │ ├── cha.svg │ │ ├── tishi.svg │ │ ├── jiangxu.svg │ │ ├── ppt.svg │ │ ├── gengduoneirong.svg │ │ ├── wenjianjia.svg │ │ ├── shanchu-mianxing.svg │ │ ├── jiaoseguanli.svg │ │ ├── xiaoji.svg │ │ ├── qingchu-xianxing.svg │ │ ├── shezhi02.svg │ │ ├── suoxiao.svg │ │ ├── shezhi01.svg │ │ ├── tianjiarenyuan.svg │ │ ├── yingwen.svg │ │ ├── suoding.svg │ │ ├── xihuan-xuanzhong.svg │ │ ├── jiesuo.svg │ │ ├── daima.svg │ │ ├── tongzhi.svg │ │ ├── jiji.svg │ │ ├── shangchuan.svg │ │ ├── bukejian.svg │ │ ├── tianjiawenjianjia.svg │ │ ├── xiazai.svg │ │ ├── zhiding.svg │ │ ├── baocundaobendi.svg │ │ ├── fuwuqi.svg │ │ ├── tianjiawenjian.svg │ │ ├── tupian.svg │ │ ├── ziliao.svg │ │ ├── kejian.svg │ │ ├── word.svg │ │ ├── nvxing.svg │ │ ├── dayin.svg │ │ ├── fangda.svg │ │ ├── tuwenliebiao-mianxing.svg │ │ ├── bangzhu.svg │ │ ├── jiebang.svg │ │ ├── yuangong.svg │ │ ├── xiugai.svg │ │ ├── fenlei.svg │ │ ├── yiwen.svg │ │ ├── shoucang-moren.svg │ │ ├── xiangmu.svg │ │ ├── tuwengongge-xianxing.svg │ │ ├── bangding.svg │ │ ├── wanchengrenwu.svg │ │ ├── liucheng.svg │ │ ├── dingdan.svg │ │ ├── jiagou.svg │ │ ├── xihuan-moren.svg │ │ ├── shanchu.svg │ │ ├── tongshi.svg │ │ ├── xunhuan.svg │ │ └── zichan.svg ├── vite-env.d.ts ├── utils │ ├── RegExp.ts │ ├── auth.ts │ ├── global.ts │ ├── util.ts │ └── request.ts ├── views │ ├── home │ │ └── home.vue │ ├── sysManage │ │ ├── roleManage │ │ │ ├── roleModal.vue │ │ │ └── roleManage.vue │ │ ├── menuManage │ │ │ ├── menuManage.vue │ │ │ └── menuModal.vue │ │ └── staffManage │ │ │ ├── staffModal.vue │ │ │ └── staffManage.vue │ └── login │ │ ├── login.vue │ │ └── resetPassword.vue ├── dictionary │ ├── dictionary.d.ts │ ├── user.ts │ ├── staff.ts │ └── menu.ts ├── layout │ ├── components │ │ ├── Sidebar │ │ │ ├── index.vue │ │ │ ├── SidebarItem.vue │ │ │ └── SidebarMenu.vue │ │ ├── AppMain.vue │ │ ├── Navbar.vue │ │ └── TagsView │ │ │ └── index.vue │ └── index.vue ├── api │ ├── public.ts │ ├── menu.ts │ ├── role.ts │ ├── role copy.ts │ └── staff.ts ├── request.d.ts ├── directives │ ├── index.ts │ └── module │ │ └── permission.ts ├── App.vue ├── main.ts ├── components │ └── SvgIcon │ │ └── index.vue ├── router │ └── index.ts ├── permission.ts ├── stores │ ├── tagView.ts │ ├── staff.ts │ └── permission.ts └── style.css ├── .vite └── deps_temp_767ecee9 │ └── package.json ├── .env.production ├── .vscode └── extensions.json ├── .env.development ├── tsconfig.node.json ├── README.md ├── env.d.ts ├── .gitignore ├── index.html ├── tsconfig.json ├── package.json ├── LICENSE ├── components.d.ts ├── vite.config.ts └── auto-imports.d.ts /src/assets/style/variables.scss: -------------------------------------------------------------------------------- 1 | $sideBarWidth: 210px, -------------------------------------------------------------------------------- /.vite/deps_temp_767ecee9/package.json: -------------------------------------------------------------------------------- 1 | {"type":"module"} -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/utils/RegExp.ts: -------------------------------------------------------------------------------- 1 | export const phoneReg = /^1[3456789]\d{9}$/ 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | MODE = 'production' 2 | VITE_APP_BASE_PREFIX = '/accountApi' -------------------------------------------------------------------------------- /src/assets/style/element/index.scss: -------------------------------------------------------------------------------- 1 | @use './light.scss'; 2 | @use './dark.scss'; -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/style/main.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @import './theme.scss'; 3 | @import './mixin.scss'; -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | MODE = 'development' 2 | VITE_APP_BASE_PREFIX = '/api' 3 | VITE_APP_BASE_API = http://localhost:6001 -------------------------------------------------------------------------------- /src/views/home/home.vue: -------------------------------------------------------------------------------- 1 | 2 | hello 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/dictionary/dictionary.d.ts: -------------------------------------------------------------------------------- 1 | interface boolRadio { 2 | trueValue: string 3 | [key: string]: string 4 | } 5 | 6 | interface defaultType { 7 | default: string 8 | [key: string]: string 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/icons/index.ts: -------------------------------------------------------------------------------- 1 | const icons = import.meta.glob('./svg/*.svg') 2 | 3 | let iconNames: Array = [] 4 | Object.keys(icons).forEach((key) => { 5 | const name = key.replace(/\.\/svg\/(.*)\.svg/, '$1') 6 | iconNames.push(name) 7 | }) 8 | export default iconNames 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hevue3-admin 2 | vite + vue3 + typescript + element-plus + pinia 开发通用管理后台 3 | 4 | 系列文章地址:[https://juejin.cn/column/7224147971834724407](https://juejin.cn/column/7224147971834724407) 5 | 6 | 在线体验地址 [ ]() -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /src/api/public.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取验证码 4 | 5 | export function getCaptcha(data: { 6 | phone: string 7 | uuid: string 8 | purpose: string 9 | }): any { 10 | return request({ 11 | url: '/staff/getVerifyCode', 12 | method: 'get', 13 | params: data 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | //引入vue模块中ts的方法 5 | import type { DefineComponent } from 'vue' 6 | // 定义vue組件以及类型注解 7 | const component: DefineComponent<{}, {}, any> 8 | export default component 9 | } 10 | 11 | declare module 'element-plus/dist/locale/zh-cn.mjs' 12 | -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | const TokenKey = 'token' 2 | 3 | export function getToken() { 4 | return localStorage.getItem(TokenKey) 5 | } 6 | 7 | export function setToken(token: string) { 8 | return localStorage.setItem(TokenKey, token) 9 | } 10 | 11 | export function removeToken() { 12 | return localStorage.setItem(TokenKey, '') 13 | } 14 | -------------------------------------------------------------------------------- /src/request.d.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios' 2 | 3 | declare module 'axios' { 4 | export interface AxiosRequestConfig { 5 | requestBase?: string 6 | // [自定义属性声明] 7 | } 8 | } 9 | 10 | // declare module '*.ts' 11 | 12 | // declare module '*.scss' { 13 | // const css: string 14 | // export default css 15 | // } 16 | -------------------------------------------------------------------------------- /src/directives/index.ts: -------------------------------------------------------------------------------- 1 | const directives: any = import.meta.glob('./module/*.ts', { eager: true }) 2 | 3 | export default { 4 | install(app: any) { 5 | Object.keys(directives).forEach((key) => { 6 | const name = key.replace(/\.\/module\/(.*)\.ts/, '$1') 7 | app.directive(name, directives[key].default) 8 | }) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/icons/svg/mianxingzuojiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/dictionary/user.ts: -------------------------------------------------------------------------------- 1 | // 性别 2 | export const genderDic: defaultType = { 3 | default: '1', 4 | '1': '男', 5 | '2': '女', 6 | } 7 | Object.defineProperty(genderDic, 'default', { 8 | enumerable: false, 9 | }) 10 | 11 | // 账号状态 12 | export const statusDic: object = { 13 | '0': '未激活', 14 | '1': '正常', 15 | '2': '封号', 16 | '3': '注销', 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/icons/svg/mianxingshangjiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/mianxingxiajiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/mianxingyoujiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/utils/global.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from 'vue' 2 | import { superAdmin, superAdminRole } from '../dictionary/staff' 3 | 4 | export default function useGetGlobalProperties() { 5 | const { 6 | appContext: { 7 | app: { 8 | config: { globalProperties }, 9 | }, 10 | }, 11 | } = getCurrentInstance() as any 12 | 13 | return { ...globalProperties, superAdmin, superAdminRole } 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xianxingshangjiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xianxingxiajiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xianxingyoujiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xianxingzuojiantou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/morentouxiang.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/sousuo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import { createPinia } from 'pinia' 5 | import '@/assets/style/normalize.css' 6 | import directives from './directives' 7 | import './permission' 8 | 9 | import SvgIcon from './components/SvgIcon/index.vue' 10 | import 'virtual:svg-icons-register' 11 | 12 | createApp(App) 13 | .use(router) 14 | .use(createPinia()) 15 | .use(directives) 16 | .component('svg-icon', SvgIcon) 17 | .mount('#app') 18 | -------------------------------------------------------------------------------- /src/assets/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/nanxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tuichu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fanhui.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/gengduo-shu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jingshi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/gengduo-heng.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/dictionary/staff.ts: -------------------------------------------------------------------------------- 1 | // 账号状态 2 | export const staffStatusDic: { [key: string]: { text: string; type: string } } = 3 | { 4 | // '0': { 5 | // text: '未激活', 6 | // type: 'warning' 7 | // }, 8 | '1': { 9 | text: '正常', 10 | type: 'success', 11 | }, 12 | '2': { 13 | text: '禁用', 14 | type: 'danger', 15 | }, 16 | '3': { 17 | text: '离职', 18 | type: 'info', 19 | }, 20 | } 21 | 22 | // 超级管理员 23 | export const superAdmin: string = 'admin' 24 | // 超级管理员角色 25 | export const superAdminRole: string = 'admin' 26 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shouye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/qingchu-mianxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/gou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/directives/module/permission.ts: -------------------------------------------------------------------------------- 1 | import { usePermissionStore } from '@/stores/permission' 2 | import { useStaffStore } from '@/stores/staff' 3 | import { superAdminRole } from '@/dictionary/staff' 4 | 5 | export default { 6 | mounted(el: any, binding: any) { 7 | const permissionStore = usePermissionStore() 8 | const staffStore = useStaffStore() 9 | 10 | if (staffStore.staff?.role_code === superAdminRole) { 11 | return 12 | } 13 | const hasPermission = permissionStore.permissions.includes(binding.value) 14 | if (!hasPermission) { 15 | el.remove() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shuaxin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/yidu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fasong02.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shoucang-xuanzhong.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shengxu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shezhi03.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/zhongwen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/cha.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tishi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiangxu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/ppt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/gengduoneirong.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/wenjianjia.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 23 | 32 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shanchu-mianxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiaoseguanli.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xiaoji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/qingchu-xianxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": ["ESNext", "DOM"], 13 | "skipLibCheck": true, 14 | "noEmit": true, 15 | "paths": { 16 | "@/*": ["./src/*"] 17 | } 18 | }, 19 | "include": [ 20 | "env.d.ts", 21 | "src/**/*.ts", 22 | "src/**/*.d.ts", 23 | "src/**/*.tsx", 24 | "src/**/*.vue", 25 | "auto-imports.d.ts" 26 | ], 27 | "references": [{ "path": "./tsconfig.node.json" }] 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shezhi02.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/suoxiao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shezhi01.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/menu.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 新建菜单 4 | export function createMenu(data: any): any { 5 | return request({ 6 | url: '/menu/createMenu', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | // 修改菜单 13 | export function updateMenu(data: any): any { 14 | return request({ 15 | url: '/menu/updateMenu', 16 | method: 'post', 17 | data 18 | }) 19 | } 20 | 21 | // 删除菜单 22 | export function deleteMenu(data: any): any { 23 | return request({ 24 | url: '/menu/deleteMenu', 25 | method: 'post', 26 | data 27 | }) 28 | } 29 | 30 | // 获取菜单列表 31 | export function getMenuList(): any { 32 | return request({ 33 | url: '/menu/getMenuList', 34 | method: 'get' 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tianjiarenyuan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/yingwen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/suoding.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xihuan-xuanzhong.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiesuo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/daima.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tongzhi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shangchuan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/bukejian.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tianjiawenjianjia.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xiazai.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/zhiding.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/baocundaobendi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fuwuqi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tianjiawenjian.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tupian.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/ziliao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/kejian.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/word.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/nvxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/style/element/light.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | @use '../theme.scss'as *; 3 | 4 | @forward 'element-plus/theme-chalk/src/common/var.scss'with ( // 5 | $colors: ( // 6 | 'white': map.get($light, --color-white), 7 | 'black': map.get($light, --color-black), 8 | 'primary': ( // 9 | 'base': map.get($light, --color-primary), 10 | ), 11 | 'success': ( // 12 | 'base': map.get($light, --color-success), 13 | ), 14 | 'warning': ( // 15 | 'base': map.get($light, --color-warning), 16 | ), 17 | 'danger': ( // 18 | 'base': map.get($light, --color-danger), 19 | ), 20 | 'error': ( // 21 | 'base': map.get($light, --color-error), 22 | ), 23 | 'info': ( // 24 | 'base': map.get($light, --color-info), 25 | ), 26 | ), 27 | ); 28 | // @use "element-plus/theme-chalk/src/common/var.scss"; -------------------------------------------------------------------------------- /src/assets/icons/svg/dayin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fangda.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tuwenliebiao-mianxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/style/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ''; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | 14 | &::-webkit-scrollbar { 15 | width: 6px; 16 | } 17 | 18 | &::-webkit-scrollbar-thumb { 19 | background: #99a9bf; 20 | border-radius: 20px; 21 | } 22 | } 23 | 24 | @mixin relative { 25 | position: relative; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | // 超出隐藏 31 | // @mixin text-overflow { 32 | // overflow: hidden; 33 | // text-overflow: ellipsis; 34 | // white-space: nowrap; 35 | // } 36 | 37 | // 多行超出隐藏,可配置 38 | @mixin text-overflow($line: 1) { 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | display: -webkit-box; 42 | -webkit-line-clamp: $line; 43 | -webkit-box-orient: vertical; 44 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hevue3-admin", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@esbuild/darwin-arm64": "^0.23.1", 13 | "@types/node": "^18.15.11", 14 | "axios": "^1.3.5", 15 | "element-plus": "^2.3.3", 16 | "pinia": "^2.0.34", 17 | "vue": "^3.2.47", 18 | "vue-router": "4" 19 | }, 20 | "devDependencies": { 21 | "@iconify-json/ep": "^1.1.10", 22 | "@vitejs/plugin-vue": "^4.1.0", 23 | "sass": "^1.62.0", 24 | "typescript": "^4.9.3", 25 | "unplugin-auto-import": "^0.15.2", 26 | "unplugin-icons": "^0.16.1", 27 | "unplugin-vue-components": "^0.24.1", 28 | "vite": "^4.2.0", 29 | "vite-plugin-svg-icons": "^2.0.1", 30 | "vue-tsc": "^1.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/icons/svg/bangzhu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiebang.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/yuangong.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xiugai.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fenlei.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/yiwen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createRouter, 3 | createWebHashHistory, 4 | type RouteRecordRaw, 5 | } from 'vue-router' 6 | 7 | export const publicRouters: RouteRecordRaw[] = [ 8 | { 9 | path: '/', 10 | component: () => import('@/layout/index.vue'), 11 | redirect: '/home', 12 | 13 | children: [ 14 | { 15 | path: '/home', 16 | name: 'home', 17 | component: () => import('@/views/home/home.vue'), 18 | meta: { title: '首页', icon: 'shouye', affix: true, sort: '99.99' }, 19 | }, 20 | ], 21 | meta: { 22 | sort: '99', 23 | }, 24 | }, 25 | { 26 | path: '/login', 27 | name: 'login', 28 | component: () => import('@/views/login/login.vue'), 29 | meta: { 30 | hidden: '1', 31 | }, 32 | }, 33 | ] 34 | const router = createRouter({ 35 | history: createWebHashHistory(), 36 | routes: publicRouters, 37 | }) 38 | 39 | export default router 40 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shoucang-moren.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/style/element/dark.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | @use '../theme.scss'as *; 3 | @forward 'element-plus/theme-chalk/src/dark/var.scss'with ( // 4 | $colors: ( // 5 | 'white': map.get($dark, --color-white), 6 | 'black': map.get($dark, --color-black), 7 | 'primary': ( // 8 | 'base': map.get($dark, --color-primary), 9 | ), 10 | 'success': ( // 11 | 'base': map.get($dark, --color-success), 12 | ), 13 | 'warning': ( // 14 | 'base': map.get($dark, --color-warning), 15 | ), 16 | 'danger': ( // 17 | 'base': map.get($dark, --color-danger), 18 | ), 19 | 'error': ( // 20 | 'base': map.get($dark, --color-error), 21 | ), 22 | 'info': ( // 23 | 'base': map.get($dark, --color-info), 24 | ), 25 | ), 26 | $bg-color: ( // 27 | 'page': #0a0a0a, 28 | '': #141414, 29 | 'overlay': #1d1e1f, 30 | )); 31 | 32 | @use "element-plus/theme-chalk/src/dark/css-vars.scss"; -------------------------------------------------------------------------------- /src/assets/icons/svg/xiangmu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tuwengongge-xianxing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/bangding.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/wanchengrenwu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/liucheng.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/dingdan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/role.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 添加角色 4 | export function createRole(data: any): any { 5 | return request({ 6 | url: '/role/createRole', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | // 更新角色信息 13 | export function updateRole(data: any): any { 14 | return request({ 15 | url: '/role/updateRole', 16 | method: 'post', 17 | data 18 | }) 19 | } 20 | 21 | // 获取角色信息 22 | export function getRoleInfo(): any { 23 | return request({ 24 | url: '/role/getRoleInfo', 25 | method: 'get' 26 | }) 27 | } 28 | 29 | // 获取角色列表 30 | export function getRoleList(query: any): any { 31 | return request({ 32 | url: '/role/getRoleList', 33 | method: 'get', 34 | params: query 35 | }) 36 | } 37 | 38 | // 删除角色 39 | export function deleteRole(data: any): any { 40 | return request({ 41 | url: '/role/deleteRole', 42 | method: 'post', 43 | data 44 | }) 45 | } 46 | 47 | // 获取登录者所属角色的权限 48 | export function getRolePermission(): any { 49 | return request({ 50 | url: '/role/getRolePermission', 51 | method: 'get' 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /src/api/role copy.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 添加角色 4 | export function createRole(data: any): any { 5 | return request({ 6 | url: '/role/createRole', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | // 更新角色信息 13 | export function updateRole(data: any): any { 14 | return request({ 15 | url: '/role/updateRole', 16 | method: 'post', 17 | data 18 | }) 19 | } 20 | 21 | // 获取角色信息 22 | export function getRoleInfo(): any { 23 | return request({ 24 | url: '/role/getRoleInfo', 25 | method: 'get' 26 | }) 27 | } 28 | 29 | // 获取角色列表 30 | export function getRoleList(query: any): any { 31 | return request({ 32 | url: '/role/getRoleList', 33 | method: 'get', 34 | params: query 35 | }) 36 | } 37 | 38 | // 删除角色 39 | export function deleteRole(data: any): any { 40 | return request({ 41 | url: '/role/deleteRole', 42 | method: 'post', 43 | data 44 | }) 45 | } 46 | 47 | // 获取登录者所属角色的权限 48 | export function getRolePermission(): any { 49 | return request({ 50 | url: '/role/getRolePermission', 51 | method: 'get' 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 heyongsheng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/icons/svg/jiagou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xihuan-moren.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shanchu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 50 | -------------------------------------------------------------------------------- /src/utils/util.ts: -------------------------------------------------------------------------------- 1 | // 扁平数据转树结构---暂不支持多级 2 | export function arrToTree({ 3 | list = [], 4 | id = '_id', 5 | pid = 'parentId', 6 | children = 'children', 7 | }: { 8 | list: any[] 9 | id?: string 10 | pid?: string 11 | children?: string 12 | }) { 13 | // 定义最终需要返回的树形结构数据 14 | let treeData: any[] = [] 15 | // 对传入的数组进行遍历 16 | list.forEach((item) => { 17 | // 如果pid没有值,那就是顶级对象,直接添加进数组 18 | if (!item[pid]) { 19 | treeData.push(item) 20 | } 21 | // 理解为二次遍历 :每个对象都找一遍自己的孩子添加到children 22 | let objList = list.filter((data) => data[pid] === item[id]) 23 | if (objList.length) { 24 | item[children] = objList 25 | } 26 | }) 27 | return treeData 28 | } 29 | 30 | // 版本号大小对比 31 | export function compareVersion(v1: string = '0', v2: string = '0'): number { 32 | let v1Arr = v1.split('.') 33 | let v2Arr = v2.split('.') 34 | const len = Math.max(v1.length, v2.length) 35 | 36 | while (v1Arr.length < len) { 37 | v1Arr.push('0') 38 | } 39 | while (v2Arr.length < len) { 40 | v2Arr.push('0') 41 | } 42 | 43 | for (let i = 0; i < len; i++) { 44 | const num1 = parseInt(v1Arr[i]) 45 | const num2 = parseInt(v2Arr[i]) 46 | 47 | if (num1 > num2) { 48 | return 1 49 | } else if (num1 < num2) { 50 | return -1 51 | } 52 | } 53 | 54 | return 0 55 | } 56 | -------------------------------------------------------------------------------- /src/assets/style/theme.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | $light : () !default; 3 | $light : ( // light theme 4 | '--color-white': #ffffff, 5 | '--color-black': #000000, 6 | '--color-primary': #0497e6, 7 | '--color-success': #67c23a, 8 | '--color-warning': #e6a23c, 9 | '--color-danger': #f56c6c, 10 | '--color-error': #f56c6c, 11 | '--color-info': #909399, 12 | 13 | '--color-bg-1': #fff, 14 | '--color-bg-2': #f5f5f5, 15 | '--color-primary-reverse': #fff, 16 | '--color-text-1': #000, 17 | '--color-text-2': #333, 18 | '--color-text-3': #666, 19 | '--color-menu-bg': #fff, 20 | '--color-menu-text': #7a7a7a, 21 | '--color-border-1': #ddd); 22 | $dark : () !default; 23 | // map.merge方法类似于Object.assign,用于合并两个map,如果有相同的key,后面的会覆盖前面的,但不会影响原map 24 | // map.deep-merge方法与map.merge类似,但是如果value是map,会递归合并 25 | $dark : map.deep-merge($light, ( // dark theme 26 | '--color-bg-1': #121212, 27 | '--color-bg-2': #000000, 28 | '--color-primary-reverse': #fff, 29 | '--color-text-1': #fff, 30 | '--color-text-2': #cfd3dc, 31 | '--color-menu-bg': #121212, 32 | '--color-menu-text': #7a7a7a, 33 | '--color-border-1': #2e2e2e)); 34 | 35 | html { 36 | 37 | @each $key, 38 | $value in $light { 39 | #{$key}: $value; 40 | } 41 | } 42 | 43 | html.dark { 44 | 45 | @each $key, 46 | $value in $dark { 47 | #{$key}: $value; 48 | } 49 | } -------------------------------------------------------------------------------- /src/assets/icons/svg/tongshi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from '@/router' 2 | import { useStaffStore } from '@/stores/staff' 3 | import { usePermissionStore } from '@/stores/permission' 4 | 5 | const whiteList = ['/login', '/404', '/resetPassword'] // no redirect whitelist 6 | // 路由前置守卫 7 | 8 | router.beforeEach(async (to, _from, next) => { 9 | const store = useStaffStore() 10 | // 获取token 11 | const token = store.token 12 | 13 | // 如果token存在 14 | if (token) { 15 | // 如果是登录页 16 | if (to.path === '/login') { 17 | // 跳转到首页 18 | next('/') 19 | } else { 20 | if (!store.staff) { 21 | try { 22 | await store.getStaffInfo() 23 | } catch (error) { 24 | store.logOut() 25 | next('/login') 26 | } 27 | } 28 | const permissionStore = usePermissionStore() 29 | if (!permissionStore.routes || permissionStore.routes.length == 0) { 30 | const accessRoutes = await permissionStore.getAccessRoutes() 31 | 32 | accessRoutes.forEach((route) => { 33 | router.addRoute(route) 34 | }) 35 | next({ path: to.fullPath, replace: true, query: to.query }) 36 | } else { 37 | next() 38 | } 39 | } 40 | } else { 41 | // 如果是白名单 42 | if (whiteList.indexOf(to.path) !== -1) { 43 | // 正常跳转 44 | next() 45 | } else { 46 | // 否则跳转到登录页 47 | next('/login') 48 | } 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /src/api/staff.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 员工登录 4 | export function login(data: any): any { 5 | return request({ 6 | url: '/staff/login', 7 | method: 'post', 8 | data, 9 | }) 10 | } 11 | 12 | // 添加员工 13 | export function createStaff(data: any): any { 14 | return request({ 15 | url: '/staff/createStaff', 16 | method: 'post', 17 | data, 18 | }) 19 | } 20 | 21 | // 更新员工信息 22 | export function updateStaff(data: any): any { 23 | return request({ 24 | url: '/staff/updateStaff', 25 | method: 'post', 26 | data, 27 | }) 28 | } 29 | // 获取员工信息 30 | export function getStaffInfo(): any { 31 | return request({ 32 | url: '/staff/getStaffInfo', 33 | method: 'get', 34 | }) 35 | } 36 | // 修改密码 37 | export function updatePassword(data: { 38 | phone: string 39 | uuid: string 40 | captcha: string 41 | password: string 42 | }): Promise { 43 | return request({ 44 | url: '/staff/updateStaffPassword', 45 | method: 'post', 46 | data, 47 | }) 48 | } 49 | 50 | // 获取员工列表 51 | export function getStaffList(query: any): any { 52 | return request({ 53 | url: '/staff/getStaffList', 54 | method: 'get', 55 | params: query, 56 | }) 57 | } 58 | 59 | // 修改员工状态 60 | export function updateStaffStatus(data: any): any { 61 | return request({ 62 | url: '/staff/updateStaffStatus', 63 | method: 'post', 64 | data, 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 16 | {{ itemData.meta.title }} 17 | 18 | 19 | 24 | 25 | 26 | 30 | 31 | 34 | {{ itemData.meta.title }} 35 | 36 | 37 | 38 | 39 | 42 | 47 | -------------------------------------------------------------------------------- /src/stores/tagView.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export const useTagsViewStore = defineStore('tagsView', { 4 | state: () => ({ 5 | visitedViews: new Array(), 6 | cachedViews: new Array(), 7 | }), 8 | actions: { 9 | addView(view: any) { 10 | this.addVisitedView(view) 11 | this.addCachedView(view) 12 | }, 13 | addVisitedView(view: any) { 14 | if (this.visitedViews.some((v) => v.path === view.path)) return 15 | this.visitedViews.push( 16 | Object.assign({}, view, { 17 | title: view.meta.title || 'no-name', 18 | }) 19 | ) 20 | }, 21 | addCachedView(view: any) { 22 | if (this.cachedViews.includes(view.name)) return 23 | if (view.meta.cache) { 24 | this.cachedViews.push(view.name) 25 | } 26 | }, 27 | delView(view: any) { 28 | const index = this.visitedViews.findIndex((v) => v.path === view.path) 29 | if (index > -1) { 30 | this.visitedViews.splice(index, 1) 31 | } 32 | const index2 = this.cachedViews.findIndex((v) => v === view.name) 33 | if (index2 > -1) { 34 | this.cachedViews.splice(index2, 1) 35 | } 36 | }, 37 | // 二次跳转一个页面,路由信息会发生变化,需要更新 38 | updateVisitedView(view: any) { 39 | const index = this.visitedViews.findIndex((v) => v.path === view.path) 40 | if (index > -1) { 41 | this.visitedViews[index] = Object.assign({}, view) 42 | } 43 | }, 44 | }, 45 | }) 46 | -------------------------------------------------------------------------------- /src/assets/icons/svg/xunhuan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/zichan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stores/staff.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { login, getStaffInfo } from '@/api/staff' 3 | import { setToken, getToken, removeToken } from '@/utils/auth' 4 | import { usePermissionStore } from '@/stores/permission' 5 | import router from '@/router' 6 | interface Staff { 7 | staff_account: string 8 | password: string 9 | } 10 | 11 | export const useStaffStore = defineStore('staff', { 12 | state: () => ({ 13 | token: getToken() || '', 14 | staff: ref(), 15 | }), 16 | actions: { 17 | setToken(token: string) { 18 | this.token = token 19 | }, 20 | logOut() { 21 | this.token = '' 22 | this.staff = null 23 | const permissionStore = usePermissionStore() 24 | permissionStore.$reset() 25 | removeToken() 26 | router.push('/login') 27 | }, 28 | loginHandle(staff: Staff) { 29 | return new Promise((resolve, reject) => { 30 | login(staff) 31 | .then((res: any) => { 32 | this.setToken(res.data) 33 | setToken(res.data) 34 | resolve(res) 35 | // this.getUserInfo() 36 | }) 37 | .catch((err: any) => { 38 | reject(err) 39 | }) 40 | }) 41 | }, 42 | getStaffInfo() { 43 | return new Promise((resolve, reject) => { 44 | getStaffInfo() 45 | .then((res: any) => { 46 | this.staff = res.data 47 | resolve(res) 48 | }) 49 | .catch((err: any) => { 50 | reject(err) 51 | }) 52 | }) 53 | }, 54 | }, 55 | }) 56 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | .card { 59 | padding: 2em; 60 | } 61 | 62 | #app { 63 | max-width: 1280px; 64 | margin: 0 auto; 65 | padding: 2rem; 66 | text-align: center; 67 | } 68 | 69 | @media (prefers-color-scheme: light) { 70 | :root { 71 | color: #213547; 72 | background-color: #ffffff; 73 | } 74 | a:hover { 75 | color: #747bff; 76 | } 77 | button { 78 | background-color: #f9f9f9; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/dictionary/menu.ts: -------------------------------------------------------------------------------- 1 | // 菜单类型 2 | export const menuTypeDic: defaultType = { 3 | '1': '菜单', 4 | '2': '页面', 5 | '3': '按钮', 6 | default: '1', 7 | } 8 | Object.defineProperty(menuTypeDic, 'default', { 9 | enumerable: false, 10 | }) 11 | 12 | export const menuTestDic: object = { 13 | '1': '菜单', 14 | '2': '页面', 15 | '3': '按钮', 16 | } 17 | 18 | // 菜单是否隐藏 19 | export const menuHideDic: boolRadio & defaultType = { 20 | '0': '否', 21 | '1': '是', 22 | trueValue: '1', 23 | default: '0', 24 | } 25 | Object.defineProperty(menuHideDic, 'trueValue', { 26 | enumerable: false, 27 | }) 28 | Object.defineProperty(menuHideDic, 'default', { 29 | enumerable: false, 30 | }) 31 | 32 | // 菜单是缓存 33 | export const menuCacheDic: boolRadio & defaultType = { 34 | '0': '否', 35 | '1': '是', 36 | trueValue: '1', 37 | default: '0', 38 | } 39 | Object.defineProperty(menuCacheDic, 'trueValue', { 40 | enumerable: false, 41 | }) 42 | Object.defineProperty(menuCacheDic, 'default', { 43 | enumerable: false, 44 | }) 45 | 46 | // 菜单是否固定到标签栏 47 | export const menuAffixDic: boolRadio & defaultType = { 48 | '0': '否', 49 | '1': '是', 50 | trueValue: '1', 51 | default: '0', 52 | } 53 | 54 | Object.defineProperty(menuAffixDic, 'trueValue', { 55 | enumerable: false, 56 | }) 57 | Object.defineProperty(menuAffixDic, 'default', { 58 | enumerable: false, 59 | }) 60 | 61 | // 是否常显菜单栏 62 | export const menuAlwaysShowDic: boolRadio & defaultType = { 63 | '0': '否', 64 | '1': '是', 65 | trueValue: '1', 66 | default: '1', 67 | } 68 | Object.defineProperty(menuAlwaysShowDic, 'trueValue', { 69 | enumerable: false, 70 | }) 71 | Object.defineProperty(menuAlwaysShowDic, 'default', { 72 | enumerable: false, 73 | }) 74 | // 此处后期可研究一下原型的应用 75 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarMenu.vue: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 62 | 63 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 73 | -------------------------------------------------------------------------------- /src/stores/permission.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { publicRouters } from '@/router' 3 | import { getRolePermission } from '@/api/role' 4 | import type { RouteRecordRaw } from 'vue-router' 5 | import { arrToTree } from '@/utils/util' 6 | import Layout from '@/layout/index.vue' 7 | import { menuHideDic, menuCacheDic } from '@/dictionary/menu' 8 | 9 | // 给RouteRecordRaw添加_id属性 10 | 11 | //双星号是递归解释器遍历文件和文件夹的占位符或指令。它是一个简单的递归通配符,而只有一个星号表示全部没有递归 12 | const modules = import.meta.glob('../views/**/**.vue') 13 | export const usePermissionStore = defineStore('permission', { 14 | state: () => ({ 15 | routes: new Array(), 16 | permissions: new Array(), 17 | }), 18 | actions: { 19 | async getAccessRoutes() { 20 | let result = (await getRolePermission()).data 21 | let { menus, permissions } = result 22 | // 23 | 24 | menus.map((item: any) => { 25 | if (!item.parentId) { 26 | item.component = Layout 27 | } else { 28 | item.component = modules[`../views${item.component}.vue`] 29 | } 30 | item.meta = { 31 | title: item.title, 32 | icon: item.icon, 33 | sort: item.sort, 34 | cache: item.cache === menuCacheDic.trueValue, 35 | affix: item.menuType === '2' && item.affix === menuHideDic.trueValue, 36 | hidden: item.hidden === menuHideDic.trueValue, 37 | alwaysShow: 38 | item.menuType === '1' && item.alwaysShow === menuHideDic.trueValue, 39 | } 40 | }) 41 | // 递归处理后台返回的路由数据 42 | const routes: RouteRecordRaw[] = arrToTree({ 43 | list: menus, 44 | id: '_id', 45 | pid: 'parentId', 46 | children: 'children', 47 | }) 48 | 49 | this.routes = publicRouters.concat(routes) 50 | this.permissions = permissions 51 | return routes 52 | }, 53 | }, 54 | }) 55 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { useStaffStore } from '@/stores/staff' 3 | import { getToken } from '@/utils/auth' 4 | import { ElNotification } from 'element-plus' 5 | 6 | // create an axios instance 7 | const service = axios.create({ 8 | headers: { 9 | 'Content-Type': 'application/x-www-form-urlencoded', 10 | }, 11 | timeout: 20000, // request timeout 12 | }) 13 | 14 | service.interceptors.request.use( 15 | (config) => { 16 | const staffStore = useStaffStore() 17 | config.baseURL = import.meta.env['VITE_APP_BASE_PREFIX'] 18 | 19 | if (staffStore.token) { 20 | config.headers['x-authorization'] = 'Bearer ' + getToken() 21 | } 22 | return config 23 | }, 24 | (error) => { 25 | console.log(error) // for debug 26 | return Promise.reject(error) 27 | } 28 | ) 29 | 30 | service.interceptors.response.use( 31 | (response) => { 32 | const staffStore = useStaffStore() 33 | const res = response.data 34 | if (res.code === 401) { 35 | ElNotification.error({ 36 | title: '错误', 37 | message: res.msg, 38 | }) 39 | 40 | staffStore.logOut() 41 | return Promise.reject(res) 42 | } else if (res.code !== 200) { 43 | ElNotification.error({ 44 | title: '错误', 45 | message: res.msg || '网络错误,请稍后重试', 46 | }) 47 | return Promise.reject(res) 48 | } else { 49 | return res 50 | } 51 | }, 52 | (error) => { 53 | console.log(error) // for debug 54 | const errContent = error.response 55 | const staffStore = useStaffStore() 56 | if (errContent?.status === 401) { 57 | ElNotification.error({ 58 | title: '错误', 59 | message: errContent.data.msg, 60 | }) 61 | 62 | staffStore.logOut() 63 | return Promise.reject(error) 64 | } else { 65 | ElNotification.error({ 66 | title: '错误', 67 | message: errContent?.data?.msg || '网络错误,请稍后重试', 68 | }) 69 | return Promise.reject(error) 70 | } 71 | } 72 | ) 73 | 74 | export default service 75 | -------------------------------------------------------------------------------- /src/assets/style/normalize.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | div, 4 | span, 5 | applet, 6 | object, 7 | h1, 8 | h2, 9 | h3, 10 | h4, 11 | h5, 12 | h6, 13 | p, 14 | blockquote, 15 | pre, 16 | a, 17 | abbr, 18 | acronym, 19 | address, 20 | big, 21 | cite, 22 | code, 23 | del, 24 | dfn, 25 | em, 26 | img, 27 | ins, 28 | kbd, 29 | q, 30 | s, 31 | samp, 32 | small, 33 | strike, 34 | strong, 35 | sub, 36 | sup, 37 | tt, 38 | var, 39 | b, 40 | u, 41 | i, 42 | center, 43 | dl, 44 | dt, 45 | dd, 46 | ol, 47 | ul, 48 | li, 49 | fieldset, 50 | form, 51 | label, 52 | legend, 53 | table, 54 | caption, 55 | tbody, 56 | tfoot, 57 | thead, 58 | tr, 59 | th, 60 | td, 61 | article, 62 | aside, 63 | canvas, 64 | details, 65 | embed, 66 | figure, 67 | figcaption, 68 | footer, 69 | header, 70 | hgroup, 71 | menu, 72 | nav, 73 | output, 74 | ruby, 75 | section, 76 | summary, 77 | time, 78 | mark, 79 | audio, 80 | video { 81 | margin: 0; 82 | padding: 0; 83 | border: 0; 84 | font-size: 100%; 85 | font: inherit; 86 | font-weight: normal; 87 | vertical-align: baseline; 88 | } 89 | 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | 105 | ol, 106 | ul, 107 | li { 108 | list-style: none; 109 | } 110 | 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | 116 | blockquote:before, 117 | blockquote:after, 118 | q:before, 119 | q:after { 120 | content: ''; 121 | content: none; 122 | } 123 | 124 | table { 125 | border-collapse: collapse; 126 | border-spacing: 0; 127 | } 128 | 129 | th, 130 | td { 131 | vertical-align: middle; 132 | } 133 | 134 | /* custom */ 135 | a { 136 | outline: none; 137 | color: #16418a; 138 | text-decoration: none; 139 | -webkit-backface-visibility: hidden; 140 | } 141 | 142 | a:focus { 143 | outline: none; 144 | } 145 | 146 | input:focus, 147 | select:focus, 148 | textarea:focus { 149 | outline: -webkit-focus-ring-color auto 0; 150 | } -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | import '@vue/runtime-core' 7 | 8 | export {} 9 | 10 | declare module '@vue/runtime-core' { 11 | export interface GlobalComponents { 12 | ElButton: typeof import('element-plus/es')['ElButton'] 13 | ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] 14 | ElDrawer: typeof import('element-plus/es')['ElDrawer'] 15 | ElDropdown: typeof import('element-plus/es')['ElDropdown'] 16 | ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] 17 | ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] 18 | ElForm: typeof import('element-plus/es')['ElForm'] 19 | ElFormItem: typeof import('element-plus/es')['ElFormItem'] 20 | ElIcon: typeof import('element-plus/es')['ElIcon'] 21 | ElInput: typeof import('element-plus/es')['ElInput'] 22 | ElMenu: typeof import('element-plus/es')['ElMenu'] 23 | ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] 24 | ElOption: typeof import('element-plus/es')['ElOption'] 25 | ElPagination: typeof import('element-plus/es')['ElPagination'] 26 | ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm'] 27 | ElPopover: typeof import('element-plus/es')['ElPopover'] 28 | ElRadio: typeof import('element-plus/es')['ElRadio'] 29 | ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] 30 | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] 31 | ElSelect: typeof import('element-plus/es')['ElSelect'] 32 | ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] 33 | ElSwitch: typeof import('element-plus/es')['ElSwitch'] 34 | ElTable: typeof import('element-plus/es')['ElTable'] 35 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] 36 | ElTag: typeof import('element-plus/es')['ElTag'] 37 | ElTooltip: typeof import('element-plus/es')['ElTooltip'] 38 | ElTree: typeof import('element-plus/es')['ElTree'] 39 | ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] 40 | IEpClose: typeof import('~icons/ep/close')['default'] 41 | RouterLink: typeof import('vue-router')['RouterLink'] 42 | RouterView: typeof import('vue-router')['RouterView'] 43 | SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default'] 44 | } 45 | export interface ComponentCustomProperties { 46 | vLoading: typeof import('element-plus/es')['ElLoadingDirective'] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import AutoImport from 'unplugin-auto-import/vite' 4 | import Components from 'unplugin-vue-components/vite' 5 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 6 | import path from 'path' 7 | import Icons from 'unplugin-icons/vite' 8 | import IconsResolver from 'unplugin-icons/resolver' 9 | 10 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' 11 | 12 | // https://vitejs.dev/config/ 13 | export default defineConfig(({ mode }) => { 14 | const config = loadEnv(mode, './') 15 | return { 16 | base: './', 17 | plugins: [ 18 | vue(), 19 | AutoImport({ 20 | resolvers: [ 21 | ElementPlusResolver({ 22 | importStyle: 'sass', 23 | }), 24 | // 自动导入图标组件 25 | IconsResolver({ 26 | prefix: 'Icon', 27 | }), 28 | ], 29 | imports: ['vue', 'vue-router'], 30 | }), 31 | Components({ 32 | resolvers: [ 33 | ElementPlusResolver({ 34 | importStyle: 'sass', 35 | }), 36 | // 自动注册图标组件 37 | IconsResolver({ 38 | enabledCollections: ['ep'], 39 | }), 40 | ], 41 | }), 42 | Icons({ 43 | autoInstall: true, 44 | }), 45 | createSvgIconsPlugin({ 46 | // 指定需要缓存的图标文件夹 47 | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')], 48 | // 指定symbolId格式 49 | symbolId: 'icon-[dir]-[name]', 50 | svgoOptions: { 51 | // 移除svg默认颜色 52 | plugins: [ 53 | { 54 | name: 'removeAttrs', 55 | params: { attrs: ['class', 'data-name', 'fill', 'stroke'] }, 56 | }, 57 | ], 58 | }, 59 | }), 60 | ], 61 | resolve: { 62 | alias: { 63 | '@': path.resolve(__dirname, 'src'), 64 | }, 65 | }, 66 | css: { 67 | preprocessorOptions: { 68 | scss: { 69 | additionalData: ` 70 | @use "./src/assets/style/main.scss" as globalScss;@use "./src/assets/style/element/index.scss" as *; 71 | `, 72 | }, 73 | }, 74 | }, 75 | server: { 76 | proxy: { 77 | [config.VITE_APP_BASE_PREFIX]: { 78 | target: config.VITE_APP_BASE_API, 79 | changeOrigin: true, 80 | rewrite: (path) => 81 | path.replace(new RegExp(`^${config.VITE_APP_BASE_PREFIX}`), ''), 82 | }, 83 | }, 84 | }, 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /src/layout/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | 28 | {{ staffStore.staff?.staff_name }} 29 | 30 | 31 | 32 | 修改密码 35 | 退出登录 38 | 39 | 40 | 41 | 42 | 47 | 48 | 49 | 50 | 69 | 98 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-auto-import 5 | export {} 6 | declare global { 7 | const EffectScope: typeof import('vue')['EffectScope'] 8 | const ElNotification: typeof import('element-plus/es')['ElNotification'] 9 | const computed: typeof import('vue')['computed'] 10 | const createApp: typeof import('vue')['createApp'] 11 | const customRef: typeof import('vue')['customRef'] 12 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 13 | const defineComponent: typeof import('vue')['defineComponent'] 14 | const effectScope: typeof import('vue')['effectScope'] 15 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 16 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 17 | const h: typeof import('vue')['h'] 18 | const inject: typeof import('vue')['inject'] 19 | const isProxy: typeof import('vue')['isProxy'] 20 | const isReactive: typeof import('vue')['isReactive'] 21 | const isReadonly: typeof import('vue')['isReadonly'] 22 | const isRef: typeof import('vue')['isRef'] 23 | const markRaw: typeof import('vue')['markRaw'] 24 | const nextTick: typeof import('vue')['nextTick'] 25 | const onActivated: typeof import('vue')['onActivated'] 26 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 27 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 28 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 29 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 30 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 31 | const onDeactivated: typeof import('vue')['onDeactivated'] 32 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 33 | const onMounted: typeof import('vue')['onMounted'] 34 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 35 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 36 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 37 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 38 | const onUnmounted: typeof import('vue')['onUnmounted'] 39 | const onUpdated: typeof import('vue')['onUpdated'] 40 | const provide: typeof import('vue')['provide'] 41 | const reactive: typeof import('vue')['reactive'] 42 | const readonly: typeof import('vue')['readonly'] 43 | const ref: typeof import('vue')['ref'] 44 | const resolveComponent: typeof import('vue')['resolveComponent'] 45 | const shallowReactive: typeof import('vue')['shallowReactive'] 46 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 47 | const shallowRef: typeof import('vue')['shallowRef'] 48 | const toRaw: typeof import('vue')['toRaw'] 49 | const toRef: typeof import('vue')['toRef'] 50 | const toRefs: typeof import('vue')['toRefs'] 51 | const triggerRef: typeof import('vue')['triggerRef'] 52 | const unref: typeof import('vue')['unref'] 53 | const useAttrs: typeof import('vue')['useAttrs'] 54 | const useCssModule: typeof import('vue')['useCssModule'] 55 | const useCssVars: typeof import('vue')['useCssVars'] 56 | const useLink: typeof import('vue-router')['useLink'] 57 | const useRoute: typeof import('vue-router')['useRoute'] 58 | const useRouter: typeof import('vue-router')['useRouter'] 59 | const useSlots: typeof import('vue')['useSlots'] 60 | const watch: typeof import('vue')['watch'] 61 | const watchEffect: typeof import('vue')['watchEffect'] 62 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 63 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 64 | } 65 | // for type re-export 66 | declare global { 67 | // @ts-ignore 68 | export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' 69 | } 70 | -------------------------------------------------------------------------------- /src/views/sysManage/roleManage/roleModal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 14 | 15 | 16 | 24 | 25 | 26 | 27 | 取消 28 | 确定 34 | 35 | 36 | 37 | 38 | 144 | 145 | -------------------------------------------------------------------------------- /src/views/sysManage/roleManage/roleManage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 重置 16 | 查询 17 | 18 | 19 | 20 | 21 | 添加角色 24 | 25 | 26 | 33 | 34 | 35 | 36 | 编辑 44 | 49 | 50 | 删除 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 137 | 138 | -------------------------------------------------------------------------------- /src/layout/components/TagsView/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 16 | {{ item.meta.title }} 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 123 | 165 | -------------------------------------------------------------------------------- /src/views/sysManage/menuManage/menuManage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 添加菜单 9 | 10 | 17 | 18 | 19 | 20 | {{ menuTypeDic[row.menuType] }} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 37 | 38 | {{ menuHideDic[row.hidden] }} 39 | 40 | 41 | 42 | 43 | {{ menuCacheDic[row.cache] }} 44 | 45 | 46 | 52 | 53 | {{ menuAffixDic[row.affix] }} 54 | 55 | 56 | 62 | 63 | {{ menuAlwaysShowDic[row.alwaysShow] }} 64 | 65 | 66 | 67 | 68 | 69 | 添加 81 | 编辑 88 | 92 | 93 | 删除 96 | 97 | 98 | 99 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 157 | 163 | -------------------------------------------------------------------------------- /src/views/sysManage/staffManage/staffModal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 账号: 12 | 17 | 21 | 22 | 23 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 手机号: 38 | 43 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | {{ 67 | value 68 | }} 69 | 70 | 71 | 72 | 73 | 取消 74 | 确定 80 | 81 | 82 | 83 | 84 | 196 | 197 | -------------------------------------------------------------------------------- /src/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 登录 21 | 28 | 29 | 35 | 36 | 37 | 44 | 45 | 46 | 47 | 48 | 忘记密码? 51 | 52 | 登录 59 | 60 | 65 | 66 | 67 | 68 | 125 | 141 | 252 | -------------------------------------------------------------------------------- /src/views/login/resetPassword.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 重置密码 5 | 6 | 12 | 13 | 18 | 19 | 20 | 25 | 26 | {{ codeText }} 32 | 33 | 34 | 35 | 36 | 42 | 47 | 48 | 49 | 55 | 重置密码 56 | 57 | 58 | 59 | 取消 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 171 | 237 | -------------------------------------------------------------------------------- /src/views/sysManage/staffManage/staffManage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | id 14 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 重置 58 | 查询 59 | 60 | 61 | 62 | 添加员工 68 | 69 | 76 | 77 | 78 | 79 | 80 | 81 | {{ genderDic[row.gender] }} 82 | 83 | 84 | 85 | 86 | 87 | {{ getRoleName(row.roleId) }} 88 | 89 | 90 | 91 | 92 | {{ 93 | staffStatusDic[row.status].text 94 | }} 95 | 96 | 97 | 98 | 99 | 编辑 106 | 禁用 115 | 启用 123 | 124 | 125 | 126 | 135 | 136 | 141 | 142 | 143 | 144 | 235 | 241 | -------------------------------------------------------------------------------- /src/views/sysManage/menuManage/menuModal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | {{ value }} 17 | 18 | 19 | 23 | 24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 44 | 45 | 46 | 51 | 63 | 64 | 65 | 70 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | 90 | 97 | 98 | {{ item }} 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 111 | 112 | 113 | 114 | 115 | {{ value }} 121 | 122 | 123 | 124 | 125 | {{ value }} 131 | 132 | 133 | 134 | 135 | {{ value }} 141 | 142 | 143 | 144 | 145 | {{ value }} 151 | 152 | 153 | 154 | 155 | 取消 156 | 确定 162 | 163 | 164 | 165 | 166 | 305 | 335 | --------------------------------------------------------------------------------