Переглянути джерело

合同主体和附件单独分页打印

chenjun 4 тижнів тому
батько
коміт
7a09c62f55

+ 4 - 0
src/views/elderly/contracts/ContractManageForm.vue

@@ -984,6 +984,10 @@ const renderAttachmentContent = (key: string) => {
     margin: 0 !important;
     padding: 0 !important;
     box-sizing: border-box !important;
+    /* 每个附件强制另起一页:合同主体与附件、附件与附件之间都分页,
+       避免附件与前序内容挤在同一页 */
+    page-break-before: always;
+    break-before: page;
   }
   .print-container .print-attachment .mgb {
     width: 210mm !important;

+ 90 - 1
src/views/elderly/contracts/components/DocumentAttachmentBody.vue

@@ -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>

+ 6 - 3
src/views/elderly/elder/family-appointment/time-setting/index.vue

@@ -35,7 +35,7 @@
                 </el-radio-group>
               </el-form-item>
 
-              <el-form-item label="开始预约日" prop="startDay">
+              <el-form-item v-show="false" label="开始预约日" prop="startDay">
                 <el-select
                   v-model="formData.startDay"
                   placeholder="请选择"
@@ -60,7 +60,7 @@
                   <el-option label="未来15天" :value="15" />
                 </el-select>
               </el-form-item>
-              <el-form-item label="提前预约天数" prop="advanceDays">
+              <el-form-item label="提前预约" prop="advanceDays">
                 <el-input-number
                   v-model="formData.advanceDays"
                   :min="1"
@@ -68,8 +68,11 @@
                   style="width: 100%"
                   :disabled="isDisabled"
                 >
+                  <template #prefix>
+                    <span>可提前</span>
+                  </template>
                   <template #suffix>
-                    <span>天</span>
+                    <span>天预约</span>
                   </template>
                 </el-input-number>
               </el-form-item>