- 取消操作列内 el-link 默认加粗,统一为常规字体粗细,避免字体显得突兀 - 在 element-ui.scss 中新增样式覆盖 el-link 的 font-weight 为 normal - 全站表格开启 Element Plus 全局配置 showOverflowTooltip,统一显示单元格文字省略提示 - Avue 全局 crudOption 和手写表格均启用 showOverflowTooltip,覆盖所有相关表格列 - 通过 CSS 豁免操作列单元格不受 tooltip 固定宽度裁切影响,保证操作列内部链接显示完整 - 调整部分菜单宽度以适配视觉和布局需求 - 验证构建成功,无新增 CSS 警告,确保兼容性和功能正常
3893 lines
133 KiB
Vue
3893 lines
133 KiB
Vue
<template>
|
||
<basic-container class="customer-archive-page">
|
||
<avue-crud
|
||
:option="option"
|
||
:table-loading="loading"
|
||
:data="data"
|
||
v-model:page="page"
|
||
v-model="form"
|
||
ref="crud"
|
||
:permission="permissionList"
|
||
@search-change="searchChange"
|
||
@search-reset="searchReset"
|
||
@selection-change="selectionChange"
|
||
@current-change="currentChange"
|
||
@size-change="sizeChange"
|
||
@refresh-change="refreshChange"
|
||
@on-load="onLoad"
|
||
>
|
||
<template #menu-left>
|
||
<el-button
|
||
type="primary"
|
||
icon="el-icon-plus"
|
||
v-if="hasPermission('customer_archive_add')"
|
||
@click="openArchive()"
|
||
>新增
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
icon="el-icon-download"
|
||
plain
|
||
v-if="hasPermission('customer_archive_export')"
|
||
@click="handleExport"
|
||
>批量导出
|
||
</el-button>
|
||
<el-button
|
||
type="danger"
|
||
icon="el-icon-delete"
|
||
plain
|
||
v-if="hasPermission('customer_archive_delete')"
|
||
@click="handleDelete"
|
||
>批量删除
|
||
</el-button>
|
||
</template>
|
||
<template #customerCode="{ row }">
|
||
<el-button type="primary" link @click="openArchive(row, true)">
|
||
{{ row.customerCode }}
|
||
</el-button>
|
||
</template>
|
||
<template #accessType="{ row }">
|
||
<el-tag :type="row.accessType === 'formal' ? 'success' : 'warning'">
|
||
{{ accessTypeMap[row.accessType] || '临时客商' }}
|
||
</el-tag>
|
||
</template>
|
||
<template #approvalStatus="{ row }">
|
||
<el-tag :type="approvalStatusMap[row.approvalStatus]?.type || 'info'">
|
||
{{ approvalStatusMap[row.approvalStatus]?.label || '草稿' }}
|
||
</el-tag>
|
||
</template>
|
||
<template #status="{ row }">
|
||
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
|
||
{{ row.status === 1 ? '启用' : '停用' }}
|
||
</el-tag>
|
||
</template>
|
||
<template #menu="{ row }">
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('customer_archive_view')"
|
||
@click="openArchive(row, true)"
|
||
>
|
||
查看
|
||
</el-link>
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('customer_archive_edit') && row.approvalStatus !== 'reviewing'"
|
||
@click="openArchive(row)"
|
||
>
|
||
编辑
|
||
</el-link>
|
||
<el-link
|
||
type="primary"
|
||
v-if="
|
||
hasPermission('customer_archive_submit') &&
|
||
['draft', 'rejected'].includes(row.approvalStatus)
|
||
"
|
||
@click="handleSubmitApproval(row)"
|
||
>
|
||
提交
|
||
</el-link>
|
||
<el-link
|
||
type="warning"
|
||
v-if="
|
||
hasPermission('customer_archive_submit') &&
|
||
['draft', 'reviewing'].includes(row.approvalStatus)
|
||
"
|
||
@click="handleWithdrawApproval(row)"
|
||
>
|
||
撤回
|
||
</el-link>
|
||
<el-link
|
||
type="success"
|
||
v-if="hasPermission('customer_archive_approve') && row.approvalStatus === 'reviewing'"
|
||
@click="handleApprove(row)"
|
||
>
|
||
审核通过
|
||
</el-link>
|
||
<el-link
|
||
type="warning"
|
||
v-if="hasPermission('customer_archive_reject') && row.approvalStatus === 'reviewing'"
|
||
@click="handleReject(row)"
|
||
>
|
||
审核不通过
|
||
</el-link>
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('customer_archive_status')"
|
||
@click="handleStatus(row)"
|
||
>
|
||
{{ row.status === 1 ? '停用' : '启用' }}
|
||
</el-link>
|
||
<el-link
|
||
type="danger"
|
||
v-if="hasPermission('customer_archive_delete') && row.approvalStatus === 'draft'"
|
||
@click="rowDel(row)"
|
||
>
|
||
删除
|
||
</el-link>
|
||
</template>
|
||
</avue-crud>
|
||
<empty-pagination
|
||
:page="page"
|
||
@size-change="sizeChange"
|
||
@current-change="currentChange"
|
||
@load="onLoad(page, query)"
|
||
/>
|
||
|
||
<el-dialog
|
||
:title="dialogTitle"
|
||
append-to-body
|
||
v-model="archiveBox"
|
||
width="92%"
|
||
top="4vh"
|
||
class="archive-dialog"
|
||
@closed="resetArchive"
|
||
>
|
||
<el-form
|
||
ref="archiveForm"
|
||
:model="archiveForm"
|
||
:rules="formRules"
|
||
label-position="right"
|
||
label-width="auto"
|
||
:disabled="readonly"
|
||
class="archive-form"
|
||
>
|
||
<div class="section-title dialog-section-title">基本信息</div>
|
||
<div class="archive-form__group">
|
||
<div class="dialog-section-title archive-form__group-title">工商信息</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="6">
|
||
<el-form-item label="客商编号" prop="customerCode">
|
||
<el-input v-model="archiveForm.customerCode" disabled placeholder="保存后自动生成" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="客商性质" prop="customerNature">
|
||
<el-select
|
||
v-model="archiveForm.customerNature"
|
||
placeholder="请输入"
|
||
filterable
|
||
clearable
|
||
>
|
||
<el-option
|
||
v-for="item in customerNatureOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="统一社会信用代码" prop="unifiedCreditCode">
|
||
<el-input
|
||
v-model="archiveForm.unifiedCreditCode"
|
||
placeholder="请输入"
|
||
maxlength="18"
|
||
show-word-limit
|
||
@input="
|
||
archiveForm.unifiedCreditCode = String(
|
||
archiveForm.unifiedCreditCode || ''
|
||
).toUpperCase()
|
||
"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="客商名称" prop="fullName">
|
||
<el-input
|
||
v-model="archiveForm.fullName"
|
||
placeholder="请输入"
|
||
maxlength="100"
|
||
show-word-limit
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="客商类型" prop="customerType">
|
||
<el-select
|
||
v-model="archiveForm.customerType"
|
||
multiple
|
||
placeholder="请选择"
|
||
filterable
|
||
clearable
|
||
>
|
||
<el-option
|
||
v-for="item in customerTypeOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="企业法人" prop="legalPerson">
|
||
<el-input v-model="archiveForm.legalPerson" maxlength="10" show-word-limit />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="经营范围" prop="businessScope" required>
|
||
<el-select v-model="archiveForm.businessScope" multiple filterable clearable>
|
||
<el-option
|
||
v-for="item in businessScopeOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item
|
||
label="营业期限"
|
||
prop="businessEndDate"
|
||
:required="archiveForm.businessTermType === '固定期限'"
|
||
>
|
||
<div class="business-term-field">
|
||
<el-date-picker
|
||
v-model="archiveForm.businessEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
:disabled="archiveForm.businessTermType === '长期'"
|
||
@change="handleBusinessEndDateChange"
|
||
/>
|
||
<el-checkbox
|
||
:model-value="archiveForm.businessTermType === '长期'"
|
||
@change="handleNoFixedTermChange"
|
||
>
|
||
无固定期限
|
||
</el-checkbox>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="助记码" prop="mnemonicCode">
|
||
<el-input v-model="archiveForm.mnemonicCode" maxlength="15" show-word-limit />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="客户简称" prop="shortName">
|
||
<el-input
|
||
v-model="archiveForm.shortName"
|
||
placeholder="请输入"
|
||
maxlength="20"
|
||
show-word-limit
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="地址" prop="registeredDetailAddress" required>
|
||
<div class="archive-form__address">
|
||
<el-cascader
|
||
ref="registeredRegionCascader"
|
||
v-model="archiveForm.registeredRegionPath"
|
||
:options="regionOptions"
|
||
:props="regionCascaderProps"
|
||
:placeholder="archiveForm.registeredRegionName || '请选择省市区'"
|
||
clearable
|
||
filterable
|
||
@change="handleRegisteredRegionChange"
|
||
/>
|
||
<el-input
|
||
v-model="archiveForm.registeredDetailAddress"
|
||
maxlength="255"
|
||
placeholder="请输入详细地址"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="archive-form__group">
|
||
<div class="dialog-section-title archive-form__group-title">联系信息</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="客商负责人" prop="principal">
|
||
<el-input v-model="archiveForm.principal" maxlength="10" show-word-limit />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="联系电话" prop="contactPhone">
|
||
<el-input v-model="archiveForm.contactPhone" maxlength="20" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="所属组织" prop="deptName">
|
||
<el-tree-select
|
||
v-model="archiveForm.deptId"
|
||
:data="deptTree"
|
||
:props="{ label: 'label', value: 'value', children: 'children' }"
|
||
multiple
|
||
node-key="value"
|
||
check-strictly
|
||
filterable
|
||
clearable
|
||
:render-after-expand="false"
|
||
@change="onDeptChange"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="archive-form__group">
|
||
<div class="dialog-section-title archive-form__group-title">财务/信用信息</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="6">
|
||
<el-form-item label="注册资金(万元)" prop="registeredCapital">
|
||
<el-input
|
||
v-model="archiveForm.registeredCapital"
|
||
maxlength="18"
|
||
@input="value => handleDecimalInput('registeredCapital', value)"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="开票税点" prop="invoiceTaxRate">
|
||
<el-input
|
||
v-model="archiveForm.invoiceTaxRate"
|
||
maxlength="5"
|
||
@input="value => handleDecimalInput('invoiceTaxRate', value, 13)"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="客户等级" prop="customerLevel">
|
||
<el-input v-model="archiveForm.customerLevel" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="最大资金使用额度(万元)" prop="maxCreditLimit">
|
||
<el-input
|
||
v-model="archiveForm.maxCreditLimit"
|
||
maxlength="18"
|
||
disabled
|
||
@input="value => handleDecimalInput('maxCreditLimit', value)"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="申请总资金使用额度(万元)" prop="applyCreditLimit">
|
||
<el-input
|
||
v-model="archiveForm.applyCreditLimit"
|
||
maxlength="18"
|
||
disabled
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="archive-form__group">
|
||
<div class="dialog-section-title archive-form__group-title">其他信息</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="24">
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input
|
||
v-model="archiveForm.remark"
|
||
type="textarea"
|
||
maxlength="500"
|
||
show-word-limit
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</el-form>
|
||
|
||
<el-tabs v-model="activeTab" class="archive-tabs">
|
||
<el-tab-pane label="评分表" name="scores">
|
||
<div class="section-head">
|
||
<span></span>
|
||
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="addScore">
|
||
添加评分记录
|
||
</el-button>
|
||
</div>
|
||
<el-table :data="scoreTableData" border class="score-list-table">
|
||
<el-table-column label="序号" width="90" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.__index }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="评分日期" min-width="150" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.scoreDate }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="自评得分" min-width="145" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ formatScoreValue(row.selfScore) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="复评得分" min-width="145" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ formatScoreValue(row.reviewScore) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="信用等级" min-width="150" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.creditLevel }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column min-width="185" align="center">
|
||
<template #header>
|
||
<span>最大资金使用额度(万<br />元)</span>
|
||
</template>
|
||
<template #default="{ row }">
|
||
<span>{{ formatScoreValue(row.maxCreditLimit) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column min-width="190" align="center">
|
||
<template #header>
|
||
<span>拟申请总资金使用额<br />度(万元)</span>
|
||
</template>
|
||
<template #default="{ row }">
|
||
<span>{{ formatScoreValue(row.applyCreditLimit) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="自评状态" min-width="130" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.selfStatus }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="复评状态" min-width="130" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.reviewStatus }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" min-width="135" align="center">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="openScore(row.__raw, row.__rawIndex)">
|
||
详情
|
||
</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<empty-pagination
|
||
:page="scorePaginationPage"
|
||
always-show
|
||
@size-change="handleScoreSizeChange"
|
||
@current-change="handleScoreCurrentChange"
|
||
/>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="联系人" name="contacts">
|
||
<div class="section-head">
|
||
<span></span>
|
||
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openContact()">
|
||
新增联系人
|
||
</el-button>
|
||
</div>
|
||
<el-table :data="contactTableData" border class="contact-table">
|
||
<el-table-column label="序号" prop="__index" width="90" align="center" />
|
||
<el-table-column label="联系人姓名" prop="contactName" min-width="150" />
|
||
<el-table-column label="联系人电话" prop="contactPhone" min-width="150" />
|
||
<el-table-column label="邮箱" prop="email" min-width="180" />
|
||
<el-table-column label="联系地址" min-width="240">
|
||
<template #default="{ row }">
|
||
<span>{{ formatContactAddress(row) || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="备注" prop="remark" min-width="180" />
|
||
<el-table-column label="所属分公司/事业部" min-width="170">
|
||
<template #default="{ row }">
|
||
<span>{{ row.branchName || archiveForm.deptName || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="130" align="center" v-if="!readonly">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="openContact(row.__raw, row.__rawIndex)">
|
||
修改
|
||
</el-link>
|
||
<el-link type="danger" @click="deleteContact(row.__rawIndex)">删除</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<empty-pagination
|
||
:page="contactPaginationPage"
|
||
always-show
|
||
@size-change="size => handleDetailSizeChange('contact', size)"
|
||
@current-change="page => handleDetailCurrentChange('contact', page)"
|
||
/>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="收款信息" name="receiptAccounts">
|
||
<div class="section-head">
|
||
<span></span>
|
||
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openReceipt()">
|
||
新增收款信息
|
||
</el-button>
|
||
</div>
|
||
<el-table :data="receiptTableData" border class="receipt-table">
|
||
<el-table-column label="序号" prop="__index" width="90" align="center" />
|
||
<el-table-column label="收款单位名称" prop="accountName" min-width="180" />
|
||
<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="remark" min-width="180" />
|
||
<el-table-column label="操作" width="130" align="center" v-if="!readonly">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="openReceipt(row.__raw, row.__rawIndex)">
|
||
修改
|
||
</el-link>
|
||
<el-link type="danger" @click="deleteReceipt(row.__rawIndex)">删除</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<empty-pagination
|
||
:page="receiptPaginationPage"
|
||
always-show
|
||
@size-change="size => handleDetailSizeChange('receipt', size)"
|
||
@current-change="page => handleDetailCurrentChange('receipt', page)"
|
||
/>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="发票信息" name="invoices">
|
||
<div class="section-head">
|
||
<span></span>
|
||
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openInvoice()">
|
||
新增发票信息
|
||
</el-button>
|
||
</div>
|
||
<el-table :data="invoiceTableData" border class="invoice-table">
|
||
<el-table-column label="序号" prop="__index" width="90" align="center" />
|
||
<el-table-column label="收票方名称" prop="invoiceTitle" min-width="180" />
|
||
<el-table-column label="纳税人识别号" prop="taxNo" min-width="180" />
|
||
<el-table-column label="开户行名称" prop="bankName" min-width="180" />
|
||
<el-table-column label="注册电话" prop="registeredPhone" min-width="150" />
|
||
<el-table-column label="银行账号" prop="bankAccount" min-width="180" />
|
||
<el-table-column label="注册地址" min-width="220">
|
||
<template #default="{ row }">
|
||
<span>{{ formatInvoiceRegisteredAddress(row) || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="邮箱" prop="email" min-width="180" />
|
||
<el-table-column label="操作" width="130" align="center" v-if="!readonly">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="openInvoice(row.__raw, row.__rawIndex)">
|
||
修改
|
||
</el-link>
|
||
<el-link type="danger" @click="deleteInvoice(row.__rawIndex)">删除</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<empty-pagination
|
||
:page="invoicePaginationPage"
|
||
always-show
|
||
@size-change="size => handleDetailSizeChange('invoice', size)"
|
||
@current-change="page => handleDetailCurrentChange('invoice', page)"
|
||
/>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<div class="customer-material-section">
|
||
<div class="section-head">
|
||
<div class="section-title dialog-section-title">客商材料</div>
|
||
<div v-if="!readonly" class="section-actions">
|
||
<el-button icon="el-icon-download" @click="downloadAttachments">批量下载</el-button>
|
||
<el-button type="primary" icon="el-icon-plus" @click="addAttachment"
|
||
>普通上传</el-button
|
||
>
|
||
<el-button type="primary" icon="el-icon-upload" @click="addAttachment">
|
||
OCR上传识别
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<el-alert
|
||
type="info"
|
||
:closable="false"
|
||
title="支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件大小限制500M"
|
||
class="archive-rule-alert"
|
||
/>
|
||
<el-alert
|
||
v-if="missingQualificationText"
|
||
type="error"
|
||
:closable="false"
|
||
:title="missingQualificationText"
|
||
class="archive-rule-alert"
|
||
/>
|
||
<el-table :data="qualificationFiles" border>
|
||
<el-table-column label="序号" type="index" width="90" align="center" />
|
||
<el-table-column label="附件类型" min-width="160">
|
||
<template #default="{ row }">
|
||
<el-select v-model="row.type" :disabled="readonly" filterable>
|
||
<el-option label="法人身份证" value="法人身份证" />
|
||
<el-option label="道路运输许可证" value="道路运输许可证" />
|
||
<el-option label="其他附件" value="其他附件" />
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="文件名称" min-width="180">
|
||
<template #default="{ row }">
|
||
<vehicle-attachment-upload
|
||
v-model="row.files"
|
||
:readonly="readonly"
|
||
:multiple="false"
|
||
accept=".pdf,.bmp,.jpeg,.png,.jpg,.doc,.docx,.ppt,.pptx,.xlsx,.xls,.eml,.msg,.zip"
|
||
:max-size="500"
|
||
button-text="上传文件"
|
||
:show-tip="false"
|
||
@success="file => handleQualificationUploadSuccess(file, row)"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="附件描述" min-width="220">
|
||
<template #default="{ row }">
|
||
<el-input v-model="row.description" :disabled="readonly" maxlength="200" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="文件大小" min-width="120">
|
||
<template #default="{ row }">
|
||
<span>{{ row.size || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="上传人" min-width="120">
|
||
<template #default="{ row }">
|
||
<span>{{ row.uploadUserName || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="上传时间" min-width="170">
|
||
<template #default="{ row }">
|
||
<span>{{ row.uploadTime || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="90" align="center" v-if="!readonly">
|
||
<template #default="{ $index }">
|
||
<el-link type="danger" @click="qualificationFiles.splice($index, 1)">
|
||
删除
|
||
</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<div class="change-record-section">
|
||
<div class="section-title dialog-section-title">变更记录</div>
|
||
<el-table :data="archiveForm.changeRecords" border>
|
||
<el-table-column label="序号" type="index" width="90" align="center" />
|
||
<el-table-column label="变更日期" prop="changeTime" min-width="170" />
|
||
<el-table-column label="变更内容" prop="changeContent" min-width="260" />
|
||
<el-table-column label="变更账号" prop="changeUserName" min-width="160" />
|
||
</el-table>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<el-button @click="archiveBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
|
||
<el-button type="primary" v-if="!readonly" @click="saveArchive">保存</el-button>
|
||
<el-button type="success" v-if="!readonly" @click="saveAndSubmitArchive">提交</el-button>
|
||
<el-button v-if="!readonly" @click="archiveBox = false">返回</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:title="contactDialogTitle"
|
||
append-to-body
|
||
v-model="contactBox"
|
||
width="680px"
|
||
class="contact-dialog"
|
||
@closed="resetContact"
|
||
>
|
||
<el-form
|
||
ref="contactForm"
|
||
:model="contactForm"
|
||
:rules="contactRules"
|
||
label-position="right"
|
||
label-width="110px"
|
||
class="contact-form"
|
||
>
|
||
<el-form-item label="联系人姓名" prop="contactName" class="contact-form__compact-field">
|
||
<el-input v-model="contactForm.contactName" maxlength="30" />
|
||
</el-form-item>
|
||
<el-form-item label="手机号" prop="contactPhone" class="contact-form__compact-field">
|
||
<el-input v-model="contactForm.contactPhone" maxlength="11" />
|
||
</el-form-item>
|
||
<el-form-item label="邮箱" prop="email" class="contact-form__compact-field">
|
||
<el-input v-model="contactForm.email" maxlength="100" />
|
||
</el-form-item>
|
||
<el-form-item label="地址" prop="detailAddress">
|
||
<div class="contact-form__address">
|
||
<el-input
|
||
v-model="contactForm.regionName"
|
||
readonly
|
||
maxlength="100"
|
||
placeholder="行政区划自动带出"
|
||
/>
|
||
<el-input
|
||
v-model="contactForm.detailAddress"
|
||
readonly
|
||
maxlength="255"
|
||
placeholder="点击后弹出地图组件"
|
||
suffix-icon="el-icon-location"
|
||
@click="handleContactMapPick"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark" class="contact-form__compact-field">
|
||
<el-input v-model="contactForm.remark" maxlength="200" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="contact-dialog__footer-actions">
|
||
<el-button type="primary" @click="saveContact">保存</el-button>
|
||
<el-button @click="contactBox = false">返回</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
title="地图选择行政区划"
|
||
append-to-body
|
||
v-model="contactMapBox"
|
||
width="920px"
|
||
class="contact-form__map-dialog"
|
||
@opened="initContactAmap"
|
||
>
|
||
<div class="contact-form__map-toolbar">
|
||
<el-input
|
||
v-model="contactMapKeyword"
|
||
clearable
|
||
placeholder="输入地址关键词"
|
||
@keyup.enter="searchContactMapKeyword"
|
||
/>
|
||
<el-button
|
||
type="primary"
|
||
icon="el-icon-search"
|
||
:loading="contactMapLoading"
|
||
@click="searchContactMapKeyword"
|
||
>
|
||
搜索
|
||
</el-button>
|
||
</div>
|
||
<div ref="contactAmap" class="contact-form__map"></div>
|
||
<div class="contact-form__map-info">
|
||
<span>{{ contactMapStatus }}</span>
|
||
<span v-if="contactMapSelected.regionName">
|
||
行政区划:{{ contactMapSelected.regionName }}
|
||
</span>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="contactMapBox = false">取消</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:disabled="!contactMapSelected.longitude"
|
||
@click="confirmContactMapPick"
|
||
>
|
||
确定
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:title="receiptDialogTitle"
|
||
append-to-body
|
||
v-model="receiptBox"
|
||
width="640px"
|
||
class="receipt-dialog"
|
||
@closed="resetReceipt"
|
||
>
|
||
<el-form
|
||
ref="receiptForm"
|
||
:model="receiptForm"
|
||
:rules="receiptRules"
|
||
label-position="right"
|
||
label-width="120px"
|
||
class="receipt-form"
|
||
>
|
||
<el-form-item label="收款单位名称" prop="accountName">
|
||
<el-input v-model="receiptForm.accountName" maxlength="100" />
|
||
</el-form-item>
|
||
<el-form-item label="开户人姓名" prop="accountHolderName">
|
||
<el-input v-model="receiptForm.accountHolderName" maxlength="50" />
|
||
</el-form-item>
|
||
<el-form-item label="开户行" prop="bankName">
|
||
<el-select v-model="receiptForm.bankName" filterable clearable>
|
||
<el-option
|
||
v-for="item in bankListOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="收款账号" prop="bankAccount">
|
||
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input v-model="receiptForm.remark" maxlength="200" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="receipt-dialog__footer-actions">
|
||
<el-button type="primary" @click="saveReceipt">保存</el-button>
|
||
<el-button @click="receiptBox = false">返回</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:title="invoiceDialogTitle"
|
||
append-to-body
|
||
v-model="invoiceBox"
|
||
width="84%"
|
||
class="invoice-dialog"
|
||
@closed="resetInvoice"
|
||
>
|
||
<el-form
|
||
ref="invoiceForm"
|
||
:model="invoiceForm"
|
||
:rules="invoiceRules"
|
||
label-position="right"
|
||
label-width="120px"
|
||
class="invoice-form"
|
||
>
|
||
<div class="invoice-form__section-title">发票信息</div>
|
||
<el-row :gutter="34">
|
||
<el-col :span="12">
|
||
<el-form-item label="纳税人识别号" prop="taxNo">
|
||
<el-input v-model="invoiceForm.taxNo" maxlength="30" />
|
||
</el-form-item>
|
||
<el-form-item label="注册电话" prop="registeredPhone">
|
||
<el-input v-model="invoiceForm.registeredPhone" maxlength="20" />
|
||
</el-form-item>
|
||
<el-form-item label="注册地址" prop="registeredRegionName">
|
||
<el-input
|
||
v-model="invoiceForm.registeredRegionName"
|
||
readonly
|
||
maxlength="100"
|
||
placeholder="点击后弹出地图组件"
|
||
suffix-icon="el-icon-location"
|
||
@click="handleInvoiceRegisteredMapPick"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="详细地址" prop="registeredDetailAddress">
|
||
<el-input
|
||
v-model="invoiceForm.registeredDetailAddress"
|
||
readonly
|
||
maxlength="255"
|
||
placeholder="点击后弹出地图组件"
|
||
suffix-icon="el-icon-location"
|
||
@click="handleInvoiceRegisteredMapPick"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="收票方名称" prop="invoiceTitle">
|
||
<el-input v-model="invoiceForm.invoiceTitle" maxlength="100" />
|
||
</el-form-item>
|
||
<el-form-item label="开户行名称" prop="bankName">
|
||
<el-select v-model="invoiceForm.bankName" filterable clearable>
|
||
<el-option
|
||
v-for="item in bankListOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="银行账号" prop="bankAccount">
|
||
<el-input v-model="invoiceForm.bankAccount" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<div class="invoice-form__section-title">邮寄信息</div>
|
||
<el-row :gutter="34">
|
||
<el-col :span="12">
|
||
<el-form-item label="收件人姓名" prop="receiverName">
|
||
<el-input v-model="invoiceForm.receiverName" maxlength="30" />
|
||
</el-form-item>
|
||
<el-form-item label="收件人地址" prop="receiverRegionName">
|
||
<div class="invoice-form__address">
|
||
<el-input
|
||
v-model="invoiceForm.receiverRegionName"
|
||
maxlength="100"
|
||
placeholder="地址控件选择"
|
||
/>
|
||
<el-input
|
||
v-model="invoiceForm.receiverDetailAddress"
|
||
maxlength="255"
|
||
placeholder="请输入详细地址"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="收件人电话" prop="receiverPhone">
|
||
<el-input v-model="invoiceForm.receiverPhone" maxlength="20" />
|
||
</el-form-item>
|
||
<el-form-item label="邮箱" prop="email">
|
||
<el-input v-model="invoiceForm.email" maxlength="100" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="invoice-dialog__footer-actions">
|
||
<el-button type="primary" @click="saveInvoice">保存</el-button>
|
||
<el-button @click="invoiceBox = false">返回</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
title="评分详情表"
|
||
append-to-body
|
||
v-model="scoreBox"
|
||
width="92%"
|
||
top="3vh"
|
||
class="score-detail-dialog"
|
||
@closed="resetScoreDialog"
|
||
>
|
||
<el-form
|
||
ref="scoreForm"
|
||
:model="currentScore"
|
||
label-position="right"
|
||
label-width="auto"
|
||
:disabled="readonly"
|
||
class="score-detail-form"
|
||
>
|
||
<el-row :gutter="42">
|
||
<el-col :span="12">
|
||
<el-form-item label="评定日期" required>
|
||
<el-date-picker
|
||
v-model="currentScore.scoreDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="拟申请总资金使用额度(万元)" required>
|
||
<el-input
|
||
v-model="currentScore.applyCreditLimit"
|
||
maxlength="18"
|
||
@input="value => handleScoreDecimalInput('applyCreditLimit', value)"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="最终得分">
|
||
<el-input :model-value="formatScoreValue(currentScore.finalScore)" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="临时额度申请使用期限">
|
||
<div class="score-detail-form__range">
|
||
<el-date-picker
|
||
v-model="currentScore.tempCreditStartDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
<span>至</span>
|
||
<el-date-picker
|
||
v-model="currentScore.tempCreditEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="最大资金使用额度(万元)">
|
||
<el-input
|
||
:model-value="formatScoreValue(currentScore.maxCreditLimit)"
|
||
disabled
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="信用等级">
|
||
<el-input :model-value="formatScoreValue(currentScore.creditLevel)" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="临时申请额度">
|
||
<el-input
|
||
v-model="currentScore.tempApplyCreditLimit"
|
||
maxlength="18"
|
||
@input="value => handleScoreDecimalInput('tempApplyCreditLimit', value)"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="备注">
|
||
<el-input v-model="currentScore.remark" maxlength="200" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="评分量化表" required>
|
||
<el-select
|
||
v-model="currentScore.quantificationId"
|
||
filterable
|
||
clearable
|
||
:loading="scoreQuantificationLoading"
|
||
placeholder="请选择评分量化表"
|
||
@change="handleScoreQuantificationChange"
|
||
>
|
||
<el-option
|
||
v-for="item in scoreQuantificationOptions"
|
||
:key="item.id"
|
||
:label="item.name"
|
||
:value="item.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
|
||
<el-table
|
||
v-if="currentScore.quantificationId"
|
||
ref="scoreCategoryTable"
|
||
:data="scoreCategoryRows"
|
||
border
|
||
:show-header="false"
|
||
row-key="categoryCode"
|
||
:expand-row-keys="expandedScoreCategoryKeys"
|
||
class="score-category-table"
|
||
@row-click="toggleScoreCategory"
|
||
@expand-change="handleScoreCategoryExpandChange"
|
||
>
|
||
<el-table-column type="expand" width="1" class-name="score-category-table__expand-column">
|
||
<template #default="{ row }">
|
||
<el-table
|
||
:data="getScoreCategoryDetails(row.categoryCode)"
|
||
border
|
||
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="评分标准" min-width="430">
|
||
<template #default="{ row: detail }">
|
||
<div class="score-standard-cell">
|
||
<div class="score-standard-cell__label">请选择:</div>
|
||
<el-select
|
||
v-model="detail.selectedOption"
|
||
filterable
|
||
:disabled="readonly"
|
||
@change="scoreOptionChange(detail, $event)"
|
||
>
|
||
<el-option
|
||
v-for="item in parseScoreOptions(detail.optionsJson)"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="得分说明" prop="scoreDescription" min-width="160" />
|
||
<el-table-column label="自评得分" width="130" align="center">
|
||
<template #default="{ row: detail }">
|
||
<span>{{ formatScoreValue(detail.selfScore) }}分</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="复评得分" width="150" align="center">
|
||
<template #default="{ row: detail }">
|
||
<el-input
|
||
v-model="detail.reviewScore"
|
||
maxlength="10"
|
||
:disabled="readonly"
|
||
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
|
||
>
|
||
<template #suffix>分</template>
|
||
</el-input>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="序号" prop="index" width="50" align="center" />
|
||
<el-table-column min-width="560">
|
||
<template #default="{ row }">
|
||
<span class="score-category-table__name">{{ row.categoryName }}</span>
|
||
<span>自评:{{ row.selfScore }}分</span>
|
||
<span>复评:{{ row.reviewScore }}分</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column width="60" align="center">
|
||
<template #default="{ row }">
|
||
<el-icon
|
||
class="score-category-table__arrow"
|
||
:class="{ 'is-expanded': isScoreCategoryExpanded(row.categoryCode) }"
|
||
>
|
||
<ArrowRight />
|
||
</el-icon>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="score-material-section">
|
||
<div class="score-material-section__head">
|
||
<div class="dialog-section-title">证明材料</div>
|
||
<vehicle-attachment-upload
|
||
v-if="!readonly"
|
||
v-model="scoreAttachmentFiles"
|
||
button-text="上传附件"
|
||
button-icon="el-icon-upload"
|
||
:show-tip="false"
|
||
@success="handleScoreAttachmentUploadSuccess"
|
||
/>
|
||
</div>
|
||
<el-table :data="scoreAttachmentFiles" border class="score-material-table">
|
||
<el-table-column label="序号" type="index" width="90" align="center" />
|
||
<el-table-column label="文件名" prop="name" min-width="220" />
|
||
<el-table-column label="附件描述" prop="description" min-width="220" />
|
||
<el-table-column label="文件大小" min-width="120" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ formatAttachmentSize(row.size) || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="上传人" min-width="140" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.uploadUserName || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="上传时间" min-width="180" align="center">
|
||
<template #default="{ row }">
|
||
<span>{{ row.uploadTime || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="120" align="center" v-if="!readonly">
|
||
<template #default="{ $index }">
|
||
<el-link type="primary" @click="deleteScoreAttachment($index)">
|
||
删除
|
||
</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<el-button type="primary" v-if="!readonly" @click="saveScoreDetail">保存</el-button>
|
||
<el-button type="primary" v-if="!readonly" @click="confirmReviewScore">复评确认</el-button>
|
||
<el-button type="primary" @click="scoreBox = false">关闭</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</basic-container>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
getList,
|
||
getDetail,
|
||
submit,
|
||
submitApproval,
|
||
withdrawApproval,
|
||
approve,
|
||
reject,
|
||
changeStatus,
|
||
remove,
|
||
getScoreTemplate,
|
||
} from '@/api/vehicle/customer-archive';
|
||
import {
|
||
getList as getCreditScoreQuantificationList,
|
||
getDetail as getCreditScoreQuantificationDetail,
|
||
} from '@/api/vehicle/credit-score-quantification';
|
||
import { getDictionary } from '@/api/system/dictbiz';
|
||
import { getDeptTree } from '@/api/system/dept';
|
||
import { getLazyTree } from '@/api/base/region';
|
||
import { exportBlob } from '@/api/common';
|
||
import { downloadXls } from '@/utils/util';
|
||
import { getToken } from '@/utils/auth';
|
||
import { getUploadHeaders } from '@/utils/upload';
|
||
import { normalizeSearchRangeParams } from '@/utils/search-range';
|
||
import { ArrowRight } from '@element-plus/icons-vue';
|
||
import { mapGetters } from 'vuex';
|
||
import NProgress from 'nprogress';
|
||
import 'nprogress/nprogress.css';
|
||
|
||
const createTimeRangeMap = {
|
||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||
};
|
||
|
||
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
||
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||
let amapLoader;
|
||
|
||
export default {
|
||
components: {
|
||
ArrowRight,
|
||
},
|
||
data() {
|
||
const nonNegativeLabelMap = {
|
||
invoiceTaxRate: '开票税点',
|
||
registeredCapital: '注册资金',
|
||
maxCreditLimit: '最大资金使用额度',
|
||
applyCreditLimit: '申请总资金使用额度',
|
||
};
|
||
const validateNonNegative = (rule, value, callback) => {
|
||
if (value === undefined || value === null || value === '') {
|
||
callback();
|
||
} else if (Number(value) < 0) {
|
||
callback(new Error(`${nonNegativeLabelMap[rule.field] || '数值'}不能小于0`));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
const validateInvoiceTaxRate = (rule, value, callback) => {
|
||
if (value === undefined || value === null || value === '') {
|
||
callback();
|
||
} else if (Number(value) < 0 || Number(value) > 13) {
|
||
callback(new Error('开票税点必须在0到13之间'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
const validateBusinessEndDate = (rule, value, callback) => {
|
||
if (this.archiveForm.businessTermType === '固定期限' && !value) {
|
||
callback(new Error('请选择营业期限截止日'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
const validateRegisteredAddress = (rule, value, callback) => {
|
||
const regionName = (this.archiveForm.registeredRegionName || '').trim();
|
||
const detailAddress = String(value || '').trim();
|
||
if (!regionName) {
|
||
callback(new Error('请选择省市区'));
|
||
} else if (!detailAddress) {
|
||
callback(new Error('请输入详细地址'));
|
||
} else if (detailAddress.length > 255) {
|
||
callback(new Error('详细地址最多255个字符'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
return {
|
||
form: {},
|
||
query: {},
|
||
deptSearchInitialized: false,
|
||
loading: true,
|
||
data: [],
|
||
page: {
|
||
pageSize: 10,
|
||
pageSizes: [10, 20, 50, 100],
|
||
currentPage: 1,
|
||
total: 0,
|
||
},
|
||
selectionList: [],
|
||
archiveBox: false,
|
||
scoreBox: false,
|
||
contactBox: false,
|
||
receiptBox: false,
|
||
invoiceBox: false,
|
||
readonly: false,
|
||
activeTab: 'scores',
|
||
archiveForm: this.emptyArchive(),
|
||
currentScore: { details: [] },
|
||
scoreRecordIndex: -1,
|
||
scoreAttachmentFiles: [],
|
||
expandedScoreCategoryKeys: [],
|
||
scoreQuantificationLoading: false,
|
||
scoreQuantificationOptions: [],
|
||
scorePage: {
|
||
currentPage: 1,
|
||
pageSize: 3,
|
||
pageSizes: [3, 10, 20, 50],
|
||
},
|
||
contactPage: {
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
pageSizes: [10, 20, 50, 100],
|
||
},
|
||
receiptPage: {
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
pageSizes: [10, 20, 50, 100],
|
||
},
|
||
invoicePage: {
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
pageSizes: [10, 20, 50, 100],
|
||
},
|
||
contactIndex: -1,
|
||
contactForm: this.emptyContact(),
|
||
contactMapBox: false,
|
||
contactMapLoading: false,
|
||
contactMapKeyword: '',
|
||
contactMapStatus: '可搜索地址或点击地图选点',
|
||
contactMapSelected: {},
|
||
contactMapTarget: 'contact',
|
||
contactAmap: null,
|
||
contactAmapMarker: null,
|
||
contactAmapGeocoder: null,
|
||
receiptIndex: -1,
|
||
receiptForm: this.emptyReceiptAccount(),
|
||
invoiceIndex: -1,
|
||
invoiceForm: this.emptyInvoice(),
|
||
qualificationFiles: [],
|
||
deptTree: [],
|
||
deptOptions: [],
|
||
regionOptions: [],
|
||
customerNatureOptions: [],
|
||
customerTypeOptions: [],
|
||
businessScopeOptions: [],
|
||
bankListOptions: [],
|
||
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
|
||
label: `${item}级`,
|
||
value: `${item}级`,
|
||
})),
|
||
accessTypeMap: {
|
||
temporary: '临时客商',
|
||
formal: '正式客商',
|
||
},
|
||
approvalStatusOptions: [
|
||
{ label: '草稿', value: 'draft' },
|
||
{ label: '审核中', value: 'reviewing' },
|
||
{ label: '审核通过', value: 'approved' },
|
||
{ label: '审核不通过', value: 'rejected' },
|
||
],
|
||
approvalStatusMap: {
|
||
draft: { label: '草稿', type: 'info' },
|
||
reviewing: { label: '审核中', type: 'warning' },
|
||
approved: { label: '审核通过', type: 'success' },
|
||
rejected: { label: '审核不通过', type: 'danger' },
|
||
},
|
||
formRules: {
|
||
fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }],
|
||
customerNature: [{ required: true, message: '请选择客商性质', trigger: 'change' }],
|
||
unifiedCreditCode: [
|
||
{ required: true, message: '请输入统一社会信用代码', trigger: 'blur' },
|
||
{
|
||
pattern: /^[A-Z0-9]{18}$/,
|
||
message: '统一社会信用代码必须为18位大写字母或数字',
|
||
trigger: 'blur',
|
||
},
|
||
],
|
||
customerType: [{ required: true, message: '请选择客商类型', trigger: 'change' }],
|
||
legalPerson: [{ required: true, message: '请输入企业法人', trigger: 'blur' }],
|
||
contactPhone: [
|
||
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||
{ pattern: /^1\d{10}$/, message: '联系电话必须为11位手机号', trigger: 'blur' },
|
||
],
|
||
deptName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
||
invoiceTaxRate: [{ validator: validateInvoiceTaxRate, trigger: 'blur' }],
|
||
registeredCapital: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||
maxCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||
applyCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||
businessEndDate: [{ validator: validateBusinessEndDate, trigger: 'change' }],
|
||
registeredDetailAddress: [
|
||
{ validator: validateRegisteredAddress, trigger: ['change', 'blur'] },
|
||
],
|
||
businessScope: [
|
||
{
|
||
required: true,
|
||
type: 'array',
|
||
min: 1,
|
||
message: '请选择经营范围',
|
||
trigger: 'change',
|
||
},
|
||
],
|
||
remark: [{ max: 500, message: '备注最多500个字符', trigger: 'blur' }],
|
||
},
|
||
contactRules: {
|
||
contactName: [{ required: true, message: '请输入联系人姓名', trigger: 'blur' }],
|
||
contactPhone: [
|
||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||
{ pattern: /^1\d{10}$/, message: '手机号必须为11位手机号', trigger: 'blur' },
|
||
],
|
||
email: [{ type: 'email', message: '请输入正确的邮箱', trigger: 'blur' }],
|
||
detailAddress: [{ max: 255, message: '详细地址最多255个字符', trigger: 'blur' }],
|
||
remark: [{ max: 200, message: '备注最多200个字符', trigger: 'blur' }],
|
||
},
|
||
receiptRules: {
|
||
accountName: [{ required: true, message: '请输入收款单位名称', trigger: 'blur' }],
|
||
accountHolderName: [{ required: true, message: '请输入开户人姓名', trigger: 'blur' }],
|
||
bankName: [{ required: true, message: '请输入开户行', trigger: 'blur' }],
|
||
bankAccount: [{ required: true, message: '请输入收款账号', trigger: 'blur' }],
|
||
remark: [{ max: 200, message: '备注最多200个字符', trigger: 'blur' }],
|
||
},
|
||
invoiceRules: {
|
||
taxNo: [{ required: true, message: '请输入纳税人识别号', trigger: 'blur' }],
|
||
invoiceTitle: [{ required: true, message: '请输入收票方名称', trigger: 'blur' }],
|
||
registeredPhone: [{ required: true, message: '请输入注册电话', trigger: 'blur' }],
|
||
bankName: [{ required: true, message: '请输入开户行名称', trigger: 'blur' }],
|
||
registeredRegionName: [
|
||
{ required: true, message: '请选择注册地址', trigger: 'blur' },
|
||
],
|
||
registeredDetailAddress: [
|
||
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
||
{ max: 255, message: '详细地址最多255个字符', trigger: 'blur' },
|
||
],
|
||
bankAccount: [{ required: true, message: '请输入银行账号', trigger: 'blur' }],
|
||
receiverDetailAddress: [
|
||
{ max: 255, message: '收件人详细地址最多255个字符', trigger: 'blur' },
|
||
],
|
||
email: [{ type: 'email', message: '请输入正确的邮箱', trigger: 'blur' }],
|
||
},
|
||
option: {
|
||
height: 'auto',
|
||
calcHeight: 32,
|
||
tip: false,
|
||
searchBtnText: '查询',
|
||
emptyBtnText: '重置',
|
||
searchShow: true,
|
||
searchMenuSpan: 24,
|
||
searchIcon: true,
|
||
searchIndex: 4,
|
||
searchMenuPosition: 'right',
|
||
border: true,
|
||
index: true,
|
||
indexLabel: '序号',
|
||
indexWidth: 90,
|
||
addBtn: false,
|
||
viewBtn: false,
|
||
delBtn: false,
|
||
editBtn: false,
|
||
selection: true,
|
||
dialogClickModal: false,
|
||
menuWidth: 260,
|
||
column: [
|
||
{
|
||
label: '客商编号',
|
||
prop: 'customerCode',
|
||
slot: true,
|
||
search: true,
|
||
searchOrder: 10,
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 140,
|
||
},
|
||
{
|
||
label: '客商简称',
|
||
prop: 'shortName',
|
||
search: true,
|
||
searchOrder: 2,
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 150,
|
||
},
|
||
{
|
||
label: '客商名称',
|
||
prop: 'fullName',
|
||
search: true,
|
||
searchOrder: 9,
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 220,
|
||
overHidden: true,
|
||
},
|
||
{
|
||
label: '客户类型',
|
||
prop: 'customerType',
|
||
search: true,
|
||
searchType: 'select',
|
||
searchOrder: 7,
|
||
searchValue: '',
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 140,
|
||
dicData: [{ label: '全部', value: '' }],
|
||
},
|
||
{
|
||
label: '客户性质',
|
||
prop: 'customerNature',
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 5,
|
||
searchValue: '',
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 120,
|
||
dicData: [
|
||
{ label: '全部', value: '' },
|
||
{ label: '有限公司', value: '有限公司' },
|
||
{ label: '个体工商户', value: '个体工商户' },
|
||
],
|
||
},
|
||
{
|
||
label: '统一信用代码',
|
||
prop: 'unifiedCreditCode',
|
||
search: true,
|
||
searchOrder: 3,
|
||
searchPlaceholder: '请输入',
|
||
minWidth: 190,
|
||
},
|
||
{
|
||
label: '所属组织',
|
||
prop: 'deptName',
|
||
search: true,
|
||
searchType: 'select',
|
||
searchOrder: 1,
|
||
searchValue: '',
|
||
minWidth: 180,
|
||
dicData: [{ label: '全部', value: '' }],
|
||
},
|
||
{
|
||
label: '准入标准',
|
||
prop: 'accessType',
|
||
slot: true,
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 8,
|
||
minWidth: 120,
|
||
dicData: [
|
||
{ label: '临时', value: 'temporary' },
|
||
{ label: '正式', value: 'formal' },
|
||
],
|
||
},
|
||
{
|
||
label: '审批状态',
|
||
prop: 'approvalStatus',
|
||
slot: true,
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 4,
|
||
searchValue: '',
|
||
minWidth: 130,
|
||
dicData: [
|
||
{ label: '全部', value: '' },
|
||
{ label: '草稿', value: 'draft' },
|
||
{ label: '审核中', value: 'reviewing' },
|
||
{ label: '审核通过', value: 'approved' },
|
||
{ label: '审核不通过', value: 'rejected' },
|
||
],
|
||
},
|
||
{ label: '当前节点', prop: 'currentNode', minWidth: 150 },
|
||
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 150 },
|
||
{
|
||
label: '审核通过时间',
|
||
prop: 'approvedTime',
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
minWidth: 170,
|
||
},
|
||
{
|
||
label: '状态',
|
||
prop: 'status',
|
||
slot: true,
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 6,
|
||
searchValue: '',
|
||
dataType: 'number',
|
||
minWidth: 100,
|
||
dicData: [
|
||
{ label: '全部', value: '' },
|
||
{ label: '启用', value: 1 },
|
||
{ label: '停用', value: 2 },
|
||
],
|
||
clearable: true,
|
||
},
|
||
],
|
||
},
|
||
};
|
||
},
|
||
created() {
|
||
this.initDeptTree();
|
||
this.initRegionOptions();
|
||
this.initBusinessDictionaries();
|
||
this.initScoreQuantificationOptions();
|
||
},
|
||
computed: {
|
||
...mapGetters(['permission', 'userInfo']),
|
||
isAdmin() {
|
||
const authority = this.userInfo.authority || '';
|
||
return authority.includes('admin');
|
||
},
|
||
permissionList() {
|
||
return {
|
||
addBtn: false,
|
||
};
|
||
},
|
||
regionCascaderProps() {
|
||
return {
|
||
value: 'id',
|
||
label: 'title',
|
||
children: 'children',
|
||
emitPath: true,
|
||
};
|
||
},
|
||
ids() {
|
||
let ids = [];
|
||
this.selectionList.forEach(ele => {
|
||
ids.push(ele.id);
|
||
});
|
||
return ids.join(',');
|
||
},
|
||
dialogTitle() {
|
||
if (this.readonly) return '查看客商档案';
|
||
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
|
||
},
|
||
contactDialogTitle() {
|
||
return this.contactIndex > -1 ? '编辑联系人' : '新增联系人';
|
||
},
|
||
receiptDialogTitle() {
|
||
return this.receiptIndex > -1 ? '编辑收款信息' : '新增收款信息';
|
||
},
|
||
invoiceDialogTitle() {
|
||
return this.invoiceIndex > -1 ? '编辑发票信息' : '新增发票信息';
|
||
},
|
||
missingQualificationText() {
|
||
if (this.archiveForm.accessType !== 'formal') return '';
|
||
const types = this.qualificationFiles
|
||
.filter(item => item.url)
|
||
.map(item => item.type || item.name)
|
||
.filter(Boolean);
|
||
const requiredMaterials = [
|
||
{ label: '法人身份证', aliases: ['法人身份证'] },
|
||
{ label: '道路运输许可证', aliases: ['道路运输许可证', '道路运输证'] },
|
||
];
|
||
const missing = requiredMaterials
|
||
.filter(
|
||
item => !types.some(type => item.aliases.some(alias => String(type).includes(alias)))
|
||
)
|
||
.map(item => item.label);
|
||
return missing.length ? `缺失核心客商材料:${missing.join('、')}` : '';
|
||
},
|
||
uploadHeaders() {
|
||
return getUploadHeaders();
|
||
},
|
||
scoreList() {
|
||
return this.archiveForm.scores || [];
|
||
},
|
||
scorePageCount() {
|
||
return Math.max(Math.ceil(this.scoreList.length / this.scorePage.pageSize), 1);
|
||
},
|
||
scorePaginationPage() {
|
||
return {
|
||
...this.scorePage,
|
||
total: this.scoreList.length,
|
||
};
|
||
},
|
||
scoreTableData() {
|
||
const start = (this.scorePage.currentPage - 1) * this.scorePage.pageSize;
|
||
return this.scoreList
|
||
.slice(start, start + this.scorePage.pageSize)
|
||
.map((item, index) => ({
|
||
...item,
|
||
__raw: item,
|
||
__rawIndex: start + index,
|
||
__index: start + index + 1,
|
||
maxCreditLimit: item.maxCreditLimit || '',
|
||
}));
|
||
},
|
||
contactPaginationPage() {
|
||
return this.buildDetailPaginationPage('contact');
|
||
},
|
||
contactTableData() {
|
||
return this.buildDetailTableData('contact');
|
||
},
|
||
receiptPaginationPage() {
|
||
return this.buildDetailPaginationPage('receipt');
|
||
},
|
||
receiptTableData() {
|
||
return this.buildDetailTableData('receipt');
|
||
},
|
||
invoicePaginationPage() {
|
||
return this.buildDetailPaginationPage('invoice');
|
||
},
|
||
invoiceTableData() {
|
||
return this.buildDetailTableData('invoice');
|
||
},
|
||
scoreCategoryRows() {
|
||
const categoryMap = [
|
||
{ categoryCode: 'basic', categoryName: '基础得分项' },
|
||
{ categoryCode: 'plus', categoryName: '加分项目' },
|
||
{ categoryCode: 'minus', categoryName: '减分项目' },
|
||
];
|
||
return categoryMap.map((category, index) => {
|
||
const details = this.getScoreCategoryDetails(category.categoryCode);
|
||
const selfScore = details.reduce((sum, item) => sum + Number(item.selfScore || 0), 0);
|
||
const reviewScore = details.reduce((sum, item) => sum + Number(item.reviewScore || 0), 0);
|
||
return {
|
||
...category,
|
||
index: index + 1,
|
||
selfScore: Number(selfScore.toFixed(2)),
|
||
reviewScore: Number(reviewScore.toFixed(2)),
|
||
};
|
||
});
|
||
},
|
||
},
|
||
methods: {
|
||
hasPermission(code) {
|
||
return this.isAdmin || this.validData(this.permission[code], false);
|
||
},
|
||
emptyArchive() {
|
||
return {
|
||
accessType: 'temporary',
|
||
approvalStatus: 'draft',
|
||
status: 1,
|
||
deptId: [],
|
||
deptIds: '',
|
||
deptName: '',
|
||
customerType: [],
|
||
businessScope: [],
|
||
businessTermType: '固定期限',
|
||
registeredRegionPath: [],
|
||
registeredRegionName: '',
|
||
registeredDetailAddress: '',
|
||
registeredAddress: '',
|
||
contacts: [],
|
||
receiptAccounts: [],
|
||
invoices: [],
|
||
scores: [],
|
||
changeRecords: [],
|
||
};
|
||
},
|
||
getDetailList(type) {
|
||
const listMap = {
|
||
contact: this.archiveForm.contacts,
|
||
receipt: this.archiveForm.receiptAccounts,
|
||
invoice: this.archiveForm.invoices,
|
||
};
|
||
return listMap[type] || [];
|
||
},
|
||
getDetailPage(type) {
|
||
const pageMap = {
|
||
contact: this.contactPage,
|
||
receipt: this.receiptPage,
|
||
invoice: this.invoicePage,
|
||
};
|
||
return pageMap[type];
|
||
},
|
||
getDetailPageCount(type) {
|
||
const page = this.getDetailPage(type);
|
||
return Math.max(Math.ceil(this.getDetailList(type).length / page.pageSize), 1);
|
||
},
|
||
buildDetailPaginationPage(type) {
|
||
return {
|
||
...this.getDetailPage(type),
|
||
total: this.getDetailList(type).length,
|
||
};
|
||
},
|
||
buildDetailTableData(type) {
|
||
const page = this.getDetailPage(type);
|
||
const start = (page.currentPage - 1) * page.pageSize;
|
||
return this.getDetailList(type)
|
||
.slice(start, start + page.pageSize)
|
||
.map((item, index) => ({
|
||
...item,
|
||
__raw: item,
|
||
__rawIndex: start + index,
|
||
__index: start + index + 1,
|
||
}));
|
||
},
|
||
handleDetailCurrentChange(type, currentPage) {
|
||
const page = this.getDetailPage(type);
|
||
const nextPage = Number(currentPage) || 1;
|
||
page.currentPage = Math.min(Math.max(nextPage, 1), this.getDetailPageCount(type));
|
||
},
|
||
handleDetailSizeChange(type, pageSize) {
|
||
const page = this.getDetailPage(type);
|
||
page.pageSize = Number(pageSize) || 10;
|
||
this.handleDetailCurrentChange(type, 1);
|
||
},
|
||
resetDetailPagination() {
|
||
this.handleScoreCurrentChange(1);
|
||
this.handleDetailCurrentChange('contact', 1);
|
||
this.handleDetailCurrentChange('receipt', 1);
|
||
this.handleDetailCurrentChange('invoice', 1);
|
||
},
|
||
emptyContact() {
|
||
return {
|
||
contactName: '',
|
||
contactPhone: '',
|
||
email: '',
|
||
regionName: '',
|
||
regionCode: '',
|
||
detailAddress: '',
|
||
longitude: '',
|
||
latitude: '',
|
||
contactAddress: '',
|
||
branchName: '',
|
||
positionName: '',
|
||
isDefault: 0,
|
||
remark: '',
|
||
};
|
||
},
|
||
emptyReceiptAccount() {
|
||
return {
|
||
accountName: '',
|
||
accountHolderName: '',
|
||
bankName: '',
|
||
bankAccount: '',
|
||
registeredPhone: '',
|
||
registeredAddress: '',
|
||
taxNo: '',
|
||
isDefault: 0,
|
||
remark: '',
|
||
};
|
||
},
|
||
emptyInvoice() {
|
||
return {
|
||
invoiceTitle: '',
|
||
taxNo: '',
|
||
bankName: '',
|
||
registeredPhone: '',
|
||
bankAccount: '',
|
||
registeredRegionName: '',
|
||
registeredDetailAddress: '',
|
||
registeredAddress: '',
|
||
email: '',
|
||
receiverName: '',
|
||
receiverPhone: '',
|
||
receiverRegionName: '',
|
||
receiverDetailAddress: '',
|
||
receiverAddress: '',
|
||
isDefault: 0,
|
||
};
|
||
},
|
||
initDeptTree() {
|
||
getDeptTree(this.userInfo.tenantId)
|
||
.then(res => {
|
||
this.deptTree = this.normalizeDeptTree(res.data.data || []);
|
||
this.deptOptions = this.flattenDept(this.deptTree);
|
||
const deptColumn = this.findColumn(this.option.column, 'deptName');
|
||
deptColumn.dicData = [
|
||
{ label: '全部', value: '' },
|
||
...this.deptOptions.map(item => ({
|
||
label: item.label,
|
||
value: item.rawLabel,
|
||
})),
|
||
];
|
||
this.applyDefaultDeptSearch(deptColumn);
|
||
})
|
||
.finally(() => {
|
||
this.deptSearchInitialized = true;
|
||
this.$nextTick(() => this.onLoad(this.page, this.query));
|
||
});
|
||
},
|
||
applyDefaultDeptSearch(deptColumn) {
|
||
if (this.isAdmin) return;
|
||
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
|
||
const currentDept = this.deptOptions.find(
|
||
item => String(item.value) === String(currentDeptId)
|
||
);
|
||
const deptName = currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name;
|
||
if (!deptName) return;
|
||
deptColumn.searchValue = deptName;
|
||
this.query = { ...this.query, deptName };
|
||
if (this.$refs.crud?.searchForm) {
|
||
this.$refs.crud.searchForm.deptName = deptName;
|
||
}
|
||
},
|
||
initRegionOptions() {
|
||
getLazyTree().then(res => {
|
||
const regions = this.buildRegionTree(res.data.data || []);
|
||
this.regionOptions = this.extractChinaRegionOptions(regions);
|
||
});
|
||
},
|
||
buildRegionTree(regions = []) {
|
||
const flatRegions = [];
|
||
const collectRegions = list => {
|
||
(list || []).forEach(item => {
|
||
flatRegions.push({
|
||
...item,
|
||
children: undefined,
|
||
});
|
||
if (item.children?.length) collectRegions(item.children);
|
||
});
|
||
};
|
||
collectRegions(regions);
|
||
|
||
const nodeMap = new Map();
|
||
flatRegions.forEach(item => {
|
||
const id = item.id === undefined || item.id === null ? item.value : item.id;
|
||
if (id === undefined || id === null) return;
|
||
nodeMap.set(String(id), {
|
||
...item,
|
||
id: String(id),
|
||
parentId:
|
||
item.parentId === undefined || item.parentId === null ? '' : String(item.parentId),
|
||
children: [],
|
||
});
|
||
});
|
||
|
||
const rootNodes = [];
|
||
nodeMap.forEach(node => {
|
||
const parent = nodeMap.get(node.parentId);
|
||
if (parent && parent !== node) {
|
||
parent.children.push(node);
|
||
} else {
|
||
rootNodes.push(node);
|
||
}
|
||
});
|
||
return rootNodes.map(item => this.normalizeRegionTreeNode(item));
|
||
},
|
||
normalizeRegionTreeNode(region) {
|
||
const children = (region.children || []).map(item => this.normalizeRegionTreeNode(item));
|
||
return {
|
||
...region,
|
||
children: children.length ? children : undefined,
|
||
leaf: !children.length,
|
||
};
|
||
},
|
||
isChinaRegion(region = {}) {
|
||
const title = String(region.title || region.name || '').trim();
|
||
return title === '中国' || title === '中华人民共和国';
|
||
},
|
||
extractChinaRegionOptions(regions) {
|
||
const china = (regions || []).find(item => this.isChinaRegion(item));
|
||
if (china?.children?.length) return china.children;
|
||
if (regions.length === 1 && regions[0].children?.length) return regions[0].children;
|
||
return regions;
|
||
},
|
||
findRegionOption(options, value) {
|
||
for (const item of options || []) {
|
||
if (String(item.id) === String(value)) return item;
|
||
const child = this.findRegionOption(item.children, value);
|
||
if (child) return child;
|
||
}
|
||
return null;
|
||
},
|
||
getRegisteredRegionLabels(value) {
|
||
const nodes = this.$refs.registeredRegionCascader?.getCheckedNodes?.() || [];
|
||
if (nodes.length && nodes[0].pathLabels?.length) {
|
||
return nodes[0].pathLabels;
|
||
}
|
||
return (value || [])
|
||
.map(code => this.findRegionOption(this.regionOptions, code)?.title)
|
||
.filter(Boolean);
|
||
},
|
||
handleRegisteredRegionChange(value) {
|
||
if (!value || !value.length) {
|
||
this.archiveForm.registeredRegionName = '';
|
||
this.$refs.archiveForm?.validateField('registeredDetailAddress');
|
||
return;
|
||
}
|
||
this.$nextTick(() => {
|
||
this.archiveForm.registeredRegionName = this.getRegisteredRegionLabels(value).join('');
|
||
this.$refs.archiveForm?.validateField('registeredDetailAddress');
|
||
});
|
||
},
|
||
initBusinessDictionaries() {
|
||
this.loadDictOptions(
|
||
'nature_of_client',
|
||
'customerNatureOptions',
|
||
['有限公司', '个体工商户'],
|
||
options => {
|
||
const customerNatureColumn = this.findColumn(this.option.column, 'customerNature');
|
||
customerNatureColumn.dicData = [{ label: '全部', value: '' }, ...options];
|
||
}
|
||
);
|
||
this.loadDictOptions('customer_type', 'customerTypeOptions', ['客户', '承运商'], options => {
|
||
const customerTypeColumn = this.findColumn(this.option.column, 'customerType');
|
||
customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options];
|
||
});
|
||
this.loadDictOptions('scope_of_business', 'businessScopeOptions');
|
||
this.loadDictOptions('bank_list', 'bankListOptions');
|
||
},
|
||
initScoreQuantificationOptions() {
|
||
this.scoreQuantificationLoading = true;
|
||
getCreditScoreQuantificationList(1, 9999, { status: 1 })
|
||
.then(res => {
|
||
const data = res.data.data || {};
|
||
const records = Array.isArray(data) ? data : data.records || [];
|
||
this.scoreQuantificationOptions = records
|
||
.filter(item => Number(item.status) === 1)
|
||
.map(item => ({
|
||
id: item.id,
|
||
name: item.name || item.configName || item.title || item.id,
|
||
}));
|
||
})
|
||
.finally(() => {
|
||
this.scoreQuantificationLoading = false;
|
||
});
|
||
},
|
||
loadDictOptions(code, target, fallback = [], callback) {
|
||
getDictionary({ code }).then(res => {
|
||
const options = (res.data.data || []).map(item => ({
|
||
label: item.dictValue,
|
||
value: item.dictValue,
|
||
}));
|
||
fallback.forEach(value => {
|
||
if (!options.some(item => item.value === value)) {
|
||
options.push({ label: value, value });
|
||
}
|
||
});
|
||
this[target] = options;
|
||
if (callback) callback(options);
|
||
});
|
||
},
|
||
normalizeDeptTree(tree = []) {
|
||
return (tree || []).map(item => {
|
||
const label = item.label || item.title || item.deptName || item.name || '';
|
||
const value = String(item.value || item.id || '');
|
||
return {
|
||
...item,
|
||
label,
|
||
value,
|
||
children: this.normalizeDeptTree(item.children || []),
|
||
};
|
||
});
|
||
},
|
||
flattenDept(tree, level = 0) {
|
||
const result = [];
|
||
tree.forEach(item => {
|
||
result.push({
|
||
label: `${' '.repeat(level)}${item.label || item.title || item.deptName || item.name}`,
|
||
value: item.value || item.id,
|
||
rawLabel: item.label || item.title || item.deptName || item.name,
|
||
});
|
||
if (item.children && item.children.length) {
|
||
result.push(...this.flattenDept(item.children, level + 1));
|
||
}
|
||
});
|
||
return result;
|
||
},
|
||
onDeptChange(value) {
|
||
const deptIds = Array.isArray(value) ? value : value ? [value] : [];
|
||
const deptNames = deptIds
|
||
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
|
||
.filter(Boolean);
|
||
this.archiveForm.deptIds = deptIds.join(',');
|
||
this.archiveForm.deptName = deptNames.join(',');
|
||
this.$refs.archiveForm?.validateField('deptName');
|
||
},
|
||
handleNoFixedTermChange(checked) {
|
||
this.archiveForm.businessTermType = checked ? '长期' : '固定期限';
|
||
if (checked) {
|
||
this.archiveForm.businessEndDate = '';
|
||
}
|
||
this.$nextTick(() => {
|
||
this.$refs.archiveForm?.clearValidate('businessEndDate');
|
||
if (!checked) {
|
||
this.$refs.archiveForm?.validateField('businessEndDate');
|
||
}
|
||
});
|
||
},
|
||
handleBusinessEndDateChange(value) {
|
||
if (value) {
|
||
this.archiveForm.businessTermType = '固定期限';
|
||
}
|
||
},
|
||
handleDecimalInput(prop, value, max) {
|
||
const normalized = String(value || '')
|
||
.replace(/[^\d.]/g, '')
|
||
.replace(/^\./, '')
|
||
.replace(/(\..*)\./g, '$1')
|
||
.replace(/^(\d+)(\.\d{0,2})?.*$/, '$1$2');
|
||
if (max !== undefined && normalized !== '' && Number(normalized) > max) {
|
||
this.archiveForm[prop] = String(max);
|
||
return;
|
||
}
|
||
this.archiveForm[prop] = normalized;
|
||
},
|
||
splitValue(value) {
|
||
return value
|
||
? String(value)
|
||
.split(',')
|
||
.map(item => item.trim())
|
||
.filter(Boolean)
|
||
: [];
|
||
},
|
||
normalizeDeptIds(detail) {
|
||
const value = detail.deptIds || detail.deptId;
|
||
return value
|
||
? String(value)
|
||
.split(',')
|
||
.map(item => item.trim())
|
||
.filter(item => item !== '')
|
||
: [];
|
||
},
|
||
formatArchiveAddress(archive = {}) {
|
||
return [archive.registeredRegionName, archive.registeredDetailAddress]
|
||
.map(item => String(item || '').trim())
|
||
.filter(Boolean)
|
||
.join('');
|
||
},
|
||
formatContactAddress(row = {}) {
|
||
return (
|
||
row.contactAddress ||
|
||
[row.regionName, row.detailAddress]
|
||
.map(item => String(item || '').trim())
|
||
.filter(Boolean)
|
||
.join('')
|
||
);
|
||
},
|
||
formatInvoiceRegisteredAddress(row = {}) {
|
||
return (
|
||
row.registeredAddress ||
|
||
[row.registeredRegionName, row.registeredDetailAddress]
|
||
.map(item => String(item || '').trim())
|
||
.filter(Boolean)
|
||
.join('')
|
||
);
|
||
},
|
||
formatInvoiceReceiverAddress(row = {}) {
|
||
return (
|
||
row.receiverAddress ||
|
||
[row.receiverRegionName, row.receiverDetailAddress]
|
||
.map(item => String(item || '').trim())
|
||
.filter(Boolean)
|
||
.join('')
|
||
);
|
||
},
|
||
normalizeContact(contact = {}) {
|
||
const contactAddress =
|
||
contact.contactAddress || contact.address || this.formatContactAddress(contact);
|
||
return {
|
||
...this.emptyContact(),
|
||
...contact,
|
||
contactAddress,
|
||
branchName: contact.branchName || contact.deptName || this.archiveForm.deptName || '',
|
||
};
|
||
},
|
||
openContact(row, index = -1) {
|
||
this.contactIndex = index;
|
||
this.contactForm = row
|
||
? this.normalizeContact(row)
|
||
: {
|
||
...this.emptyContact(),
|
||
branchName: this.archiveForm.deptName || '',
|
||
};
|
||
this.contactBox = true;
|
||
},
|
||
resetContact() {
|
||
this.contactIndex = -1;
|
||
this.contactForm = this.emptyContact();
|
||
this.$refs.contactForm?.clearValidate();
|
||
},
|
||
saveContact() {
|
||
this.$refs.contactForm.validate(valid => {
|
||
if (!valid) return;
|
||
const contact = this.normalizeContact({
|
||
...this.contactForm,
|
||
contactAddress: this.formatContactAddress(this.contactForm),
|
||
});
|
||
if (this.contactIndex > -1) {
|
||
this.archiveForm.contacts.splice(this.contactIndex, 1, contact);
|
||
} else {
|
||
this.archiveForm.contacts.push(contact);
|
||
this.handleDetailCurrentChange('contact', this.getDetailPageCount('contact'));
|
||
}
|
||
this.contactBox = false;
|
||
});
|
||
},
|
||
deleteContact(index) {
|
||
this.$confirm('确定删除该联系人?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
this.archiveForm.contacts.splice(index, 1);
|
||
this.handleDetailCurrentChange('contact', this.contactPage.currentPage);
|
||
})
|
||
.catch(() => {});
|
||
},
|
||
handleContactMapPick() {
|
||
this.contactMapTarget = 'contact';
|
||
this.contactMapKeyword = this.contactForm.detailAddress || this.contactForm.regionName || '';
|
||
this.contactMapSelected = this.buildContactMapSelection({
|
||
lng: this.contactForm.longitude,
|
||
lat: this.contactForm.latitude,
|
||
address: this.contactForm.detailAddress,
|
||
regionName: this.contactForm.regionName,
|
||
regionCode: this.contactForm.regionCode,
|
||
});
|
||
this.contactMapStatus = this.contactMapSelected.longitude
|
||
? '已加载当前选点,可重新选点'
|
||
: '可搜索地址或点击地图选点';
|
||
this.contactMapBox = true;
|
||
},
|
||
handleInvoiceRegisteredMapPick() {
|
||
this.contactMapTarget = 'invoiceRegistered';
|
||
this.contactMapKeyword =
|
||
this.invoiceForm.registeredDetailAddress || this.invoiceForm.registeredRegionName || '';
|
||
this.contactMapSelected = this.buildContactMapSelection({
|
||
address: this.invoiceForm.registeredDetailAddress,
|
||
regionName: this.invoiceForm.registeredRegionName,
|
||
});
|
||
this.contactMapStatus = '可搜索地址或点击地图选点';
|
||
this.contactMapBox = true;
|
||
},
|
||
loadAmap() {
|
||
if (window.AMap && window.AMap.Map) {
|
||
return Promise.resolve();
|
||
}
|
||
if (!amapLoader) {
|
||
amapLoader = new Promise((resolve, reject) => {
|
||
window._AMapSecurityConfig = {
|
||
securityJsCode: AMAP_SECURITY_CODE,
|
||
};
|
||
const script = document.createElement('script');
|
||
script.src = `https://webapi.amap.com/maps?v=2.0&key=${AMAP_KEY}`;
|
||
script.async = true;
|
||
script.onload = resolve;
|
||
script.onerror = () => reject(new Error('高德地图组件加载失败'));
|
||
document.body.appendChild(script);
|
||
});
|
||
}
|
||
return amapLoader;
|
||
},
|
||
initContactAmap() {
|
||
this.loadAmap()
|
||
.then(() => {
|
||
this.$nextTick(() => {
|
||
if (!this.contactAmap) {
|
||
const center = this.toLngLat(
|
||
this.contactMapSelected.longitude || 116.40769,
|
||
this.contactMapSelected.latitude || 39.89945
|
||
);
|
||
this.contactAmap = new window.AMap.Map(this.$refs.contactAmap, {
|
||
center,
|
||
zoom: this.contactMapSelected.longitude ? 14 : 11,
|
||
});
|
||
window.AMap.plugin(['AMap.ToolBar', 'AMap.Geocoder'], () => {
|
||
this.contactAmap.addControl(new window.AMap.ToolBar());
|
||
if (!this.contactAmapGeocoder) {
|
||
this.contactAmapGeocoder = new window.AMap.Geocoder();
|
||
}
|
||
});
|
||
this.contactAmap.on('click', event => this.pickContactMapPoint(event.lnglat));
|
||
} else if (typeof this.contactAmap.resize === 'function') {
|
||
this.contactAmap.resize();
|
||
}
|
||
if (this.contactMapSelected.longitude) {
|
||
this.renderContactMapMarker(
|
||
this.toLngLat(
|
||
this.contactMapSelected.longitude,
|
||
this.contactMapSelected.latitude
|
||
)
|
||
);
|
||
} else {
|
||
this.clearContactMapMarker();
|
||
}
|
||
});
|
||
})
|
||
.catch(() => {
|
||
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
|
||
this.contactMapBox = false;
|
||
});
|
||
},
|
||
searchContactMapKeyword() {
|
||
const keyword = String(this.contactMapKeyword || '').trim();
|
||
if (!keyword) {
|
||
this.$message.warning('请输入地址关键词');
|
||
return;
|
||
}
|
||
this.loadAmap()
|
||
.then(() => {
|
||
this.contactMapLoading = true;
|
||
this.ensureContactAmapGeocoder()
|
||
.then(() => this.runAmapGeocode('location', keyword))
|
||
.then(result => {
|
||
const point = this.resolveMapPoint(result);
|
||
if (!point) {
|
||
this.contactMapStatus = '未找到匹配地址';
|
||
this.$message.warning('地图搜索无匹配地址');
|
||
return;
|
||
}
|
||
this.pickContactMapPoint(point, keyword);
|
||
})
|
||
.catch(() => {
|
||
this.contactMapStatus = '地图搜索失败';
|
||
this.$message.error('地图搜索失败,请稍后重试');
|
||
})
|
||
.finally(() => {
|
||
this.contactMapLoading = false;
|
||
});
|
||
})
|
||
.catch(() => {
|
||
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
|
||
});
|
||
},
|
||
pickContactMapPoint(lnglat, keyword) {
|
||
const longitude = this.getPointLng(lnglat);
|
||
const latitude = this.getPointLat(lnglat);
|
||
if (longitude === undefined || latitude === undefined) {
|
||
this.$message.warning('选点坐标无效');
|
||
return;
|
||
}
|
||
const point = this.toLngLat(longitude, latitude);
|
||
this.renderContactMapMarker(point);
|
||
this.contactMapSelected = this.buildContactMapSelection({
|
||
lng: longitude,
|
||
lat: latitude,
|
||
address: keyword,
|
||
});
|
||
this.contactMapStatus = '正在反查地址...';
|
||
this.ensureContactAmapGeocoder()
|
||
.then(() => this.runAmapGeocode('address', point))
|
||
.then(result => {
|
||
const address = this.resolveMapAddress(result);
|
||
this.contactMapSelected = {
|
||
...this.contactMapSelected,
|
||
detailAddress: address.detailAddress || this.contactMapSelected.detailAddress,
|
||
regionName: address.regionName || this.contactMapSelected.regionName,
|
||
regionCode: address.regionCode || this.contactMapSelected.regionCode,
|
||
};
|
||
this.contactMapKeyword = this.contactMapSelected.detailAddress || this.contactMapKeyword;
|
||
this.contactMapStatus = this.contactMapSelected.detailAddress || '已选点,可确认回填';
|
||
})
|
||
.catch(() => {
|
||
this.contactMapStatus = '反查地址失败';
|
||
this.$message.error('反查地址失败,请重新选点');
|
||
});
|
||
},
|
||
renderContactMapMarker(point) {
|
||
if (!this.contactAmap || !window.AMap) return;
|
||
this.clearContactMapMarker();
|
||
this.contactAmapMarker = new window.AMap.Marker({
|
||
position: point,
|
||
});
|
||
this.contactAmapMarker.setMap(this.contactAmap);
|
||
this.contactAmap.setCenter(point);
|
||
},
|
||
clearContactMapMarker() {
|
||
if (this.contactAmapMarker) {
|
||
this.contactAmapMarker.setMap(null);
|
||
this.contactAmapMarker = null;
|
||
}
|
||
},
|
||
confirmContactMapPick() {
|
||
if (!this.contactMapSelected.longitude) {
|
||
this.$message.warning('请先搜索或点击地图完成选点');
|
||
return;
|
||
}
|
||
if (this.contactMapTarget === 'invoiceRegistered') {
|
||
this.invoiceForm.registeredRegionName =
|
||
this.contactMapSelected.regionName || this.invoiceForm.registeredRegionName;
|
||
this.invoiceForm.registeredDetailAddress = this.formatMapDetailAddress(
|
||
this.contactMapSelected.detailAddress || this.invoiceForm.registeredDetailAddress,
|
||
this.invoiceForm.registeredRegionName
|
||
);
|
||
this.$refs.invoiceForm?.validateField('registeredRegionName');
|
||
this.contactMapBox = false;
|
||
return;
|
||
}
|
||
this.contactForm.detailAddress =
|
||
this.contactMapSelected.detailAddress || this.contactForm.detailAddress;
|
||
this.contactForm.regionName =
|
||
this.contactMapSelected.regionName || this.contactForm.regionName;
|
||
this.contactForm.regionCode =
|
||
this.contactMapSelected.regionCode || this.contactForm.regionCode;
|
||
this.contactForm.longitude = this.contactMapSelected.longitude;
|
||
this.contactForm.latitude = this.contactMapSelected.latitude;
|
||
this.$refs.contactForm?.validateField('detailAddress');
|
||
this.contactMapBox = false;
|
||
},
|
||
formatMapDetailAddress(address, regionName) {
|
||
const rawAddress = String(address || '').trim();
|
||
const rawRegionName = String(regionName || '').trim();
|
||
if (!rawAddress || !rawRegionName) return rawAddress;
|
||
return rawAddress.startsWith(rawRegionName)
|
||
? rawAddress.slice(rawRegionName.length).trim()
|
||
: rawAddress;
|
||
},
|
||
buildContactMapSelection({ lng, lat, address, regionName, regionCode }) {
|
||
const longitude = this.formatCoordinate(lng);
|
||
const latitude = this.formatCoordinate(lat);
|
||
return {
|
||
longitude,
|
||
latitude,
|
||
detailAddress: address || '',
|
||
regionName: regionName || '',
|
||
regionCode: regionCode || '',
|
||
};
|
||
},
|
||
formatCoordinate(value) {
|
||
if (value === undefined || value === null || value === '') {
|
||
return '';
|
||
}
|
||
const numberValue = Number(value);
|
||
return Number.isFinite(numberValue) ? numberValue.toFixed(6) : '';
|
||
},
|
||
toLngLat(lng, lat) {
|
||
return new window.AMap.LngLat(Number(lng), Number(lat));
|
||
},
|
||
getPointLng(point) {
|
||
if (!point) return undefined;
|
||
if (typeof point.getLng === 'function') return point.getLng();
|
||
return point.lng ?? point.lon;
|
||
},
|
||
getPointLat(point) {
|
||
if (!point) return undefined;
|
||
if (typeof point.getLat === 'function') return point.getLat();
|
||
return point.lat;
|
||
},
|
||
ensureContactAmapGeocoder() {
|
||
if (this.contactAmapGeocoder) {
|
||
return Promise.resolve(this.contactAmapGeocoder);
|
||
}
|
||
return new Promise((resolve, reject) => {
|
||
if (!window.AMap || !window.AMap.plugin) {
|
||
reject(new Error('高德地图组件未就绪'));
|
||
return;
|
||
}
|
||
window.AMap.plugin(['AMap.Geocoder'], () => {
|
||
try {
|
||
this.contactAmapGeocoder = new window.AMap.Geocoder();
|
||
resolve(this.contactAmapGeocoder);
|
||
} catch (error) {
|
||
reject(error);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
runAmapGeocode(action, input) {
|
||
return new Promise((resolve, reject) => {
|
||
const timer = window.setTimeout(() => {
|
||
reject(new Error('高德地图请求超时'));
|
||
}, 10000);
|
||
const done = (status, result) => {
|
||
window.clearTimeout(timer);
|
||
if (status === 'complete' && result) {
|
||
resolve(result);
|
||
return;
|
||
}
|
||
reject(new Error('高德地图请求失败'));
|
||
};
|
||
try {
|
||
if (action === 'location') {
|
||
this.contactAmapGeocoder.getLocation(input, done);
|
||
} else {
|
||
this.contactAmapGeocoder.getAddress(input, done);
|
||
}
|
||
} catch (error) {
|
||
window.clearTimeout(timer);
|
||
reject(error);
|
||
}
|
||
});
|
||
},
|
||
resolveMapPoint(result) {
|
||
if (!result) return null;
|
||
const geocode = Array.isArray(result.geocodes) ? result.geocodes[0] : null;
|
||
if (geocode && geocode.location) return geocode.location;
|
||
if (result.location) return result.location;
|
||
if (result.lnglat) return result.lnglat;
|
||
if (result.getLng || result.lng || result.lon) return result;
|
||
return null;
|
||
},
|
||
resolveMapAddress(result = {}) {
|
||
const regeocode = result.regeocode || {};
|
||
const component = regeocode.addressComponent || {};
|
||
const city = Array.isArray(component.city) ? '' : component.city;
|
||
const regionName = [component.province, city, component.district].filter(Boolean).join('');
|
||
return {
|
||
detailAddress: regeocode.formattedAddress || '',
|
||
regionName,
|
||
regionCode: component.adcode || '',
|
||
};
|
||
},
|
||
normalizeReceipt(receipt = {}) {
|
||
return {
|
||
...this.emptyReceiptAccount(),
|
||
...receipt,
|
||
};
|
||
},
|
||
openReceipt(row, index = -1) {
|
||
this.receiptIndex = index;
|
||
this.receiptForm = row ? this.normalizeReceipt(row) : this.emptyReceiptAccount();
|
||
this.receiptBox = true;
|
||
},
|
||
resetReceipt() {
|
||
this.receiptIndex = -1;
|
||
this.receiptForm = this.emptyReceiptAccount();
|
||
this.$refs.receiptForm?.clearValidate();
|
||
},
|
||
saveReceipt() {
|
||
this.$refs.receiptForm.validate(valid => {
|
||
if (!valid) return;
|
||
const receipt = this.normalizeReceipt(this.receiptForm);
|
||
if (this.receiptIndex > -1) {
|
||
this.archiveForm.receiptAccounts.splice(this.receiptIndex, 1, receipt);
|
||
} else {
|
||
this.archiveForm.receiptAccounts.push(receipt);
|
||
this.handleDetailCurrentChange('receipt', this.getDetailPageCount('receipt'));
|
||
}
|
||
this.receiptBox = false;
|
||
});
|
||
},
|
||
deleteReceipt(index) {
|
||
this.$confirm('确定删除该收款信息?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
this.archiveForm.receiptAccounts.splice(index, 1);
|
||
this.handleDetailCurrentChange('receipt', this.receiptPage.currentPage);
|
||
})
|
||
.catch(() => {});
|
||
},
|
||
normalizeInvoice(invoice = {}) {
|
||
const registeredDetailAddress =
|
||
invoice.registeredDetailAddress ||
|
||
(invoice.registeredRegionName ? '' : invoice.registeredAddress) ||
|
||
'';
|
||
const receiverDetailAddress =
|
||
invoice.receiverDetailAddress ||
|
||
(invoice.receiverRegionName ? '' : invoice.receiverAddress) ||
|
||
'';
|
||
return {
|
||
...this.emptyInvoice(),
|
||
...invoice,
|
||
registeredDetailAddress,
|
||
registeredAddress: this.formatInvoiceRegisteredAddress({
|
||
...invoice,
|
||
registeredDetailAddress,
|
||
}),
|
||
receiverDetailAddress,
|
||
receiverAddress: this.formatInvoiceReceiverAddress({
|
||
...invoice,
|
||
receiverDetailAddress,
|
||
}),
|
||
};
|
||
},
|
||
openInvoice(row, index = -1) {
|
||
this.invoiceIndex = index;
|
||
this.invoiceForm = row ? this.normalizeInvoice(row) : this.emptyInvoice();
|
||
this.invoiceBox = true;
|
||
},
|
||
resetInvoice() {
|
||
this.invoiceIndex = -1;
|
||
this.invoiceForm = this.emptyInvoice();
|
||
this.$refs.invoiceForm?.clearValidate();
|
||
},
|
||
saveInvoice() {
|
||
this.$refs.invoiceForm.validate(valid => {
|
||
if (!valid) return;
|
||
const invoice = this.normalizeInvoice({
|
||
...this.invoiceForm,
|
||
registeredAddress: this.formatInvoiceRegisteredAddress(this.invoiceForm),
|
||
receiverAddress: this.formatInvoiceReceiverAddress(this.invoiceForm),
|
||
});
|
||
if (this.invoiceIndex > -1) {
|
||
this.archiveForm.invoices.splice(this.invoiceIndex, 1, invoice);
|
||
} else {
|
||
this.archiveForm.invoices.push(invoice);
|
||
this.handleDetailCurrentChange('invoice', this.getDetailPageCount('invoice'));
|
||
}
|
||
this.invoiceBox = false;
|
||
});
|
||
},
|
||
deleteInvoice(index) {
|
||
this.$confirm('确定删除该发票信息?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
this.archiveForm.invoices.splice(index, 1);
|
||
this.handleDetailCurrentChange('invoice', this.invoicePage.currentPage);
|
||
})
|
||
.catch(() => {});
|
||
},
|
||
deleteScoreAttachment(index) {
|
||
this.$confirm('确定删除该评分证明材料?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
this.scoreAttachmentFiles.splice(index, 1);
|
||
})
|
||
.catch(() => {});
|
||
},
|
||
parseAttachments(value) {
|
||
if (!value) return [];
|
||
if (Array.isArray(value)) return value;
|
||
try {
|
||
const list = JSON.parse(value);
|
||
return Array.isArray(list)
|
||
? list.map(item => ({
|
||
...item,
|
||
files: item.url ? [item] : [],
|
||
}))
|
||
: [];
|
||
} catch (error) {
|
||
return String(value)
|
||
.split(',')
|
||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||
.filter(item => item.url)
|
||
.map(item => ({
|
||
...item,
|
||
files: [item],
|
||
}));
|
||
}
|
||
},
|
||
stringifyAttachments(list) {
|
||
const files = (list || [])
|
||
.filter(item => item.url)
|
||
.map(item => ({
|
||
type: String(item.type || '').trim(),
|
||
name: String(item.name || '').trim(),
|
||
description: String(item.description || '').trim(),
|
||
size: String(item.size || '').trim(),
|
||
uploadUserName: String(item.uploadUserName || '').trim(),
|
||
uploadTime: String(item.uploadTime || '').trim(),
|
||
url: String(item.url || '').trim(),
|
||
}))
|
||
.filter(item => item.url);
|
||
return files.length ? JSON.stringify(files) : '';
|
||
},
|
||
addAttachment() {
|
||
this.qualificationFiles.push({
|
||
type: '',
|
||
name: '',
|
||
description: '',
|
||
size: '',
|
||
uploadUserName: this.userInfo.realName || this.userInfo.userName || '',
|
||
uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||
url: '',
|
||
files: [],
|
||
});
|
||
},
|
||
formatAttachmentSize(size) {
|
||
if (!size) return '';
|
||
const byteSize = Number(size);
|
||
if (!Number.isFinite(byteSize)) return String(size);
|
||
const mb = byteSize / 1024 / 1024;
|
||
if (mb >= 1) return `${mb.toFixed(2)} MB`;
|
||
return `${(byteSize / 1024).toFixed(2)} KB`;
|
||
},
|
||
handleQualificationUploadSuccess(file, row) {
|
||
row.name = file.name || row.name || '客商材料';
|
||
row.size = this.formatAttachmentSize(file.size || row.size);
|
||
row.uploadUserName = this.userInfo.realName || this.userInfo.userName || '';
|
||
row.uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
row.url = file.url || row.url || '';
|
||
row.files = [file];
|
||
},
|
||
downloadAttachments() {
|
||
const files = this.qualificationFiles.filter(item => item.url);
|
||
if (!files.length) {
|
||
this.$message.warning('暂无可下载附件');
|
||
return;
|
||
}
|
||
files.forEach(item => window.open(item.url, '_blank'));
|
||
},
|
||
normalizeDetail(detail) {
|
||
const businessScope = this.splitValue(detail.businessScope);
|
||
const registeredDetailAddress =
|
||
detail.registeredDetailAddress ||
|
||
(detail.registeredRegionName ? '' : detail.registeredAddress) ||
|
||
'';
|
||
const archive = {
|
||
...this.emptyArchive(),
|
||
...detail,
|
||
deptId: this.normalizeDeptIds(detail),
|
||
customerType: this.splitValue(detail.customerType),
|
||
invoiceTaxRate: this.normalizeOptionalAmount(detail.invoiceTaxRate),
|
||
registeredCapital: this.normalizeOptionalAmount(detail.registeredCapital),
|
||
businessScope,
|
||
businessTermType: detail.businessTermType || '固定期限',
|
||
registeredRegionPath: Array.isArray(detail.registeredRegionPath)
|
||
? detail.registeredRegionPath
|
||
: [],
|
||
registeredRegionName: detail.registeredRegionName || '',
|
||
registeredDetailAddress,
|
||
contacts: (detail.contacts || []).map(item =>
|
||
this.normalizeContact({
|
||
...item,
|
||
branchName: item.branchName || item.deptName || detail.deptName || '',
|
||
})
|
||
),
|
||
receiptAccounts: (detail.receiptAccounts || []).map(item => this.normalizeReceipt(item)),
|
||
invoices: (detail.invoices || []).map(item => this.normalizeInvoice(item)),
|
||
scores: (detail.scores || []).map(score => this.calculateScore(this.normalizeScore(score))),
|
||
changeRecords: detail.changeRecords || [],
|
||
};
|
||
return this.applyArchiveCreditFromScores(archive);
|
||
},
|
||
normalizeScore(score) {
|
||
return {
|
||
quantificationId: '',
|
||
scoreDate: this.$dayjs().format('YYYY-MM-DD'),
|
||
applyCreditLimit: this.archiveForm.applyCreditLimit || '',
|
||
maxCreditLimit: '',
|
||
creditLevel: '',
|
||
standards: [],
|
||
tempApplyCreditLimit: '',
|
||
tempCreditStartDate: '',
|
||
tempCreditEndDate: '',
|
||
remark: '',
|
||
selfStatus: '未完成',
|
||
reviewStatus: '未完成',
|
||
proofAttachments: '',
|
||
attachments: [],
|
||
details: [],
|
||
...score,
|
||
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
|
||
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
|
||
details: score.details || [],
|
||
};
|
||
},
|
||
normalizeOptionalAmount(value) {
|
||
const amount = Number(value);
|
||
return Number.isFinite(amount) && amount < 0 ? '' : value ?? '';
|
||
},
|
||
getScoreTimeValue(value) {
|
||
if (!value) return 0;
|
||
const time = this.$dayjs(value).valueOf();
|
||
return Number.isFinite(time) ? time : 0;
|
||
},
|
||
getLatestCreditScore(scores = []) {
|
||
return (scores || [])
|
||
.map((score, index) => ({ score, index }))
|
||
.filter(item => String(item.score.creditLevel || '').trim())
|
||
.sort((a, b) => {
|
||
const scoreDateDiff =
|
||
this.getScoreTimeValue(b.score.scoreDate) - this.getScoreTimeValue(a.score.scoreDate);
|
||
if (scoreDateDiff) return scoreDateDiff;
|
||
const updateTimeDiff =
|
||
this.getScoreTimeValue(b.score.updateTime) - this.getScoreTimeValue(a.score.updateTime);
|
||
if (updateTimeDiff) return updateTimeDiff;
|
||
const createTimeDiff =
|
||
this.getScoreTimeValue(b.score.createTime) - this.getScoreTimeValue(a.score.createTime);
|
||
if (createTimeDiff) return createTimeDiff;
|
||
return b.index - a.index;
|
||
})[0]?.score;
|
||
},
|
||
applyArchiveCreditFromScores(archive) {
|
||
const latestScore = this.getLatestCreditScore(archive.scores || []);
|
||
archive.customerLevel = latestScore?.creditLevel || archive.customerLevel || '';
|
||
archive.maxCreditLimit = latestScore?.maxCreditLimit || archive.maxCreditLimit || '';
|
||
archive.applyCreditLimit = archive.maxCreditLimit;
|
||
return archive;
|
||
},
|
||
syncArchiveCreditFromScores() {
|
||
this.applyArchiveCreditFromScores(this.archiveForm);
|
||
},
|
||
openArchive(row, readonly = false) {
|
||
this.readonly = readonly;
|
||
this.activeTab = 'scores';
|
||
this.resetDetailPagination();
|
||
if (row && row.id) {
|
||
getDetail(row.id).then(res => {
|
||
this.archiveForm = this.normalizeDetail(res.data.data);
|
||
this.qualificationFiles = this.parseAttachments(
|
||
this.archiveForm.qualificationAttachments
|
||
);
|
||
this.archiveBox = true;
|
||
});
|
||
return;
|
||
}
|
||
this.archiveForm = this.emptyArchive();
|
||
this.qualificationFiles = [];
|
||
this.archiveBox = true;
|
||
},
|
||
resetArchive() {
|
||
this.readonly = false;
|
||
this.archiveForm = this.emptyArchive();
|
||
this.qualificationFiles = [];
|
||
this.scoreBox = false;
|
||
this.resetScoreDialog();
|
||
this.resetDetailPagination();
|
||
this.contactBox = false;
|
||
this.contactIndex = -1;
|
||
this.contactForm = this.emptyContact();
|
||
this.receiptBox = false;
|
||
this.receiptIndex = -1;
|
||
this.receiptForm = this.emptyReceiptAccount();
|
||
this.invoiceBox = false;
|
||
this.invoiceIndex = -1;
|
||
this.invoiceForm = this.emptyInvoice();
|
||
this.$refs.archiveForm?.clearValidate();
|
||
},
|
||
validateFormalForApproval(archive) {
|
||
if (archive.accessType !== 'formal') return true;
|
||
if (!archive.qualificationAttachments) {
|
||
this.$message.warning('正式客商提交审核前必须维护客商材料');
|
||
return false;
|
||
}
|
||
const hasCompletedScore = (archive.scores || []).some(
|
||
item => item.selfStatus === '已完成' || item.reviewStatus === '已完成' || item.finalScore
|
||
);
|
||
if (!hasCompletedScore) {
|
||
this.$message.warning('正式客商提交审核前必须完成信用评分');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
normalizeArchive() {
|
||
const archive = JSON.parse(JSON.stringify(this.archiveForm));
|
||
if (archive.businessTermType === '长期') {
|
||
archive.businessEndDate = null;
|
||
}
|
||
const invoiceTaxRate = this.normalizeOptionalAmount(archive.invoiceTaxRate);
|
||
const registeredCapital = this.normalizeOptionalAmount(archive.registeredCapital);
|
||
archive.invoiceTaxRate = invoiceTaxRate === '' ? null : invoiceTaxRate;
|
||
archive.registeredCapital = registeredCapital === '' ? null : registeredCapital;
|
||
archive.customerType = Array.isArray(archive.customerType)
|
||
? archive.customerType.join(',')
|
||
: archive.customerType;
|
||
archive.businessScope = Array.isArray(archive.businessScope)
|
||
? archive.businessScope.join(',')
|
||
: archive.businessScope;
|
||
const deptIds = Array.isArray(archive.deptId)
|
||
? archive.deptId
|
||
: archive.deptId
|
||
? [archive.deptId]
|
||
: [];
|
||
const deptNames = deptIds
|
||
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
|
||
.filter(Boolean);
|
||
archive.deptIds = deptIds.join(',');
|
||
archive.deptId = deptIds.length ? deptIds[0] : '';
|
||
archive.deptName = deptNames.length ? deptNames.join(',') : archive.deptName;
|
||
archive.registeredAddress = this.formatArchiveAddress(archive);
|
||
delete archive.registeredRegionPath;
|
||
archive.qualificationAttachments = this.stringifyAttachments(this.qualificationFiles);
|
||
archive.contacts = (archive.contacts || [])
|
||
.map(item =>
|
||
this.normalizeContact({
|
||
...item,
|
||
contactAddress: this.formatContactAddress(item),
|
||
})
|
||
)
|
||
.filter(item => item.contactName || item.contactPhone);
|
||
archive.receiptAccounts = (archive.receiptAccounts || [])
|
||
.map(item => this.normalizeReceipt(item))
|
||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount);
|
||
archive.invoices = (archive.invoices || [])
|
||
.map(item => this.normalizeInvoice(item))
|
||
.filter(item => item.invoiceTitle || item.taxNo || item.bankAccount);
|
||
archive.scores = (archive.scores || []).map(score => this.calculateScore(score));
|
||
this.applyArchiveCreditFromScores(archive);
|
||
return archive;
|
||
},
|
||
saveArchive() {
|
||
this.$refs.archiveForm.validate(valid => {
|
||
if (!valid) {
|
||
this.$message.warning('请先完整填写必填项');
|
||
return;
|
||
}
|
||
submit(this.normalizeArchive()).then(() => {
|
||
this.archiveBox = false;
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '操作成功!' });
|
||
});
|
||
});
|
||
},
|
||
saveAndSubmitArchive() {
|
||
this.$refs.archiveForm.validate(valid => {
|
||
if (!valid) {
|
||
this.$message.warning('请先完整填写必填项');
|
||
return;
|
||
}
|
||
const archive = this.normalizeArchive();
|
||
if (!this.validateFormalForApproval(archive)) return;
|
||
submit(archive).then(res => {
|
||
const data = res.data.data;
|
||
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
|
||
if (!id) {
|
||
this.archiveBox = false;
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
|
||
return;
|
||
}
|
||
submitApproval(id).then(() => {
|
||
this.archiveBox = false;
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '提交成功!' });
|
||
});
|
||
});
|
||
});
|
||
},
|
||
addScore() {
|
||
this.scoreRecordIndex = -1;
|
||
this.currentScore = this.normalizeScore({});
|
||
this.scoreAttachmentFiles = [];
|
||
this.expandedScoreCategoryKeys = [];
|
||
this.scoreBox = true;
|
||
if (!this.scoreQuantificationOptions.length) {
|
||
this.initScoreQuantificationOptions();
|
||
}
|
||
},
|
||
openScore(row, index = -1) {
|
||
this.scoreRecordIndex = index;
|
||
this.currentScore = this.normalizeScore(JSON.parse(JSON.stringify(row || {})));
|
||
this.scoreAttachmentFiles = JSON.parse(JSON.stringify(this.currentScore.attachments || []));
|
||
this.expandedScoreCategoryKeys = [];
|
||
this.scoreBox = true;
|
||
if (!this.scoreQuantificationOptions.length) {
|
||
this.initScoreQuantificationOptions();
|
||
}
|
||
},
|
||
resetScoreDialog() {
|
||
this.scoreRecordIndex = -1;
|
||
this.currentScore = { details: [] };
|
||
this.scoreAttachmentFiles = [];
|
||
this.expandedScoreCategoryKeys = [];
|
||
},
|
||
handleScoreQuantificationChange(quantificationId) {
|
||
if (!quantificationId) {
|
||
this.currentScore.details = [];
|
||
this.expandedScoreCategoryKeys = [];
|
||
return;
|
||
}
|
||
Promise.all([
|
||
getScoreTemplate(quantificationId),
|
||
getCreditScoreQuantificationDetail(quantificationId),
|
||
]).then(([templateRes, quantificationRes]) => {
|
||
const templateData = templateRes.data.data || {};
|
||
const quantificationData = quantificationRes.data.data || {};
|
||
const template = this.normalizeScore({
|
||
...templateData,
|
||
quantificationId,
|
||
scoreDate: this.currentScore.scoreDate,
|
||
applyCreditLimit: this.currentScore.applyCreditLimit,
|
||
maxCreditLimit: '',
|
||
creditLevel: '',
|
||
tempApplyCreditLimit: this.currentScore.tempApplyCreditLimit,
|
||
tempCreditStartDate: this.currentScore.tempCreditStartDate,
|
||
tempCreditEndDate: this.currentScore.tempCreditEndDate,
|
||
remark: this.currentScore.remark,
|
||
attachments: this.scoreAttachmentFiles,
|
||
standards: quantificationData.standards || templateData.standards || [],
|
||
details: this.normalizeScoreTemplateDetails(templateData),
|
||
});
|
||
if (!template.details || !template.details.length) {
|
||
this.$message.warning('当前评分量化表未配置评分项目');
|
||
}
|
||
this.currentScore = template;
|
||
this.expandedScoreCategoryKeys = [];
|
||
this.calculateScore(this.currentScore);
|
||
});
|
||
},
|
||
normalizeScoreTemplateDetails(template = {}) {
|
||
const categoryDetails = (template.categories || []).flatMap(category =>
|
||
(category.items || []).map(item => ({
|
||
...item,
|
||
categoryCode: category.categoryCode,
|
||
categoryName: category.categoryName,
|
||
optionsJson:
|
||
item.optionsJson ||
|
||
JSON.stringify(
|
||
(item.options || []).map(option => ({
|
||
label: option.label || option.optionName || option.value,
|
||
value: option.value || option.optionName || option.label,
|
||
score: option.score,
|
||
}))
|
||
),
|
||
}))
|
||
);
|
||
if (!template.details || !template.details.length) return categoryDetails;
|
||
return template.details.map(detail => {
|
||
const category = (template.categories || []).find(item => {
|
||
if (detail.categoryCode) return item.categoryCode === detail.categoryCode;
|
||
if (detail.categoryName) return item.categoryName === detail.categoryName;
|
||
return (item.items || []).some(scoreItem => {
|
||
if (detail.itemId && scoreItem.id) return String(scoreItem.id) === String(detail.itemId);
|
||
return scoreItem.itemName && scoreItem.itemName === detail.itemName;
|
||
});
|
||
});
|
||
return {
|
||
...detail,
|
||
categoryCode: detail.categoryCode || category?.categoryCode || '',
|
||
categoryName: detail.categoryName || category?.categoryName || '',
|
||
};
|
||
});
|
||
},
|
||
getScoreDetailCategoryCode(detail = {}) {
|
||
const rawCode = String(
|
||
detail.categoryCode ||
|
||
detail.scoreCategory ||
|
||
detail.categoryType ||
|
||
detail.itemCategory ||
|
||
detail.type ||
|
||
''
|
||
).toLowerCase();
|
||
if (['basic', 'plus', 'minus'].includes(rawCode)) return rawCode;
|
||
if (rawCode === '1') return 'basic';
|
||
if (rawCode === '2') return 'plus';
|
||
if (rawCode === '3') return 'minus';
|
||
const categoryText = [
|
||
detail.categoryName,
|
||
detail.scoreCategoryName,
|
||
detail.category,
|
||
detail.categoryTypeName,
|
||
detail.typeName,
|
||
]
|
||
.filter(Boolean)
|
||
.join(' ');
|
||
if (categoryText.includes('减分')) return 'minus';
|
||
if (categoryText.includes('加分')) return 'plus';
|
||
if (categoryText.includes('基础')) return 'basic';
|
||
return '';
|
||
},
|
||
getScoreCategoryDetailsByList(details, categoryCode) {
|
||
return (details || []).filter(item => this.getScoreDetailCategoryCode(item) === categoryCode);
|
||
},
|
||
getScoreCategoryDetails(categoryCode) {
|
||
return this.getScoreCategoryDetailsByList(this.currentScore.details || [], categoryCode);
|
||
},
|
||
isScoreCategoryExpanded(categoryCode) {
|
||
return this.expandedScoreCategoryKeys.includes(categoryCode);
|
||
},
|
||
toggleScoreCategory(row) {
|
||
const categoryCode = row.categoryCode;
|
||
this.expandedScoreCategoryKeys = this.isScoreCategoryExpanded(categoryCode)
|
||
? []
|
||
: [categoryCode];
|
||
},
|
||
handleScoreCategoryExpandChange(row, expandedRows) {
|
||
const expandedCategoryCodes = (expandedRows || []).map(item => item.categoryCode);
|
||
this.expandedScoreCategoryKeys = expandedCategoryCodes.includes(row.categoryCode)
|
||
? [row.categoryCode]
|
||
: [];
|
||
},
|
||
parseScoreOptions(value) {
|
||
if (!value) return [];
|
||
const normalize = list =>
|
||
list.map(item => ({
|
||
...item,
|
||
label: item.label || item.optionName || item.value,
|
||
value: item.value || item.optionName || item.label,
|
||
score: item.score,
|
||
}));
|
||
if (Array.isArray(value)) return normalize(value);
|
||
try {
|
||
const options = JSON.parse(value);
|
||
return Array.isArray(options) ? normalize(options) : [];
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
},
|
||
scoreOptionChange(detail, optionValue) {
|
||
const option = this.parseScoreOptions(detail.optionsJson).find(
|
||
item => item.value === optionValue
|
||
);
|
||
const score = Number(option?.score || 0);
|
||
detail.selfScore = score;
|
||
this.calculateScore(this.currentScore);
|
||
},
|
||
handleScoreDetailScoreInput(detail, prop, value) {
|
||
const normalized = String(value || '')
|
||
.replace(/[^\d.]/g, '')
|
||
.replace(/^\./, '')
|
||
.replace(/(\..*)\./g, '$1')
|
||
.replace(/^(\d+)(\.\d{0,2})?.*$/, '$1$2');
|
||
if (prop === 'reviewScore' && normalized !== '') {
|
||
const maxScore = this.getScoreItemFullScore(detail);
|
||
if (Number(normalized) > maxScore) {
|
||
detail[prop] = String(maxScore);
|
||
this.$message.warning(
|
||
`${detail.itemName || '评分项目'}复评得分不能高于配置的最高得分${this.formatScoreValue(
|
||
maxScore
|
||
)}分`
|
||
);
|
||
this.calculateScore(this.currentScore);
|
||
return;
|
||
}
|
||
}
|
||
detail[prop] = normalized;
|
||
this.calculateScore(this.currentScore);
|
||
},
|
||
handleScoreDecimalInput(prop, value) {
|
||
const normalized = String(value || '')
|
||
.replace(/[^\d.]/g, '')
|
||
.replace(/^\./, '')
|
||
.replace(/(\..*)\./g, '$1')
|
||
.replace(/^(\d+)(\.\d{0,2})?.*$/, '$1$2');
|
||
this.currentScore[prop] = normalized;
|
||
},
|
||
handleScoreAttachmentUploadSuccess(file) {
|
||
const index = this.scoreAttachmentFiles.findIndex(
|
||
item => item.uid === file.uid || item.url === file.url
|
||
);
|
||
const attachment = {
|
||
...file,
|
||
size: file.size || '',
|
||
uploadUserName: this.userInfo.realName || this.userInfo.userName || '',
|
||
uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||
};
|
||
if (index > -1) {
|
||
this.scoreAttachmentFiles.splice(index, 1, {
|
||
...this.scoreAttachmentFiles[index],
|
||
...attachment,
|
||
});
|
||
return;
|
||
}
|
||
this.scoreAttachmentFiles.push(attachment);
|
||
},
|
||
isMinusScoreDetail(detail = {}) {
|
||
return this.getScoreDetailCategoryCode(detail) === 'minus';
|
||
},
|
||
isBasicScoreDetail(detail = {}) {
|
||
return this.getScoreDetailCategoryCode(detail) === 'basic';
|
||
},
|
||
getSignedScore(detail, prop) {
|
||
const score = Number(detail[prop] || 0);
|
||
return this.isMinusScoreDetail(detail) ? -score : score;
|
||
},
|
||
getScoreItemFullScore(detail = {}) {
|
||
const itemScore = Number(detail.score);
|
||
if (Number.isFinite(itemScore) && itemScore > 0) return itemScore;
|
||
const optionScores = this.parseScoreOptions(detail.optionsJson)
|
||
.map(item => Number(item.score))
|
||
.filter(score => Number.isFinite(score));
|
||
return optionScores.length ? Math.max(...optionScores) : 0;
|
||
},
|
||
getScoreFullMark(details = []) {
|
||
return details.reduce((sum, item) => {
|
||
if (!this.isBasicScoreDetail(item)) return sum;
|
||
return sum + this.getScoreItemFullScore(item);
|
||
}, 0);
|
||
},
|
||
matchCreditStandard(score, scoreRate) {
|
||
const standards = (score.standards || [])
|
||
.filter(item => item && item.creditLevel)
|
||
.sort((a, b) => Number(b.scoreRateLower || 0) - Number(a.scoreRateLower || 0));
|
||
return standards.find(standard => {
|
||
const lower = Number(standard.scoreRateLower);
|
||
const upper = Number(standard.scoreRateUpper);
|
||
if (!Number.isFinite(lower) || !Number.isFinite(upper)) return false;
|
||
return scoreRate >= lower && scoreRate <= upper;
|
||
});
|
||
},
|
||
calculateCreditLimitByStandard(standard, scoreRate) {
|
||
if (!standard) return '';
|
||
const lowerLimit = Number(standard.creditLimitLower);
|
||
const upperLimit = Number(standard.creditLimitUpper);
|
||
if (!Number.isFinite(lowerLimit) || !Number.isFinite(upperLimit)) return '';
|
||
const scoreRateIncrease = Number(standard.scoreRateIncrease || 0);
|
||
const creditLimitIncrease = Number(standard.creditLimitIncrease || 0);
|
||
if (scoreRateIncrease <= 0 || creditLimitIncrease <= 0) {
|
||
return Number(Math.min(lowerLimit, upperLimit).toFixed(2));
|
||
}
|
||
const steps = Math.max(
|
||
0,
|
||
Math.floor((scoreRate - Number(standard.scoreRateLower || 0)) / scoreRateIncrease)
|
||
);
|
||
const limit = lowerLimit + steps * creditLimitIncrease;
|
||
return Number(Math.min(limit, upperLimit).toFixed(2));
|
||
},
|
||
calculateScore(score) {
|
||
const details = score.details || [];
|
||
const basicDetails = this.getScoreCategoryDetailsByList(details, 'basic');
|
||
const plusDetails = this.getScoreCategoryDetailsByList(details, 'plus');
|
||
const minusDetails = this.getScoreCategoryDetailsByList(details, 'minus');
|
||
const sumScore = (list, prop) =>
|
||
list.reduce((sum, item) => sum + Number(item[prop] || 0), 0);
|
||
const selfScore =
|
||
sumScore(basicDetails, 'selfScore') +
|
||
sumScore(plusDetails, 'selfScore') -
|
||
sumScore(minusDetails, 'selfScore');
|
||
const reviewScore =
|
||
sumScore(basicDetails, 'reviewScore') +
|
||
sumScore(plusDetails, 'reviewScore') -
|
||
sumScore(minusDetails, 'reviewScore');
|
||
const hasReviewScore = details.some(
|
||
item =>
|
||
item.reviewScore !== undefined && item.reviewScore !== null && item.reviewScore !== ''
|
||
);
|
||
const finalScore = hasReviewScore ? reviewScore : selfScore;
|
||
const fullMark = this.getScoreFullMark(details);
|
||
const scoreRate =
|
||
fullMark > 0 ? Math.max(0, Math.min(100, (finalScore / fullMark) * 100)) : 0;
|
||
const standard = this.matchCreditStandard(score, scoreRate);
|
||
const hasStandards = (score.standards || []).some(item => item && item.creditLevel);
|
||
score.selfScore = Number(selfScore.toFixed(2));
|
||
score.reviewScore = Number(reviewScore.toFixed(2));
|
||
score.finalScore = Number(finalScore.toFixed(2));
|
||
score.scoreRate = Number(scoreRate.toFixed(2));
|
||
if (hasStandards) {
|
||
score.creditLevel = standard?.creditLevel || '';
|
||
score.maxCreditLimit = standard
|
||
? this.calculateCreditLimitByStandard(standard, scoreRate)
|
||
: '';
|
||
}
|
||
return score;
|
||
},
|
||
hasIncompleteBasicScoreDetail(details = []) {
|
||
return this.getScoreCategoryDetailsByList(details, 'basic').some(item => !item.selectedOption);
|
||
},
|
||
finishScore(score) {
|
||
if (!score.details || !score.details.length) {
|
||
this.$message.warning('请先读取评分量化表');
|
||
return;
|
||
}
|
||
if (this.hasIncompleteBasicScoreDetail(score.details)) {
|
||
this.$message.warning('请完整选择基础得分项评分明细选项');
|
||
return;
|
||
}
|
||
this.calculateScore(score);
|
||
score.selfStatus = '已完成';
|
||
score.reviewStatus = '已完成';
|
||
this.$message.success('评分已完成');
|
||
},
|
||
saveScoreDetail() {
|
||
if (!this.validateScoreForm(false)) return;
|
||
this.currentScore.attachments = JSON.parse(JSON.stringify(this.scoreAttachmentFiles || []));
|
||
this.currentScore.proofAttachments = this.stringifyAttachments(this.scoreAttachmentFiles);
|
||
this.calculateScore(this.currentScore);
|
||
const score = JSON.parse(JSON.stringify(this.currentScore));
|
||
if (this.scoreRecordIndex > -1) {
|
||
this.archiveForm.scores.splice(this.scoreRecordIndex, 1, score);
|
||
} else {
|
||
this.archiveForm.scores.push(score);
|
||
this.handleScoreCurrentChange(this.scorePageCount);
|
||
}
|
||
this.syncArchiveCreditFromScores();
|
||
this.activeTab = 'scores';
|
||
this.scoreBox = false;
|
||
this.saveScoreArchive();
|
||
},
|
||
saveScoreArchive() {
|
||
if (!this.archiveForm.id) {
|
||
this.$message.success('评分记录已添加,请保存客商档案后生效');
|
||
return;
|
||
}
|
||
submit(this.normalizeArchive()).then(() => {
|
||
this.$message.success('评分记录已保存');
|
||
});
|
||
},
|
||
confirmReviewScore() {
|
||
if (!this.validateScoreForm(true)) return;
|
||
this.currentScore.selfStatus = '已完成';
|
||
this.currentScore.reviewStatus = '已完成';
|
||
this.saveScoreDetail();
|
||
this.$message.success('复评确认成功');
|
||
},
|
||
validateScoreForm(requireComplete) {
|
||
if (!this.currentScore.scoreDate) {
|
||
this.$message.warning('请选择评定日期');
|
||
return false;
|
||
}
|
||
if (!this.currentScore.applyCreditLimit) {
|
||
this.$message.warning('请输入拟申请总资金使用额度');
|
||
return false;
|
||
}
|
||
if (Number(this.currentScore.applyCreditLimit) < 0) {
|
||
this.$message.warning('拟申请总资金使用额度不能小于0');
|
||
return false;
|
||
}
|
||
if (!this.currentScore.quantificationId) {
|
||
this.$message.warning('请选择评分量化表');
|
||
return false;
|
||
}
|
||
if (!this.currentScore.details || !this.currentScore.details.length) {
|
||
this.$message.warning('当前评分量化表未配置评分项目');
|
||
return false;
|
||
}
|
||
const overMaxReviewScore = this.currentScore.details.find(item => {
|
||
if (item.reviewScore === undefined || item.reviewScore === null || item.reviewScore === '') {
|
||
return false;
|
||
}
|
||
return Number(item.reviewScore) > this.getScoreItemFullScore(item);
|
||
});
|
||
if (overMaxReviewScore) {
|
||
this.$message.warning(
|
||
`${
|
||
overMaxReviewScore.itemName || '评分项目'
|
||
}复评得分不能高于配置的最高得分${this.formatScoreValue(
|
||
this.getScoreItemFullScore(overMaxReviewScore)
|
||
)}分`
|
||
);
|
||
return false;
|
||
}
|
||
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
|
||
this.$message.warning('请完整选择基础得分项评分明细选项');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
handleScoreCurrentChange(currentPage) {
|
||
const page = Number(currentPage) || 1;
|
||
this.scorePage.currentPage = Math.min(Math.max(page, 1), this.scorePageCount);
|
||
},
|
||
handleScoreSizeChange(pageSize) {
|
||
this.scorePage.pageSize = Number(pageSize) || 3;
|
||
this.handleScoreCurrentChange(1);
|
||
},
|
||
formatScoreValue(value) {
|
||
return value === undefined || value === null || value === '' ? '' : value;
|
||
},
|
||
rowDel(row) {
|
||
this.$confirm('仅草稿状态客商可删除,确定删除该数据?', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => remove(row.id))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '操作成功!' });
|
||
});
|
||
},
|
||
handleDelete() {
|
||
if (this.selectionList.length === 0) {
|
||
this.$message.warning('请选择至少一条数据');
|
||
return;
|
||
}
|
||
if (this.selectionList.some(item => item.approvalStatus !== 'draft')) {
|
||
this.$message.warning('仅草稿状态客商可删除');
|
||
return;
|
||
}
|
||
this.$confirm('确定删除所选草稿客商?', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => remove(this.ids))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '操作成功!' });
|
||
this.selectionClear();
|
||
});
|
||
},
|
||
handleSubmitApproval(row) {
|
||
getDetail(row.id).then(res => {
|
||
const archive = this.normalizeDetail(res.data.data);
|
||
archive.qualificationAttachments = archive.qualificationAttachments || '';
|
||
if (!this.validateFormalForApproval(archive)) return;
|
||
this.$confirm('是否提交客商准入审批?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => submitApproval(row.id))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '提交成功!' });
|
||
});
|
||
});
|
||
},
|
||
handleWithdrawApproval(row) {
|
||
this.$confirm('是否确认撤回该客商审批?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => withdrawApproval(row.id))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '已撤回至草稿状态!' });
|
||
});
|
||
},
|
||
handleApprove(row) {
|
||
this.$confirm('是否确认审核通过?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => approve(row.id))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '审核通过!' });
|
||
});
|
||
},
|
||
handleReject(row) {
|
||
this.$confirm('是否确认审核不通过?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => reject(row.id))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '操作成功!' });
|
||
});
|
||
},
|
||
handleStatus(row) {
|
||
const nextStatus = row.status === 1 ? 2 : 1;
|
||
const actionName = nextStatus === 1 ? '启用' : '停用';
|
||
this.$confirm(`是否确认${actionName}该客商?`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => changeStatus(row.id, nextStatus))
|
||
.then(() => {
|
||
this.onLoad(this.page);
|
||
this.$message({ type: 'success', message: '操作成功!' });
|
||
});
|
||
},
|
||
searchReset() {
|
||
this.query = this.isAdmin ? {} : { deptName: this.getCurrentDeptName() };
|
||
this.onLoad(this.page);
|
||
},
|
||
searchChange(params, done) {
|
||
this.query = this.buildQuery(params);
|
||
this.page.currentPage = 1;
|
||
this.onLoad(this.page);
|
||
done();
|
||
},
|
||
selectionChange(list) {
|
||
this.selectionList = list;
|
||
},
|
||
selectionClear() {
|
||
this.selectionList = [];
|
||
this.$refs.crud.toggleSelection();
|
||
},
|
||
currentChange(currentPage) {
|
||
this.page.currentPage = currentPage;
|
||
},
|
||
sizeChange(pageSize) {
|
||
this.page.pageSize = pageSize;
|
||
},
|
||
refreshChange() {
|
||
this.onLoad(this.page, this.query);
|
||
},
|
||
onLoad(page, params = {}) {
|
||
if (!this.deptSearchInitialized) return;
|
||
this.loading = true;
|
||
getList(page.currentPage, page.pageSize, this.buildQuery(params))
|
||
.then(res => {
|
||
const pageData = res.data.data;
|
||
this.page.total = pageData.total;
|
||
this.data = pageData.records;
|
||
this.selectionClear();
|
||
})
|
||
.finally(() => {
|
||
this.loading = false;
|
||
});
|
||
},
|
||
handleExport() {
|
||
this.$confirm('是否导出客商档案数据?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
}).then(() => {
|
||
NProgress.start();
|
||
exportBlob(
|
||
'/blade-transport/customer-archive/export-customer-archive',
|
||
this.buildExportParams(),
|
||
{ feedback: true }
|
||
)
|
||
.then(res => {
|
||
downloadXls(res.data, `客商档案${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||
})
|
||
.finally(() => {
|
||
NProgress.done();
|
||
});
|
||
});
|
||
},
|
||
buildExportParams() {
|
||
return {
|
||
...this.buildQuery(),
|
||
ids: this.ids,
|
||
[this.website.tokenHeader]: getToken(),
|
||
};
|
||
},
|
||
buildQuery(params = {}) {
|
||
return normalizeSearchRangeParams({ ...params, ...this.query }, createTimeRangeMap);
|
||
},
|
||
getCurrentDeptName() {
|
||
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
|
||
const currentDept = this.deptOptions.find(
|
||
item => String(item.value) === String(currentDeptId)
|
||
);
|
||
return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || '';
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.customer-archive-page {
|
||
:deep(.el-table th .cell),
|
||
:deep(.el-table td .cell) {
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.archive-form {
|
||
:deep(.el-input-number),
|
||
:deep(.el-select),
|
||
:deep(.el-cascader),
|
||
:deep(.el-date-editor) {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.archive-form__group {
|
||
padding: 16px 20px;
|
||
margin-bottom: 16px;
|
||
background-color: #f0f2f5;
|
||
border-radius: 8px;
|
||
|
||
.dialog-section-title {
|
||
margin: 0 0 12px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
}
|
||
|
||
// 内层白色卡片:字段区域白底,浮于灰色分组块之上
|
||
> .el-row {
|
||
margin: 0;
|
||
padding: 18px 16px 4px;
|
||
background-color: #fff;
|
||
border-radius: 6px;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||
}
|
||
}
|
||
|
||
.business-term-field,
|
||
.archive-form__address {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
width: 100%;
|
||
}
|
||
|
||
.business-term-field {
|
||
:deep(.el-date-editor) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
:deep(.el-checkbox) {
|
||
flex: 0 0 auto;
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
|
||
.archive-form__address {
|
||
.el-input,
|
||
:deep(.el-cascader) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
:deep(.el-cascader) {
|
||
flex: 0 0 36%;
|
||
}
|
||
}
|
||
|
||
.section-title {
|
||
margin: 16px 0 12px;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
}
|
||
|
||
.section-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin: 18px 0 12px;
|
||
|
||
.section-title {
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.section-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.archive-rule-alert {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.archive-tabs {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.score-list-table {
|
||
width: 100%;
|
||
}
|
||
|
||
.score-detail-form {
|
||
padding: 8px 36px 0;
|
||
|
||
:deep(.el-form-item) {
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
:deep(.el-form-item__label) {
|
||
color: #70757a;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
:deep(.el-input),
|
||
:deep(.el-select),
|
||
:deep(.el-date-editor) {
|
||
width: 230px;
|
||
}
|
||
|
||
:deep(.el-col:nth-child(2) .el-form-item) {
|
||
justify-content: flex-end;
|
||
}
|
||
}
|
||
|
||
.score-detail-form__range {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
|
||
:deep(.el-date-editor) {
|
||
width: 230px;
|
||
}
|
||
}
|
||
|
||
.score-category-table {
|
||
width: calc(100% - 72px);
|
||
margin: 8px 36px 0;
|
||
|
||
:deep(.el-table__body td) {
|
||
height: 56px;
|
||
color: #303133;
|
||
}
|
||
|
||
:deep(.el-table__body tr) {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.score-category-table__name {
|
||
display: inline-block;
|
||
min-width: 96px;
|
||
margin-right: 10px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.score-category-table__arrow {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
transition: transform 0.2s ease;
|
||
|
||
&.is-expanded {
|
||
transform: rotate(90deg);
|
||
}
|
||
}
|
||
|
||
.score-category-table {
|
||
:deep(.score-category-table__expand-column) {
|
||
width: 0 !important;
|
||
padding: 0 !important;
|
||
border-right: 0;
|
||
|
||
.cell {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
:deep(.el-table__expanded-cell) {
|
||
padding: 0;
|
||
background-color: #fff;
|
||
}
|
||
}
|
||
|
||
.score-standard-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
min-height: 104px;
|
||
padding: 8px 24px;
|
||
}
|
||
|
||
.score-standard-cell__label {
|
||
margin-bottom: 12px;
|
||
color: #303133;
|
||
}
|
||
|
||
.score-detail-table--inline {
|
||
margin-top: 0;
|
||
|
||
:deep(.el-table__header th) {
|
||
height: 46px;
|
||
background-color: #f5f7fa;
|
||
color: #303133;
|
||
font-weight: 700;
|
||
}
|
||
|
||
:deep(.el-table__body td) {
|
||
min-height: 112px;
|
||
color: #303133;
|
||
}
|
||
|
||
:deep(.el-select),
|
||
:deep(.el-input),
|
||
:deep(.el-select) {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.customer-material-section {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.change-record-section {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.score-list-table,
|
||
.contact-table,
|
||
.receipt-table,
|
||
.invoice-table {
|
||
:deep(.el-table__header th) {
|
||
background-color: #f5f7fa;
|
||
}
|
||
}
|
||
|
||
.contact-form,
|
||
.receipt-form,
|
||
.invoice-form {
|
||
max-width: 620px;
|
||
|
||
:deep(.el-form-item) {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
:deep(.el-input) {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.invoice-form {
|
||
max-width: none;
|
||
|
||
&__section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
margin: 8px 0 20px;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
|
||
&::before,
|
||
&::after {
|
||
flex: 1;
|
||
height: 1px;
|
||
content: '';
|
||
background-color: #dcdfe6;
|
||
}
|
||
}
|
||
}
|
||
|
||
.contact-form__address {
|
||
display: flex;
|
||
gap: 10px;
|
||
width: 100%;
|
||
|
||
.el-input {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.el-input:first-child {
|
||
flex: 0 0 38%;
|
||
}
|
||
}
|
||
|
||
.contact-form__compact-field {
|
||
:deep(.el-input) {
|
||
width: 60%;
|
||
}
|
||
}
|
||
|
||
.contact-dialog__footer-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.receipt-form {
|
||
:deep(.el-input),
|
||
:deep(.el-select) {
|
||
width: 60%;
|
||
}
|
||
}
|
||
|
||
.receipt-dialog__footer-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.invoice-dialog__footer-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.contact-form__map-toolbar {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-bottom: 12px;
|
||
|
||
.el-input {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.contact-form__map {
|
||
width: 100%;
|
||
height: 460px;
|
||
border: 1px solid #dcdfe6;
|
||
}
|
||
|
||
.contact-form__map-info {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
min-height: 22px;
|
||
margin-top: 10px;
|
||
color: #606266;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.invoice-form__address {
|
||
display: flex;
|
||
gap: 10px;
|
||
width: 100%;
|
||
|
||
.el-input {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.el-input:first-child {
|
||
flex: 0 0 38%;
|
||
}
|
||
}
|
||
|
||
.score-detail-table {
|
||
width: 100%;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
:deep(.score-detail-dialog .el-dialog__body) {
|
||
max-height: 74vh;
|
||
overflow: auto;
|
||
padding: 18px 20px 22px;
|
||
}
|
||
|
||
:deep(.score-detail-dialog .el-dialog__footer) {
|
||
text-align: center;
|
||
|
||
.el-button {
|
||
min-width: 160px;
|
||
height: 42px;
|
||
margin: 0 18px;
|
||
}
|
||
}
|
||
|
||
.score-material-section {
|
||
margin: 26px 36px 0;
|
||
}
|
||
|
||
.score-material-section__head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
margin-bottom: 16px;
|
||
|
||
.dialog-section-title {
|
||
margin: 0;
|
||
}
|
||
|
||
:deep(.vehicle-attachment-upload) {
|
||
width: auto;
|
||
}
|
||
|
||
:deep(.el-upload-list) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.score-material-table {
|
||
width: 100%;
|
||
}
|
||
|
||
:deep(.archive-dialog .el-dialog__body) {
|
||
max-height: 72vh;
|
||
overflow: auto;
|
||
}
|
||
|
||
:deep(.contact-dialog .el-dialog__body) {
|
||
max-height: 70vh;
|
||
overflow: auto;
|
||
}
|
||
|
||
:deep(.receipt-dialog .el-dialog__body) {
|
||
max-height: 70vh;
|
||
overflow: auto;
|
||
}
|
||
|
||
:deep(.invoice-dialog .el-dialog__body) {
|
||
max-height: 72vh;
|
||
overflow: auto;
|
||
}
|
||
</style>
|