优化PrintDesigner组件,新增列的顺序属性,调整列排序逻辑,确保列在打印预览中的顺序与设计一致。
This commit is contained in:
@@ -61,7 +61,7 @@
|
|||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="tableColumnsJson"
|
v-model:value="tableColumnsJson"
|
||||||
:rows="6"
|
: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"
|
@focus="onColumnsEditorFocus"
|
||||||
@blur="onColumnsEditorBlur"
|
@blur="onColumnsEditorBlur"
|
||||||
/>
|
/>
|
||||||
@@ -254,6 +254,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SimpleColumn = {
|
type SimpleColumn = {
|
||||||
|
order?: number;
|
||||||
title: string;
|
title: string;
|
||||||
field: string;
|
field: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
@@ -261,11 +262,24 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function normalizeColumns(cols: any[]): any[] {
|
function normalizeColumns(cols: any[]): any[] {
|
||||||
return cols.map((c) => ({
|
const normalized = (Array.isArray(cols) ? cols : []).map((c, idx) => {
|
||||||
title: c.title || c.field || '列',
|
const orderNum = Number(c?.order);
|
||||||
field: c.field || '',
|
return {
|
||||||
width: Number(c.width || 60),
|
_idx: idx,
|
||||||
align: c.align || 'left',
|
_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,
|
colspan: 1,
|
||||||
rowspan: 1,
|
rowspan: 1,
|
||||||
}));
|
}));
|
||||||
@@ -274,6 +288,7 @@
|
|||||||
function compactColumnsForEditor(cols: any[]) {
|
function compactColumnsForEditor(cols: any[]) {
|
||||||
return normalizeColumns(cols).map((c) => ({
|
return normalizeColumns(cols).map((c) => ({
|
||||||
title: c.title,
|
title: c.title,
|
||||||
|
order: c.order,
|
||||||
field: c.field,
|
field: c.field,
|
||||||
width: c.width,
|
width: c.width,
|
||||||
align: c.align,
|
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 list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
|
||||||
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
||||||
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
||||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||||
{ title: '金额', field: 'amount', 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 globalGroups = printData && Array.isArray(printData.__qhmesGroupFields) ? printData.__qhmesGroupFields : [];
|
||||||
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
||||||
var style = opts.__qhmesStyle || {};
|
var style = opts.__qhmesStyle || {};
|
||||||
@@ -502,9 +524,11 @@ function(t,e,printData){
|
|||||||
}
|
}
|
||||||
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
||||||
title: c.title,
|
title: c.title,
|
||||||
|
order: c.order,
|
||||||
field: c.field,
|
field: c.field,
|
||||||
width: c.width,
|
width: c.width,
|
||||||
align: c.align,
|
align: c.align,
|
||||||
|
headerBg: (c as any).headerBg || '',
|
||||||
}));
|
}));
|
||||||
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
||||||
let changedCount = 0;
|
let changedCount = 0;
|
||||||
@@ -649,6 +673,7 @@ function(t,e,printData){
|
|||||||
}
|
}
|
||||||
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
const mergedCols = normalizeColumns(parsedCols).map((c) => ({
|
||||||
title: c.title,
|
title: c.title,
|
||||||
|
order: c.order,
|
||||||
field: c.field,
|
field: c.field,
|
||||||
width: c.width,
|
width: c.width,
|
||||||
align: c.align,
|
align: c.align,
|
||||||
@@ -656,18 +681,30 @@ function(t,e,printData){
|
|||||||
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
|
||||||
const readCanvasTableStyle = () => {
|
const readCanvasTableStyle = () => {
|
||||||
try {
|
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;
|
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 ths = Array.from(tb.querySelectorAll('thead th')) as HTMLElement[];
|
||||||
const th = ths.length > 0 ? ths[0] : null;
|
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 td = tb.querySelector('tbody td') as HTMLElement | null;
|
||||||
const ref = th || td;
|
const ref = th || td;
|
||||||
if (!ref) return null;
|
if (!ref) return null;
|
||||||
const cs = window.getComputedStyle(ref);
|
const cs = window.getComputedStyle(ref);
|
||||||
const tbcs = window.getComputedStyle(tb);
|
const tbcs = window.getComputedStyle(tb);
|
||||||
const validColor = (c: string) => !!c && c !== 'rgba(0, 0, 0, 0)' && c !== 'transparent';
|
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 firstValidHeaderBg = headerColors.find((c) => validColor(c)) || '';
|
||||||
const bg = firstValidHeaderBg || (th ? window.getComputedStyle(th).backgroundColor : '');
|
const bg = firstValidHeaderBg || (th ? window.getComputedStyle(th).backgroundColor : '');
|
||||||
const headerTitles = ths.map((cell) => (cell.textContent || '').trim());
|
const headerTitles = ths.map((cell) => (cell.textContent || '').trim());
|
||||||
@@ -753,32 +790,29 @@ function(t,e,printData){
|
|||||||
};
|
};
|
||||||
// 保真模式:优先用画布真实样式兜底(含表头填充色)
|
// 保真模式:优先用画布真实样式兜底(含表头填充色)
|
||||||
if (preserveDesignStyle.value && domStyle) {
|
if (preserveDesignStyle.value && domStyle) {
|
||||||
// 保真模式:列顺序与表头标题优先按画布 DOM 对齐
|
// 保真模式:预览列顺序优先沿用画布组件本身的字段顺序,避免被 JSON 列配置重排
|
||||||
if (Array.isArray(domStyle.headerTitles) && domStyle.headerTitles.length > 0) {
|
// 仅当画布拿不到列定义时,才回退到列配置 JSON。
|
||||||
const srcCols = Array.isArray(node.options.columns) ? [...node.options.columns] : [];
|
const baseCols = originColumns.length > 0 ? originColumns : mergedCols;
|
||||||
const unused = [...srcCols];
|
if (baseCols.length > 0) {
|
||||||
const ordered = domStyle.headerTitles.map((title: string, idx: number) => {
|
let orderedCols = [...baseCols];
|
||||||
let hitIdx = -1;
|
// 进一步按画布可见表头文本重排,保证预览列顺序与画布视觉顺序一致
|
||||||
if (title) {
|
if (Array.isArray(domStyle.headerTitles) && domStyle.headerTitles.length === baseCols.length) {
|
||||||
hitIdx = unused.findIndex((c: any) => (c?.title || '').trim() === title);
|
const used = new Set<number>();
|
||||||
}
|
orderedCols = domStyle.headerTitles.map((ht: string, idx: number) => {
|
||||||
let base: any;
|
const hitIdx = baseCols.findIndex(
|
||||||
if (hitIdx >= 0) {
|
(c: any, i: number) => !used.has(i) && String(c?.title || '').trim() === String(ht || '').trim()
|
||||||
base = unused.splice(hitIdx, 1)[0];
|
);
|
||||||
} else if (unused.length > 0) {
|
if (hitIdx >= 0) {
|
||||||
base = unused.shift();
|
used.add(hitIdx);
|
||||||
} else {
|
return { ...baseCols[hitIdx], title: ht || baseCols[hitIdx].title };
|
||||||
base = srcCols[idx] || mergedCols[idx] || { title: title || `列${idx + 1}`, field: '' };
|
}
|
||||||
}
|
return { ...baseCols[idx], title: ht || baseCols[idx].title };
|
||||||
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.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 = {
|
node.options.__qhmesStyle = {
|
||||||
fontSize: node.options.__qhmesStyle.fontSize || domStyle.fontSize,
|
fontSize: node.options.__qhmesStyle.fontSize || domStyle.fontSize,
|
||||||
@@ -829,6 +863,7 @@ function(t,e,printData){
|
|||||||
type.options = type.options || {};
|
type.options = type.options || {};
|
||||||
type.options.columns = mergedCols.map((c) => ({
|
type.options.columns = mergedCols.map((c) => ({
|
||||||
title: c.title,
|
title: c.title,
|
||||||
|
order: c.order,
|
||||||
field: c.field,
|
field: c.field,
|
||||||
width: c.width,
|
width: c.width,
|
||||||
align: c.align,
|
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 allEnglishLike = keys.every((k) => {
|
||||||
const t = titleMap.get(k);
|
const t = titleMap.get(k);
|
||||||
return !t || t === k;
|
return !t || t === k;
|
||||||
@@ -1027,12 +1100,20 @@ function(t,e,printData){
|
|||||||
price: '单价',
|
price: '单价',
|
||||||
total: '合计',
|
total: '合计',
|
||||||
};
|
};
|
||||||
const cols = keys.map((k) => ({
|
const cols = keys.map((k, idx) => ({
|
||||||
title: titleMap.get(k) || zhFallback[k.toLowerCase()] || k,
|
title: titleMap.get(k) || zhFallback[k.toLowerCase()] || k,
|
||||||
|
order: idx,
|
||||||
field: k,
|
field: k,
|
||||||
width: 60,
|
width: 60,
|
||||||
align: typeof firstRow[k] === 'number' ? 'right' : 'left',
|
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);
|
tableColumnsJson.value = JSON.stringify(cols, null, 2);
|
||||||
createMessage.success('已根据 table[0] 推导列配置');
|
createMessage.success('已根据 table[0] 推导列配置');
|
||||||
}
|
}
|
||||||
@@ -1152,6 +1233,19 @@ function(t,e,printData){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handlePreview() {
|
function handlePreview() {
|
||||||
|
const printExt = {
|
||||||
|
styleHandler: () => {
|
||||||
|
return `
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
-webkit-print-color-adjust: exact !important;
|
||||||
|
print-color-adjust: exact !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>`;
|
||||||
|
},
|
||||||
|
};
|
||||||
if (!hiprintTemplate) {
|
if (!hiprintTemplate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1173,10 +1267,10 @@ function(t,e,printData){
|
|||||||
const previewTemplate = buildPreviewTemplateWithGrouping();
|
const previewTemplate = buildPreviewTemplateWithGrouping();
|
||||||
if (previewTemplate) {
|
if (previewTemplate) {
|
||||||
const previewPrintTpl = new (hiprint as any).PrintTemplate({ template: previewTemplate });
|
const previewPrintTpl = new (hiprint as any).PrintTemplate({ template: previewTemplate });
|
||||||
previewPrintTpl.print(data, {}, { callback: () => {} });
|
previewPrintTpl.print(data, {}, { ...printExt, callback: () => {} });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hiprintTemplate.print(data, {}, { callback: () => {} });
|
hiprintTemplate.print(data, {}, { ...printExt, callback: () => {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ export function createQhmesProvider() {
|
|||||||
height: 60,
|
height: 60,
|
||||||
__qhmesManaged: true,
|
__qhmesManaged: true,
|
||||||
columns: [
|
columns: [
|
||||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||||
{ title: '金额', field: 'amount', width: 45, align: 'right' },
|
{ title: '金额', order: 2, field: 'amount', width: 45, align: 'right' },
|
||||||
],
|
],
|
||||||
groupFields: [],
|
groupFields: [],
|
||||||
formatter: `
|
formatter: `
|
||||||
@@ -86,10 +86,17 @@ function(t,e,printData){
|
|||||||
var list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
|
var list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
|
||||||
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
|
||||||
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
|
||||||
{ title: '物料', field: 'name', width: 90, align: 'left' },
|
{ title: '物料', order: 0, field: 'name', width: 90, align: 'left' },
|
||||||
{ title: '数量', field: 'qty', width: 45, align: 'right' },
|
{ title: '数量', order: 1, field: 'qty', width: 45, align: 'right' },
|
||||||
{ title: '金额', field: 'amount', 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 globalGroups = printData && Array.isArray(printData.__qhmesGroupFields) ? printData.__qhmesGroupFields : [];
|
||||||
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
|
||||||
var style = opts.__qhmesStyle || {};
|
var style = opts.__qhmesStyle || {};
|
||||||
|
|||||||
Reference in New Issue
Block a user