Browse Source

打印功能

chrislee 1 month ago
parent
commit
1cd1dacc4b
2 changed files with 127 additions and 3 deletions
  1. 112 0
      src/utils/print-images.js
  2. 15 3
      src/views/modules/tech/draw-management.vue

+ 112 - 0
src/utils/print-images.js

@@ -0,0 +1,112 @@
+import { downloadUrl } from '@/api/file'
+
+function escapeHtml(str) {
+  return String(str).replace(/[&<>"']/g, s => ({
+    '&': '&amp;',
+    '<': '&lt;',
+    '>': '&gt;',
+    '"': '&quot;',
+    "'": '&#39;'
+  })[s] || s)
+}
+
+export function printImages(attachList = [], vueCtx) {
+  const ctx = vueCtx || {}
+  const msg = (type, text) => {
+    if (ctx.$message && ctx.$message[type]) ctx.$message[type](text)
+  }
+
+  if (!attachList || !attachList.length) {
+    msg('warning', '没有可打印的图片文件')
+    return
+  }
+
+  const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
+  const items = attachList
+    .filter(a => a && a.fileName && imageExts.includes(a.fileName.split('.').pop().toLowerCase()) && a.url)
+    .map(a => ({ name: a.fileName, url: /^(https?:)?\/\//i.test(a.url) ? a.url : (downloadUrl + a.url) }))
+
+  if (!items.length) {
+    msg('warning', '附件中没有图片类型文件')
+    return
+  }
+
+  const win = window.open('', '_blank')
+  if (!win) {
+    msg('error', '浏览器阻止了打印预览窗口,请允许弹窗')
+    return
+  }
+
+  // 替换地址,避免打印页脚显示 about:blank
+  try {
+    win.history.replaceState(null, '', '/print-drawings')
+  } catch (e) { /* 忽略 */ }
+
+  const html = `<!DOCTYPE html>
+  <html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <title>打印图纸</title>
+    <style>
+      html, body { height: 100%; }
+      html, body { margin: 0; padding: 0; background: #fff; color: #333; font: 14px/1.4 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; }
+      .viewport { position: fixed; inset: 0 0 0 0; overflow: auto; background: #fafafa; }
+      .page { box-sizing: border-box; margin: 12px auto; padding: 12px; max-width: 95vw; background: #fff; border: 1px solid #eee; box-shadow: 0 2px 6px rgba(0,0,0,.04); }
+      .title { font-weight: 600; margin: 0 0 8px 0; font-size: 14px; color: #222; text-align: center; }
+      .canvas { position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; background: #fff; }
+      .canvas img { display: block; max-width: 100%; max-height: calc(100vh - 160px); transform-origin: center center; image-rendering: auto; }
+      .hint { color: #999; font-size: 12px; margin-left: auto; }
+      /* 屏幕预览时隐藏内容,直接弹出打印,不让用户看到页面 */
+      @media screen { body { opacity: 0; } }
+      @media print {
+        .viewport { position: static; }
+        .viewport { overflow: visible; }
+        .page { page-break-after: always; border: none; box-shadow: none; margin: 0; padding: 10mm 10mm 0 10mm; max-width: 100%; }
+        .title { margin-bottom: 6mm; font-size: 12pt; }
+        .canvas img { max-height: none; width: 100%; }
+      }
+    </style>
+  </head>
+  <body>
+    <div class="viewport" id="viewport">
+      ${items.map((it, i) => `
+        <div class="page">
+          <h3 class="title">${escapeHtml(it.name)}</h3>
+          <div class="canvas"><img data-idx="${i}" src="${it.url}" /></div>
+        </div>
+      `).join('')}
+    </div>
+    <script>
+      (function(){
+        function allImagesLoaded(imgs){
+          if (!imgs || !imgs.length) return true;
+          return imgs.every(function(img){ return img.complete; });
+        }
+        function triggerPrint(){
+          try { window.focus(); } catch(e){}
+          window.print();
+          setTimeout(function(){ try { window.close(); } catch(e){} }, 100);
+        }
+        var imgs = Array.prototype.slice.call(document.querySelectorAll('img'));
+        if (allImagesLoaded(imgs)) { triggerPrint(); return; }
+        var remain = imgs.length;
+        function done(){ if(--remain <= 0) triggerPrint(); }
+        imgs.forEach(function(img){
+          if (img.complete) { done(); }
+          else {
+            img.addEventListener('load', done);
+            img.addEventListener('error', done);
+          }
+        });
+        // 兜底超时,防止某些图片不触发事件
+        setTimeout(triggerPrint, 3000);
+      })();
+    </script>
+  </body>
+  </html>`
+
+  win.document.open()
+  win.document.write(html)
+  win.document.close()
+}

+ 15 - 3
src/views/modules/tech/draw-management.vue

@@ -40,7 +40,7 @@
         <el-table-column prop="version" header-align="center" align="center" min-width="120"
           :show-tooltip-when-overflow="true" label="版本">
         </el-table-column>
-        <el-table-column header-align="center" align="center" min-width="50" label="图纸文件">
+        <el-table-column header-align="center" align="center" min-width="60" label="图纸文件">
           <template slot-scope="scope">
             <el-button v-if="scope.row.attachList && scope.row.attachList.length" type="text" size="small"
               @click="openImagePreview(scope.row.attachList)">
@@ -81,7 +81,7 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column fixed="right" header-align="center" align="center" width="100" label="操作">
+        <el-table-column fixed="right" header-align="center" align="center" width="160" label="操作">
           <template slot-scope="scope">
             <!-- 查看 -->
             <el-button type="text" size="small" @click="detailHandle(scope.row.drawingId)">查看</el-button>
@@ -89,6 +89,9 @@
             <el-button v-if="isAuth('pro:drawing:update')" type="text" size="small" @click="toggleState(scope.row)">
               {{ Number(scope.row.state) === 1 ? '停用' : '启用' }}
             </el-button>
+            <!-- 打印 -->
+            <el-button v-if="scope.row.attachList && scope.row.attachList.length" type="text" size="small"
+              class="print-btn" @click="printImages(scope.row.attachList)">打印</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -110,6 +113,7 @@
 import AddOrUpdate from './draw-add-or-update'
 import Detail from './draw-detail'
 import { getDrawList } from '@/api/product'
+import { printImages as printImagesUtil } from '@/utils/print-images'
 import ProductComponent from '@/views/modules/common/product-component'
 import PreviewComponent from '@/views/modules/common/preview-component.vue'
 import ImagePreview from '@/views/modules/common/image-preview.vue'
@@ -280,8 +284,16 @@ export default {
         this.$refs.detail.init(id)
       })
     }
+    ,
+    printImages(attachList = []) {
+      printImagesUtil(attachList, this)
+    }
   }
 }
 </script>
 
-<style scoped></style>
+<style scoped>
+.print-btn {
+  color: #11C26D;
+}
+</style>