优化PrintDesigner组件,新增列的顺序属性,调整列排序逻辑,确保列在打印预览中的顺序与设计一致。
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
<a-textarea
|
||||
v-model:value="tableColumnsJson"
|
||||
:rows="6"
|
||||
placeholder='例如:[{"title":"物料","field":"name","width":90},{"title":"数量","field":"qty","width":45}]'
|
||||
placeholder='例如:[{"order":0,"title":"物料","field":"name","width":90},{"order":1,"title":"数量","field":"qty","width":45}]'
|
||||
@focus="onColumnsEditorFocus"
|
||||
@blur="onColumnsEditorBlur"
|
||||
/>
|
||||
@@ -254,6 +254,7 @@
|
||||
}
|
||||
|
||||
type SimpleColumn = {
|
||||
order?: number;
|
||||
title: string;
|
||||
field: string;
|
||||
width?: number;
|
||||
@@ -261,11 +262,24 @@
|
||||
};
|
||||
|
||||
function normalizeColumns(cols: any[]): any[] {
|
||||
return cols.map((c) => ({
|
||||
title: c.title || c.field || '列',
|
||||
field: c.field || '',
|
||||
width: Number(c.width || 60),
|
||||
align: c.align || 'left',
|
||||
const normalized = (Array.isArray(cols) ? cols : []).map((c, idx) => {
|
||||
const orderNum = Number(c?.order);
|
||||
return {
|
||||
_idx: idx,
|
||||
_order: Number.isFinite(orderNum) ? orderNum : idx,
|
||||
title: c?.title || c?.field || '列',
|
||||
field: c?.field || '',
|
||||
width: Number(c?.width || 60),
|
||||
align: c?.align || 'left',
|
||||
};
|
||||
});
|
||||
normalized.sort((a, b) => a._order - b._order || a._idx - b._idx);
|
||||
return normalized.map((c, idx) => ({
|
||||
title: c.title,
|
||||
order: idx,
|
||||
field: c.field,
|
||||
width: c.width,
|
||||
align: c.align,
|
||||
colspan: 1,
|
||||
rowspan: 1,
|
||||
}));
|
||||
@@ -274,6 +288,7 @@
|
||||
function compactColumnsForEditor(cols: any[]) {
|
||||
return normalizeColumns(cols).map((c) => ({
|
||||
title: c.title,
|
||||
order: c.order,
|
||||
field: c.field,
|
||||
width: c.width,
|
||||
align: c.align,
|
||||
@@ -296,10 +311,17 @@ function(t,e,printData){
|
||||
var list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
|
||||
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
||||
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', field: 'amount', width: 45, align: 'right' }
|
||||
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', order: 2, field: 'amount', width: 45, align: 'right' }
|
||||
]);
|
||||
columns = columns.slice().sort(function(a,b){
|
||||
var ao = Number(a && a.order);
|
||||
var bo = Number(b && b.order);
|
||||
var av = isFinite(ao) ? ao : 9999;
|
||||
var bv = isFinite(bo) ? bo : 9999;
|
||||
return av - bv;
|
||||
});
|
||||
var globalGroups = printData && Array.isArray(printData.__qhmesGroupFields) ? printData.__qhmesGroupFields : [];
|
||||
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
||||
var style = opts.__qhmesStyle || {};
|
||||
@@ -502,9 +524,11 @@ function(t,e,printData){
|
||||
}
|
||||
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
||||
title: c.title,
|
||||
order: c.order,
|
||||
field: c.field,
|
||||
width: c.width,
|
||||
align: c.align,
|
||||
headerBg: (c as any).headerBg || '',
|
||||
}));
|
||||
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
||||
let changedCount = 0;
|
||||
@@ -649,6 +673,7 @@ function(t,e,printData){
|
||||
}
|
||||
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
||||
title: c.title,
|
||||
order: c.order,
|
||||
field: c.field,
|
||||
width: c.width,
|
||||
align: c.align,
|
||||
@@ -656,18 +681,30 @@ function(t,e,printData){
|
||||
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
||||
const readCanvasTableStyle = () => {
|
||||
try {
|
||||
const tables = Array.from(document.querySelectorAll('#hiprint-printTemplate table'));
|
||||
const tables = Array.from(document.querySelectorAll('#hiprint-printTemplate table')) as HTMLTableElement[];
|
||||
if (!tables.length) return null;
|
||||
const tb = tables[0] as HTMLTableElement;
|
||||
// 选择“最像目标明细表”的 table:表头列最多、且可见
|
||||
const scoreTable = (tb: HTMLTableElement) => {
|
||||
const thCount = tb.querySelectorAll('thead th').length;
|
||||
const hasBody = tb.querySelectorAll('tbody tr').length > 0 ? 5 : 0;
|
||||
const visible = (tb as HTMLElement).offsetParent ? 3 : 0;
|
||||
return thCount * 10 + hasBody + visible;
|
||||
};
|
||||
const tb = [...tables].sort((a, b) => scoreTable(b) - scoreTable(a))[0];
|
||||
const ths = Array.from(tb.querySelectorAll('thead th')) as HTMLElement[];
|
||||
const th = ths.length > 0 ? ths[0] : null;
|
||||
const trHead = tb.querySelector('thead tr') as HTMLElement | 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 trHeadBg = trHead ? window.getComputedStyle(trHead).backgroundColor || '' : '';
|
||||
const headerColors = ths.map((cell) => {
|
||||
const c = window.getComputedStyle(cell).backgroundColor || '';
|
||||
return validColor(c) ? c : trHeadBg;
|
||||
});
|
||||
const firstValidHeaderBg = headerColors.find((c) => validColor(c)) || '';
|
||||
const bg = firstValidHeaderBg || (th ? window.getComputedStyle(th).backgroundColor : '');
|
||||
const headerTitles = ths.map((cell) => (cell.textContent || '').trim());
|
||||
@@ -753,32 +790,29 @@ function(t,e,printData){
|
||||
};
|
||||
// 保真模式:优先用画布真实样式兜底(含表头填充色)
|
||||
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;
|
||||
// 保真模式:预览列顺序优先沿用画布组件本身的字段顺序,避免被 JSON 列配置重排
|
||||
// 仅当画布拿不到列定义时,才回退到列配置 JSON。
|
||||
const baseCols = originColumns.length > 0 ? originColumns : mergedCols;
|
||||
if (baseCols.length > 0) {
|
||||
let orderedCols = [...baseCols];
|
||||
// 进一步按画布可见表头文本重排,保证预览列顺序与画布视觉顺序一致
|
||||
if (Array.isArray(domStyle.headerTitles) && domStyle.headerTitles.length === baseCols.length) {
|
||||
const used = new Set<number>();
|
||||
orderedCols = domStyle.headerTitles.map((ht: string, idx: number) => {
|
||||
const hitIdx = baseCols.findIndex(
|
||||
(c: any, i: number) => !used.has(i) && String(c?.title || '').trim() === String(ht || '').trim()
|
||||
);
|
||||
if (hitIdx >= 0) {
|
||||
used.add(hitIdx);
|
||||
return { ...baseCols[hitIdx], title: ht || baseCols[hitIdx].title };
|
||||
}
|
||||
return { ...baseCols[idx], title: ht || baseCols[idx].title };
|
||||
});
|
||||
}
|
||||
node.options.columns = orderedCols.map((c: any, idx: number) => ({
|
||||
...c,
|
||||
headerBg: Array.isArray(domStyle.headerColors) ? domStyle.headerColors[idx] || (c as any).headerBg || '' : (c as any).headerBg || '',
|
||||
}));
|
||||
}
|
||||
node.options.__qhmesStyle = {
|
||||
fontSize: node.options.__qhmesStyle.fontSize || domStyle.fontSize,
|
||||
@@ -829,6 +863,7 @@ function(t,e,printData){
|
||||
type.options = type.options || {};
|
||||
type.options.columns = mergedCols.map((c) => ({
|
||||
title: c.title,
|
||||
order: c.order,
|
||||
field: c.field,
|
||||
width: c.width,
|
||||
align: c.align,
|
||||
@@ -989,7 +1024,45 @@ function(t,e,printData){
|
||||
}
|
||||
};
|
||||
|
||||
const keys = Object.keys(firstRow);
|
||||
// 字段顺序优先使用“画布可见表头顺序”映射,其次用画布明细列顺序,最后回退 JSON 键顺序
|
||||
const canvasFieldOrder =
|
||||
existingCols
|
||||
.map((c: any) => c?.field)
|
||||
.filter((f: any) => !!f && Object.prototype.hasOwnProperty.call(firstRow, f)) || [];
|
||||
const domHeaderTitles = (() => {
|
||||
try {
|
||||
const tables = Array.from(document.querySelectorAll('#hiprint-printTemplate table'));
|
||||
if (!tables.length) return [] as string[];
|
||||
// 与预览逻辑一致:选表头列最多的表,减少抓错表的概率
|
||||
const scoreTable = (tb: Element) => tb.querySelectorAll('thead th').length * 10 + tb.querySelectorAll('tbody tr').length;
|
||||
const tb = [...tables].sort((a, b) => scoreTable(b) - scoreTable(a))[0];
|
||||
return Array.from(tb.querySelectorAll('thead th')).map((th) => (th.textContent || '').trim());
|
||||
} catch {
|
||||
return [] as string[];
|
||||
}
|
||||
})();
|
||||
let keys = canvasFieldOrder.length > 0 ? [...canvasFieldOrder] : Object.keys(firstRow);
|
||||
// 当 DOM 表头数量与画布列一致时,按表头标题重排字段顺序(避免 existingCols 顺序异常)
|
||||
if (domHeaderTitles.length > 0 && existingCols.length === domHeaderTitles.length) {
|
||||
const used = new Set<number>();
|
||||
const byDom = domHeaderTitles
|
||||
.map((ht) => {
|
||||
const idx = existingCols.findIndex(
|
||||
(c: any, i: number) => !used.has(i) && String(c?.title || '').trim() === String(ht || '').trim()
|
||||
);
|
||||
if (idx < 0) return '';
|
||||
used.add(idx);
|
||||
return existingCols[idx]?.field || '';
|
||||
})
|
||||
.filter((f: string) => !!f && Object.prototype.hasOwnProperty.call(firstRow, f));
|
||||
if (byDom.length > 0) {
|
||||
keys = byDom;
|
||||
}
|
||||
}
|
||||
// 补齐画布中没有但数据里有的字段
|
||||
Object.keys(firstRow).forEach((k) => {
|
||||
if (!keys.includes(k)) keys.push(k);
|
||||
});
|
||||
const allEnglishLike = keys.every((k) => {
|
||||
const t = titleMap.get(k);
|
||||
return !t || t === k;
|
||||
@@ -1027,12 +1100,20 @@ function(t,e,printData){
|
||||
price: '单价',
|
||||
total: '合计',
|
||||
};
|
||||
const cols = keys.map((k) => ({
|
||||
const cols = keys.map((k, idx) => ({
|
||||
title: titleMap.get(k) || zhFallback[k.toLowerCase()] || k,
|
||||
order: idx,
|
||||
field: k,
|
||||
width: 60,
|
||||
align: typeof firstRow[k] === 'number' ? 'right' : 'left',
|
||||
headerBg: '',
|
||||
}));
|
||||
// 若画布表头数量一致,则进一步按表头文本覆盖 title(以画布视觉为准)
|
||||
if (domHeaderTitles.length === cols.length) {
|
||||
cols.forEach((c, idx) => {
|
||||
if (domHeaderTitles[idx]) c.title = domHeaderTitles[idx];
|
||||
});
|
||||
}
|
||||
tableColumnsJson.value = JSON.stringify(cols, null, 2);
|
||||
createMessage.success('已根据 table[0] 推导列配置');
|
||||
}
|
||||
@@ -1152,6 +1233,19 @@ function(t,e,printData){
|
||||
}
|
||||
|
||||
function handlePreview() {
|
||||
const printExt = {
|
||||
styleHandler: () => {
|
||||
return `
|
||||
<style>
|
||||
@media print {
|
||||
* {
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
}
|
||||
}
|
||||
</style>`;
|
||||
},
|
||||
};
|
||||
if (!hiprintTemplate) {
|
||||
return;
|
||||
}
|
||||
@@ -1173,10 +1267,10 @@ function(t,e,printData){
|
||||
const previewTemplate = buildPreviewTemplateWithGrouping();
|
||||
if (previewTemplate) {
|
||||
const previewPrintTpl = new (hiprint as any).PrintTemplate({ template: previewTemplate });
|
||||
previewPrintTpl.print(data, {}, { callback: () => {} });
|
||||
previewPrintTpl.print(data, {}, { ...printExt, callback: () => {} });
|
||||
return;
|
||||
}
|
||||
hiprintTemplate.print(data, {}, { callback: () => {} });
|
||||
hiprintTemplate.print(data, {}, { ...printExt, callback: () => {} });
|
||||
}
|
||||
|
||||
watch(
|
||||
|
||||
@@ -75,9 +75,9 @@ export function createQhmesProvider() {
|
||||
height: 60,
|
||||
__qhmesManaged: true,
|
||||
columns: [
|
||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', field: 'amount', width: 45, align: 'right' },
|
||||
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', order: 2, field: 'amount', width: 45, align: 'right' },
|
||||
],
|
||||
groupFields: [],
|
||||
formatter: `
|
||||
@@ -86,10 +86,17 @@ function(t,e,printData){
|
||||
var list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
|
||||
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
||||
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', field: 'amount', width: 45, align: 'right' }
|
||||
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||
{ title: '金额', order: 2, field: 'amount', width: 45, align: 'right' }
|
||||
]);
|
||||
columns = columns.slice().sort(function(a,b){
|
||||
var ao = Number(a && a.order);
|
||||
var bo = Number(b && b.order);
|
||||
var av = isFinite(ao) ? ao : 9999;
|
||||
var bv = isFinite(bo) ? bo : 9999;
|
||||
return av - bv;
|
||||
});
|
||||
var globalGroups = printData && Array.isArray(printData.__qhmesGroupFields) ? printData.__qhmesGroupFields : [];
|
||||
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
||||
var style = opts.__qhmesStyle || {};
|
||||
|
||||
Reference in New Issue
Block a user