diff --git a/code/fms/.workbuddy-ai/memory/2026-09-24.md b/code/fms/.workbuddy-ai/memory/2026-09-24.md new file mode 100644 index 00000000..a684c8a5 --- /dev/null +++ b/code/fms/.workbuddy-ai/memory/2026-09-24.md @@ -0,0 +1,12 @@ +# 2026-09-24 + +- 记忆整理:`memory/MEMORY.md` 已达 14.8 KB,注入时被截断(尾部「主题/UI/i18n/导出」长期丢失)。 + 重构为「精简主文件(3.8 KB)+ 分主题明细」:新建 `memory/topics/workflow.md`、`topics/frontend-ui.md`、 + `topics/env-and-baseline.md`,内容零丢失,主文件开头保留指向 topics 的索引。 + 后续新增长期约定:核心不变式写 MEMORY.md,单域细节写对应 topic 文件。 +- 模块管理页「查询配置」改造(`ModuleQueryConfigPanel.vue`):去掉内层页签分组,四个分节 + (更多条件字段 / 工作队列页签 / 关键词字段 / 快捷筛选字段)同页自上而下平铺,整页由外层 + `.mm-config-pane` 统一滚动。面板由 `flex:1 + overflow:hidden` 改为 `flex:1 0 auto`; + 三张 FmsTable 不再靠 flex 撑满,改按行数算高度(34 + 行数×30 + 2,clamp 140~420px)。 + 同步更新 `tests/unit/module-query-config-panel.spec.js`:`openViewsTab`(点页签)换成 + `viewsTable`(按 `.mm-qdesign__section--views` 定位),并新增一条「四节同页平铺 + 表格定高」断言。 diff --git a/code/fms/.workbuddy-ai/memory/MEMORY.md b/code/fms/.workbuddy-ai/memory/MEMORY.md index 9c941408..5770002e 100644 --- a/code/fms/.workbuddy-ai/memory/MEMORY.md +++ b/code/fms/.workbuddy-ai/memory/MEMORY.md @@ -1,167 +1,47 @@ -# FMS 项目长期约定 +# FMS 项目长期约定(主文件) -## 目录 / 文档 / 库 +> 明细已拆分到 `memory/topics/`,需要时再读,避免主文件被截断: +> `topics/workflow.md`(工作流设置器 + dagre 布局)、`topics/frontend-ui.md`(主题/UI 库/i18n/导出)、 +> `topics/env-and-baseline.md`(本机环境坑 + 测试基线)。每日日志见同目录 `YYYY-MM-DD.md`。 -- 后端 `fms-api`(Spring Boot + JDK21,JDBC+Druid 无 ORM);前端 `fms-vue`(Vue 3.5 + Vite 8 + Pinia,纯 JS)。 - `fms-vue-old1/old2` 仅供历史参考,**不要改**。 -- 根目录设计文档:`FMS新系统核心表结构设计.md`(权威表结构)、`FMS业务表设计.md`、`开发规范.md`(AI 规范)、 - `Fms旧系统表结构.md`(旧库 G3HY2025,只有表无视图)、`FMS删除规则引擎设计.md`。 -- 建库/模块注册脚本在 `sql/`(`fms_core.sql`、`fms_business_*.sql`、`fms_module_*.sql`)。 -- SQL Server `118.89.70.199:1433`:旧库 `G3HY2025`,新库 **FMS**(新系统一律用 FMS,**生产库,写前先确认**)。 -- 机构→库映射在 `fms-api/config/dbconfigs/{ORG_ID}.properties`(gitignore):`G3HD.properties` → FMS, - `G3HY2025.properties` → G3HY2025。**没有 FMS.properties**。 -- 后端 8088(context-path `/api`),前端 dev 5082(`/api` → 127.0.0.1:8088)。直连接口需先 `/auth/login`。 +## 目录 / 库 / 端口 -## 元数据核心表(真实列名,已连库核对,别再猜) +- 后端 `fms-api`(Spring Boot 4.1 + JDK21,JDBC+Druid 无 ORM);前端 `fms-vue`(Vue3.5 + Vite8 + Pinia,纯 JS,pnpm)。 + `fms-vue-old1/old2` 仅历史参考,**不要改**。 +- 根目录文档:`FMS新系统核心表结构设计.md`(权威表结构)、`FMS业务表设计.md`、`开发规范.md`(AI 规范)、 + `Fms旧系统表结构.md`(旧库,只有表无视图)、`FMS删除规则引擎设计.md`;建库脚本在 `sql/`。 +- SQL Server `118.89.70.199:1433`:旧库 `G3HY2025`,新库 **FMS**(生产库,写前先确认)。 + 机构→库映射 `fms-api/config/dbconfigs/{ORG_ID}.properties`(gitignore):`G3HD`→FMS、`G3HY2025`→旧库; + **没有 FMS.properties**。 +- 后端 8088(context-path `/api`),前端 dev 5082(`/api`→127.0.0.1:8088)。直连接口需先 `/auth/login`。 -- `s_module`:PK **`b_id`**(无 `b_module_code`;无 `b_route`,那是 `s_menu` 的);本身是树: - `b_parent_id`/`b_depth`/`b_path`;另有 `b_module_type`(module/data/virtual)、`b_view_table`、`b_save_table`、 - `b_key_field`、`b_order_sql`、`b_query_sql`、`b_config_json`、`b_canuse`、`b_xh`、`b_bz`、`b_name`、`b_i18n`。 -- `s_field`:PK **`(b_module_id, b_field)`**,无 `b_id`;`b_type` 实测 `input`/`number`/`checkbox`/`datetime`/`date`。 -- `s_module_schema`:PK `(b_module_id, b_schema_type∈view|edit|query)`,`b_schema_json`。 -- `s_menu`:`b_id`/`b_parent_id`/`b_depth`/`b_path`/`b_menu_type`(directory|page)/`b_route`/`b_icon`;`s_menu_module` 绑菜单与模块。 -- **权限靠 `b_id`**:permissionStore 的 `moduleCodes` 即 `s_module.b_id`,页面 `MODULE_CODE` 必须等于它; - 还要在 `s_menu_module` 里绑菜单,否则非超管看不到授权入口。 -- 列表模块编码统一 `v_`+表名(`v_b_othercompany`),`b_view_table`→视图,`b_save_table`→真实表。 -- schema JSON 形状:`view{columns:[{field,width}|{type:group,title,children}]}`、 +## 元数据表真实列名(已连库核对,别猜) + +- `s_module`:PK **`b_id`**(无 `b_module_code`;`b_route` 属 `s_menu`);树用 `b_parent_id`/`b_depth`/`b_path`; + 另有 `b_module_type`(module/data/virtual)、`b_view_table`、`b_save_table`、`b_key_field`、`b_order_sql`、 + `b_query_sql`、`b_config_json`、`b_canuse`、`b_xh`、`b_bz`、`b_name`、`b_i18n`。查询来源 `b_query_sql` > `b_view_table`。 +- `s_field`:PK `(b_module_id, b_field)`,无 `b_id`;`b_type` 实测 input/number/checkbox/datetime/date。 +- `s_module_schema`:PK `(b_module_id, b_schema_type∈view|edit|query)`,内容在 `b_schema_json`。 +- `s_menu`:`b_id`/`b_parent_id`/`b_depth`/`b_path`/`b_menu_type`(directory|page)/`b_route`/`b_icon`; + `s_menu_module` 绑菜单与模块。 +- **权限靠 `b_id`**:permissionStore.moduleCodes 即 `s_module.b_id`,页面 `MODULE_CODE` 必须等于它; + 还需在 `s_menu_module` 绑菜单,否则非超管看不到授权入口。列表模块编码统一 `v_`+表名(`v_b_othercompany`)。 +- schema JSON:`view{columns:[{field,width}|{type:group,title,children}]}`、 `edit{children:[{field,span,required,readonly,defaultValue}|group]}`、`query{groups:[{conditions}],quick}`。 -- 路由守卫只看 `meta.moduleId`/`meta.adminOnly`;`views/module/**` 是 adminOnly(仅 g3soft)。 +- 路由守卫只看 `meta.moduleId`/`meta.adminOnly`;`views/module/**` 是 adminOnly(仅 g3soft),不进 `s_menu`。 -## 树结构规范(模块/菜单/部门/分类树通用) +## 树结构规范(模块/菜单/部门/分类通用) -`b_parent_id` 是权威关系,`b_depth`(根=0)、`b_path`(`/cn/east/`)是派生字段,移动节点要同事务更新全部后代; -不一致时以 `b_parent_id` 为准。工具在 `src/utils/tree.js`(`buildSortedTree`/`computeTreeMove`/`collectDescendantIds`/ -`applyTreePosition`/`filterTreeByText`/`findTreeNode`),展示层用 `components/fms-tree/FmsTree.vue`(自管虚拟根)。 +`b_parent_id` 权威,`b_depth`(根=0)/`b_path`(`/cn/east/`) 是派生字段,移动节点同事务更新全部后代; +不一致以 `b_parent_id` 为准。工具 `src/utils/tree.js`(buildSortedTree/computeTreeMove/collectDescendantIds/ +applyTreePosition/filterTreeByText/findTreeNode),展示 `components/fms-tree/FmsTree.vue`(自管虚拟根)。 -## 后端通用接口(前端业务数据只走这几个) +## 后端通用接口(业务数据只走这几个) -- `POST /auth/login`:字段是**小写** `orgid`/`userid`/`password`;`orgid=G3HD`。 -- `/data/loaddata`:`view_name`/`search_condition`/**可选** `order_by`/`search_columns`(真 JSON 数组)。 -- `/data/page`:**`order_by` 必填**(`DbUtils.buildPageOrderBy` 会报「order_by 不能为空」), - 分页参数名是 **`page_no`/`page_size`**(不是 pageindex/pagesize)。 +- `POST /auth/login`:字段**小写** `orgid`/`userid`/`password`;`orgid=G3HD`。 +- `/data/loaddata`:`view_name`/`search_condition`/可选 `order_by`/`search_columns`(真 JSON 数组)。 +- `/data/page`:**`order_by` 必填**;分页参数名 **`page_no`/`page_size`**。 - `/data/saveobjt`:body 是数组,每项 `{table, key_field, deletes, updates, inserts}`,**`key_field` 必填** (复合主键逗号分隔),顺序 delete→update→insert。 - 模块元数据不由接口下发,前端读本地 schema JSON,改元数据无需重启后端。 - `DbUtils` 是 SQL 唯一出口:标识符加 `[]`,`validateReadOnlySql()` 只允许查询。 - -## 工作流流程设置器(fms-vue/src/views/system/workflow) - -- **代码现状 = 原始版本(2026-09-23 曾被重构,应用户要求已全部回滚)**: - 草稿是 `schemaVersion: 1`(`{definitionId, name, nodes, edges}`),编辑器是 - `WorkflowDesigner` + `WorkflowCanvas`(LogicFlow) + `WorkflowPropertyPanel` 三件套, - 节点坐标挂在 `node.x/y`,条件存单个叶子 `{field, operator, value}`, - 写入通道未实现(工具栏只有撤销/缩放/查看草稿 JSON)。 - **不要以为仓库里已经有卡片模式/流程树/条件构造器 —— 那些都被回滚了。** - 回滚前的完整实现留在 `D:\workspace\code\fms-workflow-refactor-backup-20260923\`(仓库外,含 `http.js` 的 get/put、 - `tests/unit/workflow-domain.spec.js`、改过 AST 形状的 mock SQL),需要时可取回。 -- **文档是目标,不是现状**:`FMS审批流程设置器交互设计.md`(旧名 `FMS审批流程交互重构设计.md`, - 已整体重写为 258 行)要求把设计器改成钉钉式递归流程树,并**明确作废**"线性卡片 + LogicFlow 高级模式" - 双编辑器方案(§3.2 不再用画布兜底,并行/服务节点等拓扑只读保护); - `FMS审批流程设置器二次重构实施文档.md` 给出增量执行顺序(§1.1 的交互决策已锁定,不必再问); - `FMS工作流与审批设计.md` 管表结构与运行语义(其 §19 的画布选型是旧方案)。 - 参考交互项目:`D:\workspace\code\test\Workflow-Vue3`(只借交互,不复制代码/Element UI/私有字体)。 -- **这些文件未进 git**:`design.vue`、`designer/`(除 `index.vue` 外全部)、`sql/fms_workflow_mock.sql` - 都是未跟踪文件 —— 改它们之前先备份,`git restore` 救不回来(本地历史里也没有)。 -- **`workflow/index.vue` 要小心**:它虽然被 git 跟踪,但**工作区版本比 HEAD 新**(新增按钮、表格「设计」行操作 - 都是未提交改动)。直接 `git restore` 会退回 HEAD 的 18 行空壳,把按钮冲掉 —— 要恢复就用带按钮的 76 行版本。 -- 表结构已建好:`sql/fms_workflow.sql`(13 张表)、`fms_workflow_module.sql`(视图 + s_module 注册)、 - `fms_workflow_menu.sql`(菜单 `workflow` / `approval*`)、`fms_workflow_mock.sql`(`expense_approval` 样本, - 覆盖 7 种节点类型 / 6 种审批方式 / 5 种审批人解析)。 -- **后端 `fms-api/src/main` 目前零工作流代码**:保存/校验/模拟/发布都要走流程服务,接口 404 时必须 - 明确提示"服务未接入",绝不伪造成功。 -- 若将来重新做树形重构,踩过的坑:① `b_priority` 是**同一来源节点内**的判断顺序,不是全表序号; - ② 校验只约束**出边**,入边不能锁成 1(分支汇合点天生多条入线);③ 环要单独拦,否则递归不终止; - ④ 默认出口(`condition: null`)必须唯一且固定最后。 - -### 画布「一键整理」(dagre 自动布局,2026-09-23 已落地) - -- 依赖 **`@logicflow/layout@2.1.5`**(peer 正好是 `@logicflow/core ^2.2.5`)。注意 dagre **不在** - `@logicflow/extension` 里(那只有一个标注"未完善"的 AutoLayout)。 -- 必须**深路径导入** `import { Dagre } from '@logicflow/layout/es/dagre'`:包入口同时导出 `elkLayout`, - 从入口引会把 elkjs 打进产物(实测走深路径只 +92 kB,走入口会 +1MB 以上)。包没有 `exports` 字段,深路径可用。 -- 注册方式:`new LogicFlow({ plugins: [Dagre] })`,调用 `lf.extension.dagre.layout({...})`。 - 插件实例由 `installPlugin` 的"非静态插件"分支创建(`Dagre` 只有 `static pluginName` + `render`,没有 `install`)。 -- **坑**:插件靠 `ToolOverlay` 的 `componentDidMount` → `triggerToolRender` 调用 `render(lf)` 才拿到 - `this.lf`;拿不到时 `layout()` 会**直接抛错**(它的 try/catch 只包住 applyDagreLayout)。 - 所以 `autoLayout()` 里先兜底 `if (!dagre.lf) dagre.lf = lf`,并用 try/catch + 「坐标是否变化」判断结果 - —— 插件失败时只打 console.error,不能假定成功。 -- 布局走 `lf.renderRawData()` 整图重渲染:**会丢选中态**,但节点/连线 **id 保留**(`processEdges` 只改路径字段), - 所以上层按 nodeKey/edgeKey 回填审批人与条件仍然成立。坐标变化由实例的 `history` 自动记录(deepObserve + debounce)。 -- 布局后**必须 `snapshot()`** 把新坐标 emit 给上层,否则草稿里的 x/y 还是旧的。 -- 参数:`rankdir: 'TB'`(审批流程纵向)、`align: 'UL'`、`nodesep: 60`、`ranksep: 80`、`isDefaultAnchor: true`(重算连线路径)。 -- 测试:`tests/unit/workflow-canvas-layout.spec.js`(jsdom 挂载画布真跑一次布局;需给 `SVGElement.prototype.getBBox` 打桩, - 否则 LogicFlow 的 autoWrap 文本测量会挂)。 - -## 环境坑(本机特有) - -- **编译/测试 fms-api 唯一可行命令**(start.cmd 里的 JDK/Maven 路径都是坏的): - ``` - cd /d/workspace/code/fms/fms-api && /d/devtool/jdk/jdk21/bin/java \ - -Dclassworlds.conf="D:/devtool/maven/bin/m2.conf" -Dmaven.home="D:/devtool/maven" \ - -Dmaven.multiModuleProjectDirectory="D:/workspace/code/fms/fms-api" \ - -cp "D:/devtool/maven/boot/plexus-classworlds-2.11.0.jar" \ - org.codehaus.plexus.classworlds.launcher.Launcher -o -Dtest=XxxTests test - ``` - 本地仓库是 `D:\data\maven`(不是 ~/.m2);联网下载去掉 `-o`;`target/` 被 git 跟踪,编译后有噪音。 -- 无 sqlcmd/pyodbc/pymssql:用 `fms-api/tools/migration/RunSqlFile.java` 或一次性 `SqlRunner.java` - + JDBC jar(`~/.m2/.../mssql-jdbc-13.4.0.jre11.jar`)+ `/d/devtool/jdk/jdk17/bin/java`。 - 脚本要按行首 `GO` 分批,路径传绝对路径。 -- `vite build` 被 safe-delete 守卫拦(要删 600+ 文件):用 `--outDir dist-verify --emptyOutDir`; - 清理用 `find dist-verify -type f -delete` 再 `find dist-verify -type d -empty -delete`。 -- `oxfmt --check` 全仓库都报 format issues(版本漂移),**不是自己引入的**,别顺手格式化整个仓库。 -- 本工作区可能被多会话同时编辑,验证失败项先用 `find