diff --git a/README.md b/README.md index 769609d6..2a6757fa 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,9 @@ 🖥️ 在线预览 | 📲 移动端预览 | 📑 阅读文档| 🌐 官网 | 💬 English - ## 项目简介 -[vue3-element-admin](https://gitcode.com/youlai/vue3-element-admin) 基于 Vue3、Vite7、TypeScript 和 Element-Plus 搭建的极简开箱即用企业级后台管理前端模板。 配套 Java 后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) 和 Node 后端 [youlai-nest](https://gitee.com/youlaiorg/youlai-nest) 。 提供开发简版[vue3-element-template](https://gitee.com/youlaiorg/vue3-element-template) 和 JS 版本[vue3-element-admin-js](https://gitee.com/youlaiorg/vue3-element-admin) 供开发者快速开发。 - +[vue3-element-admin](https://gitcode.com/youlai/vue3-element-admin) 基于 Vue3、Vite7、TypeScript 和 Element-Plus 搭建的极简开箱即用企业级后台管理前端模板。 配套 Java 后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot)、多租户后端 [youlai-boot-tenant](https://gitee.com/youlaiorg/youlai-boot-tenant) 和 Node 后端 [youlai-nest](https://gitee.com/youlaiorg/youlai-nest) 。 提供开发简版[vue3-element-template](https://gitee.com/youlaiorg/vue3-element-template) 和 JS 版本[vue3-element-admin-js](https://gitee.com/youlaiorg/vue3-element-admin) 供开发者快速开发。 ## 项目特色 @@ -64,10 +62,11 @@ - **系统功能:** 提供用户管理、角色管理、菜单管理、部门管理、字典管理、系统配置、通知公告等功能模块。 - **权限管理:** 支持动态路由、按钮权限、角色权限和数据权限等多种权限管理方式。 +- **多租户:** 支持多租户模式与租户隔离。 + - **基础设施:** 提供国际化、多布局、暗黑模式、全屏、水印、接口文档和代码生成器等功能。 - **持续更新**:项目持续开源更新,实时更新工具和依赖。 - ## 项目截图 🖥️ **控制台** @@ -84,40 +83,37 @@ ## 项目源码 -| 项目 | Gitee | Github | GitCode| -| ---- | ----| ---- | ---- | -| vue3-element-admin ✅| [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.com/youlai/vue3-element-admin) | -| vue3-element-admin JS版| [vue3-element-admin-js](https://gitee.com/youlaiorg/vue3-element-admin-js) | [vue3-element-admin-js](https://github.com/youlaitech/vue3-element-admin-js) | [vue3-element-admin-js](https://gitcode.com/youlai/vue3-element-admin-js) | -| vue3-element-admin 精简版 | [vue3-element-template](https://gitee.com/youlaiorg/vue3-element-template) | [vue3-element-template](https://github.com/youlaitech/vue3-element-template) |[vue3-element-template](https://gitcode.com/youlai/vue3-element-template)| -| vue-uniapp-admin 移动版 | [vue-uniapp-admin](https://gitee.com/youlaiorg/vue-uniapp-admin) | [vue-uniapp-admin](https://github.com/youlaitech/vue-uniapp-admin) |[vue-uniapp-admin](https://gitcode.com/youlai/vue-uniapp-admin)| -| Java 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) |[youlai-boot](https://gitcode.com/youlai/youlai-boot.git)| -| Node 后端 | [youlai-nest](https://gitee.com/youlaiorg/youlai-nest) | [youlai-nest](https://github.com/haoxianrui/youlai-nest.git) |[youlai-nest](https://gitcode.com/youlai/youlai-nest.git)| - +| 项目 | Gitee | Github | GitCode | +| ------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| vue3-element-admin ✅ | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.com/youlai/vue3-element-admin) | +| vue3-element-admin JS版 | [vue3-element-admin-js](https://gitee.com/youlaiorg/vue3-element-admin-js) | [vue3-element-admin-js](https://github.com/youlaitech/vue3-element-admin-js) | [vue3-element-admin-js](https://gitcode.com/youlai/vue3-element-admin-js) | +| vue3-element-admin 精简版 | [vue3-element-template](https://gitee.com/youlaiorg/vue3-element-template) | [vue3-element-template](https://github.com/youlaitech/vue3-element-template) | [vue3-element-template](https://gitcode.com/youlai/vue3-element-template) | +| vue-uniapp-admin 移动版 | [vue-uniapp-admin](https://gitee.com/youlaiorg/vue-uniapp-admin) | [vue-uniapp-admin](https://github.com/youlaitech/vue-uniapp-admin) | [vue-uniapp-admin](https://gitcode.com/youlai/vue-uniapp-admin) | +| Java 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) | [youlai-boot](https://gitcode.com/youlai/youlai-boot.git) | +| Java 多租户后端 | [youlai-boot-tenant](https://gitee.com/youlaiorg/youlai-boot-tenant) | - | - | +| Node 后端 | [youlai-nest](https://gitee.com/youlaiorg/youlai-nest) | [youlai-nest](https://github.com/haoxianrui/youlai-nest.git) | [youlai-nest](https://gitcode.com/youlai/youlai-nest.git) | ## 开发指南 -| 名称 | 地址 | -|---------------|--------------------| -| 视频教程 | [https://www.bilibili.com/video/BV1eFUuYyEFj](https://www.bilibili.com/video/BV1eFUuYyEFj) | -| 项目搭建 | [基于 Vue3 + Vite + TypeScript + Element-Plus 从0到1搭建后台管理系统](https://blog.csdn.net/u013737132/article/details/130191394) | -| 官方文档 | [https://www.youlai.tech/vue3-element-admin](https://www.youlai.tech/vue3-element-admin/) | -| 代码规范 | [ESLint V9 + Prettier + Stylelint + EditorConfig 约束和统一前端代码规范](https://youlai.blog.csdn.net/article/details/145608723) | +| 名称 | 地址 | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| 视频教程 | [https://www.bilibili.com/video/BV1eFUuYyEFj](https://www.bilibili.com/video/BV1eFUuYyEFj) | +| 项目搭建 | [基于 Vue3 + Vite + TypeScript + Element-Plus 从0到1搭建后台管理系统](https://blog.csdn.net/u013737132/article/details/130191394) | +| 官方文档 | [https://www.youlai.tech/vue3-element-admin](https://www.youlai.tech/vue3-element-admin/) | +| 代码规范 | [ESLint V9 + Prettier + Stylelint + EditorConfig 约束和统一前端代码规范](https://youlai.blog.csdn.net/article/details/145608723) | | 提交规范 | [Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范](https://youlai.blog.csdn.net/article/details/145615236) | -| 接口文档 | [https://www.apifox.cn](https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5) | - - +| 接口文档 | [https://www.apifox.cn](https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5) | ## 项目启动 - **环境准备** -| 环境类型 | 版本要求 | 备注 | -|---------|---------|------| -| **Node.js** | `^20.19.0` 或 `>=22.12.0` | 推荐使用 LTS 版本(主版本为偶数) | -| **包管理器** | `pnpm >= 8.0.0` | 项目使用 pnpm 作为包管理器 | +| 环境类型 | 版本要求 | 备注 | +| ------------ | ------------------------------------------------------------ | --------------------------------- | +| **Node.js** | `^20.19.0` 或 `>=22.12.0` | 推荐使用 LTS 版本(主版本为偶数) | +| **包管理器** | `pnpm >= 8.0.0` | 项目使用 pnpm 作为包管理器 | | **开发工具** | [Visual Studio Code](https://code.visualstudio.com/Download) | 推荐安装 Vue、TypeScript 相关插件 | - - **快速开始** ```bash @@ -140,7 +136,6 @@ pnpm install pnpm run dev ``` - ## 项目部署 执行 `pnpm run build` 命令后,项目将被打包并生成 `dist` 目录。接下来,将 `dist` 目录下的文件上传到服务器 `/usr/share/nginx/html` 目录下,并配置 Nginx 进行反向代理。 @@ -173,7 +168,7 @@ server { ## 本地Mock -项目同时支持在线和本地 Mock 接口,默认使用线上接口,如需替换为 Mock 接口,修改文件 `.env.development` 的 `VITE_MOCK_DEV_SERVER` 为 `true` **即可**。 +项目同时支持在线和本地 Mock 接口,默认使用线上接口,如需替换为 Mock 接口,修改文件 `.env.development` 的 `VITE_MOCK_DEV_SERVER` 为 `true` **即可**。 ## 后端接口 @@ -183,7 +178,6 @@ server { 2. 根据后端工程的说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动。 3. 修改 `.env.development` 文件中的 `VITE_APP_API_URL` 的值,将其从 https://api.youlai.tech 更改为 http://localhost:8989 即可。 - ## 注意事项 - **自动导入插件自动生成默认关闭** @@ -202,31 +196,27 @@ server { - **项目组件、函数和引用爆红** - 重启 VSCode 尝试 + 重启 VSCode 尝试 - **其他问题** 如果有其他问题或者建议,建议 [ISSUE](https://gitee.com/youlaiorg/vue3-element-admin/issues/new) - ## 提交规范 执行 `pnpm run commit` 唤起 git commit 交互,根据提示完成信息的输入和选择。 ![](https://foruda.gitee.com/images/1687755823165218215/c1705416_716974.png) - ## 项目统计 ![](https://repobeats.axiom.co/api/embed/aa7cca3d6fa9c308fc659fa6e09af9a1910506c3.svg "Repobeats analytics image") - Thanks to all the contributors! 感谢所有的贡献者! [![contributors](https://contrib.rocks/image?repo=youlaitech/vue3-element-admin)](https://github.com/youlaitech/vue3-element-admin/graphs/contributors) - ## 特别感谢 - 感谢 [GitCode](https://gitcode.com/) 官方的 [G-Star](https://gitcode.com/g-star) 认证 diff --git a/mock/ai.mock.ts b/mock/ai.mock.ts new file mode 100644 index 00000000..b9142cbe --- /dev/null +++ b/mock/ai.mock.ts @@ -0,0 +1,118 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "ai/assistant/parse", + method: ["POST"], + body: ({ body }) => { + return { + code: "00000", + data: { + parseLogId: "10001", + success: true, + functionCalls: [ + { + name: "navigate", + arguments: { + path: "/system/user", + }, + }, + ], + explanation: `Mock: 已解析命令:${body?.command ?? ""}`, + confidence: 0.92, + }, + msg: "一切ok", + }; + }, + }, + { + url: "ai/assistant/execute", + method: ["POST"], + body: { + code: "00000", + data: { + success: true, + message: "Mock: 执行成功", + }, + msg: "一切ok", + }, + }, + { + url: "ai/assistant/records", + method: ["GET"], + body: ({ query }) => { + const pageNum = Number(query?.pageNum ?? 1); + const pageSize = Number(query?.pageSize ?? 10); + const total = 2; + + return { + code: "00000", + data: { + list: [ + { + id: "10001", + userId: 1, + username: "admin", + originalCommand: "跳转到用户管理", + aiProvider: "qwen", + aiModel: "qwen-plus", + parseStatus: 1, + functionCalls: JSON.stringify( + [ + { + name: "navigate", + arguments: { path: "/system/user" }, + }, + ], + null, + 0 + ), + explanation: "Mock: 识别到跳转用户管理", + confidence: 0.92, + parseDurationMs: 128, + functionName: "navigate", + functionArguments: JSON.stringify({ path: "/system/user" }), + executeStatus: 1, + ipAddress: "127.0.0.1", + createTime: "2025-12-17 15:00:00", + updateTime: "2025-12-17 15:00:00", + }, + { + id: "10002", + userId: 1, + username: "admin", + originalCommand: "获取姓名为张三的用户信息", + aiProvider: "qwen", + aiModel: "qwen-plus", + parseStatus: 0, + functionCalls: "[]", + explanation: "Mock: 解析失败示例", + confidence: 0.2, + parseErrorMessage: "Mock: 无法匹配函数", + parseDurationMs: 256, + executeStatus: 0, + ipAddress: "127.0.0.1", + createTime: "2025-12-17 15:01:00", + updateTime: "2025-12-17 15:01:00", + }, + ].slice((pageNum - 1) * pageSize, pageNum * pageSize), + total, + }, + msg: "一切ok", + }; + }, + }, + { + url: "ai/assistant/records/:ids", + method: ["DELETE"], + body: ({ params }) => { + return { + code: "00000", + data: { + ids: params?.ids, + }, + msg: "一切ok", + }; + }, + }, +]); diff --git a/mock/log.mock.ts b/mock/log.mock.ts index b75963e7..d76f9f9b 100644 --- a/mock/log.mock.ts +++ b/mock/log.mock.ts @@ -164,44 +164,4 @@ export default defineMock([ msg: "一切ok", }, }, - { - url: "logs/visit-trend", - method: ["GET"], - body: { - code: "00000", - data: { - dates: [ - "2024-06-30", - "2024-07-01", - "2024-07-02", - "2024-07-03", - "2024-07-04", - "2024-07-05", - "2024-07-06", - "2024-07-07", - ], - pvList: [1751, 5168, 4882, 5301, 4721, 4885, 1901, 1003], - uvList: null, - ipList: [207, 566, 565, 631, 579, 496, 222, 152], - }, - msg: "一切ok", - }, - }, - - { - url: "logs/visit-stats", - method: ["GET"], - body: { - code: "00000", - data: { - todayPvCount: 1629, - totalPvCount: 286086, - pvGrowthRate: -0.65, - todayIpCount: 169, - totalIpCount: 19985, - ipGrowthRate: -0.57, - }, - msg: "一切ok", - }, - }, ]); diff --git a/mock/statistics.mock.ts b/mock/statistics.mock.ts new file mode 100644 index 00000000..ab360dde --- /dev/null +++ b/mock/statistics.mock.ts @@ -0,0 +1,43 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "statistics/visits/trend", + method: ["GET"], + body: { + code: "00000", + data: { + dates: [ + "2024-06-30", + "2024-07-01", + "2024-07-02", + "2024-07-03", + "2024-07-04", + "2024-07-05", + "2024-07-06", + "2024-07-07", + ], + pvList: [1751, 5168, 4882, 5301, 4721, 4885, 1901, 1003], + uvList: null, + ipList: [207, 566, 565, 631, 579, 496, 222, 152], + }, + msg: "一切ok", + }, + }, + { + url: "statistics/visits/overview", + method: ["GET"], + body: { + code: "00000", + data: { + todayUvCount: 169, + totalUvCount: 19985, + uvGrowthRate: -0.57, + todayPvCount: 1629, + totalPvCount: 286086, + pvGrowthRate: -0.65, + }, + msg: "一切ok", + }, + }, +]); diff --git a/mock/tenant.mock.ts b/mock/tenant.mock.ts new file mode 100644 index 00000000..012b1b03 --- /dev/null +++ b/mock/tenant.mock.ts @@ -0,0 +1,65 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "tenants", + method: ["GET"], + body: { + code: "00000", + data: [ + { + id: 1, + name: "默认租户", + domain: "default", + }, + { + id: 2, + name: "演示租户", + domain: "demo", + }, + ], + msg: "一切ok", + }, + }, + { + url: "tenants/current", + method: ["GET"], + body: { + code: "00000", + data: { + id: 1, + name: "默认租户", + domain: "default", + }, + msg: "一切ok", + }, + }, + { + url: "tenants/:tenantId/switch", + method: ["POST"], + body({ params }) { + const tenantId = Number(params.tenantId); + + const allTenants = [ + { + id: 1, + name: "默认租户", + domain: "default", + }, + { + id: 2, + name: "演示租户", + domain: "demo", + }, + ]; + + const tenant = allTenants.find((t) => t.id === tenantId) || null; + + return { + code: tenant ? "00000" : "A0400", + data: tenant, + msg: tenant ? "切换租户成功" : "租户不存在", + }; + }, + }, +]); diff --git a/src/api/ai/index.ts b/src/api/ai/index.ts index 802100a1..de6dc6a7 100644 --- a/src/api/ai/index.ts +++ b/src/api/ai/index.ts @@ -1,180 +1,73 @@ import request from "@/utils/request"; +import type { + AiCommandRequest, + AiCommandResponse, + AiExecuteRequest, + AiExecuteResponse, + AiCommandRecordPageQuery, + AiCommandRecordVo, +} from "@/types/api"; -/** - * AI 命令请求参数 - */ -export interface AiCommandRequest { - /** 用户输入的自然语言命令 */ - command: string; - /** 当前页面路由(用于上下文) */ - currentRoute?: string; - /** 当前激活的组件名称 */ - currentComponent?: string; - /** 额外上下文信息 */ - context?: Record; -} - -/** - * 函数调用参数 - */ -export interface FunctionCall { - /** 函数名称 */ - name: string; - /** 函数描述 */ - description?: string; - /** 参数对象 */ - arguments: Record; -} - -/** - * AI 命令解析响应 - */ -export interface AiCommandResponse { - /** 解析日志ID(用于关联执行记录) */ - parseLogId?: string; - /** 是否成功解析 */ - success: boolean; - /** 解析后的函数调用列表 */ - functionCalls: FunctionCall[]; - /** AI 的理解和说明 */ - explanation?: string; - /** 置信度 (0-1) */ - confidence?: number; - /** 错误信息 */ - error?: string; - /** 原始 LLM 响应(用于调试) */ - rawResponse?: string; -} - -/** - * AI 命令执行请求 - */ -export interface AiExecuteRequest { - /** 关联的解析日志ID */ - parseLogId?: string; - /** 原始命令(用于审计) */ - originalCommand?: string; - /** 要执行的函数调用 */ - functionCall: FunctionCall; - /** 确认模式:auto=自动执行, manual=需要用户确认 */ - confirmMode?: "auto" | "manual"; - /** 用户确认标志 */ - userConfirmed?: boolean; - /** 幂等性令牌(防止重复执行) */ - idempotencyKey?: string; - /** 当前页面路由 */ - currentRoute?: string; -} - -/** - * AI 命令执行响应 - */ -export interface AiExecuteResponse { - /** 是否执行成功 */ - success: boolean; - /** 执行结果数据 */ - data?: any; - /** 执行结果说明 */ - message?: string; - /** 影响的记录数 */ - affectedRows?: number; - /** 错误信息 */ - error?: string; - /** 记录ID(用于追踪) */ - recordId?: string; - /** 需要用户确认 */ - requiresConfirmation?: boolean; - /** 确认提示信息 */ - confirmationPrompt?: string; -} - -export interface AiCommandRecordPageQuery extends PageQuery { - keywords?: string; - executeStatus?: number; - parseStatus?: number; - userId?: number; - aiProvider?: string; - aiModel?: string; - functionName?: string; - createTime?: [string, string]; -} - -export interface AiCommandRecordVO { - id: string; - userId: number; - username: string; - originalCommand: string; - aiProvider?: string; - aiModel?: string; - parseStatus?: number; - functionCalls?: string; - explanation?: string; - confidence?: number; - parseErrorMessage?: string; - inputTokens?: number; - outputTokens?: number; - parseDurationMs?: number; - functionName?: string; - functionArguments?: string; - executeStatus?: number; - executeErrorMessage?: string; - ipAddress?: string; - createTime?: string; - updateTime?: string; -} +const AI_BASE_URL = "/api/v1/ai/assistant"; /** * AI 命令 API */ -class AiCommandApi { +const AiCommandApi = { /** - * 解析自然语言命令 + * 解析 AI 命令 * - * @param data 命令请求参数 - * @returns 解析结果 + * @param data AI 命令请求参数 + * @returns AI 命令解析响应 */ - static parseCommand(data: AiCommandRequest): Promise { + parseCommand(data: AiCommandRequest) { return request({ - url: "/api/v1/ai/command/parse", + url: `${AI_BASE_URL}/parse`, method: "post", data, }); - } + }, /** - * 执行已解析的命令 + * 执行 AI 命令 * - * @param data 执行请求参数 - * @returns 执行结果数据(成功时返回,失败时抛出异常) + * @param data AI 命令执行请求 + * @returns AI 命令执行响应 */ - static executeCommand(data: AiExecuteRequest): Promise { - return request({ - url: "/api/v1/ai/command/execute", + executeCommand(data: AiExecuteRequest) { + return request({ + url: `${AI_BASE_URL}/execute`, method: "post", data, }); - } + }, /** - * 获取命令记录分页列表 + * 获取 AI 命令记录分页列表 + * + * @param queryParams 查询参数 + * @returns AI 命令记录分页列表 */ - static getCommandRecordPage(queryParams: AiCommandRecordPageQuery) { - return request>({ - url: "/api/v1/ai/command/records", + getPage(queryParams: AiCommandRecordPageQuery) { + return request>({ + url: `${AI_BASE_URL}/records`, method: "get", params: queryParams, }); - } + }, /** - * 撤销命令执行(如果支持) + * 删除 AI 命令记录 + * + * @param ids 记录ID,多个以逗号分隔 + * @returns 删除结果 */ - static rollbackCommand(logId: string) { + deleteByIds(ids: string) { return request({ - url: `/api/v1/ai/command/rollback/${logId}`, - method: "post", + url: `${AI_BASE_URL}/records/${ids}`, + method: "delete", }); - } -} + }, +}; export default AiCommandApi; diff --git a/src/api/codegen.ts b/src/api/codegen.ts index e9863d39..5268cea0 100644 --- a/src/api/codegen.ts +++ b/src/api/codegen.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { GeneratorPreviewVo, TablePageQuery, TablePageVo, GenConfigForm } from "@/api/types"; const GENERATOR_BASE_URL = "/api/v1/codegen"; const GeneratorAPI = { /** 获取数据表分页列表 */ getTablePage(params: TablePageQuery) { - return request>({ + return request>({ url: `${GENERATOR_BASE_URL}/table/page`, method: "get", params, @@ -31,7 +32,7 @@ const GeneratorAPI = { /** 获取代码生成预览数据 */ getPreviewData(tableName: string, pageType?: "classic" | "curd") { - return request({ + return request({ url: `${GENERATOR_BASE_URL}/${tableName}/preview`, method: "get", params: pageType ? { pageType } : undefined, @@ -58,15 +59,26 @@ const GeneratorAPI = { params: pageType ? { pageType } : undefined, responseType: "blob", }).then((response) => { - const fileName = decodeURI( - response.headers["content-disposition"].split(";")[1].split("=")[1] - ); + const contentDisposition = response?.headers?.["content-disposition"] as string | undefined; + let fileName = `${tableName}.zip`; + if (contentDisposition) { + // content-disposition: attachment; filename=xxx.zip + const match = /filename\*?=(?:UTF-8''|")?([^;"]+)/i.exec(contentDisposition); + if (match?.[1]) { + try { + fileName = decodeURIComponent(match[1]); + } catch { + fileName = match[1]; + } + } + } const blob = new Blob([response.data], { type: "application/zip" }); const a = document.createElement("a"); const url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; + a.click(); window.URL.revokeObjectURL(url); }); @@ -76,124 +88,3 @@ const GeneratorAPI = { export default GeneratorAPI; /** 代码生成预览对象 */ -export interface GeneratorPreviewVO { - /** 文件生成路径 */ - path: string; - /** 文件名称 */ - fileName: string; - /** 文件内容 */ - content: string; -} - -/** 数据表分页查询参数 */ -export interface TablePageQuery extends PageQuery { - /** 关键字(表名) */ - keywords?: string; -} - -/** 数据表分页对象 */ -export interface TablePageVO { - /** 表名称 */ - tableName: string; - - /** 表描述 */ - tableComment: string; - - /** 存储引擎 */ - engine: string; - - /** 字符集排序规则 */ - tableCollation: string; - - /** 创建时间 */ - createTime: string; -} - -/** 代码生成配置表单 */ -export interface GenConfigForm { - /** 主键 */ - id?: string; - - /** 表名 */ - tableName?: string; - - /** 业务名 */ - businessName?: string; - - /** 模块名 */ - moduleName?: string; - - /** 包名 */ - packageName?: string; - - /** 实体名 */ - entityName?: string; - - /** 作者 */ - author?: string; - - /** 上级菜单 */ - parentMenuId?: string; - - /** 后端应用名 */ - backendAppName?: string; - /** 前端应用名 */ - frontendAppName?: string; - - /** 字段配置列表 */ - fieldConfigs?: FieldConfig[]; - - /** 页面类型 classic|curd */ - pageType?: "classic" | "curd"; - - /** 要移除的表前缀,如 sys_ */ - removeTablePrefix?: string; -} - -/** 字段配置 */ -export interface FieldConfig { - /** 主键 */ - id?: string; - - /** 列名 */ - columnName?: string; - - /** 列类型 */ - columnType?: string; - - /** 字段名 */ - fieldName?: string; - - /** 字段类型 */ - fieldType?: string; - - /** 字段描述 */ - fieldComment?: string; - - /** 是否在列表显示 */ - isShowInList?: number; - - /** 是否在表单显示 */ - isShowInForm?: number; - - /** 是否在查询条件显示 */ - isShowInQuery?: number; - - /** 是否必填 */ - isRequired?: number; - - /** 表单类型 */ - formType?: number; - - /** 查询类型 */ - queryType?: number; - - /** 字段长度 */ - maxLength?: number; - - /** 字段排序 */ - fieldSort?: number; - - /** 字典类型 */ - dictType?: string; -} diff --git a/src/api/file.ts b/src/api/file.ts index b899c447..82b84922 100644 --- a/src/api/file.ts +++ b/src/api/file.ts @@ -1,4 +1,5 @@ import request from "@/utils/request"; +import type { FileInfo } from "@/types/api"; const FileAPI = { /** 上传文件 (传入 FormData,上传进度回调) */ @@ -57,8 +58,3 @@ const FileAPI = { }; export default FileAPI; - -export interface FileInfo { - name: string; - url: string; -} diff --git a/src/api/system/config.ts b/src/api/system/config.ts index 1496624f..1ffe3c79 100644 --- a/src/api/system/config.ts +++ b/src/api/system/config.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { ConfigPageQuery, ConfigForm, ConfigPageVo } from "@/types/api"; const CONFIG_BASE_URL = "/api/v1/configs"; const ConfigAPI = { /** 获取配置分页数据 */ getPage(queryParams?: ConfigPageQuery) { - return request>({ + return request>({ url: `${CONFIG_BASE_URL}/page`, method: "get", params: queryParams, @@ -37,34 +38,3 @@ const ConfigAPI = { }; export default ConfigAPI; - -export interface ConfigPageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; -} - -export interface ConfigForm { - /** 主键 */ - id?: string; - /** 配置名称 */ - configName?: string; - /** 配置键 */ - configKey?: string; - /** 配置值 */ - configValue?: string; - /** 描述、备注 */ - remark?: string; -} - -export interface ConfigPageVO { - /** 主键 */ - id?: string; - /** 配置名称 */ - configName?: string; - /** 配置键 */ - configKey?: string; - /** 配置值 */ - configValue?: string; - /** 描述、备注 */ - remark?: string; -} diff --git a/src/api/system/dept.ts b/src/api/system/dept.ts index 749c37c7..e3d24b7b 100644 --- a/src/api/system/dept.ts +++ b/src/api/system/dept.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { DeptQuery, DeptVo, DeptForm } from "@/types/api"; const DEPT_BASE_URL = "/api/v1/depts"; const DeptAPI = { /** 获取部门树形列表 */ getList(queryParams?: DeptQuery) { - return request({ url: `${DEPT_BASE_URL}`, method: "get", params: queryParams }); + return request({ url: `${DEPT_BASE_URL}`, method: "get", params: queryParams }); }, /** 获取部门下拉数据源 */ getOptions() { @@ -30,46 +31,3 @@ const DeptAPI = { }; export default DeptAPI; - -export interface DeptQuery { - /** 搜索关键字 */ - keywords?: string; - /** 状态 */ - status?: number; -} - -export interface DeptVO { - /** 子部门 */ - children?: DeptVO[]; - /** 创建时间 */ - createTime?: Date; - /** 部门ID */ - id?: string; - /** 部门名称 */ - name?: string; - /** 部门编号 */ - code?: string; - /** 父部门ID */ - parentid?: string; - /** 排序 */ - sort?: number; - /** 状态(1:启用;0:禁用) */ - status?: number; - /** 修改时间 */ - updateTime?: Date; -} - -export interface DeptForm { - /** 部门ID(新增不填) */ - id?: string; - /** 部门名称 */ - name?: string; - /** 部门编号 */ - code?: string; - /** 父部门ID */ - parentId: string; - /** 排序 */ - sort?: number; - /** 状态(1:启用;0:禁用) */ - status?: number; -} diff --git a/src/api/system/dict.ts b/src/api/system/dict.ts index 3976b8d4..bf08e462 100644 --- a/src/api/system/dict.ts +++ b/src/api/system/dict.ts @@ -1,11 +1,20 @@ import request from "@/utils/request"; +import type { + DictPageQuery, + DictPageVo, + DictForm, + DictItemPageQuery, + DictItemPageVo, + DictItemForm, + DictItemOption, +} from "@/types/api"; const DICT_BASE_URL = "/api/v1/dicts"; const DictAPI = { /** 字典分页列表 */ getPage(queryParams: DictPageQuery) { - return request>({ + return request>({ url: `${DICT_BASE_URL}/page`, method: "get", params: queryParams, @@ -34,7 +43,7 @@ const DictAPI = { /** 获取字典项分页列表 */ getDictItemPage(dictCode: string, queryParams: DictItemPageQuery) { - return request>({ + return request>({ url: `${DICT_BASE_URL}/${dictCode}/items/page`, method: "get", params: queryParams, @@ -69,77 +78,3 @@ const DictAPI = { }; export default DictAPI; - -export interface DictPageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; - /** 状态(1:启用;0:禁用) */ - status?: number; -} -export interface DictPageVO { - /** 字典ID */ - id: string; - /** 字典名称 */ - name: string; - /** 字典编码 */ - dictCode: string; - /** 状态(1:启用;0:禁用) */ - status: number; -} -export interface DictForm { - /** 字典ID(新增不填) */ - id?: string; - /** 字典名称 */ - name?: string; - /** 字典编码 */ - dictCode?: string; - /** 状态(1:启用;0:禁用) */ - status?: number; - /** 备注 */ - remark?: string; -} -export interface DictItemPageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; - /** 字典编码 */ - dictCode?: string; -} -export interface DictItemPageVO { - /** 字典项ID */ - id: string; - /** 字典编码 */ - dictCode: string; - /** 字典项值 */ - value: string; - /** 字典项标签 */ - label: string; - /** 状态(1:启用;0:禁用) */ - status: number; - /** 排序 */ - sort?: number; -} -export interface DictItemForm { - /** 字典项ID(新增不填) */ - id?: string; - /** 字典编码 */ - dictCode?: string; - /** 字典项值 */ - value?: string; - /** 字典项标签 */ - label?: string; - /** 状态(1:启用;0:禁用) */ - status?: number; - /** 排序 */ - sort?: number; - /** 标签类型 */ - tagType?: "success" | "warning" | "info" | "primary" | "danger" | ""; -} -export interface DictItemOption { - /** 值 */ - value: number | string; - /** 标签 */ - label: string; - /** 标签类型 */ - tagType?: "" | "success" | "info" | "warning" | "danger"; - [key: string]: any; -} diff --git a/src/api/system/log.ts b/src/api/system/log.ts index 9e08cc62..2f6c77aa 100644 --- a/src/api/system/log.ts +++ b/src/api/system/log.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { LogPageQuery, LogPageVo } from "@/types/api"; const LOG_BASE_URL = "/api/v1/logs"; const LogAPI = { /** 获取日志分页列表 */ getPage(queryParams: LogPageQuery) { - return request>({ + return request>({ url: `${LOG_BASE_URL}/page`, method: "get", params: queryParams, @@ -14,35 +15,3 @@ const LogAPI = { }; export default LogAPI; - -export interface LogPageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; - /** 操作时间 */ - createTime?: [string, string]; -} - -export interface LogPageVO { - /** 主键 */ - id: string; - /** 日志模块 */ - module: string; - /** 日志内容 */ - content: string; - /** 请求路径 */ - requestUri: string; - /** 请求方法 */ - method: string; - /** IP 地址 */ - ip: string; - /** 地区 */ - region: string; - /** 浏览器 */ - browser: string; - /** 终端系统 */ - os: string; - /** 执行时间(毫秒) */ - executionTime: number; - /** 操作人 */ - operator: string; -} diff --git a/src/api/system/menu.ts b/src/api/system/menu.ts index 41e6dfba..aee3b534 100644 --- a/src/api/system/menu.ts +++ b/src/api/system/menu.ts @@ -1,16 +1,17 @@ import request from "@/utils/request"; import type { MenuTypeEnum } from "@/enums/business"; +import type { MenuQuery, MenuVo, MenuForm, MenuOption, RouteVo, Meta } from "@/types/api"; const MENU_BASE_URL = "/api/v1/menus"; const MenuAPI = { /** 获取当前用户的路由列表 */ getRoutes() { - return request({ url: `${MENU_BASE_URL}/routes`, method: "get" }); + return request({ url: `${MENU_BASE_URL}/routes`, method: "get" }); }, /** 获取菜单树形列表 */ getList(queryParams: MenuQuery) { - return request({ url: `${MENU_BASE_URL}`, method: "get", params: queryParams }); + return request({ url: `${MENU_BASE_URL}`, method: "get", params: queryParams }); }, /** 获取菜单下拉数据源 */ getOptions(onlyParent?: boolean) { @@ -39,98 +40,3 @@ const MenuAPI = { }; export default MenuAPI; - -export interface MenuQuery { - /** 搜索关键字 */ - keywords?: string; -} -export interface MenuVO { - /** 子菜单 */ - children?: MenuVO[]; - /** 组件路径 */ - component?: string; - /** ICON */ - icon?: string; - /** 菜单ID */ - id?: string; - /** 菜单名称 */ - name?: string; - /** 父菜单ID */ - parentId?: string; - /** 按钮权限标识 */ - perm?: string; - /** 跳转路径 */ - redirect?: string; - /** 路由名称 */ - routeName?: string; - /** 路由相对路径 */ - routePath?: string; - /** 菜单排序(数字越小排名越靠前) */ - sort?: number; - /** 菜单类型 */ - type?: MenuTypeEnum; - /** 是否可见(1:显示;0:隐藏) */ - visible?: number; -} -export interface MenuForm { - /** 菜单ID */ - id?: string; - /** 父菜单ID */ - parentId?: string; - /** 菜单名称 */ - name?: string; - /** 是否可见(1-是 0-否) */ - visible: number; - /** ICON */ - icon?: string; - /** 排序 */ - sort?: number; - /** 路由名称 */ - routeName?: string; - /** 路由路径 */ - routePath?: string; - /** 组件路径 */ - component?: string; - /** 跳转路由路径 */ - redirect?: string; - /** 菜单类型 */ - type?: MenuTypeEnum; - /** 权限标识 */ - perm?: string; - /** 【菜单】是否开启页面缓存 */ - keepAlive?: number; - /** 【目录】只有一个子路由是否始终显示 */ - alwaysShow?: number; - /** 其他参数 */ - params?: KeyValue[]; -} -interface KeyValue { - key: string; - value: string; -} -export interface RouteVO { - /** 子路由列表 */ - children: RouteVO[]; - /** 组件路径 */ - component?: string; - /** 路由属性 */ - meta?: Meta; - /** 路由名称 */ - name?: string; - /** 路由路径 */ - path?: string; - /** 跳转链接 */ - redirect?: string; -} -export interface Meta { - /** 【目录】只有一个子路由是否始终显示 */ - alwaysShow?: boolean; - /** 是否隐藏(true-是 false-否) */ - hidden?: boolean; - /** ICON */ - icon?: string; - /** 【菜单】是否开启页面缓存 */ - keepAlive?: boolean; - /** 路由title */ - title?: string; -} diff --git a/src/api/system/notice.ts b/src/api/system/notice.ts index c8eb7140..f0e1a9ad 100644 --- a/src/api/system/notice.ts +++ b/src/api/system/notice.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { NoticePageQuery, NoticeForm, NoticePageVo, NoticeDetailVo } from "@/types/api"; const NOTICE_BASE_URL = "/api/v1/notices"; const NoticeAPI = { /** 获取通知公告分页数据 */ getPage(queryParams?: NoticePageQuery) { - return request>({ + return request>({ url: `${NOTICE_BASE_URL}/page`, method: "get", params: queryParams, @@ -37,7 +38,7 @@ const NoticeAPI = { }, /** 查看通知 */ getDetail(id: string) { - return request({ url: `${NOTICE_BASE_URL}/${id}/detail`, method: "get" }); + return request({ url: `${NOTICE_BASE_URL}/${id}/detail`, method: "get" }); }, /** 全部已读 */ readAll() { @@ -45,7 +46,7 @@ const NoticeAPI = { }, /** 获取我的通知分页列表 */ getMyNoticePage(queryParams?: NoticePageQuery) { - return request>({ + return request>({ url: `${NOTICE_BASE_URL}/my`, method: "get", params: queryParams, @@ -54,68 +55,3 @@ const NoticeAPI = { }; export default NoticeAPI; - -export interface NoticePageQuery extends PageQuery { - /** 标题 */ - title?: string; - /** 发布状态(0:草稿;1:已发布;2:已撤回) */ - publishStatus?: number; - /** 是否已读(1:是;0:否) */ - isRead?: number; -} -export interface NoticeForm { - /** 通知ID(新增不填) */ - id?: string; - /** 标题 */ - title?: string; - /** 内容 */ - content?: string; - /** 类型 */ - type?: number; - /** 优先级/级别 */ - level?: string; - /** 目标类型 */ - targetType?: number; - /** 目标用户ID(多个以英文逗号(,)分割) */ - targetUserIds?: string; -} -export interface NoticePageVO { - /** 通知ID */ - id: string; - /** 标题 */ - title?: string; - /** 内容 */ - content?: string; - /** 类型 */ - type?: number; - /** 发布人ID */ - publisherId?: bigint; - /** 优先级 */ - priority?: number; - /** 目标类型 */ - targetType?: number; - /** 发布状态 */ - publishStatus?: number; - /** 发布时间 */ - publishTime?: Date; - /** 撤回时间 */ - revokeTime?: Date; -} -export interface NoticeDetailVO { - /** 通知ID */ - id?: string; - /** 标题 */ - title?: string; - /** 内容 */ - content?: string; - /** 类型 */ - type?: number; - /** 发布人名称 */ - publisherName?: string; - /** 优先级/级别 */ - level?: string; - /** 发布时间 */ - publishTime?: Date; - /** 发布状态 */ - publishStatus?: number; -} diff --git a/src/api/system/role.ts b/src/api/system/role.ts index bf713b99..5dfc3276 100644 --- a/src/api/system/role.ts +++ b/src/api/system/role.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { RolePageQuery, RolePageVo, RoleForm } from "@/types/api"; const ROLE_BASE_URL = "/api/v1/roles"; const RoleAPI = { /** 获取角色分页数据 */ getPage(queryParams?: RolePageQuery) { - return request>({ + return request>({ url: `${ROLE_BASE_URL}/page`, method: "get", params: queryParams, @@ -42,38 +43,3 @@ const RoleAPI = { }; export default RoleAPI; - -export interface RolePageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; -} -export interface RolePageVO { - /** 角色ID */ - id?: string; - /** 角色编码 */ - code?: string; - /** 角色名称 */ - name?: string; - /** 排序 */ - sort?: number; - /** 角色状态 */ - status?: number; - /** 创建时间 */ - createTime?: Date; - /** 修改时间 */ - updateTime?: Date; -} -export interface RoleForm { - /** 角色ID */ - id?: string; - /** 角色编码 */ - code?: string; - /** 数据权限 */ - dataScope?: number; - /** 角色名称 */ - name?: string; - /** 排序 */ - sort?: number; - /** 角色状态(1-正常;0-停用) */ - status?: number; -} diff --git a/src/api/system/statistics.ts b/src/api/system/statistics.ts index fdd8b6b9..4bcedf35 100644 --- a/src/api/system/statistics.ts +++ b/src/api/system/statistics.ts @@ -1,11 +1,12 @@ import request from "@/utils/request"; +import type { VisitTrendQuery, VisitTrendVo, VisitStatsVo } from "@/types/api"; const STATISTICS_BASE_URL = "/api/v1/statistics"; const StatisticsAPI = { /** 获取访问趋势统计 */ getVisitTrend(queryParams: VisitTrendQuery) { - return request({ + return request({ url: `${STATISTICS_BASE_URL}/visits/trend`, method: "get", params: queryParams, @@ -13,7 +14,7 @@ const StatisticsAPI = { }, /** 获取访问概览统计 */ getVisitOverview() { - return request({ + return request({ url: `${STATISTICS_BASE_URL}/visits/overview`, method: "get", }); @@ -21,36 +22,3 @@ const StatisticsAPI = { }; export default StatisticsAPI; - -export interface VisitTrendQuery { - /** 开始日期 */ - startDate: string; - /** 结束日期 */ - endDate: string; -} - -export interface VisitTrendVO { - /** 日期列表 */ - dates: string[]; - /** 浏览量(PV) */ - pvList: number[]; - /** 访客数(UV) */ - uvList: number[]; - /** IP数 */ - ipList: number[]; -} - -export interface VisitStatsVO { - /** 今日访客数(UV) */ - todayUvCount: number; - /** 总访客数 */ - totalUvCount: number; - /** 访客数同比增长率(相对于昨天同一时间段的增长率) */ - uvGrowthRate: number; - /** 今日浏览量(PV) */ - todayPvCount: number; - /** 总浏览量 */ - totalPvCount: number; - /** 同比增长率(相对于昨天同一时间段的增长率) */ - pvGrowthRate: number; -} diff --git a/src/api/system/tenant.ts b/src/api/system/tenant.ts index 1043a993..b650ec2d 100644 --- a/src/api/system/tenant.ts +++ b/src/api/system/tenant.ts @@ -1,39 +1,22 @@ import request from "@/utils/request"; +import type { + TenantCreateForm, + TenantCreateResultVo, + TenantForm, + TenantInfo, + TenantPageQuery, + TenantPageVo, +} from "@/types/api"; const TENANT_BASE_URL = "/api/v1/tenants"; /** * 租户信息 */ -export interface TenantInfo { - /** 租户ID */ - id: number; - /** 租户名称 */ - name: string; - /** 租户编码 */ - code?: string; - /** 租户状态(1-正常 0-禁用) */ - status?: number; - /** 联系人姓名 */ - contactName?: string; - /** 联系人电话 */ - contactPhone?: string; - /** 联系人邮箱 */ - contactEmail?: string; - /** 租户域名 */ - domain?: string; - /** 租户Logo */ - logo?: string; - /** 是否默认租户 */ - isDefault?: boolean; -} -/** - * 租户 API - */ const TenantAPI = { /** - * 获取当前用户的租户列表 + * 获取当前用户可访问的租户列表 */ getTenantList() { return request({ @@ -63,6 +46,58 @@ const TenantAPI = { method: "post", }); }, + + /** 获取租户分页数据(平台租户管理) */ + getPage(queryParams?: TenantPageQuery) { + return request>({ + url: `${TENANT_BASE_URL}/page`, + method: "get", + params: queryParams, + }); + }, + + /** 获取租户表单数据 */ + getFormData(tenantId: string) { + return request({ + url: `${TENANT_BASE_URL}/${tenantId}/form`, + method: "get", + }); + }, + + /** 新增租户并初始化默认数据 */ + create(data: TenantCreateForm) { + return request({ + url: `${TENANT_BASE_URL}`, + method: "post", + data, + }); + }, + + /** 修改租户 */ + update(tenantId: string, data: TenantForm) { + return request({ + url: `${TENANT_BASE_URL}/${tenantId}`, + method: "put", + data, + }); + }, + + /** 删除租户(批量) */ + deleteByIds(ids: string) { + return request({ + url: `${TENANT_BASE_URL}/${ids}`, + method: "delete", + }); + }, + + /** 修改租户状态 */ + updateStatus(tenantId: string, status: number) { + return request({ + url: `${TENANT_BASE_URL}/${tenantId}/status`, + method: "put", + params: { status }, + }); + }, }; export default TenantAPI; diff --git a/src/api/system/user.ts b/src/api/system/user.ts index 2a26b34c..59a7cbca 100644 --- a/src/api/system/user.ts +++ b/src/api/system/user.ts @@ -1,4 +1,15 @@ import request from "@/utils/request"; +import type { + UserInfo, + UserPageQuery, + UserPageVo, + UserForm, + UserProfileVo, + UserProfileForm, + PasswordChangeForm, + MobileUpdateForm, + EmailUpdateForm, +} from "@/types/api"; const USER_BASE_URL = "/api/v1/users"; @@ -21,7 +32,7 @@ const UserAPI = { * @param queryParams 查询参数 */ getPage(queryParams: UserPageQuery) { - return request>({ + return request>({ url: `${USER_BASE_URL}/page`, method: "get", params: queryParams, @@ -139,7 +150,7 @@ const UserAPI = { /** 获取个人中心用户信息 */ getProfile() { - return request({ + return request({ url: `${USER_BASE_URL}/profile`, method: "get", }); @@ -211,174 +222,3 @@ const UserAPI = { }; export default UserAPI; - -/** 登录用户信息 */ -export interface UserInfo { - /** 用户ID */ - userId?: string; - - /** 用户名 */ - username?: string; - - /** 昵称 */ - nickname?: string; - - /** 头像URL */ - avatar?: string; - - /** 角色 */ - roles: string[]; - - /** 权限 */ - perms: string[]; -} - -/** - * 用户分页查询对象 - */ -export interface UserPageQuery extends PageQuery { - /** 搜索关键字 */ - keywords?: string; - - /** 用户状态 */ - status?: number; - - /** 部门ID */ - deptId?: string; - - /** 开始时间 */ - createTime?: [string, string]; -} - -/** 用户分页对象 */ -export interface UserPageVO { - /** 用户ID */ - id: string; - /** 用户头像URL */ - avatar?: string; - /** 创建时间 */ - createTime?: Date; - /** 部门名称 */ - deptName?: string; - /** 用户邮箱 */ - email?: string; - /** 性别 */ - gender?: number; - /** 手机号 */ - mobile?: string; - /** 用户昵称 */ - nickname?: string; - /** 角色名称,多个使用英文逗号(,)分割 */ - roleNames?: string; - /** 用户状态(1:启用;0:禁用) */ - status?: number; - /** 用户名 */ - username?: string; -} - -/** 用户表单类型 */ -export interface UserForm { - /** 用户ID */ - id?: string; - /** 用户头像 */ - avatar?: string; - /** 部门ID */ - deptId?: string; - /** 邮箱 */ - email?: string; - /** 性别 */ - gender?: number; - /** 手机号 */ - mobile?: string; - /** 昵称 */ - nickname?: string; - /** 角色ID集合 */ - roleIds?: number[]; - /** 用户状态(1:正常;0:禁用) */ - status?: number; - /** 用户名 */ - username?: string; -} - -/** 个人中心用户信息 */ -export interface UserProfileVO { - /** 用户ID */ - id?: string; - - /** 用户名 */ - username?: string; - - /** 昵称 */ - nickname?: string; - - /** 头像URL */ - avatar?: string; - - /** 性别 */ - gender?: number; - - /** 手机号 */ - mobile?: string; - - /** 邮箱 */ - email?: string; - - /** 部门名称 */ - deptName?: string; - - /** 角色名称,多个使用英文逗号(,)分割 */ - roleNames?: string; - - /** 创建时间 */ - createTime?: Date; -} - -/** 个人中心用户信息表单 */ -export interface UserProfileForm { - /** 用户ID */ - id?: string; - - /** 用户名 */ - username?: string; - - /** 昵称 */ - nickname?: string; - - /** 头像URL */ - avatar?: string; - - /** 性别 */ - gender?: number; - - /** 手机号 */ - mobile?: string; - - /** 邮箱 */ - email?: string; -} - -/** 修改密码表单 */ -export interface PasswordChangeForm { - /** 原密码 */ - oldPassword?: string; - /** 新密码 */ - newPassword?: string; - /** 确认新密码 */ - confirmPassword?: string; -} - -/** 修改手机表单 */ -export interface MobileUpdateForm { - /** 手机号 */ - mobile?: string; - /** 验证码 */ - code?: string; -} - -/** 修改邮箱表单 */ -export interface EmailUpdateForm { - /** 邮箱 */ - email?: string; - /** 验证码 */ - code?: string; -} diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 00000000..952c4118 --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1 @@ +export * from "@/types/api"; diff --git a/src/assets/icons/ai.svg b/src/assets/icons/ai.svg index c3a1c1a3..2ad5041a 100644 --- a/src/assets/icons/ai.svg +++ b/src/assets/icons/ai.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/components/AiAssistant/index.vue b/src/components/AiAssistant/index.vue index b5b0f548..12a42be6 100644 --- a/src/components/AiAssistant/index.vue +++ b/src/components/AiAssistant/index.vue @@ -3,16 +3,28 @@
+ +
+ AI +
+ - - diff --git a/src/components/TenantSelectDialog/index.vue b/src/components/TenantSelectDialog/index.vue deleted file mode 100644 index 18467040..00000000 --- a/src/components/TenantSelectDialog/index.vue +++ /dev/null @@ -1,187 +0,0 @@ - - - - - diff --git a/src/components/TenantSelector/index.vue b/src/components/TenantSelector/index.vue deleted file mode 100644 index 2aa545c5..00000000 --- a/src/components/TenantSelector/index.vue +++ /dev/null @@ -1,124 +0,0 @@ - - - - - diff --git a/src/components/TenantSwitcher/index.vue b/src/components/TenantSwitcher/index.vue index 77fed027..1805c39a 100644 --- a/src/components/TenantSwitcher/index.vue +++ b/src/components/TenantSwitcher/index.vue @@ -1,17 +1,33 @@ + + diff --git a/src/config/index.ts b/src/config/index.ts index e13b8e84..5414b628 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,5 +1,3 @@ /** * 配置统一导出 */ - -export * from "./storage"; diff --git a/src/config/storage.ts b/src/config/storage.ts deleted file mode 100644 index d816cf10..00000000 --- a/src/config/storage.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 本地存储键名配置 - * - * @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/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..363b1220 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,108 @@ +/** + * 常量统一导出 + * + * @description + * 包含应用中所有的常量定义 + */ + +/** + * 超级管理员角色标识 + * + * @description + * 拥有系统最高权限,可以访问所有资源 + */ +export const ROLE_ROOT = "ROOT"; + +/** + * 应用存储前缀 + */ +export const APP_PREFIX = "vea"; + +/** + * 存储命名空间 + */ +const NAMESPACES = { + AUTH: "auth", + TENANT: "tenant", + SYSTEM: "system", + UI: "ui", + APP: "app", +} as const; + +/** + * 存储键名常量 + */ +export const STORAGE_KEYS = { + // ===== 认证相关 ===== + ACCESS_TOKEN: `${APP_PREFIX}:${NAMESPACES.AUTH}:access_token`, + REFRESH_TOKEN: `${APP_PREFIX}:${NAMESPACES.AUTH}:refresh_token`, + REMEMBER_ME: `${APP_PREFIX}:${NAMESPACES.AUTH}:remember_me`, + + // ===== 租户相关 ===== + TENANT_ID: `${APP_PREFIX}:${NAMESPACES.TENANT}:id`, + TENANT_INFO: `${APP_PREFIX}:${NAMESPACES.TENANT}:info`, + + // ===== 系统相关 ===== + DICT_CACHE: `${APP_PREFIX}:${NAMESPACES.SYSTEM}:dict_cache`, + + // ===== UI 设置 ===== + SHOW_TAGS_VIEW: `${APP_PREFIX}:${NAMESPACES.UI}:show_tags_view`, + SHOW_APP_LOGO: `${APP_PREFIX}:${NAMESPACES.UI}:show_app_logo`, + SHOW_WATERMARK: `${APP_PREFIX}:${NAMESPACES.UI}:show_watermark`, + ENABLE_AI_ASSISTANT: `${APP_PREFIX}:${NAMESPACES.UI}:enable_ai_assistant`, + LAYOUT: `${APP_PREFIX}:${NAMESPACES.UI}:layout`, + SIDEBAR_COLOR_SCHEME: `${APP_PREFIX}:${NAMESPACES.UI}:sidebar_color_scheme`, + THEME: `${APP_PREFIX}:${NAMESPACES.UI}:theme`, + THEME_COLOR: `${APP_PREFIX}:${NAMESPACES.UI}:theme_color`, + + // ===== 应用状态 ===== + DEVICE: `${APP_PREFIX}:${NAMESPACES.APP}:device`, + SIZE: `${APP_PREFIX}:${NAMESPACES.APP}:size`, + LANGUAGE: `${APP_PREFIX}:${NAMESPACES.APP}:language`, + SIDEBAR_STATUS: `${APP_PREFIX}:${NAMESPACES.APP}:sidebar_status`, + ACTIVE_TOP_MENU_PATH: `${APP_PREFIX}:${NAMESPACES.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; + +/** + * UI设置相关键名 + */ +export const UI_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, + LAYOUT: STORAGE_KEYS.LAYOUT, + SIDEBAR_COLOR_SCHEME: STORAGE_KEYS.SIDEBAR_COLOR_SCHEME, + THEME: STORAGE_KEYS.THEME, + THEME_COLOR: STORAGE_KEYS.THEME_COLOR, +} 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 type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS]; diff --git a/src/directives/permission/index.ts b/src/directives/permission/index.ts index 5661aa04..bd462078 100644 --- a/src/directives/permission/index.ts +++ b/src/directives/permission/index.ts @@ -1,7 +1,7 @@ import type { Directive, DirectiveBinding } from "vue"; import { useUserStore } from "@/store"; -import { ROLE_ROOT } from "@/enums"; +import { ROLE_ROOT } from "@/constants"; /** * 按钮权限 diff --git a/src/enums/business.ts b/src/enums/business.ts index 04a8447e..b59fcb14 100644 --- a/src/enums/business.ts +++ b/src/enums/business.ts @@ -25,23 +25,3 @@ export enum UserGender { /** 女 */ FEMALE = 2, } - -/** - * 超级管理员角色标识 - * - * @description - * 拥有系统最高权限,可以访问所有资源 - */ -export const ROLE_ROOT = "ROOT"; - -/** - * 角色类型枚举 - */ -export enum RoleType { - /** 超级管理员 */ - ROOT = "ROOT", - /** 管理员 */ - ADMIN = "ADMIN", - /** 普通用户 */ - USER = "USER", -} diff --git a/src/layouts/components/NavBar/components/NavbarActions.vue b/src/layouts/components/NavBar/components/NavbarActions.vue index fa008b78..c71c1824 100644 --- a/src/layouts/components/NavBar/components/NavbarActions.vue +++ b/src/layouts/components/NavBar/components/NavbarActions.vue @@ -279,13 +279,18 @@ function handleSettingsClick() { } // 租户选择器在白色文字模式下的样式 - :deep(.tenant-select) { + ::v-deep(.tenant-switcher__trigger) { color: rgba(255, 255, 255, 0.85); - - &:hover { - color: #fff; - background: rgba(255, 255, 255, 0.1); - } + } + ::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) { + color: rgba(255, 255, 255, 0.85); + } + ::v-deep(.tenant-switcher__trigger:hover) { + color: #fff; + background: rgba(255, 255, 255, 0.1); + } + ::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) { + color: #fff; } } @@ -310,18 +315,23 @@ function handleSettingsClick() { } // 租户选择器在深色文字模式下的样式 - :deep(.tenant-select) { + ::v-deep(.tenant-switcher__trigger) { color: var(--el-text-color-regular) !important; - - &:hover { - color: var(--el-color-primary) !important; - background: rgba(0, 0, 0, 0.04); - } + } + ::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) { + color: var(--el-text-color-regular) !important; + } + ::v-deep(.tenant-switcher__trigger:hover) { + color: var(--el-color-primary) !important; + background: rgba(0, 0, 0, 0.04); + } + ::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) { + color: var(--el-color-primary) !important; } } // 确保下拉菜单中的图标不受影响 -:deep(.el-dropdown-menu) { +::v-deep(.el-dropdown-menu) { [class^="i-svg:"] { color: var(--el-text-color-regular) !important; diff --git a/src/store/modules/app-store.ts b/src/store/modules/app-store.ts index b52f7d94..940e9388 100644 --- a/src/store/modules/app-store.ts +++ b/src/store/modules/app-store.ts @@ -5,7 +5,7 @@ import zhCn from "element-plus/es/locale/lang/zh-cn"; import en from "element-plus/es/locale/lang/en"; import { store } from "@/store"; import { DeviceEnum, SidebarStatus } from "@/enums"; -import { STORAGE_KEYS } from "@/config/storage"; +import { STORAGE_KEYS } from "@/constants"; export const useAppStore = defineStore("app", () => { // 设备类型 diff --git a/src/store/modules/dict-store.ts b/src/store/modules/dict-store.ts index 4ea53640..1e34488e 100644 --- a/src/store/modules/dict-store.ts +++ b/src/store/modules/dict-store.ts @@ -1,6 +1,7 @@ import { store } from "@/store"; -import DictAPI, { type DictItemOption } from "@/api/system/dict"; -import { STORAGE_KEYS } from "@/config/storage"; +import DictAPI from "@/api/system/dict"; +import type { DictItemOption } from "@/types/api"; +import { STORAGE_KEYS } from "@/constants"; export const useDictStore = defineStore("dict", () => { // 字典数据缓存 diff --git a/src/store/modules/settings-store.ts b/src/store/modules/settings-store.ts index d259468c..0a695900 100644 --- a/src/store/modules/settings-store.ts +++ b/src/store/modules/settings-store.ts @@ -2,7 +2,7 @@ import { defaultSettings } from "@/settings"; import { SidebarColor, ThemeMode } from "@/enums"; import type { LayoutMode } from "@/enums"; import { applyTheme, generateThemeColors, toggleDarkMode, toggleSidebarColor } from "@/utils/theme"; -import { STORAGE_KEYS } from "@/config/storage"; +import { STORAGE_KEYS } from "@/constants"; // 🎯 设置项类型定义 interface SettingsState { diff --git a/src/store/modules/tenant-store.ts b/src/store/modules/tenant-store.ts index ddd7500b..051c527c 100644 --- a/src/store/modules/tenant-store.ts +++ b/src/store/modules/tenant-store.ts @@ -1,6 +1,7 @@ import { store } from "@/store"; -import TenantAPI, { type TenantInfo } from "@/api/system/tenant"; -import { STORAGE_KEYS } from "@/config/storage"; +import TenantAPI from "@/api/system/tenant"; +import type { TenantInfo } from "@/types/api"; +import { STORAGE_KEYS } from "@/constants"; /** * 租户 Store @@ -63,28 +64,52 @@ export const useTenantStore = defineStore("tenant", () => { * 此方法由路由守卫调用,仅在启用多租户时执行 */ async function loadTenant() { + restoreTenant(); + // 1. 获取租户列表 await fetchTenantList(); - // 2. 如果已有租户列表且未设置当前租户 - if (tenantList.value.length > 0 && !currentTenantId.value) { - try { - // 尝试从后端获取当前租户 - const currentTenantInfo = await TenantAPI.getCurrentTenant(); - if (currentTenantInfo) { - setCurrentTenant(currentTenantInfo); - return; + // 2. 校验本地恢复的租户是否仍然可用(避免 tenantId 不在列表导致无默认选中) + if ( + currentTenantId.value && + tenantList.value.length > 0 && + !tenantList.value.some((t) => t.id === currentTenantId.value) + ) { + console.debug("[Tenant] 本地租户已不可用,清除并重新选择:", currentTenantId.value); + currentTenantId.value = null; + currentTenant.value = null; + localStorage.removeItem(STORAGE_KEYS.TENANT_ID); + localStorage.removeItem(STORAGE_KEYS.TENANT_INFO); + } + + // 3. 如果已有租户列表,则保证一定有一个默认租户被选中 + if (tenantList.value.length > 0) { + // 3.1 优先后端当前租户 + if (!currentTenantId.value) { + try { + const currentTenantInfo = await TenantAPI.getCurrentTenant(); + if (currentTenantInfo) { + setCurrentTenant(currentTenantInfo); + return; + } + } catch (error) { + console.debug("[Tenant] 获取当前租户失败,尝试本地/默认选择:", error); } - } catch (error) { - console.debug("[Tenant] 获取当前租户失败,尝试自动选择:", error); } - // 3. 如果只有一个租户,自动选中 - if (tenantList.value.length === 1) { + // 3.2 本地已有 tenantId,但 currentTenant 为空时,从列表补全 tenantInfo(保持展示名称一致) + if (currentTenantId.value && !currentTenant.value) { + const matched = tenantList.value.find((t) => t.id === currentTenantId.value); + if (matched) { + setCurrentTenant(matched); + return; + } + } + + // 3.3 兜底:默认选中第一个(即使有多个租户,也保证 TenantSwitcher 有默认选中) + if (!currentTenantId.value) { setCurrentTenant(tenantList.value[0]); - console.debug("[Tenant] 自动选中唯一租户:", tenantList.value[0].name); - } else { - console.debug("[Tenant] 多个租户可用,等待用户选择"); + console.debug("[Tenant] 默认选中第一个租户:", tenantList.value[0].name); } } } diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 995d4dfe..543038d5 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -76,16 +76,3 @@ $sidebar-width: 210px; // 侧边栏宽度 $sidebar-width-collapsed: 54px; // 侧边栏收缩宽度 $navbar-height: 50px; // 导航栏高度 $tags-view-height: 34px; // TagsView 高度 - -/* 供 JS/TS 侧按需读取的变量导出 */ -/* stylelint-disable property-no-unknown */ -:export { - sidebar-width: $sidebar-width; - navbar-height: $navbar-height; - tags-view-height: $tags-view-height; - menu-background: $menu-background; - menu-text: $menu-text; - menu-active-text: $menu-active-text; - menu-hover: $menu-hover; -} -/* stylelint-enable property-no-unknown */ diff --git a/src/types/api/ai.ts b/src/types/api/ai.ts new file mode 100644 index 00000000..46f58ce3 --- /dev/null +++ b/src/types/api/ai.ts @@ -0,0 +1,147 @@ +/** + * AI 模块类型定义 + */ + +/** AI命令请求参数 */ +export interface AiCommandRequest { + /** 用户输入的自然语言命令 */ + command: string; + /** 当前页面路由(用于上下文) */ + currentRoute?: string; + /** 当前激活的组件名称 */ + currentComponent?: string; + /** 额外上下文信息 */ + context?: Record; +} + +/** 函数调用参数 */ +export interface FunctionCall { + /** 函数名称 */ + name: string; + /** 函数描述 */ + description?: string; + /** 参数对象 */ + arguments: Record; +} + +/** AI命令解析响应 */ +export interface AiCommandResponse { + /** 解析日志ID(用于关联执行记录) */ + parseLogId?: string; + /** 是否成功解析 */ + success: boolean; + /** 解析后的函数调用列表 */ + functionCalls: FunctionCall[]; + /** AI的理解和说明 */ + explanation?: string; + /** 置信度(0-1) */ + confidence?: number; + /** 错误信息 */ + error?: string; + /** 原始LLM响应(用于调试) */ + rawResponse?: string; +} + +/** AI命令执行请求 */ +export interface AiExecuteRequest { + /** 关联的解析日志ID */ + parseLogId?: string; + /** 原始命令(用于审计) */ + originalCommand?: string; + /** 要执行的函数调用 */ + functionCall: FunctionCall; + /** 确认模式:auto=自动执行, manual=需要用户确认 */ + confirmMode?: "auto" | "manual"; + /** 用户确认标志 */ + userConfirmed?: boolean; + /** 幂等性令牌(防止重复执行) */ + idempotencyKey?: string; + /** 当前页面路由 */ + currentRoute?: string; +} + +/** AI命令执行响应 */ +export interface AiExecuteResponse { + /** 是否执行成功 */ + success: boolean; + /** 执行结果数据 */ + data?: any; + /** 执行结果说明 */ + message?: string; + /** 影响的记录数 */ + affectedRows?: number; + /** 错误信息 */ + error?: string; + /** 记录ID(用于追踪) */ + recordId?: string; + /** 需要用户确认 */ + requiresConfirmation?: boolean; + /** 确认提示信息 */ + confirmationPrompt?: string; +} + +/** AI命令记录分页查询参数 */ +export interface AiCommandRecordPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; + /** 执行状态 */ + executeStatus?: number; + /** 解析状态 */ + parseStatus?: number; + /** 用户ID */ + userId?: number; + /** AI提供商 */ + aiProvider?: string; + /** AI模型 */ + aiModel?: string; + /** 函数名称 */ + functionName?: string; + /** 创建时间 */ + createTime?: [string, string]; +} + +/** AI命令记录视图对象 */ +export interface AiCommandRecordVo { + /** 记录ID */ + id: string; + /** 用户ID */ + userId: number; + /** 用户名 */ + username: string; + /** 原始命令 */ + originalCommand: string; + /** AI提供商 */ + aiProvider?: string; + /** AI模型 */ + aiModel?: string; + /** 解析状态 */ + parseStatus?: number; + /** 函数调用列表 */ + functionCalls?: string; + /** 解析说明 */ + explanation?: string; + /** 置信度 */ + confidence?: number; + /** 解析错误信息 */ + parseErrorMessage?: string; + /** 输入Token数 */ + inputTokens?: number; + /** 输出Token数 */ + outputTokens?: number; + /** 解析耗时(毫秒) */ + parseDurationMs?: number; + /** 函数名称 */ + functionName?: string; + /** 函数参数 */ + functionArguments?: string; + /** 执行状态 */ + executeStatus?: number; + /** 执行错误信息 */ + executeErrorMessage?: string; + /** IP地址 */ + ipAddress?: string; + /** 创建时间 */ + createTime?: string; + /** 更新时间 */ + updateTime?: string; +} diff --git a/src/types/api/codegen.ts b/src/types/api/codegen.ts new file mode 100644 index 00000000..d37d4eec --- /dev/null +++ b/src/types/api/codegen.ts @@ -0,0 +1,99 @@ +/** + * CodeGen 代码生成类型定义 + */ + +/** 代码生成预览对象 */ +export interface GeneratorPreviewVo { + /** 文件生成路径 */ + path: string; + /** 文件名称 */ + fileName: string; + /** 文件内容 */ + content: string; +} + +/** 数据表分页查询参数 */ +export interface TablePageQuery extends PageQuery { + /** 搜索关键字(表名) */ + keywords?: string; +} + +/** 数据表分页对象 */ +export interface TablePageVo { + /** 表名称 */ + tableName: string; + /** 表描述 */ + tableComment: string; + /** 是否已配置(1:是;0:否) */ + isConfigured?: number; + /** 存储引擎 */ + engine: string; + /** 字符集排序规则 */ + tableCollation: string; + /** 创建时间 */ + createTime: string; +} + +/** 代码生成配置表单 */ +export interface GenConfigForm { + /** 主键 */ + id?: string; + /** 表名 */ + tableName?: string; + /** 业务名 */ + businessName?: string; + /** 模块名 */ + moduleName?: string; + /** 包名 */ + packageName?: string; + /** 实体名 */ + entityName?: string; + /** 作者 */ + author?: string; + /** 上级菜单 */ + parentMenuId?: string; + /** 后端应用名 */ + backendAppName?: string; + /** 前端应用名 */ + frontendAppName?: string; + /** 字段配置列表 */ + fieldConfigs?: FieldConfig[]; + /** 页面类型 classic|curd */ + pageType?: "classic" | "curd"; + /** 要移除的表前缀,如 sys_ */ + removeTablePrefix?: string; +} + +/** 字段配置 */ +export interface FieldConfig { + /** 主键 */ + id?: string; + /** 列名 */ + columnName?: string; + /** 列类型 */ + columnType?: string; + /** 字段名 */ + fieldName?: string; + /** 字段类型 */ + fieldType?: string; + /** 字段描述 */ + fieldComment?: string; + /** 是否在列表显示 */ + isShowInList?: number; + /** 是否在表单显示 */ + isShowInForm?: number; + /** 是否在查询条件显示 */ + isShowInQuery?: number; + /** 是否必填 */ + isRequired?: number; + /** 表单类型 */ + formType?: number; + /** 查询类型 */ + queryType?: number; + /** 字段长度 */ + maxLength?: number; + /** 字段排序 */ + fieldSort?: number; + /** 字典类型 */ + dictType?: string; +} diff --git a/src/types/api/common.ts b/src/types/api/common.ts index d0bc7ad4..412bd132 100644 --- a/src/types/api/common.ts +++ b/src/types/api/common.ts @@ -2,31 +2,50 @@ * 通用 API 类型定义 */ +/** 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[]; } +/** Excel 导入结果 */ export interface ExcelResult { + /** 响应码 */ code: string; + /** 无效数据数量 */ invalidCount: number; + /** 有效数据数量 */ validCount: number; + /** 错误信息列表 */ messageList: string[]; } diff --git a/src/types/api/config.ts b/src/types/api/config.ts new file mode 100644 index 00000000..67f4d745 --- /dev/null +++ b/src/types/api/config.ts @@ -0,0 +1,35 @@ +/** + * Config 配置类型定义 + */ + +/** 配置分页查询参数 */ +export interface ConfigPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; +} + +/** 配置表单对象 */ +export interface ConfigForm { + /** 配置ID */ + id?: string; + /** 配置名称 */ + configName?: string; + /** 配置键 */ + configKey?: string; + /** 配置值 */ + configValue?: string; + /** 备注 */ + remark?: string; +} + +/** 配置分页对象 */ +export interface ConfigPageVo { + /** 配置ID */ + id?: string; + /** 配置名称 */ + configName?: string; + /** 配置键 */ + configKey?: string; + /** 配置值 */ + configValue?: string; +} diff --git a/src/types/api/dept.ts b/src/types/api/dept.ts new file mode 100644 index 00000000..0af6773b --- /dev/null +++ b/src/types/api/dept.ts @@ -0,0 +1,49 @@ +/** + * Dept 部门类型定义 + */ + +/** 部门查询参数 */ +export interface DeptQuery { + /** 搜索关键字 */ + keywords?: string; + /** 状态 */ + status?: number; +} + +/** 部门视图对象 */ +export interface DeptVo { + /** 子部门 */ + children?: DeptVo[]; + /** 创建时间 */ + createTime?: Date; + /** 部门ID */ + id?: string; + /** 部门名称 */ + name?: string; + /** 父部门ID */ + parentId?: string; + /** 排序 */ + sort?: number; + /** 状态(1:启用;0:禁用) */ + status?: number; + /** 父节点ID路径 */ + treePath?: string; + /** 修改时间 */ + updateTime?: Date; +} + +/** 部门表单对象 */ +export interface DeptForm { + /** 部门ID */ + id?: string; + /** 部门名称 */ + name?: string; + /** 部门编号 */ + code?: string; + /** 父部门ID */ + parentId?: string; + /** 排序 */ + sort?: number; + /** 状态(1:启用;0:禁用) */ + status?: number; +} diff --git a/src/types/api/dict.ts b/src/types/api/dict.ts new file mode 100644 index 00000000..4c2ef1ba --- /dev/null +++ b/src/types/api/dict.ts @@ -0,0 +1,90 @@ +/** + * Dict 字典类型定义 + */ + +/** 字典分页查询参数 */ + +export interface DictPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; + /** 状态(1:启用;0:禁用) */ + status?: number; +} + +/** 字典分页对象 */ +export interface DictPageVo { + /** 字典ID */ + id: string; + /** 字典名称 */ + name: string; + /** 字典编码 */ + dictCode: string; + /** 状态(1:启用;0:禁用) */ + status: number; +} + +/** 字典表单对象 */ +export interface DictForm { + /** 字典ID */ + id?: string; + /** 字典名称 */ + name?: string; + /** 字典编码 */ + dictCode?: string; + /** 状态(1:启用;0:禁用) */ + status?: number; + /** 备注 */ + remark?: string; +} + +/** 字典项分页查询参数 */ +export interface DictItemPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; + /** 字典编码 */ + dictCode?: string; +} + +/** 字典项分页对象 */ +export interface DictItemPageVo { + /** 字典项ID */ + id: string; + /** 字典编码 */ + dictCode: string; + /** 字典项标签 */ + label: string; + /** 字典项值 */ + value: string; + /** 状态(1:启用;0:禁用) */ + status: number; + /** 排序 */ + sort?: number; +} + +/** 字典项表单对象 */ +export interface DictItemForm { + /** 字典项ID */ + id?: string; + /** 字典编码 */ + dictCode?: string; + /** 字典项标签 */ + label?: string; + /** 字典项值 */ + value?: string; + /** 状态(1:启用;0:禁用) */ + status?: number; + /** 排序 */ + sort?: number; + /** 标签类型 */ + tagType?: "success" | "warning" | "info" | "primary" | "danger" | ""; +} + +/** 字典项选项 */ +export interface DictItemOption { + /** 字典项值 */ + value: number | string; + /** 字典项标签 */ + label: string; + /** 标签类型 */ + tagType?: "success" | "warning" | "info" | "primary" | "danger" | ""; +} diff --git a/src/types/api/file.ts b/src/types/api/file.ts new file mode 100644 index 00000000..02a3d095 --- /dev/null +++ b/src/types/api/file.ts @@ -0,0 +1,11 @@ +/** + * File 文件上传类型定义 + */ + +/** 文件信息 */ +export interface FileInfo { + /** 文件名称 */ + name: string; + /** 文件URL */ + url: string; +} diff --git a/src/types/api/index.ts b/src/types/api/index.ts index 7f04ac8d..63d5208d 100644 --- a/src/types/api/index.ts +++ b/src/types/api/index.ts @@ -2,5 +2,22 @@ * API 类型统一导出 */ -export * from "./common"; export * from "./auth"; +export * from "./common"; + +// System 模块 +export * from "./user"; +export * from "./role"; +export * from "./menu"; +export * from "./dept"; +export * from "./dict"; +export * from "./config"; +export * from "./log"; +export * from "./statistics"; +export * from "./notice"; +export * from "./tenant"; + +// 其他模块 +export * from "./ai"; +export * from "./file"; +export * from "./codegen"; diff --git a/src/types/api/log.ts b/src/types/api/log.ts new file mode 100644 index 00000000..545072cf --- /dev/null +++ b/src/types/api/log.ts @@ -0,0 +1,37 @@ +/** + * Log 日志类型定义 + */ + +/** 日志分页查询参数 */ +export interface LogPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; + /** 操作时间 */ + createTime?: [string, string]; +} + +/** 日志分页对象 */ +export interface LogPageVo { + /** 日志ID */ + id: string; + /** 日志模块 */ + module: string; + /** 日志内容 */ + content: string; + /** 请求路径 */ + requestUri: string; + /** 请求方法 */ + method: string; + /** IP地址 */ + ip: string; + /** 地区 */ + region: string; + /** 浏览器 */ + browser: string; + /** 终端系统 */ + os: string; + /** 执行时间(毫秒) */ + executionTime: number; + /** 操作人 */ + operator: string; +} diff --git a/src/types/api/menu.ts b/src/types/api/menu.ts new file mode 100644 index 00000000..83a4d293 --- /dev/null +++ b/src/types/api/menu.ts @@ -0,0 +1,103 @@ +/** + * Menu 菜单类型定义 + */ + +/** 菜单查询参数 */ +export interface MenuQuery { + /** 搜索关键字 */ + keywords?: string; +} + +/** 菜单视图对象 */ +export interface MenuVo { + /** 子菜单 */ + children?: MenuVo[]; + /** 组件路径 */ + component?: string; + /** ICON */ + icon?: string; + /** 菜单ID */ + id?: string; + /** 菜单名称 */ + name?: string; + /** 父菜单ID */ + parentId?: string; + /** 路由路径 */ + path?: string; + /** 按钮权限标识 */ + perm?: string; + /** 跳转路径 */ + redirect?: string; + /** 菜单排序(数字越小排名越靠前) */ + sort?: number; + /** 菜单类型(C-目录 M-菜单 B-按钮) */ + type?: string; + /** 菜单是否可见(1:显示;0:隐藏) */ + visible?: number; +} + +/** 菜单表单对象 */ +export interface MenuForm { + /** 菜单ID */ + id?: string; + /** 父菜单ID */ + parentId?: string; + /** 菜单名称 */ + name?: string; + /** 菜单类型(C-目录 M-菜单 B-按钮) */ + type?: string; + /** 路由路径 */ + path?: string; + /** 跳转路径 */ + redirect?: string; + /** 组件路径 */ + component?: string; + /** ICON */ + icon?: string; + /** 排序 */ + sort?: number; + /** 菜单是否可见 */ + visible?: number; + /** 按钮权限标识 */ + perm?: string; +} + +/** 菜单选项 */ +export interface MenuOption { + key: string; + value: string; +} + +/** 路由对象 */ +export interface RouteVo { + /** 子路由列表 */ + children: RouteVo[]; + /** 组件路径 */ + component?: string; + /** 路由名称 */ + name?: string; + /** 路由路径 */ + path?: string; + /** 路由属性 */ + meta?: Meta; + /** 跳转链接 */ + redirect?: string; +} + +/** 路由属性 */ +export interface Meta { + /** 【目录】只有一个子路由是否始终显示 */ + alwaysShow?: boolean; + /** 是否隐藏(true-是 false-否) */ + hidden?: boolean; + /** ICON */ + icon?: string; + /** 【菜单】是否开启页面缓存 */ + keepAlive?: boolean; + /** 路由参数 */ + params?: Record; + /** 角色集合 */ + roles?: string[]; + /** 路由title */ + title?: string; +} diff --git a/src/types/api/notice.ts b/src/types/api/notice.ts new file mode 100644 index 00000000..534f1b9f --- /dev/null +++ b/src/types/api/notice.ts @@ -0,0 +1,71 @@ +/** + * Notice 通知类型定义 + */ + +/** 通知分页查询参数 */ +export interface NoticePageQuery extends PageQuery { + /** 通知标题 */ + title?: string; + /** 发布状态(0:草稿;1:已发布;2:已撤回) */ + publishStatus?: number; + /** 是否已读(1:是;0:否) */ + isRead?: number; +} + +/** 通知表单对象 */ +export interface NoticeForm { + /** 通知ID */ + id?: string; + /** 通知标题 */ + title?: string; + /** 通知内容 */ + content?: string; + /** 通知类型 */ + type?: number; + /** 通知等级 */ + level?: string; + /** 发布状态(0:草稿;1:已发布;2:已撤回) */ + publishStatus?: number; + /** 目标用户ID(多个以英文逗号(,)分割) */ + targetUserIds?: string; +} + +/** 通知分页对象 */ +export interface NoticePageVo { + /** 通知ID */ + id: string; + /** 通知标题 */ + title: string; + /** 通知内容 */ + content: string; + /** 通知类型 */ + type: number; + /** 通知等级 */ + level: string; + /** 发布状态 */ + publishStatus: number; + /** 是否已读 */ + isRead: number; + /** 发布时间 */ + publishTime?: Date; + /** 撤回时间 */ + revokeTime?: Date; +} + +/** 通知详情对象 */ +export interface NoticeDetailVo { + /** 通知ID */ + id?: string; + /** 通知标题 */ + title?: string; + /** 通知内容 */ + content?: string; + /** 通知类型 */ + type?: number; + /** 通知等级 */ + level?: string; + /** 发布状态 */ + publishStatus?: number; + /** 目标用户ID */ + targetUserIds?: string; +} diff --git a/src/types/api/role.ts b/src/types/api/role.ts new file mode 100644 index 00000000..17e224ac --- /dev/null +++ b/src/types/api/role.ts @@ -0,0 +1,45 @@ +/** + * Role 角色类型定义 + */ + +/** 角色分页查询参数 */ +export interface RolePageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; +} + +/** 角色分页对象 */ +export interface RolePageVo { + /** 角色ID */ + id?: string; + /** 角色编码 */ + code?: string; + /** 角色名称 */ + name?: string; + /** 排序 */ + sort?: number; + /** 角色状态 */ + status?: number; + /** 创建时间 */ + createTime?: Date; + /** 修改时间 */ + updateTime?: Date; +} + +/** 角色表单对象 */ +export interface RoleForm { + /** 角色ID */ + id?: string; + /** 角色编码 */ + code?: string; + /** 角色名称 */ + name?: string; + /** 排序 */ + sort?: number; + /** 数据权限 */ + dataScope?: number; + /** 角色状态 */ + status?: number; + /** 备注 */ + remark?: string; +} diff --git a/src/types/api/statistics.ts b/src/types/api/statistics.ts new file mode 100644 index 00000000..3b5a6f2a --- /dev/null +++ b/src/types/api/statistics.ts @@ -0,0 +1,39 @@ +/** + * Statistics 统计类型定义 + */ + +/** 访问趋势查询参数 */ +export interface VisitTrendQuery { + /** 开始日期 */ + startDate: string; + /** 结束日期 */ + endDate: string; +} + +/** 访问趋势视图对象 */ +export interface VisitTrendVo { + /** 日期列表 */ + dates: string[]; + /** 浏览量(PV)列表 */ + pvList: number[]; + /** 访客数(UV)列表 */ + uvList: number[]; + /** IP数列表 */ + ipList: number[]; +} + +/** 访问量统计视图对象 */ +export interface VisitStatsVo { + /** 今日独立访客数(UV) */ + todayUvCount: number; + /** 累计独立访客数(UV) */ + totalUvCount: number; + /** 独立访客增长率 */ + uvGrowthRate: number; + /** 今日页面浏览量(PV) */ + todayPvCount: number; + /** 累计页面浏览量(PV) */ + totalPvCount: number; + /** 页面浏览量增长率 */ + pvGrowthRate: number; +} diff --git a/src/types/api/tenant.ts b/src/types/api/tenant.ts new file mode 100644 index 00000000..d9c46bc2 --- /dev/null +++ b/src/types/api/tenant.ts @@ -0,0 +1,79 @@ +/** + * Tenant 租户类型定义 + */ + +import type { PageQuery } from "./common"; + +/** 租户信息 */ +export interface TenantInfo { + /** 租户ID */ + id: number; + /** 租户名称 */ + name: string; + /** 租户域名 */ + domain?: string; +} + +/** 租户分页查询参数 */ +export interface TenantPageQuery extends PageQuery { + /** 关键字(租户名称/租户编码/域名) */ + keywords?: string; + /** 租户状态(1-正常 0-禁用) */ + status?: number; +} + +/** 租户分页对象 */ +export interface TenantPageVo { + id?: string; + name?: string; + code?: string; + contactName?: string; + contactPhone?: string; + contactEmail?: string; + domain?: string; + logo?: string; + status?: number; + remark?: string; + expireTime?: string; + createTime?: string; + updateTime?: string; +} + +/** 租户表单对象(编辑) */ +export interface TenantForm { + id?: string; + name?: string; + code?: string; + contactName?: string; + contactPhone?: string; + contactEmail?: string; + domain?: string; + logo?: string; + status?: number; + remark?: string; + expireTime?: string; +} + +/** 新增租户表单对象 */ +export interface TenantCreateForm { + name?: string; + code?: string; + contactName?: string; + contactPhone?: string; + contactEmail?: string; + domain?: string; + logo?: string; + remark?: string; + expireTime?: string; + adminUsername?: string; +} + +/** 新增租户结果 */ +export interface TenantCreateResultVo { + tenantId?: string; + tenantCode?: string; + tenantName?: string; + adminUsername?: string; + adminInitialPassword?: string; + adminRoleCode?: string; +} diff --git a/src/types/api/user.ts b/src/types/api/user.ts new file mode 100644 index 00000000..83e78182 --- /dev/null +++ b/src/types/api/user.ts @@ -0,0 +1,149 @@ +/** + * User 用户类型定义 + */ + +/** 登录用户信息 */ +export interface UserInfo { + /** 用户ID */ + userId?: string; + /** 用户名 */ + username?: string; + /** 用户昵称 */ + nickname?: string; + /** 头像URL */ + avatar?: string; + /** 角色集合 */ + roles: string[]; + /** 权限集合 */ + perms: string[]; +} + +/** 用户分页查询参数 */ +export interface UserPageQuery extends PageQuery { + /** 搜索关键字 */ + keywords?: string; + /** 用户状态 */ + status?: number; + /** 部门ID */ + deptId?: string; + /** 创建时间 */ + createTime?: [string, string]; +} + +/** 用户分页对象 */ +export interface UserPageVo { + /** 用户ID */ + id: string; + /** 用户头像地址 */ + avatar?: string; + /** 创建时间 */ + createTime?: Date; + /** 部门名称 */ + deptName?: string; + /** 用户邮箱 */ + email?: string; + /** 性别 */ + gender?: number; + /** 手机号 */ + mobile?: string; + /** 用户昵称 */ + nickname?: string; + /** 角色名称,多个使用英文逗号(,)分割 */ + roleNames?: string; + /** 用户状态(1:启用;0:禁用) */ + status?: number; + /** 用户名 */ + username?: string; +} + +/** 用户表单对象 */ +export interface UserForm { + /** 用户ID */ + id?: string; + /** 用户头像 */ + avatar?: string; + /** 部门ID */ + deptId?: string; + /** 用户邮箱 */ + email?: string; + /** 性别 */ + gender?: number; + /** 手机号 */ + mobile?: string; + /** 用户昵称 */ + nickname?: string; + /** 角色ID集合 */ + roleIds?: number[]; + /** 用户状态(1:正常;0:禁用) */ + status?: number; + /** 用户名 */ + username?: string; +} + +/** 个人中心用户信息 */ +export interface UserProfileVo { + /** 用户ID */ + id?: string; + /** 用户名 */ + username?: string; + /** 用户昵称 */ + nickname?: string; + /** 头像URL */ + avatar?: string; + /** 性别 */ + gender?: number; + /** 手机号 */ + mobile?: string; + /** 邮箱 */ + email?: string; + /** 部门名称 */ + deptName?: string; + /** 角色名称 */ + roleNames?: string; + /** 创建时间 */ + createTime?: Date; +} + +/** 个人中心用户信息表单 */ +export interface UserProfileForm { + /** 用户ID */ + id?: string; + /** 用户名 */ + username?: string; + /** 用户昵称 */ + nickname?: string; + /** 头像URL */ + avatar?: string; + /** 性别 */ + gender?: number; + /** 手机号 */ + mobile?: string; + /** 邮箱 */ + email?: string; +} + +/** 修改密码表单 */ +export interface PasswordChangeForm { + /** 原密码 */ + oldPassword?: string; + /** 新密码 */ + newPassword?: string; + /** 确认新密码 */ + confirmPassword?: string; +} + +/** 修改手机表单 */ +export interface MobileUpdateForm { + /** 手机号 */ + mobile?: string; + /** 验证码 */ + code?: string; +} + +/** 修改邮箱表单 */ +export interface EmailUpdateForm { + /** 邮箱 */ + email?: string; + /** 验证码 */ + code?: string; +} diff --git a/src/utils/auth.ts b/src/utils/auth.ts index fd522b1b..0d27dd55 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,6 +1,6 @@ import { Storage } from "./storage"; -import { AUTH_KEYS } from "@/config/storage"; -import { ROLE_ROOT } from "@/enums"; +import { AUTH_KEYS } from "@/constants"; +import { ROLE_ROOT } from "@/constants"; import { useUserStoreHook } from "@/store/modules/user-store"; import router from "@/router"; diff --git a/src/utils/request.ts b/src/utils/request.ts index 57905b5c..4a56995e 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -2,6 +2,7 @@ import axios, { type InternalAxiosRequestConfig, type AxiosResponse } from "axio import qs from "qs"; import { ApiCodeEnum } from "@/enums/api"; import { AuthStorage, redirectToLogin } from "@/utils/auth"; +import { STORAGE_KEYS } from "@/constants"; import { useTokenRefresh } from "@/composables/auth/useTokenRefresh"; import { authConfig } from "@/settings"; @@ -28,8 +29,14 @@ httpRequest.interceptors.request.use( // 如果 Authorization 设置为 no-auth,则不携带 Token if (config.headers.Authorization !== "no-auth" && accessToken) { config.headers.Authorization = `Bearer ${accessToken}`; + + const tenantId = localStorage.getItem(STORAGE_KEYS.TENANT_ID); + if (tenantId) { + config.headers["tenant-id"] = tenantId; + } } else { delete config.headers.Authorization; + delete config.headers["tenant-id"]; } return config; diff --git a/src/utils/storage.ts b/src/utils/storage.ts index a549775d..75d5e3fe 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -1,4 +1,4 @@ -import { STORAGE_KEYS, APP_PREFIX } from "@/config/storage"; +import { STORAGE_KEYS, APP_PREFIX } from "@/constants"; /** * 存储工具类 diff --git a/src/views/ai/command-record/index.vue b/src/views/ai/command-record/index.vue index 199bbac6..6238757b 100644 --- a/src/views/ai/command-record/index.vue +++ b/src/views/ai/command-record/index.vue @@ -1,7 +1,7 @@