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 @@
-
+
请先选择所属渠道
@@ -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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ index + 1 }}
+
+ 选择
+
+
+
+
+ 手动输入产品编号
+
+
+ 确定
+
+
+
+
+
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` 查询。 -->
-
+
@@ -18,7 +27,7 @@
-
+
{{ opt.label }}
@@ -43,6 +52,12 @@ import ProTable from '@/components/ProTable.vue'
import StatusTag from '@/components/StatusTag.vue'
import { fetchWalletAccountPageApi } from '@/api/wallet'
+const props = defineProps({
+ title: { type: String, default: '选择钱包账户' },
+ // 'PERSONAL'(个人钱包)/'ENTERPRISE'(企业钱包),见组件顶部注释;不传则保留原有的
+ // "账户类型(A1~A7)"筛选行为,不做客户类型区分
+ customerType: { type: String, default: undefined }
+})
const emit = defineEmits(['select'])
const open = ref(false)
@@ -73,7 +88,9 @@ const columns = [
]
async function loadList(params) {
- const res = await fetchWalletAccountPageApi(params)
+ // customerType 固定时始终以它为准,忽略(已隐藏的)搜索表单里的 accountType 值
+ const query = props.customerType ? { ...params, accountType: props.customerType } : params
+ const res = await fetchWalletAccountPageApi(query)
if (res.data.code === 200) {
return { list: res.data.data.records || [], total: res.data.data.total || 0 }
}
@@ -91,3 +108,4 @@ function show() {
defineExpose({ show })
+
diff --git a/src/constants/creditEnums.js b/src/constants/creditEnums.js
index 7e813bd..2eba857 100644
--- a/src/constants/creditEnums.js
+++ b/src/constants/creditEnums.js
@@ -123,3 +123,34 @@ export const LOAN_APPLICATION_STATUS_OPTIONS = [
{ value: "APPLY_FAILED", label: "申请失败" }
]
+// 个人贷款申请"本人已阅读并同意"协议链接对应的银行协议模板 fileType(BankAgreementFileTypeEnum,
+// 取自 swagger,与 fryl_h5/src/constants/enums.js 的 BANK_AGREEMENT_FILE_TYPE_LABELS 同源但
+// 该文件只列了 PAY_SERVICE_AGREEMENT/PAY_SERVICE_PRIVACY 两项,这里补充贷款场景实际用到的
+// CREDIT_REPORT_AUTH/THIRD_PARTY_DATA_AUTH 两项标签,详见《缺失的后端接口.md》第112条)。
+export const BANK_AGREEMENT_FILE_TYPE_LABELS = {
+ CREDIT_REPORT_AUTH: "个人信用信息报送查询使用授权书",
+ THIRD_PARTY_DATA_AUTH: "第三方数据信息查询和使用授权书"
+}
+
+// 影像资料上传时提交给 LoanApplicationImageBto.fileType 的取值:身份证正面/身份证反面沿用
+// swagger description 里给出的中文自由文本("身份证正面"/"身份证反面"),经营证明按业务方
+// 给定的编码约定传字符串 "17"(非 swagger 已声明枚举,系人工指定的既有编码,详见
+// 《缺失的后端接口.md》相关登记条目)。
+export const LOAN_APPLICATION_IMAGE_FILE_TYPE = {
+ ID_CARD_FRONT: "身份证正面",
+ ID_CARD_BACK: "身份证反面",
+ BUSINESS_PROOF: "17",
+ FACE_PHOTO: "04"
+}
+
+// 个人贷款申请"经营实体(企业)"区块"与经营企业关系"下拉选项:业务方直接指定的5个固定选项,
+// 未在 swagger 中定义对应枚举,CreatePersonalLoanApplicationDraftBto 也没有承接该字段的
+// 位置(已知缺口,登记于《缺失的后端接口.md》),仅作页面展示,不随表单提交。
+export const ENTERPRISE_RELATION_OPTIONS = [
+ { value: "LEGAL_REPRESENTATIVE", label: "法定代表人" },
+ { value: "SHAREHOLDER", label: "股东" },
+ { value: "PARTNER", label: "合伙人" },
+ { value: "CONTRACTOR", label: "承包人" },
+ { value: "ACTUAL_CONTROLLER", label: "实控人" }
+]
+
diff --git a/src/utils/qrCode.js b/src/utils/qrCode.js
index 7e17484..f462407 100644
--- a/src/utils/qrCode.js
+++ b/src/utils/qrCode.js
@@ -7,3 +7,15 @@ export function buildPersonalOpenQrCodeUrl(applicationNo) {
const path = import.meta.env.VITE_H5_QRCODE_PATH || '/mobile/personal-open'
return `${base}${path}?applicationNo=${applicationNo}`
}
+
+// 个人贷款申请"扫码确认"(PersonalLoanForm.vue)专用的 H5 确认页二维码地址拼接,与上面个人开户
+// 同一模式:域名复用同一个 VITE_H5_QRCODE_BASE_URL,路径走独立的 VITE_H5_QRCODE_PATH_LOAN
+// (与企业开户 EnterpriseOpenForm.vue 内联的 VITE_H5_QRCODE_PATH_ENTERPRISE 是同类做法,只是放在
+// 本文件统一维护)。查询参数用 loanApplicationId(贷款申请单号),与后端字段命名保持一致。
+// 扫码后的 H5 确认页面本身不在本项目前端实现范围(见《缺失的后端接口.md》第19条),这里只负责
+// 生成二维码内容,不阻断柜员操作。
+export function buildLoanApplicationQrCodeUrl(loanApplicationId) {
+ const base = import.meta.env.VITE_H5_QRCODE_BASE_URL || 'https://example.com'
+ const path = import.meta.env.VITE_H5_QRCODE_PATH_LOAN || '/mobile/loan-application'
+ return `${base}${path}?loanApplicationId=${loanApplicationId}`
+}
diff --git a/src/views/credit/loan-application/PersonalLoanForm.vue b/src/views/credit/loan-application/PersonalLoanForm.vue
index dd2b285..e1de7b8 100644
--- a/src/views/credit/loan-application/PersonalLoanForm.vue
+++ b/src/views/credit/loan-application/PersonalLoanForm.vue
@@ -1,14 +1,33 @@
+ CreatePersonalLoanApplicationDraftBto/UpdatePersonalLoanApplicationBto,融资条件/渠道机构/
+ 配偶信息区块维持原有设计,2026-08-10 按最新原型截图补充调整了"贷款申请人钱包/贷款产品"
+ 选择弹窗、五级行政区划级联、证件号码展示、协议勾选与预览、经营证明影像项等交互,详见
+ docs/superpowers/specs 目录同日期设计讨论与《缺失的后端接口.md》相关登记条目。
+ "确认"按钮点击后不直接提交,而是弹出二次确认弹窗(保存/取消/扫码确认),与
+ PersonalOpenForm.vue/EnterpriseOpenForm.vue 同款模式:"保存"与"扫码确认"共用同一次真实
+ create/update 提交,区别仅在成功后是否切到二维码态;二维码内容是前端拼接的一个 H5 URL
+ (`buildLoanApplicationQrCodeUrl`,域名/路径来自 VITE_H5_QRCODE_BASE_URL/
+ VITE_H5_QRCODE_PATH_LOAN),扫码后走向的移动端 H5 确认页本身不在本项目前端实现范围。
+ 已知缺口(登记于《缺失的后端接口.md》):该 Bto 不含 customerId/accountNo(钱包账号)/
+ certificateNumber(证件号码)字段,选择钱包/产品仅用于回填 customerName/phoneNo/
+ permanentAddress/loanProductCode 等文本字段,不产生实际的外键关联,钱包账号/证件号码
+ 只在页面展示,不随表单提交;短信验证码校验与人脸识别属客户微信扫码后的移动端流程
+ (/api/credit-apply/loan-application/confirm),不在本页面实现。 -->
-
+
+
+
+ 本人已阅读并同意
+ 【模板】个人征信授权书
+ 【模板】个人信息查询使用授权书
+ 确认
+ 取消
+
+
+
@@ -18,6 +37,7 @@
+
@@ -33,74 +53,18 @@
-
-
-
-
-
-
- {{ form.customerName }}
- 选择客户
+
+
+ 选择
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ opt.label }}
-
-
-
-
-
-
- {{ opt.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {{ walletAccountNo }} (余额:{{ walletMainBalance }})
+ {{ walletAccountNo ? '重新选择' : '选择' }}
+
@@ -109,89 +73,132 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
{{ opt.label }}
-
+
{{ opt.label }}
-
+
{{ opt.label }}
-
+
-
+
-
-
+
+
+
+ 经营实体(企业)
+
+
+
+
-
-
+
+
-
+
+
+ {{ form.enterpriseName }}
+ {{ form.enterpriseName ? '重新选择' : '选择' }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 请输入经营范围,至少10个汉字
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
{{ opt.label }}
@@ -236,54 +243,135 @@
-
营业执照(选填)
-
+
代付情况说明
+
+
+ 下载模板
+
+
+
+
-
- 提交
- 取消
-
+
+
+
-
+
+
+
+ 是否确认{{ isCreate ? '提交' : '修改' }}贷款申请?
+
+
+
+
+
+
申请单号:{{ qrApplicationNo }}
+
关闭
+
+
+
+
+
+
+
+
+
+