Merge remote-tracking branch 'origin/master' into feature/bfi-api-rate
# Conflicts: # src/views/vehicle/customer-archive.vue
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div ref="page" class="customer-archive-public-view">
|
||||
<customer-archive ref="archive" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CustomerArchive from './customer-archive.vue';
|
||||
import { postPublicProcessMessage } from '@/api/vehicle/customer-archive';
|
||||
|
||||
export default {
|
||||
name: 'CustomerArchivePublicView',
|
||||
components: {
|
||||
CustomerArchive,
|
||||
},
|
||||
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.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: {
|
||||
sendIframeHeight() {
|
||||
this.$nextTick(() => {
|
||||
var tbody = document.body;
|
||||
var height = tbody.clientHeight;
|
||||
// 如需动态改变表单高度,可以直接发送此postMessage
|
||||
window.parent.postMessage({ height: height }, '*');
|
||||
});
|
||||
},
|
||||
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;
|
||||
console.log('客商公开页收到流程消息:', data);
|
||||
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,
|
||||
};
|
||||
console.log('客商公开页提交流程数据:', payload);
|
||||
postPublicProcessMessage(payload).catch(error => {
|
||||
console.error('客商公开页提交流程数据失败:', error);
|
||||
});
|
||||
},
|
||||
buildFormData() {
|
||||
const archive = this.getArchiveForm();
|
||||
return {
|
||||
...archive,
|
||||
subject: archive.fullName || archive.shortName || '',
|
||||
formInstanceId: this.getFormId(),
|
||||
};
|
||||
},
|
||||
getArchiveForm() {
|
||||
const archive = this.$refs.archive && this.$refs.archive.archiveForm;
|
||||
if (!archive) return {};
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(archive));
|
||||
} catch (error) {
|
||||
return { ...archive };
|
||||
}
|
||||
},
|
||||
getFormId() {
|
||||
return this.$route.query.id || this.getArchiveForm().id || '';
|
||||
},
|
||||
getLoginName(formValues = {}) {
|
||||
return (
|
||||
formValues.loginName ||
|
||||
this.$route.query.loginName ||
|
||||
this.$route.query.submitIdentity ||
|
||||
''
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</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>
|
||||
.customer-archive-public-view {
|
||||
min-height: 100%;
|
||||
padding: 12px 0 24px;
|
||||
box-sizing: border-box;
|
||||
background: #f0f2f5;
|
||||
|
||||
:deep(.basic-container) {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
:deep(.archive-page-form .archive-form__footer) {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<basic-container class="customer-archive-page">
|
||||
<basic-container class="customer-archive-page" :class="{ 'is-public-view': isPublicViewPage }">
|
||||
<avue-crud
|
||||
v-if="!isArchivePage"
|
||||
:option="option"
|
||||
@@ -150,9 +150,24 @@
|
||||
>
|
||||
撤回
|
||||
</el-link>
|
||||
<el-link
|
||||
type="primary"
|
||||
v-if="
|
||||
hasPermission('customer_type_re_cert') &&
|
||||
row.approvalStatus === 'reviewing' &&
|
||||
row.reviewStatus !== '已完成'
|
||||
"
|
||||
@click="openReview(row)"
|
||||
>
|
||||
复评
|
||||
</el-link>
|
||||
<el-link
|
||||
type="success"
|
||||
v-if="hasPermission('customer_archive_approve') && row.approvalStatus === 'reviewing'"
|
||||
v-if="
|
||||
hasPermission('customer_archive_approve') &&
|
||||
row.approvalStatus === 'reviewing' &&
|
||||
row.reviewStatus === '已完成'
|
||||
"
|
||||
@click="handleApprove(row)"
|
||||
>
|
||||
审核通过
|
||||
@@ -547,7 +562,7 @@
|
||||
<span>{{ formatScoreValue(row.selfScore) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复评得分" min-width="145" align="center">
|
||||
<el-table-column v-if="archiveForm.id" label="复评得分" min-width="145" align="center">
|
||||
<template #default="{ row }">
|
||||
<span>{{ formatScoreValue(row.reviewScore) }}</span>
|
||||
</template>
|
||||
@@ -583,11 +598,18 @@
|
||||
<span>{{ row.reviewStatus }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="135" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openScore(row.__raw, row.__rawIndex)">
|
||||
详情
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="showScoreDelete"
|
||||
type="danger"
|
||||
@click="deleteScore(row.__rawIndex)"
|
||||
>
|
||||
删除
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -654,6 +676,7 @@
|
||||
<el-table-column label="开户人姓名" prop="accountHolderName" min-width="150" />
|
||||
<el-table-column label="收款账号" prop="bankAccount" min-width="180" />
|
||||
<el-table-column label="开户行" prop="bankName" min-width="180" />
|
||||
<el-table-column label="联行号" prop="cnapsCode" min-width="160" />
|
||||
<el-table-column label="备注" prop="remark" min-width="180" />
|
||||
<el-table-column
|
||||
label="操作"
|
||||
@@ -905,45 +928,12 @@
|
||||
/>
|
||||
</section-card>
|
||||
|
||||
<el-dialog
|
||||
<change-record-detail-dialog
|
||||
v-model="changeRecordDetailVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="changeRecordDetail" class="change-record-detail-meta">
|
||||
<span>变更日期:{{ changeRecordDetail.changeTime || '-' }}</span>
|
||||
<span>变更账号:{{ changeRecordDetail.changeUserName || '-' }}</span>
|
||||
</div>
|
||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!changeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
:rows="changeRecordDetailRows"
|
||||
/>
|
||||
|
||||
<div class="archive-form__footer">
|
||||
<div class="archive-form__footer" v-if="!isPublicViewPage">
|
||||
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
||||
<el-button @click="closeArchive">{{ readonly ? '关闭' : '取消' }}</el-button>
|
||||
<el-button type="primary" plain v-if="!readonly" @click="saveArchive">保存</el-button>
|
||||
@@ -1052,6 +1042,9 @@
|
||||
<el-form-item label="开户行" prop="bankName">
|
||||
<el-input v-model="receiptForm.bankName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联行号" prop="cnapsCode">
|
||||
<el-input v-model="receiptForm.cnapsCode" maxlength="32" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收款账号" prop="bankAccount">
|
||||
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
||||
</el-form-item>
|
||||
@@ -1169,17 +1162,18 @@
|
||||
<el-input v-model="row.email" maxlength="100" :disabled="readonly" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属部门" min-width="200">
|
||||
<el-table-column label="所属部门" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!isInternalCustomer"
|
||||
:model-value="row.deptNames"
|
||||
disabled
|
||||
/>
|
||||
<el-select
|
||||
<el-cascader
|
||||
v-else
|
||||
v-model="row.deptIdList"
|
||||
multiple
|
||||
:options="deptTree"
|
||||
:props="invoiceDeptCascaderProps"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
filterable
|
||||
@@ -1187,14 +1181,7 @@
|
||||
:disabled="readonly"
|
||||
placeholder="请选择所属部门"
|
||||
@change="() => handleInvoiceContactDeptChange(row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in invoiceDeptOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="160">
|
||||
@@ -1349,7 +1336,25 @@
|
||||
class="score-detail-table score-detail-table--inline"
|
||||
max-height="520"
|
||||
>
|
||||
<el-table-column label="评分项目" prop="itemName" width="150" align="center" />
|
||||
<el-table-column
|
||||
label="评分项目"
|
||||
width="150"
|
||||
align="center"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<template #default="{ row: detail }">
|
||||
<el-tooltip
|
||||
:content="detail.itemName"
|
||||
placement="top"
|
||||
effect="dark"
|
||||
append-to="body"
|
||||
:show-after="200"
|
||||
:disabled="!detail.itemName"
|
||||
>
|
||||
<span class="score-item-name">{{ detail.itemName }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分标准" min-width="430">
|
||||
<template #default="{ row: detail }">
|
||||
<div class="score-standard-cell">
|
||||
@@ -1359,6 +1364,7 @@
|
||||
inputmode="decimal"
|
||||
:disabled="readonly"
|
||||
@input="value => handleScoreValueInput(detail, value)"
|
||||
@blur="handleScoreValueBlur(detail)"
|
||||
>
|
||||
<template #append>{{ getScoreRule(detail)?.changeUnit || '' }}</template>
|
||||
</el-input>
|
||||
@@ -1388,13 +1394,15 @@
|
||||
<span>{{ formatScoreValue(getSignedScore(detail, 'selfScore')) }}分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复评得分" width="150" align="center">
|
||||
<el-table-column v-if="!isNewScore" label="复评得分" width="150" align="center">
|
||||
<template #default="{ row: detail }">
|
||||
<el-input
|
||||
v-model="detail.reviewScore"
|
||||
maxlength="10"
|
||||
inputmode="decimal"
|
||||
:disabled="readonly || !hasPermission('customer_type_re_cert')"
|
||||
:disabled="
|
||||
(!reviewMode && readonly) || !hasPermission('customer_type_re_cert')
|
||||
"
|
||||
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
|
||||
>
|
||||
<template
|
||||
@@ -1415,7 +1423,7 @@
|
||||
<template #default="{ row }">
|
||||
<span class="score-category-table__name">{{ row.categoryName }}</span>
|
||||
<span>自评:{{ getSignedScore(row, 'selfScore') }}分</span>
|
||||
<span>复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
||||
<span v-if="!isNewScore">复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="60" align="center">
|
||||
@@ -1489,12 +1497,15 @@
|
||||
<div class="score-detail-dialog__total">
|
||||
<span>总分合计</span>
|
||||
<span>自评:{{ formatScoreValue(currentScore.selfScore) }}分</span>
|
||||
<span>复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
||||
<span v-if="!isNewScore">复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
||||
</div>
|
||||
<el-button @click="scoreBox = false">取消</el-button>
|
||||
<el-button type="primary" plain v-if="!readonly" @click="saveScoreDetail">保存</el-button>
|
||||
<el-button v-if="isNewScore" type="primary" @click="submitSelfScore">提交自评</el-button>
|
||||
<el-button type="primary" plain v-if="!readonly && !isNewScore" @click="saveScoreDetail">
|
||||
保存
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!readonly && hasPermission('customer_type_re_cert')"
|
||||
v-if="!isNewScore && (!readonly || reviewMode) && hasPermission('customer_type_re_cert')"
|
||||
type="primary"
|
||||
@click="confirmReviewScore"
|
||||
>
|
||||
@@ -1512,7 +1523,9 @@ import { ElCascader } from 'element-plus';
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
getPublicDetail,
|
||||
getChangeRecordList,
|
||||
getPublicChangeRecordList,
|
||||
submit,
|
||||
submitApproval,
|
||||
withdrawApproval,
|
||||
@@ -1530,6 +1543,7 @@ import {
|
||||
getDetail as getCreditScoreQuantificationDetail,
|
||||
} from '@/api/vehicle/credit-score-quantification';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { getLazyTree } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
@@ -1548,6 +1562,7 @@ import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import SectionCard from '@/components/section-card/main.vue';
|
||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||
|
||||
const createTimeRangeMap = {
|
||||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||||
@@ -1563,11 +1578,18 @@ const qualificationViewerPlugins = [
|
||||
export default {
|
||||
components: {
|
||||
SectionCard,
|
||||
ChangeRecordDetailDialog,
|
||||
ArrowRight,
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
PdfPreview,
|
||||
},
|
||||
props: {
|
||||
embeddedPublicId: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const nonNegativeLabelMap = {
|
||||
invoiceTaxRate: '开票税点',
|
||||
@@ -1640,6 +1662,8 @@ export default {
|
||||
receiptBox: false,
|
||||
invoiceBox: false,
|
||||
readonly: false,
|
||||
reviewMode: false,
|
||||
listActivated: false,
|
||||
originalAccessType: '',
|
||||
activeTab: 'scores',
|
||||
archiveForm: this.emptyArchive(),
|
||||
@@ -1800,6 +1824,7 @@ export default {
|
||||
accountName: [{ required: true, message: '请输入收款单位名称', trigger: 'blur' }],
|
||||
accountHolderName: [{ required: true, message: '请输入开户人姓名', trigger: 'blur' }],
|
||||
bankName: [{ required: true, message: '请输入开户行', trigger: 'blur' }],
|
||||
cnapsCode: [{ required: true, message: '请输入联行号', trigger: 'blur' }],
|
||||
bankAccount: [{ required: true, message: '请输入收款账号', trigger: 'blur' }],
|
||||
remark: [{ max: 200, message: '备注最多200个字符', trigger: 'blur' }],
|
||||
},
|
||||
@@ -1835,7 +1860,7 @@ export default {
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 280,
|
||||
menuWidth: 320,
|
||||
column: [
|
||||
{
|
||||
label: '客商编号',
|
||||
@@ -2019,15 +2044,36 @@ export default {
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.isPublicViewPage) {
|
||||
this.readonly = true;
|
||||
const id = this.embeddedPublicId || this.$route.query.id;
|
||||
if (!id) {
|
||||
this.archiveBox = true;
|
||||
this.$message.error('缺少客商ID');
|
||||
return;
|
||||
}
|
||||
this.openArchive({ id }, true);
|
||||
return;
|
||||
}
|
||||
this.initDeptTree();
|
||||
this.initRegionOptions();
|
||||
this.initBusinessDictionaries();
|
||||
this.initScoreQuantificationOptions();
|
||||
if (this.isArchivePage) {
|
||||
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
|
||||
this.reviewMode = this.$route.query.review === '1';
|
||||
const readonly =
|
||||
this.$route.query.view === '1' || this.$route.query.view === 'true' || this.reviewMode;
|
||||
this.openArchive(this.$route.query.id ? { id: this.$route.query.id } : null, readonly);
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
if (this.isArchivePage || this.isPublicViewPage) return;
|
||||
if (!this.listActivated) {
|
||||
this.listActivated = true;
|
||||
return;
|
||||
}
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
@@ -2043,7 +2089,14 @@ export default {
|
||||
};
|
||||
},
|
||||
isArchivePage() {
|
||||
return this.$route.path === '/vehicle/customer-archive/form';
|
||||
return (
|
||||
this.$route.path === '/vehicle/customer-archive/form' || this.isPublicViewPage
|
||||
);
|
||||
},
|
||||
isPublicViewPage() {
|
||||
return (
|
||||
this.$route.path === '/vehicle/customer-archive/public-view' || !!this.embeddedPublicId
|
||||
);
|
||||
},
|
||||
archiveContainer() {
|
||||
return 'div';
|
||||
@@ -2069,6 +2122,18 @@ export default {
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 发票联系信息所属部门:级联多选,可勾选任意层级
|
||||
invoiceDeptCascaderProps() {
|
||||
return {
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
multiple: true,
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 所属组织级联值:archiveForm.deptId 只存选中的部门 ID(单选),
|
||||
// 级联控件需要根到选中节点的完整路径,两者在此做双向转换——
|
||||
// 部门树是异步加载的,依赖 deptTree 可保证树加载完成后回显自动刷新。
|
||||
@@ -2109,6 +2174,7 @@ export default {
|
||||
return ids.join(',');
|
||||
},
|
||||
dialogTitle() {
|
||||
if (this.reviewMode) return '客商复评';
|
||||
if (this.readonly) return '查看客商档案';
|
||||
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
|
||||
},
|
||||
@@ -2129,12 +2195,6 @@ export default {
|
||||
isInternalCustomer() {
|
||||
return this.archiveForm.customerKind === 'internal';
|
||||
},
|
||||
invoiceDeptOptions() {
|
||||
return this.flattenDept(this.deptTree).map(item => ({
|
||||
label: item.rawLabel,
|
||||
value: String(item.value),
|
||||
}));
|
||||
},
|
||||
isTemporaryToFormal() {
|
||||
return (
|
||||
Boolean(this.archiveForm.id) &&
|
||||
@@ -2152,6 +2212,14 @@ export default {
|
||||
scoreList() {
|
||||
return this.archiveForm.scores || [];
|
||||
},
|
||||
showScoreDelete() {
|
||||
if (this.readonly) return false;
|
||||
const status = this.archiveForm.approvalStatus || 'draft';
|
||||
return status === 'draft' || status === 'rejected';
|
||||
},
|
||||
isNewScore() {
|
||||
return this.scoreRecordIndex < 0 && !this.reviewMode;
|
||||
},
|
||||
scorePageCount() {
|
||||
return Math.max(Math.ceil(this.scoreList.length / this.scorePage.pageSize), 1);
|
||||
},
|
||||
@@ -2226,6 +2294,45 @@ export default {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
mergeSelectOptions(target, values) {
|
||||
const list = Array.isArray(values) ? values : values ? [values] : [];
|
||||
list.forEach(value => {
|
||||
if (value === undefined || value === null || value === '') return;
|
||||
if (!this[target].some(item => String(item.value) === String(value))) {
|
||||
this[target].push({ label: String(value), value });
|
||||
}
|
||||
});
|
||||
},
|
||||
ensurePublicViewOptions(archive = {}) {
|
||||
this.mergeSelectOptions('customerNatureOptions', archive.customerNature);
|
||||
this.mergeSelectOptions('customerTypeOptions', archive.customerType);
|
||||
this.mergeSelectOptions('businessScopeOptions', archive.businessScope);
|
||||
this.mergeSelectOptions(
|
||||
'qualificationTypeOptions',
|
||||
(this.qualificationFiles || []).map(item => item.type)
|
||||
);
|
||||
const deptId = Array.isArray(archive.deptId) ? archive.deptId[0] : archive.deptId;
|
||||
if (deptId && archive.deptName) {
|
||||
this.deptTree = [
|
||||
{
|
||||
label: archive.deptName,
|
||||
value: String(deptId),
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
this.deptOptions = this.flattenDept(this.deptTree);
|
||||
}
|
||||
(archive.scores || []).forEach(score => {
|
||||
if (!score.quantificationId) return;
|
||||
const id = String(score.quantificationId);
|
||||
if (!this.scoreQuantificationOptions.some(item => String(item.id) === id)) {
|
||||
this.scoreQuantificationOptions.push({
|
||||
id,
|
||||
name: score.quantificationName || score.name || id,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
unifiedCreditCodeError(value) {
|
||||
const code = String(value || '')
|
||||
.trim()
|
||||
@@ -2430,7 +2537,8 @@ export default {
|
||||
loadChangeRecords() {
|
||||
if (!this.archiveForm.id) return;
|
||||
const page = this.changeRecordPage;
|
||||
getChangeRecordList(this.archiveForm.id, page.currentPage, page.pageSize).then(res => {
|
||||
const requestList = this.isPublicViewPage ? getPublicChangeRecordList : getChangeRecordList;
|
||||
requestList(this.archiveForm.id, page.currentPage, page.pageSize).then(res => {
|
||||
const data = res.data.data || {};
|
||||
page.records = this.replaceNegativeOneWithBlank(data.records || []);
|
||||
page.total = Number(data.total || 0);
|
||||
@@ -2788,6 +2896,7 @@ export default {
|
||||
accountName: '',
|
||||
accountHolderName: '',
|
||||
bankName: '',
|
||||
cnapsCode: '',
|
||||
bankAccount: '',
|
||||
registeredPhone: '',
|
||||
registeredAddress: '',
|
||||
@@ -3475,7 +3584,17 @@ export default {
|
||||
this.invoiceForm.contacts.splice(index, 1);
|
||||
},
|
||||
handleInvoiceContactDeptChange(row) {
|
||||
const ids = Array.isArray(row.deptIdList) ? row.deptIdList.map(item => String(item)) : [];
|
||||
// emitPath=false 时,多选级联值为选中节点 id 数组
|
||||
const rawList = Array.isArray(row.deptIdList) ? row.deptIdList : [];
|
||||
const ids = rawList
|
||||
.map(item => {
|
||||
if (Array.isArray(item)) {
|
||||
return item.length ? String(item[item.length - 1]) : '';
|
||||
}
|
||||
return item !== undefined && item !== null && item !== '' ? String(item) : '';
|
||||
})
|
||||
.filter(Boolean);
|
||||
row.deptIdList = ids;
|
||||
row.deptIds = ids.join(',');
|
||||
row.deptNames = ids.map(id => this.findDeptLabel(id)).filter(Boolean).join('、');
|
||||
},
|
||||
@@ -4041,7 +4160,16 @@ export default {
|
||||
query: { id: row.id, name: '查看客商档案', view: '1' },
|
||||
});
|
||||
},
|
||||
openReview(row) {
|
||||
this.$router.push({
|
||||
path: '/vehicle/customer-archive/form',
|
||||
query: { id: row.id, name: '客商复评', review: '1' },
|
||||
});
|
||||
},
|
||||
closeArchive() {
|
||||
if (this.isPublicViewPage) return;
|
||||
this.archiveBox = false;
|
||||
this.$router.$avueRouter?.closeTag?.();
|
||||
this.$router.push('/vehicle/customer-archive');
|
||||
},
|
||||
openArchive(row, readonly = false) {
|
||||
@@ -4050,7 +4178,9 @@ export default {
|
||||
this.resetDetailPagination();
|
||||
this.resetChangeRecordPage();
|
||||
if (row && row.id) {
|
||||
getDetail(row.id).then(res => {
|
||||
const requestDetail = this.isPublicViewPage ? getPublicDetail : getDetail;
|
||||
requestDetail(row.id)
|
||||
.then(res => {
|
||||
const archive = this.normalizeDetail(res.data.data);
|
||||
this.archiveForm = readonly ? this.replaceNegativeOneWithBlank(archive) : archive;
|
||||
this.originalAccessType = this.archiveForm.accessType || '';
|
||||
@@ -4064,9 +4194,15 @@ export default {
|
||||
this.qualificationUploadFiles = [];
|
||||
this.ocrQualificationUploadFiles = [];
|
||||
this.selectedQualificationFiles = [];
|
||||
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
||||
this.archiveBox = true;
|
||||
this.loadChangeRecords();
|
||||
});
|
||||
if (this.reviewMode) this.openReviewScore();
|
||||
})
|
||||
.catch(() => {
|
||||
this.archiveBox = true;
|
||||
if (this.isPublicViewPage) this.$message.error('客商信息加载失败');
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.archiveForm = this.emptyArchive();
|
||||
@@ -4105,14 +4241,15 @@ export default {
|
||||
this.invoiceForm = this.emptyInvoice();
|
||||
this.$refs.archiveForm?.clearValidate();
|
||||
},
|
||||
// 客商材料不做提交校验,仅在页面上提示未上传项
|
||||
validateFormalForApproval(archive) {
|
||||
if (archive.accessType !== 'formal') return true;
|
||||
const hasCompletedScore = (archive.scores || []).some(
|
||||
item => item.selfStatus === '已完成' || item.reviewStatus === '已完成' || item.finalScore
|
||||
);
|
||||
if (!hasCompletedScore) {
|
||||
this.$message.warning('正式客商提交审核前必须完成信用评分');
|
||||
// 客商材料不做提交校验,仅在页面上提示未上传项。提交审批前必须完成自评,且评分记录只能有一条。
|
||||
validateScoreForApproval(archive) {
|
||||
const scores = archive.scores || [];
|
||||
if (scores.length > 1) {
|
||||
this.$message.warning('评分记录只能有一条');
|
||||
return false;
|
||||
}
|
||||
if (scores.length !== 1 || scores[0].selfStatus !== '已完成') {
|
||||
this.$message.warning('提交审批前必须完成自评');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -4164,7 +4301,7 @@ export default {
|
||||
.filter(item => item.contactName || item.contactPhone);
|
||||
archive.receiptAccounts = (archive.receiptAccounts || [])
|
||||
.map(item => this.normalizeReceipt(item))
|
||||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount);
|
||||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount || item.cnapsCode);
|
||||
archive.invoices = (archive.invoices || [])
|
||||
.map(item => {
|
||||
const invoice = this.normalizeInvoice(item);
|
||||
@@ -4230,7 +4367,7 @@ export default {
|
||||
const archive = this.normalizeArchive();
|
||||
// 仅提交时落变更记录;保存不记录
|
||||
archive.recordChange = true;
|
||||
if (!this.validateFormalForApproval(archive)) return;
|
||||
if (!this.validateScoreForApproval(archive)) return;
|
||||
submit(archive).then(res => {
|
||||
const data = res.data.data;
|
||||
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
|
||||
@@ -4240,7 +4377,16 @@ export default {
|
||||
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
|
||||
return;
|
||||
}
|
||||
submitApproval(id).then(() => {
|
||||
const fullName =
|
||||
(data && typeof data === 'object' ? data.fullName : '') ||
|
||||
archive.fullName ||
|
||||
this.archiveForm.fullName ||
|
||||
'';
|
||||
this.submitCustomerApproval(
|
||||
id,
|
||||
fullName,
|
||||
archive.approvalStatus || this.archiveForm.approvalStatus
|
||||
).then(() => {
|
||||
this.closeArchive();
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '提交成功!' });
|
||||
@@ -4248,9 +4394,32 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 列表 / 新增 / 编辑提交共用:开启 MK 时提交审核流,并更新客商审批状态
|
||||
*/
|
||||
submitCustomerApproval(id, fullName = '', approvalStatus = '') {
|
||||
return submitMkApprovalFlow({
|
||||
bizType: 'customer-archive',
|
||||
formInstanceId: id,
|
||||
subjectName: fullName,
|
||||
approvalStatus,
|
||||
}).then(() => submitApproval(id));
|
||||
},
|
||||
openReviewScore() {
|
||||
const scores = this.archiveForm.scores || [];
|
||||
if (!scores.length) {
|
||||
this.$message.warning('暂无评分记录,无法复评');
|
||||
return;
|
||||
}
|
||||
this.$nextTick(() => this.openScore(scores[0], 0));
|
||||
},
|
||||
addScore() {
|
||||
if ((this.archiveForm.scores || []).length) {
|
||||
this.$message.warning('请勿重复添加');
|
||||
return;
|
||||
}
|
||||
this.scoreRecordIndex = -1;
|
||||
this.currentScore = this.normalizeScore({});
|
||||
this.currentScore = this.normalizeScore({ applyCreditLimit: '' });
|
||||
this.scoreAttachmentFiles = [];
|
||||
this.expandedScoreCategoryKeys = [];
|
||||
this.scoreBox = true;
|
||||
@@ -4332,7 +4501,7 @@ export default {
|
||||
JSON.stringify(
|
||||
(item.options || []).map(option => ({
|
||||
label: option.label || option.optionName || option.value,
|
||||
value: option.value || option.optionName || option.label,
|
||||
value: option.value ?? option.optionName ?? option.label,
|
||||
score: option.score,
|
||||
changeType: option.changeType,
|
||||
changeValue: option.changeValue,
|
||||
@@ -4422,20 +4591,21 @@ export default {
|
||||
const isScoreOption = item.changeType || item.changeValue || item.changeUnit;
|
||||
const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
|
||||
const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
|
||||
const rawScore = item.score;
|
||||
const generatedLabel = isScoreOption
|
||||
? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${
|
||||
item.score || ''
|
||||
this.isBlankScore(rawScore) ? '' : rawScore
|
||||
}分`
|
||||
: '';
|
||||
const label = item.label || item.optionName || item.value || generatedLabel;
|
||||
return {
|
||||
...item,
|
||||
label,
|
||||
value: item.value || item.optionName || item.label || generatedLabel,
|
||||
value: item.value ?? item.optionName ?? item.label ?? generatedLabel,
|
||||
score:
|
||||
item.scoreType === 'subtract' && item.score !== undefined
|
||||
? -Math.abs(Number(item.score))
|
||||
: item.score,
|
||||
item.scoreType === 'subtract' && !this.isBlankScore(rawScore)
|
||||
? -Math.abs(Number(rawScore))
|
||||
: rawScore,
|
||||
};
|
||||
});
|
||||
if (Array.isArray(value)) return normalize(value);
|
||||
@@ -4499,23 +4669,35 @@ export default {
|
||||
const valid = increase ? input >= base : input <= base;
|
||||
if (!valid) {
|
||||
detail.selfScore = '';
|
||||
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
||||
this.calculateScore(this.currentScore);
|
||||
return;
|
||||
}
|
||||
detail.selfScore = this.getScoreTypeCalculatedScore(detail);
|
||||
this.calculateScore(this.currentScore);
|
||||
},
|
||||
handleScoreValueBlur(detail) {
|
||||
const normalized = String(detail.scoreInput ?? '').trim();
|
||||
if (!normalized || normalized === '-' || normalized === '.') return;
|
||||
const rule = this.getScoreRule(detail);
|
||||
const base = Number(detail.baseValue);
|
||||
const input = Number(normalized);
|
||||
if (!rule || !Number.isFinite(base) || !Number.isFinite(input)) return;
|
||||
const increase = rule.changeType !== 'decrease';
|
||||
const valid = increase ? input >= base : input <= base;
|
||||
if (!valid) {
|
||||
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
||||
}
|
||||
},
|
||||
scoreOptionChange(detail, optionValue) {
|
||||
const option = this.parseScoreOptions(detail.optionsJson).find(
|
||||
item => item.value === optionValue
|
||||
);
|
||||
const score = Number(option?.score || 0);
|
||||
detail.selfScore = score;
|
||||
const rawScore = option?.score;
|
||||
detail.selfScore = this.isBlankScore(rawScore) ? 0 : Number(rawScore);
|
||||
this.calculateScore(this.currentScore);
|
||||
},
|
||||
handleScoreDetailScoreInput(detail, prop, value) {
|
||||
const normalized = String(value || '')
|
||||
const normalized = String(value ?? '')
|
||||
.replace(/[^\d.]/g, '')
|
||||
.replace(/^\./, '')
|
||||
.replace(/(\..*)\./g, '$1')
|
||||
@@ -4647,8 +4829,7 @@ export default {
|
||||
sumScore(plusDetails, 'reviewScore') -
|
||||
sumScore(minusDetails, 'reviewScore');
|
||||
const hasReviewScore = details.some(
|
||||
item =>
|
||||
item.reviewScore !== undefined && item.reviewScore !== null && item.reviewScore !== ''
|
||||
item => !this.isBlankScore(item.reviewScore)
|
||||
);
|
||||
const finalScore = hasReviewScore ? reviewScore : selfScore;
|
||||
const fullMark = this.getScoreFullMark(details);
|
||||
@@ -4668,13 +4849,17 @@ export default {
|
||||
}
|
||||
return score;
|
||||
},
|
||||
isBlankScore(value) {
|
||||
return value === undefined || value === null || String(value).trim() === '';
|
||||
},
|
||||
hasIncompleteBasicScoreDetail(details = []) {
|
||||
return this.getScoreCategoryDetailsByList(details, 'basic').some(item => {
|
||||
if (this.isScoreTypeDetail(item)) {
|
||||
const input = String(item.scoreInput ?? '').trim();
|
||||
return !input || !Number.isFinite(Number(input)) || item.selfScore === '';
|
||||
const inputBlank = input === '' || input === '-' || input === '.';
|
||||
return inputBlank || !Number.isFinite(Number(input)) || this.isBlankScore(item.selfScore);
|
||||
}
|
||||
return !item.selectedOption;
|
||||
return this.isBlankScore(item.selectedOption);
|
||||
});
|
||||
},
|
||||
finishScore(score) {
|
||||
@@ -4691,10 +4876,13 @@ export default {
|
||||
score.reviewStatus = '已完成';
|
||||
this.$message.success('评分已完成');
|
||||
},
|
||||
saveScoreDetail() {
|
||||
saveScoreDetail(successMessage) {
|
||||
if (!this.validateScoreForm(false)) return;
|
||||
this.currentScore.attachments = JSON.parse(JSON.stringify(this.scoreAttachmentFiles || []));
|
||||
this.currentScore.proofAttachments = this.stringifyAttachments(this.scoreAttachmentFiles);
|
||||
this.currentScore.selfStatus = this.hasIncompleteBasicScoreDetail(this.currentScore.details)
|
||||
? '未完成'
|
||||
: '已完成';
|
||||
this.calculateScore(this.currentScore);
|
||||
const score = JSON.parse(JSON.stringify(this.currentScore));
|
||||
if (this.scoreRecordIndex > -1) {
|
||||
@@ -4706,23 +4894,62 @@ export default {
|
||||
this.syncArchiveCreditFromScores();
|
||||
this.activeTab = 'scores';
|
||||
this.scoreBox = false;
|
||||
this.saveScoreArchive();
|
||||
},
|
||||
saveScoreArchive() {
|
||||
if (!this.archiveForm.id) {
|
||||
this.$message.success('评分记录已添加,请保存客商档案后生效');
|
||||
return;
|
||||
const pending = this.saveScoreArchive(successMessage);
|
||||
if (this.reviewMode && successMessage && pending && pending.then) {
|
||||
pending.then(() => this.closeArchive());
|
||||
}
|
||||
submit(this.normalizeArchive()).then(() => {
|
||||
this.$message.success('评分记录已保存');
|
||||
return pending;
|
||||
},
|
||||
deleteScore(index) {
|
||||
this.$confirm('确定删除该评分记录?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
if (!Array.isArray(this.archiveForm.scores)) this.archiveForm.scores = [];
|
||||
this.archiveForm.scores.splice(index, 1);
|
||||
if (this.scoreRecordIndex === index) {
|
||||
this.scoreBox = false;
|
||||
} else if (this.scoreRecordIndex > index) {
|
||||
this.scoreRecordIndex -= 1;
|
||||
}
|
||||
const latestScore = this.getLatestCreditScore(this.archiveForm.scores);
|
||||
if (latestScore) {
|
||||
this.syncArchiveCreditFromScores();
|
||||
} else {
|
||||
this.archiveForm.customerLevel = '';
|
||||
this.archiveForm.maxCreditLimit = '';
|
||||
this.archiveForm.applyCreditLimit = '';
|
||||
}
|
||||
this.handleScoreCurrentChange(this.scorePage.currentPage);
|
||||
this.saveScoreArchive('评分记录已删除', '评分记录已删除,请保存客商档案后生效');
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
saveScoreArchive(
|
||||
successMessage = '评分记录已保存',
|
||||
draftMessage = '评分记录已添加,请保存客商档案后生效'
|
||||
) {
|
||||
if (!this.archiveForm.id) {
|
||||
this.$message.success(draftMessage);
|
||||
return Promise.resolve();
|
||||
}
|
||||
return submit(this.normalizeArchive()).then(() => {
|
||||
this.$message.success(successMessage);
|
||||
});
|
||||
},
|
||||
submitSelfScore() {
|
||||
if (!this.validateScoreForm(true)) return;
|
||||
this.currentScore.selfStatus = '已完成';
|
||||
this.currentScore.reviewStatus = '未完成';
|
||||
this.saveScoreDetail('自评已提交');
|
||||
},
|
||||
confirmReviewScore() {
|
||||
if (!this.validateScoreForm(true)) return;
|
||||
this.currentScore.selfStatus = '已完成';
|
||||
this.currentScore.reviewStatus = '已完成';
|
||||
this.saveScoreDetail();
|
||||
this.$message.success('复评确认成功');
|
||||
this.saveScoreDetail('复评确认成功');
|
||||
},
|
||||
validateScoreForm(requireComplete) {
|
||||
if (!this.currentScore.scoreDate) {
|
||||
@@ -4760,7 +4987,7 @@ export default {
|
||||
if (
|
||||
item.reviewScore === undefined ||
|
||||
item.reviewScore === null ||
|
||||
item.reviewScore === ''
|
||||
String(item.reviewScore).trim() === ''
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -4777,7 +5004,7 @@ export default {
|
||||
return false;
|
||||
}
|
||||
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
|
||||
this.$message.warning('请完整填写基础得分项评分明细');
|
||||
this.$message.warning('请完整填写所有基础项目后再提交自评');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -4832,13 +5059,19 @@ export default {
|
||||
getDetail(row.id).then(res => {
|
||||
const archive = this.normalizeDetail(res.data.data);
|
||||
archive.qualificationAttachments = archive.qualificationAttachments || '';
|
||||
if (!this.validateFormalForApproval(archive)) return;
|
||||
if (!this.validateScoreForApproval(archive)) return;
|
||||
this.$confirm('是否提交客商准入审批?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => submitApproval(row.id))
|
||||
.then(() =>
|
||||
this.submitCustomerApproval(
|
||||
row.id,
|
||||
archive.fullName || row.fullName,
|
||||
archive.approvalStatus || row.approvalStatus
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '提交成功!' });
|
||||
@@ -4939,7 +5172,7 @@ export default {
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
this.$refs.crud?.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
@@ -5332,10 +5565,16 @@ export default {
|
||||
}
|
||||
|
||||
:deep(.el-table__body td) {
|
||||
//min-height: 112px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
:deep(.score-item-name) {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.el-select),
|
||||
:deep(.el-input),
|
||||
:deep(.el-select) {
|
||||
@@ -5370,13 +5609,28 @@ export default {
|
||||
|
||||
.invoice-form {
|
||||
max-width: none;
|
||||
|
||||
:deep(.el-input),
|
||||
:deep(.el-cascader),
|
||||
:deep(.el-select) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-cascader .el-input) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-contact-table {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-input),
|
||||
:deep(.el-select) {
|
||||
:deep(.el-select),
|
||||
:deep(.el-cascader) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-cascader .el-input) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -5508,25 +5762,10 @@ export default {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:deep(.change-record-detail-dialog .el-dialog__body) {
|
||||
max-height: 65vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.change-record-detail-dialog .change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.change-record-detail-meta {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
.customer-archive-page.is-public-view {
|
||||
:deep(.archive-page-form .archive-form__footer) {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.archive-page-form {
|
||||
|
||||
@@ -21,6 +21,15 @@
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
v-if="hasPermission('equipment_ledger_delete')"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasPermission('equipment_ledger_import')"
|
||||
type="primary"
|
||||
@@ -310,6 +319,23 @@ export default {
|
||||
this.$message.success('操作成功');
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确认删除选中的数据?删除后将不可恢复!', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message.success('操作成功');
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
this.boxType = type;
|
||||
if (type === 'add') {
|
||||
|
||||
@@ -217,7 +217,7 @@ import { getList as getOcrTemplateList } from '@/api/base/insurance-ocr-template
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { handleImportExcel } from '@/utils/import-excel';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getUploadHeaders } from '@/utils/upload';
|
||||
import { normalizeSearchRangeParams } from '@/utils/search-range';
|
||||
@@ -540,7 +540,96 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
openImportDialog(this, '保险记录');
|
||||
const column = this.findColumn(this.excelOption.column, 'excelFile');
|
||||
if (!column) {
|
||||
this.$message.error('导入配置异常,请稍后重试');
|
||||
return;
|
||||
}
|
||||
// 上传前先归一化日期列(2026-9-1 → 2026-09-01),兼容两种格式
|
||||
column.httpRequest = async (uploadOption, uploadColumn) => {
|
||||
try {
|
||||
const normalizedFile = await this.normalizeExcelDates(uploadOption.file);
|
||||
await handleImportExcel(
|
||||
this,
|
||||
{ ...uploadOption, file: normalizedFile },
|
||||
uploadColumn,
|
||||
'保险记录',
|
||||
null,
|
||||
{ timeout: 60000 }
|
||||
);
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '导入失败');
|
||||
if (typeof uploadOption.onError === 'function') {
|
||||
uploadOption.onError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.excelBox = true;
|
||||
},
|
||||
// 读取 Excel,把指定日期列统一归一化为 YYYY-MM-DD,再写回新文件
|
||||
async normalizeExcelDates(file) {
|
||||
const rows = await this.readExcelRows(file);
|
||||
const dateHeaders = ['开始日期', '结束日期', '开票日期'];
|
||||
const headerIndex = (rows[0] || []).reduce((map, label, index) => {
|
||||
const key = String(label || '').trim();
|
||||
if (key) map[key] = index;
|
||||
return map;
|
||||
}, {});
|
||||
const targetIndexes = dateHeaders
|
||||
.map(label => headerIndex[label])
|
||||
.filter(index => index !== undefined);
|
||||
if (!targetIndexes.length) return file;
|
||||
rows.forEach((row, rowIndex) => {
|
||||
if (rowIndex === 0) return;
|
||||
targetIndexes.forEach(index => {
|
||||
if (row[index] !== undefined && row[index] !== '') {
|
||||
const normalized = this.normalizeDateString(String(row[index]));
|
||||
if (normalized) row[index] = normalized;
|
||||
}
|
||||
});
|
||||
});
|
||||
return this.writeExcelFile(rows, file.name);
|
||||
},
|
||||
async readExcelRows(file) {
|
||||
const XLSX = await import('xlsx');
|
||||
const buffer = await file.arrayBuffer();
|
||||
const workbook = XLSX.read(buffer, { type: 'array', cellDates: false });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
return XLSX.utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
defval: '',
|
||||
raw: false,
|
||||
blankrows: false,
|
||||
});
|
||||
},
|
||||
async writeExcelFile(rows, fileName) {
|
||||
const XLSX = await import('xlsx');
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(rows), 'Sheet1');
|
||||
const binary = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
||||
return new File([binary], fileName || 'insurance-record.xlsx', {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
});
|
||||
},
|
||||
// 兼容 YYYY-M-D / YYYY-MM-DD / YYYY/M/D / YYYY.年.月.日,非法或已标准格式原样返回 null
|
||||
normalizeDateString(value) {
|
||||
const text = String(value || '').trim();
|
||||
if (!text) return null;
|
||||
const match = text.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
|
||||
if (!match) return null;
|
||||
const year = Number(match[1]);
|
||||
const month = Number(match[2]);
|
||||
const day = Number(match[3]);
|
||||
const date = new Date(year, month - 1, day);
|
||||
if (
|
||||
date.getFullYear() !== year ||
|
||||
date.getMonth() !== month - 1 ||
|
||||
date.getDate() !== day
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
return `${year}-${pad(month)}-${pad(day)}`;
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出保险记录数据?', '提示', {
|
||||
|
||||
@@ -516,7 +516,7 @@ export default {
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件;日期支持 2026-08-02、2026-8-2、2026/8/2 等写法',
|
||||
action: '/blade-transport/maintenance-plan/import-maintenance-plan',
|
||||
},
|
||||
{
|
||||
@@ -564,8 +564,8 @@ export default {
|
||||
this.addressMapPickerVisible = true;
|
||||
}
|
||||
},
|
||||
handleAddressMapConfirm(address) {
|
||||
this.form.address = address;
|
||||
handleAddressMapConfirm(payload) {
|
||||
this.form.address = typeof payload === 'string' ? payload : payload?.address || '';
|
||||
},
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
|
||||
@@ -489,7 +489,7 @@ export default {
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件;日期支持 2026-08-02、2026-8-2、2026/8/2 等写法',
|
||||
action: '/blade-transport/maintenance-record/import-maintenance-record',
|
||||
},
|
||||
{
|
||||
@@ -545,8 +545,8 @@ export default {
|
||||
this.addressMapPickerVisible = true;
|
||||
}
|
||||
},
|
||||
handleAddressMapConfirm(address) {
|
||||
this.form.address = address;
|
||||
handleAddressMapConfirm(payload) {
|
||||
this.form.address = typeof payload === 'string' ? payload : payload?.address || '';
|
||||
},
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
|
||||
@@ -288,8 +288,8 @@ export default {
|
||||
this.locationMapPickerVisible = true;
|
||||
}
|
||||
},
|
||||
handleLocationMapConfirm(address) {
|
||||
this.form.location = address;
|
||||
handleLocationMapConfirm(payload) {
|
||||
this.form.location = typeof payload === 'string' ? payload : payload?.address || '';
|
||||
},
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
|
||||
Reference in New Issue
Block a user