质量模块: - 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫 - 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度 - 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包 - 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件 - 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据 报表中心: - 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册) 其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
9.8 KiB
工作进度-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 个注册控件分组下拉 | ✅ |
| 探测失败不阻断:提示原因,仍可手工加列保存 | ✅ |
六、遗留与后续建议
- Provider 参数声明需手工补:新增
IReportDataProvider时建议同时实现IReportDataProviderMeta,否则编辑器只能识别字段、参数要手填。 _WR7_模板不能反解列:编辑这类报表时「明细字段」为空,重新生成会覆盖设计器排版(已有二次确认)。若需要,可在后端通过 GR7 库或gr.wr.decodeWR的等价实现解码后再解析。- 探测会执行真实查询:大报表(如 942 条职工)识别需要等待查询完成;后续可给 Provider 增加「仅取结构」的轻量方法(如返回空参数 +
LIMIT 0)。 - 列宽均分:自动生成的列宽按 17cm 均分,超宽字段(地址、备注)仍需到设计器里调整。
- 进度-4 遗留项仍在:
Report_FavoriteGroup未实现、历史废弃列未 DROP、进度-3 的Number()精度隐患等。