From 9fb1942619f913322b7984dac901545c3dfdde96 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 12 Dec 2025 13:59:40 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84=20-=20enums/config/types/plugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 enums: 按业务域合并为 5 个文件 - 创建 config: storage.ts, vxe-table.ts - 删除 plugins,功能迁移到 main.ts - 创建完整 types 结构 - 新增 utils: validators, websocket, register-components - 创建 router/guards/permission.ts - 更新配置文件 --- eslint.config.ts | 1 + src/config/index.ts | 5 + src/config/storage.ts | 60 ++++++ src/config/vxe-table.ts | 65 +++++++ src/constants/index.ts | 181 ++---------------- src/enums/{api/code-enum.ts => api.ts} | 9 +- src/enums/business.ts | 47 +++++ .../{codegen/query-enum.ts => codegen.ts} | 23 +++ src/enums/codegen/form-enum.ts | 15 -- src/enums/common.ts | 46 +++++ src/enums/common/dialog-enum.ts | 12 -- src/enums/common/status-enum.ts | 22 --- src/enums/index.ts | 25 ++- src/enums/settings.ts | 123 ++++++++++++ src/enums/settings/device-enum.ts | 14 -- src/enums/settings/layout-enum.ts | 53 ----- src/enums/settings/locale-enum.ts | 14 -- src/enums/settings/theme-enum.ts | 32 ---- src/enums/system/menu-enum.ts | 6 - src/enums/system/user-enum.ts | 11 -- src/main.ts | 58 +++++- src/plugins/icons.ts | 9 - src/plugins/index.ts | 34 ---- src/plugins/vxeTable.ts | 70 ------- src/plugins/websocket.ts | 141 -------------- src/{plugins => router/guards}/permission.ts | 2 +- src/types/api/auth.ts | 24 +++ src/types/api/common.ts | 32 ++++ src/types/api/index.ts | 6 + src/types/env.d.ts | 35 ---- src/types/global.d.ts | 121 ++---------- src/types/index.ts | 6 + src/types/router.d.ts | 54 ------ src/types/shims-vue.d.ts | 5 - src/types/socket.d.ts | 6 - src/types/ui/index.ts | 6 + src/types/ui/settings.ts | 22 +++ src/types/ui/tagsview.ts | 14 ++ src/utils/register-components.ts | 18 ++ src/utils/validators.ts | 69 +++++++ src/utils/websocket.ts | 114 +++++++++++ tsconfig.json | 1 + types/env.d.ts | 29 +++ types/modules.d.ts | 14 ++ types/router.d.ts | 16 ++ 45 files changed, 836 insertions(+), 834 deletions(-) create mode 100644 src/config/index.ts create mode 100644 src/config/storage.ts create mode 100644 src/config/vxe-table.ts rename src/enums/{api/code-enum.ts => api.ts} (73%) create mode 100644 src/enums/business.ts rename src/enums/{codegen/query-enum.ts => codegen.ts} (51%) delete mode 100644 src/enums/codegen/form-enum.ts create mode 100644 src/enums/common.ts delete mode 100644 src/enums/common/dialog-enum.ts delete mode 100644 src/enums/common/status-enum.ts create mode 100644 src/enums/settings.ts delete mode 100644 src/enums/settings/device-enum.ts delete mode 100644 src/enums/settings/layout-enum.ts delete mode 100644 src/enums/settings/locale-enum.ts delete mode 100644 src/enums/settings/theme-enum.ts delete mode 100644 src/enums/system/menu-enum.ts delete mode 100644 src/enums/system/user-enum.ts delete mode 100644 src/plugins/icons.ts delete mode 100644 src/plugins/index.ts delete mode 100644 src/plugins/vxeTable.ts delete mode 100644 src/plugins/websocket.ts rename src/{plugins => router/guards}/permission.ts (98%) create mode 100644 src/types/api/auth.ts create mode 100644 src/types/api/common.ts create mode 100644 src/types/api/index.ts delete mode 100644 src/types/env.d.ts create mode 100644 src/types/index.ts delete mode 100644 src/types/router.d.ts delete mode 100644 src/types/shims-vue.d.ts delete mode 100644 src/types/socket.d.ts create mode 100644 src/types/ui/index.ts create mode 100644 src/types/ui/settings.ts create mode 100644 src/types/ui/tagsview.ts create mode 100644 src/utils/register-components.ts create mode 100644 src/utils/validators.ts create mode 100644 src/utils/websocket.ts create mode 100644 types/env.d.ts create mode 100644 types/modules.d.ts create mode 100644 types/router.d.ts diff --git a/eslint.config.ts b/eslint.config.ts index 20aade82..0a207584 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -64,6 +64,7 @@ export default [ "**/*.min.*", "**/auto-imports.d.ts", "**/components.d.ts", + "types/**/*.d.ts", ], }, diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 00000000..e13b8e84 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,5 @@ +/** + * 配置统一导出 + */ + +export * from "./storage"; diff --git a/src/config/storage.ts b/src/config/storage.ts new file mode 100644 index 00000000..d816cf10 --- /dev/null +++ b/src/config/storage.ts @@ -0,0 +1,60 @@ +/** + * 本地存储键名配置 + * + * @description + * 统一管理 localStorage/sessionStorage 的键名 + * 命名规范:{prefix}:{namespace}:{key} + */ + +export const APP_PREFIX = "vea"; + +/** + * 存储键名常量 + */ +export const STORAGE_KEYS = { + // ===== 认证相关 ===== + ACCESS_TOKEN: `${APP_PREFIX}:auth:access_token`, + REFRESH_TOKEN: `${APP_PREFIX}:auth:refresh_token`, + REMEMBER_ME: `${APP_PREFIX}:auth:remember_me`, + + // ===== 租户相关 ===== + TENANT_ID: `${APP_PREFIX}:tenant:id`, + TENANT_INFO: `${APP_PREFIX}:tenant:info`, + + // ===== 系统相关 ===== + DICT_CACHE: `${APP_PREFIX}:system:dict_cache`, + + // ===== UI 设置 ===== + SHOW_TAGS_VIEW: `${APP_PREFIX}:ui:show_tags_view`, + SHOW_APP_LOGO: `${APP_PREFIX}:ui:show_app_logo`, + SHOW_WATERMARK: `${APP_PREFIX}:ui:show_watermark`, + ENABLE_AI_ASSISTANT: `${APP_PREFIX}:ui:enable_ai_assistant`, + LAYOUT: `${APP_PREFIX}:ui:layout`, + SIDEBAR_COLOR_SCHEME: `${APP_PREFIX}:ui:sidebar_color_scheme`, + THEME: `${APP_PREFIX}:ui:theme`, + THEME_COLOR: `${APP_PREFIX}:ui:theme_color`, + + // ===== 应用状态 ===== + DEVICE: `${APP_PREFIX}:app:device`, + SIZE: `${APP_PREFIX}:app:size`, + LANGUAGE: `${APP_PREFIX}:app:language`, + SIDEBAR_STATUS: `${APP_PREFIX}:app:sidebar_status`, + ACTIVE_TOP_MENU_PATH: `${APP_PREFIX}:app:active_top_menu_path`, +} as const; + +/** + * 认证相关键名(便于批量操作) + */ +export const AUTH_KEYS = { + ACCESS_TOKEN: STORAGE_KEYS.ACCESS_TOKEN, + REFRESH_TOKEN: STORAGE_KEYS.REFRESH_TOKEN, + REMEMBER_ME: STORAGE_KEYS.REMEMBER_ME, +} as const; + +/** + * 租户相关键名(便于批量操作) + */ +export const TENANT_KEYS = { + TENANT_ID: STORAGE_KEYS.TENANT_ID, + TENANT_INFO: STORAGE_KEYS.TENANT_INFO, +} as const; diff --git a/src/config/vxe-table.ts b/src/config/vxe-table.ts new file mode 100644 index 00000000..042d9d67 --- /dev/null +++ b/src/config/vxe-table.ts @@ -0,0 +1,65 @@ +/** + * VxeTable 全局配置 + * + * @description + * VxeTable 是一个基于 Vue 的 PC 端表格组件 + * @see https://vxetable.cn/v4.6/#/table/start/install + */ + +import VXETable from "vxe-table"; + +/** + * 配置 VxeTable 全局参数 + */ +export function configureVxeTable() { + VXETable.setConfig({ + size: "medium", + zIndex: 9999, + version: 0, + loadingText: null, + table: { + showHeader: true, + showOverflow: "tooltip", + showHeaderOverflow: "tooltip", + autoResize: true, + border: "inner", + emptyText: "暂无数据", + rowConfig: { + isHover: true, + isCurrent: true, + keyField: "_VXE_ID", + }, + columnConfig: { + resizable: false, + }, + align: "center", + headerAlign: "center", + }, + pager: { + perfect: false, + pageSize: 10, + pagerCount: 7, + pageSizes: [10, 20, 50], + layouts: [ + "Total", + "PrevJump", + "PrevPage", + "Number", + "NextPage", + "NextJump", + "Sizes", + "FullJump", + ], + }, + modal: { + minWidth: 500, + minHeight: 400, + lockView: true, + mask: true, + dblclickZoom: false, + showTitleOverflow: true, + transfer: true, + draggable: false, + }, + }); +} diff --git a/src/constants/index.ts b/src/constants/index.ts index 34a64764..aa7fc222 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,172 +1,13 @@ -/** - * 项目常量统一管理 - * 存储键命名规范:{prefix}:{namespace}:{key} +/** + * 常量统一导出 + * + * @deprecated 此文件已废弃,请使用以下路径: + * - 存储键常量 @/config/storage + * - 验证规则 @/utils/validators + * - 角色常量 @/enums */ -export const APP_PREFIX = "vea"; - -export const STORAGE_KEYS = { - // 用户认证相关 - ACCESS_TOKEN: `${APP_PREFIX}:auth:access_token`, // JWT访问令牌 - REFRESH_TOKEN: `${APP_PREFIX}:auth:refresh_token`, // JWT刷新令牌 - REMEMBER_ME: `${APP_PREFIX}:auth:remember_me`, // 记住登录状态 - - // 租户相关 - TENANT_ID: `${APP_PREFIX}:tenant:id`, // 当前租户ID - TENANT_INFO: `${APP_PREFIX}:tenant:info`, // 当前租户信息 - - // 系统核心相关 - DICT_CACHE: `${APP_PREFIX}:system:dict_cache`, // 字典数据缓存 - - // UI设置相关 - SHOW_TAGS_VIEW: `${APP_PREFIX}:ui:show_tags_view`, // 显示标签页视图 - SHOW_APP_LOGO: `${APP_PREFIX}:ui:show_app_logo`, // 显示应用Logo - SHOW_WATERMARK: `${APP_PREFIX}:ui:show_watermark`, // 显示水印 - ENABLE_AI_ASSISTANT: `${APP_PREFIX}:ui:enable_ai_assistant`, // 启用 AI 助手 - LAYOUT: `${APP_PREFIX}:ui:layout`, // 布局模式 - SIDEBAR_COLOR_SCHEME: `${APP_PREFIX}:ui:sidebar_color_scheme`, // 侧边栏配色方案 - THEME: `${APP_PREFIX}:ui:theme`, // 主题模式 - THEME_COLOR: `${APP_PREFIX}:ui:theme_color`, // 主题色 - - // 应用状态相关 - DEVICE: `${APP_PREFIX}:app:device`, // 设备类型 - SIZE: `${APP_PREFIX}:app:size`, // 屏幕尺寸 - LANGUAGE: `${APP_PREFIX}:app:language`, // 应用语言 - SIDEBAR_STATUS: `${APP_PREFIX}:app:sidebar_status`, // 侧边栏状态 - ACTIVE_TOP_MENU_PATH: `${APP_PREFIX}:app:active_top_menu_path`, // 当前激活的顶部菜单路径 -} as const; - -export const ROLE_ROOT = "ROOT"; // 超级管理员角色 - -// 分组键集合(便于批量操作) -export const AUTH_KEYS = { - ACCESS_TOKEN: STORAGE_KEYS.ACCESS_TOKEN, - REFRESH_TOKEN: STORAGE_KEYS.REFRESH_TOKEN, - REMEMBER_ME: STORAGE_KEYS.REMEMBER_ME, -} as const; - -export const TENANT_KEYS = { - TENANT_ID: STORAGE_KEYS.TENANT_ID, - TENANT_INFO: STORAGE_KEYS.TENANT_INFO, -} as const; - -export const SYSTEM_KEYS = { - DICT_CACHE: STORAGE_KEYS.DICT_CACHE, -} as const; - -export const SETTINGS_KEYS = { - SHOW_TAGS_VIEW: STORAGE_KEYS.SHOW_TAGS_VIEW, - SHOW_APP_LOGO: STORAGE_KEYS.SHOW_APP_LOGO, - SHOW_WATERMARK: STORAGE_KEYS.SHOW_WATERMARK, - ENABLE_AI_ASSISTANT: STORAGE_KEYS.ENABLE_AI_ASSISTANT, - SIDEBAR_COLOR_SCHEME: STORAGE_KEYS.SIDEBAR_COLOR_SCHEME, - LAYOUT: STORAGE_KEYS.LAYOUT, - THEME_COLOR: STORAGE_KEYS.THEME_COLOR, - THEME: STORAGE_KEYS.THEME, -} as const; - -export const APP_KEYS = { - DEVICE: STORAGE_KEYS.DEVICE, - SIZE: STORAGE_KEYS.SIZE, - LANGUAGE: STORAGE_KEYS.LANGUAGE, - SIDEBAR_STATUS: STORAGE_KEYS.SIDEBAR_STATUS, - ACTIVE_TOP_MENU_PATH: STORAGE_KEYS.ACTIVE_TOP_MENU_PATH, -} as const; - -export const ALL_STORAGE_KEYS = { - ...AUTH_KEYS, - ...TENANT_KEYS, - ...SYSTEM_KEYS, - ...SETTINGS_KEYS, - ...APP_KEYS, -} as const; - -export type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS]; - -/** - * 表单验证规则常量 - * 提供常用的验证规则,减少重复代码 - * - * @example - * ```ts - * const rules = reactive({ - * username: [VALIDATORS.required("用户名不能为空")], - * email: [VALIDATORS.email], - * mobile: [VALIDATORS.mobile], - * }); - * ``` - */ -export const VALIDATORS = { - /** - * 必填验证 - * @param message 错误提示信息 - */ - required: (message: string) => ({ - required: true, - message, - trigger: "blur", - }), - - /** - * 邮箱格式验证 - */ - email: { - pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/, - message: "请输入正确的邮箱地址", - trigger: "blur", - }, - - /** - * 手机号码验证(中国大陆) - */ - mobile: { - pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, - message: "请输入正确的手机号码", - trigger: "blur", - }, - - /** - * 身份证号码验证(中国大陆) - */ - idCard: { - pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, - message: "请输入正确的身份证号码", - trigger: "blur", - }, - - /** - * URL 格式验证 - */ - url: { - pattern: /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i, - message: "请输入正确的URL地址", - trigger: "blur", - }, - - /** - * 数字验证 - */ - number: { - pattern: /^\d+$/, - message: "请输入数字", - trigger: "blur", - }, - - /** - * 整数验证(正整数、负整数、0) - */ - integer: { - pattern: /^-?\d+$/, - message: "请输入整数", - trigger: "blur", - }, - - /** - * 正整数验证 - */ - positiveInteger: { - pattern: /^[1-9]\d*$/, - message: "请输入正整数", - trigger: "blur", - }, -} as const; +// 向后兼容导出 +export * from "@/config/storage"; +export { ROLE_ROOT } from "@/enums"; +export { VALIDATORS } from "@/utils/validators"; diff --git a/src/enums/api/code-enum.ts b/src/enums/api.ts similarity index 73% rename from src/enums/api/code-enum.ts rename to src/enums/api.ts index fd06b2c3..a98e0c87 100644 --- a/src/enums/api/code-enum.ts +++ b/src/enums/api.ts @@ -1,5 +1,12 @@ /** - * API响应码枚举 + * API 相关枚举 + * + * @description + * 包含 API 响应码、请求状态等枚举定义 + */ + +/** + * API 响应码枚举 */ export const enum ApiCodeEnum { /** diff --git a/src/enums/business.ts b/src/enums/business.ts new file mode 100644 index 00000000..04a8447e --- /dev/null +++ b/src/enums/business.ts @@ -0,0 +1,47 @@ +/** + * 业务相关枚举 + * + * @description + * 包含菜单、用户、角色等业务实体的枚举定义 + */ + +/** + * 菜单类型枚举 + */ +export enum MenuTypeEnum { + CATALOG = "C", // 目录 + MENU = "M", // 菜单 + BUTTON = "B", // 按钮 +} + +/** + * 用户性别枚举 + */ +export enum UserGender { + /** 未知 */ + UNKNOWN = 0, + /** 男 */ + MALE = 1, + /** 女 */ + FEMALE = 2, +} + +/** + * 超级管理员角色标识 + * + * @description + * 拥有系统最高权限,可以访问所有资源 + */ +export const ROLE_ROOT = "ROOT"; + +/** + * 角色类型枚举 + */ +export enum RoleType { + /** 超级管理员 */ + ROOT = "ROOT", + /** 管理员 */ + ADMIN = "ADMIN", + /** 普通用户 */ + USER = "USER", +} diff --git a/src/enums/codegen/query-enum.ts b/src/enums/codegen.ts similarity index 51% rename from src/enums/codegen/query-enum.ts rename to src/enums/codegen.ts index 253efc0d..5d74234b 100644 --- a/src/enums/codegen/query-enum.ts +++ b/src/enums/codegen.ts @@ -1,3 +1,26 @@ +/** + * 代码生成相关枚举 + * + * @description + * 包含表单类型、查询类型等代码生成功能的枚举定义 + */ + +/** + * 表单类型枚举 + */ +export const FormTypeEnum: Record = { + INPUT: { value: 1, label: "输入框" }, + SELECT: { value: 2, label: "下拉框" }, + RADIO: { value: 3, label: "单选框" }, + CHECK_BOX: { value: 4, label: "复选框" }, + INPUT_NUMBER: { value: 5, label: "数字输入框" }, + SWITCH: { value: 6, label: "开关" }, + TEXT_AREA: { value: 7, label: "文本域" }, + DATE: { value: 8, label: "日期框" }, + DATE_TIME: { value: 9, label: "日期时间框" }, + HIDDEN: { value: 10, label: "隐藏域" }, +}; + /** * 查询类型枚举 */ diff --git a/src/enums/codegen/form-enum.ts b/src/enums/codegen/form-enum.ts deleted file mode 100644 index 96c54626..00000000 --- a/src/enums/codegen/form-enum.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 表单类型枚举 - */ -export const FormTypeEnum: Record = { - INPUT: { value: 1, label: "输入框" }, - SELECT: { value: 2, label: "下拉框" }, - RADIO: { value: 3, label: "单选框" }, - CHECK_BOX: { value: 4, label: "复选框" }, - INPUT_NUMBER: { value: 5, label: "数字输入框" }, - SWITCH: { value: 6, label: "开关" }, - TEXT_AREA: { value: 7, label: "文本域" }, - DATE: { value: 8, label: "日期框" }, - DATE_TIME: { value: 9, label: "日期时间框" }, - HIDDEN: { value: 10, label: "隐藏域" }, -}; diff --git a/src/enums/common.ts b/src/enums/common.ts new file mode 100644 index 00000000..e8819549 --- /dev/null +++ b/src/enums/common.ts @@ -0,0 +1,46 @@ +/** + * 通用枚举 + * + * @description + * 包含对话框模式、通用状态等跨业务的枚举定义 + */ + +/** + * 对话框模式枚举 + * + * @description + * 定义对话框的操作模式(创建、编辑、查看) + */ +export enum DialogMode { + /** 创建模式 - 新增数据 */ + CREATE = "create", + /** 编辑模式 - 修改数据 */ + EDIT = "edit", + /** 查看模式 - 只读展示 */ + VIEW = "view", +} + +/** + * 通用状态枚举 + * + * @description + * 适用于大多数业务实体的启用/禁用状态 + */ +export enum CommonStatus { + /** 禁用 */ + DISABLED = 0, + /** 启用 */ + ENABLED = 1, +} + +/** + * 审核状态枚举 + */ +export enum AuditStatus { + /** 待审核 */ + PENDING = 0, + /** 已通过 */ + APPROVED = 1, + /** 已拒绝 */ + REJECTED = 2, +} diff --git a/src/enums/common/dialog-enum.ts b/src/enums/common/dialog-enum.ts deleted file mode 100644 index 90c983cc..00000000 --- a/src/enums/common/dialog-enum.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 通用对话框模式枚举 - * @description 定义对话框的操作模式(创建、编辑、查看) - */ -export enum DialogMode { - /** 创建模式 - 新增数据 */ - CREATE = "create", - /** 编辑模式 - 修改数据 */ - EDIT = "edit", - /** 查看模式 - 只读展示 */ - VIEW = "view", -} diff --git a/src/enums/common/status-enum.ts b/src/enums/common/status-enum.ts deleted file mode 100644 index cce10afd..00000000 --- a/src/enums/common/status-enum.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 通用状态枚举 - * 适用于大多数业务实体的启用/禁用状态 - */ -export enum CommonStatus { - /** 禁用 */ - DISABLED = 0, - /** 启用 */ - ENABLED = 1, -} - -/** - * 审核状态枚举 - */ -export enum AuditStatus { - /** 待审核 */ - PENDING = 0, - /** 已通过 */ - APPROVED = 1, - /** 已拒绝 */ - REJECTED = 2, -} diff --git a/src/enums/index.ts b/src/enums/index.ts index c7f6fa99..bc41a3f0 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -1,15 +1,12 @@ -export * from "./api/code-enum"; +/** + * 枚举统一导出 + * + * @description + * 按业务域分组的枚举定义 + */ -export * from "./codegen/form-enum"; -export * from "./codegen/query-enum"; - -export * from "./settings/layout-enum"; -export * from "./settings/theme-enum"; -export * from "./settings/locale-enum"; -export * from "./settings/device-enum"; - -export * from "./common/dialog-enum"; -export * from "./common/status-enum"; - -export * from "./system/menu-enum"; -export * from "./system/user-enum"; +export * from "./api"; +export * from "./business"; +export * from "./codegen"; +export * from "./common"; +export * from "./settings"; diff --git a/src/enums/settings.ts b/src/enums/settings.ts new file mode 100644 index 00000000..7ba9b62b --- /dev/null +++ b/src/enums/settings.ts @@ -0,0 +1,123 @@ +/** + * 设置相关枚举 + * + * @description + * 包含主题、布局、语言、设备等应用设置的枚举定义 + */ + +/** + * 主题模式枚举 + */ +export const enum ThemeMode { + /** + * 明亮主题 + */ + LIGHT = "light", + /** + * 暗黑主题 + */ + DARK = "dark", + + /** + * 系统自动 + */ + AUTO = "auto", +} + +/** + * 侧边栏配色方案枚举 + */ +export const enum SidebarColor { + /** + * 经典蓝 + */ + CLASSIC_BLUE = "classic-blue", + /** + * 极简白 + */ + MINIMAL_WHITE = "minimal-white", +} + +/** + * 菜单布局枚举 + */ +export const enum LayoutMode { + /** + * 左侧菜单布局 + */ + LEFT = "left", + /** + * 顶部菜单布局 + */ + TOP = "top", + + /** + * 混合菜单布局 + */ + MIX = "mix", +} + +/** + * 侧边栏状态枚举 + */ +export const enum SidebarStatus { + /** + * 展开 + */ + OPENED = "opened", + + /** + * 关闭 + */ + CLOSED = "closed", +} + +/** + * 组件尺寸枚举 + */ +export const enum ComponentSize { + /** + * 默认 + */ + DEFAULT = "default", + + /** + * 大型 + */ + LARGE = "large", + + /** + * 小型 + */ + SMALL = "small", +} + +/** + * 语言枚举 + */ +export const enum LanguageEnum { + /** + * 中文 + */ + ZH_CN = "zh-cn", + + /** + * 英文 + */ + EN = "en", +} + +/** + * 设备枚举 + */ +export const enum DeviceEnum { + /** + * 宽屏设备 + */ + DESKTOP = "desktop", + + /** + * 窄屏设备 + */ + MOBILE = "mobile", +} diff --git a/src/enums/settings/device-enum.ts b/src/enums/settings/device-enum.ts deleted file mode 100644 index 709bcb3c..00000000 --- a/src/enums/settings/device-enum.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 设备枚举 - */ -export const enum DeviceEnum { - /** - * 宽屏设备 - */ - DESKTOP = "desktop", - - /** - * 窄屏设备 - */ - MOBILE = "mobile", -} diff --git a/src/enums/settings/layout-enum.ts b/src/enums/settings/layout-enum.ts deleted file mode 100644 index e1e4406b..00000000 --- a/src/enums/settings/layout-enum.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 菜单布局枚举 - */ -export const enum LayoutMode { - /** - * 左侧菜单布局 - */ - LEFT = "left", - /** - * 顶部菜单布局 - */ - TOP = "top", - - /** - * 混合菜单布局 - */ - MIX = "mix", -} - -/** - * 侧边栏状态枚举 - */ -export const enum SidebarStatus { - /** - * 展开 - */ - OPENED = "opened", - - /** - * 关闭 - */ - CLOSED = "closed", -} - -/** - * 组件尺寸枚举 - */ -export const enum ComponentSize { - /** - * 默认 - */ - DEFAULT = "default", - - /** - * 大型 - */ - LARGE = "large", - - /** - * 小型 - */ - SMALL = "small", -} diff --git a/src/enums/settings/locale-enum.ts b/src/enums/settings/locale-enum.ts deleted file mode 100644 index a50b6555..00000000 --- a/src/enums/settings/locale-enum.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 语言枚举 - */ -export const enum LanguageEnum { - /** - * 中文 - */ - ZH_CN = "zh-cn", - - /** - * 英文 - */ - EN = "en", -} diff --git a/src/enums/settings/theme-enum.ts b/src/enums/settings/theme-enum.ts deleted file mode 100644 index 21799c3e..00000000 --- a/src/enums/settings/theme-enum.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 主题枚举 - */ -export const enum ThemeMode { - /** - * 明亮主题 - */ - LIGHT = "light", - /** - * 暗黑主题 - */ - DARK = "dark", - - /** - * 系统自动 - */ - AUTO = "auto", -} - -/** - * 侧边栏配色方案枚举 - */ -export const enum SidebarColor { - /** - * 经典蓝 - */ - CLASSIC_BLUE = "classic-blue", - /** - * 极简白 - */ - MINIMAL_WHITE = "minimal-white", -} diff --git a/src/enums/system/menu-enum.ts b/src/enums/system/menu-enum.ts deleted file mode 100644 index 5e8352d1..00000000 --- a/src/enums/system/menu-enum.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 核心枚举定义 -export enum MenuTypeEnum { - CATALOG = "C", // 目录 - MENU = "M", // 菜单 - BUTTON = "B", // 按钮 -} diff --git a/src/enums/system/user-enum.ts b/src/enums/system/user-enum.ts deleted file mode 100644 index af4258c5..00000000 --- a/src/enums/system/user-enum.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 用户性别枚举 - */ -export enum UserGender { - /** 未知 */ - UNKNOWN = 0, - /** 男 */ - MALE = 1, - /** 女 */ - FEMALE = 2, -} diff --git a/src/main.ts b/src/main.ts index 253922b2..6f7db78c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,19 +1,63 @@ +/** + * 应用启动入口 + * + * @description + * Vue3 应用初始化,包括样式、插件、配置的加载 + */ + import { createApp } from "vue"; import App from "./App.vue"; -import setupPlugins from "@/plugins"; -// 暗黑主题样式 +// ===== 样式导入 ===== import "element-plus/theme-chalk/dark/css-vars.css"; import "vxe-table/lib/style.css"; -// 暗黑模式自定义变量 import "@/styles/dark/css-vars.css"; import "@/styles/index.scss"; import "uno.css"; - -// 过渡动画 import "animate.css"; +// ===== 核心配置 ===== +import { setupDirective } from "@/directives"; +import { setupI18n } from "@/lang"; +import { setupRouter } from "@/router"; +import { setupStore } from "@/store"; + +// ===== 全局组件 ===== +import { registerElementIcons } from "@/utils/register-components"; + +// ===== 第三方插件 ===== +import VXETable from "vxe-table"; +import { InstallCodeMirror } from "codemirror-editor-vue3"; +import { configureVxeTable } from "@/config/vxe-table"; + +// ===== 路由守卫 ===== +import { setupPermissionGuard } from "@/router/guards/permission"; + +// ===== 业务服务 ===== +import { setupWebSocket } from "@/utils/websocket"; + +// 创建 Vue 应用实例 const app = createApp(App); -// 注册插件 -app.use(setupPlugins); + +// 1️⃣ 核心配置 +setupDirective(app); +setupRouter(app); +setupStore(app); +setupI18n(app); + +// 2️⃣ 全局组件 +registerElementIcons(app); + +// 3️⃣ 第三方插件 +configureVxeTable(); +app.use(VXETable); +app.use(InstallCodeMirror); + +// 4️⃣ 路由守卫 +setupPermissionGuard(); + +// 5️⃣ WebSocket 初始化 +setupWebSocket(); + +// 6️⃣ 挂载应用 app.mount("#app"); diff --git a/src/plugins/icons.ts b/src/plugins/icons.ts deleted file mode 100644 index fa85ba14..00000000 --- a/src/plugins/icons.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { App } from "vue"; -import * as ElementPlusIconsVue from "@element-plus/icons-vue"; - -// 注册所有图标 -export function setupElIcons(app: App) { - for (const [key, component] of Object.entries(ElementPlusIconsVue)) { - app.component(key, component); - } -} diff --git a/src/plugins/index.ts b/src/plugins/index.ts deleted file mode 100644 index 96f32b85..00000000 --- a/src/plugins/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { App } from "vue"; - -import { setupDirective } from "@/directives"; -import { setupI18n } from "@/lang"; -import { setupRouter } from "@/router"; -import { setupStore } from "@/store"; -import { setupElIcons } from "./icons"; -import { setupPermission } from "./permission"; -import { setupWebSocket } from "./websocket"; -import { InstallCodeMirror } from "codemirror-editor-vue3"; -import { setupVxeTable } from "./vxeTable"; - -export default { - install(app: App) { - // 自定义指令(directive) - setupDirective(app); - // 路由(router) - setupRouter(app); - // 状态管理(store) - setupStore(app); - // 国际化 - setupI18n(app); - // Element-plus图标 - setupElIcons(app); - // 路由守卫 - setupPermission(); - // WebSocket服务 - setupWebSocket(); - // vxe-table - setupVxeTable(app); - // 注册 CodeMirror - app.use(InstallCodeMirror); - }, -}; diff --git a/src/plugins/vxeTable.ts b/src/plugins/vxeTable.ts deleted file mode 100644 index 5826fafb..00000000 --- a/src/plugins/vxeTable.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { App } from "vue"; -import VXETable from "vxe-table"; // https://vxetable.cn/v4.6/#/table/start/install - -// 全局默认参数 -VXETable.setConfig({ - // 全局尺寸 - size: "medium", - // 全局 zIndex 起始值,如果项目的的 z-index 样式值过大时就需要跟随设置更大,避免被遮挡 - zIndex: 9999, - // 版本号,对于某些带数据缓存的功能有用到,上升版本号可以用于重置数据 - version: 0, - // 全局 loading 提示内容,如果为 null 则不显示文本 - loadingText: null, - table: { - showHeader: true, - showOverflow: "tooltip", - showHeaderOverflow: "tooltip", - autoResize: true, - // stripe: false, - border: "inner", - // round: false, - emptyText: "暂无数据", - rowConfig: { - isHover: true, - isCurrent: true, - // 行数据的唯一主键字段名 - keyField: "_VXE_ID", - }, - columnConfig: { - resizable: false, - }, - align: "center", - headerAlign: "center", - }, - pager: { - // size: "medium", - // 配套的样式 - perfect: false, - pageSize: 10, - pagerCount: 7, - pageSizes: [10, 20, 50], - layouts: [ - "Total", - "PrevJump", - "PrevPage", - "Number", - "NextPage", - "NextJump", - "Sizes", - "FullJump", - ], - }, - modal: { - minWidth: 500, - minHeight: 400, - lockView: true, - mask: true, - // duration: 3000, - // marginSize: 20, - dblclickZoom: false, - showTitleOverflow: true, - transfer: true, - draggable: false, - }, -}); - -export function setupVxeTable(app: App) { - // Vxe Table 组件完整引入 - app.use(VXETable); -} diff --git a/src/plugins/websocket.ts b/src/plugins/websocket.ts deleted file mode 100644 index adb7b690..00000000 --- a/src/plugins/websocket.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { useDictSync } from "@/composables"; -import { AuthStorage } from "@/utils/auth"; - -/** - * WebSocket 服务实例约定接口 - * 至少包含 disconnect/closeWebSocket/cleanup 三者之一 - */ -type WebSocketService = { - disconnect?: () => void; - closeWebSocket?: () => void; - cleanup?: () => void; - [key: string]: any; -}; - -// 全局 WebSocket 实例管理 -const websocketInstances = new Map(); - -// 用于防止重复初始化的状态标记 -let isInitialized = false; -let dictWebSocketInstance: ReturnType | null = null; - -/** - * 注册 WebSocket 实例,便于统一清理 - */ -export function registerWebSocketInstance(key: string, instance: WebSocketService) { - websocketInstances.set(key, instance); -} - -/** - * 获取 WebSocket 实例 - */ -export function getWebSocketInstance(key: string) { - return websocketInstances.get(key); -} - -/** - * 初始化WebSocket服务 - */ -export function setupWebSocket() { - // 检查是否已经初始化 - if (isInitialized) { - return; - } - - // 检查环境变量是否配置 - const wsEndpoint = import.meta.env.VITE_APP_WS_ENDPOINT; - if (!wsEndpoint) { - console.log("[WebSocketPlugin] 未配置WebSocket端点,跳过WebSocket初始化"); - return; - } - - // 检查是否已登录(基于是否存在访问令牌) - if (!AuthStorage.getAccessToken()) { - console.warn( - "[WebSocketPlugin] 未找到访问令牌,WebSocket初始化已跳过。用户登录后将自动重新连接。" - ); - return; - } - - try { - // 延迟初始化,确保应用完全启动 - setTimeout(() => { - // 保存实例引用 - dictWebSocketInstance = useDictSync(); - registerWebSocketInstance("dictSync", dictWebSocketInstance); - - // 初始化字典WebSocket服务 - dictWebSocketInstance.initWebSocket(); - // 初始化在线用户计数WebSocket - import("@/composables").then(({ useOnlineCount }) => { - const onlineCountInstance = useOnlineCount({ autoInit: false }); - onlineCountInstance.initWebSocket(); - }); - - // 在窗口关闭前断开WebSocket连接 - window.addEventListener("beforeunload", handleWindowClose); - isInitialized = true; - }, 1000); // 延迟1秒初始化 - } catch (error) { - console.error("[WebSocketPlugin] 初始化WebSocket服务失败:", error); - } -} - -/** - * 处理窗口关闭 - */ -function handleWindowClose() { - cleanupWebSocket(); -} - -/** - * 清理WebSocket连接 - */ -export function cleanupWebSocket() { - // 清理字典 WebSocket - if (dictWebSocketInstance) { - try { - dictWebSocketInstance.closeWebSocket(); - } catch (error) { - console.error("[WebSocketPlugin] 断开字典WebSocket连接失败:", error); - } - } - - // 清理所有注册的 WebSocket 实例 - websocketInstances.forEach((instance, key) => { - try { - if (instance && typeof instance.disconnect === "function") { - instance.disconnect(); - } else if (instance && typeof instance.closeWebSocket === "function") { - instance.closeWebSocket(); - } else if (instance && typeof instance.cleanup === "function") { - instance.cleanup(); - } - } catch (error) { - console.error(`[WebSocketPlugin] 断开 ${key} WebSocket连接失败:`, error); - } - }); - - // 清空实例映射 - websocketInstances.clear(); - - // 移除事件监听器 - window.removeEventListener("beforeunload", handleWindowClose); - - // 重置状态 - dictWebSocketInstance = null; - isInitialized = false; -} - -/** - * 重新初始化WebSocket(用于登录后重连) - */ -export function reinitializeWebSocket() { - // 先清理现有连接 - cleanupWebSocket(); - - // 延迟后重新初始化 - setTimeout(() => { - setupWebSocket(); - }, 500); -} diff --git a/src/plugins/permission.ts b/src/router/guards/permission.ts similarity index 98% rename from src/plugins/permission.ts rename to src/router/guards/permission.ts index 43235e8b..f0c18b65 100644 --- a/src/plugins/permission.ts +++ b/src/router/guards/permission.ts @@ -33,7 +33,7 @@ async function initTenantContextIfEnabled(): Promise { } } -export function setupPermission() { +export function setupPermissionGuard() { const whiteList = ["/login"]; router.beforeEach(async (to, from, next) => { diff --git a/src/types/api/auth.ts b/src/types/api/auth.ts new file mode 100644 index 00000000..00777d82 --- /dev/null +++ b/src/types/api/auth.ts @@ -0,0 +1,24 @@ +/** + * 认证相关类型定义 + */ + +export interface LoginRequest { + username: string; + password: string; + captchaId?: string; + captchaCode?: string; + rememberMe?: boolean; + tenantId?: number; +} + +export interface LoginResult { + accessToken: string; + refreshToken: string; + tokenType: string; + expiresIn: number; +} + +export interface CaptchaInfo { + captchaId: string; + captchaBase64: string; +} diff --git a/src/types/api/common.ts b/src/types/api/common.ts new file mode 100644 index 00000000..d0bc7ad4 --- /dev/null +++ b/src/types/api/common.ts @@ -0,0 +1,32 @@ +/** + * 通用 API 类型定义 + */ + +export interface ApiResponse { + code: string; + data: T; + msg: string; +} + +export interface PageQuery { + pageNum: number; + pageSize: number; +} + +export interface PageResult { + list: T; + total: number; +} + +export interface OptionType { + value: string | number; + label: string; + children?: OptionType[]; +} + +export interface ExcelResult { + code: string; + invalidCount: number; + validCount: number; + messageList: string[]; +} diff --git a/src/types/api/index.ts b/src/types/api/index.ts new file mode 100644 index 00000000..7f04ac8d --- /dev/null +++ b/src/types/api/index.ts @@ -0,0 +1,6 @@ +/** + * API 类型统一导出 + */ + +export * from "./common"; +export * from "./auth"; diff --git a/src/types/env.d.ts b/src/types/env.d.ts deleted file mode 100644 index 1342f61e..00000000 --- a/src/types/env.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -// https://cn.vitejs.dev/guide/env-and-mode - -// TypeScript 类型提示都为 string: https://github.com/vitejs/vite/issues/6930 -interface ImportMetaEnv { - /** 应用端口 */ - VITE_APP_PORT: number; - /** 应用名称 */ - VITE_APP_NAME: string; - /** API 基础路径(代理前缀) */ - VITE_APP_BASE_API: string; - /** API 地址 */ - VITE_APP_API_URL: string; - /** 是否开启 Mock 服务 */ - VITE_MOCK_DEV_SERVER: boolean; -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} - -/** - * 平台的名称、版本、运行所需的`node`版本、依赖、构建时间的类型提示 - */ -declare const __APP_INFO__: { - pkg: { - name: string; - version: string; - engines: { - node: string; - }; - dependencies: Record; - devDependencies: Record; - }; - buildTimestamp: number; -}; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 1806e218..95fb2d5d 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,111 +1,16 @@ +/** + * 全局类型声明 + * + * @deprecated 请使用 @/types 下的具名导出 + */ declare global { - /** - * 响应数据 - */ - interface ApiResponse { - code: string; - data: T; - msg: string; - } - - /** - * 分页查询参数 - */ - interface PageQuery { - pageNum: number; - pageSize: number; - } - - /** - * 分页响应对象 - */ - interface PageResult { - /** 数据列表 */ - list: T; - /** 总数 */ - total: number; - } - - /** - * 页签对象 - */ - interface TagView { - /** 页签名称 */ - name: string; - /** 页签标题 */ - title: string; - /** 页签路由路径 */ - path: string; - /** 页签路由完整路径 */ - fullPath: string; - /** 页签图标 */ - icon?: string; - /** 是否固定页签 */ - affix?: boolean; - /** 是否开启缓存 */ - keepAlive?: boolean; - /** 路由查询参数 */ - query?: any; - } - - /** - * 系统设置 - */ - interface AppSettings { - /** 系统标题 */ - title: string; - /** 系统版本 */ - version: string; - /** 是否显示设置 */ - showSettings: boolean; - /** 是否显示多标签导航 */ - showTagsView: boolean; - /** 是否显示应用Logo */ - showAppLogo: boolean; - /** 导航栏布局(left|top|mix) */ - layout: "left" | "top" | "mix"; - /** 主题颜色 */ - themeColor: string; - /** 主题模式(dark|light) */ - theme: import("@/enums/settings/theme-enum").ThemeMode; - /** 布局大小(default |large |small) */ - size: string; - /** 语言( zh-cn| en) */ - language: string; - /** 是否显示水印 */ - showWatermark: boolean; - /** 水印内容 */ - watermarkContent: string; - /** 侧边栏配色方案 */ - sidebarColorScheme: "classic-blue" | "minimal-white"; - /** 是否启用 AI 助手 */ - enableAiAssistant: boolean; - } - - /** - * 下拉选项数据类型 - */ - interface OptionType { - /** 值 */ - value: string | number; - /** 文本 */ - label: string; - /** 子列表 */ - children?: OptionType[]; - } - - /** - * 导入结果 - */ - interface ExcelResult { - /** 状态码 */ - code: string; - /** 无效数据条数 */ - invalidCount: number; - /** 有效数据条数 */ - validCount: number; - /** 错误信息 */ - messageList: Array; - } + type ApiResponse = import("@/types/api").ApiResponse; + type PageQuery = import("@/types/api").PageQuery; + type PageResult = import("@/types/api").PageResult; + type OptionType = import("@/types/api").OptionType; + type ExcelResult = import("@/types/api").ExcelResult; + type TagView = import("@/types/ui").TagView; + type AppSettings = import("@/types/ui").AppSettings; } + export {}; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..49586b42 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,6 @@ +/** + * 类型统一导出 + */ + +export * from "./api"; +export * from "./ui"; diff --git a/src/types/router.d.ts b/src/types/router.d.ts deleted file mode 100644 index 17a3cb46..00000000 --- a/src/types/router.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -import "vue-router"; - -declare module "vue-router" { - // https://router.vuejs.org/zh/guide/advanced/meta.html#typescript - // 可以通过扩展 RouteMeta 接口来输入 meta 字段 - interface RouteMeta { - /** - * 菜单名称 - * @example 'Dashboard' - */ - title?: string; - - /** - * 菜单图标 - * @example 'el-icon-edit' - */ - icon?: string; - - /** - * 是否隐藏菜单 - * true 隐藏, false 显示 - * @default false - */ - hidden?: boolean; - - /** - * 始终显示父级菜单,即使只有一个子菜单 - * true 显示父级菜单, false 隐藏父级菜单,显示唯一子节点 - * @default false - */ - alwaysShow?: boolean; - - /** - * 是否固定在页签上 - * true 固定, false 不固定 - * @default false - */ - affix?: boolean; - - /** - * 是否缓存页面 - * true 缓存, false 不缓存 - * @default false - */ - keepAlive?: boolean; - - /** - * 是否在面包屑导航中隐藏 - * true 隐藏, false 显示 - * @default false - */ - breadcrumb?: boolean; - } -} diff --git a/src/types/shims-vue.d.ts b/src/types/shims-vue.d.ts deleted file mode 100644 index d77b62be..00000000 --- a/src/types/shims-vue.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module "*.vue" { - import type { DefineComponent } from "vue"; - const component: DefineComponent<{}, {}, any>; - export default component; -} diff --git a/src/types/socket.d.ts b/src/types/socket.d.ts deleted file mode 100644 index 15a8ed78..00000000 --- a/src/types/socket.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -// https://github.com/sockjs/sockjs-client/issues/565 - -declare module "sockjs-client/dist/sockjs.min.js" { - import Client from "sockjs-client"; - export default Client; -} diff --git a/src/types/ui/index.ts b/src/types/ui/index.ts new file mode 100644 index 00000000..7460b7b1 --- /dev/null +++ b/src/types/ui/index.ts @@ -0,0 +1,6 @@ +/** + * UI 类型统一导出 + */ + +export * from "./tagsview"; +export * from "./settings"; diff --git a/src/types/ui/settings.ts b/src/types/ui/settings.ts new file mode 100644 index 00000000..7020e8b2 --- /dev/null +++ b/src/types/ui/settings.ts @@ -0,0 +1,22 @@ +/** + * 应用设置相关类型定义 + */ + +import type { ThemeMode } from "@/enums"; + +export interface AppSettings { + title: string; + version: string; + showSettings: boolean; + showTagsView: boolean; + showAppLogo: boolean; + layout: "left" | "top" | "mix"; + themeColor: string; + theme: ThemeMode; + size: string; + language: string; + showWatermark: boolean; + watermarkContent: string; + sidebarColorScheme: "classic-blue" | "minimal-white"; + enableAiAssistant: boolean; +} diff --git a/src/types/ui/tagsview.ts b/src/types/ui/tagsview.ts new file mode 100644 index 00000000..5cc68ce8 --- /dev/null +++ b/src/types/ui/tagsview.ts @@ -0,0 +1,14 @@ +/** + * 标签页相关类型定义 + */ + +export interface TagView { + name: string; + title: string; + path: string; + fullPath: string; + icon?: string; + affix?: boolean; + keepAlive?: boolean; + query?: any; +} diff --git a/src/utils/register-components.ts b/src/utils/register-components.ts new file mode 100644 index 00000000..e1142ed2 --- /dev/null +++ b/src/utils/register-components.ts @@ -0,0 +1,18 @@ +/** + * 全局组件注册工具 + * + * @description + * 批量注册 Element Plus 图标等全局组件 + */ + +import type { App } from "vue"; +import * as ElementPlusIconsVue from "@element-plus/icons-vue"; + +/** + * 注册 Element Plus 所有图标为全局组件 + */ +export function registerElementIcons(app: App) { + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } +} diff --git a/src/utils/validators.ts b/src/utils/validators.ts new file mode 100644 index 00000000..83905870 --- /dev/null +++ b/src/utils/validators.ts @@ -0,0 +1,69 @@ +/** + * 表单验证规则工具 + * + * @description + * 提供常用的表单验证规则 + */ + +import type { FormItemRule } from "element-plus"; + +/** + * 验证规则生成器 + */ +export const VALIDATORS = { + /** + * 必填项验证 + */ + required(message: string): FormItemRule { + return { + required: true, + message, + trigger: "blur", + }; + }, + + /** + * 邮箱验证 + */ + email: { + type: "email", + message: "请输入正确的邮箱地址", + trigger: "blur", + } as FormItemRule, + + /** + * 手机号验证 + */ + mobile: { + pattern: /^1[3-9]\d{9}$/, + message: "请输入正确的手机号码", + trigger: "blur", + } as FormItemRule, + + /** + * URL 验证 + */ + url: { + type: "url", + message: "请输入正确的URL地址", + trigger: "blur", + } as FormItemRule, + + /** + * 数字验证 + */ + number: { + type: "number", + message: "请输入数字", + trigger: "blur", + } as FormItemRule, + + /** + * 整数验证 + */ + integer: { + type: "integer", + message: "请输入整数", + trigger: "blur", + } as FormItemRule, +}; diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts new file mode 100644 index 00000000..d1508210 --- /dev/null +++ b/src/utils/websocket.ts @@ -0,0 +1,114 @@ +/** + * WebSocket 服务管理 + * + * @description + * 统一管理应用中的所有 WebSocket 连接 + * - 字典同步 WebSocket + * - 在线用户计数 WebSocket + * - 其他业务 WebSocket + * + * @author 有来技术团队 + */ + +import { useDictSync } from "@/composables"; +import { AuthStorage } from "@/utils/auth"; + +/** + * WebSocket 服务实例约定接口 + */ +type WebSocketService = { + disconnect?: () => void; + closeWebSocket?: () => void; + cleanup?: () => void; + [key: string]: any; +}; + +/** + * 全局 WebSocket 实例管理 + */ +const websocketInstances = new Map(); + +/** + * 防止重复初始化的状态标记 + */ +let isInitialized = false; +let dictWebSocketInstance: ReturnType | null = null; + +/** + * 注册 WebSocket 实例 + */ +export function registerWebSocketInstance(key: string, instance: WebSocketService) { + websocketInstances.set(key, instance); +} + +/** + * 获取 WebSocket 实例 + */ +export function getWebSocketInstance(key: string) { + return websocketInstances.get(key); +} + +/** + * 初始化 WebSocket 服务 + */ +export function setupWebSocket() { + if (isInitialized) { + console.warn("[WebSocket] 已初始化,跳过重复初始化"); + return; + } + + if (!AuthStorage.getAccessToken()) { + console.warn("[WebSocket] 未登录,跳过 WebSocket 初始化"); + return; + } + + try { + dictWebSocketInstance = useDictSync(); + registerWebSocketInstance("dict-sync", dictWebSocketInstance); + isInitialized = true; + console.log("[WebSocket] 初始化成功"); + } catch (error) { + console.error("[WebSocket] 初始化失败:", error); + } +} + +/** + * 清理所有 WebSocket 连接 + */ +export function cleanupWebSocket() { + console.log("[WebSocket] 开始清理连接..."); + + websocketInstances.forEach((instance, key) => { + try { + if (instance.disconnect) { + instance.disconnect(); + } else if (instance.closeWebSocket) { + instance.closeWebSocket(); + } else if (instance.cleanup) { + instance.cleanup(); + } + console.log(`[WebSocket] ${key} 已断开`); + } catch (error) { + console.error(`[WebSocket] ${key} 清理失败:`, error); + } + }); + + websocketInstances.clear(); + dictWebSocketInstance = null; + isInitialized = false; + console.log("[WebSocket] 清理完成"); +} + +/** + * 重新初始化 WebSocket + */ +export function reinitializeWebSocket() { + cleanupWebSocket(); + setupWebSocket(); +} + +if (typeof window !== "undefined") { + window.addEventListener("beforeunload", () => { + cleanupWebSocket(); + }); +} diff --git a/tsconfig.json b/tsconfig.json index 4b4ebba7..e314cd1b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,6 +29,7 @@ }, "include": [ + "types/**/*.d.ts", "mock/**/*.ts", "src/**/*.ts", "src/**/*.vue", diff --git a/types/env.d.ts b/types/env.d.ts new file mode 100644 index 00000000..55ff6657 --- /dev/null +++ b/types/env.d.ts @@ -0,0 +1,29 @@ +/// + +/** + * Vite 环境变量类型定义 + */ +interface ImportMetaEnv { + readonly VITE_APP_PORT: number; + readonly VITE_APP_NAME: string; + readonly VITE_APP_BASE_API: string; + readonly VITE_APP_API_URL: string; + readonly VITE_MOCK_DEV_SERVER: boolean; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} + +declare const __APP_INFO__: { + pkg: { + name: string; + version: string; + engines: { + node: string; + }; + dependencies: Record; + devDependencies: Record; + }; + buildTimestamp: number; +}; diff --git a/types/modules.d.ts b/types/modules.d.ts new file mode 100644 index 00000000..a3ef2268 --- /dev/null +++ b/types/modules.d.ts @@ -0,0 +1,14 @@ +/** + * 第三方模块类型声明 + */ + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} + +declare module "sockjs-client/dist/sockjs.min.js" { + import Client from "sockjs-client"; + export default Client; +} diff --git a/types/router.d.ts b/types/router.d.ts new file mode 100644 index 00000000..ef63e390 --- /dev/null +++ b/types/router.d.ts @@ -0,0 +1,16 @@ +/** + * Vue Router 类型扩展 + */ +import "vue-router"; + +declare module "vue-router" { + interface RouteMeta { + title?: string; + icon?: string; + hidden?: boolean; + alwaysShow?: boolean; + affix?: boolean; + keepAlive?: boolean; + breadcrumb?: boolean; + } +}