|
@@ -1,4 +1,5 @@
|
|
|
<template>
|
|
<template>
|
|
|
|
|
+ <div>
|
|
|
<ele-modal
|
|
<ele-modal
|
|
|
:visible="formDetailDialogFlag"
|
|
:visible="formDetailDialogFlag"
|
|
|
:width="modelWidth"
|
|
:width="modelWidth"
|
|
@@ -26,7 +27,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
<div style="display: flex; height: 100%; justify-content: space-between">
|
|
<div style="display: flex; height: 100%; justify-content: space-between">
|
|
|
- <div class="form-box" :style="fmStyle">
|
|
|
|
|
|
|
+ <div id="printSection" class="form-box" :style="fmStyle">
|
|
|
<fm-generate-form
|
|
<fm-generate-form
|
|
|
v-if="Object.keys(form?.formJson || {}).length !== 0"
|
|
v-if="Object.keys(form?.formJson || {}).length !== 0"
|
|
|
:data="jsonData"
|
|
:data="jsonData"
|
|
@@ -206,9 +207,21 @@
|
|
|
:isInterior="true"
|
|
:isInterior="true"
|
|
|
></outgoingDetails>
|
|
></outgoingDetails>
|
|
|
<div slot="footer" class="footer">
|
|
<div slot="footer" class="footer">
|
|
|
|
|
+ <el-button type="primary" @click="print">打印</el-button>
|
|
|
|
|
+ <!-- <el-button @click="openPreview">预览</el-button> -->
|
|
|
<el-button @click="cancel">返回</el-button>
|
|
<el-button @click="cancel">返回</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</ele-modal>
|
|
</ele-modal>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 打印 HTML 预览弹窗 -->
|
|
|
|
|
+ <el-dialog :visible.sync="previewDialog" width="80%" top="20px" title="打印预览">
|
|
|
|
|
+ <div style="max-height:70vh; overflow:auto; background:#fff; padding:16px;" v-html="previewHtml"></div>
|
|
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
|
|
+ <el-button @click="previewDialog = false">关闭</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="printPreview">打印预览</el-button>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
<script>
|
|
<script>
|
|
|
import dictMixins from '@/mixins/dictMixins';
|
|
import dictMixins from '@/mixins/dictMixins';
|
|
@@ -227,6 +240,8 @@ import Detail from '@/views/bpm/processInstance/detailNew.vue';
|
|
|
import applicationComponent from '@/BIZComponents/processSubmitDialog/components/applicationComponent.vue';
|
|
import applicationComponent from '@/BIZComponents/processSubmitDialog/components/applicationComponent.vue';
|
|
|
import purchaseRequisitionComponent from '@/BIZComponents/processSubmitDialog/components/purchaseRequisitionComponent.vue';
|
|
import purchaseRequisitionComponent from '@/BIZComponents/processSubmitDialog/components/purchaseRequisitionComponent.vue';
|
|
|
import businessTripComponent from '@/BIZComponents/processSubmitDialog/components/businessTripComponent.vue';
|
|
import businessTripComponent from '@/BIZComponents/processSubmitDialog/components/businessTripComponent.vue';
|
|
|
|
|
+import { getGroupUserTree, getProduceTreeByCode } from '@/api/main';
|
|
|
|
|
+import { listOrganizations, getUserPage } from '@/api/system/organization';
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'formDetailDialog',
|
|
name: 'formDetailDialog',
|
|
@@ -259,7 +274,10 @@ export default {
|
|
|
form: {},
|
|
form: {},
|
|
|
jsonData: {},
|
|
jsonData: {},
|
|
|
outgoingData: {},
|
|
outgoingData: {},
|
|
|
- isRight: false
|
|
|
|
|
|
|
+ isRight: false,
|
|
|
|
|
+ previewDialog: false,
|
|
|
|
|
+ previewHtml: '',
|
|
|
|
|
+ remoteOptionsCache: {}
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -303,6 +321,364 @@ export default {
|
|
|
};
|
|
};
|
|
|
// item.url = item.url && item.url.replace('/api', this.APIUrl);
|
|
// item.url = item.url && item.url.replace('/api', this.APIUrl);
|
|
|
});
|
|
});
|
|
|
|
|
+ // 预加载远程数据(例如 getGroupUserTree)以便打印时将 id 转为名称
|
|
|
|
|
+ this.ensureRemoteData(this.jsonData).catch((e) => {
|
|
|
|
|
+ console.warn('预加载远程数据失败', e);
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async ensureRemoteData(makingJson) {
|
|
|
|
|
+ if (!makingJson || !makingJson.list) return;
|
|
|
|
|
+ const tasks = [];
|
|
|
|
|
+ makingJson.list.forEach((item) => {
|
|
|
|
|
+ const remoteKey = item.options && item.options.remoteDataSource;
|
|
|
|
|
+ if (remoteKey && !this.remoteOptionsCache[remoteKey]) {
|
|
|
|
|
+ // 目前只实现 getGroupUserTree 的预加载
|
|
|
|
|
+ if (remoteKey === 'getGroupUserTree') {
|
|
|
|
|
+ tasks.push(
|
|
|
|
|
+ getGroupUserTree()
|
|
|
|
|
+ .then((res) => {
|
|
|
|
|
+ // res 可能是树形,扁平化为 id->node 映射
|
|
|
|
|
+ const map = {};
|
|
|
|
|
+ const walk = (nodes) => {
|
|
|
|
|
+ if (!nodes) return;
|
|
|
|
|
+ nodes.forEach((n) => {
|
|
|
|
|
+ if (n.id !== undefined) map[n.id] = n;
|
|
|
|
|
+ if (n.children && n.children.length) walk(n.children);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ walk(res || []);
|
|
|
|
|
+ this.remoteOptionsCache[remoteKey] = map;
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((e) => {
|
|
|
|
|
+ console.warn('加载远程数据失败', remoteKey, e);
|
|
|
|
|
+ })
|
|
|
|
|
+ );
|
|
|
|
|
+ } else if (remoteKey === 'getUserPage') {
|
|
|
|
|
+ tasks.push(
|
|
|
|
|
+ getUserPage({ pageNum: 1, pageSize: -1 })
|
|
|
|
|
+ .then((res) => {
|
|
|
|
|
+ const map = {};
|
|
|
|
|
+ const list = res?.list || [];
|
|
|
|
|
+ list.forEach((user) => {
|
|
|
|
|
+ if (user && user.id !== undefined) {
|
|
|
|
|
+ map[user.id] = user;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ this.remoteOptionsCache[remoteKey] = map;
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((e) => {
|
|
|
|
|
+ console.warn('加载远程数据失败', remoteKey, e);
|
|
|
|
|
+ })
|
|
|
|
|
+ );
|
|
|
|
|
+ } else if (remoteKey === 'getDeptTree') {
|
|
|
|
|
+ tasks.push(
|
|
|
|
|
+ listOrganizations()
|
|
|
|
|
+ .then((res) => {
|
|
|
|
|
+ const map = {};
|
|
|
|
|
+ const list = Array.isArray(res) ? res : [];
|
|
|
|
|
+ list.forEach((dept) => {
|
|
|
|
|
+ if (dept && dept.id !== undefined) {
|
|
|
|
|
+ map[dept.id] = dept;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ this.remoteOptionsCache[remoteKey] = map;
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((e) => {
|
|
|
|
|
+ console.warn('加载远程数据失败', remoteKey, e);
|
|
|
|
|
+ })
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ await Promise.all(tasks);
|
|
|
|
|
+ },
|
|
|
|
|
+ print1() {
|
|
|
|
|
+ const printSection = document.getElementById('printSection');
|
|
|
|
|
+ // 创建打印任务
|
|
|
|
|
+ const printWindow = window.open('', '_blank');
|
|
|
|
|
+ printWindow.document.open();
|
|
|
|
|
+ printWindow.document.write('<html><head><title>打印预览</title>');
|
|
|
|
|
+ printWindow.document.write(
|
|
|
|
|
+ '<link rel="stylesheet" href="your-stylesheet-url.css" type="text/css" />'
|
|
|
|
|
+ );
|
|
|
|
|
+ printWindow.document.write('</head><body>');
|
|
|
|
|
+ printWindow.document.write(printSection.innerHTML);
|
|
|
|
|
+ printWindow.document.write('</body></html>');
|
|
|
|
|
+ printWindow.document.close();
|
|
|
|
|
+ printWindow.onload = function () {
|
|
|
|
|
+ printWindow.print();
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ async print() {
|
|
|
|
|
+ // 将 formJson.makingJson 与 valueJson 合并生成 HTML,再打开新窗口打印
|
|
|
|
|
+ try {
|
|
|
|
|
+ const making = this.jsonData || (this.form.formJson && JSON.parse(this.form.formJson.makingJson));
|
|
|
|
|
+ // 确保远程数据加载完成(用于 id->name 映射)
|
|
|
|
|
+ await this.ensureRemoteData(making);
|
|
|
|
|
+ const values = this.form.valueJson || {};
|
|
|
|
|
+ const html = this.generatePrintHtml(making, values, this.form);
|
|
|
|
|
+ const w = window.open('', '_blank');
|
|
|
|
|
+ if (!w) {
|
|
|
|
|
+ this.$message && this.$message.error && this.$message.error('弹窗被拦截,无法打开预览窗口');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ w.document.open();
|
|
|
|
|
+ w.document.write(html);
|
|
|
|
|
+ w.document.close();
|
|
|
|
|
+ w.focus();
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ w.print();
|
|
|
|
|
+ }, 300);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('打印失败', e);
|
|
|
|
|
+ console.log('打印 JSON 数据:', this.jsonData, this.form.valueJson);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async openPreview() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const making = this.jsonData || (this.form.formJson && JSON.parse(this.form.formJson.makingJson));
|
|
|
|
|
+ // 确保远程数据加载完成(用于 id->name 映射)
|
|
|
|
|
+ await this.ensureRemoteData(making);
|
|
|
|
|
+ const values = this.form.valueJson || {};
|
|
|
|
|
+ const full = this.generatePrintHtml(making, values, this.form);
|
|
|
|
|
+ // 提取 <style> 和 .pf-container,用相同样式渲染预览
|
|
|
|
|
+ const styleMatch = full.match(/<style>([\s\S]*?)<\/style>/);
|
|
|
|
|
+ const containerMatch = full.match(/<div class="pf-container">[\s\S]*<\/div>/);
|
|
|
|
|
+ const styleTag = styleMatch ? `<style>${styleMatch[1]}</style>` : '';
|
|
|
|
|
+ this.previewHtml = styleTag + (containerMatch ? containerMatch[0] : full);
|
|
|
|
|
+ this.previewDialog = true;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('生成预览失败', e);
|
|
|
|
|
+ this.$message && this.$message.error && this.$message.error('生成预览失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async printPreview() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const making = this.jsonData || (this.form.formJson && JSON.parse(this.form.formJson.makingJson));
|
|
|
|
|
+ await this.ensureRemoteData(making);
|
|
|
|
|
+ const values = this.form.valueJson || {};
|
|
|
|
|
+ const full = this.generatePrintHtml(making, values, this.form);
|
|
|
|
|
+ const w = window.open('', '_blank');
|
|
|
|
|
+ if (!w) {
|
|
|
|
|
+ this.$message && this.$message.error && this.$message.error('弹窗被拦截,无法打开预览窗口');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ w.document.open();
|
|
|
|
|
+ w.document.write(full);
|
|
|
|
|
+ w.document.close();
|
|
|
|
|
+ w.focus();
|
|
|
|
|
+ setTimeout(() => w.print(), 300);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('打印预览失败', e);
|
|
|
|
|
+ this.$message && this.$message.error && this.$message.error('打印预览失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ generatePrintHtml(makingJson, valueJson, formMeta = {}) {
|
|
|
|
|
+ // 加载自定义区域表头映射(如果存在)
|
|
|
|
|
+ let headerMap = {};
|
|
|
|
|
+ try {
|
|
|
|
|
+ // eslint-disable-next-line no-undef
|
|
|
|
|
+ headerMap = require('@/BIZComponents/processSubmitDialog/components/blank_table_headers.json');
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ headerMap = {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const title = this.escapeHtml(formMeta.name || formMeta.formName || '表单打印');
|
|
|
|
|
+ const list = (makingJson && makingJson.list) || [];
|
|
|
|
|
+
|
|
|
|
|
+ const rows = [];
|
|
|
|
|
+ const isBlankField = (item) => item.type === 'blank' || (item.model && String(item.model).indexOf('blank_') === 0);
|
|
|
|
|
+ const renderBlankTable = (item, rawRows = []) => {
|
|
|
|
|
+ const mapKey = item.model || item.key || '';
|
|
|
|
|
+ let cols = [];
|
|
|
|
|
+ if (headerMap && headerMap[mapKey]) {
|
|
|
|
|
+ cols = headerMap[mapKey].map(c => c.prop);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const set = new Set();
|
|
|
|
|
+ rawRows.forEach((r) => {
|
|
|
|
|
+ if (r && typeof r === 'object') {
|
|
|
|
|
+ Object.keys(r).forEach(k => set.add(k));
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ cols = Array.from(set);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const ths = cols.map(c => {
|
|
|
|
|
+ let headLabel = c;
|
|
|
|
|
+ if (headerMap && headerMap[mapKey]) {
|
|
|
|
|
+ const found = headerMap[mapKey].find(h => h.prop === c);
|
|
|
|
|
+ if (found && found.label) headLabel = found.label;
|
|
|
|
|
+ }
|
|
|
|
|
+ return '<th>' + this.escapeHtml(headLabel) + '</th>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+
|
|
|
|
|
+ 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>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+ return '<tr>' + tds + '</tr>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+
|
|
|
|
|
+ return '<table class="pf-table"><thead><tr>' + ths + '</tr></thead><tbody>' + trs + '</tbody></table>';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
|
|
+ const item = list[i];
|
|
|
|
|
+ // 标题优先取 name/label,若不存在则回退到 model 的可读化,最终回退到 ''
|
|
|
|
|
+ const rawLabel = item.name || item.label || (item.model ? String(item.model).replace(/^blank_/, '').replace(/_/g, ' ') : '') || '';
|
|
|
|
|
+ const label = this.escapeHtml(rawLabel);
|
|
|
|
|
+ const key = item.model || item.key || '';
|
|
|
|
|
+ let val = '';
|
|
|
|
|
+ try {
|
|
|
|
|
+ const raw = valueJson && (valueJson[key] !== undefined ? valueJson[key] : valueJson[item.model]);
|
|
|
|
|
+ if (raw === null || raw === undefined || raw === '') {
|
|
|
|
|
+ if (isBlankField(item)) {
|
|
|
|
|
+ val = renderBlankTable(item, []);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (typeof raw === 'string' || typeof raw === 'number') {
|
|
|
|
|
+ const remoteKey = item.options && item.options.remoteDataSource;
|
|
|
|
|
+ if (remoteKey && this.remoteOptionsCache[remoteKey]) {
|
|
|
|
|
+ const map = this.remoteOptionsCache[remoteKey];
|
|
|
|
|
+ val = map && map[raw] ? (map[raw].name || map[raw]) : String(raw);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = this.escapeHtml(String(raw));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (Array.isArray(raw)) {
|
|
|
|
|
+ if (raw.length === 0) {
|
|
|
|
|
+ if (isBlankField(item)) {
|
|
|
|
|
+ val = renderBlankTable(item, []);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const first = raw[0];
|
|
|
|
|
+ // 先检测是否为附件数组(对象形式或 id 列表)
|
|
|
|
|
+ const isAttachmentObjectArray = raw.every(r => r && (r.id !== undefined || r.fileId !== undefined || r.uid !== undefined));
|
|
|
|
|
+ const looksLikeAttachmentByMeta = (item.type && String(item.type).toLowerCase().includes('upload')) || (item.model && String(item.model).toLowerCase().includes('file')) || (label && label.includes('附件'));
|
|
|
|
|
+ if (isAttachmentObjectArray) {
|
|
|
|
|
+ val = raw.length + ' 个附件';
|
|
|
|
|
+ } else if ((typeof first === 'string' || typeof first === 'number') && looksLikeAttachmentByMeta) {
|
|
|
|
|
+ // 原始为 id 列表,并且通过字段/label 推断为附件,显示数量
|
|
|
|
|
+ val = raw.length + ' 个附件';
|
|
|
|
|
+ } else if ((typeof first === 'string' || typeof first === 'number')) {
|
|
|
|
|
+ const remoteKey = item.options && item.options.remoteDataSource;
|
|
|
|
|
+ if (remoteKey && this.remoteOptionsCache[remoteKey]) {
|
|
|
|
|
+ const map = this.remoteOptionsCache[remoteKey];
|
|
|
|
|
+ val = raw.map(id => (map[id] ? (map[id].name || map[id]) : id)).join(', ');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = raw.map(r => this.escapeHtml(String(r))).join(', ');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (item.model === 'manage_workorder' && typeof first === 'object' && first !== null) {
|
|
|
|
|
+ val = raw
|
|
|
|
|
+ .map(r => (r && r.code !== undefined ? this.escapeHtml(String(r.code)) : this.escapeHtml(JSON.stringify(r))))
|
|
|
|
|
+ .join(', ');
|
|
|
|
|
+ } else if (typeof first === 'object' && first !== null && (item.type === 'blank' || (item.model && item.model.indexOf('blank_') === 0))) {
|
|
|
|
|
+ // 自定义区域渲染为表格
|
|
|
|
|
+ const mapKey = item.model || item.key || '';
|
|
|
|
|
+ let cols = [];
|
|
|
|
|
+ if (headerMap && headerMap[mapKey]) {
|
|
|
|
|
+ cols = headerMap[mapKey].map(c => c.prop);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const set = new Set();
|
|
|
|
|
+ for (let r of raw) {
|
|
|
|
|
+ if (r && typeof r === 'object') {
|
|
|
|
|
+ Object.keys(r).forEach(k => set.add(k));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ cols = Array.from(set);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 表头
|
|
|
|
|
+ const ths = cols.map(c => {
|
|
|
|
|
+ let headLabel = c;
|
|
|
|
|
+ if (headerMap && headerMap[mapKey]) {
|
|
|
|
|
+ const found = headerMap[mapKey].find(h => h.prop === c);
|
|
|
|
|
+ if (found && found.label) headLabel = found.label;
|
|
|
|
|
+ }
|
|
|
|
|
+ return '<th>' + this.escapeHtml(headLabel) + '</th>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+
|
|
|
|
|
+ // 行
|
|
|
|
|
+ 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>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+ return '<tr>' + tds + '</tr>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+
|
|
|
|
|
+ val = '<table class="pf-table"><thead><tr>' + ths + '</tr></thead><tbody>' + trs + '</tbody></table>';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 其他对象数组,简要渲染
|
|
|
|
|
+ val = raw.map(r => {
|
|
|
|
|
+ if (r === null || r === undefined) return '';
|
|
|
|
|
+ if (typeof r === 'string' || typeof r === 'number') return this.escapeHtml(String(r));
|
|
|
|
|
+ if (r && (r.name || r.fileName || r.label)) return this.escapeHtml(r.name || r.fileName || r.label);
|
|
|
|
|
+ if (r && (r.url || r.path)) return '<a href="' + this.escapeHtml(r.url || r.path) + '" target="_blank">文件</a>';
|
|
|
|
|
+ return this.escapeHtml(JSON.stringify(r));
|
|
|
|
|
+ }).join(', ');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (typeof raw === 'object') {
|
|
|
|
|
+ if (item.model === 'manage_workorder' && raw.code !== undefined) {
|
|
|
|
|
+ val = this.escapeHtml(String(raw.code));
|
|
|
|
|
+ } else if (raw.name) {
|
|
|
|
|
+ val = this.escapeHtml(String(raw.name));
|
|
|
|
|
+ } else if (raw.label) {
|
|
|
|
|
+ val = this.escapeHtml(String(raw.label));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = this.escapeHtml(JSON.stringify(raw));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val = this.escapeHtml(String(raw));
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ val = '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是自定义区域(blank)并且渲染结果为表格,则用整行显示区域名称 + 表格
|
|
|
|
|
+ const isBlankArea = (item.type === 'blank' || (item.model && String(item.model).indexOf('blank_') === 0));
|
|
|
|
|
+ if (isBlankArea && val && String(val).indexOf('<table') !== -1) {
|
|
|
|
|
+ // 不显示自定义区域标题,仅渲染表格
|
|
|
|
|
+ rows.push('<div class="pf-blank-area">' + val + '</div>');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rows.push('<div class="pf-row"><div class="pf-label">' + label + '</div><div class="pf-value">' + val + '</div></div>');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const style = 'body{font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial; color:#333} '
|
|
|
|
|
+ + '.pf-container{padding:20px;max-width:900px;margin:0 auto} '
|
|
|
|
|
+ + '.pf-title{font-size:18px;margin-bottom:12px} '
|
|
|
|
|
+ + '.pf-row{display:flex;padding:8px 0;border-bottom:1px solid #eee} '
|
|
|
|
|
+ + '.pf-label{width:160px;color:#666} '
|
|
|
|
|
+ + '.pf-value{flex:1} '
|
|
|
|
|
+ + '.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} ';
|
|
|
|
|
+
|
|
|
|
|
+ return '<!doctype html><html><head><meta charset="utf-8" /><title>' + title + '</title><style>' + style + '</style></head><body>'
|
|
|
|
|
+ + '<div class="pf-container"><div class="pf-title">' + title + '</div>' + rows.join('\n') + '</div></body></html>';
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ escapeHtml(str) {
|
|
|
|
|
+ if (str === null || str === undefined) return '';
|
|
|
|
|
+ return String(str)
|
|
|
|
|
+ .replace(/&/g, '&')
|
|
|
|
|
+ .replace(/</g, '<')
|
|
|
|
|
+ .replace(/>/g, '>')
|
|
|
|
|
+ .replace(/"/g, '"')
|
|
|
|
|
+ .replace(/'/g, ''');
|
|
|
},
|
|
},
|
|
|
getTimelineItemType(result) {
|
|
getTimelineItemType(result) {
|
|
|
if (result === '通过') {
|
|
if (result === '通过') {
|