更改前端代码
parent
acd5ffce20
commit
63555f9094
|
|
@ -394,7 +394,9 @@ function toPersonalOpenListVo(a) {
|
||||||
customerName: a.customerName,
|
customerName: a.customerName,
|
||||||
certificateNumber: a.certificateNumber,
|
certificateNumber: a.certificateNumber,
|
||||||
mobilePhone: a.mobilePhone,
|
mobilePhone: a.mobilePhone,
|
||||||
bankCardNumber: a.bankCardNumber,
|
// 列表页展示开户成功后的主账户账号(未开立成功前为空字符串),不展示银行卡号,
|
||||||
|
// 与企业开户申请列表 toEnterpriseOpenListVo 的字段取舍保持一致
|
||||||
|
mainAccountNo: a.mainAccountNo || '',
|
||||||
channelNo: a.channelNo,
|
channelNo: a.channelNo,
|
||||||
applicationStatus: a.applicationStatus,
|
applicationStatus: a.applicationStatus,
|
||||||
failReason: a.failReason,
|
failReason: a.failReason,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
:row-key="rowKey"
|
:row-key="rowKey"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:row-selection="tableRowSelection"
|
:row-selection="tableRowSelection"
|
||||||
|
:scroll="scroll"
|
||||||
@change="handleTableChange"
|
@change="handleTableChange"
|
||||||
>
|
>
|
||||||
<template v-for="name in forwardSlotNames" #[name]="slotProps" :key="name">
|
<template v-for="name in forwardSlotNames" #[name]="slotProps" :key="name">
|
||||||
|
|
@ -41,7 +42,12 @@ const props = defineProps({
|
||||||
pageSize: { type: Number, default: 10 },
|
pageSize: { type: Number, default: 10 },
|
||||||
// 兼容原有 Boolean 用法(checkbox 多选);新增字符串 'radio' 支持单选场景(如钱包账户列表)
|
// 兼容原有 Boolean 用法(checkbox 多选);新增字符串 'radio' 支持单选场景(如钱包账户列表)
|
||||||
rowSelection: { type: [Boolean, String], default: true },
|
rowSelection: { type: [Boolean, String], default: true },
|
||||||
selectedRowKeys: { type: Array, default: () => [] }
|
selectedRowKeys: { type: Array, default: () => [] },
|
||||||
|
// 透传给内部 a-table 的 scroll 配置。列较多且有列声明了 fixed(如"操作"列固定在最右侧)时,
|
||||||
|
// 需要显式传 { x: 'max-content' } 之类的配置,否则 antd vue 会因存在 fixed 列而强制把
|
||||||
|
// table-layout 改成 fixed,导致未设置 width 的列被平均压缩宽度、表头文字挤在一起换行成竖排
|
||||||
|
// (仅有 fixed 列而不设 scroll.x 时的典型 bug 表现),详见 vc-table Table.js mergedTableLayout。
|
||||||
|
scroll: { type: Object, default: undefined }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:selectedRowKeys', 'search'])
|
const emit = defineEmits(['update:selectedRowKeys', 'search'])
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<a-spin :spinning="busy" :tip="busyTip">
|
<a-spin :spinning="busy" :tip="busyTip">
|
||||||
<a-page-header :title="pageTitle" @back="goBack">
|
<a-page-header :title="pageTitle" @back="goBack">
|
||||||
<template v-if="isCreate && !submittedApplicationNo" #extra>
|
<template v-if="isCreate && !submittedApplicationNo" #extra>
|
||||||
<a-space wrap>
|
<a-space wrap ref="agreementSectionRef">
|
||||||
<a-checkbox v-model:checked="agreementChecked">本人已阅读并同意</a-checkbox>
|
<a-checkbox v-model:checked="agreementChecked">本人已阅读并同意</a-checkbox>
|
||||||
<a @click="showAgreement('service')">《浙江稠州商业银行用户支付服务协议》</a>
|
<a @click="showAgreement('service')">《浙江稠州商业银行用户支付服务协议》</a>
|
||||||
<a @click="showAgreement('privacy')">《浙江稠州商业银行用户支付服务隐私政策》</a>
|
<a @click="showAgreement('privacy')">《浙江稠州商业银行用户支付服务隐私政策》</a>
|
||||||
|
|
@ -62,15 +62,19 @@
|
||||||
:disabled="isDetail"
|
:disabled="isDetail"
|
||||||
class="open-form"
|
class="open-form"
|
||||||
>
|
>
|
||||||
|
<div ref="channelSectionRef">
|
||||||
<a-form-item label="渠道归属" required>
|
<a-form-item label="渠道归属" required>
|
||||||
<ChannelSelect v-model:model-value="channelNo" placeholder="请选择核心企业" style="width: 300px" />
|
<ChannelSelect v-model:model-value="channelNo" placeholder="请选择核心企业" style="width: 300px" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div ref="customerSectionRef">
|
||||||
<a-form-item label="客户" required>
|
<a-form-item label="客户" required>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-input :value="form.customerName" placeholder="请选择客户" disabled style="width: 200px" />
|
<a-input :value="form.customerName" placeholder="请选择客户" disabled style="width: 200px" />
|
||||||
<a-button v-if="!isDetail" @click="openPicker">{{ form.customerId ? '重新选择' : '选择' }}</a-button>
|
<a-button v-if="!isDetail" @click="openPicker">{{ form.customerId ? '重新选择' : '选择' }}</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</div>
|
||||||
<a-form-item label="身份证号" required>
|
<a-form-item label="身份证号" required>
|
||||||
<a-input :value="form.certificateNumber" disabled placeholder="请输入身份证号" />
|
<a-input :value="form.certificateNumber" disabled placeholder="请输入身份证号" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -112,6 +116,7 @@
|
||||||
<a-input v-model:value="form.jobNote" placeholder="职业为“其他”时请填写备注" style="width: 300px" />
|
<a-input v-model:value="form.jobNote" placeholder="职业为“其他”时请填写备注" style="width: 300px" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<div ref="certPhotoSectionRef">
|
||||||
<a-form-item label="证件照片" :required="isCreate">
|
<a-form-item label="证件照片" :required="isCreate">
|
||||||
<a-space size="large" align="start">
|
<a-space size="large" align="start">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -156,6 +161,7 @@
|
||||||
身份证信息与所选客户信息不一致,请核实后重新上传身份证或重新选择客户,当前无法提交
|
身份证信息与所选客户信息不一致,请核实后重新上传身份证或重新选择客户,当前无法提交
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-form-item label="银行卡号" name="bankCardNumber" required validate-trigger="blur">
|
<a-form-item label="银行卡号" name="bankCardNumber" required validate-trigger="blur">
|
||||||
<a-input v-model:value="form.bankCardNumber" placeholder="请输入16位或19位银行卡号" style="width: 300px" />
|
<a-input v-model:value="form.bankCardNumber" placeholder="请输入16位或19位银行卡号" style="width: 300px" />
|
||||||
|
|
@ -245,6 +251,19 @@ const pickerRef = ref(null)
|
||||||
const channelNo = ref(undefined)
|
const channelNo = ref(undefined)
|
||||||
const agreementChecked = ref(false)
|
const agreementChecked = ref(false)
|
||||||
|
|
||||||
|
// 提交前校验未通过时,定位到对应必填项所在区域(部分必填项不是 a-form-item 的 name 字段,
|
||||||
|
// 无法复用 antd 表单自带的 scrollToField,需要各自挂一个容器 ref 手动滚动)
|
||||||
|
const agreementSectionRef = ref(null)
|
||||||
|
const channelSectionRef = ref(null)
|
||||||
|
const customerSectionRef = ref(null)
|
||||||
|
const certPhotoSectionRef = ref(null)
|
||||||
|
function scrollToSection(sectionRef) {
|
||||||
|
const el = sectionRef?.value?.$el || sectionRef?.value
|
||||||
|
if (el && typeof el.scrollIntoView === 'function') {
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mode = computed(() => {
|
const mode = computed(() => {
|
||||||
if (route.path.endsWith('/edit')) return 'edit'
|
if (route.path.endsWith('/edit')) return 'edit'
|
||||||
if (route.path.endsWith('/detail')) return 'detail'
|
if (route.path.endsWith('/detail')) return 'detail'
|
||||||
|
|
@ -371,8 +390,10 @@ const rules = {
|
||||||
if (res.data.code === 200) {
|
if (res.data.code === 200) {
|
||||||
lastVerifiedCardKey.value = key
|
lastVerifiedCardKey.value = key
|
||||||
// 静默回填开户行信息,不在页面展示,仅随提交请求体一起带给后端(选填字段)
|
// 静默回填开户行信息,不在页面展示,仅随提交请求体一起带给后端(选填字段)
|
||||||
|
// 注意:query-card-info 响应里 bankNo 是"银行编码"(标识是哪家银行),
|
||||||
|
// payBankNo 才是"支付行号"(即 create-draft 需要的"开户行号"),两者语义不同,取行号必须用 payBankNo
|
||||||
form.bankName = res.data.data?.bankName || ''
|
form.bankName = res.data.data?.bankName || ''
|
||||||
form.bankNo = res.data.data?.bankNo || ''
|
form.bankNo = res.data.data?.payBankNo || ''
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
form.bankName = ''
|
form.bankName = ''
|
||||||
|
|
@ -575,36 +596,50 @@ onMounted(() => {
|
||||||
|
|
||||||
// 提交前的全部前置校验:协议勾选/渠道/客户/证件照片上传状态/身份证信息一致性/表单必填项。
|
// 提交前的全部前置校验:协议勾选/渠道/客户/证件照片上传状态/身份证信息一致性/表单必填项。
|
||||||
// 供"确认"按钮预检(create 模式,通过后弹二次确认弹窗)与编辑模式"保存"按钮共用。
|
// 供"确认"按钮预检(create 模式,通过后弹二次确认弹窗)与编辑模式"保存"按钮共用。
|
||||||
|
// 校验未通过时,按字段在页面上出现的先后顺序定位并滚动到第一个未填写的必填项,而不是只弹错误提示,
|
||||||
|
// 避免表单字段较多时用户不知道要去哪里补填。
|
||||||
async function validateBeforeSubmit() {
|
async function validateBeforeSubmit() {
|
||||||
if (isCreate.value && !agreementChecked.value) {
|
if (isCreate.value && !agreementChecked.value) {
|
||||||
|
scrollToSection(agreementSectionRef)
|
||||||
showErrorModal('请先阅读并勾选同意用户支付服务协议与隐私政策')
|
showErrorModal('请先阅读并勾选同意用户支付服务协议与隐私政策')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!channelNo.value) {
|
if (!channelNo.value) {
|
||||||
|
scrollToSection(channelSectionRef)
|
||||||
showErrorModal('请先选择渠道归属')
|
showErrorModal('请先选择渠道归属')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!form.customerId) {
|
if (!form.customerId) {
|
||||||
|
scrollToSection(customerSectionRef)
|
||||||
showErrorModal('请先选择客户')
|
showErrorModal('请先选择客户')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (idCardFrontUploading.value || idCardBackUploading.value || facePhotoUploading.value) {
|
if (idCardFrontUploading.value || idCardBackUploading.value || facePhotoUploading.value) {
|
||||||
|
scrollToSection(certPhotoSectionRef)
|
||||||
showErrorModal('证件照片正在上传中,请稍候')
|
showErrorModal('证件照片正在上传中,请稍候')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// 编辑模式下若未重新上传,保留 loadDetail 回填的原 fileNo 即可提交,故只在创建时强制校验必须已上传
|
// 编辑模式下若未重新上传,保留 loadDetail 回填的原 fileNo 即可提交,故只在创建时强制校验必须已上传
|
||||||
if (isCreate.value && (!idCardFrontFileNo.value || !idCardBackFileNo.value || !facePhotoFileNo.value)) {
|
if (isCreate.value && (!idCardFrontFileNo.value || !idCardBackFileNo.value || !facePhotoFileNo.value)) {
|
||||||
|
scrollToSection(certPhotoSectionRef)
|
||||||
showErrorModal('请上传身份证人像面、国徽面和正面免冠照')
|
showErrorModal('请上传身份证人像面、国徽面和正面免冠照')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (identityMismatch.value) {
|
if (identityMismatch.value) {
|
||||||
|
scrollToSection(certPhotoSectionRef)
|
||||||
showErrorModal('身份证信息与所选客户信息不一致,请核实后重新上传身份证或重新选择客户')
|
showErrorModal('身份证信息与所选客户信息不一致,请核实后重新上传身份证或重新选择客户')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await formRef.value.validate()
|
await formRef.value.validate()
|
||||||
return true
|
return true
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
// 职业/职业备注/银行卡号/手机号等 a-form-item 已声明 name+rules 的字段,校验失败时定位到
|
||||||
|
// 表单内第一个报错字段(antd vue Form 实例内置 scrollToField,按 fieldId 找到对应 DOM 节点滚动)
|
||||||
|
const firstErrorName = err?.errorFields?.[0]?.name
|
||||||
|
if (firstErrorName) {
|
||||||
|
formRef.value.scrollToField(firstErrorName, { behavior: 'smooth', block: 'center' })
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:fetch-data="loadList"
|
:fetch-data="loadList"
|
||||||
:row-selection="false"
|
:row-selection="false"
|
||||||
|
:scroll="{ x: 'max-content' }"
|
||||||
:initial-search="{ applicationNo: '', customerNameLike: '', certificateNumber: '', applicationStatus: undefined, startDate: undefined, endDate: undefined, organizationId: getCurrentOrganizationId() }"
|
:initial-search="{ applicationNo: '', customerNameLike: '', certificateNumber: '', applicationStatus: undefined, startDate: undefined, endDate: undefined, organizationId: getCurrentOrganizationId() }"
|
||||||
>
|
>
|
||||||
<template #search="{ form }">
|
<template #search="{ form }">
|
||||||
|
|
@ -97,18 +98,24 @@ const statusMap = {
|
||||||
APPLY_FAILED: { text: '申请失败', color: 'red' }
|
APPLY_FAILED: { text: '申请失败', color: 'red' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 列数较多且"操作"列需要 fixed:'right' 固定,必须给每一列都设置明确的 width,并配合
|
||||||
|
// ProTable 的 scroll={x:'max-content'}(见上方模板),否则 antd vue 会因存在 fixed 列强制把
|
||||||
|
// table-layout 改成 fixed,未设宽度的列会被平均压缩,导致表头文字挤在一起换行成竖排。
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '序号', dataIndex: 'index', width: 60 },
|
{ title: '序号', dataIndex: 'index', width: 60 },
|
||||||
{ title: '申请编号', dataIndex: 'applicationNo' },
|
{ title: '申请编号', dataIndex: 'applicationNo', width: 220 },
|
||||||
{ title: '客户姓名', dataIndex: 'customerName' },
|
{ title: '客户姓名', dataIndex: 'customerName', width: 110 },
|
||||||
{ title: '身份证号', dataIndex: 'certificateNumber' },
|
{ title: '身份证号', dataIndex: 'certificateNumber', width: 180 },
|
||||||
{ title: '手机号', dataIndex: 'mobilePhone' },
|
{ title: '手机号', dataIndex: 'mobilePhone', width: 130 },
|
||||||
{ title: '银行卡号', dataIndex: 'bankCardNumber' },
|
// 列表页不需要展示银行卡号(客户提交时用于绑卡的敏感信息),改为展示开户成功后的主账户账号,
|
||||||
{ title: '状态', dataIndex: 'applicationStatus' },
|
// 未开立成功前该字段为空,与 EnterpriseOpenList.vue 的"主账户账号"列保持一致的展示方式
|
||||||
{ title: '渠道编号', dataIndex: 'channelNo' },
|
{ title: '主账户账号', dataIndex: 'mainAccountNo', width: 180 },
|
||||||
{ title: '失败原因', dataIndex: 'failReason' },
|
{ title: '状态', dataIndex: 'applicationStatus', width: 110 },
|
||||||
{ title: '创建时间', dataIndex: 'createdAt' },
|
{ title: '渠道编号', dataIndex: 'channelNo', width: 110 },
|
||||||
{ title: '操作', dataIndex: 'action', width: 200 }
|
{ title: '失败原因', dataIndex: 'failReason', width: 320 },
|
||||||
|
{ title: '创建时间', dataIndex: 'createdAt', width: 170 },
|
||||||
|
// 操作列固定在表格最右侧,避免字段较多、横向滚动时"查看/编辑/生成二维码"按钮被滚出可视区域
|
||||||
|
{ title: '操作', dataIndex: 'action', width: 200, fixed: 'right' }
|
||||||
]
|
]
|
||||||
|
|
||||||
async function loadList(params) {
|
async function loadList(params) {
|
||||||
|
|
|
||||||
|
|
@ -905,7 +905,10 @@
|
||||||
`applicationNo`/`customerNameLike`(客户姓名模糊)/`certificateNumber`/`applicationStatus`/
|
`applicationNo`/`customerNameLike`(客户姓名模糊)/`certificateNumber`/`applicationStatus`/
|
||||||
`startDate`/`endDate`/`page`/`pageSize`/`orgId`,响应 `{total,page,pageSize,records}`,
|
`startDate`/`endDate`/`page`/`pageSize`/`orgId`,响应 `{total,page,pageSize,records}`,
|
||||||
`records` 内每项含 `applicationNo`/`customerName`/`certificateNumber`/`mobilePhone`/
|
`records` 内每项含 `applicationNo`/`customerName`/`certificateNumber`/`mobilePhone`/
|
||||||
`bankCardNumber`/`channelNo`/`applicationStatus`/`failReason`/`createdAt`/`updatedAt`。
|
`mainAccountNo`(开户成功后的主账户账号,未开立成功前为空)/`channelNo`/
|
||||||
|
`applicationStatus`/`failReason`/`createdAt`/`updatedAt`。**2026-08-07 起列表页不再展示
|
||||||
|
银行卡号,改为展示 `mainAccountNo`**,与 `enterprise-open-application/list` 的
|
||||||
|
"主账户账号"列口径保持一致。
|
||||||
- `POST /api/wallet-management/personal-opening/detail`:假设请求 `{applicationNo}`,响应
|
- `POST /api/wallet-management/personal-opening/detail`:假设请求 `{applicationNo}`,响应
|
||||||
字段在 `create-draft` 请求体(`accountProperty`/`customerId`/`certificateType`/
|
字段在 `create-draft` 请求体(`accountProperty`/`customerId`/`certificateType`/
|
||||||
`certificateNumber`/`certificateEffectiveDate`/`certificateExpiryDate`/`issuingAuthority`/
|
`certificateNumber`/`certificateEffectiveDate`/`certificateExpiryDate`/`issuingAuthority`/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue