Files
f9-web-ai-test/工作进度/工作进度-5.md
T
yanpeng200212 a8890c81e9 新增质量模块(工艺执行书 / 样茧试缫)与报表中心
质量模块:
- 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫
- 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度
- 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包
- 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件
- 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据

报表中心:
- 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册)

其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
2026-10-22 10:30:37 +08:00

9.8 KiB
Raw Blame History

工作进度-5:新增报表「自动配置参数与列 + 默认初始化报表」

时间:2026-10-20(承接工作进度-4) 环境:后端 .NET 8 + FreeSql + MediatR(Windows/SQLite),前端 Vue3 + Element Plus + Vite 目标:把工作进度-4 删除内嵌设计器时一起丢失的能力补回来 —— 新增报表时自动识别数据源参数与明细列,并默认生成一张可用的报表(模板 + 配置一次落库)


一、背景

工作进度-4 移除了自研网页设计器(ReportDesigner.vue),报表管理页只保留「配置 / 参数 / 预览 / 模板源码」。

结果:

  • 新增报表必须先手工上传 .grf 才能预览,否则只有 1 个 Field1 占位列;
  • Config.Parms(查询参数)全靠手填,控件类型是纯文本;
  • 数据源有哪些字段(明细列)只能去下载模板里看。

本轮补齐:选数据源 → 自动带出参数与明细列 → 保存时自动生成 .grf → 立即可预览。


二、后端

2.1 新增文件

文件 说明
Application/Dtos/SYS/Reports/ReportScaffoldDtos.cs ReportParameterDefDto(参数定义) / ReportDataSourceInfoDto(数据源) / ReportFieldDefDto(明细列) / ReportProbeRequestDto / ReportProbeResultDto
Application/Interfaces/Reports/IReportDataProviderMeta.cs 可选元数据接口:Label / GetParameters() / GetFieldLabels(),未实现的 Provider 行为不变(默认接口方法)

2.2 服务

IReportService / ReportService 新增两个方法:

方法 说明
GetDataSourcesAsync() 遍历 DI 注册的 IReportDataProvider 字典,返回 DsName + 中文名 + 参数定义
ProbeAsync(request) 按 DsName(优先)或 DataSql 执行一次查询,返回参数定义 + 明细字段(列名/中文名/类型)+ 前 3 行样本;失败时 Error 带原因且不抛 500

探测细节:

  • Provider 分支:provider.GetData(pairs, mediator) → 取第一个有数据的记录集 → 首行键名即列名 → IReportDataProviderMeta.GetFieldLabels() 提供中文标题
  • SQL 分支:ExecuteDataTable(sql)(只绑定 SQL 里出现的 @参数)→ DataTable.Columns 即列名
  • 参数取值:请求值 > Parms[].DefaultValue;缺少默认值直接传空,查询失败只记 Error
  • 错误分级:有 DsName 但找不到实现 → 未找到数据源实现:xxx;两者都空 → 请先选择数据源(DsName)或填写数据 SQL

2.3 接口

端点 说明
GET api/sys/report/datasources 数据源清单(参数定义随附)
POST api/sys/report/scaffold 结构探测:{ dsName, dataSql, parms, params } → { parameters, fields, rows, total, error }

路由说明:datasources 为字面量段,优先于 {id} 匹配,不影响原详情接口。

2.4 示例 Provider 补元数据

RepDt_Sys_Users 实现 IReportDataProviderMeta:

  • 参数:q_dep(部门 / AntdUI.Select / Ds_Service=/sys/department)、q_like(关键字 / AntdUI.Input)
  • 字段中文名:EmployeeCode=工号, EmployeeName=姓名, Gender=性别, DepName=部门, WorktypeName=工种, Phone=电话, RuzhiDate=入职日期

新增数据源时实现该接口即可被编辑器自动识别,未实现也能正常查询。


三、前端(components/report/ReportEditor.vue)

3.1 数据源与自动识别

项 实现
数据源输入 el-select(filterable + allow-create):列出来自 /sys/report/datasources 的「中文名(DsName)」,也可手输未登记名称
自动触发 选中数据源即调用 POST /sys/report/scaffold(onDsChange)
手动触发 配置页「自动识别参数与列」按钮;参数页 / 明细字段页也可再次识别
结果 覆盖 Parms(名称/标签/提示/控件类型/Ds_Service/Ds_Static/默认值)+ 填充明细列,并自动切到「明细字段」tab

3.2 明细字段 tab

新增第三个左栏 tab(明细字段 (n)):

  • 列卡片:启用勾选 / 字段名(可改) / 列标题(可改) / 列宽(0.8~8cm)/ 删除
  • + 添加列 手工补列(数据源无数据时用)
  • 生成模板:按当前字段生成 .grf,新建时随保存写入,编辑时点保存才覆盖(有二次确认)
  • 底部展示探测返回的样本行,便于核对字段含义

编辑已有报表时,若模板是 JSON 格式,会反解出 DetailGrid.Recordset.Field + Column.Width + ColumnTitleCell.Text 回填到明细字段;_WR7_ 压缩模板无法反解(留空,不影响继续使用)。

3.3 默认生成模板(buildTemplate())

结构派生自内置示例模板 employee_directory.grf(GR7 兼容 Version=6.8.5.0):

区块 内容
DetailGrid 记录集字段 = 启用的明细列;Column.Width 按 17cm 均分(1.2~4);ColumnTitleCell.Text = 列标题
ReportHeader[0] 报表标题(名称)
ReportHeader[1] 每个查询参数一组「标签 StaticBox + Parameter 绑定 StaticBox」,每行 4 组
Parameter 参数名 = Parms[].Label(与后端 Parameter_Data 键一致,如 部门 / 关键字)
PageHeader / PageFooter 系统名水印 + 页码 + 打印时间(与示例一致)

保存语义:

  • 新建:templateJson = buildTemplate()(无字段时退化为单列占位,保证不会出现「未配置模板」)
  • 编辑:默认不提交模板(避免覆盖设计器上传的 .grf);只有点了「生成模板」(templateDirty)才写入

3.4 顺带修复(工作进度-4 遗留 5 的一部分)

参数「控件类型」由纯文本输入改为分组下拉 + 可手输,数据来自 utils/gridreport/controls 注册中心(38 个 AntdUI / F9 控件),与报表中心查询面板同源。


四、验证记录

4.1 接口层

GET  /api/sys/report/datasources
  → [{ dsName: RepDt_Sys_Users, label: 职工花名册,
       parameters: [q_dep/部门/AntdUI.Select/Ds_Service=/sys/department, q_like/关键字/AntdUI.Input] }]

POST /api/sys/report/scaffold  { dsName: RepDt_Sys_Users }
  → PARMS=2  FIELDS=7  TOTAL=942  ERR=(空)
    EmployeeCode/工号, EmployeeName/姓名, Gender/性别, DepName/部门,
    WorktypeName/工种, Phone/电话, RuzhiDate/入职日期
    ROW0={EmployeeCode:"",EmployeeName:"付华芳",Gender:"女",DepName:"丝业生产中心",...}

POST /api/sys/report/scaffold  { dataSql: "SELECT id AS Id, code AS Code, name AS Name FROM sys_rbac_menus LIMIT 5" }
  → SQL_FIELDS=3  TOTAL=5  ERR=(空)   Id,Code,Name

POST /api/sys/report/scaffold  { dsName: RepDt_Not_Exists }
  → BAD_ERR=未找到数据源实现:RepDt_Not_Exists

4.2 页面全链路(无头 Edge,/report/manage)

LIST_ITEMS=2  TOPBAR=新增报表
ADD_BTN=true  LEFT_TABS=配置,参数 (0),明细字段 (0)   ← 新增后进入空表单
DS_OPTIONS=1 [职工花名册(RepDt_Sys_Users)]          ← 下拉来自注册表
选中后自动识别:
  LEFT_TABS2=配置,参数 (2),明细字段 (7)
  DETECT_INFO=已识别 7 列 / 2 参数
  FIELD_NAMES=EmployeeCode,EmployeeName,Gender,DepName,WorktypeName,Phone,RuzhiDate
  SAMPLE=EmployeeCode= | EmployeeName=付芳华 | Gender=女 | DepName=丝业生产中心 | ...
保存(未上传任何 .grf):
  AFTER_SAVE_TOPBAR=编辑报表 · 诊断自动配置报表
  AFTER_SAVE_TAG=模板已配置
  VIEWER_CHILDREN=3
  VIEWER_TEXT=诊断自动配置报表 部门:(全部) 关键字:(全部) 工号 姓名 性别 部门 工种 电话 入职日期
              付芳华 女 丝业生产中心 15884942961 2012-02-01 何均 女 ...
  ALERT=保存成功
后端核对:
  API_ROW={ hasTemplate:true }
  TPL_FIRST3={            ← 生成的是 JSON 模板(非 _WR)
  TPL_FIELDS=7
  TPL_COLUMNS=工号,姓名,性别,部门,工种,电话,入职日期
  TPL_PARAM=部门,关键字
  CFG_DS=RepDt_Sys_Users  PARMS=q_dep/AntdUI.Select,q_like/AntdUI.Input
  CLEANUP=200             ← 测试报表已删除

4.3 编译 / 运行

  • 后端 dotnet build:0 错误 0 警告(先停 F9WebAiTest.Api 进程再编译,避免 MSB3027)
  • 前端无 lint 诊断;无头 Edge 实跑整页(识别 → 保存 → GR7 渲染)通过
  • 服务:后端 http://localhost:3000、前端 http://localhost:5173 均在运行

五、当前状态

项 状态
新增报表:数据源下拉 + 一键识别参数与列 ✅
新增报表:保存自动生成 .grf(默认初始化),保存后直接可预览 ✅
明细字段可编辑(字段名 / 标题 / 列宽 / 启用)并据此生成模板 ✅
编辑已有 JSON 模板:反解明细列回填 ✅(_WR7_ 压缩模板无法反解)
SQL 数据源(无 DsName)也能探测列 ✅
参数控件类型:38 个注册控件分组下拉 ✅
探测失败不阻断:提示原因,仍可手工加列保存 ✅

六、遗留与后续建议

  1. Provider 参数声明需手工补:新增 IReportDataProvider 时建议同时实现 IReportDataProviderMeta,否则编辑器只能识别字段、参数要手填。
  2. _WR7_ 模板不能反解列:编辑这类报表时「明细字段」为空,重新生成会覆盖设计器排版(已有二次确认)。若需要,可在后端通过 GR7 库或 gr.wr.decodeWR 的等价实现解码后再解析。
  3. 探测会执行真实查询:大报表(如 942 条职工)识别需要等待查询完成;后续可给 Provider 增加「仅取结构」的轻量方法(如返回空参数 + LIMIT 0)。
  4. 列宽均分:自动生成的列宽按 17cm 均分,超宽字段(地址、备注)仍需到设计器里调整。
  5. 进度-4 遗留项仍在:Report_FavoriteGroup 未实现、历史废弃列未 DROP、进度-3 的 Number() 精度隐患等。