From 4ec846a7029dc264bf856fb029c8c9f81edc95ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?cls=5F=E5=AE=81=E6=B3=A2=E6=9C=AC=E6=9C=BA?= <908705107@qq.com> Date: Mon, 22 Jun 2026 04:50:11 +0800 Subject: [PATCH] feat: harden ai agent admin workflow experience --- .gitignore | 23 + web/apps/web-ele/package.json | 1 + .../web-ele/src/adapter/component/index.ts | 297 +- web/apps/web-ele/src/adapter/form.ts | 21 +- web/apps/web-ele/src/adapter/vxe-table.ts | 142 +- web/apps/web-ele/src/api/core/data-source.ts | 25 + web/apps/web-ele/src/api/core/index.ts | 22 +- web/apps/web-ele/src/api/core/login-log.ts | 2 - web/apps/web-ele/src/api/core/menu.ts | 5 + web/apps/web-ele/src/api/core/post.ts | 13 +- .../web-ele/src/api/core/server-monitor.ts | 59 + web/apps/web-ele/src/api/core/ui-config.ts | 2 +- web/apps/web-ele/src/api/core/websocket.ts | 271 +- web/apps/web-ele/src/app.vue | 2 +- web/apps/web-ele/src/bootstrap.ts | 81 +- .../src/components/ChatBox/MessageBubble.vue | 143 +- .../web-ele/src/components/ChatBox/index.vue | 26 +- .../web-ele/src/components/ChatBox/types.ts | 46 +- .../components/ai-chat-panel/AiChatPanel.vue | 351 ++- .../ai-chat-panel/composables/useChatApi.ts | 17 +- .../src/components/ai-chat-panel/types.ts | 18 +- .../ai-loading/AiWorkingAnimation.vue | 12 +- .../dashboard-design/DashboardRenderer.vue | 2 +- .../components/WidgetRenderer.vue | 62 +- .../components/widgets/ApprovalCenter.vue | 97 +- .../components/widgets/DataTable.vue | 285 ++ .../components/widgets/FilterDate.vue | 127 + .../components/widgets/FilterDateRange.vue | 159 ++ .../components/widgets/FilterInput.vue | 139 + .../components/widgets/FilterSelect.vue | 180 ++ .../components/widgets/IframeWidget.vue | 202 ++ .../components/widgets/ImageCarousel.vue | 252 ++ .../components/widgets/ImageWidget.vue | 117 + .../components/widgets/MyApps.vue | 107 +- .../components/widgets/ProgressCard.vue | 28 + .../components/widgets/RankingList.vue | 71 + .../components/widgets/ServerMonitor.vue | 5 +- .../components/widgets/StatCard.vue | 91 + .../components/widgets/TodoList.vue | 81 + .../components/widgets/VideoPlayer.vue | 277 ++ .../src/components/dashboard-design/index.ts | 21 +- .../components/form-editor/AppDesignPanel.vue | 133 + .../form-editor/AppSettingsPanel.vue | 536 ++++ .../form-editor/BasicInfoEditor.vue | 117 + .../DashboardBasicInfoConfirmPanel.vue | 122 + .../DashboardDesignConfirmPanel.vue | 134 + .../DashboardPublishConfirmPanel.vue | 137 + .../form-editor/DesignEditorPanel.vue | 288 ++ .../form-editor/FormEditorContent.vue | 816 ++++++ .../form-editor/LightConfirmPanel.vue | 93 + .../form-editor/PageBasicInfoEditor.vue | 140 + .../form-editor/PageEditorContent.vue | 381 +++ .../form-editor/PagePublishInfoEditor.vue | 129 + .../form-editor/PublishInfoEditor.vue | 117 + .../form-editor/SystemSummaryConfirmPanel.vue | 317 +++ .../src/components/form-editor/index.ts | 27 + .../src/components/fu-page/fu-page.vue | 112 +- .../image-cropper/image-cropper.vue | 131 + .../src/components/image-cropper/index.ts | 1 + .../multi-data-view/multi-data-view.vue | 4 +- .../notification/NotificationDrawer.vue | 101 +- .../notification/NotificationPopup.vue | 4 +- .../src/components/user-avatar/index.vue | 63 +- .../src/components/user-list-panel/index.vue | 9 +- .../zq-form/ai-image-ocr/ai-image-ocr.vue | 807 ++++++ .../components/zq-form/ai-image-ocr/index.ts | 2 + .../components/zq-form/ai-image-ocr/types.ts | 18 + .../barcode-generator/barcode-generator.vue | 275 ++ .../zq-form/barcode-generator/index.ts | 2 + .../zq-form/barcode-generator/types.ts | 23 + .../zq-form/code-editor/code-editor.vue | 259 +- .../components/zq-form/code-editor/index.ts | 2 + .../zq-form/code-editor/languages.ts | 18 + .../components/zq-form/code-editor/types.ts | 47 + .../zq-form/form-selector/form-selector.vue | 539 ++-- .../components/zq-form/form-selector/types.ts | 13 + .../zq-form/image-selector/image-selector.vue | 2136 ++++++++++---- .../zq-form/linked-field/linked-field.vue | 4 +- .../zq-form/notion-editor/NotionEditor.vue | 1113 ++++++++ .../components/SlashCommandMenu.vue | 224 ++ .../notion-editor/components/TableHandles.vue | 302 ++ .../notion-editor/components/TableMenu.vue | 320 +++ .../notion-editor/extensions/slash-command.ts | 237 ++ .../components/zq-form/notion-editor/index.ts | 1 + .../zq-form/qrcode-generator/index.ts | 2 + .../qrcode-generator/qrcode-generator.vue | 311 ++ .../zq-form/qrcode-generator/types.ts | 33 + .../extensions/ResizableImageComponent.vue | 654 +++++ .../extensions/ResizableVideoComponent.vue | 536 ++++ .../rich-text-editor/extensions/attachment.ts | 115 + .../rich-text-editor/extensions/font-size.ts | 66 + .../extensions/resizable-image.ts | 57 + .../rich-text-editor/extensions/video.ts | 124 + .../zq-form/rich-text-editor/index.ts | 1 + .../rich-text-editor/rich-text-editor.vue | 630 ++--- .../zq-form/rich-text-editor/types.ts | 76 + .../zq-form/scan-input/ScanDialog.vue | 103 + .../components/zq-form/scan-input/index.ts | 2 + .../zq-form/scan-input/scan-input.vue | 135 + .../components/zq-form/scan-input/types.ts | 25 + .../zq-form/shared/formatCodeContent.ts | 127 + .../src/components/zq-form/shared/types.ts | 66 + .../zq-form/shared/useCodeContent.ts | 28 + .../zq-form/shared/validateBarcode.ts | 54 + .../signature-pad/MobileSignatureDialog.vue | 371 +++ .../zq-form/signature-pad/SignatureDialog.vue | 363 +++ .../components/zq-form/signature-pad/index.ts | 2 + .../zq-form/signature-pad/signature-pad.vue | 306 ++ .../components/zq-form/signature-pad/types.ts | 29 + .../zq-form/table-selector/index.ts | 7 +- .../zq-form/table-selector/table-selector.vue | 555 ++-- .../zq-form/table-selector/types.ts | 11 +- .../zq-form/zq-icon-picker/icons.ts | 43 - .../web-ele/src/components/zq-table/api.ts | 8 +- .../web-ele/src/components/zq-table/init.ts | 4 +- .../web-ele/src/components/zq-table/style.css | 6 + .../web-ele/src/components/zq-table/types.ts | 4 +- .../src/components/zq-table/use-zq-table.ts | 2 +- .../src/components/zq-table/zq-table.vue | 131 +- .../src/composables/useNotification.ts | 4 +- web/apps/web-ele/src/layouts/auth.vue | 6 +- web/apps/web-ele/src/layouts/basic.vue | 139 +- web/apps/web-ele/src/locales/index.ts | 177 +- .../src/locales/langs/en-US/about.json | 15 + .../locales/langs/en-US/account-settings.json | 3 + .../src/locales/langs/en-US/ai-platform.json | 2500 +++++++++++++++++ .../src/locales/langs/en-US/announcement.json | 73 + .../src/locales/langs/en-US/apiToken.json | 37 + .../src/locales/langs/en-US/application.json | 75 + .../src/locales/langs/en-US/approval.json | 62 + .../locales/langs/en-US/authentication.json | 41 + .../web-ele/src/locales/langs/en-US/chat.json | 91 + .../src/locales/langs/en-US/common.json | 188 ++ .../src/locales/langs/en-US/contract.json | 150 + .../web-ele/src/locales/langs/en-US/core.json | 55 + .../locales/langs/en-US/dashboard-design.json | 894 ++++++ .../src/locales/langs/en-US/data-source.json | 283 ++ .../langs/en-US/database-connection.json | 52 + .../locales/langs/en-US/database-manager.json | 394 +++ .../locales/langs/en-US/database-monitor.json | 138 + .../src/locales/langs/en-US/demos.json | 48 + .../web-ele/src/locales/langs/en-US/dept.json | 41 + .../web-ele/src/locales/langs/en-US/dict.json | 22 + .../locales/langs/en-US/dingtalk-sync.json | 106 + .../langs/en-US/document-generator.json | 527 ++++ .../locales/langs/en-US/electronic-seal.json | 60 + .../src/locales/langs/en-US/feishu-sync.json | 65 + .../src/locales/langs/en-US/file-manager.json | 42 + .../src/locales/langs/en-US/form-manager.json | 850 ++++++ .../src/locales/langs/en-US/loginLog.json | 60 + .../src/locales/langs/en-US/menu-title.json | 61 + .../web-ele/src/locales/langs/en-US/menu.json | 63 + .../src/locales/langs/en-US/message.json | 90 + .../src/locales/langs/en-US/org-chart.json | 10 + .../src/locales/langs/en-US/page-manager.json | 126 + .../web-ele/src/locales/langs/en-US/page.json | 14 + .../src/locales/langs/en-US/permission.json | 74 + .../web-ele/src/locales/langs/en-US/post.json | 34 + .../locales/langs/en-US/redis-manager.json | 73 + .../locales/langs/en-US/redis-monitor.json | 181 ++ .../locales/langs/en-US/report-manager.json | 437 +++ .../web-ele/src/locales/langs/en-US/role.json | 134 + .../src/locales/langs/en-US/scheduler.json | 130 + .../locales/langs/en-US/server-monitor.json | 160 ++ .../locales/langs/en-US/system-config.json | 75 + .../src/locales/langs/en-US/system.json | 10 + .../src/locales/langs/en-US/ui-config.json | 172 ++ .../web-ele/src/locales/langs/en-US/ui.json | 15 + .../src/locales/langs/en-US/user-avatar.json | 13 + .../web-ele/src/locales/langs/en-US/user.json | 69 + .../src/locales/langs/en-US/wecom-sync.json | 67 + .../langs/en-US/workflow-designer.json | 738 +++++ .../src/locales/langs/en-US/workflow.json | 310 ++ .../locales/langs/zh-CN/account-settings.json | 3 + .../src/locales/langs/zh-CN/ai-platform.json | 1307 ++++++++- .../src/locales/langs/zh-CN/apiToken.json | 40 +- .../src/locales/langs/zh-CN/application.json | 3 +- .../locales/langs/zh-CN/dashboard-design.json | 776 +++++ .../src/locales/langs/zh-CN/data-source.json | 298 ++ .../locales/langs/zh-CN/database-manager.json | 396 +++ .../src/locales/langs/zh-CN/form-manager.json | 857 ++++++ .../src/locales/langs/zh-CN/loginLog.json | 9 - .../src/locales/langs/zh-CN/menu-title.json | 77 +- .../src/locales/langs/zh-CN/page-manager.json | 126 + .../locales/langs/zh-CN/report-manager.json | 437 +++ .../locales/langs/zh-CN/system-config.json | 35 +- .../src/locales/langs/zh-CN/ui-config.json | 101 +- .../web-ele/src/locales/langs/zh-CN/ui.json | 2 +- .../web-ele/src/locales/langs/zh-CN/user.json | 1 + .../langs/zh-CN/workflow-designer.json | 737 +++++ .../src/locales/langs/zh-CN/workflow.json | 310 ++ .../src/locales/langs/zh-TW/about.json | 15 + .../locales/langs/zh-TW/account-settings.json | 3 + .../src/locales/langs/zh-TW/ai-platform.json | 2500 +++++++++++++++++ .../src/locales/langs/zh-TW/announcement.json | 73 + .../src/locales/langs/zh-TW/apiToken.json | 37 + .../src/locales/langs/zh-TW/application.json | 75 + .../src/locales/langs/zh-TW/approval.json | 62 + .../locales/langs/zh-TW/authentication.json | 41 + .../web-ele/src/locales/langs/zh-TW/chat.json | 91 + .../src/locales/langs/zh-TW/common.json | 188 ++ .../src/locales/langs/zh-TW/contract.json | 150 + .../locales/langs/zh-TW/dashboard-design.json | 894 ++++++ .../src/locales/langs/zh-TW/data-source.json | 283 ++ .../langs/zh-TW/database-connection.json | 52 + .../locales/langs/zh-TW/database-manager.json | 399 +++ .../locales/langs/zh-TW/database-monitor.json | 138 + .../src/locales/langs/zh-TW/demos.json | 48 + .../web-ele/src/locales/langs/zh-TW/dept.json | 41 + .../web-ele/src/locales/langs/zh-TW/dict.json | 22 + .../locales/langs/zh-TW/dingtalk-sync.json | 86 + .../langs/zh-TW/document-generator.json | 527 ++++ .../locales/langs/zh-TW/electronic-seal.json | 60 + .../src/locales/langs/zh-TW/email.json | 125 + .../src/locales/langs/zh-TW/feishu-sync.json | 65 + .../src/locales/langs/zh-TW/file-manager.json | 42 + .../src/locales/langs/zh-TW/form-manager.json | 851 ++++++ .../src/locales/langs/zh-TW/loginLog.json | 60 + .../src/locales/langs/zh-TW/menu-title.json | 61 + .../web-ele/src/locales/langs/zh-TW/menu.json | 63 + .../src/locales/langs/zh-TW/message.json | 90 + .../src/locales/langs/zh-TW/org-chart.json | 10 + .../src/locales/langs/zh-TW/page-manager.json | 126 + .../web-ele/src/locales/langs/zh-TW/page.json | 14 + .../src/locales/langs/zh-TW/permission.json | 74 + .../web-ele/src/locales/langs/zh-TW/post.json | 34 + .../locales/langs/zh-TW/redis-manager.json | 73 + .../locales/langs/zh-TW/redis-monitor.json | 181 ++ .../locales/langs/zh-TW/report-manager.json | 437 +++ .../web-ele/src/locales/langs/zh-TW/role.json | 134 + .../src/locales/langs/zh-TW/scheduler.json | 119 + .../locales/langs/zh-TW/server-monitor.json | 167 ++ .../locales/langs/zh-TW/system-config.json | 75 + .../src/locales/langs/zh-TW/system.json | 10 + .../src/locales/langs/zh-TW/ui-config.json | 172 ++ .../web-ele/src/locales/langs/zh-TW/ui.json | 15 + .../src/locales/langs/zh-TW/user-avatar.json | 13 + .../web-ele/src/locales/langs/zh-TW/user.json | 69 + .../src/locales/langs/zh-TW/wecom-sync.json | 67 + .../langs/zh-TW/workflow-designer.json | 709 +++++ .../src/locales/langs/zh-TW/workflow.json | 310 ++ web/apps/web-ele/src/main.ts | 72 +- web/apps/web-ele/src/preferences.ts | 40 +- web/apps/web-ele/src/store/app-context.ts | 34 +- web/apps/web-ele/src/store/auth.ts | 13 +- .../web-ele/src/types/zq-smart-table/field.ts | 133 + .../web-ele/src/types/zq-smart-table/index.ts | 2 + .../web-ele/src/types/zq-smart-table/table.ts | 140 + web/apps/web-ele/src/utils/column-sql.ts | 592 ++++ web/apps/web-ele/src/utils/constraint-sql.ts | 483 ++++ web/apps/web-ele/src/utils/database-types.ts | 393 +++ .../web-ele/src/utils/form-data-filters.ts | 140 + .../web-ele/src/utils/form-lifecycle-hooks.ts | 531 ++++ .../src/utils/form-list-application-id.ts | 21 + web/apps/web-ele/src/utils/index-sql.ts | 217 ++ .../web-ele/src/utils/reverse-auto-fill.ts | 90 + .../views/_core/account-settings/index.vue | 2 +- .../src/views/_core/agent-chat/index.vue | 121 +- .../src/views/_core/announcement/index.vue | 25 +- .../src/views/_core/application/index.vue | 15 + .../views/_core/authentication/code-login.vue | 5 +- .../_core/authentication/forget-password.vue | 5 +- .../src/views/_core/authentication/login.vue | 571 +++- .../_core/authentication/oauth-callback.vue | 5 +- .../_core/authentication/qrcode-login.vue | 2 +- .../views/_core/authentication/register.vue | 5 +- .../src/views/_core/fallback/coming-soon.vue | 2 +- .../src/views/_core/fallback/forbidden.vue | 2 +- .../views/_core/fallback/internal-error.vue | 2 +- .../src/views/_core/fallback/not-found.vue | 2 +- .../src/views/_core/fallback/offline.vue | 2 +- .../src/views/_core/file-preview/index.vue | 150 +- .../web-ele/src/views/_core/menu/index.vue | 2 +- .../_core/menu/modules/menu-form-modal.vue | 5 +- .../views/_core/menu/modules/menu-tree.vue | 9 +- .../web-ele/src/views/_core/message/index.vue | 4 +- .../src/views/_core/permission/index.vue | 2 +- .../modules/permission-left-panel.vue | 5 +- web/apps/web-ele/src/views/_core/role/data.ts | 13 +- .../web-ele/src/views/_core/role/index.vue | 2 +- .../role/modules/permission-assign-panel.vue | 24 +- .../src/views/_core/system-config/index.vue | 220 +- .../modules/dingtalk-sync-form.vue | 838 ++++++ .../modules/feishu-sync-form.vue | 575 ++++ .../system-config/modules/wecom-sync-form.vue | 562 ++++ .../ui-config/modules/app-settings-form.vue | 4 +- web/apps/web-ele/src/views/_core/user/data.ts | 16 + .../web-ele/src/views/_core/user/index.vue | 6 +- .../src/views/_core/user/modules/form.vue | 4 +- .../agent/components/AgentChatPanel.vue | 340 ++- .../agent/components/PublishToMenuDialog.vue | 16 +- .../views/ai-platform/agent/editor/index.vue | 10 +- .../src/views/ai-platform/agent/index.vue | 19 +- .../ai-platform/knowledge/detail/index.vue | 2 +- .../src/views/ai-platform/knowledge/index.vue | 14 +- .../components/RunLogTimeline.vue | 60 +- .../components/WorkflowReadonlyCanvas.vue | 55 +- .../views/ai-platform/workflow-runs/data.ts | 21 - .../views/ai-platform/workflow-runs/index.vue | 67 +- .../workflow-runs/modules/detail-dialog.vue | 1195 +------- .../workflow/editor/components/ChatPanel.vue | 615 +++- .../editor/components/ConfirmModeSelect.vue | 121 + .../editor/components/DbConfigSummary.vue | 40 + .../editor/components/DbConnectionSelect.vue | 107 + .../editor/components/DbDatabaseSelect.vue | 118 + .../components/DbNodeConnectionBadge.vue | 36 + .../editor/components/NodeResultCard.vue | 10 +- .../workflow/editor/components/Panel.vue | 80 + .../workflow/editor/components/RunPanel.vue | 43 +- .../editor/components/SchemaConfigDialog.vue | 22 +- .../editor/components/TableSelectDialog.vue | 577 ++++ .../editor/components/TableSelectModal.vue | 1297 +++++++++ .../editor/components/VariableSelector.vue | 620 +++- .../ai-platform/workflow/editor/index.vue | 528 +++- .../workflow/editor/nodes/AppCreateNode.vue | 59 + .../workflow/editor/nodes/AppDesignNode.vue | 57 + .../workflow/editor/nodes/AppSettingsNode.vue | 69 + .../workflow/editor/nodes/AppUpdateNode.vue | 57 + .../workflow/editor/nodes/BaseNode.vue | 344 ++- .../editor/nodes/DashboardBasicInfoNode.vue | 58 + .../editor/nodes/DashboardCreateNode.vue | 65 + .../editor/nodes/DashboardDesignNode.vue | 55 + .../editor/nodes/DashboardPublishNode.vue | 57 + .../workflow/editor/nodes/DbDeleteNode.vue | 68 + .../workflow/editor/nodes/DbInsertNode.vue | 85 + .../workflow/editor/nodes/DbQueryNode.vue | 81 + .../workflow/editor/nodes/DbSqlNode.vue | 61 + .../workflow/editor/nodes/DbUpdateNode.vue | 93 + .../editor/nodes/FormBasicInfoNode.vue | 75 + .../workflow/editor/nodes/FormCreateNode.vue | 51 + .../workflow/editor/nodes/FormDataNode.vue | 189 ++ .../editor/nodes/FormDatabaseCreateNode.vue | 74 + .../editor/nodes/FormDatabaseDesignNode.vue | 80 + .../editor/nodes/FormListDesignNode.vue | 86 + .../workflow/editor/nodes/FormPublishNode.vue | 56 + .../editor/nodes/FormUIDesignNode.vue | 89 + .../nodes/SnowflakeCortexAnalystNode.vue | 77 + .../editor/nodes/SnowflakeCortexLLMNode.vue | 114 + .../editor/nodes/SystemSummaryNode.vue | 80 + .../workflow/editor/nodes/TextToSqlNode.vue | 62 + .../workflow/editor/panels/AppCreatePanel.vue | 189 ++ .../workflow/editor/panels/AppDesignPanel.vue | 114 + .../editor/panels/AppSettingsPanel.vue | 196 ++ .../workflow/editor/panels/AppUpdatePanel.vue | 103 + .../workflow/editor/panels/CodePanel.vue | 10 +- .../editor/panels/DashboardBasicInfoPanel.vue | 164 ++ .../editor/panels/DashboardCreatePanel.vue | 135 + .../editor/panels/DashboardDesignPanel.vue | 119 + .../editor/panels/DashboardPublishPanel.vue | 165 ++ .../workflow/editor/panels/DbDeletePanel.vue | 239 ++ .../workflow/editor/panels/DbInsertPanel.vue | 365 +++ .../workflow/editor/panels/DbQueryPanel.vue | 275 ++ .../workflow/editor/panels/DbSqlPanel.vue | 364 +++ .../workflow/editor/panels/DbUpdatePanel.vue | 353 +++ .../editor/panels/FormBasicInfoPanel.vue | 206 ++ .../editor/panels/FormCreatePanel.vue | 228 ++ .../workflow/editor/panels/FormDataPanel.vue | 308 ++ .../editor/panels/FormDatabaseCreatePanel.vue | 206 ++ .../editor/panels/FormDatabaseDesignPanel.vue | 360 +++ .../editor/panels/FormListDesignPanel.vue | 286 ++ .../editor/panels/FormPublishPanel.vue | 132 + .../editor/panels/FormUIDesignPanel.vue | 248 ++ .../workflow/editor/panels/HttpPanel.vue | 3 +- .../workflow/editor/panels/IntentPanel.vue | 4 +- .../workflow/editor/panels/LLMPanel.vue | 4 - .../panels/SnowflakeCortexAnalystPanel.vue | 432 +++ .../editor/panels/SnowflakeCortexLLMPanel.vue | 711 +++++ .../workflow/editor/panels/StartPanel.vue | 29 +- .../editor/panels/SystemSummaryPanel.vue | 214 ++ .../workflow/editor/panels/TextToSqlPanel.vue | 331 +++ .../src/views/ai-platform/workflow/index.vue | 425 +-- .../workflow/shared/useRunReplay.ts | 9 +- .../workflow/shared/workflowNodeTypes.ts | 92 +- .../dashboard/analytics/analytics-trends.vue | 33 +- .../analytics/analytics-visits-data.vue | 41 +- .../analytics/analytics-visits-sales.vue | 17 +- .../analytics/analytics-visits-source.vue | 13 +- .../dashboard/analytics/analytics-visits.vue | 15 +- .../src/views/dashboard/analytics/index.vue | 65 +- .../src/views/dashboard/workspace/index.vue | 749 ++--- .../views/online-dev/page-render/index.vue | 85 + web/apps/web-ele/vite.config.mts | 2 +- web/packages/effects/layouts/src/index.ts | 1 + .../src/widgets/theme-toggle/theme-button.vue | 83 +- web/pnpm-lock.yaml | 18 + 385 files changed, 61461 insertions(+), 5886 deletions(-) create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/DataTable.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/FilterDate.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/FilterDateRange.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/FilterInput.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/FilterSelect.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/IframeWidget.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/ImageCarousel.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/ImageWidget.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/ProgressCard.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/RankingList.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/StatCard.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/TodoList.vue create mode 100644 web/apps/web-ele/src/components/dashboard-design/components/widgets/VideoPlayer.vue create mode 100644 web/apps/web-ele/src/components/form-editor/AppDesignPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/AppSettingsPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/BasicInfoEditor.vue create mode 100644 web/apps/web-ele/src/components/form-editor/DashboardBasicInfoConfirmPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/DashboardDesignConfirmPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/DashboardPublishConfirmPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/DesignEditorPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/FormEditorContent.vue create mode 100644 web/apps/web-ele/src/components/form-editor/LightConfirmPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/PageBasicInfoEditor.vue create mode 100644 web/apps/web-ele/src/components/form-editor/PageEditorContent.vue create mode 100644 web/apps/web-ele/src/components/form-editor/PagePublishInfoEditor.vue create mode 100644 web/apps/web-ele/src/components/form-editor/PublishInfoEditor.vue create mode 100644 web/apps/web-ele/src/components/form-editor/SystemSummaryConfirmPanel.vue create mode 100644 web/apps/web-ele/src/components/form-editor/index.ts create mode 100644 web/apps/web-ele/src/components/image-cropper/image-cropper.vue create mode 100644 web/apps/web-ele/src/components/image-cropper/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/ai-image-ocr/ai-image-ocr.vue create mode 100644 web/apps/web-ele/src/components/zq-form/ai-image-ocr/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/ai-image-ocr/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/barcode-generator/barcode-generator.vue create mode 100644 web/apps/web-ele/src/components/zq-form/barcode-generator/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/barcode-generator/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/code-editor/languages.ts create mode 100644 web/apps/web-ele/src/components/zq-form/code-editor/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/NotionEditor.vue create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/components/SlashCommandMenu.vue create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/components/TableHandles.vue create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/components/TableMenu.vue create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/extensions/slash-command.ts create mode 100644 web/apps/web-ele/src/components/zq-form/notion-editor/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/qrcode-generator/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/qrcode-generator/qrcode-generator.vue create mode 100644 web/apps/web-ele/src/components/zq-form/qrcode-generator/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/ResizableImageComponent.vue create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/ResizableVideoComponent.vue create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/attachment.ts create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/font-size.ts create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/resizable-image.ts create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/extensions/video.ts create mode 100644 web/apps/web-ele/src/components/zq-form/rich-text-editor/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/scan-input/ScanDialog.vue create mode 100644 web/apps/web-ele/src/components/zq-form/scan-input/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/scan-input/scan-input.vue create mode 100644 web/apps/web-ele/src/components/zq-form/scan-input/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/shared/formatCodeContent.ts create mode 100644 web/apps/web-ele/src/components/zq-form/shared/types.ts create mode 100644 web/apps/web-ele/src/components/zq-form/shared/useCodeContent.ts create mode 100644 web/apps/web-ele/src/components/zq-form/shared/validateBarcode.ts create mode 100644 web/apps/web-ele/src/components/zq-form/signature-pad/MobileSignatureDialog.vue create mode 100644 web/apps/web-ele/src/components/zq-form/signature-pad/SignatureDialog.vue create mode 100644 web/apps/web-ele/src/components/zq-form/signature-pad/index.ts create mode 100644 web/apps/web-ele/src/components/zq-form/signature-pad/signature-pad.vue create mode 100644 web/apps/web-ele/src/components/zq-form/signature-pad/types.ts create mode 100644 web/apps/web-ele/src/locales/langs/en-US/about.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/account-settings.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/ai-platform.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/announcement.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/apiToken.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/application.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/approval.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/authentication.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/chat.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/common.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/contract.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/core.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/dashboard-design.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/data-source.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/database-connection.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/database-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/database-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/demos.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/dept.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/dict.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/dingtalk-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/document-generator.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/electronic-seal.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/feishu-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/file-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/form-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/loginLog.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/menu-title.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/menu.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/message.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/org-chart.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/page-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/page.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/permission.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/post.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/redis-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/redis-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/report-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/role.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/scheduler.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/server-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/system-config.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/system.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/ui-config.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/ui.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/user-avatar.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/user.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/wecom-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/workflow-designer.json create mode 100644 web/apps/web-ele/src/locales/langs/en-US/workflow.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/account-settings.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/data-source.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/database-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/form-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/page-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/report-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/workflow-designer.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/workflow.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/about.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/account-settings.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/ai-platform.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/announcement.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/apiToken.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/application.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/approval.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/authentication.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/chat.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/common.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/contract.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/dashboard-design.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/data-source.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/database-connection.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/database-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/database-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/demos.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/dept.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/dict.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/dingtalk-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/document-generator.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/electronic-seal.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/email.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/feishu-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/file-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/form-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/loginLog.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/menu-title.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/menu.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/message.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/org-chart.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/page-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/page.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/permission.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/post.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/redis-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/redis-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/report-manager.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/role.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/scheduler.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/server-monitor.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/system-config.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/system.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/ui-config.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/ui.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/user-avatar.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/user.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/wecom-sync.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/workflow-designer.json create mode 100644 web/apps/web-ele/src/locales/langs/zh-TW/workflow.json create mode 100644 web/apps/web-ele/src/types/zq-smart-table/field.ts create mode 100644 web/apps/web-ele/src/types/zq-smart-table/index.ts create mode 100644 web/apps/web-ele/src/types/zq-smart-table/table.ts create mode 100644 web/apps/web-ele/src/utils/column-sql.ts create mode 100644 web/apps/web-ele/src/utils/constraint-sql.ts create mode 100644 web/apps/web-ele/src/utils/database-types.ts create mode 100644 web/apps/web-ele/src/utils/form-data-filters.ts create mode 100644 web/apps/web-ele/src/utils/form-lifecycle-hooks.ts create mode 100644 web/apps/web-ele/src/utils/form-list-application-id.ts create mode 100644 web/apps/web-ele/src/utils/index-sql.ts create mode 100644 web/apps/web-ele/src/utils/reverse-auto-fill.ts create mode 100644 web/apps/web-ele/src/views/_core/system-config/modules/dingtalk-sync-form.vue create mode 100644 web/apps/web-ele/src/views/_core/system-config/modules/feishu-sync-form.vue create mode 100644 web/apps/web-ele/src/views/_core/system-config/modules/wecom-sync-form.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/ConfirmModeSelect.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/DbConfigSummary.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/DbConnectionSelect.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/DbDatabaseSelect.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/DbNodeConnectionBadge.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/TableSelectDialog.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/components/TableSelectModal.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/AppCreateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/AppDesignNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/AppSettingsNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/AppUpdateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DashboardBasicInfoNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DashboardCreateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DashboardDesignNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DashboardPublishNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DbDeleteNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DbInsertNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DbQueryNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DbSqlNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/DbUpdateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormBasicInfoNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormCreateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormDataNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormDatabaseCreateNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormDatabaseDesignNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormListDesignNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormPublishNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/FormUIDesignNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/SnowflakeCortexAnalystNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/SnowflakeCortexLLMNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/SystemSummaryNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/nodes/TextToSqlNode.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/AppCreatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/AppDesignPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/AppSettingsPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/AppUpdatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DashboardBasicInfoPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DashboardCreatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DashboardDesignPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DashboardPublishPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DbDeletePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DbInsertPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DbQueryPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DbSqlPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/DbUpdatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormBasicInfoPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormCreatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormDataPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormDatabaseCreatePanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormDatabaseDesignPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormListDesignPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormPublishPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/FormUIDesignPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/SnowflakeCortexAnalystPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/SnowflakeCortexLLMPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/SystemSummaryPanel.vue create mode 100644 web/apps/web-ele/src/views/ai-platform/workflow/editor/panels/TextToSqlPanel.vue create mode 100644 web/apps/web-ele/src/views/online-dev/page-render/index.vue diff --git a/.gitignore b/.gitignore index 854e995..008fab2 100644 --- a/.gitignore +++ b/.gitignore @@ -187,8 +187,15 @@ docker/web/index/* uniapp-zq/ web/apps/web-ele/src/components/screen-design/ web/apps/web-ele/src/components/form-design/ +web/apps/web-ele/src/components/contract-design/ +web/apps/web-ele/src/components/import-export-manager/ +web/apps/web-ele/src/components/wiki/ +web/apps/web-ele/src/components/zq-draw/ web/apps/web-ele/src/components/zq-editor/ +web/apps/web-ele/src/components/zq-smart-table/ +web/apps/web-ele/src/components/zq-whiteboard/ web/apps/web-ele/src/components/whiteboard/ +web/apps/web-ele/src/components/workflow/ web/apps/web-ele/src/views/online-dev/ !web/apps/web-ele/src/views/online-dev/ web/apps/web-ele/src/views/online-dev/* @@ -252,11 +259,26 @@ web/docs/src/public/guide/ web/packages/zq-univer/ web/apps/web-ele/src/locales/langs/*/dashboard-design.json* !web/apps/web-ele/src/locales/langs/*/dashboard-design.json +web/apps/web-ele/src/locales/langs/*/*.json_new +web/apps/web-ele/src/locales/langs/*/*.json_sync +web/apps/web-ele/src/locales/langs/*/contract-design.json +web/apps/web-ele/src/locales/langs/*/draw.json web/apps/web-ele/src/locales/langs/*/form-design.json web/apps/web-ele/src/locales/langs/*/screen-design.json +web/apps/web-ele/src/locales/langs/*/whiteboard.json +web/apps/web-ele/src/locales/langs/*/wiki.json +web/apps/web-ele/src/locales/langs/*/zq-editor.json +web/apps/web-ele/src/locales/langs/*/zq-smart-table.json web/apps/web-ele/src/router/routes/modules/form-layout.ts web/apps/web-ele/src/router/routes/modules/online-dev.ts +web/apps/web-ele/src/router/routes/modules/chat.ts +web/apps/web-ele/src/router/routes/modules/demos.ts web/apps/web-ele/src/router/routes/modules/zq-smart-table.ts +web/apps/web-ele/src/api/smart-table.ts +web/apps/web-ele/src/composables/use-offline-cache.ts +web/apps/web-ele/src/store/wiki.ts +web/apps/web-ele/src/store/zq-smart-table.ts +web/apps/web-ele/src/styles/zq-smart-table/ web/apps/web-ele/src/components/report-design/ backend-fastapi/online_dev/ !backend-fastapi/online_dev/ @@ -329,6 +351,7 @@ web/apps/web-ele/src/views/_core/database-monitor/ !web/apps/web-ele/src/views/_core/database-monitor/ !web/apps/web-ele/src/views/_core/database-monitor/** web/apps/web-ele/src/views/_core/demo/ +web/apps/web-ele/src/views/_core/login-log/README.md web/apps/web-ele/src/views/_core/mobile-signature/ web/apps/web-ele/src/views/_core/redis-manager/ web/apps/web-ele/src/views/_core/redis-monitor/ diff --git a/web/apps/web-ele/package.json b/web/apps/web-ele/package.json index 6111541..681dff2 100644 --- a/web/apps/web-ele/package.json +++ b/web/apps/web-ele/package.json @@ -54,6 +54,7 @@ "grid-layout-plus": "^1.1.1", "pinia": "catalog:", "vue": "catalog:", + "vuedraggable": "^4.1.0", "vue-router": "catalog:" }, "devDependencies": { diff --git a/web/apps/web-ele/src/adapter/component/index.ts b/web/apps/web-ele/src/adapter/component/index.ts index 79b064f..59f6ad9 100644 --- a/web/apps/web-ele/src/adapter/component/index.ts +++ b/web/apps/web-ele/src/adapter/component/index.ts @@ -5,84 +5,72 @@ import type { Component } from 'vue'; -import type { BaseFormComponentType } from '@vben-core/form-ui'; +import type { BaseFormComponentType } from '@vben/common-ui'; import type { Recordable } from '@vben/types'; import { defineAsyncComponent, defineComponent, h, ref } from 'vue'; -import { globalShareState } from '@vben-core/shared/global-state'; +import { ApiComponent, globalShareState } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { ApiComponent } from '@vben/common-ui/es/api-component'; +import { ElNotification } from 'element-plus'; const ElButton = defineAsyncComponent(() => - import('element-plus/es/components/button/index').then((res) => res.ElButton), + Promise.all([ + import('element-plus/es/components/button/index'), + import('element-plus/es/components/button/style/css'), + ]).then(([res]) => res.ElButton), ); const ElCheckbox = defineAsyncComponent(() => - import('element-plus/es/components/checkbox/index').then( - (res) => res.ElCheckbox, - ), + Promise.all([ + import('element-plus/es/components/checkbox/index'), + import('element-plus/es/components/checkbox/style/css'), + ]).then(([res]) => res.ElCheckbox), ); const ElCheckboxButton = defineAsyncComponent(() => - import('element-plus/es/components/checkbox/index').then( - (res) => res.ElCheckboxButton, - ), + Promise.all([ + import('element-plus/es/components/checkbox/index'), + import('element-plus/es/components/checkbox-button/style/css'), + ]).then(([res]) => res.ElCheckboxButton), ); const ElCheckboxGroup = defineAsyncComponent(() => - import('element-plus/es/components/checkbox/index').then( - (res) => res.ElCheckboxGroup, - ), + Promise.all([ + import('element-plus/es/components/checkbox/index'), + import('element-plus/es/components/checkbox-group/style/css'), + ]).then(([res]) => res.ElCheckboxGroup), ); -const ElDivider = defineAsyncComponent(() => - import('element-plus/es/components/divider/index').then( - (res) => res.ElDivider, - ), -); -const ElInput = defineAsyncComponent(() => - import('element-plus/es/components/input/index').then((res) => res.ElInput), -); -const ElRadio = defineAsyncComponent(() => - import('element-plus/es/components/radio/index').then((res) => res.ElRadio), -); -const ElRadioButton = defineAsyncComponent(() => - import('element-plus/es/components/radio/index').then( - (res) => res.ElRadioButton, - ), -); -const ElRadioGroup = defineAsyncComponent(() => - import('element-plus/es/components/radio/index').then( - (res) => res.ElRadioGroup, - ), -); -const ElSpace = defineAsyncComponent(() => - import('element-plus/es/components/space/index').then((res) => res.ElSpace), +const ElCascader = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/cascader/index'), + import('element-plus/es/components/cascader/style/css'), + ]).then(([res]) => res.ElCascader), ); const ElDatePicker = defineAsyncComponent(() => - import('element-plus/es/components/date-picker/index').then( - (res) => res.ElDatePicker, + Promise.all([ + import('element-plus/es/components/date-picker/index'), + import('element-plus/es/components/date-picker/style/css'), + ]).then(([res]) => res.ElDatePicker), +); +const DeptSelector = defineAsyncComponent(() => + import('#/components/zq-form/dept-selector/dept-selector.vue').then( + (res) => res.default, ), ); -const ElInputNumber = defineAsyncComponent(() => - import('element-plus/es/components/input-number/index').then( - (res) => res.ElInputNumber, +const PostSelector = defineAsyncComponent(() => + import('#/components/zq-form/post-selector/post-selector.vue').then( + (res) => res.default, ), ); -const ElSelectV2 = defineAsyncComponent(() => - import('element-plus/es/components/select-v2/index').then( - (res) => res.ElSelectV2, - ), -); -const ElTreeSelect = defineAsyncComponent(() => - import('element-plus/es/components/tree-select/index').then( - (res) => res.ElTreeSelect, - ), -); - const RoleSelector = defineAsyncComponent(() => import('#/components/zq-form/role-selector/role-selector.vue').then( (res) => res.default, ), ); +const FileSelector = defineAsyncComponent(() => + import('#/components/zq-form/file-selector/file-selector.vue').then( + (res) => res.default, + ), +); const ImageSelector = defineAsyncComponent(() => import('#/components/zq-form/image-selector/image-selector.vue').then( (res) => res.default, @@ -93,11 +81,106 @@ const UserSelector = defineAsyncComponent(() => (res) => res.default, ), ); +const JsonEditor = defineAsyncComponent(() => + import('#/components/json-editor/json-editor.vue').then((res) => res.default), +); +const FormSelector = defineAsyncComponent(() => + import('#/components/zq-form/form-selector/form-selector.vue').then( + (res) => res.default, + ), +); +const TableSelector = defineAsyncComponent(() => + import('#/components/zq-form/table-selector/table-selector.vue').then( + (res) => res.default, + ), +); const ZqIconPicker = defineAsyncComponent(() => import('#/components/zq-form/zq-icon-picker/zq-icon-picker.vue').then( (res) => res.default, ), ); +const CronSelector = defineAsyncComponent(() => + import('#/components/zq-form/cron-selector/cron-selector.vue').then( + (res) => res.default, + ), +); +const CodeEditor = defineAsyncComponent(() => + import('#/components/zq-form/code-editor/code-editor.vue').then( + (res) => res.default, + ), +); +const ElDivider = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/divider/index'), + import('element-plus/es/components/divider/style/css'), + ]).then(([res]) => res.ElDivider), +); +const ElInput = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/input/index'), + import('element-plus/es/components/input/style/css'), + ]).then(([res]) => res.ElInput), +); +const ElInputNumber = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/input-number/index'), + import('element-plus/es/components/input-number/style/css'), + ]).then(([res]) => res.ElInputNumber), +); +const ElRadio = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/radio/index'), + import('element-plus/es/components/radio/style/css'), + ]).then(([res]) => res.ElRadio), +); +const ElRadioButton = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/radio/index'), + import('element-plus/es/components/radio-button/style/css'), + ]).then(([res]) => res.ElRadioButton), +); +const ElRadioGroup = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/radio/index'), + import('element-plus/es/components/radio-group/style/css'), + ]).then(([res]) => res.ElRadioGroup), +); +const ElSelectV2 = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/select-v2/index'), + import('element-plus/es/components/select-v2/style/css'), + ]).then(([res]) => res.ElSelectV2), +); +const ElSpace = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/space/index'), + import('element-plus/es/components/space/style/css'), + ]).then(([res]) => res.ElSpace), +); +const ElSwitch = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/switch/index'), + import('element-plus/es/components/switch/style/css'), + ]).then(([res]) => res.ElSwitch), +); +const ElTimePicker = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/time-picker/index'), + import('element-plus/es/components/time-picker/style/css'), + ]).then(([res]) => res.ElTimePicker), +); +const ElTreeSelect = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/tree-select/index'), + import('element-plus/es/components/tree-select/style/css'), + ]).then(([res]) => res.ElTreeSelect), +); +const ElUpload = defineAsyncComponent(() => + Promise.all([ + import('element-plus/es/components/upload/index'), + import('element-plus/es/components/upload/style/css'), + ]).then(([res]) => res.ElUpload), +); const withDefaultPlaceholder = ( component: T, @@ -105,7 +188,7 @@ const withDefaultPlaceholder = ( componentProps: Recordable = {}, ) => { return defineComponent({ - name: (component as any).name, + name: component.name, inheritAttrs: false, setup: (props: any, { attrs, expose, slots }) => { const placeholder = @@ -134,33 +217,41 @@ const withDefaultPlaceholder = ( }; // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 -let initialized = false; - export type ComponentType = | 'ApiSelect' | 'ApiTreeSelect' + | 'Cascader' | 'Checkbox' | 'CheckboxGroup' | 'CodeEditor' + | 'CronSelector' | 'DatePicker' + | 'DeptSelector' | 'Divider' + | 'FileSelector' + | 'FormSelector' | 'IconPicker' | 'ImageSelector' | 'Input' | 'InputNumber' + | 'JsonEditor' + | 'PostSelector' | 'RadioGroup' | 'RoleSelector' | 'Select' | 'Space' + | 'Switch' + | 'TableSelector' + | 'TaskArgsEditor' + | 'TaskKwargsEditor' | 'Textarea' + | 'TimePicker' + | 'TreeSelect' + | 'Upload' | 'UserSelector' | BaseFormComponentType; -function initComponentAdapter() { - if (initialized) { - return; - } - +async function initComponentAdapter() { const components: Partial> = { // 如果你的组件体积比较大,可以使用异步加载 // Button: () => @@ -192,26 +283,24 @@ function initComponentAdapter() { visibleEvent: 'onVisibleChange', }, ), + Cascader: withDefaultPlaceholder(ElCascader, 'select'), Checkbox: ElCheckbox, CheckboxGroup: (props, { attrs, slots }) => { - const mergedProps = { ...props, ...attrs } as Recordable; let defaultSlot; if (Reflect.has(slots, 'default')) { defaultSlot = slots.default; } else { - const { options, isButton } = mergedProps; + const { options, isButton } = attrs; if (Array.isArray(options)) { defaultSlot = () => options.map((option) => - h(isButton ? ElCheckboxButton : ElCheckbox, option, () => - option.label ?? option.value, - ), + h(isButton ? ElCheckboxButton : ElCheckbox, option), ); } } return h( ElCheckboxGroup, - mergedProps, + { ...props, ...attrs }, { ...slots, default: defaultSlot }, ); }, @@ -223,36 +312,47 @@ function initComponentAdapter() { PrimaryButton: (props, { attrs, slots }) => { return h(ElButton, { ...props, attrs, type: 'primary' }, slots); }, + DeptSelector: withDefaultPlaceholder(DeptSelector, 'select'), + PostSelector: withDefaultPlaceholder(PostSelector, 'select'), RoleSelector: withDefaultPlaceholder(RoleSelector, 'select'), Divider: ElDivider, + FileSelector: withDefaultPlaceholder(FileSelector, 'select'), + FormSelector: withDefaultPlaceholder(FormSelector, 'select'), + TableSelector: withDefaultPlaceholder(TableSelector, 'select'), IconPicker: withDefaultPlaceholder(ZqIconPicker, 'select'), ImageSelector: withDefaultPlaceholder(ImageSelector, 'select'), Input: withDefaultPlaceholder(ElInput, 'input'), Textarea: withDefaultPlaceholder(ElInput, 'input', { type: 'textarea' }), - CodeEditor: withDefaultPlaceholder(ElInput, 'input', { - autosize: { minRows: 8 }, - type: 'textarea', - }), + JsonEditor, + CodeEditor, + CronSelector, + TaskArgsEditor: defineAsyncComponent(() => + import('#/views/_core/scheduler/modules/task-args-editor.vue').then( + (res) => res.default, + ), + ), + TaskKwargsEditor: defineAsyncComponent(() => + import('#/views/_core/scheduler/modules/task-kwargs-editor.vue').then( + (res) => res.default, + ), + ), InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), RadioGroup: (props, { attrs, slots }) => { - const mergedProps = { ...props, ...attrs } as Recordable; let defaultSlot; if (Reflect.has(slots, 'default')) { defaultSlot = slots.default; } else { - const { options, isButton } = mergedProps; + const { options } = attrs; if (Array.isArray(options)) { defaultSlot = () => options.map((option) => - h(isButton ? ElRadioButton : ElRadio, option, () => - option.label ?? option.value, - ), + h(attrs.isButton ? ElRadioButton : ElRadio, option), ); } } return h( ElRadioGroup, - mergedProps, + { ...props, ...attrs }, { ...slots, default: defaultSlot }, ); }, @@ -260,6 +360,28 @@ function initComponentAdapter() { return h(ElSelectV2, { ...props, attrs }, slots); }, Space: ElSpace, + Switch: ElSwitch, + TimePicker: (props, { attrs, slots }) => { + const { name, id, isRange } = props; + const extraProps: Recordable = {}; + if (isRange) { + if (name && !Array.isArray(name)) { + extraProps.name = [name, `${name}_end`]; + } + if (id && !Array.isArray(id)) { + extraProps.id = [id, `${id}_end`]; + } + } + return h( + ElTimePicker, + { + ...props, + ...attrs, + ...extraProps, + }, + slots, + ); + }, DatePicker: (props, { attrs, slots }) => { const { name, id, type } = props; const extraProps: Recordable = {}; @@ -281,28 +403,25 @@ function initComponentAdapter() { slots, ); }, + TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'), + Upload: ElUpload, UserSelector: withDefaultPlaceholder(UserSelector, 'select'), }; // 将组件注册到全局共享状态中 globalShareState.setComponents(components); - initialized = true; // 定义全局共享状态中的消息提示 globalShareState.defineMessage({ // 复制成功消息提示 - copyPreferencesSuccess: (title: string, content: string) => { - void import('element-plus/es/components/notification/index').then( - ({ ElNotification }) => { - ElNotification({ - title, - message: content, - position: 'bottom-right', - duration: 0, - type: 'success', - }); - }, - ); + copyPreferencesSuccess: (title, content) => { + ElNotification({ + title, + message: content, + position: 'bottom-right', + duration: 0, + type: 'success', + }); }, }); } diff --git a/web/apps/web-ele/src/adapter/form.ts b/web/apps/web-ele/src/adapter/form.ts index cea1d89..936c3fe 100644 --- a/web/apps/web-ele/src/adapter/form.ts +++ b/web/apps/web-ele/src/adapter/form.ts @@ -1,23 +1,14 @@ import type { VbenFormSchema as FormSchema, VbenFormProps, -} from '@vben-core/form-ui'; +} from '@vben/common-ui'; import type { ComponentType } from './component'; -import { setupVbenForm, useVbenForm as useForm, z } from '@vben-core/form-ui'; +import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { initComponentAdapter } from './component'; - -let initialized = false; - -function initSetupVbenForm() { - if (initialized) { - return; - } - - initComponentAdapter(); +async function initSetupVbenForm() { setupVbenForm({ config: { modelPropNameMap: { @@ -40,13 +31,9 @@ function initSetupVbenForm() { }, }, }); - initialized = true; } -const useVbenForm = ((...args: Parameters>) => { - initSetupVbenForm(); - return useForm(...args); -}) as typeof useForm; +const useVbenForm = useForm; export { initSetupVbenForm, useVbenForm, z }; diff --git a/web/apps/web-ele/src/adapter/vxe-table.ts b/web/apps/web-ele/src/adapter/vxe-table.ts index 330e025..2c24656 100644 --- a/web/apps/web-ele/src/adapter/vxe-table.ts +++ b/web/apps/web-ele/src/adapter/vxe-table.ts @@ -27,16 +27,17 @@ setupVbenVxeTable({ columnConfig: { resizable: true, }, + minHeight: 180, formConfig: { + // 全局禁用vxe-table的表单配置,使用formOptions enabled: false, }, - minHeight: 180, proxyConfig: { autoLoad: true, response: { - list: '', result: 'items', total: 'total', + list: '', }, showActionMsg: true, showResponseMsg: false, @@ -47,31 +48,34 @@ setupVbenVxeTable({ } as VxeTableGridOptions, }); + // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { renderTableDefault(_renderOpts, params) { const { column, row } = params; const src = row[column.field]; - return h(ElImage, { previewSrcList: [src], src }); + return h(ElImage, { src, previewSrcList: [src] }); }, }); + // 表格配置项可以用 cellRender: { name: 'CellLink' }, vxeUI.renderer.add('CellLink', { renderTableDefault(renderOpts) { const { props } = renderOpts; return h( ElButton, - { link: true, size: 'small' }, + { size: 'small', link: true }, { default: () => props?.text }, ); }, }); + // 单元格渲染: Tag vxeUI.renderer.add('CellTag', { renderTableDefault({ options, props }, { column, row }) { const value = get(row, column.field); const tagOptions = options ?? [ - { label: $t('common.enabled'), type: 'success', value: true }, - { label: $t('common.disabled'), type: 'danger', value: false }, + { type: 'success', label: $t('common.enabled'), value: true }, + { type: 'danger', label: $t('common.disabled'), value: false }, ]; const tagItem = tagOptions.find((item) => item.value === value); return h( @@ -85,24 +89,36 @@ setupVbenVxeTable({ }, }); + /** + * 注册表格的操作按钮渲染器 + */ vxeUI.renderer.add('CellOperation', { renderTableDefault({ attrs, options, props }, { column, row }) { - const defaultProps = { link: true, size: 'small', ...props }; - const align = - column.align === 'center' - ? 'center' - : column.align === 'left' - ? 'start' - : 'end'; + const defaultProps = { size: 'small', link: true, ...props }; + let align = 'end'; + switch (column.align) { + case 'center': { + align = 'center'; + break; + } + case 'left': { + align = 'start'; + break; + } + default: { + align = 'end'; + break; + } + } const presets: Recordable> = { delete: { - icon: 'ep:delete', - text: $t('common.delete'), type: 'danger', + text: $t('common.delete'), + icon: 'ep:delete', }, edit: { - icon: 'ep:edit', text: $t('common.edit'), + icon: 'ep:edit', type: 'primary', }, }; @@ -116,20 +132,21 @@ setupVbenVxeTable({ : { code: opt, text: $te(`common.${opt}`) ? $t(`common.${opt}`) : opt, - type: 'primary', + type: 'primary', // 自定义按钮默认使用主题色 ...defaultProps, }; + } else { + // 对象配置的按钮,如果没有 type,也默认为 primary + const buttonConfig = { + ...defaultProps, + ...presets[opt.code], + ...opt, + }; + if (!buttonConfig.type && !presets[opt.code]) { + buttonConfig.type = 'primary'; + } + return buttonConfig; } - - const buttonConfig = { - ...defaultProps, - ...presets[opt.code], - ...opt, - }; - if (!buttonConfig.type && !presets[opt.code]) { - buttonConfig.type = 'primary'; - } - return buttonConfig; }) .map((opt) => { const optBtn: Recordable = {}; @@ -141,20 +158,22 @@ setupVbenVxeTable({ .filter((opt) => opt.show !== false); function renderBtn(opt: Recordable, listen = true) { - const { code, icon, text, ...btnProps } = opt; + const { icon, text, code, ...btnProps } = opt; const buttonType = btnProps.type === 'danger' ? 'danger' - : btnProps.type === 'primary' + : (btnProps.type === 'primary' ? 'primary' - : 'default'; + : 'default'); const button = h( ElButton, { ...btnProps, - circle: !!icon, + type: buttonType, + size: 'small', link: true, + circle: !!icon, onClick: listen ? () => attrs?.onClick?.({ @@ -162,27 +181,32 @@ setupVbenVxeTable({ row, }) : undefined, - size: 'small', - type: buttonType, }, { - default: () => - icon ? h(IconifyIcon, { class: 'size-4', icon }) : text, + default: () => { + if (icon) { + return h(IconifyIcon, { class: 'size-4', icon }); + } + return text; + }, }, ); - if (!icon) return button; + // 如果有图标,用 Tooltip 包装;否则直接返回按钮 + if (icon) { + return h( + ElTooltip, + { + content: text, + placement: 'top', + }, + { + default: () => button, + }, + ); + } - return h( - ElTooltip, - { - content: text, - placement: 'top', - }, - { - default: () => button, - }, - ); + return button; } function renderConfirm(opt: Recordable) { @@ -191,23 +215,28 @@ setupVbenVxeTable({ const button = h( ElButton, { - circle: !!icon, - link: true, - size: 'small', - title: icon ? text : undefined, type: 'danger', + size: 'small', + link: true, + circle: !!icon, + title: icon ? text : undefined, }, { - default: () => - icon ? h(IconifyIcon, { class: 'size-4', icon }) : text, + default: () => { + if (icon) { + return h(IconifyIcon, { class: 'size-4', icon }); + } + return text; + }, }, ); return h( ElPopconfirm, { - cancelButtonText: $t('common.cancel'), + title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']), confirmButtonText: $t('common.confirm'), + cancelButtonText: $t('common.cancel'), confirmButtonType: 'danger', onConfirm: () => { attrs?.onClick?.({ @@ -215,14 +244,13 @@ setupVbenVxeTable({ row, }); }, - title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']), }, { + reference: () => button, default: () => $t('ui.actionMessage.deleteConfirm', [ row[attrs?.nameField || 'name'], ]), - reference: () => button, }, ); } @@ -240,10 +268,14 @@ setupVbenVxeTable({ ); }, }); + + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 + // vxeUI.formats.add }, useVbenForm, }); +// 自定义类型定义 export type OnActionClickParams> = { code: string; row: T; diff --git a/web/apps/web-ele/src/api/core/data-source.ts b/web/apps/web-ele/src/api/core/data-source.ts index 6d859d4..0f76099 100644 --- a/web/apps/web-ele/src/api/core/data-source.ts +++ b/web/apps/web-ele/src/api/core/data-source.ts @@ -64,6 +64,16 @@ export interface ParamDefinition { default?: any; } +/** + * 列表后处理配置(排序 + 条数限制) + */ +export interface ListConfig { + sort_field?: string; + sort_order?: 'asc' | 'desc'; + /** 0 表示不额外限制(仍受系统最大 1000 条约束) */ + limit?: number; +} + /** * 树形配置 */ @@ -131,12 +141,17 @@ export interface DataSource { // SQL 配置 sql_content?: string; db_connection?: string; + /** SQL 执行目标库/catalog */ + db_database?: string; + /** SQL 执行目标模式/schema */ + db_schema?: string; // 静态数据 static_data?: any[]; // 参数定义 params?: ParamDefinition[]; // 结果处理 result_type?: ResultType; + list_config?: ListConfig; tree_config?: TreeConfig; field_mapping?: Record; chart_config?: ChartConfig; @@ -195,12 +210,17 @@ export interface DataSourceInput { // SQL 配置 sql_content?: string; db_connection?: string; + /** SQL 执行目标库/catalog */ + db_database?: string; + /** SQL 执行目标模式/schema */ + db_schema?: string; // 静态数据 static_data?: any[]; // 参数定义 params?: ParamDefinition[]; // 结果处理 result_type?: ResultType; + list_config?: ListConfig; tree_config?: TreeConfig; field_mapping?: Record; chart_config?: ChartConfig; @@ -268,6 +288,10 @@ export interface TestRequest { // SQL 配置 sql_content?: string; db_connection?: string; + /** SQL 执行目标库/catalog */ + db_database?: string; + /** SQL 执行目标模式/schema */ + db_schema?: string; // 静态数据 static_data?: any[]; // 参数 @@ -275,6 +299,7 @@ export interface TestRequest { params?: Record; // 结果处理 result_type?: ResultType; + list_config?: ListConfig; tree_config?: TreeConfig; field_mapping?: Record; chart_config?: ChartConfig; diff --git a/web/apps/web-ele/src/api/core/index.ts b/web/apps/web-ele/src/api/core/index.ts index 0cb39c3..ec6a5ef 100644 --- a/web/apps/web-ele/src/api/core/index.ts +++ b/web/apps/web-ele/src/api/core/index.ts @@ -1,16 +1,22 @@ -export * from './announcement'; +export * from '../online-dev/form-data-api'; +export * from '../online-dev/form-manager'; +// 注意: login-log 模块请直接导入以避免 PaginatedResponse 类型冲突 +// import { getLoginLogListApi, ... } from '#/api/core/login-log'; +// 注意: ai-platform 模块请直接导入以避免类型冲突 +// import { ... } from '#/api/core/ai-platform'; +export * from '../online-dev/screen-design'; export * from './application'; export * from './auth'; +export * from './data-source'; +export * from './database-connection'; +export * from './database-manager'; export * from './dept'; export * from './dict'; -export * from './file'; -export * from './field-permission'; export * from './menu'; -export * from './message'; +export * from './oauth'; export * from './permission'; +export * from './post'; export * from './role'; -export * from './resource-scope'; -export * from './server-health'; -export * from './ui-config'; +export * from './scheduler'; +export * from './server-monitor'; export * from './user'; -export * from './websocket'; diff --git a/web/apps/web-ele/src/api/core/login-log.ts b/web/apps/web-ele/src/api/core/login-log.ts index cda128c..f33b116 100644 --- a/web/apps/web-ele/src/api/core/login-log.ts +++ b/web/apps/web-ele/src/api/core/login-log.ts @@ -10,7 +10,6 @@ export interface LoginLog { user_id?: string; username: string; status: number; - login_type?: string; failure_reason?: number; failure_message?: string; login_ip: string; @@ -34,7 +33,6 @@ export interface LoginLogListParams { username?: string; user_id?: string; status?: number; - login_type?: string; failure_reason?: number; login_ip?: string; device_type?: string; diff --git a/web/apps/web-ele/src/api/core/menu.ts b/web/apps/web-ele/src/api/core/menu.ts index 5bae8b4..e157b5b 100644 --- a/web/apps/web-ele/src/api/core/menu.ts +++ b/web/apps/web-ele/src/api/core/menu.ts @@ -110,14 +110,19 @@ export interface MenuStats { /** * 获取用户所有菜单(路由树) * @param applicationCode 应用编码,用于过滤应用专属菜单 + * @param devMode 开发模式:true只返回系统菜单,false只返回应用菜单 */ export async function getAllMenusApi( applicationCode?: string, + devMode?: boolean, ) { const params: Record = {}; if (applicationCode) { params.application_code = applicationCode; } + if (devMode !== undefined) { + params.devMode = devMode; + } return requestClient.get( '/api/core/menu/route/tree', { params }, diff --git a/web/apps/web-ele/src/api/core/post.ts b/web/apps/web-ele/src/api/core/post.ts index 5d5b14a..c30b4d3 100644 --- a/web/apps/web-ele/src/api/core/post.ts +++ b/web/apps/web-ele/src/api/core/post.ts @@ -29,7 +29,6 @@ export interface PostCreateInput { status?: boolean; description?: string; dept_id?: string; - sort?: number; } export interface PostUpdateInput extends Partial {} @@ -121,7 +120,7 @@ export async function deletePostApi(postId: string) { */ export async function batchDeletePostApi(data: PostBatchDeleteInput) { return requestClient.post<{ count: number }>( - '/api/core/post/batch/delete', + '/api/core/post/batch_delete', data, ); } @@ -133,7 +132,7 @@ export async function batchUpdatePostStatusApi( data: PostBatchUpdateStatusInput, ) { return requestClient.post<{ count: number }>( - '/api/core/post/batch/status', + '/api/core/post/batch_update_status', data, ); } @@ -142,7 +141,7 @@ export async function batchUpdatePostStatusApi( * 根据部门ID获取岗位列表 */ export async function getPostsByDeptApi(deptId: string) { - return requestClient.get(`/api/core/post/by/dept/${deptId}`); + return requestClient.get(`/api/core/post/by_dept/${deptId}`); } /** @@ -195,7 +194,7 @@ export async function getPostStatsApi() { * 导出岗位数据 */ export async function exportPostApi(params?: PostListParams) { - return requestClient.get('/api/core/post/export/excel', { + return requestClient.get('/api/core/post/export', { params, responseType: 'blob', }); @@ -208,7 +207,7 @@ export async function importPostApi(file: File) { const formData = new FormData(); formData.append('file', file); return requestClient.post<{ error_count: number; success_count: number }>( - '/api/core/post/import/excel', + '/api/core/post/import', formData, { headers: { @@ -222,7 +221,7 @@ export async function importPostApi(file: File) { * 获取简单岗位列表(用于选择器) */ export async function getSimplePostListApi() { - return requestClient.get('/api/core/post/all'); + return requestClient.get('/api/core/post/simple'); } /** diff --git a/web/apps/web-ele/src/api/core/server-monitor.ts b/web/apps/web-ele/src/api/core/server-monitor.ts index b1faf41..5531b3d 100644 --- a/web/apps/web-ele/src/api/core/server-monitor.ts +++ b/web/apps/web-ele/src/api/core/server-monitor.ts @@ -1,5 +1,10 @@ import { requestClient } from '#/api/request'; +/** + * 服务器监控相关类型定义 + */ + +// 基础信息 export interface BasicInfo { hostname: string; ip_address: string; @@ -14,6 +19,7 @@ export interface BasicInfo { version: string; } +// CPU信息 export interface CpuInfo { physical_cores: number; total_cores: number; @@ -26,6 +32,7 @@ export interface CpuInfo { cpu_stats: Record; } +// 内存信息 export interface MemoryVirtual { total: number; available: number; @@ -53,6 +60,7 @@ export interface MemoryInfo { swap: MemorySwap; } +// 磁盘信息 export interface DiskPartition { device: string; mountpoint: string; @@ -73,6 +81,7 @@ export interface DiskInfo { total_write_time?: number; } +// 网络信息 export interface NetworkTotal { bytes_sent: number; bytes_recv: number; @@ -127,6 +136,7 @@ export interface NetworkInfo { connections: NetworkConnection[]; } +// 进程信息 export interface Process { pid: number; name: string; @@ -143,6 +153,7 @@ export interface ProcessInfo { sleeping_processes: number; } +// 系统负载 export interface SystemLoad { load_1min: number; load_5min: number; @@ -150,6 +161,7 @@ export interface SystemLoad { cpu_count: number; } +// 启动时间 export interface BootTime { boot_time: string; uptime_seconds: number; @@ -159,6 +171,7 @@ export interface BootTime { uptime_minutes: number; } +// 用户信息 export interface UserInfo { name: string; terminal?: string; @@ -167,12 +180,14 @@ export interface UserInfo { pid?: number; } +// 电池信息 export interface BatteryInfo { percent: number; power_plugged: boolean; seconds_left?: number; } +// 实时统计 export interface RealtimeStats { cpu_percent: number; memory_percent: number; @@ -202,6 +217,7 @@ export interface RealtimeStats { timestamp: string; } +// 服务器监控完整响应 export interface ServerMonitorResponse { basic_info: BasicInfo; cpu_info: CpuInfo; @@ -215,62 +231,105 @@ export interface ServerMonitorResponse { timestamp: string; } +/** + * API调用函数 + */ + +/** + * 获取服务器完整监控信息 + */ export async function getServerOverviewApi() { return requestClient.get( '/api/core/server_monitor/overview', ); } +/** + * 获取实时统计信息 + */ export async function getRealtimeStatsApi() { return requestClient.get('/api/core/server_monitor/realtime'); } +/** + * 获取基础系统信息 + */ export async function getBasicInfoApi() { return requestClient.get('/api/core/server_monitor/basic_info'); } +/** + * 获取CPU信息 + */ export async function getCpuInfoApi() { return requestClient.get('/api/core/server_monitor/cpu_info'); } +/** + * 获取内存信息 + */ export async function getMemoryInfoApi() { return requestClient.get('/api/core/server_monitor/memory_info'); } +/** + * 获取磁盘信息 + */ export async function getDiskInfoApi() { return requestClient.get('/api/core/server_monitor/disk_info'); } +/** + * 获取网络信息 + */ export async function getNetworkInfoApi() { return requestClient.get( '/api/core/server_monitor/network_info', ); } +/** + * 获取进程信息 + */ export async function getProcessInfoApi() { return requestClient.get( '/api/core/server_monitor/process_info', ); } +/** + * 获取系统负载信息 + */ export async function getSystemLoadApi() { return requestClient.get('/api/core/server_monitor/system_load'); } +/** + * 获取系统启动时间信息 + */ export async function getBootTimeApi() { return requestClient.get('/api/core/server_monitor/boot_time'); } +/** + * 获取用户信息 + */ export async function getUsersInfoApi() { return requestClient.get('/api/core/server_monitor/users_info'); } +/** + * 获取温度信息 + */ export async function getTemperatureInfoApi() { return requestClient.get>( '/api/core/server_monitor/temperature_info', ); } +/** + * 获取电池信息 + */ export async function getBatteryInfoApi() { return requestClient.get( '/api/core/server_monitor/battery_info', diff --git a/web/apps/web-ele/src/api/core/ui-config.ts b/web/apps/web-ele/src/api/core/ui-config.ts index 1297b8d..8691b84 100644 --- a/web/apps/web-ele/src/api/core/ui-config.ts +++ b/web/apps/web-ele/src/api/core/ui-config.ts @@ -71,7 +71,7 @@ export function mergePreferencesConfig( current: PreferencesConfig, updates: PreferencesConfig, ): PreferencesConfig { - return merge({}, current, updates) as PreferencesConfig; + return merge({}, updates, current) as PreferencesConfig; } // ============ 前端偏好配置API ============ diff --git a/web/apps/web-ele/src/api/core/websocket.ts b/web/apps/web-ele/src/api/core/websocket.ts index 8151d68..4a52f16 100644 --- a/web/apps/web-ele/src/api/core/websocket.ts +++ b/web/apps/web-ele/src/api/core/websocket.ts @@ -39,6 +39,46 @@ export namespace WebSocketApi { heartbeatInterval?: number; } + /** 监控数据类型 */ + export interface MonitorMessage { + type: + | 'get_overview' + | 'get_realtime' + | 'set_interval' + | 'start_monitor' + | 'stop_monitor' + | 'test_connection'; + interval?: number; + timestamp?: string; + } + + /** 服务器监控数据 */ + export interface ServerMonitorData { + basic_info?: any; + cpu_info?: any; + memory_info?: any; + disk_info?: any; + network_info?: any; + process_info?: any; + system_load?: any; + boot_time?: any; + users_info?: any; + timestamp?: string; + } + + /** Redis监控数据 */ + export interface RedisMonitorData { + connection_id?: string; + connection_name?: string; + status?: string; + info?: any; + memory?: any; + stats?: any; + keyspace?: any[]; + clients?: any[]; + slow_log?: any[]; + timestamp?: string; + } } /** @@ -359,26 +399,34 @@ export class WebSocketManager { /** * 创建WebSocket连接 */ +function buildWebSocketUrl(endpoint: string): string { + const apiUrl = import.meta.env.VITE_GLOB_API_URL || ''; + const currentProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; + + let wsOrigin = `${currentProtocol}//${location.host}`; + let apiPath = apiUrl; + + if (/^https?:\/\//i.test(apiUrl)) { + const parsed = new URL(apiUrl); + wsOrigin = `${parsed.protocol === 'https:' ? 'wss:' : 'ws:'}//${parsed.host}`; + apiPath = parsed.pathname; + } + + const basePath = apiPath + .replace(/\/$/, '') + .replace(/\/(?:basic-api|api)$/i, ''); + const normalizedBase = basePath && basePath !== '/' ? basePath : ''; + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; + + return `${wsOrigin}${normalizedBase}${normalizedEndpoint}`; +} + export function createWebSocket( endpoint: string, callbacks?: WebSocketApi.WebSocketCallbacks, ): WebSocketManager { - // 构建WebSocket URL - let wsUrl: string; - - if (import.meta.env.DEV) { - // 开发环境:直接连接到后端服务器 - const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; - wsUrl = `${wsProtocol}//localhost:8000${endpoint}`; - } else { - // 生产环境:使用当前域名 - const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsHost = location.host; - wsUrl = `${wsProtocol}//${wsHost}${endpoint}`; - } - const config: WebSocketApi.WebSocketConfig = { - url: wsUrl, + url: buildWebSocketUrl(endpoint), reconnect: true, maxReconnectAttempts: 5, reconnectInterval: 3000, @@ -390,7 +438,7 @@ export function createWebSocket( } /** - * Create test WebSocket connection + * 创建测试WebSocket连接 */ export function createTestWebSocket( callbacks?: WebSocketApi.WebSocketCallbacks, @@ -399,7 +447,7 @@ export function createTestWebSocket( } /** - * Create notification WebSocket connection + * 创建通知WebSocket连接 */ export function createNotificationWebSocket( callbacks?: WebSocketApi.WebSocketCallbacks, @@ -408,10 +456,197 @@ export function createNotificationWebSocket( } /** - * Create chat WebSocket connection + * 创建服务器监控WebSocket连接 + */ +export function createServerMonitorWebSocket( + callbacks?: WebSocketApi.WebSocketCallbacks, +): WebSocketManager { + return createWebSocket('/ws/server-monitor/', callbacks); +} + +/** + * 创建Redis监控WebSocket连接 + */ +export function createRedisMonitorWebSocket( + callbacks?: WebSocketApi.WebSocketCallbacks, +): WebSocketManager { + return createWebSocket('/ws/redis-monitor/', callbacks); +} + +/** + * 创建数据库监控WebSocket连接 + */ +export function createDatabaseMonitorWebSocket( + callbacks?: WebSocketApi.WebSocketCallbacks, +): WebSocketManager { + return createWebSocket('/ws/database-monitor/', callbacks); +} + +/** + * 监控WebSocket管理器类 + */ +export class MonitorWebSocketManager extends WebSocketManager { + /** + * 获取概览信息 + */ + getOverview(): boolean { + return this.send({ + type: 'get_overview', + timestamp: new Date().toISOString(), + }); + } + + /** + * 获取实时统计 + */ + getRealtime(): boolean { + return this.send({ + type: 'get_realtime', + timestamp: new Date().toISOString(), + }); + } + + /** + * 开始监控 + */ + startMonitoring(): boolean { + return this.send({ + type: 'start_monitor', + timestamp: new Date().toISOString(), + }); + } + + /** + * 停止监控 + */ + stopMonitoring(): boolean { + return this.send({ + type: 'stop_monitor', + timestamp: new Date().toISOString(), + }); + } + + /** + * 测试连接(适用于Redis监控) + */ + testConnection(): boolean { + return this.send({ + type: 'test_connection', + timestamp: new Date().toISOString(), + }); + } +} + +/** + * 创建服务器监控WebSocket管理器 + */ +export function createServerMonitorManager( + callbacks?: WebSocketApi.WebSocketCallbacks, +): MonitorWebSocketManager { + const wsManager = createServerMonitorWebSocket(callbacks); + // 创建增强版管理器 + return Object.setPrototypeOf(wsManager, MonitorWebSocketManager.prototype); +} + +/** + * 创建Redis监控WebSocket管理器 + */ +export function createRedisMonitorManager( + callbacks?: WebSocketApi.WebSocketCallbacks, +): MonitorWebSocketManager { + const wsManager = createRedisMonitorWebSocket(callbacks); + // 创建增强版管理器 + return Object.setPrototypeOf(wsManager, MonitorWebSocketManager.prototype); +} + +/** + * 数据库监控WebSocket管理器类 + */ +export class DatabaseMonitorWebSocketManager extends WebSocketManager { + /** + * 获取数据库配置列表 + */ + getConfigs(): boolean { + return this.send({ + type: 'get_configs', + timestamp: new Date().toISOString(), + }); + } + + /** + * 获取概览信息 + */ + getOverview(dbName: string): boolean { + return this.send({ + type: 'get_overview', + db_name: dbName, + timestamp: new Date().toISOString(), + }); + } + + /** + * 获取实时统计 + */ + getRealtime(dbName: string): boolean { + return this.send({ + type: 'get_realtime', + db_name: dbName, + timestamp: new Date().toISOString(), + }); + } + + /** + * 开始监控 + */ + startMonitoring(dbName: string): boolean { + return this.send({ + type: 'start_monitor', + db_name: dbName, + timestamp: new Date().toISOString(), + }); + } + + /** + * 停止监控 + */ + stopMonitoring(): boolean { + return this.send({ + type: 'stop_monitor', + timestamp: new Date().toISOString(), + }); + } + + /** + * 测试连接 + */ + testConnection(dbName: string): boolean { + return this.send({ + type: 'test_connection', + db_name: dbName, + timestamp: new Date().toISOString(), + }); + } +} + +/** + * 创建聊天WebSocket连接 */ export function createChatWebSocket( callbacks?: WebSocketApi.WebSocketCallbacks, ): WebSocketManager { return createWebSocket('/ws/chat/', callbacks); } + +/** + * 创建数据库监控WebSocket管理器 + */ +export function createDatabaseMonitorManager( + callbacks?: WebSocketApi.WebSocketCallbacks, +): DatabaseMonitorWebSocketManager { + const wsManager = createDatabaseMonitorWebSocket(callbacks); + // 创建增强版管理器 + return Object.setPrototypeOf( + wsManager, + DatabaseMonitorWebSocketManager.prototype, + ); +} diff --git a/web/apps/web-ele/src/app.vue b/web/apps/web-ele/src/app.vue index a3e4e54..1217658 100644 --- a/web/apps/web-ele/src/app.vue +++ b/web/apps/web-ele/src/app.vue @@ -1,7 +1,7 @@ diff --git a/web/apps/web-ele/src/components/dashboard-design/components/WidgetRenderer.vue b/web/apps/web-ele/src/components/dashboard-design/components/WidgetRenderer.vue index 95877ff..d8ff8ec 100644 --- a/web/apps/web-ele/src/components/dashboard-design/components/WidgetRenderer.vue +++ b/web/apps/web-ele/src/components/dashboard-design/components/WidgetRenderer.vue @@ -39,7 +39,35 @@ const widgetComponents: Record< string, ReturnType > = { + 'stat-card': defineAsyncComponent(() => import('./widgets/StatCard.vue')), + 'progress-card': defineAsyncComponent( + () => import('./widgets/ProgressCard.vue'), + ), + 'chart-line': defineAsyncComponent(() => import('./widgets/ChartLine.vue')), + 'chart-bar': defineAsyncComponent(() => import('./widgets/ChartBar.vue')), + 'chart-pie': defineAsyncComponent(() => import('./widgets/ChartPie.vue')), + 'chart-gauge': defineAsyncComponent(() => import('./widgets/ChartGauge.vue')), + 'chart-area': defineAsyncComponent(() => import('./widgets/ChartArea.vue')), + 'chart-radar': defineAsyncComponent(() => import('./widgets/ChartRadar.vue')), + 'chart-funnel': defineAsyncComponent( + () => import('./widgets/ChartFunnel.vue'), + ), + 'chart-scatter': defineAsyncComponent( + () => import('./widgets/ChartScatter.vue'), + ), + 'chart-ring': defineAsyncComponent(() => import('./widgets/ChartRing.vue')), + 'chart-heatmap': defineAsyncComponent( + () => import('./widgets/ChartHeatmap.vue'), + ), + 'chart-kline': defineAsyncComponent(() => import('./widgets/ChartKline.vue')), + 'chart-sankey': defineAsyncComponent( + () => import('./widgets/ChartSankey.vue'), + ), + 'todo-list': defineAsyncComponent(() => import('./widgets/TodoList.vue')), 'notice-list': defineAsyncComponent(() => import('./widgets/NoticeList.vue')), + 'ranking-list': defineAsyncComponent( + () => import('./widgets/RankingList.vue'), + ), 'announcement-list': defineAsyncComponent( () => import('./widgets/AnnouncementList.vue'), ), @@ -53,6 +81,18 @@ const widgetComponents: Record< ), clock: defineAsyncComponent(() => import('./widgets/ClockWidget.vue')), weather: defineAsyncComponent(() => import('./widgets/WeatherWidget.vue')), + 'image-carousel': defineAsyncComponent( + () => import('./widgets/ImageCarousel.vue'), + ), + 'data-table': defineAsyncComponent(() => import('./widgets/DataTable.vue')), + 'form-render': defineAsyncComponent( + () => import('./widgets/FormRenderWidget.vue'), + ), + iframe: defineAsyncComponent(() => import('./widgets/IframeWidget.vue')), + 'video-player': defineAsyncComponent( + () => import('./widgets/VideoPlayer.vue'), + ), + image: defineAsyncComponent(() => import('./widgets/ImageWidget.vue')), 'approval-center': defineAsyncComponent( () => import('./widgets/ApprovalCenter.vue'), ), @@ -60,6 +100,18 @@ const widgetComponents: Record< 'server-monitor': defineAsyncComponent( () => import('./widgets/ServerMonitor.vue'), ), + 'filter-input': defineAsyncComponent( + () => import('./widgets/FilterInput.vue'), + ), + 'filter-select': defineAsyncComponent( + () => import('./widgets/FilterSelect.vue'), + ), + 'filter-date': defineAsyncComponent( + () => import('./widgets/FilterDate.vue'), + ), + 'filter-date-range': defineAsyncComponent( + () => import('./widgets/FilterDateRange.vue'), + ), }; const store = useDashboardDesignStore(); @@ -249,7 +301,7 @@ const animationClass = computed(() => {
@@ -296,9 +348,7 @@ const animationClass = computed(() => { .widget-enter { opacity: 0; transform: translateY(20px) scale(0.95); - transition: - opacity 0.22s cubic-bezier(0.22, 1, 0.36, 1), - transform 0.22s cubic-bezier(0.22, 1, 0.36, 1); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } .widget-entered { @@ -312,8 +362,4 @@ const animationClass = computed(() => { } /* 入场动画 */ - -.widget-loading-mask { - background: color-mix(in srgb, var(--el-bg-color) 78%, transparent); -} diff --git a/web/apps/web-ele/src/components/dashboard-design/components/widgets/ApprovalCenter.vue b/web/apps/web-ele/src/components/dashboard-design/components/widgets/ApprovalCenter.vue index 11fe63b..65c5c48 100644 --- a/web/apps/web-ele/src/components/dashboard-design/components/widgets/ApprovalCenter.vue +++ b/web/apps/web-ele/src/components/dashboard-design/components/widgets/ApprovalCenter.vue @@ -1,136 +1,88 @@