From 78eb811ff712298468f303e68b2a153307f6d849 Mon Sep 17 00:00:00 2001 From: oneao Date: Sun, 11 Oct 2026 22:14:13 +0800 Subject: [PATCH] u --- code/fms/FMS元数据驱动架构重构方案.md | 275 +++++- code/g3soft-erp/web/.bc-bg.png | Bin 0 -> 4615 bytes code/g3soft-erp/web/.bc-normal.png | Bin 0 -> 4075 bytes code/g3soft-erp/web/.bc.png | Bin 0 -> 35704 bytes code/g3soft-erp/web/.bc2.png | Bin 0 -> 30722 bytes code/g3soft-erp/web/.h-bg.png | Bin 0 -> 4611 bytes code/g3soft-erp/web/.h-normal.png | Bin 0 -> 4396 bytes code/g3soft-erp/web/index.html | 314 +++++- code/g3soft-erp/web/src/App.vue | 43 +- .../web/src/composables/useOnline.ts | 63 ++ .../web/src/layouts/LayoutBreadcrumb.vue | 179 +++- .../web/src/layouts/LayoutHeader.vue | 140 ++- .../web/src/layouts/LayoutTabbar.vue | 262 ++++- code/g3soft-erp/web/src/main.ts | 94 +- code/g3soft-erp/web/src/preferences/config.ts | 2 + code/g3soft-erp/web/src/preferences/types.ts | 4 + .../src/preferences/update-css-variables.ts | 26 +- code/g3soft-erp/web/src/router/index.ts | 44 +- .../g3soft-erp/web/src/router/modules/auth.ts | 19 + code/g3soft-erp/web/src/services/request.ts | 127 ++- code/g3soft-erp/web/src/stores/app.ts | 72 +- code/g3soft-erp/web/src/views/auth/Login.vue | 600 ++++++++++++ .../web/src/views/error/NotFound.vue | 42 - .../web/src/views/fallback/ErrorBoundary.vue | 27 + .../web/src/views/fallback/FallbackPage.vue | 70 ++ .../web/src/views/fallback/Forbidden.vue | 15 + .../web/src/views/fallback/NotFound.vue | 12 + .../web/src/views/fallback/ServerError.vue | 12 + code/g3soft-libs/.changeset/attachment.md | 18 + .../.changeset/form-item-vertical-stretch.md | 5 + .../.changeset/rich-text-format.md | 44 + .../g3soft-libs/.changeset/rich-text-table.md | 31 + code/g3soft-libs/.changeset/slider-captcha.md | 12 + code/g3soft-libs/docs/.vitepress/config.mts | 3 + .../docs/content/ui/data/attachment.md | 234 +++++ .../docs/content/ui/form/rich-text.md | 173 ++++ .../docs/content/ui/form/slider-captcha.md | 124 +++ .../docs/content/ui/overlay/dropdown.md | 3 +- .../docs/content/ui/overlay/popover.md | 1 + .../docs/examples/attachment/basic.vue | 80 ++ .../docs/examples/attachment/modal.vue | 58 ++ .../docs/examples/attachment/readonly.vue | 20 + .../docs/examples/attachment/select.vue | 61 ++ .../docs/examples/captcha/basic.vue | 16 + .../docs/examples/captcha/in-form.vue | 43 + .../docs/examples/captcha/sizes.vue | 17 + .../docs/examples/rich-text/basic.vue | 11 + .../docs/examples/rich-text/methods.vue | 34 + .../docs/examples/rich-text/readonly.vue | 26 + .../docs/examples/rich-text/upload.vue | 45 + code/g3soft-libs/docs/examples/spin/delay.vue | 43 +- .../packages/ui/e2e/attachment.spec.ts | 56 ++ .../packages/ui/e2e/captcha.spec.ts | 155 +++ .../packages/ui/e2e/rich-text-focus.spec.ts | 188 ++++ .../packages/ui/e2e/rich-text-table.spec.ts | 445 +++++++++ .../packages/ui/e2e/rich-text-toolbar.spec.ts | 233 +++++ code/g3soft-libs/packages/ui/package.json | 17 +- .../packages/ui/playground/main.ts | 73 ++ .../ui/src/_composables/useFloating.ts | 23 +- .../ui/src/attachment/Attachment.test.ts | 273 ++++++ .../packages/ui/src/attachment/Attachment.vue | 393 ++++++++ .../ui/src/attachment/AttachmentCard.vue | 376 ++++++++ .../ui/src/attachment/AttachmentList.vue | 848 ++++++++++++++++ .../ui/src/attachment/AttachmentPreview.vue | 241 +++++ .../packages/ui/src/attachment/file.test.ts | 90 ++ .../packages/ui/src/attachment/file.ts | 116 +++ .../packages/ui/src/attachment/icons.ts | 26 + .../packages/ui/src/attachment/index.ts | 29 + .../packages/ui/src/attachment/types.ts | 204 ++++ .../packages/ui/src/button/Button.vue | 21 + .../ui/src/captcha/SliderCaptcha.test.ts | 291 ++++++ .../packages/ui/src/captcha/SliderCaptcha.vue | 480 +++++++++ .../packages/ui/src/captcha/index.ts | 7 + .../packages/ui/src/captcha/types.ts | 32 + .../packages/ui/src/dropdown/Dropdown.test.ts | 32 + .../packages/ui/src/dropdown/Dropdown.vue | 50 +- .../packages/ui/src/dropdown/DropdownMenu.vue | 20 +- .../ui/src/dropdown/DropdownSubmenu.test.ts | 43 + .../packages/ui/src/dropdown/context.ts | 21 +- .../packages/ui/src/dropdown/types.ts | 13 + .../packages/ui/src/form/FormItem.vue | 4 + code/g3soft-libs/packages/ui/src/index.ts | 12 + .../packages/ui/src/locale/index.ts | 155 +++ .../packages/ui/src/popover/Popover.vue | 5 +- .../packages/ui/src/popover/types.ts | 8 + .../packages/ui/src/result/Result.vue | 150 +++ .../packages/ui/src/result/icons/Icon403.vue | 133 +++ .../packages/ui/src/result/icons/Icon404.vue | 154 +++ .../packages/ui/src/result/icons/Icon500.vue | 161 ++++ .../ui/src/result/icons/IconEmpty.vue | 46 + .../ui/src/result/icons/IconOffline.vue | 101 ++ .../packages/ui/src/result/index.ts | 2 + .../packages/ui/src/result/types.ts | 19 + .../packages/ui/src/rich-text/RichText.vue | 593 ++++++++++++ .../packages/ui/src/rich-text/TableMenu.vue | 454 +++++++++ .../packages/ui/src/rich-text/Toolbar.vue | 911 ++++++++++++++++++ .../packages/ui/src/rich-text/VideoNode.ts | 91 ++ .../packages/ui/src/rich-text/extensions.ts | 140 +++ .../packages/ui/src/rich-text/index.ts | 9 + .../packages/ui/src/rich-text/types.ts | 78 ++ .../packages/ui/src/rich-text/upload.ts | 102 ++ code/g3soft-libs/pnpm-lock.yaml | 608 ++++++++++++ 102 files changed, 12067 insertions(+), 209 deletions(-) create mode 100644 code/g3soft-erp/web/.bc-bg.png create mode 100644 code/g3soft-erp/web/.bc-normal.png create mode 100644 code/g3soft-erp/web/.bc.png create mode 100644 code/g3soft-erp/web/.bc2.png create mode 100644 code/g3soft-erp/web/.h-bg.png create mode 100644 code/g3soft-erp/web/.h-normal.png create mode 100644 code/g3soft-erp/web/src/composables/useOnline.ts create mode 100644 code/g3soft-erp/web/src/router/modules/auth.ts create mode 100644 code/g3soft-erp/web/src/views/auth/Login.vue delete mode 100644 code/g3soft-erp/web/src/views/error/NotFound.vue create mode 100644 code/g3soft-erp/web/src/views/fallback/ErrorBoundary.vue create mode 100644 code/g3soft-erp/web/src/views/fallback/FallbackPage.vue create mode 100644 code/g3soft-erp/web/src/views/fallback/Forbidden.vue create mode 100644 code/g3soft-erp/web/src/views/fallback/NotFound.vue create mode 100644 code/g3soft-erp/web/src/views/fallback/ServerError.vue create mode 100644 code/g3soft-libs/.changeset/attachment.md create mode 100644 code/g3soft-libs/.changeset/form-item-vertical-stretch.md create mode 100644 code/g3soft-libs/.changeset/rich-text-format.md create mode 100644 code/g3soft-libs/.changeset/rich-text-table.md create mode 100644 code/g3soft-libs/.changeset/slider-captcha.md create mode 100644 code/g3soft-libs/docs/content/ui/data/attachment.md create mode 100644 code/g3soft-libs/docs/content/ui/form/rich-text.md create mode 100644 code/g3soft-libs/docs/content/ui/form/slider-captcha.md create mode 100644 code/g3soft-libs/docs/examples/attachment/basic.vue create mode 100644 code/g3soft-libs/docs/examples/attachment/modal.vue create mode 100644 code/g3soft-libs/docs/examples/attachment/readonly.vue create mode 100644 code/g3soft-libs/docs/examples/attachment/select.vue create mode 100644 code/g3soft-libs/docs/examples/captcha/basic.vue create mode 100644 code/g3soft-libs/docs/examples/captcha/in-form.vue create mode 100644 code/g3soft-libs/docs/examples/captcha/sizes.vue create mode 100644 code/g3soft-libs/docs/examples/rich-text/basic.vue create mode 100644 code/g3soft-libs/docs/examples/rich-text/methods.vue create mode 100644 code/g3soft-libs/docs/examples/rich-text/readonly.vue create mode 100644 code/g3soft-libs/docs/examples/rich-text/upload.vue create mode 100644 code/g3soft-libs/packages/ui/e2e/attachment.spec.ts create mode 100644 code/g3soft-libs/packages/ui/e2e/captcha.spec.ts create mode 100644 code/g3soft-libs/packages/ui/e2e/rich-text-focus.spec.ts create mode 100644 code/g3soft-libs/packages/ui/e2e/rich-text-table.spec.ts create mode 100644 code/g3soft-libs/packages/ui/e2e/rich-text-toolbar.spec.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/Attachment.test.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/Attachment.vue create mode 100644 code/g3soft-libs/packages/ui/src/attachment/AttachmentCard.vue create mode 100644 code/g3soft-libs/packages/ui/src/attachment/AttachmentList.vue create mode 100644 code/g3soft-libs/packages/ui/src/attachment/AttachmentPreview.vue create mode 100644 code/g3soft-libs/packages/ui/src/attachment/file.test.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/file.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/icons.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/index.ts create mode 100644 code/g3soft-libs/packages/ui/src/attachment/types.ts create mode 100644 code/g3soft-libs/packages/ui/src/captcha/SliderCaptcha.test.ts create mode 100644 code/g3soft-libs/packages/ui/src/captcha/SliderCaptcha.vue create mode 100644 code/g3soft-libs/packages/ui/src/captcha/index.ts create mode 100644 code/g3soft-libs/packages/ui/src/captcha/types.ts create mode 100644 code/g3soft-libs/packages/ui/src/result/Result.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/icons/Icon403.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/icons/Icon404.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/icons/Icon500.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/icons/IconEmpty.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/icons/IconOffline.vue create mode 100644 code/g3soft-libs/packages/ui/src/result/index.ts create mode 100644 code/g3soft-libs/packages/ui/src/result/types.ts create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/RichText.vue create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/TableMenu.vue create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/Toolbar.vue create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/VideoNode.ts create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/extensions.ts create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/index.ts create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/types.ts create mode 100644 code/g3soft-libs/packages/ui/src/rich-text/upload.ts diff --git a/code/fms/FMS元数据驱动架构重构方案.md b/code/fms/FMS元数据驱动架构重构方案.md index e5fbb627..a4d1f46e 100644 --- a/code/fms/FMS元数据驱动架构重构方案.md +++ b/code/fms/FMS元数据驱动架构重构方案.md @@ -7,17 +7,17 @@ ## 1. 结论摘要 -本次重构保留并强化**元数据驱动**这一核心概念。模块定义、字段语义、关系、页面视图和通用行为仍由元数据描述,通用运行时读取已发布的元数据运行。 +本次重构保留并强化**元数据驱动**这一核心概念。模块定义、字段语义、关系、页面视图和通用行为由元数据描述,通用运行时读取已发布的元数据运行;列表、查询、保存、权限、关系、工作流和公式等能力统一通过通用接口执行,复杂逻辑由 SQL、函数库和业务处理器补充。 项目属于草稿 / Demo 阶段,不要求兼容现有元数据表结构和配置 JSON。可以重做元数据数据库,并整体替换配置和运行时主链路;不需要新旧格式长期双写或并行维护。 ```text 物理数据源与表结构 → 模块元数据 → 模板 / 默认值 → 校验 / 编译 / 发布 ↓ - 统一模块运行时 → SQL 业务表 / 视图 + 统一模块运行时 + 通用代码协议 → SQL 业务表 / 视图 ``` -SQL Server 中的真实业务表和视图继续作为业务数据事实来源。元数据描述如何理解、呈现和操作这些数据;不采用 EAV 存储所有业务记录,也不让元数据引擎任意生成业务表。 +SQL Server 中的真实业务表和视图继续作为业务数据事实来源。元数据描述如何理解、呈现和操作这些数据,通用 SQL、函数库和业务处理器协议可以补充复杂逻辑;不采用 EAV 存储所有业务记录,也不让元数据引擎任意生成业务表。 ## 2. 重构目标与边界 @@ -27,9 +27,10 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 - 默认配置可推导、可重复生成;用户明确调整过的配置不被覆盖。 - 元数据发布前经过统一校验,运行时消费同一份有效定义。 - 元数据数据库可以整体重做,不受 `s_module`、`s_field`、`s_module_schema` 现有结构约束。 -- 复杂查询和业务动作保留 SQL / 后端扩展出口,不强迫所有逻辑都配置化。 +- 列表、查询、保存、权限、关系、工作流和公式均提供元数据默认实现,并保留统一 SQL / 函数库 / 业务处理器协议。 +- 后端通过通用接口绑定 SQL、函数和处理器,不为具体模块增加特殊 API,也不在元数据中保存可执行代码。 - 不将所有字段值改为 EAV 或通用 JSON;业务表仍通过 SQL migration / 数据库变更管理。 -- 不建设可执行任意脚本的规则引擎,不把库存、财务、结算等复杂事务塞进配置。 +- 不建设可执行任意脚本的规则引擎,不把库存、财务、结算等复杂事务强行塞进配置;复杂逻辑通过受控的代码扩展实现。 - 不为本次目标无差别重写认证、文件、导出等稳定模块,只调整必要集成点。 - 不要求兼容旧元数据格式;需要保留的 Demo 数据可一次性导入,否则重建 / 播种。 @@ -48,7 +49,7 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 ↓ SQL Server 表、视图与只读查询 SQL ↓ - 注册的后端业务动作及工作流扩展 + 统一模块接口、SQL、函数库及业务处理器 ``` ### 配置面 @@ -57,11 +58,31 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 ### 元数据编译与校验 -后端维护权威校验与解析逻辑。发布时将模块定义、模板默认值和用户覆盖项合成为有效定义,并检查字段引用、数据源、视图、关系、查询条件和动作引用。前端预览使用后端返回的有效定义,避免前后端分别推导默认规则。 +后端维护权威校验与解析逻辑。发布时将模块定义、模板默认值和用户覆盖项合成为有效定义,并检查字段引用、数据源、视图、关系和查询条件。前端预览使用后端返回的有效定义,避免前后端分别推导默认规则。 ### 运行面 -通用运行时负责适合通用化的列表、过滤、排序、分页、字段呈现、标准保存和权限约束。查询值参数化;自定义查询入口仅用于只读查询。多表事务、库存过账、财务结算等动作由后端注册的业务处理器执行。元数据声明动作入口和参数契约,不保存可执行代码。 +后端只提供统一的模块接口。统一接口读取已发布的 `ModuleDefinition`,负责列表、查询、保存、权限、关系、工作流和公式等通用能力;查询值参数化,保存和动作在统一事务上下文中执行。SQL、公式函数和业务代码作为通用接口内部的实现来源,不为每个模块新增专用 API。 + +### 通用接口与代码控制 + +每项能力都遵循同一套调用方式: + +1. 元数据提供模块、字段、视图、关系和默认规则。 +2. 通用接口根据模块定义执行默认列表、查询、保存、权限、关系、工作流和公式逻辑。 +3. 需要代码控制时,在通用接口内部调用统一的 SQL、函数或业务处理器协议;调用方式不随模块变化。 + +| 能力 | 元数据默认职责 | 代码扩展或接管职责 | +|---|---|---| +| 列表 | 数据源、字段、过滤、排序、分页和列展示 | 自定义查询、聚合、行转换和复杂数据范围 | +| 查询 | 条件字段、操作符、条件组和参数定义 | 特殊条件解析、跨表查询和只读 SQL | +| 保存 | 标准增删改、字段校验、主键和关系处理 | 事务编排、状态流转、库存扣减和保存前后钩子 | +| 权限 | 模块、字段、角色和数据范围声明 | 上下文权限判断、动态授权和特殊拒绝规则 | +| 关系 | 主从、引用、多对多和加载规则 | 复杂关联加载、级联校验和同步逻辑 | +| 工作流 | 节点、连线、参与者和状态定义 | 审批条件、业务动作、会签规则和外部调用 | +| 公式 | 表达式、字段依赖和受限通用函数 | 通用函数库、复杂计算、跨单据取数和异步计算 | + +代码实现通过后端统一注册表或 SQL 目录管理,不在数据库中保存 Java 类名、脚本或任意可执行内容。通用权限约束不能被业务代码绕过;业务代码可以补充通用接口的规则。模块只声明需要的标准能力和参数,不声明专用接口。 ## 4. 元数据逻辑模型 @@ -69,7 +90,7 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 ### 模块 -包含稳定编码、名称、多语言键、类型、状态、组织层级、查询数据源、标准保存目标 / 主键、字段、关系、视图、动作引用、模板和发布版本。模块类型收敛为少数明确类别,如标准数据、只读查询、业务动作模块。 +包含稳定编码、名称、多语言键、类型、状态、组织层级、查询数据源、标准保存目标 / 主键、数据范围字段、字段、关系、视图、模板和发布版本。模块类型收敛为少数明确类别,如标准数据、只读查询、业务动作模块。 ### 字段 @@ -87,7 +108,7 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 - 表单、列表、查询、详情是不同视图类型,只保存顺序、分组、布局、列宽、排序、操作符等视图特有信息;布局采用可读、可校验的结构化定义,不保存画布坐标。 - 字段参与视图的默认状态由字段能力、模板和默认策略推导;个性化差异作为显式覆盖。 - 简单校验和通用 CRUD 可由元数据表达;复杂业务动作映射到稳定的后端处理器编码。 -- 工作流、权限、公式等能力通过稳定引用接入,不重复定义模块字段。 +- 列表、查询、保存、权限、关系、工作流和公式通过统一模块接口接入;这些能力引用模块定义和已发布快照,不为每个模块建立特殊接口。 ## 5. 元数据数据库重构 @@ -95,31 +116,226 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 | 逻辑实体 | 职责 | 建议存储 | |---|---|---| -| 模块 | 身份、类型、数据源、状态、组织关系 | 规范化关系表 | +| 模块 | 身份、类型、状态、树结构、数据源和通用运行参数 | 规范化关系表 | | 模块字段 | 字段编码、物理映射、业务语义、能力标记 | 规范化关系表;模块内编码唯一 | | 模块关系 | 关系类型及源 / 目标字段 | 规范化关系表;发布时校验引用 | -| 模块视图 | 表单、列表、查询、详情定义 | 按模块和视图类型区分;灵活布局可用 JSON | +| 模块视图 | 表单、列表、查询、详情及布局 | `s_module_schema`,按视图类型保存受校验 JSON | | 模板 | 默认字段能力、视图结构、推荐规则 | 可版本化定义 | | 发布版本 | 不可变快照、版本号、校验结果、发布时间 | 版本记录;运行时按已发布版本读取 | -| 动作注册 | 动作编码与后端处理器参数契约 | 只存稳定编码和契约,不存可执行代码 | + +### 推荐目标表结构 + +建议继续使用现有的 `s_` 前缀和核心表。后端是通用接口,因此不再为数据源、视图和代码扩展分别增加运行时表:模块数据源保留在 `s_module`,视图 JSON 保留在 `s_module_schema`,复杂代码由后端统一实现和注册。只新增发布版本和快照表解决草稿与运行态隔离。 + +| 目标实体 | 关键字段 / 组成 | 说明 | +|---|---|---| +| 模块 `s_module` | 模块编码、父级、名称、多语言键、模块类型、查询/保存来源、主键、数据范围和默认排序 | 保留当前模块主表,清理无约束的大 JSON;数据源由通用接口读取 | +| 模块字段 `s_field` | 模块、字段编码、来源类型、来源名称/别名、名称、多语言键、语义类型、字段能力、启用状态 | 支持物理列、查询别名、计算字段和虚拟字段;物理 SQL 类型仍优先从数据库读取 | +| 模块视图 `s_module_schema` | 模块、视图类型、结构版本、`b_schema_json` | JSON 保存分组、字段引用、列属性、查询条件、控件和公式;发布时统一校验 | +| 模块关系 `s_relation` | 关系编码、源/目标模块字段、关系类型、用途、基数、加载规则、删除策略 | 发布时校验模块和字段引用,保留现有删除语义 | +| 模块版本 `s_module_version` | 模块、版本号、草稿/已发布/废弃状态、校验摘要、发布人、发布时间 | 管理草稿与当前有效版本边界 | +| 发布快照 `s_module_snapshot` | 版本、编译后的 `ModuleDefinition`、快照摘要、生成时间 | 运行时只读取不可变快照,不直接读取编辑态表 | +| 模板及模板版本 `s_template` / `s_template_version` | 模板编码、版本、默认字段能力、视图 JSON、推荐规则 | 模板是可编辑初始值,不是硬编码页面 | +| 用户偏好 `s_user_module_pref` | 用户、模块、视图、相对默认值的覆盖 | 可选保留,只能改变布局偏好,不能改变语义、权限或数据源 | + +`s_module_schema.b_schema_json` 不是无约束 JSON:必须包含 `schemaVersion`,只能引用本模块已登记的字段和关系,并由后端 JSON Schema 和发布校验共同约束。编辑态以 `s_module_schema` 为事实来源,发布后编译成 `s_module_snapshot`。 + +### 推荐 DDL(示例) + +```sql +create table dbo.s_module ( + b_id varchar(80) not null primary key, + b_parent_id varchar(80) null, + b_depth int not null default 0, + b_path varchar(1500) not null, + b_name nvarchar(200) not null, + b_i18n varchar(150) null, + b_module_type varchar(20) not null, -- module / data / virtual + b_view_table varchar(256) null, + b_save_table varchar(256) null, + b_key_field varchar(80) null, + b_scope_field varchar(80) null, + b_order_sql varchar(500) null, + b_query_sql nvarchar(max) null, + b_config_json nvarchar(max) null, + b_canuse tinyint not null default 1, + b_xh int not null default 0, + b_bz nvarchar(2000) null, + b_updated_by varchar(50) null, + b_updated_at datetime2 null +); + +create table dbo.s_field ( + b_module_id varchar(80) not null, + b_field varchar(80) not null, + b_source_type varchar(20) not null, -- physical / alias / computed / virtual + b_source_name varchar(256) null, + b_name nvarchar(200) not null, + b_i18n varchar(150) null, + b_semantic_type varchar(40) not null, + b_can_read tinyint not null default 1, + b_can_edit tinyint not null default 0, + b_can_filter tinyint not null default 0, + b_can_sort tinyint not null default 0, + b_can_export tinyint not null default 0, + b_canuse tinyint not null default 1, + b_xh int not null default 0, + primary key (b_module_id, b_field) +); + +create table dbo.s_module_schema ( + b_module_id varchar(80) not null, + b_schema_type varchar(20) not null, -- view / edit / query / detail + b_schema_version int not null default 1, + b_schema_json nvarchar(max) not null, + b_canuse tinyint not null default 1, + b_updated_by varchar(50) null, + b_updated_at datetime2 null, + primary key (b_module_id, b_schema_type) +); + +create table dbo.s_relation ( + b_relation_code varchar(100) not null primary key, + b_source_module_id varchar(80) not null, + b_source_field varchar(80) not null, + b_target_module_id varchar(80) not null, + b_target_field varchar(80) not null, + b_relation_type varchar(30) not null, -- one_to_one / one_to_many / many_to_one + b_usage varchar(30) not null, -- reference / detail / selector / load + b_load_mode varchar(20) not null default 'lazy', + b_on_delete varchar(20) not null default 'restrict', + b_canuse tinyint not null default 1, + b_xh int not null default 0 +); + +create table dbo.s_module_version ( + b_id varchar(100) not null primary key, + b_module_id varchar(80) not null, + b_version_no int not null, + b_status varchar(20) not null, -- draft / published / retired + b_checksum varchar(128) null, + b_published_by varchar(50) null, + b_published_at datetime2 null, + b_created_by varchar(50) null, + b_created_at datetime2 not null default sysdatetime(), + unique (b_module_id, b_version_no) +); + +create table dbo.s_module_snapshot ( + b_version_id varchar(100) not null primary key, + b_module_id varchar(80) not null, + b_snapshot_json nvarchar(max) not null, + b_checksum varchar(128) not null, + b_created_at datetime2 not null default sysdatetime() +); + +create table dbo.s_user_module_pref ( + b_user_id varchar(50) not null, + b_module_id varchar(80) not null, + b_schema_type varchar(50) not null, + b_preference_json nvarchar(max) not null, + b_updated_at datetime2 null, + primary key (b_user_id, b_module_id, b_schema_type) +); +``` + +模板表沿用相同原则:`s_template` 保存模板身份,`s_template_version` 保存版本、默认字段能力和视图 JSON;模板应用只是生成模块草稿,不直接成为运行时配置。 + +```sql +create table dbo.s_template ( + b_id varchar(80) not null primary key, + b_name nvarchar(200) not null, + b_template_type varchar(30) not null, -- master_data / document / detail / readonly + b_canuse tinyint not null default 1 +); + +create table dbo.s_template_version ( + b_id varchar(100) not null primary key, + b_template_id varchar(80) not null, + b_version_no int not null, + b_default_fields_json nvarchar(max) not null, + b_views_json nvarchar(max) not null, + b_extensions_json nvarchar(max) null, + b_status varchar(20) not null, -- draft / published / retired + b_created_at datetime2 not null default sysdatetime(), + unique (b_template_id, b_version_no) +); +``` + +推荐补充以下约束和发布检查: + +- `s_field.b_module_id`、`s_module_schema.b_module_id`、`s_module_version.b_module_id` 必须引用存在的模块;模块树字段由业务层校验不能成环。 +- `s_module` 的数据源字段由模块类型和通用接口校验组合有效性;查询 SQL 只允许用于受控的只读查询,排序字段必须来自字段定义。 +- `s_module_schema.b_schema_json`、`s_module.b_config_json` 和模板 JSON 使用 `ISJSON` 做基础检查,并由后端 JSON Schema 检查 `schemaVersion`、字段引用和关系引用。 +- `s_module_snapshot`、已发布的 `s_module_version` 和已发布模板版本不可原地修改;回滚通过切换当前有效版本完成。 +- 动态业务表不因为元数据引用而强制建立数据库外键;元数据内部引用使用外键或等效约束,跨 JSON 引用由发布校验保证。 + +### Demo 数据示例 + +以下示例定义一个客户资料模块,业务事实表为 `b_othercompany`,只读查询视图为 `v_b_othercompany`: + +```text +s_module + base.customer → 基础资料 / 客户资料 / data / v_b_othercompany / b_othercompany / b_id + +s_field + base.customer.b_ywid → physical / b_ywid / 客户编码 / code / read+edit+filter+sort + base.customer.b_name → physical / b_name / 客户名称 / text / read+edit+filter+sort + base.customer.b_owner_id → physical / b_owner_id / 业务员 / user_reference / read+edit+filter + +s_module_schema + base.customer / list → schema JSON:列、宽度、排序 + base.customer / form → schema JSON:分组、控件、必填、公式 + base.customer / query → schema JSON:条件字段、组件、操作符 +``` + +列表 JSON 示例: + +```json +{ + "schemaVersion": 1, + "columns": [ + { "field": "b_ywid", "visible": true, "width": 140, "sortable": true }, + { "field": "b_name", "visible": true, "width": 220, "sortable": true }, + { "field": "b_owner_id", "visible": true, "width": 120 } + ], + "defaultSort": [{ "field": "b_name", "direction": "asc" }] +} +``` + +### 现有表到目标表的改造映射 + +- `s_module.b_view_table`、`b_query_sql`、`b_save_table`、`b_key_field`、`b_scope_field`、`b_order_sql` 继续由 `s_module` 保存;删除或收紧无约束的 `b_config_json` 内容。 +- `s_field` 保留模块字段身份,并补充来源映射、语义类型和能力标记;列表、表单、查询的顺序和显示属性保留在对应 `s_module_schema.b_schema_json` 中。 +- `s_module_schema.view/edit/query` 保留为视图配置的主要存储格式,增加 `b_schema_version` 和后端 JSON Schema 校验。 +- `s_relation` 保留已有源/目标字段和删除策略,增加稳定关系编码、用途、加载规则等字段;历史关系先一次性导入,再由发布校验补足引用检查。 +- 现有模块配置可通过一次性导入工具转换;不在新旧表之间长期双写,也不让旧表继续作为第二事实来源。 + +### 不需要整体重做的表 + +- 真实业务表和业务视图继续由 SQL migration 管理,不因为元数据重构而改成 EAV 或通用 JSON。 +- `wf_definition`、`wf_version`、`wf_binding` 及工作流运行表可继续使用;`wf_binding` 需要改为引用模块的已发布版本或在发布时校验模块版本。 +- 现有权限主体和授权表可以保留;权限运行时通过通用接口读取已发布字段和模块定义。 +- 公式不要求新增业务数据表;简单公式随模块快照发布,复杂函数由通用公式接口调用后端函数库。 ### 对现有核心表的评估 **结论:需要重构元数据配置表,但不需要仅因配置器难用而整体重做所有业务数据表。** 当前表结构能表达基本模块、字段和关系;主要问题是职责混放、字段能力分散到视图 JSON,以及缺少可发布的版本边界。 -- **`s_module`**:当前模块身份 / 树结构与查询表、保存表、主键、数据范围字段、默认排序 SQL、查询 SQL 和扩展 JSON 放在同一表。建议将模块身份与数据源 / 查询定义分开建模;移除缺少强约束的通用 `b_config_json`,把常用配置变为有类型的字段或关联实体。模块树优先只保留权威父级和同级顺序;`depth/path` 仅在查询性能有证据需要时保留为派生索引。模块草稿 / 发布时间应由版本模型管理,不靠一个模块级更新时间代表所有子配置状态。 +- **`s_module`**:继续保存模块身份、树结构和默认数据源,因为后端统一通过模块接口读取这些定义;`b_config_json` 只保留少量经过 schema 校验的低频配置,模块草稿 / 发布时间由版本模型管理。 - **`s_field`**:当前主要保存名称、业务类型、启用和顺序,尚未明确区分物理列、查询别名、计算 / 虚拟字段。建议增加明确的字段来源种类与来源映射、语义类型和模块级能力;数据库物理类型由 SQL Server 结构检查获得。表单 / 列表 / 查询中的显示顺序和控件差异放到对应视图字段配置,不塞回字段主定义。 -- **`s_module_schema`**:当前每模块按 `view/edit/query` 保存整份 `nvarchar(max)` JSON,便于早期迭代,但字段参与、字段语义和复杂布局容易混在一起。既然不采用拖拽画布,建议将它替换为视图定义 + 结构化的视图字段 / 分组配置;常用属性使用有类型的列,只有少量确实灵活的扩展留在受校验 JSON。表单不保存行列像素坐标,列表不把所有列属性藏在任意 JSON 节点中。 +- **`s_module_schema`**:继续按 `view/edit/query` 保存视图 JSON,增加结构版本和发布校验;不新增 `s_module_view`、`s_module_view_group` 或 `s_module_view_field`。 - **`s_relation`**:保留关系作为独立元数据,但建议引入稳定关系编码 / 标识,显式描述关系用途、基数、展示 / 加载规则,并对模块和字段引用建立数据库约束。现有删除策略等已表达的业务含义应迁移保留,不因改表而丢失。 - **`s_user_module_pref`**:如保留个性化配置,只允许保存列表列、查询布局等用户偏好覆盖,不得改变模块字段语义、数据源、权限或保存行为。若 Demo 暂无真实个性化需求,可先不纳入新核心模型。 -- **新增发布版本实体**:保存草稿与已发布版本的边界、版本号、校验摘要和不可变运行快照;发布失败不替换当前有效版本。 +- **新增发布版本实体**:使用 `s_module_version` 保存草稿与已发布版本的边界,使用 `s_module_snapshot` 保存不可变运行快照;发布失败不替换当前有效版本。 +- **代码扩展**:不新增模块扩展绑定表;后端统一接口通过 SQL 目录、公式函数库和通用业务处理器协议调用代码实现,模块只使用通用参数和元数据配置。 元数据表之间应使用主键、唯一约束和可表达的外键保障内部引用完整性,并用发布校验补足跨 JSON / 动态数据源的检查。不要为了引用元数据而给所有动态业务表强行建立数据库外键。 ### 规范化与 JSON 边界 - 模块、字段、关系、版本等有稳定身份、查询和引用的对象采用关系表。 -- 表单布局、列表列布局、查询 UI 等灵活结构可存 JSON,但其中只能引用已定义字段和动作。 +- 表单布局、列表列布局、查询 UI、公式和少量扩展属性继续存 JSON,但其中只能引用已定义字段和关系;JSON 必须带 `schemaVersion` 并通过结构校验。 - 发布快照可采用 JSON 作为编译产物,不作为编辑态唯一事实来源。 - 对模块编码、模块内字段编码、模块 / 视图类型、版本号等建立唯一约束;可表达的元数据引用建立外键或等效约束,并由发布校验再次检查。 - 明确删除、改名和关系变更策略,不依赖前端操作顺序保证一致性。 @@ -150,7 +366,7 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 - **列表**:采用列配置表 / 字段矩阵,集中设置是否显示、顺序、宽度、格式、对齐、排序和汇总等属性;支持批量添加、移除和套用模板。 - **预览**:配置面板旁提供实时预览,但预览仅用于检查结果,不作为拖放编辑画布。 - **元数据**:保存语义结构和字段引用,不保存像素坐标、拖拽路径或依赖编辑器内部实现的状态。 -- **高级场景**:通过分组、布局模板、字段显示规则及明确的扩展配置解决;不以重新引入自由拖拽作为兜底。 +- **高级场景**:通过分组、布局模板、字段显示规则及通用接口配置解决;不以重新引入自由拖拽作为兜底。 默认规则: @@ -175,13 +391,13 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 不做新旧格式长期并行或渐进兼容;按依赖顺序拆分实施任务,便于验证: -1. **确定元数据契约**:模块类型、字段语义、关系、视图、默认覆盖、发布和动作扩展模型。 -2. **重建元数据数据库**:新表、约束、种子 / migration;决定 Demo 配置重建还是一次性导入。 -3. **实现后端目录与编译器**:读取、校验、默认合成、发布、版本切换、缓存失效和回滚。 +1. **确定元数据契约**:模块类型、字段语义、关系、视图、默认覆盖、发布和通用接口契约。 +2. **重建元数据数据库**:调整模块、字段、视图 JSON 和关系表,新增版本与快照表,补充主键 / 唯一约束 / 引用约束;决定 Demo 配置重建还是一次性导入。 +3. **实现后端目录与编译器**:读取物理结构、校验字段和视图 JSON、默认合成、生成快照、发布、版本切换、缓存失效和回滚。 4. **替换配置工作台**:数据源检查、字段导入、模板初始化、能力矩阵、预览、发布。 5. **替换运行时消费链路**:列表、查询、表单、保存统一读取发布定义。 -6. **接入扩展能力**:权限、关系、公式、工作流、导出和业务动作。 -7. **删除旧路径**:完成导入或确认弃用后,删除旧 Schema 适配和重复逻辑。 +6. **接入通用能力**:在统一模块接口中接入列表、查询、保存、权限、关系、工作流和公式的标准实现,并接入统一 SQL、函数库和业务处理器协议。 +7. **删除旧路径**:完成一次性导入或确认弃用后,删除旧 `s_module_schema` JSON 结构适配和重复逻辑;保留新格式下的同一张表作为唯一视图配置来源。 虽然不需要渐进兼容,仍建议用一个基础资料、一个主从单据、一个只读查询模块验证新契约,再批量建立 Demo 配置。这是新架构验收,不是要求新旧架构并行运行。 @@ -194,22 +410,25 @@ SQL Server 中的真实业务表和视图继续作为业务数据事实来源。 - 默认配置可重复生成,且不覆盖显式配置。 - 无效字段引用、数据源列缺失、关系目标错误、非法查询规则在发布时给出明确错误。 - 运行时只消费已发布版本;发布失败不影响当前可用版本。 +- 编辑态使用模块、字段、视图 JSON 和关系表;运行态使用对应的不可变发布快照。 +- 列表、查询、保存、权限、关系、工作流和公式均可使用元数据默认实现,并可通过统一 SQL、函数库和业务处理器协议补充复杂逻辑。 +- 统一接口的参数、字段引用和依赖关系在发布时校验;接口执行失败有明确错误和审计信息。 - 简单 CRUD 使用通用运行时;复杂业务动作由明确的后端处理器完成。 - 真实 SQL Server 业务表仍是业务数据事实来源,不引入 EAV 业务数据主存储。 - 旧元数据路径已删除或仅作为一次性导入工具,不存在双重事实来源。 ## 10. 需要同步修订的文档 -目前架构文档存在方向冲突:部分草案提出以业务域代码包取代元数据运行时,开发规范与现有系统仍要求模块 / 字段 / 界面元数据驱动通用模块。本方案评审通过后应统一修订: +目前架构文档存在方向冲突:部分草案提出以业务域代码包取代元数据运行时,而本方案采用元数据主导、通用接口承载复杂逻辑的模式。本方案评审通过后应统一修订: -- `FMS新系统核心架构设计.md`:明确元数据驱动是通用模块核心,SQL / 后端动作是扩展机制。 -- `FMS新系统核心架构实施计划.md`:改为元数据数据库、编译器、工作台和运行时重构任务。 +- `FMS新系统核心架构设计.md`:明确元数据运行时是通用能力核心,业务域代码通过统一模块接口、SQL 和业务处理器协议接入复杂逻辑。 +- `FMS新系统核心架构实施计划.md`:增加元数据数据库、编译器、工作台、运行时和通用接口实现任务。 - `FMS新系统核心表结构设计.md`:重新定义元数据表,不再将现有 `s_module`、`s_field`、`s_module_schema` 作为不可变前提。 -- `开发规范.md`:明确元数据契约、发布定义、查询 / 保存边界和动作扩展约定。 +- `开发规范.md`:明确元数据契约、发布定义、查询 / 保存边界和通用接口约定。 ## 11. 假设与待定项 - 暂保留 SQL Server 作为数据库目标;允许重做元数据表,不主动改变租户数据库路由模式。 - 元数据和 Demo 配置可重建;是否保留样例数据,实施前决定重建或一次性导入。 - 本文确定架构方向,不冻结最终 DDL、API、类名或 Vue 目录;这些在元数据契约评审后设计。 -- 权限、工作流、公式、多语言是否随模块发布版本整体冻结,需结合实际使用方式决定,避免第一版过度设计。 +- 需要确定权限、工作流、公式、多语言及通用接口实现是否随模块发布版本整体冻结,并定义接口版本兼容、事务边界、错误模型和规则优先级。 diff --git a/code/g3soft-erp/web/.bc-bg.png b/code/g3soft-erp/web/.bc-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..30329dd28e96f1cdaf09aa85093d5995190b4ae8 GIT binary patch literal 4615 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 WLF2EV4;~YD0MIATR8UIrp#K1KDuUDi literal 0 HcmV?d00001 diff --git a/code/g3soft-erp/web/.bc.png b/code/g3soft-erp/web/.bc.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d8a681592a7262b6302d879220a5f6a3a74d21 GIT binary patch literal 35704 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ν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 Dzh3~! literal 0 HcmV?d00001 diff --git a/code/g3soft-erp/web/.h-bg.png b/code/g3soft-erp/web/.h-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..e7e3d234dd8e58685bd8253eb8338cc57a960fa5 GIT binary patch literal 4611 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 literal 0 HcmV?d00001 diff --git a/code/g3soft-erp/web/.h-normal.png b/code/g3soft-erp/web/.h-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..38aeec93a237f7b929564ddfbac57dc725494cbc GIT binary patch literal 4396 zcmcIocQhN`-_DoXNsZQ~R4J{ks9I5@wyIh&V((ouRVAhNDqo|v3Tp4YTDv6#wSp)` z%plQfmH4Ioo%i4OkN2GS^T)l?sL!mjOP<)XrKwA<)8%s03dBGbt3@a3IhpC zQInBIcagq00Du9Yt^UwBByWEu%-GoJ2Gx;f6L>ez%d4qLhukPJ&Dp1!-xft1nRBN| zU39w>M8nR8_|+fJk<-4eE9RLN|0<9=x#}J7Ui>Q#pjjQa3*2E;S4{RJwFWaNK=kgDx}S2X_5hkUR`t*l&xIm{pnU-H>L z>|-D!pOjda2L}Yr&RTbdVP#jmrCJr0?FWz6QWo+EA5c{1=g^RThwJ}5v4o55>vZ~k zb6m>LEm&Mk<<=ox2B#hK`7?b6cQNM^#+ed6=hQBEn_=M(ZnfpwbFCqGq|-}r<-)Xc zu@27dNn)78ExXlA;Nr!Pm@jIVY*D{uA-K?aBL4>rLymWxFseDV_sl`?@q|@ZW%%2* zv))aQrPa|3R3Fx4RRmJT_-Y!_c5jri^2SX)xeSGQ@N(oOAcW$RNzb ze{D~`^H|4F#qg`TR5o{&;?(ksB=4U;6(4Ane@=JKGGTM|f@={G+cw41b#HIH6FhAi zWnhyi_VqB*9pCk8dc>rrZAaxL&Z<7gvQSvTI1vYPFm?=r*qTN~BHZyNGX+3A2Cb?s_F(0`P}zD(#YTumlU zTE6F$^%B6@OSe`p)h~Xe5uV9(ZF$U?F~w6zth+wF9n43BZf6bj3BBWxl*i}BpEep7 zS{1Sf;n3-qFgx21V*#PZjVC|BNd3G^*`3@O9tyL^H4sL}rlph3?uA!X>tm8~FyZ|ON)*^ZXGFH&V?d}tLOy5qMF z-k>ScM4sexw8n{mo&7lgfDaM&gzcOx)+o@NJ8L1V>Y}?)@CwFiOW&m}gVl9F?78XS z!BQJS0cOT^X15K-;Qdpe4gfG*8E^JLV)Xj8C?mCW4mA$+l7g#6QQyV;*!kC8mytxJ zZduXgr-UmzU}T=S`_>T>9oTuG{P9NmtEWLO;|Y`P=<8>3(zbdQeznf8$C+Jg&Gqv} zW?t#Mrg$SfBGJz2aoRMYHQOxmRo#Tww^*5f!Z)ZmoOg$H=F^!hGD?n$A`O*P^Ju;M ztWRU_Mbpr_mL}AFYvG_~Yc*6W$fQY3B**x8-1c)qcG{xt0~wF_;LHux%q-O~H8qk) zQ!=e}D0^@yAM6P?KMWgk7FsC+$&I-ssUBXp$>6BSjFQN=Da6F4-Ws%y2C_>LzPklD z{GM2uR93Jv`l^2H@h~flQ}Iz}cdwmi$8D?gz%grRd9a(5>?sQyI$`~re>Fi@qaLCD zVT`QW+g9(+#3`o4a`~M&WJe=qu%`sM$ZcwLZzPhLy|RoMJJR6HQS-#ncogHFSa8Bw zqsUdI_zSAqt;@Il6$p%89wiW~O>Kql@XxgBOREnvj~2LE!;UWN--B0@+IcuFF7KU{ z9uEfI)+;==wx5{V^jykjt(nB%kM1EP0iWuDfvgY zNr9QID1_;&z6v@>Tf6?NZ{utG8_dkr62{L&X;zI6n5v5v*f!y|MUo|CFi{E8B<vV;o+=fQmGpq( zI;K6$Za`nQkkv#l%$-oNywx)p{PNdkpQ32{uem||Qw9Grw01PTU*?6x-N4xA#10oV z8xW+&Ic(q32bDDxdODL8Am|6Oh#h^O${B@%aDj6{-@oHg^%$K5VJ_>}r3t->j$gfr zCj^F1W8iLfYxCyQru<)Vgw-1g!!aj+Xv2xxaE{={J&w=W43jl|C^#=TKA9W zlHz7zJh5qfdO-Cxn(^InWy4xLYbf{um~GQ=>x%&dv#_1gx9u!g8IdnA5Uqc&GpKY9sfFh?5jlU{Y@+ z;tBxJ9xNB)T>T@KY;lk7ft`HKv!k^u2&}(sh1vF3^13WhZKc3Ud7o2kKxmpoN4e9h zsoSSn+&Ef4cWcPWZ?jtxiswfI2}Wt#VC*VWvk`i*Ezu=A=VKW-ExsnPL^CC{G@I(K zRvv`MSISWzNs9r+F>~d76Yv2)@pINd@e_)r(~dQ&S$7ib2BJ&LMQ_gS37}}zEW6x> zPuvjUl!PJ|z5^RanR54M%TEY%X$aLxx1~nDeT(J!(}TKl3aXp@i9q1C4)F_WFRHTa zP~qjAUPxSfHnIo{E!HKKSwN%iF2=)6CX&-O1VUl!!Wj2}$5c0e^M4Rz+N8@omRy|P zDC(!OZJ|)spOIT_a2)l{VTTg)S0m|_M-PQ3a@BEeJqAyP62K?YtUnSfe{U^WQ`l&o z{_HvGUQO69^k=orPaZ`lRy52$6*QX^{;}&ZRzQeFUOMKB@%EOlJ{iJ^ujWg~)EDTY z|Fy@5hXnkrD-{PXHFVVsoIuwv7-^Ff<1*k;9(lNR(Ty)Ier?QOS zlZ&H!$`E+ux}hg*H7k|y4NvywdbriBriU4kRa$hxF|t!vM0I?AGM5Kj*G88|6p~xb zGNCsJS6^Vjf)0-V*<1J810s}2R2(jG6{dSmQ;i{Gp3UC zLV}(hVaB|?QbM9n>^dGig{mjFj>k<+mJF=&Rm5=A{AkeNyh>yFWT`3C)C|S{uZ&#p z!Uhr)X5sQDb?1jz=B2mhmOeg>%NfLI>VOP)n7-g1-XpiEPRe54&N@P=(K8<^`I0I* ziy+>+Uj?qakdcFYDh>Ws2S-;9adGN=jG&?!fj?wxslGtmwvnHXV49U3 zi_;5%yA;vz2=U%EJ>>usSTQcwXXmo9A_9ki!wZVONXCJl-)6_UL*G0YE;Guvyk18q zw|Eb6)79ybMm_GqSOR6s?zy`W+eXGF-(aSIDxdE)4VRk9oOI%zY5Lz|R~Sf_^V81} zR{dTxoYWe#-^ovU1)zB|^F+CH=>ZsXe?@Aj66SSfths#m#|m~vGA@N6x{{jnlS_Zv zWMg?}A4dL0uK_)WF-QPb1|sbv`$Bu#ImnZ1z{6}U;q$fqlu8Qn-q6yIK~kmC`I{Mz z`q35ag{g@pYrnAgo&~M#g=;Uc9LJA@7j>>C9B3Mk71O}WD00}x>jKq5VsyZlFU7g! z0?e(x>R8HSFl8#QN%z-DXjPe4_4psjiSwW8NrFo zG}u=0s7X)VfkYj!OwEmrEzc+N@;2bf9luF+^C0~)86Ty$Y2+DOWMowa)u~@Y)MklcnfV37?|a~+M#bF%WiptN7DZ1k*23XNee{|%l0P(*gPCDvSrYG zsNlH3WM=3ED~^L8H5EOiOH^6H#&?0lj?!!Xx4v;rykkbR<%`83%g%%gdCQ%jwy>-6 zLXb)I-l|S%yncLw=^`~W2M;v66m!eSCU@?g#69h;f-|3Rn&0RM~H2kq^jv6B8Z p$x;bm(s+4!KbI0u0+fFUk^|abMM4=|1C#&k(AF?euT`~u^&f~4DE9yW literal 0 HcmV?d00001 diff --git a/code/g3soft-erp/web/index.html b/code/g3soft-erp/web/index.html index 2d944557..9aeeb0bd 100644 --- a/code/g3soft-erp/web/index.html +++ b/code/g3soft-erp/web/index.html @@ -4,9 +4,321 @@ G3Soft ERP + + + -
+
+ +
+ +
+
+ + + +
+
+ diff --git a/code/g3soft-erp/web/src/App.vue b/code/g3soft-erp/web/src/App.vue index a78fb6fd..07cbbb5a 100644 --- a/code/g3soft-erp/web/src/App.vue +++ b/code/g3soft-erp/web/src/App.vue @@ -1,7 +1,48 @@ + + diff --git a/code/g3soft-erp/web/src/composables/useOnline.ts b/code/g3soft-erp/web/src/composables/useOnline.ts new file mode 100644 index 00000000..5fa60ff3 --- /dev/null +++ b/code/g3soft-erp/web/src/composables/useOnline.ts @@ -0,0 +1,63 @@ +import { computed, onBeforeUnmount, readonly, ref, type ComputedRef, type Ref } from 'vue' + +/** + * 网络在线状态。 + * + * 对照 fms-vue 的 App.vue —— 它在应用根监听 window 的 online/offline, + * 断网时盖一层离线缺省页(FallbackPage status="offline"),恢复后自动移除。 + * + * 为什么用 navigator.onLine + 事件监听,而不是轮询: + * 轮询会持续发请求,白耗流量与电量;这两个 API 是浏览器原生事件驱动, + * 零轮询、零定时器,断网/恢复的响应是即时的。性能开销可忽略。 + * + * ⚠️ 已知局限(fms-vue 也记了同一条):navigator.onLine 只反映【本机是否连上网】, + * 连在局域网但服务端不可达时它仍报在线。那种场景由接口层兜底 —— + * request.ts 对「无 status(服务端不可达)」统一弹「网络异常」提示。 + * 所以本 composable 不需要、也不应该去做健康检查。 + */ + +/** 模块级单例:多处读取共用一份状态与一套监听 */ +const online = ref(true) +let listenerCount = 0 + +function sync(): void { + online.value = navigator.onLine +} + +/** 首个使用者挂监听,最后一个使用者解绑 —— 避免重复注册 */ +function bind(): void { + if (listenerCount === 0) { + sync() + window.addEventListener('online', sync) + window.addEventListener('offline', sync) + } + listenerCount += 1 +} + +function unbind(): void { + listenerCount -= 1 + if (listenerCount > 0) return + + window.removeEventListener('online', sync) + window.removeEventListener('offline', sync) +} + +/** 只读的在线态(对外只读,写入只能由浏览器的 online/offline 事件驱动) */ +const isOnline = readonly(online) + +/** 离线态:取反,供模板直接 v-if */ +const isOffline: ComputedRef = computed(() => !online.value) + +export interface UseOnlineReturn { + /** 当前是否在线 */ + isOnline: Readonly> + /** 当前是否离线 */ + isOffline: ComputedRef +} + +export function useOnline(): UseOnlineReturn { + bind() + onBeforeUnmount(unbind) + + return { isOnline, isOffline } +} diff --git a/code/g3soft-erp/web/src/layouts/LayoutBreadcrumb.vue b/code/g3soft-erp/web/src/layouts/LayoutBreadcrumb.vue index d29259b6..5ee981f2 100644 --- a/code/g3soft-erp/web/src/layouts/LayoutBreadcrumb.vue +++ b/code/g3soft-erp/web/src/layouts/LayoutBreadcrumb.vue @@ -3,31 +3,59 @@ import { computed } from 'vue' import { useRoute } from 'vue-router' import { House } from '@lucide/vue' import { usePreferencesStore } from '@/preferences' -import { findMenuItemByPath, findParentKeyByPath } from '@/router' -import { menuItems } from '@/constants/menu' +import { findMenuItemByKey, findMenuItemByPath, findParentKeysByPath } from '@/router' + +/** 面包屑单项:有 path 的是可点的父级,没有的是当前页。 */ +interface Crumb { + title: string + path?: string +} /** * 面包屑。 - * 数据来自菜单常量:父级分组 + 当前页,与侧栏同源,不额外维护。 + * 数据来自菜单常量:祖先分组链 + 当前页,与侧栏同源,不额外维护。 */ const route = useRoute() const prefStore = usePreferencesStore() const pref = computed(() => prefStore.preferences) -/** 当前路径对应的祖先链:[分组, 当前页] */ -const crumbs = computed(() => { - const list: Array<{ title: string; path?: string }> = [] +/** + * 当前路径对应的祖先链:[...分组, 当前页]。 + * + * 父级用 findParentKeysByPath(返回【完整】祖先 key 链)而不是 + * findParentKeyByPath(只取 [0] = 顶级)—— 后者在多级菜单下会漏掉中间层: + * 「海运 → 订舱业务 → 订舱单」只会显示成「海运 / 订舱单」。 + */ +const crumbs = computed(() => { + const list: Crumb[] = [] if (pref.value.breadcrumb.showHome) { list.push({ title: '首页', path: '/' }) } - const current = findMenuItemByPath(route.path) - const parentKey = findParentKeyByPath(route.path) - const parent = parentKey ? menuItems.find((item) => item.key === parentKey) : undefined + // 1) 菜单里的完整祖先链(顶级 → 最近一级分组) + for (const key of findParentKeysByPath(route.path)) { + const node = findMenuItemByKey(key) + if (node) list.push({ title: node.title, path: node.path }) + } - if (parent) list.push({ title: parent.title, path: parent.path }) - if (current && current.key !== parent?.key) list.push({ title: current.title }) + const current = findMenuItemByPath(route.path) + + // 2) 菜单里查不到祖先(页面不在菜单树 / 菜单与路由不同步)时, + // 用路由层级兜底:route.matched 上带 title 的祖先记录即父级。 + // 当前页不在这里补(由下面统一追加),避免自链接。 + if (!current && list.length === 0) { + for (const record of route.matched) { + if (record.path === route.path) break + const title = record.meta.title as string | undefined + if (title) list.push({ title, path: record.path }) + } + } + + // 3) 当前页:菜单里查得到就用菜单名(业务页); + // 查不到(403/404/500 等缺省页、详情页)回退到 route.meta.title。 + const currentTitle = current?.title ?? (route.meta.title as string | undefined) + if (currentTitle) list.push({ title: currentTitle }) return list }) @@ -50,11 +78,16 @@ const visible = computed(() => { / - - {{ crumb.title }} + + + + {{ crumb.title }} + - {{ crumb.title }} + + {{ crumb.title }} + @@ -65,13 +98,8 @@ const visible = computed(() => { align-items: center; gap: 6px; min-width: 0; - font-size: 13px; - - &.is-background { - padding: 4px 10px; - background: var(--g3-color-fill-hover, #f2f3f5); - border-radius: 6px; - } + /* 字号:15px —— 比顶栏 18px 的图标按钮小一档,层次清楚又不至于太弱 */ + font-size: 15px; &__sep { color: var(--g3-color-text-tertiary, #86909c); @@ -80,7 +108,6 @@ const visible = computed(() => { &__item { display: inline-flex; align-items: center; - gap: 4px; color: var(--g3-color-text-secondary, #4e5969); text-decoration: none; @@ -92,5 +119,113 @@ const visible = computed(() => { color: var(--g3-color-text, #1f2329); } } + + &__content { + display: inline-flex; + align-items: center; + gap: 4px; + } + + /* ---- background 风格 ---- + * 照搬 vben 的 breadcrumb-background.vue:整条不是「灰底圆角块」,而是 + * 一段段 accent 底色的「>」形(面包屑步骤条)。段与段之间不再用 / 分隔符, + * 靠两块 14px 边框三角拼接: + * ::before 放在段左侧 28px 处,左三角透明 → 形成向左的凹口; + * ::after 放在段右侧,只留左三角着色 → 形成指向右的尖角。 + * 14px 边框 × 上下两半 = 28px,正好等于段高 h-7(28px)。 */ + &.is-background { + gap: 0; + font-size: 13px; + + /* 有箭头就没有分隔符了 */ + .layout-breadcrumb__sep { + display: none; + } + + .layout-breadcrumb__item { + position: relative; + display: flex; + align-items: center; + height: 28px; + /* mr-9(36px):给右箭头占的 28px 留位,另留 8px 视觉间隙 */ + margin-right: 36px; + padding: 0 8px 0 5px; + color: var(--g3-color-text-secondary, #4e5969); + background: var(--g3-color-accent, #f2f3f5); + transition: background-color 0.2s ease; + + &::before, + &::after { + content: ''; + position: absolute; + top: 0; + width: 0; + height: 0; + border: 14px solid var(--g3-color-accent, #f2f3f5); + } + + &::before { + left: -28px; + z-index: 10; + border-left-color: transparent; + } + + &::after { + left: 100%; + border-color: transparent; + border-left-color: var(--g3-color-accent, #f2f3f5); + } + + /* 首段:左端不开口,左侧留白加大并做左圆角 */ + &:first-child { + padding-left: 15px; + border-radius: 2px 0 0 2px; + + &::before { + border: none; + } + } + + /* 末段(当前页):不做右箭头,右侧留白加大并做右圆角 */ + &:last-child { + padding-right: 15px; + border-radius: 0 2px 2px 0; + + &::after { + border: none; + } + } + + &.is-current { + color: var(--g3-color-foreground, #373c43); + } + + /* 悬停只换底色,不改文字色(对齐 vben:当前页不可点,不参与 hover) */ + &:not(:last-child):hover { + color: var(--g3-color-text-secondary, #4e5969); + background: var(--g3-color-accent-hover, #e3e5e8); + + &::before { + border-color: var(--g3-color-accent-hover, #e3e5e8); + border-left-color: transparent; + } + + &::after { + border-left-color: var(--g3-color-accent-hover, #e3e5e8); + } + } + } + + /* 内容整体左移,视觉上落进「>」的口子里(照 vben 的 -ml-3 / 首段 -ml-1) */ + .layout-breadcrumb__content { + position: relative; + z-index: 10; + margin-left: -12px; + } + + .layout-breadcrumb__item:first-child .layout-breadcrumb__content { + margin-left: -4px; + } + } } diff --git a/code/g3soft-erp/web/src/layouts/LayoutHeader.vue b/code/g3soft-erp/web/src/layouts/LayoutHeader.vue index 935ade23..b9e1c5a1 100644 --- a/code/g3soft-erp/web/src/layouts/LayoutHeader.vue +++ b/code/g3soft-erp/web/src/layouts/LayoutHeader.vue @@ -1,22 +1,27 @@