From d5be70128c891f1a4304824c99aaf96faf348c13 Mon Sep 17 00:00:00 2001 From: monjack Date: Sun, 4 May 2025 23:07:09 +0800 Subject: [PATCH] new Files --- front/mock/menu.mock.ts | 2124 ++--------------- front/package-lock.json | 125 +- front/package.json | 8 +- front/public/code/AppendDoc.vue | 32 - front/public/code/BindData.vue | 96 - front/public/code/Calculate.vue | 19 - front/public/code/Command.vue | 47 - front/public/code/DataTable.vue | 60 - front/public/code/DocLang.vue | 27 - front/public/code/DocMode.vue | 20 - front/public/code/EChart.vue | 153 -- front/public/code/Editor.vue | 18 - front/public/code/Home.vue | 15 - front/public/code/SaveDoc.vue | 76 - front/public/code/Signature.vue | 44 - front/public/code/Simple.vue | 20 - front/public/code/VitalSigns.vue | 134 -- front/public/editor.html | 5 +- front/public/editor_dev.html | 54 - front/public/{vender => js}/editor.js | 0 front/src/components/AppLink/index.vue | 40 + front/src/components/Editor.vue | 18 - front/src/components/LoginView.vue | 1 + front/src/components/X-EMR/Editor.vue | 14 + front/src/directive/index.ts | 9 + front/src/directive/permission/index.ts | 64 + front/src/hook/websocket/core/useStomp.ts | 294 --- front/src/hook/websocket/index.ts | 11 - .../hook/websocket/services/useDictSync.ts | 189 -- .../hook/websocket/services/useOnlineCount.ts | 169 -- front/src/layout/components/AppMain/index.vue | 36 + .../components/SideBarMenuItemTitle.vue | 52 + .../SideBar/components/SidebarMenu.vue | 80 + .../SideBar/components/SidebarMenuItem.vue | 142 ++ front/src/layout/components/SideBar/index.vue | 168 +- front/src/layout/index.vue | 94 +- front/src/main.ts | 13 +- .../pages/zl-station/menjizhenItemView.vue | 4 +- front/src/plugins/index.ts | 5 - front/src/router/index.ts | 33 +- front/src/types/auto-imports.d.ts | 1780 -------------- front/src/types/components.d.ts | 100 - front/src/utils/i18n.ts | 12 - front/tsconfig.json | 16 +- front/vite.config.ts | 3 + 45 files changed, 924 insertions(+), 5500 deletions(-) delete mode 100644 front/public/code/AppendDoc.vue delete mode 100644 front/public/code/BindData.vue delete mode 100644 front/public/code/Calculate.vue delete mode 100644 front/public/code/Command.vue delete mode 100644 front/public/code/DataTable.vue delete mode 100644 front/public/code/DocLang.vue delete mode 100644 front/public/code/DocMode.vue delete mode 100644 front/public/code/EChart.vue delete mode 100644 front/public/code/Editor.vue delete mode 100644 front/public/code/Home.vue delete mode 100644 front/public/code/SaveDoc.vue delete mode 100644 front/public/code/Signature.vue delete mode 100644 front/public/code/Simple.vue delete mode 100644 front/public/code/VitalSigns.vue delete mode 100644 front/public/editor_dev.html rename front/public/{vender => js}/editor.js (100%) create mode 100644 front/src/components/AppLink/index.vue delete mode 100644 front/src/components/Editor.vue create mode 100644 front/src/components/X-EMR/Editor.vue create mode 100644 front/src/directive/index.ts create mode 100644 front/src/directive/permission/index.ts delete mode 100644 front/src/hook/websocket/core/useStomp.ts delete mode 100644 front/src/hook/websocket/index.ts delete mode 100644 front/src/hook/websocket/services/useDictSync.ts delete mode 100644 front/src/hook/websocket/services/useOnlineCount.ts create mode 100644 front/src/layout/components/AppMain/index.vue create mode 100644 front/src/layout/components/SideBar/components/SideBarMenuItemTitle.vue create mode 100644 front/src/layout/components/SideBar/components/SidebarMenu.vue create mode 100644 front/src/layout/components/SideBar/components/SidebarMenuItem.vue delete mode 100644 front/src/types/auto-imports.d.ts delete mode 100644 front/src/types/components.d.ts delete mode 100644 front/src/utils/i18n.ts diff --git a/front/mock/menu.mock.ts b/front/mock/menu.mock.ts index 02d72db..4600404 100644 --- a/front/mock/menu.mock.ts +++ b/front/mock/menu.mock.ts @@ -8,513 +8,77 @@ export default defineMock([ code: "00000", data: [ { - path: "/system", - component: "Layout", - redirect: "/system/user", - name: "/system", + path: "/Home", + component: "@/pages/HomePage.vue", + name: "Home", meta: { - title: "系统管理", - icon: "system", - hidden: false, - alwaysShow: false, - params: null, - }, - children: [ - { - path: "user", - component: "system/user/index", - name: "User", - meta: { - title: "用户管理", - icon: "el-icon-User", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "role", - component: "system/role/index", - name: "Role", - meta: { - title: "角色管理", - icon: "role", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "menu", - component: "system/menu/index", - name: "SysMenu", - meta: { - title: "菜单管理", - icon: "menu", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "dept", - component: "system/dept/index", - name: "Dept", - meta: { - title: "部门管理", - icon: "tree", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "dict", - component: "system/dict/index", - name: "Dict", - meta: { - title: "字典管理", - icon: "dict", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "log", - component: "system/log/index", - name: "Log", - meta: { - title: "系统日志", - icon: "document", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "dict-item", - component: "system/dict/dict-item", - name: "DictItem", - meta: { - title: "字典项", - icon: "", - hidden: true, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "config", - component: "system/config/index", - name: "Config", - meta: { - title: "系统配置", - icon: "setting", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "notice", - component: "system/notice/index", - name: "Notice", - meta: { - title: "通知公告", - icon: "", - hidden: false, - alwaysShow: false, - params: null, - }, - }, - ], - }, - { - path: "/codegen", - component: "Layout", - name: "/codegen", - meta: { - title: "系统工具", - icon: "menu", - hidden: false, - alwaysShow: false, - params: null, - }, - children: [ - { - path: "codegen", - component: "codegen/index", - name: "Codegen", - meta: { - title: "代码生成", - icon: "code", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - ], - }, - { - path: "/api", - component: "Layout", - name: "/api", - meta: { - title: "接口文档", - icon: "api", + title: "首页", + icon: "home-icon", hidden: false, alwaysShow: true, - params: null, + keepAlive: true, + params: null }, children: [ { - path: "apifox", - component: "demo/api/apifox", - name: "Apifox", + path: "/menjizhen-item", + component: "@/pages/zl-station/menjizhenItemView.vue", + name: "MenjizhenItem", meta: { - title: "Apifox", - icon: "api", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - ], - }, - { - path: "/doc", - component: "Layout", - redirect: "https://juejin.cn/post/7228990409909108793", - name: "/doc", - meta: { - title: "平台文档", - icon: "document", - hidden: false, - alwaysShow: false, - params: null, - }, - children: [ - { - path: "internal-doc", - component: "demo/internal-doc", - name: "InternalDoc", - meta: { - title: "document", - icon: "document", - hidden: false, - alwaysShow: false, - params: null, - }, - }, - { - path: "https://juejin.cn/post/7228990409909108793", - name: "Https://juejin.cn/post/7228990409909108793", - meta: { - title: "平台文档(外链)", - icon: "link", - hidden: false, - alwaysShow: false, - params: null, - }, - }, - ], - }, - { - path: "/multi-level", - component: "Layout", - name: "/multiLevel", - meta: { - title: "多级菜单", - icon: "cascader", - hidden: false, - alwaysShow: true, - params: null, - }, - children: [ - { - path: "multi-level1", - component: "demo/multi-level/level1", - name: "MultiLevel1", - meta: { - title: "菜单一级", - icon: "", + title: "门急诊医生站", + icon: "doctor-icon", hidden: false, alwaysShow: true, - params: null, - }, - children: [ - { - path: "multi-level2", - component: "demo/multi-level/children/level2", - name: "MultiLevel2", - meta: { - title: "菜单二级", - icon: "", - hidden: false, - alwaysShow: false, - params: null, - }, - children: [ - { - path: "multi-level3-1", - component: "demo/multi-level/children/children/level3-1", - name: "MultiLevel31", - meta: { - title: "菜单三级-1", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "multi-level3-2", - component: "demo/multi-level/children/children/level3-2", - name: "MultiLevel32", - meta: { - title: "菜单三级-2", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - ], - }, - ], + keepAlive: true, + params: null + } }, - ], + { + path: "/zhuyuan-item", + component: "@/pages/zl-station/zhuyuanItemView.vue", + name: "ZhuyuanItem", + meta: { + title: "住院医生站", + icon: "hospital-icon", + hidden: false, + alwaysShow: true, + keepAlive: true, + params: null + }, + } + ] }, { - path: "/component", - component: "Layout", - name: "/component", + path: "/login", + component: "@/pages/LoginPage.vue", + name: "LoginView", meta: { - title: "组件封装", - icon: "menu", - hidden: false, + title: "登录", + icon: "login-icon", + hidden: true, alwaysShow: false, - params: null, + keepAlive: false, + params: null }, - children: [ - { - path: "curd", - component: "demo/curd/index", - name: "Curd", - meta: { - title: "增删改查", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "table-select", - component: "demo/table-select/index", - name: "TableSelect", - meta: { - title: "列表选择器", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "wang-editor", - component: "demo/wang-editor", - name: "WangEditor", - meta: { - title: "富文本编辑器", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "upload", - component: "demo/upload", - name: "Upload", - meta: { - title: "图片上传", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "dict-demo", - component: "demo/dictionary", - name: "DictDemo", - meta: { - title: "字典组件", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "icon-selector", - component: "demo/icon-selector", - name: "IconSelector", - meta: { - title: "图标选择器", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "drag", - component: "demo/drag", - name: "Drag", - meta: { - title: "拖拽组件", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "text-scroll", - component: "demo/text-scroll", - name: "TextScroll", - meta: { - title: "滚动文本", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - ], + children: [] }, { - path: "/route-param", - component: "Layout", - name: "/routeParam", + path: "/:pathMatch(.*)*", + component: "@/pages/404/notFoundPage.vue", + name: "NotFound", meta: { - title: "路由参数", - icon: "el-icon-ElementPlus", - hidden: false, - alwaysShow: true, - params: null, - }, - children: [ - { - path: "route-param-type1", - component: "demo/route-param", - name: "RouteParamType1", - meta: { - title: "参数(type=1)", - icon: "el-icon-Star", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: { - type: "1", - }, - }, - }, - { - path: "route-param-type2", - component: "demo/route-param", - name: "RouteParamType2", - meta: { - title: "参数(type=2)", - icon: "el-icon-StarFilled", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: { - type: "2", - }, - }, - }, - ], - }, - { - path: "/function", - component: "Layout", - name: "/function", - meta: { - title: "功能演示", - icon: "menu", - hidden: false, + title: "404", + icon: "error-icon", + hidden: true, alwaysShow: false, - params: null, + keepAlive: false, + params: null }, - children: [ - { - path: "icon-demo", - component: "demo/icons", - name: "IconDemo", - meta: { - title: "Icons", - icon: "el-icon-Notification", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "/function/websocket", - component: "demo/websocket", - name: "/function/websocket", - meta: { - title: "Websocket", - icon: "", - hidden: false, - keepAlive: true, - alwaysShow: false, - params: null, - }, - }, - { - path: "other/:id", - component: "demo/other", - name: "Other/:id", - meta: { - title: "敬请期待...", - icon: "", - hidden: false, - alwaysShow: false, - params: null, - }, - }, - ], - }, + children: [] + } ], - msg: "一切ok", - }, + msg: "获取菜单路由成功" + } }, // 获取菜单树形表格列表 @@ -523,1060 +87,68 @@ export default defineMock([ method: ["GET"], body: { code: "00000", - data: [ + "data": [ { - id: 1, - parentId: 0, - name: "系统管理", - type: "CATALOG", - routeName: "", - routePath: "/system", - component: "Layout", - sort: 1, - visible: 1, - icon: "system", - redirect: "/system/user", - perm: null, - children: [ + "id": 1, + "parentId": 0, + "name": "首页", + "type": "CATALOG", + "routeName": "Home", + "routePath": "/Home", + "component": "@/pages/HomePage.vue", + "sort": 1, + "visible": 1, + "icon": "home-icon", + "redirect": null, + "perm": null, + "children": [ { - id: 2, - parentId: 1, - name: "用户管理", - type: "MENU", - routeName: "User", - routePath: "user", - component: "system/user/index", - sort: 1, - visible: 1, - icon: "el-icon-User", - redirect: null, - perm: null, - children: [ - { - id: 105, - parentId: 2, - name: "用户查询", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 0, - visible: 1, - icon: "", - redirect: null, - perm: "sys:user:query", - children: [], - }, - { - id: 31, - parentId: 2, - name: "用户新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: "", - perm: "sys:user:add", - children: [], - }, - { - id: 32, - parentId: 2, - name: "用户编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: "", - perm: "sys:user:edit", - children: [], - }, - { - id: 33, - parentId: 2, - name: "用户删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: "", - perm: "sys:user:delete", - children: [], - }, - { - id: 88, - parentId: 2, - name: "重置密码", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 4, - visible: 1, - icon: "", - redirect: null, - perm: "sys:user:password:reset", - children: [], - }, - { - id: 106, - parentId: 2, - name: "用户导入", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 5, - visible: 1, - icon: "", - redirect: null, - perm: "sys:user:import", - children: [], - }, - { - id: 107, - parentId: 2, - name: "用户导出", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 6, - visible: 1, - icon: "", - redirect: null, - perm: "sys:user:export", - children: [], - }, - ], + "id": 11, + "parentId": 1, + "name": "门急诊医生站", + "type": "MENU", + "routeName": "MenjizhenItem", + "routePath": "/menjizhen-item", + "component": "@/pages/zl-station/menjizhenItemView.vue", + "sort": 1, + "visible": 1, + "icon": "doctor-icon", + "redirect": null, + "perm": null, + "children": [] }, { - id: 3, - parentId: 1, - name: "角色管理", - type: "MENU", - routeName: "Role", - routePath: "role", - component: "system/role/index", - sort: 2, - visible: 1, - icon: "role", - redirect: null, - perm: null, - children: [ - { - id: 70, - parentId: 3, - name: "角色新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:role:add", - children: [], - }, - { - id: 71, - parentId: 3, - name: "角色编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: null, - perm: "sys:role:edit", - children: [], - }, - { - id: 72, - parentId: 3, - name: "角色删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:role:delete", - children: [], - }, - ], - }, - { - id: 4, - parentId: 1, - name: "菜单管理", - type: "MENU", - routeName: "Menu", - routePath: "menu", - component: "system/menu/index", - sort: 3, - visible: 1, - icon: "menu", - redirect: null, - perm: null, - children: [ - { - id: 73, - parentId: 4, - name: "菜单新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:menu:add", - children: [], - }, - { - id: 74, - parentId: 4, - name: "菜单编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:menu:edit", - children: [], - }, - { - id: 75, - parentId: 4, - name: "菜单删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:menu:delete", - children: [], - }, - ], - }, - { - id: 5, - parentId: 1, - name: "部门管理", - type: "MENU", - routeName: "Dept", - routePath: "dept", - component: "system/dept/index", - sort: 4, - visible: 1, - icon: "tree", - redirect: null, - perm: null, - children: [ - { - id: 76, - parentId: 5, - name: "部门新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dept:add", - children: [], - }, - { - id: 77, - parentId: 5, - name: "部门编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dept:edit", - children: [], - }, - { - id: 78, - parentId: 5, - name: "部门删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dept:delete", - children: [], - }, - ], - }, - { - id: 6, - parentId: 1, - name: "字典管理", - type: "MENU", - routeName: "Dict", - routePath: "dict", - component: "system/dict/index", - sort: 5, - visible: 1, - icon: "dict", - redirect: null, - perm: null, - children: [ - { - id: 79, - parentId: 6, - name: "字典新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict:add", - children: [], - }, - { - id: 81, - parentId: 6, - name: "字典编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict_type:edit", - children: [], - }, - { - id: 84, - parentId: 6, - name: "字典删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict_type:delete", - children: [], - }, - ], - }, - { - id: 135, - parentId: 1, - name: "字典项", - type: "MENU", - routeName: "DictData", - routePath: "dict-item", - component: "system/dict/dict-item", - sort: 6, - visible: 0, - icon: "", - redirect: null, - perm: null, - children: [ - { - id: 136, - parentId: 135, - name: "字典项新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 4, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict-item:add", - children: [], - }, - { - id: 137, - parentId: 135, - name: "字典项编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 5, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict-item:edit", - children: [], - }, - { - id: 138, - parentId: 135, - name: "字典项删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 6, - visible: 1, - icon: "", - redirect: null, - perm: "sys:dict-item:delete", - children: [], - }, - ], - }, - { - id: 117, - parentId: 1, - name: "系统日志", - type: "MENU", - routeName: "Log", - routePath: "log", - component: "system/log/index", - sort: 6, - visible: 1, - icon: "document", - redirect: null, - perm: null, - children: [], - }, - { - id: 120, - parentId: 1, - name: "系统配置", - type: "MENU", - routeName: "Config", - routePath: "config", - component: "system/config/index", - sort: 7, - visible: 1, - icon: "setting", - redirect: null, - perm: null, - children: [ - { - id: 121, - parentId: 120, - name: "查询系统配置", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:config:query", - children: [], - }, - { - id: 122, - parentId: 120, - name: "新增系统配置", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: null, - perm: "sys:config:add", - children: [], - }, - { - id: 123, - parentId: 120, - name: "修改系统配置", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:config:update", - children: [], - }, - { - id: 124, - parentId: 120, - name: "删除系统配置", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 4, - visible: 1, - icon: "", - redirect: null, - perm: "sys:config:delete", - children: [], - }, - { - id: 125, - parentId: 120, - name: "刷新系统配置", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 5, - visible: 1, - icon: "", - redirect: null, - perm: "sys:config:refresh", - children: [], - }, - ], - }, - { - id: 126, - parentId: 1, - name: "通知公告", - type: "MENU", - routeName: "Notice", - routePath: "notice", - component: "system/notice/index", - sort: 9, - visible: 1, - icon: "", - redirect: null, - perm: null, - children: [ - { - id: 127, - parentId: 126, - name: "查询", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:query", - children: [], - }, - { - id: 128, - parentId: 126, - name: "新增", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 2, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:add", - children: [], - }, - { - id: 129, - parentId: 126, - name: "编辑", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 3, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:edit", - children: [], - }, - { - id: 130, - parentId: 126, - name: "删除", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 4, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:delete", - children: [], - }, - { - id: 133, - parentId: 126, - name: "发布", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 5, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:publish", - children: [], - }, - { - id: 134, - parentId: 126, - name: "撤回", - type: "BUTTON", - routeName: null, - routePath: "", - component: null, - sort: 6, - visible: 1, - icon: "", - redirect: null, - perm: "sys:notice:revoke", - children: [], - }, - ], - }, - ], + "id": 12, + "parentId": 1, + "name": "住院医生站", + "type": "MENU", + "routeName": "ZhuyuanItem", + "routePath": "/zhuyuan-item", + "component": "@/pages/zl-station/zhuyuanItemView.vue", + "sort": 2, + "visible": 1, + "icon": "hospital-icon", + "redirect": null, + "perm": null, + "children": [] + } + ] }, { - id: 118, - parentId: 0, - name: "系统工具", - type: "CATALOG", - routeName: null, - routePath: "/codegen", - component: "Layout", - sort: 2, - visible: 1, - icon: "menu", - redirect: null, - perm: null, - children: [ - { - id: 119, - parentId: 118, - name: "代码生成", - type: "MENU", - routeName: "Codegen", - routePath: "codegen", - component: "codegen/index", - sort: 1, - visible: 1, - icon: "code", - redirect: null, - perm: null, - children: [], - }, - ], - }, - { - id: 40, - parentId: 0, - name: "接口文档", - type: "CATALOG", - routeName: null, - routePath: "/api", - component: "Layout", - sort: 7, - visible: 1, - icon: "api", - redirect: "", - perm: null, - children: [ - { - id: 41, - parentId: 40, - name: "Apifox", - type: "MENU", - routeName: null, - routePath: "apifox", - component: "demo/api/apifox", - sort: 1, - visible: 1, - icon: "api", - redirect: "", - perm: null, - children: [], - }, - ], - }, - { - id: 26, - parentId: 0, - name: "平台文档", - type: "CATALOG", - routeName: null, - routePath: "/doc", - component: "Layout", - sort: 8, - visible: 1, - icon: "document", - redirect: "https://juejin.cn/post/7228990409909108793", - perm: null, - children: [ - { - id: 102, - parentId: 26, - name: "平台文档(内嵌)", - type: "EXTLINK", - routeName: null, - routePath: "internal-doc", - component: "demo/internal-doc", - sort: 1, - visible: 1, - icon: "document", - redirect: "", - perm: null, - children: [], - }, - { - id: 30, - parentId: 26, - name: "平台文档(外链)", - type: "EXTLINK", - routeName: null, - routePath: "https://juejin.cn/post/7228990409909108793", - component: "", - sort: 2, - visible: 1, - icon: "link", - redirect: "", - perm: null, - children: [], - }, - ], - }, - { - id: 20, - parentId: 0, - name: "多级菜单", - type: "CATALOG", - routeName: null, - routePath: "/multi-level", - component: "Layout", - sort: 9, - visible: 1, - icon: "cascader", - redirect: "", - perm: null, - children: [ - { - id: 21, - parentId: 20, - name: "菜单一级", - type: "MENU", - routeName: null, - routePath: "multi-level1", - component: "demo/multi-level/level1", - sort: 1, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [ - { - id: 22, - parentId: 21, - name: "菜单二级", - type: "MENU", - routeName: null, - routePath: "multi-level2", - component: "demo/multi-level/children/level2", - sort: 1, - visible: 1, - icon: "", - redirect: null, - perm: null, - children: [ - { - id: 23, - parentId: 22, - name: "菜单三级-1", - type: "MENU", - routeName: null, - routePath: "multi-level3-1", - component: "demo/multi-level/children/children/level3-1", - sort: 1, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 24, - parentId: 22, - name: "菜单三级-2", - type: "MENU", - routeName: null, - routePath: "multi-level3-2", - component: "demo/multi-level/children/children/level3-2", - sort: 2, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - ], - }, - ], - }, - ], - }, - { - id: 36, - parentId: 0, - name: "组件封装", - type: "CATALOG", - routeName: null, - routePath: "/component", - component: "Layout", - sort: 10, - visible: 1, - icon: "menu", - redirect: "", - perm: null, - children: [ - { - id: 108, - parentId: 36, - name: "增删改查", - type: "MENU", - routeName: null, - routePath: "curd", - component: "demo/curd/index", - sort: 0, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 109, - parentId: 36, - name: "列表选择器", - type: "MENU", - routeName: null, - routePath: "table-select", - component: "demo/table-select/index", - sort: 1, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 37, - parentId: 36, - name: "富文本编辑器", - type: "MENU", - routeName: null, - routePath: "wang-editor", - component: "demo/wang-editor", - sort: 2, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 38, - parentId: 36, - name: "图片上传", - type: "MENU", - routeName: null, - routePath: "upload", - component: "demo/upload", - sort: 3, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 95, - parentId: 36, - name: "字典组件", - type: "MENU", - routeName: null, - routePath: "dict-demo", - component: "demo/dict", - sort: 4, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 39, - parentId: 36, - name: "图标选择器", - type: "MENU", - routeName: null, - routePath: "icon-selector", - component: "demo/icon-selector", - sort: 4, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - ], - }, - { - id: 110, - parentId: 0, - name: "路由参数", - type: "CATALOG", - routeName: null, - routePath: "/route-param", - component: "Layout", - sort: 11, - visible: 1, - icon: "el-icon-ElementPlus", - redirect: null, - perm: null, - children: [ - { - id: 111, - parentId: 110, - name: "参数(type=1)", - type: "MENU", - routeName: null, - routePath: "route-param-type1", - component: "demo/route-param", - sort: 1, - visible: 1, - icon: "el-icon-Star", - redirect: null, - perm: null, - children: [], - }, - { - id: 112, - parentId: 110, - name: "参数(type=2)", - type: "MENU", - routeName: null, - routePath: "route-param-type2", - component: "demo/route-param", - sort: 2, - visible: 1, - icon: "el-icon-StarFilled", - redirect: null, - perm: null, - children: [], - }, - ], - }, - { - id: 89, - parentId: 0, - name: "功能演示", - type: "CATALOG", - routeName: null, - routePath: "/function", - component: "Layout", - sort: 12, - visible: 1, - icon: "menu", - redirect: "", - perm: null, - children: [ - { - id: 97, - parentId: 89, - name: "Icons", - type: "MENU", - routeName: null, - routePath: "icon-demo", - component: "demo/icons", - sort: 2, - visible: 1, - icon: "el-icon-Notification", - redirect: "", - perm: null, - children: [], - }, - { - id: 90, - parentId: 89, - name: "Websocket", - type: "MENU", - routeName: null, - routePath: "/function/websocket", - component: "demo/websocket", - sort: 3, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - { - id: 91, - parentId: 89, - name: "敬请期待...", - type: "CATALOG", - routeName: null, - routePath: "other/:id", - component: "demo/other", - sort: 4, - visible: 1, - icon: "", - redirect: "", - perm: null, - children: [], - }, - ], - }, + "id": 2, + "parentId": 0, + "name": "登录", + "type": "MENU", + "routeName": "LoginView", + "routePath": "/login", + "component": "@/pages/LoginPage.vue", + "sort": 99, + "visible": 0, // 对应hidden: true + "icon": "login-icon", + "redirect": null, + "perm": null, + "children": [] + } ], msg: "一切ok", }, @@ -1588,343 +160,29 @@ export default defineMock([ method: ["GET"], body: { code: "00000", - data: [ - { - value: "1", - label: "系统管理", - children: [ - { - value: "2", - label: "用户管理", - children: [ - { - value: "105", - label: "用户查询", - }, - { - value: "31", - label: "用户新增", - }, - { - value: "32", - label: "用户编辑", - }, - { - value: "33", - label: "用户删除", - }, - { - value: "88", - label: "重置密码", - }, - { - value: "106", - label: "用户导入", - }, - { - value: "107", - label: "用户导出", - }, - ], - }, - { - value: "3", - label: "角色管理", - children: [ - { - value: "139", - label: "角色查询", - }, - { - value: "70", - label: "角色新增", - }, - { - value: "71", - label: "角色编辑", - }, - { - value: "72", - label: "角色删除", - }, - ], - }, - { - value: "4", - label: "菜单管理", - children: [ - { - value: "73", - label: "菜单新增", - }, - { - value: "140", - label: "菜单查询", - }, - { - value: "75", - label: "菜单删除", - }, - { - value: "74", - label: "菜单编辑", - }, - ], - }, - { - value: "5", - label: "部门管理", - children: [ - { - value: "76", - label: "部门新增", - }, - { - value: "141", - label: "部门查询", - }, - { - value: "77", - label: "部门编辑", - }, - { - value: "78", - label: "部门删除", - }, - ], - }, - { - value: "6", - label: "字典管理", - children: [ - { - value: "79", - label: "字典新增", - }, - { - value: "142", - label: "字典查询", - }, - { - value: "81", - label: "字典编辑", - }, - { - value: "84", - label: "字典删除", - }, - ], - }, - { - value: "117", - label: "系统日志", - }, - { - value: "135", - label: "字典项", - children: [ - { - value: "143", - label: "字典项查询", - }, - { - value: "136", - label: "字典项新增", - }, - { - value: "137", - label: "字典项编辑", - }, - { - value: "138", - label: "字典项删除", - }, - ], - }, - { - value: "120", - label: "系统配置", - children: [ - { - value: "121", - label: "系统配置查询", - }, - { - value: "122", - label: "系统配置新增", - }, - { - value: "123", - label: "系统配置修改", - }, - { - value: "124", - label: "系统配置删除", - }, - { - value: "125", - label: "系统配置刷新", - }, - ], - }, - { - value: "126", - label: "通知公告", - children: [ - { - value: "127", - label: "通知查询", - }, - { - value: "128", - label: "通知新增", - }, - { - value: "129", - label: "通知编辑", - }, - { - value: "130", - label: "通知删除", - }, - { - value: "133", - label: "通知发布", - }, - { - value: "134", - label: "通知撤回", - }, - ], - }, - ], - }, - { - value: "118", - label: "系统工具", - children: [ - { - value: "119", - label: "代码生成", - }, - ], - }, - { - value: "40", - label: "接口文档", - children: [ - { - value: "41", - label: "Apifox", - }, - ], - }, - { - value: "26", - label: "平台文档", - children: [ - { - value: "102", - label: "document", - }, - { - value: "30", - label: "平台文档(外链)", - }, - ], - }, - { - value: "20", - label: "多级菜单", - children: [ - { - value: "21", - label: "菜单一级", - children: [ - { - value: "22", - label: "菜单二级", - children: [ - { - value: "23", - label: "菜单三级-1", - }, - { - value: "24", - label: "菜单三级-2", - }, - ], - }, - ], - }, - ], - }, - { - value: "36", - label: "组件封装", - children: [ - { - value: "108", - label: "增删改查", - }, - { - value: "109", - label: "列表选择器", - }, - { - value: "37", - label: "富文本编辑器", - }, - { - value: "38", - label: "图片上传", - }, - { - value: "39", - label: "图标选择器", - }, - { - value: "95", - label: "字典组件", - }, - ], - }, - { - value: "110", - label: "路由参数", - children: [ - { - value: "111", - label: "参数(type=1)", - }, - { - value: "112", - label: "参数(type=2)", - }, - ], - }, - { - value: "89", - label: "功能演示", - children: [ - { - value: "97", - label: "Icons", - }, - { - value: "90", - label: "Websocket", - }, - { - value: "91", - label: "敬请期待...", - }, - ], - }, - ], - + "data": [ + { + "value": 1, + "label": "首页", + "children": [ + { + "value": 11, + "label": "门急诊医生站", + }, + { + "value": 12, + "label": "住院医生站", + } + ] + }, + { + "value": 2, + "label": "登录", + "children": [] + } + ], msg: "一切ok", - }, + } }, // 新增菜单 @@ -1981,102 +239,74 @@ export default defineMock([ ]); // 菜单映射表数据 + const menuMap: Record = { 1: { id: 1, parentId: 0, - name: "系统管理", + name: "首页", type: "CATALOG", - routeName: "", - routePath: "/system", - component: "Layout", + routeName: "Home", + routePath: "/Home", + component: "@/pages/HomePage.vue", perm: null, visible: 1, sort: 1, - icon: "system", - redirect: "/system/user", - keepAlive: null, - alwaysShow: null, - params: null, - }, - 2: { - id: 2, - parentId: 1, - name: "用户管理", - type: "MENU", - routeName: "User", - routePath: "user", - component: "system/user/index", - perm: null, - visible: 1, - sort: 1, - icon: "user", + icon: "home-icon", redirect: null, keepAlive: 1, - alwaysShow: null, + alwaysShow: 1, + params: null }, - 3: { - id: 3, + 11: { + id: 11, parentId: 1, - name: "角色管理", + name: "门急诊医生站", type: "MENU", - routeName: "Role", - routePath: "role", - component: "system/role/index", + routeName: "MenjizhenItem", + routePath: "/menjizhen-item", + component: "@/pages/zl-station/menjizhenItemView.vue", + perm: null, + visible: 1, + sort: 1, + icon: "doctor-icon", + redirect: null, + keepAlive: 1, + alwaysShow: 1, + params: null + }, + 12: { + id: 12, + parentId: 1, + name: "住院医生站", + type: "MENU", + routeName: "ZhuyuanItem", + routePath: "/zhuyuan-item", + component: "@/pages/zl-station/zhuyuanItemView.vue", perm: null, visible: 1, sort: 2, - icon: "role", + icon: "hospital-icon", redirect: null, keepAlive: 1, - alwaysShow: null, + alwaysShow: 1, + params: null }, - 4: { - id: 4, - parentId: 1, - name: "菜单管理", + 2: { + id: 2, + parentId: 0, + name: "登录", type: "MENU", - routeName: "Menu", - routePath: "menu", - component: "system/menu/index", + routeName: "LoginView", + routePath: "/login", + component: "@/pages/LoginPage.vue", perm: null, - visible: 1, - sort: 3, - icon: "menu", + visible: 0, + sort: 99, + icon: "login-icon", redirect: null, - keepAlive: 1, - alwaysShow: null, - }, - 5: { - id: 5, - parentId: 1, - name: "部门管理", - type: "MENU", - routeName: "Dept", - routePath: "dept", - component: "system/dept/index", - perm: null, - visible: 1, - sort: 4, - icon: "tree", - redirect: null, - keepAlive: 1, - alwaysShow: null, - }, - 6: { - id: 6, - parentId: 1, - name: "字典管理", - type: "MENU", - routeName: "Dict", - routePath: "dict", - component: "system/dict/index", - perm: null, - visible: 1, - sort: 5, - icon: "dict", - redirect: null, - keepAlive: 1, - alwaysShow: null, - }, -}; + keepAlive: 0, + alwaysShow: 0, + params: null + } +}; \ No newline at end of file diff --git a/front/package-lock.json b/front/package-lock.json index 032c6ee..78dec8c 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -9,12 +9,14 @@ "version": "0.0.0", "dependencies": { "@devui-design/icons": "^1.4.0", + "@vue/compiler-sfc": "^3.5.13", "axios": "^1.9.0", "bootstrap": "^5.3.5", "chart.js": "^4.4.9", "devui-theme": "^0.0.7", "jquery": "^3.7.1", "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", "pinia": "^3.0.2", "qs": "^6.14.0", "stompjs": "^2.3.3", @@ -26,11 +28,15 @@ "devDependencies": { "@popperjs/core": "^2.11.8", "@types/node": "^22.15.3", + "@types/nprogress": "^0.2.3", + "@types/path-browserify": "^1.0.3", "@types/qs": "^6.9.18", "@vitejs/plugin-vue": "^5.2.3", + "esbuild-plugin-vue": "^0.2.4", "sass-embedded": "^1.86.3", "unplugin-auto-import": "^19.1.2", - "vite": "^6.2.4", + "unplugin-vue-components": "^28.5.0", + "vite": "6.3.4", "vite-plugin-mock-dev-server": "^1.8.5", "vite-plugin-vue-devtools": "^7.7.2" }, @@ -1486,6 +1492,20 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/nprogress": { + "version": "0.2.3", + "resolved": "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.3.tgz", + "integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/path-browserify": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@types/path-browserify/-/path-browserify-1.0.3.tgz", + "integrity": "sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.18.tgz", @@ -1510,7 +1530,7 @@ }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", "integrity": "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==", "dev": true, "license": "MIT", @@ -1600,7 +1620,7 @@ }, "node_modules/@vue/compiler-sfc": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { @@ -3399,6 +3419,20 @@ "@esbuild/win32-x64": "0.25.2" } }, + "node_modules/esbuild-plugin-vue": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/esbuild-plugin-vue/-/esbuild-plugin-vue-0.2.4.tgz", + "integrity": "sha512-1noq1Tnnv4WAgdt5UROuNNIwQ7ppE5jlLB2MQD+XdzgprmOksGNTZlAEyYYF8oPMkq0qk1GB8KJJMTr2aE2Nzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.11 <=1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3527,9 +3561,9 @@ } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3821,6 +3855,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4659,6 +4700,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4908,6 +4955,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", @@ -5701,13 +5758,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.13", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { @@ -5950,6 +6007,42 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/unplugin-vue-components": { + "version": "28.5.0", + "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-28.5.0.tgz", + "integrity": "sha512-o7fMKU/uI8NiP+E0W62zoduuguWqB0obTfHFtbr1AP2uo2lhUPnPttWUE92yesdiYfo9/0hxIrj38FMc1eaySg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "debug": "^4.4.0", + "local-pkg": "^1.1.1", + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "tinyglobby": "^0.2.12", + "unplugin": "^2.3.2", + "unplugin-utils": "^0.2.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@babel/parser": "^7.15.8", + "@nuxt/kit": "^3.2.2", + "vue": "2 || 3" + }, + "peerDependenciesMeta": { + "@babel/parser": { + "optional": true + }, + "@nuxt/kit": { + "optional": true + } + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -6026,18 +6119,18 @@ } }, "node_modules/vite": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.0.tgz", - "integrity": "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==", + "version": "6.3.4", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.4.tgz", + "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", - "tinyglobby": "^0.2.12" + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -6250,7 +6343,7 @@ }, "node_modules/vue": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", "dependencies": { diff --git a/front/package.json b/front/package.json index 5b28051..22dd31b 100644 --- a/front/package.json +++ b/front/package.json @@ -10,12 +10,14 @@ }, "dependencies": { "@devui-design/icons": "^1.4.0", + "@vue/compiler-sfc": "^3.5.13", "axios": "^1.9.0", "bootstrap": "^5.3.5", "chart.js": "^4.4.9", "devui-theme": "^0.0.7", "jquery": "^3.7.1", "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", "pinia": "^3.0.2", "qs": "^6.14.0", "stompjs": "^2.3.3", @@ -27,11 +29,15 @@ "devDependencies": { "@popperjs/core": "^2.11.8", "@types/node": "^22.15.3", + "@types/nprogress": "^0.2.3", + "@types/path-browserify": "^1.0.3", "@types/qs": "^6.9.18", "@vitejs/plugin-vue": "^5.2.3", + "esbuild-plugin-vue": "^0.2.4", "sass-embedded": "^1.86.3", "unplugin-auto-import": "^19.1.2", - "vite": "^6.2.4", + "unplugin-vue-components": "^28.5.0", + "vite": "6.3.4", "vite-plugin-mock-dev-server": "^1.8.5", "vite-plugin-vue-devtools": "^7.7.2" }, diff --git a/front/public/code/AppendDoc.vue b/front/public/code/AppendDoc.vue deleted file mode 100644 index 33e4be4..0000000 --- a/front/public/code/AppendDoc.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - - \ No newline at end of file diff --git a/front/public/code/BindData.vue b/front/public/code/BindData.vue deleted file mode 100644 index a8ab79b..0000000 --- a/front/public/code/BindData.vue +++ /dev/null @@ -1,96 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/Calculate.vue b/front/public/code/Calculate.vue deleted file mode 100644 index 3569a6c..0000000 --- a/front/public/code/Calculate.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/Command.vue b/front/public/code/Command.vue deleted file mode 100644 index 6eba34b..0000000 --- a/front/public/code/Command.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/DataTable.vue b/front/public/code/DataTable.vue deleted file mode 100644 index d2704ef..0000000 --- a/front/public/code/DataTable.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - \ No newline at end of file diff --git a/front/public/code/DocLang.vue b/front/public/code/DocLang.vue deleted file mode 100644 index fccfb43..0000000 --- a/front/public/code/DocLang.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/DocMode.vue b/front/public/code/DocMode.vue deleted file mode 100644 index 2dab3de..0000000 --- a/front/public/code/DocMode.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/EChart.vue b/front/public/code/EChart.vue deleted file mode 100644 index 68f22ca..0000000 --- a/front/public/code/EChart.vue +++ /dev/null @@ -1,153 +0,0 @@ - - - - \ No newline at end of file diff --git a/front/public/code/Editor.vue b/front/public/code/Editor.vue deleted file mode 100644 index f15a5db..0000000 --- a/front/public/code/Editor.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/Home.vue b/front/public/code/Home.vue deleted file mode 100644 index c1c5380..0000000 --- a/front/public/code/Home.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/SaveDoc.vue b/front/public/code/SaveDoc.vue deleted file mode 100644 index a27ec52..0000000 --- a/front/public/code/SaveDoc.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - - \ No newline at end of file diff --git a/front/public/code/Signature.vue b/front/public/code/Signature.vue deleted file mode 100644 index 8b041ef..0000000 --- a/front/public/code/Signature.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - \ No newline at end of file diff --git a/front/public/code/Simple.vue b/front/public/code/Simple.vue deleted file mode 100644 index 68975c6..0000000 --- a/front/public/code/Simple.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/code/VitalSigns.vue b/front/public/code/VitalSigns.vue deleted file mode 100644 index 70cfc75..0000000 --- a/front/public/code/VitalSigns.vue +++ /dev/null @@ -1,134 +0,0 @@ - - - \ No newline at end of file diff --git a/front/public/editor.html b/front/public/editor.html index 01eb8c7..ace4e21 100644 --- a/front/public/editor.html +++ b/front/public/editor.html @@ -2,17 +2,16 @@ - - + - + - - - - - - - - - - - diff --git a/front/public/vender/editor.js b/front/public/js/editor.js similarity index 100% rename from front/public/vender/editor.js rename to front/public/js/editor.js diff --git a/front/src/components/AppLink/index.vue b/front/src/components/AppLink/index.vue new file mode 100644 index 0000000..0b0b914 --- /dev/null +++ b/front/src/components/AppLink/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/front/src/components/Editor.vue b/front/src/components/Editor.vue deleted file mode 100644 index 2b8290f..0000000 --- a/front/src/components/Editor.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - \ No newline at end of file diff --git a/front/src/components/LoginView.vue b/front/src/components/LoginView.vue index 5e0bf30..79a5f4c 100644 --- a/front/src/components/LoginView.vue +++ b/front/src/components/LoginView.vue @@ -74,6 +74,7 @@ \ No newline at end of file diff --git a/front/src/directive/index.ts b/front/src/directive/index.ts new file mode 100644 index 0000000..9c22eb6 --- /dev/null +++ b/front/src/directive/index.ts @@ -0,0 +1,9 @@ +import type { App } from "vue"; + +import { hasPerm } from "./permission"; + +// 全局注册 directive +export function setupDirective(app: App) { + // 使 v-hasPerm 在所有组件中都可用 + app.directive("hasPerm", hasPerm); +} diff --git a/front/src/directive/permission/index.ts b/front/src/directive/permission/index.ts new file mode 100644 index 0000000..2683d22 --- /dev/null +++ b/front/src/directive/permission/index.ts @@ -0,0 +1,64 @@ +import type { Directive, DirectiveBinding } from "vue"; + +import { useUserStore } from "@/store"; + +/** + * 按钮权限 + */ +export const hasPerm: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const requiredPerms = binding.value; + + // 校验传入的权限值是否合法 + if (!requiredPerms || (typeof requiredPerms !== "string" && !Array.isArray(requiredPerms))) { + throw new Error( + "需要提供权限标识!例如:v-has-perm=\"'sys:user:add'\" 或 v-has-perm=\"['sys:user:add', 'sys:user:edit']\"" + ); + } + + const { roles, perms } = useUserStore().userInfo; + + // 超级管理员拥有所有权限,如果是”*:*:*”权限标识,则不需要进行权限校验 + if (roles.includes("ROOT") || requiredPerms.includes("*:*:*")) { + return; + } + + // 检查权限 + const hasAuth = Array.isArray(requiredPerms) + ? requiredPerms.some((perm) => perms.includes(perm)) + : perms.includes(requiredPerms); + + // 如果没有权限,移除该元素 + if (!hasAuth && el.parentNode) { + el.parentNode.removeChild(el); + } + }, +}; + +/** + * 角色权限指令 + */ +export const hasRole: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const requiredRoles = binding.value; + + // 校验传入的角色值是否合法 + if (!requiredRoles || (typeof requiredRoles !== "string" && !Array.isArray(requiredRoles))) { + throw new Error( + "需要提供角色标识!例如:v-has-role=\"'ADMIN'\" 或 v-has-role=\"['ADMIN', 'TEST']\"" + ); + } + + const { roles } = useUserStore().userInfo; + + // 检查是否有对应角色权限 + const hasAuth = Array.isArray(requiredRoles) + ? requiredRoles.some((role) => roles.includes(role)) + : roles.includes(requiredRoles); + + // 如果没有权限,移除元素 + if (!hasAuth && el.parentNode) { + el.parentNode.removeChild(el); + } + }, +}; diff --git a/front/src/hook/websocket/core/useStomp.ts b/front/src/hook/websocket/core/useStomp.ts deleted file mode 100644 index 41d4a7a..0000000 --- a/front/src/hook/websocket/core/useStomp.ts +++ /dev/null @@ -1,294 +0,0 @@ - import { Client, IMessage, StompSubscription } from "@stomp/stompjs"; -import { getAccessToken } from "@/utils/auth"; -import { ref, watch } from "vue"; - -export interface UseStompOptions { - /** WebSocket 地址,不传时使用 VITE_APP_WS_ENDPOINT 环境变量 */ - brokerURL?: string; - /** 用于鉴权的 token,不传时使用 getAccessToken() 的返回值 */ - token?: string; - /** 重连延迟,单位毫秒,默认为 8000 */ - reconnectDelay?: number; - /** 连接超时时间,单位毫秒,默认为 10000 */ - connectionTimeout?: number; - /** 是否开启指数退避重连策略 */ - useExponentialBackoff?: boolean; - /** 最大重连次数,默认为 5 */ - maxReconnectAttempts?: number; - /** 最大重连延迟,单位毫秒,默认为 60000 */ - maxReconnectDelay?: number; - /** 是否开启调试日志 */ - debug?: boolean; -} - -/** - * STOMP WebSocket连接Hook - * 用于管理WebSocket连接的建立、断开、重连和消息订阅 - */ -export function useStomp(options: UseStompOptions = {}) { - // 默认值:brokerURL 从环境变量中获取,token 从 getAccessToken() 获取 - const defaultBrokerURL = import.meta.env.VITE_APP_WS_ENDPOINT || ""; - // 不再使用defaultToken,每次连接时直接获取最新token - - const brokerURL = ref(options.brokerURL ?? defaultBrokerURL); - // 不再存储token,改为在初始化时获取 - const reconnectDelay = options.reconnectDelay ?? 8000; - const connectionTimeout = options.connectionTimeout ?? 10000; - const useExponentialBackoff = options.useExponentialBackoff ?? false; - const maxReconnectAttempts = options.maxReconnectAttempts ?? 5; - const maxReconnectDelay = options.maxReconnectDelay ?? 60000; - - // 连接状态标记 - const isConnected = ref(false); - // 重连尝试次数 - const reconnectCount = ref(0); - // 重连计时器 - let reconnectTimer: any = null; - // 连接超时计时器 - let connectionTimeoutTimer: any = null; - // 存储所有订阅 - const subscriptions = new Map(); - - // 用于保存 STOMP 客户端的实例 - let client = ref(null); - - /** - * 初始化 STOMP 客户端 - */ - const initializeClient = () => { - if (client.value) { - return; - } - - // 每次连接前重新获取最新令牌,不依赖之前的token值 - const currentToken = getAccessToken(); - - // 检查令牌是否为空,如果为空则不进行连接 - if (!currentToken) { - console.error("WebSocket连接失败:授权令牌为空,请先登录"); - return; - } - - // 创建 STOMP 客户端 - client.value = new Client({ - brokerURL: brokerURL.value, - connectHeaders: { - Authorization: `Bearer ${currentToken}`, - }, - debug: options.debug ? console.log : () => {}, - reconnectDelay: useExponentialBackoff ? 0 : reconnectDelay, // 使用自定义退避策略时禁用内置重连 - heartbeatIncoming: 4000, - heartbeatOutgoing: 4000, - }); - - // 设置连接监听器 - client.value.onConnect = () => { - isConnected.value = true; - reconnectCount.value = 0; - clearTimeout(connectionTimeoutTimer); - console.log("WebSocket连接已建立"); - }; - - // 设置断开连接监听器 - client.value.onDisconnect = () => { - isConnected.value = false; - console.log("WebSocket连接已断开"); - - // 如果使用自定义指数退避重连策略,则在这里处理 - if (useExponentialBackoff && reconnectCount.value < maxReconnectAttempts) { - handleReconnect(); - } - }; - - // 设置 Web Socket 关闭监听器 - client.value.onWebSocketClose = (event) => { - isConnected.value = false; - console.log(`WebSocket已关闭: ${event?.code} ${event?.reason}`); - - // 如果是授权问题导致的关闭,尝试重新获取令牌 - if (event?.code === 1000 || event?.code === 1006 || event?.code === 1008) { - console.log("可能是授权问题导致连接关闭,尝试重新建立连接"); - - // 等待一段时间后再尝试重连,避免立即重连 - setTimeout(() => { - // 强制重新初始化客户端,获取最新令牌 - client.value = null; - - // 检查当前是否有有效令牌 - const freshToken = getAccessToken(); - if (freshToken) { - initializeClient(); - connect(); - } else { - console.warn("没有有效令牌,暂不重连WebSocket"); - } - }, 3000); - } - }; - - // 设置错误监听器 - client.value.onStompError = (frame) => { - console.error("STOMP错误:", frame.headers, frame.body); - - // 检查是否是授权错误 - if ( - frame.headers?.message?.includes("Unauthorized") || - frame.body?.includes("Unauthorized") || - frame.body?.includes("Token") - ) { - console.warn("WebSocket授权错误,请检查登录状态"); - } - }; - }; - - /** - * 处理重连逻辑 - */ - const handleReconnect = () => { - if (reconnectCount.value >= maxReconnectAttempts) { - console.error(`已达到最大重连次数(${maxReconnectAttempts}),停止重连`); - return; - } - - reconnectCount.value++; - console.log(`尝试重连(${reconnectCount.value}/${maxReconnectAttempts})...`); - - // 使用指数退避策略增加重连间隔 - const delay = useExponentialBackoff - ? Math.min(reconnectDelay * Math.pow(2, reconnectCount.value - 1), maxReconnectDelay) - : reconnectDelay; - - // 清除之前的计时器 - if (reconnectTimer) { - clearTimeout(reconnectTimer); - } - - // 设置重连计时器 - reconnectTimer = setTimeout(() => { - if (!isConnected.value && client.value) { - client.value.activate(); - } - }, delay); - }; - - // 监听 brokerURL 的变化,若地址改变则重新初始化 - watch(brokerURL, (newURL, oldURL) => { - if (newURL !== oldURL) { - console.log(`brokerURL changed from ${oldURL} to ${newURL}`); - // 断开当前连接,重新激活客户端 - if (client.value && client.value.connected) { - client.value.deactivate(); - } - brokerURL.value = newURL; - initializeClient(); // 重新初始化客户端 - } - }); - - // 初始化客户端 - initializeClient(); - - /** - * 激活连接(如果已经连接或正在激活则直接返回) - */ - const connect = () => { - if (!client.value) { - initializeClient(); - } - - if (client.value && (client.value.connected || client.value.active)) { - return; - } - - if (!client.value) { - console.error("STOMP客户端初始化失败"); - return; - } - - // 设置连接超时 - clearTimeout(connectionTimeoutTimer); - connectionTimeoutTimer = setTimeout(() => { - if (!isConnected.value) { - console.warn("WebSocket连接超时"); - if (useExponentialBackoff) { - handleReconnect(); - } - } - }, connectionTimeout); - - client.value.activate(); - }; - - /** - * 订阅指定主题 - * @param destination 目标主题地址 - * @param callback 接收到消息时的回调函数 - * @returns 返回订阅 id,用于后续取消订阅 - */ - const subscribe = (destination: string, callback: (message: IMessage) => void): string => { - if (!client.value) { - return ""; - } - - if (!client.value.connected) { - return ""; - } - - try { - const subscription = client.value.subscribe(destination, callback); - subscriptions.set(subscription.id, subscription); - console.log(`订阅成功: ${destination}, ID: ${subscription.id}`); - return subscription.id; - } catch (error) { - console.error(`订阅失败(${destination}):`, error); - return ""; - } - }; - - /** - * 取消指定订阅 - * @param subscriptionId 要取消的订阅 id - */ - const unsubscribe = (subscriptionId: string) => { - const subscription = subscriptions.get(subscriptionId); - if (subscription) { - subscription.unsubscribe(); - subscriptions.delete(subscriptionId); - } - }; - - /** - * 主动断开连接(如果未连接则不执行) - */ - const disconnect = () => { - if (client.value && !(client.value.connected || client.value.active)) { - console.log("Already disconnected, skipping disconnect() call."); - return; - } - - // 清除所有计时器 - if (reconnectTimer) { - clearTimeout(reconnectTimer); - reconnectTimer = null; - } - - if (connectionTimeoutTimer) { - clearTimeout(connectionTimeoutTimer); - connectionTimeoutTimer = null; - } - - client.value?.deactivate(); - isConnected.value = false; - reconnectCount.value = 0; - }; - - return { - client, - isConnected, - reconnectCount, - connect, - subscribe, - unsubscribe, - disconnect, - brokerURL, - }; -} diff --git a/front/src/hook/websocket/index.ts b/front/src/hook/websocket/index.ts deleted file mode 100644 index 3d44e74..0000000 --- a/front/src/hook/websocket/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * WebSocket相关Hook入口文件 - * 统一导出所有WebSocket相关Hook - */ - -// 核心基础Hook -export { useStomp } from "./core/useStomp"; - -// 业务服务Hook -export { useOnlineCount } from "./services/useOnlineCount"; -export { useDictSync } from "./services/useDictSync"; diff --git a/front/src/hook/websocket/services/useDictSync.ts b/front/src/hook/websocket/services/useDictSync.ts deleted file mode 100644 index d37fa8b..0000000 --- a/front/src/hook/websocket/services/useDictSync.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { useDictStoreHook } from "@/store/modules/dict.store"; -import { useStomp } from "../core/useStomp"; -import { IMessage } from "@stomp/stompjs"; -import { ref } from "vue"; - -// 字典消息类型 -export interface DictMessage { - dictCode: string; - timestamp: number; -} - -// 字典事件回调类型 -export type DictMessageCallback = (message: DictMessage) => void; - -// 全局单例实例 -let instance: ReturnType | null = null; - -/** - * 创建字典同步Hook - * 负责监听后端字典变更并同步到前端 - */ -function createDictSyncHook() { - const dictStore = useDictStoreHook(); - - // 使用现有的useStomp,配置适合字典场景的重连参数 - const { isConnected, connect, subscribe, unsubscribe, disconnect } = useStomp({ - reconnectDelay: 10000, // 使用更长的重连延迟 - 10秒 - connectionTimeout: 15000, // 更长的连接超时时间 - 15秒 - useExponentialBackoff: false, // 字典数据不需要指数退避策略 - }); - - // 存储订阅ID - const subscriptionIds = ref([]); - - // 已订阅的主题 - const subscribedTopics = ref>(new Set()); - - // 消息回调函数列表 - const messageCallbacks = ref([]); - - /** - * 注册字典消息回调 - * @param callback 回调函数 - */ - const onDictMessage = (callback: DictMessageCallback) => { - messageCallbacks.value.push(callback); - return () => { - // 返回取消注册的函数 - const index = messageCallbacks.value.indexOf(callback); - if (index !== -1) { - messageCallbacks.value.splice(index, 1); - } - }; - }; - - /** - * 初始化WebSocket - */ - const initWebSocket = async () => { - try { - // 连接WebSocket - connect(); - - // 设置字典订阅 - setupDictSubscription(); - } catch (error) { - console.error("[WebSocket] 初始化失败:", error); - } - }; - - /** - * 关闭WebSocket - */ - const closeWebSocket = () => { - // 取消所有订阅 - subscriptionIds.value.forEach((id) => { - unsubscribe(id); - }); - subscriptionIds.value = []; - subscribedTopics.value.clear(); - - // 断开连接 - disconnect(); - }; - - /** - * 设置字典订阅 - */ - const setupDictSubscription = () => { - const topic = "/topic/dict"; - - // 防止重复订阅 - if (subscribedTopics.value.has(topic)) { - console.log(`跳过重复订阅: ${topic}`); - return; - } - - console.log(`开始尝试订阅字典主题: ${topic}`); - - // 使用简化的重试逻辑,依赖useStomp的连接管理 - const attemptSubscribe = () => { - if (!isConnected.value) { - console.log("等待WebSocket连接建立..."); - // 3秒后再次尝试 - setTimeout(attemptSubscribe, 3000); - return; - } - - // 检查是否已订阅 - if (subscribedTopics.value.has(topic)) { - return; - } - - console.log(`连接已建立,开始订阅: ${topic}`); - - // 订阅字典更新 - const subId = subscribe(topic, (message: IMessage) => { - handleDictEvent(message); - }); - - if (subId) { - subscriptionIds.value.push(subId); - subscribedTopics.value.add(topic); - console.log(`字典主题订阅成功: ${topic}`); - } else { - console.warn(`字典主题订阅失败: ${topic}`); - } - }; - - // 开始尝试订阅 - attemptSubscribe(); - }; - - /** - * 处理字典事件 - * @param message STOMP消息 - */ - const handleDictEvent = (message: IMessage) => { - if (!message.body) return; - - try { - // 记录接收到的消息 - console.log(`收到字典更新消息: ${message.body}`); - - // 尝试解析消息 - const parsedData = JSON.parse(message.body) as DictMessage; - const dictCode = parsedData.dictCode; - - if (!dictCode) return; - - // 清除缓存,等待按需加载 - dictStore.removeDictItem(dictCode); - console.log(`字典缓存已清除: ${dictCode}`); - - // 调用所有注册的回调函数 - messageCallbacks.value.forEach((callback) => { - try { - callback(parsedData); - } catch (callbackError) { - console.error("[WebSocket] 回调执行失败:", callbackError); - } - }); - - // 显示提示消息 - console.info(`字典 ${dictCode} 已变更,将在下次使用时自动加载`); - } catch (error) { - console.error("[WebSocket] 解析消息失败:", error); - } - }; - - return { - isConnected, - initWebSocket, - closeWebSocket, - handleDictEvent, - onDictMessage, - }; -} - -/** - * 字典同步Hook - * 用于监听后端字典变更并同步到前端 - */ -export function useDictSync() { - if (!instance) { - instance = createDictSyncHook(); - } - return instance; -} diff --git a/front/src/hook/websocket/services/useOnlineCount.ts b/front/src/hook/websocket/services/useOnlineCount.ts deleted file mode 100644 index 20f8a74..0000000 --- a/front/src/hook/websocket/services/useOnlineCount.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { ref, onMounted, onUnmounted, watch } from "vue"; -import { useStomp } from "../core/useStomp"; -import { ElMessage } from "element-plus"; -import { getAccessToken } from "@/utils/auth"; - -/** - * 在线用户计数Hook - * 用于订阅后端推送的在线用户数量变化 - */ -export function useOnlineCount() { - // 在线用户数量 - const onlineUserCount = ref(0); - - // 最后更新时间戳 - const lastUpdateTime = ref(0); - - // 连接状态 - const isConnected = ref(false); - - // 连接正在尝试中 - const isConnecting = ref(false); - - // 使用Stomp客户端 - 配置使用指数退避策略 - const { - connect, - subscribe, - unsubscribe, - disconnect, - isConnected: stompConnected, - } = useStomp({ - reconnectDelay: 5000, // 初始重连延迟5秒 - maxReconnectAttempts: 3, // 最大重连3次 - connectionTimeout: 10000, // 连接超时10秒 - useExponentialBackoff: true, // 启用指数退避 - }); - - // 订阅ID - let subscriptionId = ""; - - // 连接超时计时器 - let connectionTimeoutTimer: any = null; - - // 监听Stomp连接状态 - watch(stompConnected, (connected) => { - if (connected && isConnecting.value) { - isConnected.value = true; - isConnecting.value = false; - - // 一旦连接成功,立即订阅主题 - subscribeToOnlineCount(); - console.log("WebSocket连接成功,已订阅在线用户计数主题"); - } - }); - - /** - * 订阅在线用户计数主题 - */ - const subscribeToOnlineCount = () => { - if (!stompConnected.value) return; - - // 如果已经订阅,先取消订阅 - if (subscriptionId) { - unsubscribe(subscriptionId); - } - - // 订阅在线用户计数主题 - subscriptionId = subscribe("/topic/online-count", (message) => { - try { - const data = message.body; - - const jsonData = JSON.parse(data); - const count = typeof jsonData === "number" ? jsonData : jsonData.count; - - if (!isNaN(count)) { - onlineUserCount.value = count; - lastUpdateTime.value = Date.now(); - } - } catch (error) { - console.error("解析在线用户数量失败:", error); - } - }); - }; - - /** - * 初始化WebSocket连接并订阅在线用户主题 - */ - const initWebSocket = () => { - if (isConnecting.value) return; - - // 检查是否有可用的令牌 - const hasToken = !!getAccessToken(); - if (!hasToken) { - console.log("没有检测到有效令牌,不尝试WebSocket连接"); - return; - } - - isConnecting.value = true; - - // 连接WebSocket - connect(); - - // 设置连接超时显示UI提示 - clearTimeout(connectionTimeoutTimer); - connectionTimeoutTimer = setTimeout(() => { - if (!isConnected.value) { - console.warn("WebSocket连接超时,将自动尝试重连"); - ElMessage.warning("正在尝试连接服务器,请稍候..."); - - // 超时后尝试重新连接 - closeWebSocket(); - setTimeout(() => { - // 再次检查令牌有效性 - if (getAccessToken()) { - initWebSocket(); - } else { - console.log("令牌无效,放弃重连"); - } - }, 3000); - } - }, 10000); // 较长的UI提示超时 - - // 监听连接状态变化,连接成功后清除超时计时器 - const unwatch = watch(stompConnected, (connected) => { - if (connected) { - clearTimeout(connectionTimeoutTimer); - unwatch(); - } - }); - }; - - /** - * 关闭WebSocket连接 - */ - const closeWebSocket = () => { - if (subscriptionId) { - unsubscribe(subscriptionId); - subscriptionId = ""; - } - - // 清除连接超时计时器 - if (connectionTimeoutTimer) { - clearTimeout(connectionTimeoutTimer); - connectionTimeoutTimer = null; - } - - disconnect(); - isConnected.value = false; - isConnecting.value = false; - }; - - // 组件挂载时初始化WebSocket - onMounted(() => { - initWebSocket(); - }); - - // 组件卸载时关闭WebSocket - onUnmounted(() => { - closeWebSocket(); - }); - - return { - onlineUserCount, - lastUpdateTime, - isConnected, - isConnecting, - initWebSocket, - closeWebSocket, - }; -} diff --git a/front/src/layout/components/AppMain/index.vue b/front/src/layout/components/AppMain/index.vue new file mode 100644 index 0000000..1f2937d --- /dev/null +++ b/front/src/layout/components/AppMain/index.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/front/src/layout/components/SideBar/components/SideBarMenuItemTitle.vue b/front/src/layout/components/SideBar/components/SideBarMenuItemTitle.vue new file mode 100644 index 0000000..a0883cd --- /dev/null +++ b/front/src/layout/components/SideBar/components/SideBarMenuItemTitle.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/front/src/layout/components/SideBar/components/SidebarMenu.vue b/front/src/layout/components/SideBar/components/SidebarMenu.vue new file mode 100644 index 0000000..165118b --- /dev/null +++ b/front/src/layout/components/SideBar/components/SidebarMenu.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/front/src/layout/components/SideBar/components/SidebarMenuItem.vue b/front/src/layout/components/SideBar/components/SidebarMenuItem.vue new file mode 100644 index 0000000..b87c4c4 --- /dev/null +++ b/front/src/layout/components/SideBar/components/SidebarMenuItem.vue @@ -0,0 +1,142 @@ + + + + + \ No newline at end of file diff --git a/front/src/layout/components/SideBar/index.vue b/front/src/layout/components/SideBar/index.vue index c3aee36..8fc24a7 100644 --- a/front/src/layout/components/SideBar/index.vue +++ b/front/src/layout/components/SideBar/index.vue @@ -1,10 +1,16 @@ -