feat: 新增企业客户新增/编辑/查看表单页
parent
3e3a2097a3
commit
801cadbcc4
|
|
@ -0,0 +1,275 @@
|
||||||
|
<!-- src/views/customer/enterprise/EnterpriseForm.vue -->
|
||||||
|
<template>
|
||||||
|
<div class="enterprise-customer-form-page">
|
||||||
|
<a-page-header :title="pageTitle" @back="goBack" />
|
||||||
|
<a-card>
|
||||||
|
<a-form ref="formRef" layout="vertical" :model="form" :rules="rules" :disabled="isDetail">
|
||||||
|
<a-form-item v-if="!isDetail" label="所属渠道" required>
|
||||||
|
<ChannelSelect v-model:model-value="channelNo" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="营业执照照片">
|
||||||
|
<LicenseUpload :channel-no="channelNo" v-model:file-no="form.businessLicenseFileNo" />
|
||||||
|
<div v-if="isEdit" class="hint">编辑模式下无法读取历史营业执照图片,如需变更请重新上传</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="所属机构" name="organizationId">
|
||||||
|
<OrgTreeSelect v-model="form.organizationId" placeholder="请选择所属机构" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="企业名称" name="enterpriseName">
|
||||||
|
<a-input v-model:value="form.enterpriseName" placeholder="请输入企业名称" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="证件类型">
|
||||||
|
<a-input value="营业执照" disabled />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="营业执照号" name="businessLicense">
|
||||||
|
<a-input v-model:value="form.businessLicense" placeholder="请输入营业执照号" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="证件生效日" name="certificateEffectiveDate">
|
||||||
|
<a-date-picker v-model:value="effectiveDateValue" style="width: 100%" value-format="YYYY-MM-DD" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="证件到期日" name="certificateExpiryDate">
|
||||||
|
<a-date-picker v-model:value="expiryDateValue" style="width: 100%" value-format="YYYY-MM-DD" />
|
||||||
|
<div v-if="isExpired" class="expired-hint">证件已过期,请更换有效证件</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="手机号码" name="mobilePhone">
|
||||||
|
<a-input v-model:value="form.mobilePhone" placeholder="请输入11位手机号" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="证件地址" name="certificateAddress">
|
||||||
|
<a-input v-model:value="form.certificateAddress" placeholder="请输入详细地址" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="经营范围" name="businessScope">
|
||||||
|
<a-textarea v-model:value="form.businessScope" placeholder="请输入经营范围" :rows="3" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="法定代表人">
|
||||||
|
<a-space>
|
||||||
|
<span v-if="legalRepName">{{ legalRepName }}</span>
|
||||||
|
<a-button v-if="!isDetail" @click="openLegalRepPicker">选择</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="isCreate" label="股东高管信息">
|
||||||
|
<a-table :columns="relatedPersonColumns" :data-source="relatedPersonList" :pagination="false" row-key="individualId">
|
||||||
|
<template #bodyCell="{ column, record, index }">
|
||||||
|
<template v-if="column.dataIndex === 'customerName'">{{ record.customerName }}</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'relatedType'">
|
||||||
|
<a-select v-model:value="record.relatedType" style="width: 140px">
|
||||||
|
<a-select-option value="SHAREHOLDER">股东</a-select-option>
|
||||||
|
<a-select-option value="EXECUTIVE">高管</a-select-option>
|
||||||
|
<a-select-option value="BENEFICIARY_OWNER">受益所有人</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'certificateNumber'">{{ record.certificateNumber }}</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'action'">
|
||||||
|
<a @click="removeRelatedPerson(index)">删除</a>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<a-button style="margin-top: 8px" @click="openRelatedPersonPicker">添加股东/高管</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-else label="股东高管信息">
|
||||||
|
<div class="hint">股东高管信息仅支持新增时登记,当前接口不支持查询或修改,如需变更请联系技术支持核实数据</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<a-space v-if="!isDetail">
|
||||||
|
<a-button type="primary" :loading="submitting" @click="handleSubmit">提交</a-button>
|
||||||
|
<a-button @click="goBack">取消</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<PersonalCustomerPicker ref="legalRepPickerRef" @select="handleLegalRepSelect" />
|
||||||
|
<PersonalCustomerPicker ref="relatedPersonPickerRef" @select="handleRelatedPersonSelect" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import ChannelSelect from '@/components/ChannelSelect.vue'
|
||||||
|
import LicenseUpload from '@/components/LicenseUpload.vue'
|
||||||
|
import OrgTreeSelect from '@/components/OrgTreeSelect.vue'
|
||||||
|
import PersonalCustomerPicker from '@/components/PersonalCustomerPicker.vue'
|
||||||
|
import {
|
||||||
|
fetchEnterpriseCustomerDetailApi,
|
||||||
|
createEnterpriseCustomerApi,
|
||||||
|
updateEnterpriseCustomerApi
|
||||||
|
} from '@/api/customer'
|
||||||
|
import { fetchPersonalCustomerDetailApi } from '@/api/customer'
|
||||||
|
import { phoneValidatorRule, businessLicenseValidatorRule } from '@/utils/validators'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const mode = computed(() => route.query.mode || 'create')
|
||||||
|
const isCreate = computed(() => mode.value === 'create')
|
||||||
|
const isEdit = computed(() => mode.value === 'edit')
|
||||||
|
const isDetail = computed(() => mode.value === 'detail')
|
||||||
|
const pageTitle = computed(() => ({ create: '新增企业客户', edit: '编辑企业客户', detail: '查看企业客户' }[mode.value]))
|
||||||
|
|
||||||
|
const channelNo = ref(undefined)
|
||||||
|
const submitting = ref(false)
|
||||||
|
const formRef = ref(null)
|
||||||
|
const legalRepName = ref('')
|
||||||
|
const legalRepPickerRef = ref(null)
|
||||||
|
const relatedPersonPickerRef = ref(null)
|
||||||
|
const relatedPersonList = ref([])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
organizationId: undefined,
|
||||||
|
enterpriseName: '',
|
||||||
|
businessLicense: '',
|
||||||
|
certificateEffectiveDate: '',
|
||||||
|
certificateExpiryDate: '',
|
||||||
|
mobilePhone: '',
|
||||||
|
certificateAddress: '',
|
||||||
|
businessScope: '',
|
||||||
|
businessLicenseFileNo: '',
|
||||||
|
legalRepresentativeId: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const effectiveDateValue = computed({
|
||||||
|
get: () => (form.certificateEffectiveDate ? dayjs(form.certificateEffectiveDate) : undefined),
|
||||||
|
set: (val) => (form.certificateEffectiveDate = val ? val.format('YYYY-MM-DD') : '')
|
||||||
|
})
|
||||||
|
const expiryDateValue = computed({
|
||||||
|
get: () => (form.certificateExpiryDate ? dayjs(form.certificateExpiryDate) : undefined),
|
||||||
|
set: (val) => (form.certificateExpiryDate = val ? val.format('YYYY-MM-DD') : '')
|
||||||
|
})
|
||||||
|
const isExpired = computed(
|
||||||
|
() => !!form.certificateExpiryDate && dayjs(form.certificateExpiryDate).isBefore(dayjs(), 'day')
|
||||||
|
)
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
organizationId: [{ required: true, message: '请选择所属机构' }],
|
||||||
|
enterpriseName: [{ required: true, message: '请输入企业名称' }],
|
||||||
|
businessLicense: [businessLicenseValidatorRule()],
|
||||||
|
mobilePhone: [phoneValidatorRule()]
|
||||||
|
}
|
||||||
|
|
||||||
|
const relatedPersonColumns = [
|
||||||
|
{ title: '客户名称', dataIndex: 'customerName' },
|
||||||
|
{ title: '关联人类型', dataIndex: 'relatedType', width: 160 },
|
||||||
|
{ title: '证件号码', dataIndex: 'certificateNumber' },
|
||||||
|
{ title: '操作', dataIndex: 'action', width: 80 }
|
||||||
|
]
|
||||||
|
|
||||||
|
function openLegalRepPicker() {
|
||||||
|
legalRepPickerRef.value.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLegalRepSelect(record) {
|
||||||
|
form.legalRepresentativeId = record.id
|
||||||
|
legalRepName.value = record.customerName
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRelatedPersonPicker() {
|
||||||
|
relatedPersonPickerRef.value.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRelatedPersonSelect(record) {
|
||||||
|
// IndividualCustomerListVo 不含证件生效日,需要额外查一次详情补全
|
||||||
|
const detailRes = await fetchPersonalCustomerDetailApi({ id: record.id })
|
||||||
|
const detail = detailRes.data.code === 200 ? detailRes.data.data : record
|
||||||
|
relatedPersonList.value.push({
|
||||||
|
individualId: record.id,
|
||||||
|
customerName: record.customerName,
|
||||||
|
relatedType: 'SHAREHOLDER',
|
||||||
|
certificateType: detail.certificateType || 'ID_CARD',
|
||||||
|
certificateNumber: detail.certificateNumber,
|
||||||
|
certificateEffectiveDate: detail.certificateEffectiveDate,
|
||||||
|
certificateExpiryDate: detail.certificateExpiryDate
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRelatedPerson(index) {
|
||||||
|
relatedPersonList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadDetail(id) {
|
||||||
|
const res = await fetchEnterpriseCustomerDetailApi({ id })
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
Object.assign(form, res.data.data)
|
||||||
|
if (res.data.data.legalRepresentativeId) {
|
||||||
|
const repRes = await fetchPersonalCustomerDetailApi({ id: res.data.data.legalRepresentativeId })
|
||||||
|
if (repRes.data.code === 200) legalRepName.value = repRes.data.data.customerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.id) {
|
||||||
|
loadDetail(route.query.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.push('/customer/enterprise')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
const toFullDate = (d) => (d ? `${d} 00:00:00` : undefined)
|
||||||
|
const payload = {
|
||||||
|
organizationId: form.organizationId,
|
||||||
|
enterpriseName: form.enterpriseName,
|
||||||
|
businessLicense: form.businessLicense,
|
||||||
|
certificateType: 'BUSINESS_LICENSE',
|
||||||
|
certificateNumber: form.businessLicense,
|
||||||
|
certificateEffectiveDate: toFullDate(form.certificateEffectiveDate),
|
||||||
|
certificateExpiryDate: toFullDate(form.certificateExpiryDate),
|
||||||
|
mobilePhone: form.mobilePhone,
|
||||||
|
certificateAddress: form.certificateAddress,
|
||||||
|
businessScope: form.businessScope,
|
||||||
|
businessLicenseFileNo: form.businessLicenseFileNo,
|
||||||
|
legalRepresentativeId: form.legalRepresentativeId
|
||||||
|
}
|
||||||
|
let res
|
||||||
|
if (isCreate.value) {
|
||||||
|
const enterpriseRelatedPersonBtoList = relatedPersonList.value.map((item) => ({
|
||||||
|
individualId: item.individualId,
|
||||||
|
relatedType: item.relatedType,
|
||||||
|
certificateType: item.certificateType,
|
||||||
|
certificateNumber: item.certificateNumber,
|
||||||
|
certificateEffectiveDate: toFullDate(item.certificateEffectiveDate),
|
||||||
|
certificateExpiryDate: toFullDate(item.certificateExpiryDate)
|
||||||
|
}))
|
||||||
|
res = await createEnterpriseCustomerApi({
|
||||||
|
createEnterpriseCustomerBto: { ...payload, enterpriseRelatedPersonBtoList },
|
||||||
|
enableOcr: true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res = await updateEnterpriseCustomerApi({ id: form.id, ...payload })
|
||||||
|
}
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
message.success(isCreate.value ? '新增成功' : '修改成功')
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.expired-hint {
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue