# 工作进度-9:报表设计页(可视化画布 + 官方设计器入口 + 本系统保存) > 时间:2026-10-20(承接工作进度-8) > 触发:用户希望「做一个页面,嵌入 Grid++Report 7 的设计页面,并加上本系统的保存按钮」 --- ## 一、结论:官方设计器无法嵌入网页 先确认事实,避免做出错误承诺: | 检查项 | 结果 | |---|---| | `web/public/grlib/grhtml5-7-min.js` 是否含设计器 API(`insertReportDesigner` 等) | ❌ 无,只有 `insertReportViewer` | | `D:\GridReport7` 安装目录 | 只有查看器库、`gr7designer64.exe`(Win32 桌面程序)、`gr7webapp64.exe`(打印客户端)、`server\gr7svr*.dll`(服务端生成 DLL) | | `D:\GridReport7\Samples\Web`(html / vue / asp.net / jsp) | 只有 HTML5 查看器示例,无网页设计器 | 原因:GR7 的设计器是 WinForms/Win32 程序,浏览器无法 iframe 内嵌,也没有 ActiveX/JS 设计器接口。 因此本页提供两条**可用**的设计路径,并在同一页面里放了本系统的「保存模板」按钮: | 路径 | 排版能力 | 产物格式 | 本页能力 | |---|---|---|---| | A. 页面内可视化画布(自研) | 中(页眉/标题/参数行/明细列/页脚/边距/列宽) | JSON(`6.8.5.0`) | 直接编辑 + 保存 | | B. 唤起本机锐浪 7 设计器 | 强(官方全功能:分组/合计/图表/条码/套打) | `_WR7_` 压缩文本 | 关闭设计器自动回传,本页自动重载 | > 关键取舍:用官方设计器保存后模板变成 `_WR7_` 压缩文本,**网页画布无法再解析**(只能看真实预览、原样保存、继续用设计器)。页面在唤起设计器前会明确提示这一点。 --- ## 二、页面 ### 2.1 路由(静态注册,不依赖菜单权限) `web/src/router/index.js`: ```js { path: 'report/design/:id', name: 'ReportDesign', component: () => import('../views/report/design/index.vue'), meta: { title: '报表设计', icon: 'Edit' } } ``` 入口: - 报表管理页列表行「设计」 - 报表编辑器顶栏「设计页」 新增文件:`web/src/views/report/design/index.vue`。 ### 2.2 页面结构 ``` ┌ 返回管理 | 报表名 编号 [JSON/_WR7_] [未保存] 撤销 重新载入 真实预览 上传 下载 [锐浪7设计器] [保存模板] ┐ ├ 左(画布,自适应缩放 + 10% 档位缩放) │ 右(属性面板) │ │ A4 白纸:页眉 → 报表头(标题/参数行)→ 明细网格 → 页脚 │ 明细列 / 报表标题 / │ │ 表头单元格可点选,右边缘拖拽改列宽(cm 实时写回模板) │ 页眉页脚 / 行高边距 │ └────────────────────────────────────────────────────────────────────┴──────────────────────────┘ ``` ### 2.3 画布渲染(只读近似 + 真实数据) - 单位:模板里的 `Left/Top/Width/Height` 是 **cm**,`Font.Size` 是 **1/10000 pt**;画布按 `1cm = 37.795px` 换算,再用 `transform: scale()` 适配容器 - 版块顺序与 GR 一致:`PageHeader` → `ReportHeader[]`(逐节)→ `DetailGrid`(表头 + 数据行)→ `PageFooter` - 文本替换: - `[#{报表参数}#]` → `/sys/report/{id}/preview` 返回的 `Parameter[0]` 实际值 - `[#SystemVar(PageNumber)#] / PageCount / CurrentDateTime` → 示例值 - 明细行取 `preview.data.recordset` 前 8 行;无数据时渲染 3 行空行占位,保证排版可见 - 画布上的控件 hover 有虚线框,方便确认位置;真正的字体/边距是否精确以「真实预览」为准 ### 2.4 可编辑项(写回模板 JSON) | 面板 | 修改的节点 | 说明 | |---|---|---| | 明细列 · 列标题 | `DetailGrid.ColumnTitle.ColumnTitleCell[].Text` | 缺节点时自动补 | | 明细列 · 列宽 | `DetailGrid.Column[].Width` | 数字框或画布拖拽,0.4–14cm | | 明细列 · 添加/删除 | `Recordset.Field` / `Column` / `ColumnContentCell` / `ColumnTitleCell` | 四处同步增删,避免结构不一致 | | 报表标题 | 报表头第一节里字号最大的文本控件 `Text` + 顶层 `Title` | 找不到控件时给出提示 | | 标题字号 | 该控件 `Font.Size` | 6–36pt | | 页眉/页脚 | `PageHeader/PageFooter.Control[].Text` | 逐控件文本框 | | 页眉/页脚高度 | `PageHeader/PageFooter.Height` | 0–10cm | | 行高 / 表头高 | `ColumnContent.Height` / `ColumnTitle.Height` | 0.3–4cm | | 上下边距 | `Printer.TopMargin / BottomMargin` | 0–6cm | **只改已知且我们已在用、能被 GR 正确识别的键**,不写未知属性(例如单元格对齐、合并、边框),避免模板被 GR 判为损坏。分组、合计、图表、条码等请用官方设计器。 ### 2.5 保存 复用工作进度-8 新增的接口,不改后端: ``` PUT /api/sys/report/{id}/template/content { "content": "" } ``` - 保存前序列化 `JSON.stringify(doc, null, '\t')`;服务端做 JSON 语法校验后落库 - 有未保存修改时顶栏显示红色「未保存」,离开页面会二次确认 - `撤销修改` = 重新解析已入库文本 ### 2.6 真实预览 / 上传下载 / 官方设计器 - **真实预览**:全屏弹窗内用官方 `rubylong.grhtml5.insertReportViewer` 渲染(模板 URL 带 `_t` 时间戳防缓存);有未保存修改时提示「预览的是已保存模板」 - **上传 .grf / 下载 .grf**:复用既有接口 - **锐浪 7 设计器**: ``` f9designer://open?base=&rid=&token=&code=<报表编号> ``` 由 `tools/report-designer/F9ReportDesignerHandler.ps1` 处理:下载模板 → 启动 `gr7designer64.exe` → 关闭后自动 `POST /{id}/template` 回传 本页在 `window.focus` 后延迟 2s 自动重新载入模板(也有「重新载入」按钮兜底) ### 2.7 `_WR7_` / 非 JSON 模板兜底 - 检测到 `_WR` 前缀:不渲染画布,显示说明和三个入口(真实预览 / 用设计器修改 / 去模板源码页) - JSON 解析失败:显示错误 + 引导上传或去源码页修复 --- ## 三、验证 | 项 | 结果 | |---|---| | Vite 编译 `views/report/design/index.vue`(请求 `http://localhost:5173/src/views/report/design/index.vue`) | ✅ HTTP 200(SFC 编译通过) | | 同法验证 `ReportEditor.vue` / `manage/index.vue` / `router/index.js` | ✅ HTTP 200 | | IDE 诊断(4 个改动文件) | ✅ 0 | | 后端 | 未改动(复用 `PUT /template/content`),无需重新编译 | | 浏览器实操(登录点一遍) | ⚠ 未执行:当前环境无 node / playwright-cli,无法自动化;Vite 已确认可编译,建议人工点一遍 | > 保存模板需要后端已重启(工作进度-8 的接口)。 --- ## 四、遗留 1. **`_WR7_` 无解码器**:用过官方设计器后画布失效,这是格式决定的;若要两者兼得,需要锐浪的 `decodeWR` 算法或官方提供 JSON 导出。 2. **画布不是 100% 像素级还原**:字体度量、单元格对齐、自动行高与 GR 有差异,最终以「真实预览」为准。 3. **未支持控件级拖拽/属性编辑**:位置 `Left/Top`、边框、颜色、对齐暂不开放,避免写入未经验证的键。 4. **样本数据依赖 `/preview`**:需要参数才能出数的报表会显示空行占位;后续可加「手工填参数再取样本」。 5. **无模板版本历史**:画布改坏后只能重新生成模板或用设计器重排。