|
|
@@ -30,14 +30,49 @@ const form = reactive<{ images: string[] }>({
|
|
|
images: []
|
|
|
})
|
|
|
|
|
|
+// 打印模式下背景图 div 需要的每张图片原始尺寸
|
|
|
+const imageSizes = ref<{ width: number; height: number }[]>([])
|
|
|
+
|
|
|
+const calcImageSize = (url: string): Promise<{ width: number; height: number }> => {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ const img = new Image()
|
|
|
+ img.onload = () => {
|
|
|
+ resolve({ width: img.naturalWidth, height: img.naturalHeight })
|
|
|
+ }
|
|
|
+ img.onerror = () => {
|
|
|
+ resolve({ width: 0, height: 0 })
|
|
|
+ }
|
|
|
+ img.src = url
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const updateImageSizes = async (images: string[]) => {
|
|
|
+ if (!images || images.length === 0) {
|
|
|
+ imageSizes.value = []
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const sizes = await Promise.all(images.map(calcImageSize))
|
|
|
+ imageSizes.value = sizes
|
|
|
+}
|
|
|
+
|
|
|
+// 计算打印时背景图 div 应有的高度(容器宽度按 210mm,background-size: contain)
|
|
|
+const getPrintImageHeight = (idx: number): string => {
|
|
|
+ const size = imageSizes.value[idx]
|
|
|
+ if (!size || !size.width || !size.height) return 'auto'
|
|
|
+ const heightMm = (size.height / size.width) * 210
|
|
|
+ return `${heightMm}mm`
|
|
|
+}
|
|
|
+
|
|
|
watch(
|
|
|
() => props.modelValue,
|
|
|
(val) => {
|
|
|
if (!val) return
|
|
|
if (Array.isArray(val.images)) {
|
|
|
form.images = [...val.images]
|
|
|
+ updateImageSizes(form.images)
|
|
|
} else {
|
|
|
form.images = []
|
|
|
+ imageSizes.value = []
|
|
|
}
|
|
|
},
|
|
|
{ immediate: true, deep: true }
|
|
|
@@ -139,11 +174,22 @@ defineExpose({ form })
|
|
|
<!-- 文本/打印模式:仅展示图片 -->
|
|
|
<div v-else class="preview-section">
|
|
|
<div v-if="form.images && form.images.length > 0" class="image-preview-list">
|
|
|
+ <!-- 屏幕预览仍用 img -->
|
|
|
<img
|
|
|
v-for="(img, idx) in form.images"
|
|
|
:key="idx"
|
|
|
:src="img"
|
|
|
- class="preview-img"
|
|
|
+ class="preview-img screen-only"
|
|
|
+ />
|
|
|
+ <!-- 打印时用背景图 div,可被浏览器正常分页截断,避免整图被推到下一页产生空白页 -->
|
|
|
+ <div
|
|
|
+ v-for="(img, idx) in form.images"
|
|
|
+ :key="`print-${idx}`"
|
|
|
+ class="print-img"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${img})`,
|
|
|
+ height: getPrintImageHeight(idx)
|
|
|
+ }"
|
|
|
/>
|
|
|
</div>
|
|
|
<div v-else class="empty-box">
|
|
|
@@ -279,6 +325,15 @@ defineExpose({ form })
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
+ /* 打印专用背景图 div,默认屏幕隐藏 */
|
|
|
+ .print-img {
|
|
|
+ display: none;
|
|
|
+ width: 100%;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-position: top center;
|
|
|
+ background-size: contain;
|
|
|
+ }
|
|
|
+
|
|
|
.empty-box {
|
|
|
width: 100%;
|
|
|
min-height: 400px;
|
|
|
@@ -288,4 +343,38 @@ defineExpose({ form })
|
|
|
color: #999;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* 打印时:用背景图 div 替代 img,避免替换元素无法被分页截断导致的空白页问题 */
|
|
|
+@media print {
|
|
|
+ .preview-section .image-preview-list {
|
|
|
+ display: block !important;
|
|
|
+ gap: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 屏幕预览的 img 在打印时隐藏 */
|
|
|
+ .preview-section .preview-img.screen-only {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 打印专用 div:把图片作为背景图铺满,浏览器可以正常在 div 中间分页截断,
|
|
|
+ 从而不会出现整图被推到下一页、标题单独占一页的空白页 */
|
|
|
+ .preview-section .print-img {
|
|
|
+ display: block !important;
|
|
|
+ width: 100%;
|
|
|
+ /* 高度由 JS 动态计算,这里先给最小高度占位,避免背景图为空时 div 高度为 0 */
|
|
|
+ min-height: 50mm;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-position: top center;
|
|
|
+ background-size: contain;
|
|
|
+ page-break-inside: auto;
|
|
|
+ break-inside: auto;
|
|
|
+ break-before: auto;
|
|
|
+ break-after: auto;
|
|
|
+ /* 关键:强制浏览器在打印时渲染背景图,否则 Chrome/Edge 打印预览默认不显示背景图形,
|
|
|
+ 会导致图片整体消失(即使 div 高度已正确计算) */
|
|
|
+ -webkit-print-color-adjust: exact !important;
|
|
|
+ print-color-adjust: exact !important;
|
|
|
+ color-adjust: exact !important;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|