Просмотр исходного кода

feat: 添加报销事项的 mock 数据生成和注入功能,便于打印预览效果测试

xieyong 2 недель назад
Родитель
Сommit
7aa4ca7f9c
1 измененных файлов с 99 добавлено и 7 удалено
  1. 99 7
      src/views/bpm/collaborative/detail.vue

+ 99 - 7
src/views/bpm/collaborative/detail.vue

@@ -285,7 +285,10 @@ export default {
       previewHtml: '',
       remoteOptionsCache: {},
       tasks: [],
-      deptList: []
+      deptList: [],
+      // 调试开关:默认开启,会用下面 mockReimbursementData() 的超长数据,
+      // 方便看 max-width 400px 换行效果;正式上线前改回 false
+      mockReimburseEnabled: false
     };
   },
   computed: {
@@ -324,6 +327,58 @@ export default {
       });
       return arr;
     },
+    // 生成超长的报销事项+附件 mock 数据,用于在打印预览里看 max-width:400px + 换行效果
+    // 启用方式:URL 加 ?mockReimburse=1,或在控制台执行
+    //   sessionStorage.setItem('mockReimburse','1') 后刷新
+    buildMockReimbursementData() {
+      const longRemark1 = '2024年第三季度跨省差旅出差至北京参加为期五天的高端制造业数字化转型峰会暨行业研讨会,期间拜访了清华大学工业工程系的多位教授,并就供应商准入资质使用合规问题与法务团队进行了深入的沟通和交流,最终形成初步合作意向。';
+      const longRemark2 = '市场部组织的客户答谢晚宴及产品发布会场地布置、餐饮服务、舞台灯光音响设备租赁、现场礼仪人员差旅住宿等费用明细汇总(含税)';
+      const longFileName1 = '2024-Q3-北京-差旅-报销单据-含发票明细-清华大学会谈纪要-供应商准入合规评审会议纪要-法务沟通记录.pdf';
+      const longFileName2 = '市场部-客户答谢晚宴-场地布置-餐饮-灯光-音响-礼仪-住宿-费用明细汇总表(含增值税专用发票扫描件)-公司抬头版.xlsx';
+      const longFileName3 = '出差审批单-差旅费用报销单-行程单-登机牌-酒店水单-出租车发票-餐费发票-打车小票-打包扫描合订版.pdf';
+      return [
+        {
+          remark: longRemark1,
+          price: 8866.5,
+          file: [
+            { name: longFileName1, url: 'https://example.com/file1.pdf' },
+            { name: longFileName3, url: 'https://example.com/file3.pdf' }
+          ]
+        },
+        {
+          remark: longRemark2,
+          price: 32680,
+          file: [
+            { name: longFileName2, url: 'https://example.com/file2.xlsx' },
+            { name: '场地租赁合同正本(盖章版).pdf', url: 'https://example.com/file4.pdf' },
+            { name: '增值税专用发票_2024-09-15_001.jpg', url: 'https://example.com/file5.jpg' }
+          ]
+        }
+      ];
+    },
+    // 单独探测一次 URL/sessionStorage,覆盖 data 里的默认值(方便不改动代码就能切换)
+    detectMockReimburseFromEnv() {
+      try {
+        if (sessionStorage.getItem('mockReimburse') === '0') return false;
+        if (sessionStorage.getItem('mockReimburse') === '1') return true;
+        const params = new URLSearchParams(window.location.search || '');
+        const q = params.get('mockReimburse');
+        if (q === '0') return false;
+        if (q === '1') return true;
+      } catch (e) {
+        // 忽略探测异常,沿用 data 默认值
+      }
+      return this.mockReimburseEnabled;
+    },
+    // 把 mock 数据写入当前 form 的 blank_adopzrdd;仅在 mock 开关启用时生效
+    applyMockReimbursement() {
+      this.mockReimburseEnabled = this.detectMockReimburseFromEnv();
+      if (!this.mockReimburseEnabled) return;
+      if (!this.form.valueJson || typeof this.form.valueJson !== 'object') {
+        this.form.valueJson = {};
+      }
+      this.form.valueJson.blank_adopzrdd = this.buildMockReimbursementData();
+    },
     open(row) {
       this.form = _.cloneDeep(row);
       // 兼容后端附件字段名为 file 或 files
@@ -335,6 +390,8 @@ export default {
           }
         });
       }
+      // mock 数据注入(仅在 mock 开关启用时)
+      this.applyMockReimbursement();
       if (this.form.id) {
         storageApi.getInfoBySourceBizNo(this.form.id).then((res) => {
           this.outgoingData = res;
@@ -505,6 +562,8 @@ export default {
             }
           });
         }
+        // mock 数据注入(仅在 mock 开关启用时),便于测试 max-width:400px 换行效果
+        this.applyMockReimbursement();
         if (this.form.id) {
           storageApi.getInfoBySourceBizNo(this.form.id).then((res) => {
             this.outgoingData = res;
@@ -689,6 +748,23 @@ export default {
 
       const rows = [];
       const isBlankField = (item) => item.type === 'blank' || (item.model && String(item.model).indexOf('blank_') === 0);
+      // 渲染附件数组为多个可点击链接,长名字自动换行
+      const renderAttachmentCell = (files) => {
+        if (!Array.isArray(files) || files.length === 0) return '';
+        const items = files.map((f, idx) => {
+          if (f === null || f === undefined) return '';
+          if (typeof f === 'string' || typeof f === 'number') {
+            return '<span class="pf-attachment-item">' + this.escapeHtml(String(f)) + '</span>';
+          }
+          const name = f.name || f.fileName || f.originalName || f.label || ('附件' + (idx + 1));
+          const url = f.url || f.path || f.downloadUrl || '';
+          if (url) {
+            return '<a class="pf-attachment-item" href="' + this.escapeHtml(url) + '" target="_blank">' + this.escapeHtml(name) + '</a>';
+          }
+          return '<span class="pf-attachment-item">' + this.escapeHtml(name) + '</span>';
+        }).filter(Boolean);
+        return '<div class="pf-attachment">' + items.join('') + '</div>';
+      };
       const renderBlankTable = (item, rawRows = []) => {
         const mapKey = item.model || item.key || '';
         let cols = [];
@@ -716,8 +792,14 @@ export default {
         const trs = rawRows.map(r => {
           const tds = cols.map(c => {
             const v = (r && r[c] !== undefined && r[c] !== null) ? r[c] : '';
-            if (typeof v === 'object') return '<td>' + this.escapeHtml(JSON.stringify(v)) + '</td>';
-            return '<td>' + this.escapeHtml(String(v)) + '</td>';
+            const colClass = 'col-' + this.escapeHtml(c);
+            if (Array.isArray(v)) {
+              return '<td class="' + colClass + '">' + renderAttachmentCell(v) + '</td>';
+            }
+            if (typeof v === 'object' && v !== null) {
+              return '<td class="' + colClass + '">' + this.escapeHtml(JSON.stringify(v)) + '</td>';
+            }
+            return '<td class="' + colClass + '">' + this.escapeHtml(String(v)) + '</td>';
           }).join('');
           return '<tr>' + tds + '</tr>';
         }).join('');
@@ -831,8 +913,14 @@ export default {
                 const trs = raw.map(r => {
                   const tds = cols.map(c => {
                     const v = (r && r[c] !== undefined && r[c] !== null) ? r[c] : '';
-                    if (typeof v === 'object') return '<td>' + this.escapeHtml(JSON.stringify(v)) + '</td>';
-                    return '<td>' + this.escapeHtml(String(v)) + '</td>';
+                    const colClass = 'col-' + this.escapeHtml(c);
+                    if (Array.isArray(v)) {
+                      return '<td class="' + colClass + '">' + renderAttachmentCell(v) + '</td>';
+                    }
+                    if (typeof v === 'object' && v !== null) {
+                      return '<td class="' + colClass + '">' + this.escapeHtml(JSON.stringify(v)) + '</td>';
+                    }
+                    return '<td class="' + colClass + '">' + this.escapeHtml(String(v)) + '</td>';
                   }).join('');
                   return '<tr>' + tds + '</tr>';
                 }).join('');
@@ -885,8 +973,12 @@ export default {
         + '.pf-blank-area{margin-top:12px;margin-bottom:12px;overflow:auto} '
         + '.pf-blank-title{font-weight:600;margin-bottom:6px;color:#333;font-size:14px;word-break:break-word;white-space:normal} '
         + 'a{color:#1890ff} '
-        + '.pf-table{width:100%;border-collapse:collapse;margin-top:6px} '
-        + '.pf-table th,.pf-table td{border:1px solid #e8e8e8;padding:6px;text-align:left} '
+        + '.pf-table{width:100%;border-collapse:collapse;margin-top:6px;table-layout:fixed} '
+        + '.pf-table th,.pf-table td{border:1px solid #e8e8e8;padding:6px;text-align:left;vertical-align:top} '
+        + '.pf-table td.col-file{max-width:400px;word-wrap:break-word;word-break:break-all;white-space:pre-wrap;overflow-wrap:break-word} '
+        + '.pf-table td.col-remark{word-wrap:break-word;word-break:break-all;white-space:pre-wrap;overflow-wrap:break-word} '
+        + '.pf-table .pf-attachment{display:flex;flex-direction:column;gap:4px} '
+        + '.pf-table .pf-attachment a{color:#1890ff;text-decoration:none;word-break:break-all;display:inline-block} '
         + '.pf-tasks-title{font-size:16px;font-weight:600;margin-top:24px;margin-bottom:8px} '
         + '.pf-footer{margin-top:40px;padding-top:10px;border-top:1px solid #ccc;font-size:13px;color:#333;display:flex;justify-content:flex-start;gap:32px;font-family:"SimSun","宋体",serif} '
         + '.pf-footer .pf-footer-item{display:flex;align-items:center;gap:6px} '