diff --git a/jeecgboot-vue3/src/views/print/template/PrintDesigner.vue b/jeecgboot-vue3/src/views/print/template/PrintDesigner.vue index 7a296fc..cb4932a 100644 --- a/jeecgboot-vue3/src/views/print/template/PrintDesigner.vue +++ b/jeecgboot-vue3/src/views/print/template/PrintDesigner.vue @@ -344,7 +344,8 @@ function(t,e,printData){ for (var h=0;h'+esc(hc.title || hc.field || '')+''; } html += ''; @@ -653,6 +654,41 @@ function(t,e,printData){ align: c.align, })); const mergedGroups = groupEnabled.value ? [...groupFields.value] : []; + const readCanvasTableStyle = () => { + try { + const tables = Array.from(document.querySelectorAll('#hiprint-printTemplate table')); + if (!tables.length) return null; + const tb = tables[0] as HTMLTableElement; + const ths = Array.from(tb.querySelectorAll('thead th')) as HTMLElement[]; + const th = ths.length > 0 ? ths[0] : null; + const td = tb.querySelector('tbody td') as HTMLElement | null; + const ref = th || td; + if (!ref) return null; + const cs = window.getComputedStyle(ref); + const tbcs = window.getComputedStyle(tb); + const validColor = (c: string) => !!c && c !== 'rgba(0, 0, 0, 0)' && c !== 'transparent'; + const headerColors = ths.map((cell) => window.getComputedStyle(cell).backgroundColor || ''); + const firstValidHeaderBg = headerColors.find((c) => validColor(c)) || ''; + const bg = firstValidHeaderBg || (th ? window.getComputedStyle(th).backgroundColor : ''); + const headerTitles = ths.map((cell) => (cell.textContent || '').trim()); + const borderWidth = parseFloat(cs.borderTopWidth || '1') || 1; + const tableWidth = tb.style.width || (tbcs.width && tbcs.width !== 'auto' ? tbcs.width : '100%'); + return { + fontSize: parseFloat(cs.fontSize || '10') || 10, + borderColor: cs.borderTopColor || '#000', + borderWidth: borderWidth, + cellPadding: `${cs.paddingTop || '2px'} ${cs.paddingRight || '4px'}`, + headerBg: bg && bg !== 'rgba(0, 0, 0, 0)' ? bg : '', + headerTitles, + headerColors, + tableWidth, + }; + } catch (e) { + console.warn(e); + return null; + } + }; + const domStyle = readCanvasTableStyle(); const extractColumnsFromElement = (el: any) => { // 1) 新结构:options.columns @@ -715,6 +751,44 @@ function(t,e,printData){ headerBg: originOptions.headerBg, tableWidth: originOptions.tableWidth, }; + // 保真模式:优先用画布真实样式兜底(含表头填充色) + if (preserveDesignStyle.value && domStyle) { + // 保真模式:列顺序与表头标题优先按画布 DOM 对齐 + if (Array.isArray(domStyle.headerTitles) && domStyle.headerTitles.length > 0) { + const srcCols = Array.isArray(node.options.columns) ? [...node.options.columns] : []; + const unused = [...srcCols]; + const ordered = domStyle.headerTitles.map((title: string, idx: number) => { + let hitIdx = -1; + if (title) { + hitIdx = unused.findIndex((c: any) => (c?.title || '').trim() === title); + } + let base: any; + if (hitIdx >= 0) { + base = unused.splice(hitIdx, 1)[0]; + } else if (unused.length > 0) { + base = unused.shift(); + } else { + base = srcCols[idx] || mergedCols[idx] || { title: title || `列${idx + 1}`, field: '' }; + } + return { + ...base, + title: title || base?.title || base?.field || `列${idx + 1}`, + headerBg: Array.isArray(domStyle.headerColors) ? domStyle.headerColors[idx] || '' : '', + }; + }); + if (ordered.length > 0) { + node.options.columns = ordered; + } + } + node.options.__qhmesStyle = { + fontSize: node.options.__qhmesStyle.fontSize || domStyle.fontSize, + borderColor: node.options.__qhmesStyle.borderColor || domStyle.borderColor, + borderWidth: node.options.__qhmesStyle.borderWidth || domStyle.borderWidth, + cellPadding: node.options.__qhmesStyle.cellPadding || domStyle.cellPadding, + headerBg: node.options.__qhmesStyle.headerBg || domStyle.headerBg, + tableWidth: node.options.__qhmesStyle.tableWidth || domStyle.tableWidth, + }; + } node.options.formatter = getTableSimpleFormatter(); } @@ -963,35 +1037,6 @@ function(t,e,printData){ createMessage.success('已根据 table[0] 推导列配置'); } - function buildGroupedRowsVisualMerge(rows: any[], groups: string[]) { - if (!Array.isArray(rows) || rows.length === 0 || !Array.isArray(groups) || groups.length === 0) { - return rows; - } - const out = rows.map((r) => ({ ...r })); - for (let i = 1; i < out.length; i++) { - for (let level = 0; level < groups.length; level++) { - const field = groups[level]; - // 上层一致才比较当前层 - let upperSame = true; - for (let up = 0; up < level; up++) { - const upField = groups[up]; - if ((out[i][upField] ?? '') !== (out[i - 1][upField] ?? '')) { - upperSame = false; - break; - } - } - if (!upperSame) break; - if ((out[i][field] ?? '') === (out[i - 1][field] ?? '')) { - // 保真模式下只清空重复值,保持原组件样式不变 - out[i][field] = ''; - } else { - break; - } - } - } - return out; - } - function parseTemplatePayload(str: string | undefined) { if (!str || str === '{}') { return { template: {}, ext: null as any }; @@ -1111,14 +1156,6 @@ function(t,e,printData){ return; } const data = parsePrintData(); - if (preserveDesignStyle.value) { - // 样式优先:不改模板结构,只改分组字段的显示数据 - if (groupEnabled.value && Array.isArray(data.table) && groupFields.value.length > 0) { - data.table = buildGroupedRowsVisualMerge(data.table, groupFields.value); - } - hiprintTemplate.print(data, {}, { callback: () => {} }); - return; - } // 兜底:即使元素 options 未同步,预览时也从全局参数读取列和分组配置 let parsedCols: any[] = []; try { diff --git a/jeecgboot-vue3/src/views/print/template/hiprint/qhmesProvider.ts b/jeecgboot-vue3/src/views/print/template/hiprint/qhmesProvider.ts index 4f9c2fb..738452a 100644 --- a/jeecgboot-vue3/src/views/print/template/hiprint/qhmesProvider.ts +++ b/jeecgboot-vue3/src/views/print/template/hiprint/qhmesProvider.ts @@ -144,7 +144,8 @@ function(t,e,printData){ for (var h=0;h'+esc(hc.title || hc.field || '')+''; } html += '';