feat(web-admin): TinyMCE 表格类 Excel 调宽调高,并优化出入库/分装/送货单排版
支持合同、报价与入库/分装/送(提)货单在富文本中拖拽调整列宽行高;单据侧加宽物料编号与名称、收窄规格单位、放大 logo、加宽条款区,并弱化调整手柄绿色阴影。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2380b01a4f
commit
3d5866aa14
13 files changed
+31815
-30418
No files matched your search
@@ -782,6 +782,9 @@
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<p v-if="!isPreviewMode" class="generate-contract-edit-hint">
|
||||
编辑模式:鼠标移入表格后,列/行边界会出现绿色调整条,按住拖动即可调列宽或行高;工具栏可合并拆分、插删行列、对齐与换行。调整后请点「保存模板」。
|
||||
</p>
|
||||
<TinyMceEditor
|
||||
v-model="contractContent"
|
||||
:height="600"
|
||||
@@ -886,6 +889,7 @@ import ImagePreview from '@/components/ImagePreview/index.vue';
|
||||
import TinyMceEditor from '@/components/TinyMceEditor/index.vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import { fitRichTextTablesToMaxWidth } from '@/utils/tinymce-table-excel-resize';
|
||||
import {
|
||||
downloadCrmAttachment,
|
||||
getCrmAttachmentCategoryLabel,
|
||||
@@ -2698,12 +2702,14 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
htmlContent = replacePlaceholders(htmlContent, res.data, quoteFreight);
|
||||
}
|
||||
|
||||
// 1. 创建临时容器
|
||||
// 1. 创建临时容器(border-box:794 含 padding)
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.style.position = 'absolute';
|
||||
tempDiv.style.left = '-9999px';
|
||||
tempDiv.style.top = '0';
|
||||
tempDiv.style.width = '794px'; // A4纸宽度
|
||||
tempDiv.style.boxSizing = 'border-box';
|
||||
tempDiv.style.overflow = 'hidden';
|
||||
tempDiv.style.background = 'white';
|
||||
tempDiv.style.padding = '20px';
|
||||
|
||||
@@ -2711,6 +2717,8 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
const watermarkDiv = document.createElement('div');
|
||||
watermarkDiv.style.position = 'relative';
|
||||
watermarkDiv.style.width = '100%';
|
||||
watermarkDiv.style.maxWidth = '100%';
|
||||
watermarkDiv.style.boxSizing = 'border-box';
|
||||
|
||||
// 根据供方编码决定是否添加水印背景层
|
||||
if (isSXSupplier) {
|
||||
@@ -2734,7 +2742,7 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
|
||||
// 添加内容层
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1;';
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1; width: 100%; max-width: 100%; box-sizing: border-box; overflow: hidden;';
|
||||
|
||||
// 如果不是SX供方编码,在设置HTML之前移除logo
|
||||
let contentHtml = htmlContent;
|
||||
@@ -2753,6 +2761,12 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
}
|
||||
|
||||
contentDiv.innerHTML = contentHtml;
|
||||
const fitStyle = document.createElement('style');
|
||||
fitStyle.textContent = `
|
||||
table { max-width: 100% !important; width: 100% !important; table-layout: fixed !important; box-sizing: border-box !important; }
|
||||
table td, table th { box-sizing: border-box !important; min-width: 0 !important; word-break: break-word; overflow-wrap: anywhere; }
|
||||
`;
|
||||
contentDiv.prepend(fitStyle);
|
||||
|
||||
watermarkDiv.appendChild(contentDiv);
|
||||
tempDiv.appendChild(watermarkDiv);
|
||||
@@ -2772,6 +2786,10 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
|
||||
// 编辑器拖拽后表宽常大于 A4 内容区;截图前等比缩进,避免 PDF 右侧裁切
|
||||
const pdfContentMaxWidth = Math.max(1, tempDiv.clientWidth - 40);
|
||||
fitRichTextTablesToMaxWidth(contentDiv, pdfContentMaxWidth);
|
||||
|
||||
// 如果不是SX供方编码,再次检查并隐藏可能遗漏的logo
|
||||
if (!isSXSupplier) {
|
||||
const logoImages = contentDiv.querySelectorAll('img');
|
||||
@@ -2794,7 +2812,9 @@ const handleGeneratePdfFromDialog = async () => {
|
||||
scale: getContractPdfCanvasScale(),
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
backgroundColor: '#ffffff'
|
||||
backgroundColor: '#ffffff',
|
||||
width: 794,
|
||||
windowWidth: 794
|
||||
});
|
||||
|
||||
// 3. 移除临时容器
|
||||
@@ -2950,6 +2970,17 @@ onMounted(async () => {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.generate-contract-edit-hint {
|
||||
margin: 0 0 12px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: #047857;
|
||||
background: #ecfdf5;
|
||||
border: 1px solid #a7f3d0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.parties-section {
|
||||
margin: 8px 0 20px;
|
||||
}
|
||||
|
||||
@@ -984,6 +984,9 @@
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<p v-if="!isPreviewMode" class="generate-contract-edit-hint">
|
||||
编辑模式:鼠标移入表格后,列/行边界会出现绿色调整条,按住拖动即可调列宽或行高;工具栏可合并拆分、插删行列、对齐与换行。调整后请点「保存模板」。
|
||||
</p>
|
||||
<TinyMceEditor v-model="contractContent" :height="600" :disabled="isPreviewMode" :toolbar="!isPreviewMode" :contractNo="contract?.contractNo" />
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -1029,6 +1032,7 @@ import { useContractPerm } from '@/hooks/business/useContractPerm';
|
||||
import { canDeleteByCreator } from '@/utils/creator-delete';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { jsPDF } from 'jspdf';
|
||||
import { fitRichTextTablesToMaxWidth } from '@/utils/tinymce-table-excel-resize';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -2541,6 +2545,8 @@ const handleGeneratePdfFile = async () => {
|
||||
tempDiv.style.left = '-9999px';
|
||||
tempDiv.style.top = '0';
|
||||
tempDiv.style.width = '794px'; // A4纸宽度的像素值(210mm * 96dpi / 25.4)
|
||||
tempDiv.style.boxSizing = 'border-box';
|
||||
tempDiv.style.overflow = 'hidden';
|
||||
tempDiv.style.background = 'white';
|
||||
tempDiv.style.padding = '20px';
|
||||
|
||||
@@ -2548,6 +2554,8 @@ const handleGeneratePdfFile = async () => {
|
||||
const watermarkDiv = document.createElement('div');
|
||||
watermarkDiv.style.position = 'relative';
|
||||
watermarkDiv.style.width = '100%';
|
||||
watermarkDiv.style.maxWidth = '100%';
|
||||
watermarkDiv.style.boxSizing = 'border-box';
|
||||
|
||||
// 添加水印背景层
|
||||
const watermarkBg = document.createElement('div');
|
||||
@@ -2568,8 +2576,14 @@ const handleGeneratePdfFile = async () => {
|
||||
|
||||
// 添加内容层
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1;';
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1; width: 100%; max-width: 100%; box-sizing: border-box; overflow: hidden;';
|
||||
contentDiv.innerHTML = contractContent.value;
|
||||
const fitStyle = document.createElement('style');
|
||||
fitStyle.textContent = `
|
||||
table { max-width: 100% !important; width: 100% !important; table-layout: fixed !important; box-sizing: border-box !important; }
|
||||
table td, table th { box-sizing: border-box !important; min-width: 0 !important; word-break: break-word; overflow-wrap: anywhere; }
|
||||
`;
|
||||
contentDiv.prepend(fitStyle);
|
||||
|
||||
watermarkDiv.appendChild(watermarkBg);
|
||||
watermarkDiv.appendChild(contentDiv);
|
||||
@@ -2590,12 +2604,18 @@ const handleGeneratePdfFile = async () => {
|
||||
});
|
||||
await Promise.all(imagePromises);
|
||||
|
||||
// 编辑器拖拽后表宽常大于 A4 内容区;截图前等比缩进,避免 PDF 右侧裁切
|
||||
const pdfContentMaxWidth = Math.max(1, tempDiv.clientWidth - 40);
|
||||
fitRichTextTablesToMaxWidth(contentDiv, pdfContentMaxWidth);
|
||||
|
||||
// 使用html2canvas将HTML转换为canvas
|
||||
const canvas = await html2canvas(tempDiv, {
|
||||
scale: 2, // 提高清晰度
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
backgroundColor: '#ffffff'
|
||||
backgroundColor: '#ffffff',
|
||||
width: 794,
|
||||
windowWidth: 794
|
||||
});
|
||||
|
||||
// 移除临时div
|
||||
@@ -3014,4 +3034,15 @@ const handleGeneratePdfFile = async () => {
|
||||
.tier-letter-download-link {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.generate-contract-edit-hint {
|
||||
margin: 0 0 12px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: #047857;
|
||||
background: #ecfdf5;
|
||||
border: 1px solid #a7f3d0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -769,6 +769,7 @@ import yzImg from '@/assets/images/yz.png';
|
||||
import ydImg from '@/assets/images/yd.png';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import { fitRichTextTablesToMaxWidth } from '@/utils/tinymce-table-excel-resize';
|
||||
|
||||
import { useQuotePerm } from '@/hooks/business/useQuotePerm'
|
||||
import { filterDeletableByCreator, canDeleteByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
|
||||
@@ -2162,7 +2163,10 @@ const handlePrint = () => {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
table { max-width: 100% !important; width: 100% !important; table-layout: fixed !important; box-sizing: border-box !important; }
|
||||
table td, table th { box-sizing: border-box !important; min-width: 0 !important; word-break: break-word; overflow-wrap: anywhere; }
|
||||
${backgroundStyle}
|
||||
@media print {
|
||||
body {
|
||||
@@ -2179,8 +2183,15 @@ const handlePrint = () => {
|
||||
`);
|
||||
printWindow.document.close();
|
||||
|
||||
// 等待图片和内容加载完成后打印
|
||||
// 等待图片和内容加载完成后打印;先把超宽表缩进打印页
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const body = printWindow.document.body;
|
||||
const maxW = Math.max(1, (body?.clientWidth || 794) - 40);
|
||||
fitRichTextTablesToMaxWidth(body, maxW);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
printWindow.print();
|
||||
}, 1000);
|
||||
}
|
||||
@@ -2259,12 +2270,14 @@ const handleGeneratePdf = async () => {
|
||||
// 根据供应商编码决定是否显示背景图片
|
||||
const showBackground = currentSupplierCode.value === 'SX';
|
||||
|
||||
// 创建临时容器
|
||||
// 创建临时容器(border-box:794 含 padding,与 A4 截图像素一致)
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.style.position = 'absolute';
|
||||
tempDiv.style.left = '-9999px';
|
||||
tempDiv.style.top = '0';
|
||||
tempDiv.style.width = '794px'; // A4纸宽度
|
||||
tempDiv.style.boxSizing = 'border-box';
|
||||
tempDiv.style.overflow = 'hidden';
|
||||
tempDiv.style.background = 'white';
|
||||
tempDiv.style.padding = '20px';
|
||||
|
||||
@@ -2272,6 +2285,8 @@ const handleGeneratePdf = async () => {
|
||||
const containerDiv = document.createElement('div');
|
||||
containerDiv.style.position = 'relative';
|
||||
containerDiv.style.width = '100%';
|
||||
containerDiv.style.maxWidth = '100%';
|
||||
containerDiv.style.boxSizing = 'border-box';
|
||||
|
||||
// 如果是SX编码,添加水印背景层
|
||||
if (showBackground) {
|
||||
@@ -2295,19 +2310,36 @@ const handleGeneratePdf = async () => {
|
||||
|
||||
// 添加内容层
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1;';
|
||||
contentDiv.style.cssText = 'position: relative; z-index: 1; width: 100%; max-width: 100%; box-sizing: border-box; overflow: hidden;';
|
||||
contentDiv.innerHTML = quoteContent.value;
|
||||
|
||||
// PDF 环境无 TinyMCE content_style,补齐表格钳制,防止声明像素宽撑破
|
||||
const fitStyle = document.createElement('style');
|
||||
fitStyle.textContent = `
|
||||
table { max-width: 100% !important; width: 100% !important; table-layout: fixed !important; box-sizing: border-box !important; }
|
||||
table td, table th { box-sizing: border-box !important; min-width: 0 !important; word-break: break-word; overflow-wrap: anywhere; }
|
||||
`;
|
||||
contentDiv.prepend(fitStyle);
|
||||
|
||||
containerDiv.appendChild(contentDiv);
|
||||
tempDiv.appendChild(containerDiv);
|
||||
document.body.appendChild(tempDiv);
|
||||
|
||||
// 按声明列宽重写为百分比(编辑器视觉被 max-width 钳制时 getBoundingClientRect 会误判为已适配)
|
||||
const pdfContentMaxWidth = Math.max(1, tempDiv.clientWidth - 40);
|
||||
fitRichTextTablesToMaxWidth(contentDiv, pdfContentMaxWidth);
|
||||
await new Promise<void>((resolve) => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
|
||||
// 使用html2canvas转换为canvas
|
||||
const canvas = await html2canvas(tempDiv, {
|
||||
scale: 2, // 提高清晰度
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
backgroundColor: '#ffffff'
|
||||
backgroundColor: '#ffffff',
|
||||
width: 794,
|
||||
windowWidth: 794
|
||||
});
|
||||
|
||||
// 移除临时容器
|
||||
|
||||
Reference in new issue
Block a user