From 03fc69cd2b2151bc6bcad0fb451d50fbf2ff5891 Mon Sep 17 00:00:00 2001 From: halo Date: Mon, 10 Aug 2026 17:28:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=AA=E4=BA=BA=E8=B4=B7?= =?UTF-8?q?=E6=AC=BE=E7=94=B3=E8=AF=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 1 + .env.production | 1 + src/api/baseConfig.js | 8 + src/api/credit.js | 7 + src/api/customer.js | 9 + .../AdministrativeDivisionCascader.vue | 131 ++++ src/components/LicenseUpload.vue | 16 +- src/components/LoanProductPicker.vue | 98 +++ src/components/WalletAccountPicker.vue | 30 +- src/constants/creditEnums.js | 31 + src/utils/qrCode.js | 12 + .../loan-application/PersonalLoanForm.vue | 609 ++++++++++++++---- 12 files changed, 805 insertions(+), 148 deletions(-) create mode 100644 src/components/AdministrativeDivisionCascader.vue create mode 100644 src/components/LoanProductPicker.vue diff --git a/.env.development b/.env.development index c63e8a0..dbc4a6b 100644 --- a/.env.development +++ b/.env.development @@ -2,3 +2,4 @@ VITE_API_BASE_URL=http://localhost:8888 VITE_H5_QRCODE_BASE_URL=http://www.baidu.com VITE_H5_QRCODE_PATH=/mobile/personal-open VITE_H5_QRCODE_PATH_ENTERPRISE=/mobile/enterprise-open +VITE_H5_QRCODE_PATH_LOAN=/mobile/loan-application diff --git a/.env.production b/.env.production index 0839c93..334bd15 100644 --- a/.env.production +++ b/.env.production @@ -2,3 +2,4 @@ VITE_API_BASE_URL= VITE_H5_QRCODE_BASE_URL= VITE_H5_QRCODE_PATH=/mobile/personal-open VITE_H5_QRCODE_PATH_ENTERPRISE=/mobile/enterprise-open +VITE_H5_QRCODE_PATH_LOAN=/mobile/loan-application diff --git a/src/api/baseConfig.js b/src/api/baseConfig.js index 6b29af4..2feaefd 100644 --- a/src/api/baseConfig.js +++ b/src/api/baseConfig.js @@ -44,3 +44,11 @@ export const fetchDictItemTreeApi = (dictTypeCode) => export const createDictItemApi = (data) => request.post('/api/dict-management/dict-item/create', data) export const updateDictItemApi = (data) => request.post('/api/dict-management/dict-item/update', data) export const deleteDictItemApi = (data) => request.post('/api/dict-management/dict-item/delete', data) + +// ---------------- 行政区划(省/市/区县/乡镇/村,懒加载逐级查询) ---------------- +// GET /api/base-config/administrative-division/children?parentCode=&level= +// level:1=省 2=地市 3=区县 4=乡镇/街道 5=村/居委会(查第一级即省级时 parentCode 传空字符串)。 +// 用于个人贷款申请"户籍/常住地市"五级级联选择(AdministrativeDivisionCascader.vue), +// 提交给后端的是选中的最深一级叶子节点的 zoningCode(统计用区划代码字符串)。 +export const fetchAdministrativeDivisionChildrenApi = (parentCode, level) => + request.get('/api/base-config/administrative-division/children', { params: { parentCode: parentCode || '', level } }) diff --git a/src/api/credit.js b/src/api/credit.js index 719f29a..8eef80a 100644 --- a/src/api/credit.js +++ b/src/api/credit.js @@ -19,3 +19,10 @@ export const updateEnterpriseLoanApplicationApi = (data) => // 注:confirm 接口(/api/credit-apply/loan-application/confirm)语义为"申请人微信扫码确认", // 需短信验证码+人脸识别,属移动端客户操作流程,不在本管理后台前端实现范围,不在此导出。 + +// 贷款产品查询(用于"贷款产品"选择弹窗 LoanProductPicker.vue):后端目前没有任何贷款产品 +// 列表/字典查询接口(已在《缺失的后端接口.md》第28条登记),这里按与本文件其它 credit-apply +// 接口一致的命名风格假定一个契约,方便后端后续对齐实现;若接口暂未提供,调用会失败,弹窗内已 +// 同时提供"手动输入产品编号"兜底入口,不阻塞柜员录入。入参/出参字段为前端假定,以实际后端 +// 实现为准,登记于《缺失的后端接口.md》第135条。 +export const fetchLoanProductListApi = (data) => request.post('/api/credit-apply/loan-product/list', data) diff --git a/src/api/customer.js b/src/api/customer.js index 850348e..05ef4bb 100644 --- a/src/api/customer.js +++ b/src/api/customer.js @@ -21,6 +21,15 @@ export const uploadCustomerFileApi = (data, channelNo) => export const faceRecognizeApi = (data, channelNo) => request.post('/api/customer-info/personal/face-recognize', data, { channelNo }) +// 银行协议模板(base64 PDF):按 fileType 获取浙江稠州商业银行业务协议/授权书模板文件, +// 转 base64 返回给前端。此前仅 fryl_h5(移动端)有此封装(src/api/personalOpen.js), +// 个人贷款申请页面(PersonalLoanForm.vue)"本人已阅读并同意"协议预览需要复用同一接口, +// 故在主项目补充这份封装。fileType 取值见 BankAgreementFileTypeEnum: +// PAY_SERVICE_AGREEMENT/PAY_SERVICE_PRIVACY/CREDIT_REPORT_AUTH/THIRD_PARTY_DATA_AUTH。 +// 响应 data:{ fileType, fileName, fileContent(base64) }。 +export const fetchBankAgreementApi = (fileType) => + request.get('/api/customer-info/bank-agreement/base64', { params: { fileType } }) + // ---------------- 企业客户 ---------------- export const fetchEnterpriseCustomerPageApi = (data) => request.post('/api/enterprise-customer/page', data) export const fetchEnterpriseCustomerDetailApi = (data) => request.post('/api/enterprise-customer/detail', data) diff --git a/src/components/AdministrativeDivisionCascader.vue b/src/components/AdministrativeDivisionCascader.vue new file mode 100644 index 0000000..6d7dd66 --- /dev/null +++ b/src/components/AdministrativeDivisionCascader.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/components/LicenseUpload.vue b/src/components/LicenseUpload.vue index ef33c09..0e6c156 100644 --- a/src/components/LicenseUpload.vue +++ b/src/components/LicenseUpload.vue @@ -19,11 +19,11 @@ -
- - -
上传营业执照
-
+
+ + +
{{ uploadText }}
+
请先选择所属渠道
@@ -43,7 +43,11 @@ import { uploadCustomerFileApi } from '@/api/customer' const props = defineProps({ channelNo: { type: String, default: undefined }, // 只读态(查看详情):图片区域只保留预览,隐藏重新上传/删除操作 - readonly: { type: Boolean, default: false } + readonly: { type: Boolean, default: false }, + // 空态占位文案:默认"上传营业执照"(历史默认用途),部分调用场景(如个人贷款申请的 + // "代付情况说明"影像项)复用本组件承载不同性质的文件,需按实际展示内容自定义该文案, + // 避免出现"上传营业执照"这类与实际用途不符的误导性提示。 + uploadText: { type: String, default: '上传营业执照' } }) // update:uploading 用于告知父组件当前上传是否仍在进行,父组件应据此阻止在上传完成前提交表单, // 否则会出现 fileNo 尚未写回 form 就已提交、导致 businessLicenseFileNo 传空的竞态问题 diff --git a/src/components/LoanProductPicker.vue b/src/components/LoanProductPicker.vue new file mode 100644 index 0000000..b146689 --- /dev/null +++ b/src/components/LoanProductPicker.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/src/components/WalletAccountPicker.vue b/src/components/WalletAccountPicker.vue index 3fc4c36..fba0f14 100644 --- a/src/components/WalletAccountPicker.vue +++ b/src/components/WalletAccountPicker.vue @@ -1,15 +1,24 @@ + 用于商户管理(选择主体钱包/担保人账户)、发票管理(选择收款/归集账户)、个人贷款申请 + (选择贷款申请人钱包/经营企业钱包)等需要引用"已开立钱包账户"的场景,避免各处重复实现 + 同一套弹窗逻辑。 + + customerType(可选,'PERSONAL'/'ENTERPRISE'):按客户类型固定过滤。业务确认 + `/api/wallet-account/page` 请求体的 `accountType` 字段,除了后端响应 `WalletAccountVo` + 里代表"账户子类型"(A1主账户/A2/A3/A6保证金/A7分户,swagger 已声明为 AccountTypeEnum)外, + 在**查询入参**场景还复用同一字段名传 `PERSONAL`/`ENTERPRISE` 做客户类型过滤——这是业务口头 + 确认的取值,swagger 对请求体 `accountType` 未声明枚举(纯 `string`),不是文档化契约。同一个 + 请求只有一个 `accountType` 字段,客户类型过滤和账户子类型(A1~A7)筛选不能同时生效,故设置 + `customerType` 后会隐藏原有的"账户类型(A1~A7)"筛选框,固定按 `customerType` 查询。 -->