From 9a231aa58cf707d9d29830f2082289c676449bb7 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Mon, 11 May 2026 23:37:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E5=8D=87=E7=BA=A7=E8=87=B3=20v3.0?= =?UTF-8?q?=20=E5=B9=B6=E9=87=8D=E6=9E=84=E7=BB=84=E4=BB=B6=E4=B8=8E?= =?UTF-8?q?=E5=BC=95=E5=AF=BC=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提取应用引导逻辑至 useAppBootstrap 组合式函数 - 增强日期选择器组件,支持全类型与格式自动推导 - 修复资源泄漏:清理 RAF、定时器、事件与 mitt 监听 - 修复退出登录逻辑与异步导出流程 - 迁移 ::v-deep 至 Vue 3 标准 :deep 语法 - 移除调试日志并改进类型定义与注释文档 --- frontend/web/.github/workflows/ci.yml | 59 ++++ frontend/web/README.md | 18 + frontend/web/index.html | 12 +- frontend/web/package.json | 2 +- frontend/web/src/App.vue | 35 +- .../web/src/components/DatePicker/index.vue | 86 ++++- .../banners/fa-basic-banner/index.vue | 3 +- .../cards/fa-progress-card/index.vue | 14 +- .../components/charts/fa-map-chart/index.vue | 15 +- .../components/forms/fa-button-more/index.vue | 18 +- .../components/forms/fa-button-more/types.ts | 18 + .../components/forms/fa-drag-verify/index.vue | 6 +- .../src/components/forms/fa-form/index.vue | 18 +- .../components/forms/fa-search-bar/index.vue | 10 +- .../components/forms/fa-wang-editor/index.vue | 4 +- .../layouts/fa-global-search/index.vue | 17 +- .../layouts/fa-header-bar/index.vue | 16 +- .../fa-header-bar/widget/FaUserMenu.vue | 19 +- .../fa-menus/fa-horizontal-menu/index.vue | 6 +- .../widget/HorizontalSubmenu.vue | 2 +- .../layouts/fa-menus/fa-mixed-menu/index.vue | 6 +- .../layouts/fa-notification/index.vue | 12 +- .../composables/useSettingsPanel.ts | 1 + .../widget/BasicSettings.vue | 2 +- .../components/layouts/fa-work-tab/index.vue | 13 +- frontend/web/src/components/layouts/index.vue | 25 +- .../components/media/fa-cutter-img/index.vue | 23 +- .../src/components/modal/fa-dialog/index.vue | 2 +- .../src/components/modal/fa-drawer/index.vue | 2 +- .../modal/fa-export-dialog/index.vue | 59 +--- .../modal/fa-import-dialog/index.vue | 31 +- .../components/others/fa-calendar/index.vue | 18 +- .../others/fa-copy-button/index.vue | 20 +- .../others/fa-markdown-renderer/index.vue | 18 +- .../components/others/fa-menu-right/index.vue | 2 +- .../others/fa-table-select/index.vue | 27 +- .../src/components/others/fa-upload/index.vue | 2 +- .../src/components/tables/fa-table/index.vue | 8 +- .../src/components/tables/fa-table/style.scss | 25 +- .../components/theme/fa-theme-svg/index.vue | 2 +- .../components/views/fa-login/AuthTopBar.vue | 8 +- .../web/src/directives/business/highlight.ts | 36 +- .../web/src/hooks/core/useAppBootstrap.ts | 23 ++ frontend/web/src/hooks/core/useAuth.ts | 3 +- frontend/web/src/hooks/core/useChart.ts | 13 +- frontend/web/src/hooks/core/useHeaderBar.ts | 2 +- .../web/src/hooks/core/useLayoutHeight.ts | 8 +- frontend/web/src/hooks/core/useSiteConfig.ts | 45 +++ frontend/web/src/main.ts | 59 ++-- frontend/web/src/plugins/index.ts | 17 +- frontend/web/src/router/afterEach.ts | 6 +- frontend/web/src/router/beforeEach.ts | 47 ++- frontend/web/src/router/dynamicRoutes.ts | 8 +- frontend/web/src/router/index.ts | 10 +- frontend/web/src/router/staticRoutes.ts | 10 +- frontend/web/src/store/index.ts | 3 +- .../web/src/store/modules/setting.store.ts | 14 +- frontend/web/src/store/modules/table.store.ts | 7 +- frontend/web/src/store/modules/user.store.ts | 51 +-- frontend/web/src/styles/core/app.scss | 2 +- frontend/web/src/utils/auth/index.ts | 12 +- frontend/web/src/utils/download.ts | 81 ++--- frontend/web/src/utils/http/index.ts | 99 +++++- frontend/web/src/utils/menuIcon/index.ts | 9 + frontend/web/src/utils/navigation/index.ts | 8 +- frontend/web/src/utils/socket/index.ts | 26 +- frontend/web/src/utils/storage/index.ts | 334 +++++------------- frontend/web/src/utils/sys/index.ts | 10 + frontend/web/src/utils/table/index.ts | 18 +- frontend/web/src/views/current/profile.vue | 2 +- .../components/ArticleCommentWallDrawer.vue | 2 - .../components/ArticleDetailDrawer.vue | 10 +- .../dashboard/console/modules/new-user.vue | 2 +- frontend/web/src/views/dashboard/index.vue | 75 ++-- .../src/views/dashboard/tutorial/index.vue | 2 +- .../src/views/dashboard/workplace/index.vue | 13 +- .../module_ai/chat/components/ChatInput.vue | 4 +- .../module_ai/chat/components/ChatNavbar.vue | 4 +- .../module_ai/chat/components/MessageItem.vue | 22 +- .../module_ai/chat/components/Sidebar.vue | 4 +- .../web/src/views/module_ai/fachat/index.vue | 4 + .../web/src/views/module_ai/memory/index.vue | 68 ++-- .../views/module_application/portal/index.vue | 10 +- .../src/views/module_example/demo/index.vue | 127 +++---- .../src/views/module_example/demo01/index.vue | 123 ++++--- .../gencode/components/CreateTableDialog.vue | 10 +- .../gencode/components/GenBasicStep.vue | 16 +- .../gencode/components/GenColumnsStep.vue | 4 +- .../gencode/components/GencodeHelpPanel.vue | 8 +- .../components/ImportDbTableDialog.vue | 3 +- .../views/module_generator/gencode/index.vue | 2 +- .../src/views/module_monitor/cache/index.vue | 258 ++++++++------ .../src/views/module_monitor/online/index.vue | 64 ++-- .../views/module_monitor/resource/index.vue | 6 +- .../login/components/LoginAuthLinkRow.vue | 4 +- .../login/components/LoginMobilePanel.vue | 4 +- .../auth/login/components/LoginQrPanel.vue | 2 +- .../views/module_system/auth/login/index.vue | 35 +- .../src/views/module_system/dept/index.vue | 101 +++--- .../dict/components/DataDrawer.vue | 107 +++--- .../src/views/module_system/dict/index.vue | 113 +++--- .../web/src/views/module_system/log/index.vue | 62 ++-- .../src/views/module_system/menu/index.vue | 98 ++--- .../src/views/module_system/notice/index.vue | 108 +++--- .../param/components/ConfigInfoDrawer.vue | 7 +- .../src/views/module_system/param/index.vue | 75 ++-- .../views/module_system/position/index.vue | 102 +++--- .../src/views/module_system/role/index.vue | 103 +++--- .../src/views/module_system/tenant/index.vue | 69 ++-- .../user/components/DeptTree.vue | 15 +- .../user/components/UserTableSelect.vue | 13 +- .../src/views/module_system/user/index.vue | 182 +++++----- .../views/module_task/cronjob/job/index.vue | 91 +++-- .../views/module_task/cronjob/node/index.vue | 88 ++--- .../workflow/components/EdgeConfigPanel.vue | 6 +- .../workflow/components/NodeConfigPanel.vue | 6 +- .../components/WorkflowDesignDrawer.vue | 94 ++--- .../module_task/workflow/definition/index.vue | 148 ++++---- .../module_task/workflow/node-type/index.vue | 66 ++-- frontend/web/vite.config.ts | 26 +- 120 files changed, 2282 insertions(+), 1931 deletions(-) create mode 100644 frontend/web/.github/workflows/ci.yml create mode 100644 frontend/web/src/components/forms/fa-button-more/types.ts create mode 100644 frontend/web/src/hooks/core/useAppBootstrap.ts create mode 100644 frontend/web/src/hooks/core/useSiteConfig.ts diff --git a/frontend/web/.github/workflows/ci.yml b/frontend/web/.github/workflows/ci.yml new file mode 100644 index 00000000..315a2e9d --- /dev/null +++ b/frontend/web/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + push: + branches: [master, dev] + pull_request: + branches: [master] + +jobs: + lint: + runs-on: ubuntu-latest + defaults: + run: + working-directory: frontend/web + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: frontend/web/pnpm-lock.yaml + + - run: pnpm install --frozen-lockfile + + - name: Type Check + run: pnpm type-check + + - name: Lint + run: pnpm lint + + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: frontend/web + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: frontend/web/pnpm-lock.yaml + + - run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build:dev diff --git a/frontend/web/README.md b/frontend/web/README.md index de234730..f0fd204b 100755 --- a/frontend/web/README.md +++ b/frontend/web/README.md @@ -2,6 +2,24 @@ 基于 **Vue 3 + Vite + TypeScript + Element Plus** 的后台管理前端,与 FastAPI Admin 后端配套使用。状态管理为 **Pinia**,样式以 **Tailwind CSS 4** 与 **SCSS** 为主,接口请求使用 **Axios**。 +## 架构概览 + +``` +main.ts 启动 + └─ initPlugins(app) ← 插件注册(Pinia → Router → 指令 → i18n → Element Plus) + └─ mount("#app") + └─ App.vue + ├─ onBeforeMount: 主题初始化 + └─ onMounted: bootstrap() → 存储检查/版本升级/站点配置 + └─ 路由守卫 beforeEach + ├─ 存储失效检测 + ├─ 登录态校验 + ├─ 动态路由注册(菜单 → addRoute) + └─ 标签/标题同步 +``` + +路由采用 **Hash 模式**,静态路由(Layout/登录/404)首屏注册,业务路由由守卫根据菜单权限延迟 `addRoute`。HTTP 拦截器支持 **Token 静默续期**(401 时自动 refresh,失败后跳转登录)。 + --- ## 第一次运行(最快上手) diff --git a/frontend/web/index.html b/frontend/web/index.html index 6111faca..ffce9caf 100755 --- a/frontend/web/index.html +++ b/frontend/web/index.html @@ -12,7 +12,7 @@ diff --git a/frontend/web/src/components/DatePicker/index.vue b/frontend/web/src/components/DatePicker/index.vue index 1c791696..db3a915b 100644 --- a/frontend/web/src/components/DatePicker/index.vue +++ b/frontend/web/src/components/DatePicker/index.vue @@ -1,39 +1,91 @@ - + diff --git a/frontend/web/src/components/layouts/fa-menus/fa-mixed-menu/index.vue b/frontend/web/src/components/layouts/fa-menus/fa-mixed-menu/index.vue index 5d6588ee..edbc6993 100644 --- a/frontend/web/src/components/layouts/fa-menus/fa-mixed-menu/index.vue +++ b/frontend/web/src/components/layouts/fa-menus/fa-mixed-menu/index.vue @@ -248,13 +248,13 @@ onMounted(initScrollState); diff --git a/frontend/web/src/components/others/fa-copy-button/index.vue b/frontend/web/src/components/others/fa-copy-button/index.vue index f7f7498f..56731e66 100644 --- a/frontend/web/src/components/others/fa-copy-button/index.vue +++ b/frontend/web/src/components/others/fa-copy-button/index.vue @@ -28,18 +28,16 @@ const props = defineProps({ }, }); -function handleClipboard() { +async function handleClipboard() { if (navigator.clipboard && navigator.clipboard.writeText) { // 使用 Clipboard API - navigator.clipboard - .writeText(props.text) - .then(() => { - ElMessage.success(t("common.copySuccess")); - }) - .catch((error) => { - ElMessage.warning(t("common.copyFailed")); - console.log("[CopyButton] Copy failed", error); - }); + try { + await navigator.clipboard.writeText(props.text); + ElMessage.success(t("common.copySuccess")); + } catch (error) { + ElMessage.warning(t("common.copyFailed")); + console.warn("[CopyButton] Copy failed:", error); + } } else { // 兼容性处理(useClipboard 有兼容性问题) const input = document.createElement("input"); @@ -57,7 +55,7 @@ function handleClipboard() { } } catch (err) { ElMessage.warning(t("common.copyFailed")); - console.log("[CopyButton] Copy failed.", err); + console.warn("[CopyButton] Copy failed:", err); } finally { document.body.removeChild(input); } diff --git a/frontend/web/src/components/others/fa-markdown-renderer/index.vue b/frontend/web/src/components/others/fa-markdown-renderer/index.vue index b29a0519..27298252 100644 --- a/frontend/web/src/components/others/fa-markdown-renderer/index.vue +++ b/frontend/web/src/components/others/fa-markdown-renderer/index.vue @@ -70,7 +70,7 @@ const renderedContent = computed(() => { color: var(--el-text-color-primary); word-wrap: break-word; - ::v-deep(pre) { + :deep(pre) { padding: 12px; margin: 12px 0; overflow-x: auto; @@ -83,7 +83,7 @@ const renderedContent = computed(() => { } } - ::v-deep(code) { + :deep(code) { padding: 2px 6px; font-family: "Courier New", Courier, monospace; font-size: 13px; @@ -91,21 +91,21 @@ const renderedContent = computed(() => { border-radius: 3px; } - ::v-deep(p) { + :deep(p) { margin: 8px 0; } - ::v-deep(ul), - ::v-deep(ol) { + :deep(ul), + :deep(ol) { padding-left: 24px; margin: 8px 0; } - ::v-deep(li) { + :deep(li) { margin: 4px 0; } - ::v-deep(a) { + :deep(a) { color: var(--el-color-primary); text-decoration: none; @@ -114,14 +114,14 @@ const renderedContent = computed(() => { } } - ::v-deep(blockquote) { + :deep(blockquote) { padding: 8px 16px; margin: 12px 0; background: var(--el-fill-color-light); border-left: 4px solid var(--el-color-primary); } - ::v-deep(table) { + :deep(table) { width: 100%; margin: 12px 0; border-collapse: collapse; diff --git a/frontend/web/src/components/others/fa-menu-right/index.vue b/frontend/web/src/components/others/fa-menu-right/index.vue index 0a766412..a566a202 100644 --- a/frontend/web/src/components/others/fa-menu-right/index.vue +++ b/frontend/web/src/components/others/fa-menu-right/index.vue @@ -384,7 +384,7 @@ defineExpose({ } .menu-item.is-disabled i:not(.submenu-arrow), -.menu-item.is-disabled ::v-deep(.fa-svg-icon) { +.menu-item.is-disabled :deep(.fa-svg-icon) { color: var(--el-text-color-disabled) !important; } diff --git a/frontend/web/src/components/others/fa-table-select/index.vue b/frontend/web/src/components/others/fa-table-select/index.vue index 3dd36f82..f3182860 100644 --- a/frontend/web/src/components/others/fa-table-select/index.vue +++ b/frontend/web/src/components/others/fa-table-select/index.vue @@ -124,7 +124,7 @@ - ; @@ -264,21 +265,19 @@ function handleQuery() { } // 获取分页数据 -function fetchPageData(isRestart = false) { +async function fetchPageData(isRestart = false) { loading.value = true; if (isRestart) { queryParams.page_no = 1; queryParams.page_size = page_size; } - props.selectConfig - .indexAction(queryParams) - .then((data) => { - total.value = data.total; - pageData.value = data.list; - }) - .finally(() => { - loading.value = false; - }); + try { + const data = await props.selectConfig.indexAction(queryParams); + total.value = data.total; + pageData.value = data.list; + } finally { + loading.value = false; + } } // 列表操作 @@ -352,8 +351,8 @@ const popoverContentRef = ref(); diff --git a/frontend/web/src/components/others/fa-upload/index.vue b/frontend/web/src/components/others/fa-upload/index.vue index 34991428..85648b88 100644 --- a/frontend/web/src/components/others/fa-upload/index.vue +++ b/frontend/web/src/components/others/fa-upload/index.vue @@ -464,7 +464,7 @@ const onError = (error: any) => { } } -::v-deep(.el-upload--picture-card) { +:deep(.el-upload--picture-card) { position: relative; width: v-bind("props.style.width ?? '150px'"); height: v-bind("props.style.height ?? '150px'"); diff --git a/frontend/web/src/components/tables/fa-table/index.vue b/frontend/web/src/components/tables/fa-table/index.vue index a6c4e513..d6c6546f 100644 --- a/frontend/web/src/components/tables/fa-table/index.vue +++ b/frontend/web/src/components/tables/fa-table/index.vue @@ -4,7 +4,7 @@ - 暴露 `elTableRef`、`scrollToTop`;formatter 列见 TableFormatterOutlet 注释。 --> diff --git a/frontend/web/src/views/dashboard/article/components/ArticleDetailDrawer.vue b/frontend/web/src/views/dashboard/article/components/ArticleDetailDrawer.vue index 618fe8a1..251ead1a 100644 --- a/frontend/web/src/views/dashboard/article/components/ArticleDetailDrawer.vue +++ b/frontend/web/src/views/dashboard/article/components/ArticleDetailDrawer.vue @@ -127,12 +127,12 @@ watch( .article-detail-markdown { margin-top: 8px; - ::v-deep(img) { + :deep(img) { width: 100%; border: 1px solid var(--fa-gray-200); } - ::v-deep(pre) { + :deep(pre) { position: relative; &:hover { @@ -152,11 +152,11 @@ watch( } } - ::v-deep(.code-wrapper) { + :deep(.code-wrapper) { overflow-x: auto; } - ::v-deep(.line-number) { + :deep(.line-number) { position: sticky; left: 0; z-index: 2; @@ -169,7 +169,7 @@ watch( text-align: center; } - ::v-deep(.copy-button) { + :deep(.copy-button) { position: absolute; top: 6px; right: 6px; diff --git a/frontend/web/src/views/dashboard/console/modules/new-user.vue b/frontend/web/src/views/dashboard/console/modules/new-user.vue index 1194f69c..7fbd40be 100755 --- a/frontend/web/src/views/dashboard/console/modules/new-user.vue +++ b/frontend/web/src/views/dashboard/console/modules/new-user.vue @@ -164,7 +164,7 @@ onMounted(() => { diff --git a/frontend/web/src/views/module_application/portal/index.vue b/frontend/web/src/views/module_application/portal/index.vue index ac535223..539f3b3f 100644 --- a/frontend/web/src/views/module_application/portal/index.vue +++ b/frontend/web/src/views/module_application/portal/index.vue @@ -532,12 +532,12 @@ async function handleSubmit() { opacity: 0.55; } - ::v-deep(.el-card__header) { + :deep(.el-card__header) { padding: 14px 14px 12px; border-bottom: none; } - ::v-deep(.el-card__body) { + :deep(.el-card__body) { display: flex; flex: 1; flex-direction: column; @@ -546,7 +546,7 @@ async function handleSubmit() { padding: 12px 14px; } - ::v-deep(.el-card__footer) { + :deep(.el-card__footer) { padding: 10px 14px 14px; margin-top: auto; } @@ -634,11 +634,11 @@ async function handleSubmit() { } } -.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) { +.crud-dialog-art-form :deep(.el-row > .el-col:last-child) { display: none; } -.crud-dialog-art-form ::v-deep(.el-form-item__content) { +.crud-dialog-art-form :deep(.el-form-item__content) { max-width: 100%; } diff --git a/frontend/web/src/views/module_example/demo/index.vue b/frontend/web/src/views/module_example/demo/index.vue index 36c2b4e1..93f5bd2e 100644 --- a/frontend/web/src/views/module_example/demo/index.vue +++ b/frontend/web/src/views/module_example/demo/index.vue @@ -472,7 +472,7 @@ const exportQueryParams = computed(() => { const demoImportContentConfig = computed(() => ({ permPrefix: "module_example:demo", cols: demoCrudCols.value, - indexAction: async () => ({}) as any, + indexAction: async () => ({}), importTemplate: () => DemoAPI.downloadTemplateDemo(), })); @@ -685,84 +685,91 @@ async function handleSubmit() { }); } -const deleteDemoRow = (row: DemoTable) => { +const deleteDemoRow = async (row: DemoTable) => { if (!row.id) return; - ElMessageBox.confirm(`确定删除「${row.name ?? row.id}」吗?此操作不可恢复!`, "删除确认", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning", - }) - .then(async () => { - await DemoAPI.deleteDemo([row.id!]); - ElMessage.success("删除成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshRemove(); - }) - .catch(() => { - ElMessage.info("已取消删除"); - }); + try { + await ElMessageBox.confirm( + `确定删除「${row.name ?? row.id}」吗?此操作不可恢复!`, + "删除确认", + { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", + } + ); + await DemoAPI.deleteDemo([row.id!]); + ElMessage.success("删除成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshRemove(); + } catch { + ElMessage.info("已取消删除"); + } }; -function handleBatchDelete() { +async function handleBatchDelete() { const ids = selectedIds.value; if (ids.length === 0) return; - ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?此操作不可恢复!`, "批量删除", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning", - }) - .then(async () => { - try { - batchDeleting.value = true; - await DemoAPI.deleteDemo(ids); - ElMessage.success("删除成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshRemove(); - } finally { - batchDeleting.value = false; + try { + await ElMessageBox.confirm( + `确定删除选中的 ${ids.length} 条数据吗?此操作不可恢复!`, + "批量删除", + { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", } - }) - .catch(() => { - ElMessage.info("已取消删除"); - }); + ); + batchDeleting.value = true; + await DemoAPI.deleteDemo(ids); + ElMessage.success("删除成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshRemove(); + } catch { + ElMessage.info("已取消删除"); + } finally { + batchDeleting.value = false; + } } -function runBatchStatus(status: string) { +async function runBatchStatus(status: string) { const ids = selectedIds.value; if (ids.length === 0) { ElMessage.warning("请先在列表中勾选数据"); return; } - ElMessageBox.confirm( - `确认对选中的 ${ids.length} 条数据${status === "0" ? "启用" : "停用"}?`, - "批量设置", - { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } - ) - .then(async () => { - await DemoAPI.batchDemo({ ids, status }); - ElMessage.success("操作成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshData(); - }) - .catch(() => {}); + try { + await ElMessageBox.confirm( + `确认对选中的 ${ids.length} 条数据${status === "0" ? "启用" : "停用"}?`, + "批量设置", + { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } + ); + await DemoAPI.batchDemo({ ids, status }); + ElMessage.success("操作成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshData(); + } catch { + // 用户取消 + } } function openImportModal() { importModalVisible.value = true; } -function handleCrudImportUpload(formData: FormData) { - DemoAPI.importDemo(formData) - .then((res) => { - if (res.data.code !== ResultEnum.SUCCESS) { - ElMessage.error(res.data.msg || "导入失败"); - return; - } - ElMessage.success(res.data.msg || "导入成功"); - importModalVisible.value = false; - return refreshData(); - }) - .catch(console.error); +async function handleCrudImportUpload(formData: FormData) { + try { + const res = await DemoAPI.importDemo(formData); + if (res.data.code !== ResultEnum.SUCCESS) { + ElMessage.error(res.data.msg || "导入失败"); + return; + } + ElMessage.success(res.data.msg || "导入成功"); + importModalVisible.value = false; + await refreshData(); + } catch (error) { + console.error("[Import]", error); + ElMessage.error("导入失败"); + } } function openExportModal() { diff --git a/frontend/web/src/views/module_example/demo01/index.vue b/frontend/web/src/views/module_example/demo01/index.vue index fcf65a3c..91b6f4c2 100644 --- a/frontend/web/src/views/module_example/demo01/index.vue +++ b/frontend/web/src/views/module_example/demo01/index.vue @@ -541,84 +541,83 @@ async function handleSubmit() { }); } -const deleteDemo01Row = (row: Demo01Table) => { +const deleteDemo01Row = async (row: Demo01Table) => { if (!row.id) return; - ElMessageBox.confirm(`确定删除「${row.name ?? row.id}」吗?此操作不可恢复!`, "删除确认", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning", - }) - .then(async () => { - await Demo01API.deleteDemo01([row.id!]); - ElMessage.success("删除成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshRemove(); - }) - .catch(() => { - ElMessage.info("已取消删除"); - }); + try { + await ElMessageBox.confirm( + `确定删除「${row.name ?? row.id}」吗?此操作不可恢复!`, + "删除确认", + { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } + ); + await Demo01API.deleteDemo01([row.id!]); + ElMessage.success("删除成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshRemove(); + } catch { + ElMessage.info("已取消删除"); + } }; -function handleBatchDelete() { +async function handleBatchDelete() { const ids = selectedIds.value; if (ids.length === 0) return; - ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?此操作不可恢复!`, "批量删除", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning", - }) - .then(async () => { - try { - batchDeleting.value = true; - await Demo01API.deleteDemo01(ids); - ElMessage.success("删除成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshRemove(); - } finally { - batchDeleting.value = false; - } - }) - .catch(() => { - ElMessage.info("已取消删除"); - }); + try { + await ElMessageBox.confirm( + `确定删除选中的 ${ids.length} 条数据吗?此操作不可恢复!`, + "批量删除", + { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } + ); + batchDeleting.value = true; + await Demo01API.deleteDemo01(ids); + ElMessage.success("删除成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshRemove(); + } catch { + ElMessage.info("已取消删除"); + } finally { + batchDeleting.value = false; + } } -function runBatchStatus(status: string) { +async function runBatchStatus(status: string) { const ids = selectedIds.value; if (ids.length === 0) { ElMessage.warning("请先在列表中勾选数据"); return; } - ElMessageBox.confirm( - `确认对选中的 ${ids.length} 条数据${status === "0" ? "启用" : "停用"}?`, - "批量设置", - { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } - ) - .then(async () => { - await Demo01API.batchDemo01({ ids, status }); - ElMessage.success("操作成功"); - faTableRef.value?.elTableRef?.clearSelection(); - await refreshData(); - }) - .catch(() => {}); + try { + await ElMessageBox.confirm( + `确认对选中的 ${ids.length} 条数据${status === "0" ? "启用" : "停用"}?`, + "批量设置", + { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } + ); + await Demo01API.batchDemo01({ ids, status }); + ElMessage.success("操作成功"); + faTableRef.value?.elTableRef?.clearSelection(); + await refreshData(); + } catch { + // 用户取消操作,无需处理 + } } function openImportModal() { importModalVisible.value = true; } -function handleCrudImportUpload(formDataUpload: FormData) { - Demo01API.importDemo01(formDataUpload) - .then((res) => { - if (res.data.code !== ResultEnum.SUCCESS) { - ElMessage.error(res.data.msg || "导入失败"); - return; - } - ElMessage.success(res.data.msg || "导入成功"); - importModalVisible.value = false; - return refreshData(); - }) - .catch(console.error); +async function handleCrudImportUpload(formDataUpload: FormData) { + try { + const res = await Demo01API.importDemo01(formDataUpload); + if (res.data.code !== ResultEnum.SUCCESS) { + ElMessage.error(res.data.msg || "导入失败"); + return; + } + ElMessage.success(res.data.msg || "导入成功"); + importModalVisible.value = false; + await refreshData(); + } catch (error) { + console.error("[Import]", error); + ElMessage.error("导入失败"); + } } function openExportModal() { @@ -627,11 +626,11 @@ function openExportModal() { diff --git a/frontend/web/src/views/module_generator/gencode/components/CreateTableDialog.vue b/frontend/web/src/views/module_generator/gencode/components/CreateTableDialog.vue index b5d7aa95..07550b94 100644 --- a/frontend/web/src/views/module_generator/gencode/components/CreateTableDialog.vue +++ b/frontend/web/src/views/module_generator/gencode/components/CreateTableDialog.vue @@ -426,7 +426,7 @@ function handleCancel() { position: relative; } -.visual-pane ::v-deep(.el-textarea__inner) { +.visual-pane :deep(.el-textarea__inner) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; } @@ -437,20 +437,20 @@ function handleCancel() { /* 表结构:描述列表表格化,标签列对齐、内容区可伸缩 */ .visual-structure { .visual-desc { - ::v-deep(.el-descriptions__label) { + :deep(.el-descriptions__label) { width: 108px; vertical-align: middle; } - ::v-deep(.el-descriptions__cell) { + :deep(.el-descriptions__cell) { vertical-align: middle; } - ::v-deep(.el-descriptions__content) { + :deep(.el-descriptions__content) { min-width: 0; } - ::v-deep(.el-descriptions__title) { + :deep(.el-descriptions__title) { margin-bottom: 8px; } } diff --git a/frontend/web/src/views/module_generator/gencode/components/GenBasicStep.vue b/frontend/web/src/views/module_generator/gencode/components/GenBasicStep.vue index 5c52f85b..7982cd6e 100644 --- a/frontend/web/src/views/module_generator/gencode/components/GenBasicStep.vue +++ b/frontend/web/src/views/module_generator/gencode/components/GenBasicStep.vue @@ -237,7 +237,7 @@ const props = defineProps<{ function findOptionByValue(options: OptionType[], value: number | string): any | null { for (const opt of options) { - if (String(opt.value) === String(value)) return opt as any; + if (String(opt.value) === String(value)) return opt; if (opt.children?.length) { const hit = findOptionByValue(opt.children, value); if (hit) return hit; @@ -352,15 +352,15 @@ onUnmounted(() => { min-width: 0; } -.gen-basic-step ::v-deep(.el-col) { +.gen-basic-step :deep(.el-col) { min-width: 0; } -.gen-basic-step ::v-deep(.el-form-item__content) { +.gen-basic-step :deep(.el-form-item__content) { min-width: 0; } -.gen-basic-step ::v-deep(.el-input-group) { +.gen-basic-step :deep(.el-input-group) { width: 100%; min-width: 0; max-width: 100%; @@ -383,11 +383,11 @@ onUnmounted(() => { border: 1px solid var(--el-border-color-lighter); } -.master-sub-card ::v-deep(.el-card__header) { +.master-sub-card :deep(.el-card__header) { padding: 8px 10px; } -.master-sub-card ::v-deep(.el-card__body) { +.master-sub-card :deep(.el-card__body) { padding: 10px; } @@ -396,7 +396,7 @@ onUnmounted(() => { border: 1px solid var(--el-border-color-lighter); } -.gen-form-card ::v-deep(.el-card__body) { +.gen-form-card :deep(.el-card__body) { overflow-x: hidden; } @@ -425,7 +425,7 @@ onUnmounted(() => { border-bottom: 1px solid var(--el-border-color-lighter); } -.gen-echo-card ::v-deep(.el-card__body) { +.gen-echo-card :deep(.el-card__body) { padding: 6px 8px; } diff --git a/frontend/web/src/views/module_generator/gencode/components/GenColumnsStep.vue b/frontend/web/src/views/module_generator/gencode/components/GenColumnsStep.vue index dd86d269..614f0c71 100644 --- a/frontend/web/src/views/module_generator/gencode/components/GenColumnsStep.vue +++ b/frontend/web/src/views/module_generator/gencode/components/GenColumnsStep.vue @@ -249,7 +249,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch, nextTick } from "vue"; import { Search } from "@element-plus/icons-vue"; import { useDraggable } from "vue-draggable-plus"; -import type { GenTableSchema } from "@/api/module_generator/gencode"; +import type { GenTableSchema, GenTableColumnSchema } from "@/api/module_generator/gencode"; import type { DictTable } from "@/api/module_system/dict"; defineOptions({ name: "GenColumnsStep" }); @@ -276,7 +276,7 @@ const columnsModel = computed({ get: () => props.info.columns || [], set: (v) => { // props.info 是父组件传入的 reactive 对象,可以直接回写 - props.info.columns = v as any; + props.info.columns = v as GenTableColumnSchema[]; }, }); diff --git a/frontend/web/src/views/module_generator/gencode/components/GencodeHelpPanel.vue b/frontend/web/src/views/module_generator/gencode/components/GencodeHelpPanel.vue index a9b0e2dd..88aae17f 100644 --- a/frontend/web/src/views/module_generator/gencode/components/GencodeHelpPanel.vue +++ b/frontend/web/src/views/module_generator/gencode/components/GencodeHelpPanel.vue @@ -66,18 +66,18 @@ const rows = [ border-radius: var(--el-border-radius-base); } -.gencode-help-collapse ::v-deep(.el-collapse-item__header) { +.gencode-help-collapse :deep(.el-collapse-item__header) { padding: 8px 12px; font-size: 13px; font-weight: 500; background: var(--el-fill-color-light); } -.gencode-help-collapse ::v-deep(.el-collapse-item__wrap) { +.gencode-help-collapse :deep(.el-collapse-item__wrap) { border-top: 1px solid var(--el-border-color-lighter); } -.gencode-help-collapse ::v-deep(.el-collapse-item__content) { +.gencode-help-collapse :deep(.el-collapse-item__content) { padding: 10px 12px 12px; } @@ -91,7 +91,7 @@ const rows = [ flex-shrink: 0; } -.gencode-rules-table ::v-deep(.el-table__cell) { +.gencode-rules-table :deep(.el-table__cell) { font-size: 12px; } diff --git a/frontend/web/src/views/module_generator/gencode/components/ImportDbTableDialog.vue b/frontend/web/src/views/module_generator/gencode/components/ImportDbTableDialog.vue index 43e4e883..2630b193 100644 --- a/frontend/web/src/views/module_generator/gencode/components/ImportDbTableDialog.vue +++ b/frontend/web/src/views/module_generator/gencode/components/ImportDbTableDialog.vue @@ -73,7 +73,7 @@ > - { try { loading.value = true; const previewRes = await GencodeAPI.syncDbPreview(tableName); - const preview = previewRes.data?.data as any; + const preview = previewRes.data?.data as Record; const mainHtml = renderHtml(`主表:${tableName}`, preview); const subHtml = preview?.sub_table_name && preview?.sub diff --git a/frontend/web/src/views/module_monitor/cache/index.vue b/frontend/web/src/views/module_monitor/cache/index.vue index 9ad49061..d7b3d479 100644 --- a/frontend/web/src/views/module_monitor/cache/index.vue +++ b/frontend/web/src/views/module_monitor/cache/index.vue @@ -1,3 +1,4 @@ + - - - - - - - -