feat(credit): 完成阶段5授信管理开发(贷款申请-个人/企业)
- 新增 src/api/credit.js:credit-apply 组6个贷款申请接口(list/detail/personal-create/update/enterprise-create/update) - 新增 src/constants/creditEnums.js:从swagger程序化提取的民族/学历/婚姻/商品类目/回款周期/紧急联系人关系等枚举字典 - 新增贷款申请列表页(个人贷款/企业贷款分类新增入口,DRAFT状态可编辑) - 新增个人贷款申请表单(基础信息+融资条件+经营实体+个人信息+紧急联系人+配偶信息+影像资料) - 新增企业贷款申请表单(基础信息+融资条件+企业信息+特殊融资用途+法定代表人+配偶信息+紧急联系人+影像资料) - 路由注册授信管理列表与6条表单extraChildRoutes - 追加《缺失的后端接口.md》授信管理接口速查(1.12)及缺口事项26-31(重点:个人贷款草稿无customerId/accountNo字段、贷款产品无字典接口、两组平行贷款申请接口语义重叠)main
parent
99eaab4ef1
commit
1acc512e62
|
|
@ -0,0 +1,21 @@
|
|||
import request from './request'
|
||||
|
||||
// ---------------- 贷款申请(/api/credit-apply/*) ----------------
|
||||
// 说明:swagger 中同时存在 /api/loan-application/* (create-personal/create-enterprise/detail/page)
|
||||
// 与本文件采用的 /api/credit-apply/* 两组平行接口。本项目统一采用 credit-apply 组,因其
|
||||
// 描述明确标注为"银行柜员录入/查询"场景且遵循与阶段4一致的"create草稿(DRAFT)+柜员update+
|
||||
// 客户端扫码confirm"三段式流程,与本管理后台定位一致;/api/loan-application/* 组用途存疑,
|
||||
// 已登记于《缺失的后端接口.md》。
|
||||
export const fetchLoanApplicationListApi = (data) => request.post('/api/credit-apply/loan-application/list', data)
|
||||
export const fetchLoanApplicationDetailApi = (data) => request.post('/api/credit-apply/loan-application/detail', data)
|
||||
export const createPersonalLoanApplicationApi = (data) =>
|
||||
request.post('/api/credit-apply/personal-loan-application/create', data)
|
||||
export const updatePersonalLoanApplicationApi = (data) =>
|
||||
request.post('/api/credit-apply/personal-loan-application/update', data)
|
||||
export const createEnterpriseLoanApplicationApi = (data) =>
|
||||
request.post('/api/credit-apply/enterprise-loan-application/create', data)
|
||||
export const updateEnterpriseLoanApplicationApi = (data) =>
|
||||
request.post('/api/credit-apply/enterprise-loan-application/update', data)
|
||||
|
||||
// 注:confirm 接口(/api/credit-apply/loan-application/confirm)语义为"申请人微信扫码确认",
|
||||
// 需短信验证码+人脸识别,属移动端客户操作流程,不在本管理后台前端实现范围,不在此导出。
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
// src/constants/creditEnums.js
|
||||
// 授信管理(贷款申请)相关枚举字典,全部取值与中文标签均从 swagger 组件 schema 的
|
||||
// description 字段程序化提取,未做任何人工编造。
|
||||
|
||||
export const ETHNICITY_OPTIONS = [
|
||||
{ value: "HAN", label: "汉族" },
|
||||
{ value: "ZHUANG", label: "壮族" },
|
||||
{ value: "MANCHU", label: "满族" },
|
||||
{ value: "HUI", label: "回族" },
|
||||
{ value: "MIAO", label: "苗族" },
|
||||
{ value: "UYGHUR", label: "维吾尔族" },
|
||||
{ value: "TUJIA", label: "土家族" },
|
||||
{ value: "YI", label: "彝族" },
|
||||
{ value: "MONGOL", label: "蒙古族" },
|
||||
{ value: "TIBETAN", label: "藏族" },
|
||||
{ value: "BUYI", label: "布依族" },
|
||||
{ value: "DONG", label: "侗族" },
|
||||
{ value: "YAO", label: "瑶族" },
|
||||
{ value: "KOREAN", label: "朝鲜族" },
|
||||
{ value: "BAI", label: "白族" },
|
||||
{ value: "HANI", label: "哈尼族" },
|
||||
{ value: "LI", label: "黎族" },
|
||||
{ value: "KAZAKH", label: "哈萨克族" },
|
||||
{ value: "DAI", label: "傣族" },
|
||||
{ value: "SHE", label: "畲族" },
|
||||
{ value: "LISU", label: "傈僳族" },
|
||||
{ value: "GELAO", label: "仡佬族" },
|
||||
{ value: "DONGXIANG", label: "东乡族" },
|
||||
{ value: "LAHU", label: "拉祜族" },
|
||||
{ value: "WA", label: "佤族" },
|
||||
{ value: "SHUI", label: "水族" },
|
||||
{ value: "NAXI", label: "纳西族" },
|
||||
{ value: "QIANG", label: "羌族" },
|
||||
{ value: "TU", label: "土族" },
|
||||
{ value: "XIBE", label: "锡伯族" },
|
||||
{ value: "KYRGYZ", label: "柯尔克孜族" },
|
||||
{ value: "JINGPO", label: "景颇族" },
|
||||
{ value: "DAUR", label: "达斡尔族" },
|
||||
{ value: "MULAM", label: "仫佬族" },
|
||||
{ value: "BLANG", label: "布朗族" },
|
||||
{ value: "SALAR", label: "撒拉族" },
|
||||
{ value: "MAONAN", label: "毛南族" },
|
||||
{ value: "GILIM", label: "吉尔吉斯族" },
|
||||
{ value: "TAO", label: "塔吉克族" },
|
||||
{ value: "PUMI", label: "普米族" },
|
||||
{ value: "ACHANG", label: "阿昌族" },
|
||||
{ value: "NU", label: "怒族" },
|
||||
{ value: "EWE", label: "鄂温克族" },
|
||||
{ value: "DEANG", label: "德昂族" },
|
||||
{ value: "BONAN", label: "保安族" },
|
||||
{ value: "RUSSIAN", label: "俄罗斯族" },
|
||||
{ value: "YUGUR", label: "裕固族" },
|
||||
{ value: "UZBEK", label: "乌兹别克族" },
|
||||
{ value: "MONBA", label: "门巴族" },
|
||||
{ value: "OROCHEN", label: "鄂伦春族" },
|
||||
{ value: "DERUNG", label: "独龙族" },
|
||||
{ value: "HEZHENG", label: "赫哲族" },
|
||||
{ value: "GAOSHAN", label: "高山族" },
|
||||
{ value: "LOP", label: "珞巴族" },
|
||||
{ value: "JINO", label: "基诺族" },
|
||||
{ value: "OTHER", label: "其他" }
|
||||
]
|
||||
|
||||
export const EDUCATION_LEVEL_OPTIONS = [
|
||||
{ value: "PRIMARY_SCHOOL", label: "小学" },
|
||||
{ value: "JUNIOR_HIGH", label: "初中" },
|
||||
{ value: "SENIOR_HIGH", label: "高中" },
|
||||
{ value: "TECHNICAL_SECONDARY", label: "中专" },
|
||||
{ value: "JUNIOR_COLLEGE", label: "大专" },
|
||||
{ value: "BACHELOR", label: "本科" },
|
||||
{ value: "MASTER", label: "硕士" },
|
||||
{ value: "DOCTOR", label: "博士" },
|
||||
{ value: "OTHER", label: "其他" }
|
||||
]
|
||||
|
||||
export const MARITAL_STATUS_OPTIONS = [
|
||||
{ value: "UNMARRIED", label: "未婚" },
|
||||
{ value: "MARRIED", label: "已婚" },
|
||||
{ value: "DIVORCED", label: "离异" },
|
||||
{ value: "WIDOWED", label: "丧偶" }
|
||||
]
|
||||
|
||||
export const GOODS_CATEGORY_OPTIONS = [
|
||||
{ value: "SPECIAL_GOODS", label: "特殊商品" },
|
||||
{ value: "GENERAL_GOODS", label: "一般商品" },
|
||||
{ value: "BIG_ECOMMERCE", label: "大型电商" },
|
||||
{ value: "HEAD_GOODS", label: "头部商品" },
|
||||
{ value: "FUNDS", label: "资金类" },
|
||||
{ value: "CONSIGNMENT", label: "寄售" },
|
||||
{ value: "EQUITY", label: "权益" },
|
||||
{ value: "LOGISTICS", label: "物流" }
|
||||
]
|
||||
|
||||
export const PAYMENT_CYCLE_OPTIONS = [
|
||||
{ value: "DAY_22", label: "22天" },
|
||||
{ value: "DAY_25", label: "25天" },
|
||||
{ value: "DAY_30", label: "30天" },
|
||||
{ value: "DAY_180", label: "180天" },
|
||||
{ value: "DAY_270", label: "270天" },
|
||||
{ value: "MANUAL", label: "手动" }
|
||||
]
|
||||
|
||||
export const EMERGENCY_CONTACT_RELATION_OPTIONS = [
|
||||
{ value: "SPOUSE", label: "配偶" },
|
||||
{ value: "PARENT", label: "父母" },
|
||||
{ value: "CHILD", label: "子女" },
|
||||
{ value: "SIBLING", label: "兄弟姐妹" },
|
||||
{ value: "RELATIVE", label: "其他亲属" },
|
||||
{ value: "FRIEND", label: "朋友" },
|
||||
{ value: "COLLEAGUE", label: "同事" },
|
||||
{ value: "OTHER", label: "其他" }
|
||||
]
|
||||
|
||||
export const APPLICATION_TYPE_OPTIONS = [
|
||||
{ value: "PERSONAL", label: "个人贷款" },
|
||||
{ value: "BUSINESS", label: "企业贷款" }
|
||||
]
|
||||
|
||||
export const LOAN_APPLICATION_STATUS_OPTIONS = [
|
||||
{ value: "DRAFT", label: "待确认" },
|
||||
{ value: "CONFIRMED", label: "已确认" },
|
||||
{ value: "SUBMITTED", label: "已申请" },
|
||||
{ value: "APPLY_FAILED", label: "申请失败" }
|
||||
]
|
||||
|
||||
|
|
@ -10,7 +10,8 @@ export const routePathComponentMap = {
|
|||
'/customer/personal': 'customer/personal/PersonalList',
|
||||
'/customer/enterprise': 'customer/enterprise/EnterpriseList',
|
||||
'/wallet/account': 'wallet/account/AccountList',
|
||||
'/wallet/enterprise-open': 'wallet/enterprise-open/EnterpriseOpenList'
|
||||
'/wallet/enterprise-open': 'wallet/enterprise-open/EnterpriseOpenList',
|
||||
'/credit/loan-application': 'credit/loan-application/LoanApplicationList'
|
||||
}
|
||||
|
||||
// 新增/编辑/查看等表单页不是后端菜单节点(不出现在侧边栏),但仍需要注册为
|
||||
|
|
@ -26,5 +27,11 @@ export const extraChildRoutes = [
|
|||
{ path: 'wallet/personal-open', name: 'wallet-personal-open', component: 'wallet/personal-open/PersonalOpenForm' },
|
||||
{ path: 'wallet/enterprise-open/create', name: 'wallet-enterprise-open-create', component: 'wallet/enterprise-open/EnterpriseOpenForm' },
|
||||
{ path: 'wallet/enterprise-open/edit', name: 'wallet-enterprise-open-edit', component: 'wallet/enterprise-open/EnterpriseOpenForm' },
|
||||
{ path: 'wallet/enterprise-open/detail', name: 'wallet-enterprise-open-detail', component: 'wallet/enterprise-open/EnterpriseOpenForm' }
|
||||
{ path: 'wallet/enterprise-open/detail', name: 'wallet-enterprise-open-detail', component: 'wallet/enterprise-open/EnterpriseOpenForm' },
|
||||
{ path: 'credit/loan-application/personal/create', name: 'credit-loan-personal-create', component: 'credit/loan-application/PersonalLoanForm' },
|
||||
{ path: 'credit/loan-application/personal/edit', name: 'credit-loan-personal-edit', component: 'credit/loan-application/PersonalLoanForm' },
|
||||
{ path: 'credit/loan-application/personal/detail', name: 'credit-loan-personal-detail', component: 'credit/loan-application/PersonalLoanForm' },
|
||||
{ path: 'credit/loan-application/enterprise/create', name: 'credit-loan-enterprise-create', component: 'credit/loan-application/EnterpriseLoanForm' },
|
||||
{ path: 'credit/loan-application/enterprise/edit', name: 'credit-loan-enterprise-edit', component: 'credit/loan-application/EnterpriseLoanForm' },
|
||||
{ path: 'credit/loan-application/enterprise/detail', name: 'credit-loan-enterprise-detail', component: 'credit/loan-application/EnterpriseLoanForm' }
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,700 @@
|
|||
<!-- src/views/credit/loan-application/EnterpriseLoanForm.vue -->
|
||||
<!-- 2.2.1 贷款申请 —— 企业贷款申请表单。mode 从 route.path 后缀推导(create/edit/detail)。
|
||||
字段严格对齐 /api/credit-apply/enterprise-loan-application/create、update 的
|
||||
CreateEnterpriseLoanDraftBto/UpdateEnterpriseLoanApplicationBto。
|
||||
已知缺口(登记于《缺失的后端接口.md》):
|
||||
1) customerType 字段无 swagger enum 约束,前端按项目既有"PERSONAL/ENTERPRISE"命名习惯猜测传值'ENTERPRISE';
|
||||
2) legalPersonEducation/legalPersonMarital/legalPersonNationality/legalPersonGender/spouseGender
|
||||
均为无枚举约束的纯字符串字段,前端为提升可用性复用个人贷款相关枚举的取值域渲染下拉,
|
||||
但后端未对其做强校验,实际是否严格匹配以实测为准;
|
||||
3) "贷款钱包仅展示申请人已开立的钱包":前端在选定企业客户后按企业名称查询钱包账户列表供选择
|
||||
mainAccountNo 填入 accountNo 字段,而非专门的"按客户查已开钱包"接口(不存在该专用接口)。 -->
|
||||
<template>
|
||||
<div class="enterprise-loan-form-page">
|
||||
<a-page-header :title="pageTitle" @back="goBack" />
|
||||
|
||||
<a-card v-if="!isCreate" style="margin-bottom: 16px">
|
||||
<a-descriptions :column="3">
|
||||
<a-descriptions-item label="申请单号">{{ form.loanApplicationId || '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="申请状态">{{ statusText }}</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人">{{ detailInfo.createdBy || '-' }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
|
||||
<a-form layout="vertical" :model="form" :disabled="isDetail">
|
||||
<a-card title="基础信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="所属渠道" required>
|
||||
<ChannelSelect v-model:model-value="form.channel" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="所属机构">
|
||||
<OrgTreeSelect v-model="form.organizationId" placeholder="请选择所属机构" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="贷款产品编号" required>
|
||||
<a-input v-model:value="form.loanProductCode" placeholder="请输入贷款产品编号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="企业客户" required>
|
||||
<a-space>
|
||||
<span v-if="form.enterpriseName">{{ form.enterpriseName }}</span>
|
||||
<a-button v-if="!isDetail" @click="openCustomerPicker">选择企业客户</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="贷款钱包账号">
|
||||
<a-select
|
||||
v-model:value="form.accountNo"
|
||||
placeholder="请先选择企业客户以加载已开立钱包"
|
||||
:options="walletOptions"
|
||||
:loading="loadingWallet"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="手机号码">
|
||||
<a-input v-model:value="form.phoneNo" placeholder="请输入11位手机号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="16">
|
||||
<a-form-item label="经营范围" required>
|
||||
<a-input v-model:value="form.businessScope" placeholder="请输入经营范围/经营活动描述" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="融资条件" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="商品类目" required>
|
||||
<a-select v-model:value="form.goodsCategory" placeholder="请选择商品类目">
|
||||
<a-select-option v-for="opt in GOODS_CATEGORY_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="回款周期" required>
|
||||
<a-select v-model:value="form.paymentCycle" placeholder="请选择回款周期">
|
||||
<a-select-option v-for="opt in PAYMENT_CYCLE_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="单笔汇总订单限额(元)" required>
|
||||
<a-input v-model:value="form.singleOrderLimit" placeholder="请输入单笔汇总订单限额" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="融资比例(%)" required>
|
||||
<a-input-number v-model:value="form.financingRatio" :min="0" :max="100" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="预计退货率(%)">
|
||||
<a-input-number v-model:value="form.estimatedReturnRate" :min="0" :max="100" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="借款期限(天)" required>
|
||||
<a-input-number v-model:value="form.loanTerm" :min="1" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="订单融资期限(天)" required>
|
||||
<a-input-number v-model:value="form.orderFinancingTerm" :min="1" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="申请金额(元)" required>
|
||||
<a-input v-model:value="form.loanAmount" placeholder="请输入申请金额" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="申请利率(%)">
|
||||
<a-input v-model:value="form.applicationInterestRate" placeholder="请输入申请利率" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="贷款投向">
|
||||
<a-input v-model:value="form.loanPurpose" placeholder="请输入贷款投向" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="企业信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="注册资本">
|
||||
<a-input v-model:value="form.registeredCapital" placeholder="请输入注册资本" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="注册资本币种">
|
||||
<a-input v-model:value="form.registeredCapitalCurrency" placeholder="如:CNY" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="企业类型">
|
||||
<a-input v-model:value="form.enterpriseType" placeholder="请输入企业类型" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="营业执照登记日">
|
||||
<a-date-picker v-model:value="businessLicenseDateModel" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="营业执照到期日">
|
||||
<a-date-picker v-model:value="businessLicenseExpiryModel" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="企业经济类型">
|
||||
<a-input v-model:value="form.enterpriseOrgType" placeholder="请输入企业经济类型" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="国民经济部门">
|
||||
<a-input v-model:value="form.enterpriseEconomyType" placeholder="请输入国民经济部门" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="国标行业分类">
|
||||
<a-input v-model:value="form.industryCategory" placeholder="请输入国标行业分类" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="上年末从业人数">
|
||||
<a-input-number v-model:value="form.annualEmployeeCount" :min="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="上年营业收入(元)">
|
||||
<a-input v-model:value="form.annualRevenue" placeholder="请输入上年营业收入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="上年末资产总额(元)">
|
||||
<a-input v-model:value="form.annualTotalAssets" placeholder="请输入上年末资产总额" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item label="是否市场经营者">
|
||||
<a-radio-group v-model:value="form.isMarketOperator">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item label="是否上市公司">
|
||||
<a-radio-group v-model:value="form.isListedCompany">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item label="是否私人控股">
|
||||
<a-radio-group v-model:value="form.isPrivateControlled">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item label="是否政府融资平台">
|
||||
<a-radio-group v-model:value="form.isGovernmentPlatform">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="特殊融资用途标识" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="还贷资金来源">
|
||||
<a-input v-model:value="form.repayFundSource" placeholder="请输入还贷资金来源" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="支农用途">
|
||||
<a-input v-model:value="form.acceptAgricultureType" placeholder="请输入支农用途" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="产业结构调整类型">
|
||||
<a-input v-model:value="form.industryAdjustType" placeholder="请输入产业结构调整类型" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="工业转型升级标识">
|
||||
<a-switch v-model:checked="form.industryUpgradeFlag" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="战略新兴产业类型">
|
||||
<a-input v-model:value="form.newIndustryType" placeholder="请输入战略新兴产业类型" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="法定代表人信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人姓名" required>
|
||||
<a-input v-model:value="form.legalPersonName" placeholder="请输入法人姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人证件类型">
|
||||
<a-input v-model:value="form.legalPersonCertType" placeholder="如:身份证" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人证件号码" required>
|
||||
<a-input v-model:value="form.legalPersonCertId" placeholder="请输入法人证件号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人性别">
|
||||
<a-select v-model:value="form.legalPersonGender" placeholder="请选择性别">
|
||||
<a-select-option value="MALE">男</a-select-option>
|
||||
<a-select-option value="FEMALE">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人出生日期">
|
||||
<a-date-picker v-model:value="legalPersonBirthdayModel" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人证件到期日">
|
||||
<a-date-picker v-model:value="legalPersonCertExpiryModel" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人最高学历">
|
||||
<a-select v-model:value="form.legalPersonEducation" placeholder="请选择学历">
|
||||
<a-select-option v-for="opt in EDUCATION_LEVEL_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人婚姻状况">
|
||||
<a-select v-model:value="form.legalPersonMarital" placeholder="请选择婚姻状况">
|
||||
<a-select-option v-for="opt in MARITAL_STATUS_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人民族">
|
||||
<a-select v-model:value="form.legalPersonNationality" placeholder="请选择民族" show-search option-filter-prop="label">
|
||||
<a-select-option v-for="opt in ETHNICITY_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人居住地址">
|
||||
<a-input v-model:value="form.legalPersonResidence" placeholder="请输入法人居住地址" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人个人年收入(元)">
|
||||
<a-input v-model:value="form.legalPersonAnnualIncome" placeholder="请输入法人个人年收入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人手机号码">
|
||||
<a-input v-model:value="form.legalPersonMobile" placeholder="请输入法人手机号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="法人电子邮箱">
|
||||
<a-input v-model:value="form.legalPersonEmail" placeholder="请输入法人电子邮箱" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card v-if="form.legalPersonMarital === 'MARRIED'" title="法人配偶信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶姓名">
|
||||
<a-input v-model:value="form.spouseName" placeholder="请输入配偶姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶电话">
|
||||
<a-input v-model:value="form.spousePhone" placeholder="请输入配偶电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶性别">
|
||||
<a-select v-model:value="form.spouseGender" placeholder="请选择性别">
|
||||
<a-select-option value="MALE">男</a-select-option>
|
||||
<a-select-option value="FEMALE">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶证件类型">
|
||||
<a-input v-model:value="form.spouseCertType" placeholder="如:身份证" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶证件号码">
|
||||
<a-input v-model:value="form.spouseCertId" placeholder="请输入配偶证件号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="紧急联系人信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="姓名" required>
|
||||
<a-input v-model:value="form.emergencyContactName" placeholder="请输入紧急联系人姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="联系电话">
|
||||
<a-input v-model:value="form.emergencyContactPhone" placeholder="请输入紧急联系人电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="影像资料" style="margin-bottom: 16px">
|
||||
<a-space size="large">
|
||||
<div>
|
||||
<div class="upload-label">营业执照</div>
|
||||
<LicenseUpload :channel-no="form.channel" v-model:file-no="businessLicenseFileNo" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="upload-label">法人身份证人像面</div>
|
||||
<IdCardUpload side="front" :channel-no="form.channel" v-model:file-no="legalPersonIdFrontFileNo" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="upload-label">法人身份证国徽面</div>
|
||||
<IdCardUpload side="back" :channel-no="form.channel" v-model:file-no="legalPersonIdBackFileNo" />
|
||||
</div>
|
||||
</a-space>
|
||||
</a-card>
|
||||
</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>
|
||||
|
||||
<WalletCustomerPicker ref="customerPickerRef" mode="enterprise" @select="handleSelectCustomer" />
|
||||
</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 OrgTreeSelect from '@/components/OrgTreeSelect.vue'
|
||||
import IdCardUpload from '@/components/IdCardUpload.vue'
|
||||
import LicenseUpload from '@/components/LicenseUpload.vue'
|
||||
import WalletCustomerPicker from '@/components/WalletCustomerPicker.vue'
|
||||
import { fetchWalletAccountPageApi } from '@/api/wallet'
|
||||
import {
|
||||
createEnterpriseLoanApplicationApi,
|
||||
updateEnterpriseLoanApplicationApi,
|
||||
fetchLoanApplicationDetailApi
|
||||
} from '@/api/credit'
|
||||
import {
|
||||
GOODS_CATEGORY_OPTIONS,
|
||||
PAYMENT_CYCLE_OPTIONS,
|
||||
ETHNICITY_OPTIONS,
|
||||
EDUCATION_LEVEL_OPTIONS,
|
||||
MARITAL_STATUS_OPTIONS,
|
||||
LOAN_APPLICATION_STATUS_OPTIONS
|
||||
} from '@/constants/creditEnums'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const mode = computed(() => {
|
||||
if (route.path.endsWith('/edit')) return 'edit'
|
||||
if (route.path.endsWith('/detail')) return 'detail'
|
||||
return 'create'
|
||||
})
|
||||
const isCreate = computed(() => mode.value === 'create')
|
||||
const isDetail = computed(() => mode.value === 'detail')
|
||||
const pageTitle = computed(() => ({ create: '新增企业贷款申请', edit: '编辑企业贷款申请', detail: '查看企业贷款申请' }[mode.value]))
|
||||
|
||||
const submitting = ref(false)
|
||||
const loadingWallet = ref(false)
|
||||
const walletOptions = ref([])
|
||||
const businessLicenseFileNo = ref('')
|
||||
const legalPersonIdFrontFileNo = ref('')
|
||||
const legalPersonIdBackFileNo = ref('')
|
||||
const customerPickerRef = ref(null)
|
||||
|
||||
const detailInfo = reactive({ createdBy: '', applicationStatus: '' })
|
||||
const statusText = computed(
|
||||
() => LOAN_APPLICATION_STATUS_OPTIONS.find((o) => o.value === detailInfo.applicationStatus)?.label || detailInfo.applicationStatus
|
||||
)
|
||||
|
||||
const form = reactive({
|
||||
loanApplicationId: undefined,
|
||||
organizationId: undefined,
|
||||
channel: undefined,
|
||||
loanProductCode: '',
|
||||
customerType: 'ENTERPRISE',
|
||||
customerId: undefined,
|
||||
customerName: '',
|
||||
accountNo: undefined,
|
||||
goodsCategory: undefined,
|
||||
paymentCycle: undefined,
|
||||
singleOrderLimit: '',
|
||||
financingRatio: undefined,
|
||||
estimatedReturnRate: undefined,
|
||||
loanTerm: undefined,
|
||||
orderFinancingTerm: undefined,
|
||||
loanAmount: '',
|
||||
emergencyContactPhone: '',
|
||||
loanPurpose: '',
|
||||
enterpriseName: '',
|
||||
applicationInterestRate: '',
|
||||
repayFundSource: '',
|
||||
acceptAgricultureType: '',
|
||||
industryAdjustType: '',
|
||||
industryUpgradeFlag: false,
|
||||
newIndustryType: '',
|
||||
annualEmployeeCount: undefined,
|
||||
annualRevenue: '',
|
||||
annualTotalAssets: '',
|
||||
registeredCapital: '',
|
||||
registeredCapitalCurrency: '',
|
||||
businessLicenseDate: '',
|
||||
businessLicenseExpiry: '',
|
||||
enterpriseType: '',
|
||||
enterpriseOrgType: '',
|
||||
enterpriseEconomyType: '',
|
||||
industryCategory: '',
|
||||
isMarketOperator: 'N',
|
||||
isListedCompany: 'N',
|
||||
isPrivateControlled: 'N',
|
||||
isGovernmentPlatform: 'N',
|
||||
legalPersonCertType: '',
|
||||
legalPersonCertId: '',
|
||||
legalPersonName: '',
|
||||
legalPersonGender: undefined,
|
||||
legalPersonBirthday: '',
|
||||
legalPersonCertExpiry: '',
|
||||
legalPersonEducation: undefined,
|
||||
legalPersonMarital: undefined,
|
||||
legalPersonNationality: undefined,
|
||||
legalPersonResidence: '',
|
||||
legalPersonAnnualIncome: '',
|
||||
legalPersonMobile: '',
|
||||
legalPersonEmail: '',
|
||||
spouseName: '',
|
||||
spousePhone: '',
|
||||
spouseCertType: '',
|
||||
spouseGender: undefined,
|
||||
spouseCertId: '',
|
||||
emergencyContactName: '',
|
||||
businessScope: '',
|
||||
phoneNo: ''
|
||||
})
|
||||
|
||||
const businessLicenseDateModel = computed({
|
||||
get: () => (form.businessLicenseDate ? dayjs(form.businessLicenseDate) : undefined),
|
||||
set: (val) => (form.businessLicenseDate = val ? val.format('YYYY-MM-DD') : '')
|
||||
})
|
||||
const businessLicenseExpiryModel = computed({
|
||||
get: () => (form.businessLicenseExpiry ? dayjs(form.businessLicenseExpiry) : undefined),
|
||||
set: (val) => (form.businessLicenseExpiry = val ? val.format('YYYY-MM-DD') : '')
|
||||
})
|
||||
const legalPersonBirthdayModel = computed({
|
||||
get: () => (form.legalPersonBirthday ? dayjs(form.legalPersonBirthday) : undefined),
|
||||
set: (val) => (form.legalPersonBirthday = val ? val.format('YYYY-MM-DD') : '')
|
||||
})
|
||||
const legalPersonCertExpiryModel = computed({
|
||||
get: () => (form.legalPersonCertExpiry ? dayjs(form.legalPersonCertExpiry) : undefined),
|
||||
set: (val) => (form.legalPersonCertExpiry = val ? val.format('YYYY-MM-DD') : '')
|
||||
})
|
||||
|
||||
function openCustomerPicker() {
|
||||
customerPickerRef.value.show()
|
||||
}
|
||||
|
||||
async function loadWalletOptions(enterpriseName) {
|
||||
loadingWallet.value = true
|
||||
walletOptions.value = []
|
||||
try {
|
||||
const res = await fetchWalletAccountPageApi({ page: 1, pageSize: 50, accountName: enterpriseName })
|
||||
if (res.data.code === 200) {
|
||||
walletOptions.value = (res.data.data.records || []).map((item) => ({
|
||||
label: item.accountNo,
|
||||
value: item.accountNo
|
||||
}))
|
||||
}
|
||||
} finally {
|
||||
loadingWallet.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSelectCustomer(record) {
|
||||
form.customerId = record.id
|
||||
form.enterpriseName = record.enterpriseName
|
||||
await loadWalletOptions(record.enterpriseName)
|
||||
}
|
||||
|
||||
function toFullDate(dateStr) {
|
||||
return dateStr ? `${dateStr} 00:00:00` : undefined
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
const res = await fetchLoanApplicationDetailApi({ loanApplicationId: route.query.loanApplicationId })
|
||||
if (res.data.code !== 200) return
|
||||
const data = res.data.data
|
||||
Object.assign(form, data, {
|
||||
businessLicenseDate: data.businessLicenseDate ? data.businessLicenseDate.slice(0, 10) : '',
|
||||
businessLicenseExpiry: data.businessLicenseExpiry ? data.businessLicenseExpiry.slice(0, 10) : '',
|
||||
legalPersonBirthday: data.legalPersonBirthday ? data.legalPersonBirthday.slice(0, 10) : '',
|
||||
legalPersonCertExpiry: data.legalPersonCertExpiry ? data.legalPersonCertExpiry.slice(0, 10) : ''
|
||||
})
|
||||
detailInfo.createdBy = data.createdBy
|
||||
detailInfo.applicationStatus = data.applicationStatus
|
||||
if (data.enterpriseName) {
|
||||
await loadWalletOptions(data.enterpriseName)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!isCreate.value && route.query.loanApplicationId) {
|
||||
loadDetail()
|
||||
}
|
||||
})
|
||||
|
||||
function buildImageList() {
|
||||
const list = []
|
||||
if (businessLicenseFileNo.value) list.push({ fileType: '营业执照', fileNo: businessLicenseFileNo.value })
|
||||
if (legalPersonIdFrontFileNo.value) list.push({ fileType: '身份证正面', fileNo: legalPersonIdFrontFileNo.value })
|
||||
if (legalPersonIdBackFileNo.value) list.push({ fileType: '身份证反面', fileNo: legalPersonIdBackFileNo.value })
|
||||
return list
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.channel) {
|
||||
message.warning('请先选择所属渠道')
|
||||
return
|
||||
}
|
||||
if (!form.enterpriseName) {
|
||||
message.warning('请先选择企业客户')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload = {
|
||||
organizationId: form.organizationId,
|
||||
channel: form.channel,
|
||||
loanProductCode: form.loanProductCode,
|
||||
customerType: form.customerType,
|
||||
customerId: form.customerId,
|
||||
customerName: form.enterpriseName,
|
||||
accountNo: form.accountNo,
|
||||
goodsCategory: form.goodsCategory,
|
||||
paymentCycle: form.paymentCycle,
|
||||
singleOrderLimit: form.singleOrderLimit,
|
||||
financingRatio: form.financingRatio,
|
||||
estimatedReturnRate: form.estimatedReturnRate,
|
||||
loanTerm: form.loanTerm,
|
||||
orderFinancingTerm: form.orderFinancingTerm,
|
||||
loanAmount: form.loanAmount,
|
||||
emergencyContactPhone: form.emergencyContactPhone,
|
||||
loanPurpose: form.loanPurpose,
|
||||
enterpriseName: form.enterpriseName,
|
||||
applicationInterestRate: form.applicationInterestRate,
|
||||
repayFundSource: form.repayFundSource,
|
||||
acceptAgricultureType: form.acceptAgricultureType,
|
||||
industryAdjustType: form.industryAdjustType,
|
||||
industryUpgradeFlag: form.industryUpgradeFlag,
|
||||
newIndustryType: form.newIndustryType,
|
||||
annualEmployeeCount: form.annualEmployeeCount,
|
||||
annualRevenue: form.annualRevenue,
|
||||
annualTotalAssets: form.annualTotalAssets,
|
||||
registeredCapital: form.registeredCapital,
|
||||
registeredCapitalCurrency: form.registeredCapitalCurrency,
|
||||
businessLicenseDate: toFullDate(form.businessLicenseDate),
|
||||
businessLicenseExpiry: toFullDate(form.businessLicenseExpiry),
|
||||
enterpriseType: form.enterpriseType,
|
||||
enterpriseOrgType: form.enterpriseOrgType,
|
||||
enterpriseEconomyType: form.enterpriseEconomyType,
|
||||
industryCategory: form.industryCategory,
|
||||
isMarketOperator: form.isMarketOperator,
|
||||
isListedCompany: form.isListedCompany,
|
||||
isPrivateControlled: form.isPrivateControlled,
|
||||
isGovernmentPlatform: form.isGovernmentPlatform,
|
||||
legalPersonCertType: form.legalPersonCertType,
|
||||
legalPersonCertId: form.legalPersonCertId,
|
||||
legalPersonName: form.legalPersonName,
|
||||
legalPersonGender: form.legalPersonGender,
|
||||
legalPersonBirthday: toFullDate(form.legalPersonBirthday),
|
||||
legalPersonCertExpiry: toFullDate(form.legalPersonCertExpiry),
|
||||
legalPersonEducation: form.legalPersonEducation,
|
||||
legalPersonMarital: form.legalPersonMarital,
|
||||
legalPersonNationality: form.legalPersonNationality,
|
||||
legalPersonResidence: form.legalPersonResidence,
|
||||
legalPersonAnnualIncome: form.legalPersonAnnualIncome,
|
||||
legalPersonMobile: form.legalPersonMobile,
|
||||
legalPersonEmail: form.legalPersonEmail,
|
||||
spouseName: form.spouseName,
|
||||
spousePhone: form.spousePhone,
|
||||
spouseCertType: form.spouseCertType,
|
||||
spouseGender: form.spouseGender,
|
||||
spouseCertId: form.spouseCertId,
|
||||
emergencyContactName: form.emergencyContactName,
|
||||
businessScope: form.businessScope,
|
||||
phoneNo: form.phoneNo,
|
||||
loanApplicationImageBtoList: buildImageList()
|
||||
}
|
||||
let res
|
||||
if (isCreate.value) {
|
||||
res = await createEnterpriseLoanApplicationApi(payload)
|
||||
} else {
|
||||
res = await updateEnterpriseLoanApplicationApi({ loanApplicationId: form.loanApplicationId, ...payload })
|
||||
}
|
||||
if (res.data.code === 200) {
|
||||
message.success(isCreate.value ? '贷款申请草稿已提交' : '修改成功')
|
||||
goBack()
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push('/credit/loan-application')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-label {
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<!-- src/views/credit/loan-application/LoanApplicationList.vue -->
|
||||
<!-- 2.2.1 贷款申请:列表查询字段严格按 /api/credit-apply/loan-application/list 实际支持的
|
||||
入参实现(申请单号/申请类型/申请状态/客户名称/创建时间范围)。原型图要求的"机构/渠道/
|
||||
钱包编号"三项查询条件后端接口不支持,已在《缺失的后端接口.md》登记为缺口,未在此实现。 -->
|
||||
<template>
|
||||
<div class="loan-application-list-page">
|
||||
<ProTable
|
||||
:columns="columns"
|
||||
:fetch-data="loadList"
|
||||
:row-selection="false"
|
||||
:initial-search="{ loanApplicationId: '', customerName: '', applicationType: undefined, applicationStatus: undefined, startDate: undefined, endDate: undefined }"
|
||||
>
|
||||
<template #search="{ form }">
|
||||
<a-form-item label="申请单号">
|
||||
<a-input v-model:value="form.loanApplicationId" placeholder="请输入贷款申请单号" allow-clear style="width: 180px" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户名称">
|
||||
<a-input v-model:value="form.customerName" placeholder="请输入客户名称" allow-clear style="width: 160px" />
|
||||
</a-form-item>
|
||||
<a-form-item label="申请类型">
|
||||
<a-select v-model:value="form.applicationType" placeholder="请选择申请类型" allow-clear style="width: 140px">
|
||||
<a-select-option value="PERSONAL">个人贷款</a-select-option>
|
||||
<a-select-option value="BUSINESS">企业贷款</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="申请状态">
|
||||
<a-select v-model:value="form.applicationStatus" placeholder="请选择申请状态" allow-clear style="width: 140px">
|
||||
<a-select-option v-for="opt in statusOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="创建时间">
|
||||
<a-range-picker
|
||||
:value="form.startDate && form.endDate ? [form.startDate, form.endDate] : []"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="(vals) => { form.startDate = vals?.[0]; form.endDate = vals?.[1] }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<a-dropdown>
|
||||
<a-button type="primary">
|
||||
贷款申请 <DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleCreateMenuClick">
|
||||
<a-menu-item key="personal">个人贷款申请</a-menu-item>
|
||||
<a-menu-item key="enterprise">企业贷款申请</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.dataIndex === 'applicationType'">
|
||||
{{ record.applicationType === 'PERSONAL' ? '个人贷款' : '企业贷款' }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'applicationStatus'">
|
||||
<StatusTag :value="record.applicationStatus" :map="statusMap" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a @click="goDetail(record)">查看</a>
|
||||
<a v-if="record.applicationStatus === 'DRAFT'" @click="goEdit(record)">编辑</a>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ProTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DownOutlined } from '@ant-design/icons-vue'
|
||||
import ProTable from '@/components/ProTable.vue'
|
||||
import StatusTag from '@/components/StatusTag.vue'
|
||||
import { fetchLoanApplicationListApi } from '@/api/credit'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 'DRAFT', label: '待确认' },
|
||||
{ value: 'CONFIRMED', label: '已确认' },
|
||||
{ value: 'SUBMITTED', label: '已申请' },
|
||||
{ value: 'APPLY_FAILED', label: '申请失败' }
|
||||
]
|
||||
const statusMap = {
|
||||
DRAFT: { text: '待确认', color: 'default' },
|
||||
CONFIRMED: { text: '已确认', color: 'blue' },
|
||||
SUBMITTED: { text: '已申请', color: 'green' },
|
||||
APPLY_FAILED: { text: '申请失败', color: 'red' }
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'index', width: 60 },
|
||||
{ title: '申请单号', dataIndex: 'loanApplicationId' },
|
||||
{ title: '客户名称', dataIndex: 'customerName' },
|
||||
{ title: '申请类型', dataIndex: 'applicationType' },
|
||||
{ title: '贷款产品编号', dataIndex: 'loanProductCode' },
|
||||
{ title: '申请金额', dataIndex: 'loanAmount' },
|
||||
{ title: '申请利率(%)', dataIndex: 'applicationInterestRate' },
|
||||
{ title: '渠道', dataIndex: 'channel' },
|
||||
{ title: '状态', dataIndex: 'applicationStatus' },
|
||||
{ title: '创建人', dataIndex: 'createdBy' },
|
||||
{ title: '操作', dataIndex: 'action', width: 120 }
|
||||
]
|
||||
|
||||
async function loadList(params) {
|
||||
const payload = { ...params }
|
||||
if (payload.startDate) payload.createDateStart = `${payload.startDate} 00:00:00`
|
||||
if (payload.endDate) payload.createDateEnd = `${payload.endDate} 23:59:59`
|
||||
delete payload.startDate
|
||||
delete payload.endDate
|
||||
const res = await fetchLoanApplicationListApi(payload)
|
||||
if (res.data.code === 200) {
|
||||
return { list: res.data.data.records || [], total: res.data.data.total || 0 }
|
||||
}
|
||||
return { list: [], total: 0 }
|
||||
}
|
||||
|
||||
function handleCreateMenuClick({ key }) {
|
||||
router.push(`/credit/loan-application/${key}/create`)
|
||||
}
|
||||
|
||||
function goDetail(record) {
|
||||
const type = record.applicationType === 'PERSONAL' ? 'personal' : 'enterprise'
|
||||
router.push({ path: `/credit/loan-application/${type}/detail`, query: { loanApplicationId: record.loanApplicationId } })
|
||||
}
|
||||
|
||||
function goEdit(record) {
|
||||
const type = record.applicationType === 'PERSONAL' ? 'personal' : 'enterprise'
|
||||
router.push({ path: `/credit/loan-application/${type}/edit`, query: { loanApplicationId: record.loanApplicationId } })
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
<!-- src/views/credit/loan-application/PersonalLoanForm.vue -->
|
||||
<!-- 2.2.1 贷款申请 —— 个人贷款申请表单。mode 从 route.path 后缀推导(create/edit/detail)。
|
||||
字段严格对齐 /api/credit-apply/personal-loan-application/create、update 的
|
||||
CreatePersonalLoanApplicationDraftBto/UpdatePersonalLoanApplicationBto。
|
||||
已知缺口(登记于《缺失的后端接口.md》):该 Bto 不含 customerId/accountNo(钱包账号)字段,
|
||||
无法像企业贷款那样将申请与具体客户ID/钱包账号强关联,选择客户仅用于回填 customerName/
|
||||
phoneNo/permanentAddress 三个文本字段,不产生实际的外键关联;短信验证码校验与人脸识别
|
||||
属客户微信扫码后的移动端流程(/api/credit-apply/loan-application/confirm),不在本页面实现。 -->
|
||||
<template>
|
||||
<div class="personal-loan-form-page">
|
||||
<a-page-header :title="pageTitle" @back="goBack" />
|
||||
|
||||
<a-card v-if="!isCreate" style="margin-bottom: 16px">
|
||||
<a-descriptions :column="3">
|
||||
<a-descriptions-item label="申请单号">{{ form.loanApplicationId || '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="申请状态">{{ statusText }}</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人">{{ detailInfo.createdBy || '-' }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
|
||||
<a-form layout="vertical" :model="form" :disabled="isDetail">
|
||||
<a-card title="基础信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="所属渠道" required>
|
||||
<ChannelSelect v-model:model-value="form.channel" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="所属机构">
|
||||
<OrgTreeSelect v-model="form.organizationId" placeholder="请选择所属机构" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="贷款产品编号" required>
|
||||
<a-input v-model:value="form.loanProductCode" placeholder="请输入贷款产品编号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="客户">
|
||||
<a-space>
|
||||
<span v-if="form.customerName">{{ form.customerName }}</span>
|
||||
<a-button v-if="!isDetail" @click="openCustomerPicker">选择客户</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="手机号码">
|
||||
<a-input v-model:value="form.phoneNo" placeholder="请输入11位手机号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="户籍/常住地市">
|
||||
<a-input v-model:value="form.registeredCity" placeholder="请输入户籍/常住地市" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="16">
|
||||
<a-form-item label="户籍常驻地址">
|
||||
<a-input v-model:value="form.permanentAddress" placeholder="请输入户籍常驻地址" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="融资条件" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="商品类目" required>
|
||||
<a-select v-model:value="form.goodsCategory" placeholder="请选择商品类目">
|
||||
<a-select-option v-for="opt in GOODS_CATEGORY_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="回款周期" required>
|
||||
<a-select v-model:value="form.paymentCycle" placeholder="请选择回款周期">
|
||||
<a-select-option v-for="opt in PAYMENT_CYCLE_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="单笔汇总订单限额(元)" required>
|
||||
<a-input v-model:value="form.singleOrderLimit" placeholder="请输入单笔汇总订单限额" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="融资比例(%)" required>
|
||||
<a-input-number v-model:value="form.financingRatio" :min="0" :max="100" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="预计退货率(%)">
|
||||
<a-input-number v-model:value="form.estimatedReturnRate" :min="0" :max="100" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="借款期限(天)" required>
|
||||
<a-input-number v-model:value="form.loanTerm" :min="1" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="订单融资期限(天)" required>
|
||||
<a-input-number v-model:value="form.orderFinancingTerm" :min="1" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="申请金额(元)" required>
|
||||
<a-input v-model:value="form.loanAmount" placeholder="请输入申请金额" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="申请利率(%)">
|
||||
<a-input v-model:value="form.applicationInterestRate" placeholder="请输入申请利率" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="贷款投向">
|
||||
<a-input v-model:value="form.loanPurpose" placeholder="请输入贷款投向" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="经营实体信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="经营实体名称">
|
||||
<a-input v-model:value="form.enterpriseName" placeholder="请输入经营实体名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="经营起始日">
|
||||
<a-date-picker v-model:value="businessStartDateModel" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="经营范围" required>
|
||||
<a-input v-model:value="form.businessScope" placeholder="请输入经营范围/经营活动描述" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="个人基础信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="民族">
|
||||
<a-select v-model:value="form.ethnicity" placeholder="请选择民族" show-search option-filter-prop="label">
|
||||
<a-select-option v-for="opt in ETHNICITY_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="学历">
|
||||
<a-select v-model:value="form.educationLevel" placeholder="请选择学历">
|
||||
<a-select-option v-for="opt in EDUCATION_LEVEL_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="婚姻状况">
|
||||
<a-select v-model:value="form.maritalStatus" placeholder="请选择婚姻状况">
|
||||
<a-select-option v-for="opt in MARITAL_STATUS_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="个人年收入(元)">
|
||||
<a-input v-model:value="form.annualPersonalIncome" placeholder="请输入个人年收入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="家庭年收入(元)">
|
||||
<a-input v-model:value="form.annualFamilyIncome" placeholder="请输入家庭年收入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="紧急联系人信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="姓名" required>
|
||||
<a-input v-model:value="form.emergencyContactName" placeholder="请输入紧急联系人姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="联系电话">
|
||||
<a-input v-model:value="form.emergencyContactPhone" placeholder="请输入紧急联系人电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="与本人关系">
|
||||
<a-select v-model:value="form.emergencyContactRelation" placeholder="请选择关系">
|
||||
<a-select-option v-for="opt in EMERGENCY_CONTACT_RELATION_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card v-if="form.maritalStatus === 'MARRIED'" title="配偶信息" style="margin-bottom: 16px">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶姓名">
|
||||
<a-input v-model:value="form.spouseName" placeholder="请输入配偶姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶电话">
|
||||
<a-input v-model:value="form.spousePhone" placeholder="请输入配偶电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶性别">
|
||||
<a-select v-model:value="form.spouseGender" placeholder="请选择性别">
|
||||
<a-select-option value="MALE">男</a-select-option>
|
||||
<a-select-option value="FEMALE">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶证件类型">
|
||||
<a-input v-model:value="form.spouseCertType" placeholder="如:身份证" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="配偶证件号码">
|
||||
<a-input v-model:value="form.spouseCertId" placeholder="请输入配偶证件号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="影像资料" style="margin-bottom: 16px">
|
||||
<a-space size="large">
|
||||
<div>
|
||||
<div class="upload-label">身份证人像面</div>
|
||||
<IdCardUpload side="front" :channel-no="form.channel" v-model:file-no="idCardFrontFileNo" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="upload-label">身份证国徽面</div>
|
||||
<IdCardUpload side="back" :channel-no="form.channel" v-model:file-no="idCardBackFileNo" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="upload-label">营业执照(选填)</div>
|
||||
<LicenseUpload :channel-no="form.channel" v-model:file-no="businessLicenseFileNo" />
|
||||
</div>
|
||||
</a-space>
|
||||
</a-card>
|
||||
</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>
|
||||
|
||||
<WalletCustomerPicker ref="customerPickerRef" mode="personal" @select="handleSelectCustomer" />
|
||||
</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 OrgTreeSelect from '@/components/OrgTreeSelect.vue'
|
||||
import IdCardUpload from '@/components/IdCardUpload.vue'
|
||||
import LicenseUpload from '@/components/LicenseUpload.vue'
|
||||
import WalletCustomerPicker from '@/components/WalletCustomerPicker.vue'
|
||||
import {
|
||||
createPersonalLoanApplicationApi,
|
||||
updatePersonalLoanApplicationApi,
|
||||
fetchLoanApplicationDetailApi
|
||||
} from '@/api/credit'
|
||||
import {
|
||||
GOODS_CATEGORY_OPTIONS,
|
||||
PAYMENT_CYCLE_OPTIONS,
|
||||
ETHNICITY_OPTIONS,
|
||||
EDUCATION_LEVEL_OPTIONS,
|
||||
MARITAL_STATUS_OPTIONS,
|
||||
EMERGENCY_CONTACT_RELATION_OPTIONS,
|
||||
LOAN_APPLICATION_STATUS_OPTIONS
|
||||
} from '@/constants/creditEnums'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const mode = computed(() => {
|
||||
if (route.path.endsWith('/edit')) return 'edit'
|
||||
if (route.path.endsWith('/detail')) return 'detail'
|
||||
return 'create'
|
||||
})
|
||||
const isCreate = computed(() => mode.value === 'create')
|
||||
const isDetail = computed(() => mode.value === 'detail')
|
||||
const pageTitle = computed(() => ({ create: '新增个人贷款申请', edit: '编辑个人贷款申请', detail: '查看个人贷款申请' }[mode.value]))
|
||||
|
||||
const submitting = ref(false)
|
||||
const idCardFrontFileNo = ref('')
|
||||
const idCardBackFileNo = ref('')
|
||||
const businessLicenseFileNo = ref('')
|
||||
const customerPickerRef = ref(null)
|
||||
|
||||
const detailInfo = reactive({ createdBy: '', applicationStatus: '' })
|
||||
const statusText = computed(
|
||||
() => LOAN_APPLICATION_STATUS_OPTIONS.find((o) => o.value === detailInfo.applicationStatus)?.label || detailInfo.applicationStatus
|
||||
)
|
||||
|
||||
const form = reactive({
|
||||
loanApplicationId: undefined,
|
||||
organizationId: undefined,
|
||||
channel: undefined,
|
||||
loanProductCode: '',
|
||||
customerName: '',
|
||||
goodsCategory: undefined,
|
||||
paymentCycle: undefined,
|
||||
singleOrderLimit: '',
|
||||
financingRatio: undefined,
|
||||
estimatedReturnRate: undefined,
|
||||
loanTerm: undefined,
|
||||
orderFinancingTerm: undefined,
|
||||
loanAmount: '',
|
||||
emergencyContactPhone: '',
|
||||
loanPurpose: '',
|
||||
permanentAddress: '',
|
||||
enterpriseName: '',
|
||||
applicationInterestRate: '',
|
||||
ethnicity: undefined,
|
||||
educationLevel: undefined,
|
||||
maritalStatus: undefined,
|
||||
registeredCity: '',
|
||||
annualPersonalIncome: '',
|
||||
annualFamilyIncome: '',
|
||||
businessStartDate: '',
|
||||
emergencyContactRelation: undefined,
|
||||
spouseName: '',
|
||||
spousePhone: '',
|
||||
spouseCertType: '',
|
||||
spouseGender: undefined,
|
||||
spouseCertId: '',
|
||||
emergencyContactName: '',
|
||||
businessScope: '',
|
||||
phoneNo: ''
|
||||
})
|
||||
|
||||
const businessStartDateModel = computed({
|
||||
get: () => (form.businessStartDate ? dayjs(form.businessStartDate) : undefined),
|
||||
set: (val) => (form.businessStartDate = val ? val.format('YYYY-MM-DD') : '')
|
||||
})
|
||||
|
||||
function openCustomerPicker() {
|
||||
customerPickerRef.value.show()
|
||||
}
|
||||
|
||||
function handleSelectCustomer(record) {
|
||||
form.customerName = record.customerName
|
||||
form.phoneNo = record.mobilePhone || form.phoneNo
|
||||
form.permanentAddress = record.certificateAddress || form.permanentAddress
|
||||
}
|
||||
|
||||
function toFullDate(dateStr) {
|
||||
return dateStr ? `${dateStr} 00:00:00` : undefined
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
const res = await fetchLoanApplicationDetailApi({ loanApplicationId: route.query.loanApplicationId })
|
||||
if (res.data.code !== 200) return
|
||||
const data = res.data.data
|
||||
Object.assign(form, {
|
||||
loanApplicationId: data.loanApplicationId,
|
||||
organizationId: data.organizationId,
|
||||
channel: data.channel,
|
||||
loanProductCode: data.loanProductCode,
|
||||
customerName: data.customerName,
|
||||
goodsCategory: data.goodsCategory,
|
||||
paymentCycle: data.paymentCycle,
|
||||
singleOrderLimit: data.singleOrderLimit,
|
||||
financingRatio: data.financingRatio,
|
||||
estimatedReturnRate: data.estimatedReturnRate,
|
||||
loanTerm: data.loanTerm,
|
||||
orderFinancingTerm: data.orderFinancingTerm,
|
||||
loanAmount: data.loanAmount,
|
||||
emergencyContactPhone: data.emergencyContactPhone,
|
||||
loanPurpose: data.loanPurpose,
|
||||
permanentAddress: data.permanentAddress,
|
||||
enterpriseName: data.enterpriseName,
|
||||
applicationInterestRate: data.applicationInterestRate,
|
||||
ethnicity: data.ethnicity,
|
||||
educationLevel: data.educationLevel,
|
||||
maritalStatus: data.maritalStatus,
|
||||
registeredCity: data.registeredCity,
|
||||
annualPersonalIncome: data.annualPersonalIncome,
|
||||
annualFamilyIncome: data.annualFamilyIncome,
|
||||
businessStartDate: data.businessStartDate ? data.businessStartDate.slice(0, 10) : '',
|
||||
emergencyContactRelation: data.emergencyContactRelation,
|
||||
spouseName: data.spouseName,
|
||||
spousePhone: data.spousePhone,
|
||||
spouseCertType: data.spouseCertType,
|
||||
spouseGender: data.spouseGender,
|
||||
spouseCertId: data.spouseCertId,
|
||||
emergencyContactName: data.emergencyContactName,
|
||||
businessScope: data.businessScope,
|
||||
phoneNo: data.phoneNo
|
||||
})
|
||||
detailInfo.createdBy = data.createdBy
|
||||
detailInfo.applicationStatus = data.applicationStatus
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!isCreate.value && route.query.loanApplicationId) {
|
||||
loadDetail()
|
||||
}
|
||||
})
|
||||
|
||||
function buildImageList() {
|
||||
const list = []
|
||||
if (idCardFrontFileNo.value) list.push({ fileType: '身份证正面', fileNo: idCardFrontFileNo.value })
|
||||
if (idCardBackFileNo.value) list.push({ fileType: '身份证反面', fileNo: idCardBackFileNo.value })
|
||||
if (businessLicenseFileNo.value) list.push({ fileType: '营业执照', fileNo: businessLicenseFileNo.value })
|
||||
return list
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.channel) {
|
||||
message.warning('请先选择所属渠道')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload = {
|
||||
organizationId: form.organizationId,
|
||||
channel: form.channel,
|
||||
loanProductCode: form.loanProductCode,
|
||||
customerName: form.customerName,
|
||||
goodsCategory: form.goodsCategory,
|
||||
paymentCycle: form.paymentCycle,
|
||||
singleOrderLimit: form.singleOrderLimit,
|
||||
financingRatio: form.financingRatio,
|
||||
estimatedReturnRate: form.estimatedReturnRate,
|
||||
loanTerm: form.loanTerm,
|
||||
orderFinancingTerm: form.orderFinancingTerm,
|
||||
loanAmount: form.loanAmount,
|
||||
emergencyContactPhone: form.emergencyContactPhone,
|
||||
loanPurpose: form.loanPurpose,
|
||||
permanentAddress: form.permanentAddress,
|
||||
enterpriseName: form.enterpriseName,
|
||||
applicationInterestRate: form.applicationInterestRate,
|
||||
ethnicity: form.ethnicity,
|
||||
educationLevel: form.educationLevel,
|
||||
maritalStatus: form.maritalStatus,
|
||||
registeredCity: form.registeredCity,
|
||||
annualPersonalIncome: form.annualPersonalIncome,
|
||||
annualFamilyIncome: form.annualFamilyIncome,
|
||||
businessStartDate: toFullDate(form.businessStartDate),
|
||||
emergencyContactRelation: form.emergencyContactRelation,
|
||||
spouseName: form.spouseName,
|
||||
spousePhone: form.spousePhone,
|
||||
spouseCertType: form.spouseCertType,
|
||||
spouseGender: form.spouseGender,
|
||||
spouseCertId: form.spouseCertId,
|
||||
emergencyContactName: form.emergencyContactName,
|
||||
businessScope: form.businessScope,
|
||||
phoneNo: form.phoneNo,
|
||||
loanApplicationImageBtoList: buildImageList()
|
||||
}
|
||||
let res
|
||||
if (isCreate.value) {
|
||||
res = await createPersonalLoanApplicationApi(payload)
|
||||
} else {
|
||||
res = await updatePersonalLoanApplicationApi({ loanApplicationId: form.loanApplicationId, ...payload })
|
||||
}
|
||||
if (res.data.code === 200) {
|
||||
message.success(isCreate.value ? '贷款申请草稿已提交' : '修改成功')
|
||||
goBack()
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push('/credit/loan-application')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-label {
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
31
缺失的后端接口.md
31
缺失的后端接口.md
|
|
@ -151,6 +151,16 @@
|
|||
| `POST enterprise-open-application/update` | `UpdateEnterpriseAccountOpenApplicationBto`,**要求 `id`(int64)** |
|
||||
| `POST enterprise-bank-card/list` / `bind` / `unbind` | 企业绑卡管理三件套,字段结构同个人绑卡但用 `primaryAccount`/`accountProperty` |
|
||||
|
||||
### 1.12 授信管理 —— 贷款申请(`/api/credit-apply/*`)
|
||||
|
||||
| 接口 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST credit-apply/loan-application/list` | 柜员分页查询,入参仅支持 `loanApplicationId/applicationStatus/applicationType/customerName/createDateStart/createDateEnd`,响应 `records: LoanApplicationVo[]` |
|
||||
| `POST credit-apply/loan-application/detail` | `{loanApplicationId}`,响应 `LoanApplicationDetailVo`(个人/企业字段混合于同一模型,约60余字段) |
|
||||
| `POST credit-apply/personal-loan-application/create` / `update` | 个人贷款申请草稿创建/修改(仅 DRAFT 可改),响应 `data` 为 `loanApplicationId` 字符串 |
|
||||
| `POST credit-apply/enterprise-loan-application/create` / `update` | 企业贷款申请草稿创建/修改,响应 `CreateEnterpriseLoanDraftResultVo` |
|
||||
| `POST credit-apply/loan-application/confirm` | 申请人微信扫码确认(短验+人脸识别),移动端流程,不在本项目前端范围 |
|
||||
|
||||
---
|
||||
|
||||
## Part 2:待后端确认 / 建议补充事项
|
||||
|
|
@ -213,3 +223,24 @@
|
|||
25. **企业开户申请受益人信息 `enterpriseAccountOpenApplicationBeneficiaryBtoList` 是否支持多条受益人
|
||||
在 `update` 时的增删语义(全量替换 vs 增量)未在 swagger 说明**:前端当前只支持维护单条受益人信息,
|
||||
提交时整体覆盖该数组,若产品侧需要多受益人管理,需后端明确该数组的更新语义。
|
||||
26. **贷款申请存在两组平行接口 `/api/loan-application/*` 与 `/api/credit-apply/*`**:前者(`create-personal`
|
||||
`create-enterprise`/`detail`/`page`)与后者(`personal-loan-application/create`等)字段结构不同、
|
||||
用途描述均不完整,前端已选用后者(`credit-apply` 组,描述明确标注"银行柜员"场景且具备
|
||||
DRAFT→CONFIRMED→SUBMITTED 状态机,与阶段4风格一致),前者暂未使用,建议后端确认是否为废弃接口或
|
||||
说明二者分工,避免维护两套语义重叠的接口。
|
||||
27. **个人贷款申请草稿 `CreatePersonalLoanApplicationDraftBto` 不含 `customerId`/账户号(钱包账号)字段**:
|
||||
与企业贷款草稿 `CreateEnterpriseLoanDraftBto`(含 `customerId`+`accountNo`)不对称,个人贷款申请无法
|
||||
与具体客户ID、具体钱包账号建立强关联,选择客户仅能回填 `customerName`/`phoneNo`/`permanentAddress`
|
||||
三个文本字段,不产生外键关系,与需求文档"贷款钱包仅展示申请人已开立的钱包"的描述存在能力缺口,
|
||||
建议后端为个人贷款草稿补充 `customerId`/`accountNo` 字段。
|
||||
28. **贷款产品(`loanProductCode`)无独立的列表/字典查询接口**:前端改为柜员手动输入产品编号文本框,
|
||||
无法做下拉选择与合法性校验,建议后端补充贷款产品字典查询接口。
|
||||
29. **企业贷款草稿字段 `customerType` 无 enum 约束说明**:前端按项目既有命名习惯猜测传值 `'ENTERPRISE'`,
|
||||
需后端确认合法取值(可能是 `'ENTERPRISE'`/`'BUSINESS'`/`'企业'` 等其他形式)。
|
||||
30. **贷款申请没有"按企业客户查询其已开立钱包账号"的专用接口**:前端改为选定企业客户后,调用通用的
|
||||
`wallet-account/page` 按 `accountName`(企业名称文本)模糊匹配钱包账户列表供人工选择,若同名不同企业
|
||||
或企业名称与钱包账户名称不完全一致,可能匹配不到或匹配错误,建议后端补充按 `customerId` 精确查询
|
||||
钱包账号的专用接口。
|
||||
31. **法定代表人/配偶相关字段(`legalPersonEducation`/`legalPersonMarital`/`legalPersonNationality`/
|
||||
`legalPersonGender`/`spouseGender` 等)均为无枚举约束的纯字符串**:前端为提升可用性复用个人贷款
|
||||
对应枚举的取值域渲染下拉框,但后端未做强校验,实际入库是否要求严格匹配以实测为准。
|
||||
|
|
|
|||
Loading…
Reference in New Issue