| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- /**
- * A4 画布正文解析(配合 pages/hr/components/A4Preview.vue 使用)。
- *
- * 后端 documentContent 的字段口径:「A4画布正文JSON或HTML;变量统一使用双花括号编码占位,
- * 例如 {{EMPLOYEE_NAME}}」。这里统一解析成三种形态:
- * - blocks:JSON 画布({ blocks: [...] } 或 block 数组),拆成可渲染的块
- * - html :HTML 正文,交给 rich-text 渲染
- * - text :无法解析时按纯文本展示,保证历史数据也能看到内容
- */
- const TITLE_TYPES = ["title", "heading", "h1", "h2", "header"];
- const SIGN_TYPES = ["sign", "signature", "date", "footer"];
- /** 把一段文本按 {{变量名}} 切成普通文本 / 变量片段,供预览高亮 */
- export function splitVariableSegments(value) {
- const source = String(value == null ? "" : value);
- const pattern = /\{\{\s*([^{}]+?)\s*\}\}/g;
- const segments = [];
- let last = 0;
- let match = pattern.exec(source);
- while (match) {
- if (match.index > last) {
- segments.push({ text: source.slice(last, match.index), isVar: false });
- }
- segments.push({ text: match[1], isVar: true });
- last = match.index + match[0].length;
- match = pattern.exec(source);
- }
- if (last < source.length) segments.push({ text: source.slice(last), isVar: false });
- return segments;
- }
- function cellText(cell) {
- if (cell && typeof cell === "object") {
- return String(cell.text != null ? cell.text : cell.value != null ? cell.value : "");
- }
- return cell == null ? "" : String(cell);
- }
- function normalizeRows(block) {
- const rows = block.rows || block.cells || block.table;
- if (!Array.isArray(rows)) return [];
- const result = [];
- rows.forEach((rawRow) => {
- const rowArray = Array.isArray(rawRow) ? rawRow : [rawRow];
- // 每行的每个 cell 拆成 VariableSegment[];空行丢弃
- const cells = rowArray.map((cell) => splitVariableSegments(cellText(cell)));
- if (cells.length) result.push(cells);
- });
- return result;
- }
- function normalizeBlocks(parsed) {
- const raw = Array.isArray(parsed) ? parsed : parsed && parsed.blocks;
- if (!Array.isArray(raw)) return [];
- return raw.reduce((blocks, item, index) => {
- const key = `block-${index}`;
- if (item == null) return blocks;
- if (typeof item !== "object") {
- const text = String(item);
- if (text.trim()) blocks.push({ key, kind: "para", segments: splitVariableSegments(text) });
- return blocks;
- }
- const block = item;
- const type = String(block.type || "").toLowerCase();
- if (type === "table") {
- const rows = normalizeRows(block);
- if (rows.length) blocks.push({ key, kind: "table", rows });
- return blocks;
- }
- const text = block.text != null ? block.text : block.content != null ? block.content : block.value != null ? block.value : "";
- if (!String(text).trim()) return blocks;
- const kind = TITLE_TYPES.indexOf(type) >= 0 ? "title" : SIGN_TYPES.indexOf(type) >= 0 ? "sign" : "para";
- blocks.push({ key, kind, segments: splitVariableSegments(text) });
- return blocks;
- }, []);
- }
- /** HTML 正文里去掉脚本与内联事件,避免画布内容带出可执行代码 */
- function sanitizeHtml(html) {
- return String(html)
- .replace(/<script[\s\S]*?<\/script>/gi, "")
- .replace(/<script[^>]*>/gi, "")
- .replace(/\son\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, "");
- }
- /** 解析画布正文 */
- export function parseA4Content(content = "") {
- const text = String(content == null ? "" : content).trim();
- if (!text) return { type: "empty", blocks: [], html: "", text: "" };
- if (text.charAt(0) === "<") return { type: "html", blocks: [], html: sanitizeHtml(text), text: "" };
- if (text.charAt(0) === "{" || text.charAt(0) === "[") {
- try {
- const blocks = normalizeBlocks(JSON.parse(text));
- if (blocks.length) return { type: "blocks", blocks, html: "", text: "" };
- } catch (error) {
- // 解析失败按纯文本展示
- }
- }
- return { type: "text", blocks: [], html: "", text };
- }
- export default { parseA4Content, splitVariableSegments };
|