质量模块: - 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫 - 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度 - 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包 - 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件 - 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据 报表中心: - 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册) 其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
6.2 KiB
6.2 KiB
工作进度-8:模板源码在线编辑(免下载 / 上传)
时间:2026-10-20(承接工作进度-7) 触发:用户提出「能不能直接编辑对应报表的模板文件然后保存,而不是下载 → 改 → 上传 → 保存」
一、结论
可以。模板本来就以文本存在库里(sys_report_files.design_content,TEXT),
之前只是没有「写回文本」的入口,只能整文件上传。本次新增:
- 后端一个接口:
PUT /api/sys/report/{id}/template/content,请求体{ "content": "<模板全文>" } - 前端「模板源码」由只读改为可编辑,配「保存模板 / 格式化 / 撤销修改 / 放大编辑」
原来的「上传 .grf / 下载 .grf」保留不动(用于本机设计器排版、外部模板导入)。
二、方案取舍
| 方案 | 说明 | 结论 |
|---|---|---|
| 现状:下载 → 本地改 → 上传 | 需要本地装锐浪设计器或文本编辑器,来回一次文件 | 保留,不再作为主路径 |
| 在线编辑模板源码 + 保存 | 文本表单直接改 .grf 内容,保存即入库,刷新预览立即生效 |
✅ 本次实现 |
| 内嵌可视化设计器(拖控件、改属性) | 需要对 .grf 全量解析 / 回写;GR7 设计器格式是 _WR 压缩文本,无解码器 |
不做(见工作进度-5 遗留) |
三、后端
3.1 DTO(ReportDtos.cs)
public class ReportTemplateContentDto { public string? Content { get; set; } }
3.2 Service(IReportService / ReportService.SaveTemplateContentAsync)
public async Task<int> SaveTemplateContentAsync(long id, string content)
{
var entity = ... ?? throw new F9NotFoundException("报表不存在");
var text = NormalizeTemplateContent(content); // 去 BOM / trim,空内容拦截
if (head.StartsWith('{') || head.StartsWith('['))
JsonDocument.Parse(text); // JSON 语法校验,失败抛 F9OperationException
await SaveTemplateAsync(entity, text); // 复用「有则更新 ReportFile,无则新建并绑定」
...
return text.Length;
}
- 与上传接口走同一套落库逻辑(
SaveTemplateAsync),因此「新建 / 更新 / 绑定report_file_id」行为一致 - 校验规则:
| 内容形态 | 服务端行为 |
|---|---|
{...} / [...](GR6 JSON 模板) |
语法校验,不合法直接 400「模板 JSON 不合法:…」 |
_WR...(GR7 设计器压缩文本) |
原样保存(无法在服务端校验) |
| 空 / 仅空白 | 400「报表模板内容为空」 |
3.3 接口(SysReportController)
PUT /api/sys/report/{id}/template/content
Body: { "content": "..." }
200 → { code: 200, data: { id, size, hasTemplate: true }, message: "模板已保存" }
404 → 报表不存在 400 → 内容为空 / JSON 不合法
与已有接口的关系:
| 方法 | 路由 | 用途 |
|---|---|---|
| GET | /{id}/template |
渲染库 / 前端读模板 |
| POST | /{id}/template |
上传 .grf 文件(设计器回传) |
| PUT | /{id}/template/content |
在线编辑后保存文本(本次新增) |
| GET | /{id}/template/download |
下载 .grf 给本机设计器 |
四、前端
4.1 ReportEditor.vue —— 「模板源码」页
- 去掉
readonly;v-model="templateSource"直接编辑 - 新增状态:
templateSaved(已入库内容,用于判定「未保存」)、templateSaving、wrUnlocked、sourceMaximized - 工具条:
| 按钮 / 标签 | 行为 |
|---|---|
JSON 模板 / _WR 压缩模板 |
当前模板形态提示 |
未保存 |
templateSource !== templateSaved 时显示 |
| 格式化 | JSON.parse + JSON.stringify(…, '\t');非 JSON 禁用 |
| 撤销修改 | 确认后恢复为上次保存内容 |
| 放大编辑 | 整屏弹窗编辑同一份文本(同一 v-model) |
| 保存模板 | 前端先校验 JSON,再 PUT /sys/report/{id}/template/content |
- 保存成功后:重新拉取模板 →
hasTemplate = true→ 重置预览(提示点「刷新预览」)→emit('saved') - 「保存报表」时若模板源码有未保存修改,会先弹确认:
先保存模板/忽略模板修改,避免手改内容被静默丢弃 _WR压缩模板默认只读,需勾「允许编辑」才可改,保存前再二次确认(无本地校验能力)- 报表未保存时提示「请先保存报表,再编辑模板源码」;已保存但无模板时也可以直接粘贴 JSON 保存(相当于无文件新建模板)
4.2 预览缓存
grhtml5 拉模板是普通 GET,容易被浏览器缓存。预览 URL 追加 _t=Date.now()(编辑器与报表中心都加),保证改完模板点「刷新预览」能看到新内容。
4.3 views/report/manage/index.vue —— 列表页入口
- 每行新增「编辑模板」:选中该报表并让右侧工作台直接打开「模板源码」页(
ReportEditor新增openSourceprop,配合designerKey强制重载) - 「上传模板 / 下载模板」保留
五、验证
| 项 | 结果 |
|---|---|
后端 dotnet build(输出到独立目录避开运行中进程锁定) |
✅ 0 错误 |
IReportService / ReportService / DTO / Controller 改动 |
✅ 编译通过 |
前端 IDE 诊断(ReportEditor.vue、manage/index.vue、center/index.vue) |
✅ 0 诊断 |
前端 npm run build |
⚠ 当前环境无 node/npm,未执行;需在本机 cd web && npm run build 复核 |
运行中的
F9WebAiTest.Api仍在用旧程序集,需重启后端后新接口才生效。
六、遗留
- 无语法高亮 / 行号:目前是
el-input textarea,后续可换 CodeMirror / Monaco(需新增前端依赖)。 - 无模板历史版本:手工改坏后无法一键回滚,只能重新「生成模板」或重新上传;建议后续给
sys_report_files加版本表。 _WR仍不能结构化编辑:需要锐浪的解码 / 编码算法,暂以「允许编辑 + 风险提示」兜底。- 保存模板不刷新明细字段 / 报表参数页签:手改
DetailGrid.Recordset.Field后,左栏字段列表仍是旧值(点「识别明细列与报表参数」可同步)。