diff --git a/docs/project_context.md b/docs/project_context.md index 6bed984..9cc5de9 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -1,5 +1,13 @@ # 项目上下文记录 +## 2026-05-15:恢复前端工作台交互质量 + +- 前端工作台布局重新对齐旧版 `54773f547f6fcb853d73785f05ff5ac39ab2e5f5` 的信息架构:左侧导航、顶部标题栏、紧凑后台式内容区,导航项使用“数据上传 / 处理历史 / 数据管理 / 脚本编辑 / 系统设置”。 +- `ScriptPanel.vue` 不再使用普通 textarea,改为 `monaco-editor` SQL 编辑器,保留脚本读取、保存、执行和状态轮询接口;支持 SQL 高亮、行号、缩略图、光标行列状态和未保存状态。 +- Monaco 通过 Vue 异步组件按需加载,避免脚本编辑器依赖进入首屏主包;Vite worker 类型由 `frontend/src/vite-env.d.ts` 提供。 +- `SettingsPanel.vue` 的字段提取配置恢复为结构化树状配置:字段名、字段类型、提取来源列表、搜索、增删和去重保存,不再要求用户直接编辑 JSON。 +- 新增前端运行依赖 `monaco-editor`,构建时仍会生成 `frontend/dist/`,该目录继续作为构建产物忽略,不进入版本库。 + ## 2026-05-15:修复 Windows 启动脚本编码问题 - `run.bat` 改为纯 ASCII 输出,并规范为 CRLF 行尾,避免 Windows `cmd` 在 PowerShell 中执行 UTF-8 中文批处理时把提示文本解析成碎片命令。 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index eebdbf0..b75138d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@vicons/ionicons5": "^0.13.0", + "monaco-editor": "^0.55.1", "naive-ui": "^2.42.0", "vue": "^3.5.0" }, @@ -922,6 +923,13 @@ "@types/lodash": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@vicons/ionicons5": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@vicons/ionicons5/-/ionicons5-0.13.0.tgz", @@ -1144,6 +1152,15 @@ "date-fns": "^3.0.0 || ^4.0.0" } }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/entities": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", @@ -1273,6 +1290,28 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/monaco-editor": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", + "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "license": "MIT", + "dependencies": { + "dompurify": "3.2.7", + "marked": "14.0.0" + } + }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1cf155b..30fe2f2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@vicons/ionicons5": "^0.13.0", + "monaco-editor": "^0.55.1", "naive-ui": "^2.42.0", "vue": "^3.5.0" }, @@ -20,4 +21,3 @@ "vue-tsc": "^3.0.0" } } - diff --git a/frontend/src/AppShell.vue b/frontend/src/AppShell.vue index c7baec6..c5b0723 100644 --- a/frontend/src/AppShell.vue +++ b/frontend/src/AppShell.vue @@ -2,7 +2,7 @@ + -
+
{{ currentTitle }}
-
容量报表处理与数据维护
已登录 @@ -48,7 +52,7 @@ diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 5ee859d..8b484ce 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -12,7 +12,7 @@ body, } body { - color: #20242a; + color: #1f2933; background: #eef1f4; font-family: Inter, @@ -24,27 +24,41 @@ body { .app-layout { min-height: 100vh; + background: #eef1f4; +} + +.app-sider { + background: #ffffff; + border-right: 1px solid #d9dee7; +} + +.app-sider .n-layout-sider-scroll-container { + display: flex; + flex-direction: column; + height: 100%; } .brand { display: flex; align-items: center; + flex-shrink: 0; gap: 10px; - height: 60px; - padding: 0 16px; - border-bottom: 1px solid #e6e9ee; + height: 64px; + padding: 0 18px; + border-bottom: 1px solid #e4e8ef; } .brand-mark { display: grid; + flex: 0 0 auto; width: 34px; height: 34px; place-items: center; color: #fff; font-size: 13px; font-weight: 700; - background: #2458a6; - border-radius: 8px; + background: #1f5ea8; + border-radius: 6px; } .brand-mark.large { @@ -60,30 +74,75 @@ body { text-overflow: ellipsis; } +.app-menu { + flex: 1; + padding: 12px 8px; +} + +.sider-footer { + display: flex; + flex-direction: column; + flex-shrink: 0; + gap: 3px; + padding: 12px 18px; + color: #7b8492; + font-size: 12px; + border-top: 1px solid #e4e8ef; +} + .topbar { display: flex; align-items: center; justify-content: space-between; - height: 60px; + height: 64px; padding: 0 20px; background: #fff; } +.topbar-title { + min-width: 0; +} + .page-title { + overflow: hidden; + max-width: 52vw; font-size: 18px; font-weight: 700; + white-space: nowrap; + text-overflow: ellipsis; } .page-subtitle, .muted-line { - margin-top: 2px; - color: #6a7280; + color: #6f7885; font-size: 12px; } .content { min-width: 0; - padding: 16px; + height: calc(100vh - 64px); + padding: 14px; + overflow: auto; + background: #eef1f4; +} + +.work-card, +.n-card { + border-radius: 6px; +} + +.work-card { + border-color: #dfe4ec; + box-shadow: 0 1px 2px rgba(31, 41, 55, 0.04); +} + +.work-card .n-card-header { + min-height: 46px; + padding: 10px 14px; +} + +.work-card .n-card__content { + padding: 14px; } .login-page { @@ -91,15 +150,13 @@ body { min-height: 100vh; place-items: center; padding: 24px; - background: - linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(238, 241, 244, 0.94)), - #eef1f4; + background: #eef1f4; } .login-card { width: min(420px, 100%); border-radius: 8px; - box-shadow: 0 18px 48px rgba(21, 38, 62, 0.16); + box-shadow: 0 18px 48px rgba(21, 38, 62, 0.14); } .login-brand { @@ -133,22 +190,15 @@ body { max-height: 360px; } -.log-panel { +.log-panel, +.script-log { min-height: 320px; padding: 12px; - background: #101418; + color: #d7dde7; + background: #111820; border-radius: 6px; } -.script-editor textarea, -.json-editor textarea { - font-family: - "Cascadia Code", - Consolas, - "Courier New", - monospace; -} - .section-label { margin-bottom: 8px; color: #333b46; @@ -166,8 +216,294 @@ body { margin-top: 16px; } -.n-card { - border-radius: 8px; +.settings-workspace { + display: grid; + grid-template-columns: minmax(340px, 420px) minmax(0, 1fr); + align-items: start; + gap: 14px; +} + +.settings-column { + display: flex; + flex-direction: column; + gap: 14px; +} + +.card-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-top: 4px; +} + +.filter-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; + min-height: 34px; +} + +.field-mapping-card { + min-width: 0; + height: calc(100vh - 92px); +} + +.field-mapping-card .n-card__content { + height: calc(100% - 48px); + padding: 0 14px 14px; +} + +.field-card-title, +.script-card-title { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + font-weight: 700; +} + +.field-search { + width: 240px; +} + +.field-mapping-scroll { + height: 100%; +} + +.field-mapping-scroll .n-scrollbar-content { + display: flex; + flex-direction: column; + gap: 12px; + padding: 2px 4px 4px 0; +} + +.field-mapping-item { + position: relative; + min-width: 0; + padding: 32px 12px 12px; + background: #fff; + border: 1px solid #dfe4ec; + border-radius: 6px; +} + +.field-mapping-item:hover { + box-shadow: 0 2px 8px rgba(31, 41, 55, 0.08); +} + +.field-mapping-number { + position: absolute; + top: 8px; + left: 10px; + display: grid; + width: 22px; + height: 22px; + place-items: center; + color: #fff; + font-size: 12px; + font-weight: 700; + background: #1f5ea8; + border-radius: 4px; +} + +.field-remove-button { + position: absolute; + top: 6px; + right: 8px; +} + +.field-mapping-header { + display: grid; + grid-template-columns: minmax(180px, 1fr) 160px; + gap: 12px; + padding-bottom: 10px; + border-bottom: 1px solid #e8ecf2; +} + +.field-name-control, +.field-type-control { + margin-bottom: 0; +} + +.extract-list { + padding-top: 10px; +} + +.extract-list-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 8px; + color: #4b5563; + font-size: 13px; + font-weight: 600; +} + +.extract-add-group { + width: 300px; +} + +.extract-tree { + max-height: 130px; + overflow: auto; + background: #f6f8fb; + border: 1px solid #e1e6ee; + border-radius: 5px; +} + +.extract-tree-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + min-height: 32px; + padding: 5px 10px; + font-size: 13px; + border-bottom: 1px solid #e8edf3; +} + +.extract-tree-item:last-child { + border-bottom: 0; +} + +.extract-tree-item:hover { + background: #edf3fa; +} + +.tree-text { + display: inline-flex; + align-items: center; + min-width: 0; + gap: 6px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.tree-text::before { + content: "└"; + flex: 0 0 auto; + color: #8b95a4; +} + +.extract-empty { + padding: 9px 10px; + color: #8b95a4; + font-size: 13px; + background: #f6f8fb; + border: 1px dashed #cfd7e4; + border-radius: 5px; +} + +.script-workspace { + display: grid; + grid-template-columns: minmax(0, 1fr) 360px; + gap: 14px; + height: calc(100vh - 92px); + min-height: 620px; +} + +.script-editor-pane, +.script-log-pane { + min-width: 0; + height: 100%; +} + +.script-editor-pane .n-card__content { + display: flex; + flex-direction: column; + height: calc(100% - 48px); + padding: 0; +} + +.script-editor-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + min-height: 38px; + padding: 0 12px; + color: #596473; + font-size: 12px; + background: #f6f8fb; + border-top: 1px solid #e4e8ef; + border-bottom: 1px solid #e4e8ef; +} + +.script-path { + overflow: hidden; + min-width: 0; + font-family: + "Cascadia Code", + Consolas, + "Courier New", + monospace; + white-space: nowrap; + text-overflow: ellipsis; +} + +.script-modified { + flex: 0 0 auto; +} + +.monaco-editor-host { + flex: 1; + min-height: 480px; + overflow: hidden; +} + +.script-status-bar { + display: flex; + align-items: center; + gap: 18px; + height: 28px; + padding: 0 12px; + color: #5f6b7a; + font-size: 12px; + background: #f6f8fb; + border-top: 1px solid #e4e8ef; +} + +.script-log-pane .n-card__content { + height: calc(100% - 48px); +} + +.script-log { + height: 100%; + min-height: 0; +} + +@media (max-width: 1180px) { + .settings-workspace, + .script-workspace { + grid-template-columns: 1fr; + height: auto; + min-height: 0; + } + + .field-mapping-card, + .script-editor-pane, + .script-log-pane { + height: auto; + } + + .field-mapping-scroll { + height: min(680px, calc(100vh - 180px)); + } + + .script-workspace { + min-height: 0; + } + + .monaco-editor-host { + height: 620px; + flex: 0 0 auto; + } + + .script-log { + height: 360px; + } } @media (max-width: 720px) { @@ -176,15 +512,27 @@ body { } .content { - padding: 12px; + padding: 10px; } - .page-subtitle { - display: none; + .page-title { + max-width: 46vw; } - .table-footer { + .table-footer, + .card-actions, + .extract-list-header, + .script-editor-toolbar { align-items: flex-start; flex-direction: column; } + + .field-mapping-header { + grid-template-columns: 1fr; + } + + .field-search, + .extract-add-group { + width: 100%; + } } diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +///