Files
tms-erp-web/src/views/mk/public-biz-view.vue
T

369 lines
12 KiB
Vue

<template>
<div ref="page" class="mk-public-biz-view">
<basic-container>
<div class="mk-public-biz-view__title">{{ pageTitle }}</div>
<template v-if="detail">
<el-descriptions :column="2" border>
<el-descriptions-item
v-for="item in visibleFields"
:key="item.prop"
:label="item.label"
:span="item.span || 1"
>
{{ fieldDisplayValue(item.prop, detail[item.prop]) }}
</el-descriptions-item>
</el-descriptions>
<section
v-for="party in partySections"
:key="party.role"
class="mk-public-biz-view__party"
>
<div class="mk-public-biz-view__party-title">{{ party.label }}</div>
<el-table :data="party.rows" border empty-text="暂无数据">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="customerName" label="客商名称" min-width="180" align="center">
<template #default="{ row }">{{ row.customerName || '-' }}</template>
</el-table-column>
<el-table-column prop="creditCode" label="统一信用代码" min-width="160" align="center">
<template #default="{ row }">{{ row.creditCode || '-' }}</template>
</el-table-column>
<el-table-column prop="contactName" label="联系人" min-width="140" align="center">
<template #default="{ row }">{{ row.contactName || '-' }}</template>
</el-table-column>
<el-table-column prop="contactPhone" label="联系电话" min-width="140" align="center">
<template #default="{ row }">{{ row.contactPhone || '-' }}</template>
</el-table-column>
</el-table>
</section>
</template>
<el-empty v-else description="暂无数据" />
</basic-container>
</div>
</template>
<script>
import { getMkPublicDetail, postMkPublicProcessMessage } from '@/api/mk-process';
import { formatSealTypes } from '@/utils/settlement-seal';
const createPartyRow = (row = {}) => ({
customerId: row.customerId || '',
customerName: row.customerName || '',
creditCode: row.creditCode || row.unifiedCreditCode || '',
contactId: row.contactId || '',
contactName: row.contactName || '',
contactPhone: row.contactPhone || '',
});
const parsePartyRows = (partyJson, fallbackName) => {
const rows = (() => {
if (Array.isArray(partyJson)) return partyJson;
if (!partyJson) return [];
try {
const data = JSON.parse(partyJson);
return Array.isArray(data) ? data : [];
} catch (error) {
return [];
}
})().map(item => createPartyRow(item));
if (rows.length) return rows;
if (!fallbackName) return [];
return String(fallbackName)
.split(/[、,]/)
.map(name => name.trim())
.filter(Boolean)
.map(name => createPartyRow({ customerName: name }));
};
const FIELD_MAP = {
'project-apply': [
{ label: '申请单号', prop: 'applyNo' },
{ label: '项目编号', prop: 'projectCode' },
{ label: '项目名称', prop: 'projectName', span: 2 },
{ label: '审核状态', prop: 'approvalStatusName' },
{ label: '当前节点', prop: 'currentNode' },
{ label: '当前处理人', prop: 'currentProcessor' },
{ label: '所属组织', prop: 'deptName' },
{ label: '备注', prop: 'remark', span: 2 },
],
'contract-manage': [
{ label: '合同编号', prop: 'contractNo' },
{ label: '合同名称', prop: 'contractName', span: 2 },
{ label: '合同类别', prop: 'contractCategory' },
{ label: '审核状态', prop: 'approvalStatusName' },
{ label: '当前节点', prop: 'currentNode' },
{ label: '当前处理人', prop: 'currentProcessor' },
{ label: '项目名称', prop: 'projectName', span: 2 },
],
'pre-settlement': [
{ label: '预结算单号', prop: 'preSettlementNo' },
{ label: '合同名称', prop: 'contractName' },
{ label: '项目名称', prop: 'projectName' },
{ label: '结算金额', prop: 'settlementAmount' },
{ label: '用印类型', prop: 'sealTypes' },
{ label: '审核状态', prop: 'approvalStatusName' },
{ label: '当前节点', prop: 'currentNode' },
{ label: '当前处理人', prop: 'currentProcessor' },
{ label: '备注', prop: 'remark', span: 2 },
],
'formal-settlement': [
{ label: '正式结算单号', prop: 'formalSettlementNo' },
{ label: '预结算单号', prop: 'preSettlementNos' },
{ label: '项目名称', prop: 'projectName' },
{ label: '合同名称', prop: 'contractName' },
{ label: '结算金额', prop: 'settlementAmount' },
{ label: '用印类型', prop: 'sealTypes' },
{ label: '审核状态', prop: 'approvalStatusName' },
{ label: '当前节点', prop: 'currentNode' },
{ label: '当前处理人', prop: 'currentProcessor' },
],
'payment-application': [
{ label: '付款申请号', prop: 'paymentNo' },
{ label: '付款类型', prop: 'paymentTypeName' },
{ label: '收款方', prop: 'payeeName' },
{ label: '项目名称', prop: 'projectName' },
{ label: '申请金额', prop: 'applyAmount' },
{ label: '审核状态', prop: 'approvalStatusName' },
{ label: '当前节点', prop: 'currentNode' },
{ label: '当前处理人', prop: 'currentProcessor' },
],
};
const TITLE_MAP = {
'project-apply': '查看项目信息',
'contract-manage': '查看合同信息',
'pre-settlement': '查看预结算信息',
'formal-settlement': '查看正式结算信息',
'payment-application': '查看付款申请信息',
};
export default {
name: 'MkPublicBizView',
data() {
return {
detail: null,
};
},
computed: {
bizType() {
return this.$route.meta.bizType || this.$route.query.bizType || '';
},
pageTitle() {
return TITLE_MAP[this.bizType] || '查看详情';
},
visibleFields() {
return FIELD_MAP[this.bizType] || [];
},
partySections() {
if (!this.detail || this.bizType !== 'contract-manage') return [];
return [
{
role: 'A',
label: '甲方',
rows: parsePartyRows(this.detail.partyAJson, this.detail.partyA),
},
{
role: 'B',
label: '乙方',
rows: parsePartyRows(this.detail.partyBJson, this.detail.partyB),
},
{
role: 'C',
label: '丙方',
rows: parsePartyRows(this.detail.partyCJson, this.detail.partyC),
},
];
},
},
created() {
this.handleIframeHeight = () => this.sendIframeHeight();
this.handleProcessMessage = event => this.onProcessMessage(event);
document.addEventListener('DOMContentLoaded', this.handleIframeHeight, false);
window.addEventListener('message', this.handleProcessMessage);
},
mounted() {
document.documentElement.classList.add('mk-iframe-page');
document.body.classList.add('mk-iframe-page');
const app = document.getElementById('app');
if (app) app.classList.add('mk-iframe-page');
this.loadDetail();
this.handleIframeHeight();
window.addEventListener('load', this.handleIframeHeight);
this.mkHeightTimers = [300, 800, 1600].map(delay => setTimeout(this.handleIframeHeight, delay));
if (typeof ResizeObserver === 'undefined') return;
this.mkHeightObserver = new ResizeObserver(() => this.handleIframeHeight());
this.$nextTick(() => {
if (this.$refs.page) this.mkHeightObserver.observe(this.$refs.page);
this.mkHeightObserver.observe(document.body);
});
},
beforeUnmount() {
document.removeEventListener('DOMContentLoaded', this.handleIframeHeight, false);
window.removeEventListener('load', this.handleIframeHeight);
window.removeEventListener('message', this.handleProcessMessage);
(this.mkHeightTimers || []).forEach(timer => clearTimeout(timer));
if (this.mkHeightObserver) {
this.mkHeightObserver.disconnect();
this.mkHeightObserver = null;
}
document.documentElement.classList.remove('mk-iframe-page');
document.body.classList.remove('mk-iframe-page');
const app = document.getElementById('app');
if (app) app.classList.remove('mk-iframe-page');
},
methods: {
loadDetail() {
const id = this.getFormId();
if (!id || !this.bizType) return;
getMkPublicDetail(this.bizType, id).then(res => {
this.detail = res.data?.data || null;
this.handleIframeHeight();
});
},
sendIframeHeight() {
this.$nextTick(() => {
window.parent.postMessage({ height: document.body.clientHeight }, '*');
});
},
onProcessMessage(event) {
const data = event && event.data;
if (!data || typeof data !== 'object') return;
if (data.height && !data.status && !data.type) return;
if (data.type === 'formValues' || data.type === 'afterSubmit') return;
const formValues = data.formValues;
if (data.status === 'submit') {
this.submitData(formValues);
} else if (data.status === 'save') {
this.saveData(formValues);
}
if (data.type === 'getFormValues') {
window.parent.postMessage({ type: 'formValues', formData: this.buildFormData() }, '*');
}
},
submitData(lbpmFormValues) {
const formData = this.buildFormData();
this.postFormData('submit', lbpmFormValues, formData);
if (!lbpmFormValues) return;
const parameters = Object.assign({}, lbpmFormValues, {
loginName: this.getLoginName(lbpmFormValues),
formInstanceId: this.getFormId(),
subject: formData.subject,
});
window.parent.postMessage({ type: 'afterSubmit', success: true, parameters }, '*');
},
saveData(lbpmFormValues) {
this.postFormData('save', lbpmFormValues, this.buildFormData());
},
postFormData(status, formValues, formData) {
const payload = { status, formValues: formValues || {}, formData };
postMkPublicProcessMessage(this.bizType, payload).catch(error => {
console.error('公开页提交流程数据失败:', error);
});
},
buildFormData() {
const detail = this.detail || {};
return {
...detail,
id: this.getFormId() || detail.id,
subject: this.pageTitle,
formInstanceId: this.getFormId(),
sealTypes: formatSealTypes(detail.sealTypes) || detail.sealTypes || '',
};
},
getFormId() {
return this.$route.query.id || this.detail?.id || '';
},
getLoginName(formValues = {}) {
return (
formValues.loginName ||
this.$route.query.loginName ||
this.$route.query.submitIdentity ||
''
);
},
fieldDisplayValue(prop, value) {
if (prop === 'sealTypes') {
return formatSealTypes(value) || '-';
}
return this.displayValue(value);
},
displayValue(value) {
if (value === null || value === undefined || value === '') return '-';
if (Array.isArray(value)) return value.join('、') || '-';
if (typeof value === 'object') return JSON.stringify(value);
return value;
},
},
};
</script>
<style lang="scss">
html.mk-iframe-page,
html.mk-iframe-page body,
html.mk-iframe-page #app,
html.mk-iframe-page #app.mk-iframe-page {
height: auto !important;
min-height: 100%;
overflow: visible;
}
</style>
<style lang="scss" scoped>
.mk-public-biz-view {
min-height: 100%;
padding: 12px 0 24px;
box-sizing: border-box;
background: #f0f2f5;
:deep(.basic-container) {
padding: 0 12px;
}
&__title {
position: relative;
padding-left: 12px;
margin: 8px 0 16px;
font-size: 16px;
font-weight: 600;
line-height: 22px;
&::before {
content: '';
position: absolute;
left: 0;
top: 2px;
width: 4px;
height: 18px;
background: #409eff;
}
}
&__party {
margin-top: 16px;
padding: 14px 16px 16px;
background: #fff;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
&__party-title {
position: relative;
padding-left: 12px;
margin-bottom: 12px;
font-size: 14px;
font-weight: 600;
line-height: 22px;
color: #303133;
&::before {
content: '';
position: absolute;
left: 0;
top: 2px;
width: 4px;
height: 18px;
background: #409eff;
}
}
}
</style>