客户类型字段更新

This commit is contained in:
wkc
2026-02-02 15:25:38 +08:00
parent 6483a651c4
commit bc7011313f
39 changed files with 5253 additions and 719 deletions

View File

@@ -14,56 +14,43 @@
## 接口列表
### 1. 发起利率定价流程
### 1. 发起个人客户利率定价流程
创建的利率定价申请。
创建个人客户的利率定价申请。
**接口地址:** `POST /loanPricing/workflow/create`
**接口地址:** `POST /loanPricing/workflow/create/personal`
**权限要求:** `loanPricing:workflow:create`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| orgCode | String | 是 | 机构编码,固定值: 931000 |
| runType | String | | 运行模式,固定值: 1(同步) |
| custIsn | String | | 客户内码 |
| custType | String | | 客户类型,可选值: 个人/企业 |
| guarType | String | 是 | 担保方式,可选值: 信用/保证/抵押/质押 |
| midPerQuickPay | String | | 中间业务_个人_快捷支付,值: true/false |
| midPerEleDdc | String | 否 | 中间业务_个人_电费代扣,值: true/false |
| midEntEleDdc | String | 否 | 中间业务_企业_电费代扣,值: true/false |
| midEntWaterDdc | String | 否 | 中间业务_企业_水费代扣,值: true/false |
| applyAmt | String | | 申请金额,单位: 元 |
| isCleanEnt | String | 否 | 净身企业,值: true/false |
| hasSettleAcct | String | 否 | 开立基本结算账户,值: true/false |
| isManufacturing | String | 否 | 制造业企业,值: true/false |
| isAgriGuar | String | 否 | 省农担担保贷款,值: true/false |
| isTaxA | String | 否 | 是否纳税信用等级A级,值: true/false |
| isAgriLeading | String | 否 | 是否县级及以上农业龙头企业,值: true/false |
| loanPurpose | String | 否 | 贷款用途,可选值: consumer/business |
| bizProof | String | 否 | 是否有经营佐证,值: true/false |
| collType | String | 否 | 抵质押类型,可选值: 一线/一类/二类 |
| collThirdParty | String | 否 | 抵质押物是否三方所有,值: true/false |
| loanRate | String | 是 | 贷款利率 |
| custName | String | 否 | 客户名称 |
| idType | String | 否 | 证件类型 |
| isInclusiveFinance | String | 否 | 是否普惠小微借款人,值: true/false |
| 参数名 | 类型 | 必填 | 说明 |
|----------------|--------|----|--------------------------|
| custIsn | String | 是 | 客户内码 |
| custName | String | | 客户名称 |
| idType | String | | 证件类型 |
| idNum | String | | 证件号码 |
| guarType | String | 是 | 担保方式,可选值: 信用/保证/抵押/质押 |
| applyAmt | String | | 申请金额,单位: 元 |
| bizProof | String | 否 | 是否有经营佐证,值: true/false |
| loanLoop | String | 否 | 循环功能,值: true/false |
| collType | String | 否 | 抵质押类型,可选值: 一线/一类/二类 |
| collThirdParty | String | | 抵质押物是否三方所有,值: true/false |
**请求示例:**
```json
{
"orgCode": "931000",
"runType": "1",
"custIsn": "CUST001",
"custType": "企业",
"custName": "张三",
"idType": "身份证",
"idNum": "110101199001011234",
"guarType": "抵押",
"applyAmt": "1000000",
"loanRate": "4.35",
"custName": "某某科技有限公司",
"loanPurpose": "business"
"applyAmt": "500000",
"bizProof": "true",
"loanLoop": "false",
"collType": "一类",
"collThirdParty": "false"
}
```
@@ -80,11 +67,11 @@
"orgCode": "931000",
"runType": "1",
"custIsn": "CUST001",
"custType": "企业",
"custType": "个人",
"guarType": "抵押",
"applyAmt": "1000000",
"loanRate": "4.35",
"custName": "某某科技有限公司",
"applyAmt": "500000",
"custName": "张三",
"idType": "身份证",
"createTime": "2025-01-19 14:30:25",
"createBy": "admin"
}
@@ -93,7 +80,79 @@
---
### 2. 查询利率定价流程列表
### 2. 发起企业客户利率定价流程
创建企业客户的利率定价申请。
**接口地址:** `POST /loanPricing/workflow/create/corporate`
**权限要求:** `loanPricing:workflow:create`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|---------------------|--------|----|--------------------------|
| custIsn | String | 是 | 客户内码 |
| custName | String | 否 | 客户名称 |
| idType | String | 否 | 证件类型 |
| idNum | String | 否 | 证件号码 |
| guarType | String | 是 | 担保方式,可选值: 信用/保证/抵押/质押 |
| applyAmt | String | 是 | 申请金额,单位: 元 |
| loanTerm | String | 否 | 贷款期限(月) |
| isAgriGuar | String | 否 | 省农担担保贷款,值: true/false |
| isGreenLoan | String | 否 | 绿色贷款,值: true/false |
| isTechEnt | String | 否 | 科技型企业,值: true/false |
| isTradeConstruction | String | 否 | 贸易和建筑业企业标识,值: true/false |
| collType | String | 否 | 抵质押类型,可选值: 一线/一类/二类 |
| collThirdParty | String | 否 | 抵质押物是否三方所有,值: true/false |
**请求示例:**
```json
{
"custIsn": "CORP001",
"custName": "某某科技有限公司",
"idType": "统一社会信用代码",
"idNum": "91110000100000000X",
"guarType": "抵押",
"applyAmt": "1000000",
"loanTerm": "36",
"isAgriGuar": "false",
"isGreenLoan": "true",
"isTechEnt": "true",
"isTradeConstruction": "false",
"collType": "一类",
"collThirdParty": "false"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 2,
"modelOutputId": 101,
"serialNum": "20250119143125456",
"orgCode": "931000",
"runType": "1",
"custIsn": "CORP001",
"custType": "企业",
"guarType": "抵押",
"applyAmt": "1000000",
"custName": "某某科技有限公司",
"idType": "统一社会信用代码",
"createTime": "2025-01-19 14:31:25",
"createBy": "admin"
}
}
```
---
### 3. 查询利率定价流程列表
分页查询利率定价流程记录,支持多条件筛选。
@@ -150,7 +209,7 @@ GET /loanPricing/workflow/list?pageNum=1&pageSize=10&custName=科技
---
### 3. 查看利率定价流程详情
### 4. 查看利率定价流程详情
根据业务方流水号查询流程的完整信息,包括模型输出字段。
@@ -326,7 +385,7 @@ GET /loanPricing/workflow/20250119143025123
---
### 4. 设定执行利率
### 5. 设定执行利率
为利率定价流程设定或更新最终执行利率。
@@ -440,12 +499,6 @@ PUT /loanPricing/workflow/20250119143025123/executeRate
| 一类 | 一类抵押 |
| 二类 | 二类抵押 |
### 运行模式 (runType)
| 值 | 说明 |
|----|------|
| 1 | 同步运行 |
---
## 在线文档

15
doc/corp.csv Normal file
View File

@@ -0,0 +1,15 @@
中文名,字段名,备注
客户内码,custIsn,
客户类型,custType,企业
担保方式,guarType,"可选值:信用,保证,抵押,质押"
客户名称,custName,
证件类型,idType,
证件号码,idNum,
贸易和建筑业企业,,押类贸易和建筑业企业上调20BP
省农担担保贷款,isAgriGuar,省农担担保贷款下调40个BP
绿色贷款,isGreenLoan,绿色贷款或科技型企业最多下降5BP
科技型企业,isTechEnt,绿色贷款或科技型企业最多下降5BP
贷款期限,loanTerm,
申请金额,applyAmt,单位:元
抵质押类型,collType,
抵质押物是否三方所有,collThirdParty,
1 中文名 字段名 备注
2 客户内码 custIsn
3 客户类型 custType 企业
4 担保方式 guarType 可选值:信用,保证,抵押,质押
5 客户名称 custName
6 证件类型 idType
7 证件号码 idNum
8 贸易和建筑业企业 抵(质)押类:贸易和建筑业企业上调20BP
9 省农担担保贷款 isAgriGuar 省农担担保贷款下调40个BP
10 绿色贷款 isGreenLoan 绿色贷款或科技型企业最多下降5BP
11 科技型企业 isTechEnt 绿色贷款或科技型企业最多下降5BP
12 贷款期限 loanTerm
13 申请金额 applyAmt 单位:元
14 抵质押类型 collType
15 抵质押物是否三方所有 collThirdParty

View File

@@ -0,0 +1,127 @@
# 字段匹配分析报告
## 概述
本报告对比了 `person.csv``corp.csv` 中定义的字段与数据库表 `loan_pricing_workflow` 的实际字段,识别出需要添加的字段。
---
## 个人客户字段对比
| CSV字段名 | 中文名 | CSV要求 | 数据库字段 | 匹配状态 |
|----------------|------------|--------------------|------------------|---------|
| custIsn | 客户内码 | 必填 | cust_isn | ✅ 匹配 |
| custType | 客户类型 | 固定值"个人" | cust_type | ✅ 匹配 |
| guarType | 担保方式 | 必填,可选值:信用/保证/抵押/质押 | guar_type | ✅ 匹配 |
| custName | 客户名称 | 可选 | cust_name | ✅ 匹配 |
| idType | 证件类型 | 可选 | id_type | ✅ 匹配 |
| idNum | 证件号码 | 可选 | **❌ 缺失** | ⚠️ 需要添加 |
| applyAmt | 申请金额 | 必填,单位:元 | apply_amt | ✅ 匹配 |
| bizProof | 是否有经营佐证 | 可选 | biz_proof | ✅ 匹配 |
| loanLoop | 循环功能 | 可选 | **❌ 缺失** | ⚠️ 需要添加 |
| collType | 抵质押类型 | 可选 | coll_type | ✅ 匹配 |
| collThirdParty | 抵质押物是否三方所有 | 可选 | coll_third_party | ✅ 匹配 |
### 个人客户缺失字段
| 字段名 | 中文名 | 类型 | 说明 |
|-----------|------|--------------|-------------------------|
| id_num | 证件号码 | varchar(100) | 存储个人身份证号或其他证件号码 |
| loan_loop | 循环功能 | varchar(10) | 贷款合同是否开通循环功能true/false |
---
## 企业客户字段对比
| CSV字段名 | 中文名 | CSV要求 | 数据库字段 | 匹配状态 |
|----------------|------------|-----------------------|------------------|---------|
| custIsn | 客户内码 | 必填 | cust_isn | ✅ 匹配 |
| custType | 客户类型 | 固定值"企业" | cust_type | ✅ 匹配 |
| guarType | 担保方式 | 必填,可选值:信用/保证/抵押/质押 | guar_type | ✅ 匹配 |
| custName | 客户名称 | 可选 | cust_name | ✅ 匹配 |
| idType | 证件类型 | 可选 | id_type | ✅ 匹配 |
| idNum | 证件号码 | 可选 | **❌ 缺失** | ⚠️ 需要添加 |
| (无字段名) | 贸易和建筑业企业 | 抵押类贸易和建筑业企业上调20BP | **❌ 缺失** | ⚠️ 需要添加 |
| isAgriGuar | 省农担担保贷款 | 省农担担保贷款下调40个BP | is_agri_guar | ✅ 匹配 |
| isGreenLoan | 绿色贷款 | 绿色贷款或科技型企业最多下降5BP | **❌ 缺失** | ⚠️ 需要添加 |
| isTechEnt | 科技型企业 | 绿色贷款或科技型企业最多下降5BP | **❌ 缺失** | ⚠️ 需要添加 |
| loanTerm | 贷款期限 | 可选 | **❌ 缺失** | ⚠️ 需要添加 |
| applyAmt | 申请金额 | 必填,单位:元 | apply_amt | ✅ 匹配 |
| collType | 抵质押类型 | 可选 | coll_type | ✅ 匹配 |
| collThirdParty | 抵质押物是否三方所有 | 可选 | coll_third_party | ✅ 匹配 |
### 企业客户缺失字段
| 字段名 | 中文名 | 类型 | 说明 |
|-----------------------|------------|--------------|----------------------------------|
| id_num | 证件号码 | varchar(100) | 存储企业统一社会信用代码或其他证件号码 |
| is_trade_construction | 贸易和建筑业企业标识 | varchar(10) | 抵押类贸易和建筑业企业上调20BPtrue/false |
| is_green_loan | 绿色贷款 | varchar(10) | 绿色贷款标识true/false |
| is_tech_ent | 科技型企业 | varchar(10) | 科技型企业标识true/false |
| loan_term | 贷款期限 | varchar(50) | 贷款期限,单位:月/年 |
---
## 数据库变更 SQL
```sql
-- 添加缺失的字段到 loan_pricing_workflow 表
-- 个人和企业共同需要的字段
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `id_num` varchar(100) DEFAULT NULL COMMENT '证件号码' AFTER `id_type`;
-- 个人客户专用字段
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `loan_loop` varchar(10) DEFAULT NULL COMMENT '循环功能: true/false' AFTER `biz_proof`;
-- 企业客户专用字段
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_trade_construction` varchar(10) DEFAULT NULL COMMENT '贸易和建筑业企业标识: true/false抵质押类上调20BP' AFTER `is_agri_guar`;
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_green_loan` varchar(10) DEFAULT NULL COMMENT '绿色贷款: true/false' AFTER `is_agri_guar`;
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_tech_ent` varchar(10) DEFAULT NULL COMMENT '科技型企业: true/false' AFTER `is_agri_guar`;
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `loan_term` varchar(50) DEFAULT NULL COMMENT '贷款期限' AFTER `apply_amt`;
```
---
## Entity 类更新
`LoanPricingWorkflow.java` 需要添加以下属性:
```java
/** 证件号码 */
private String idNum;
/** 循环功能: true/false */
private String loanLoop;
/** 贸易和建筑业企业标识: true/false */
private String isTradeConstruction;
/** 绿色贷款: true/false */
private String isGreenLoan;
/** 科技型企业: true/false */
private String isTechEnt;
/** 贷款期限 */
private String loanTerm;
```
---
## 建议行动
1. **立即执行**:添加缺失的数据库字段
2. **更新 Entity**:在 `LoanPricingWorkflow` 实体类中添加对应的属性
3. **更新 Proposal**:在 `split-pricing-creation-interface` 的 tasks.md 中添加数据库变更任务
4. **测试验证**:确保新字段可以正确存储和检索数据
---
## 总结
- **个人客户缺失字段**2 个id_num, loan_loop
- **企业客户缺失字段**5 个id_num, is_trade_construction, is_green_loan, is_tech_ent, loan_term
- **共同缺失字段**1 个id_num
- **总计需要添加**6 个新字段
数据库和实体类需要同步更新,才能支持新的个人和企业发起接口。

View File

@@ -0,0 +1,161 @@
# 利率定价接口拆分实施报告
## 实施摘要
本次实施成功将原有的统一利率定价发起接口拆分为两个独立的接口:
- **个人客户发起接口**: `POST /loanPricing/workflow/create/personal`
- **企业客户发起接口**: `POST /loanPricing/workflow/create/corporate`
## 已完成的任务
### ✅ 数据库变更
- **文件**: `sql/add_missing_fields.sql`
- **新增字段**:
- `id_num` (varchar(100)) - 证件号码
- `loan_loop` (varchar(10)) - 循环功能(个人专用)
- `is_trade_construction` (varchar(10)) - 贸易和建筑业企业标识(企业专用)
- `is_green_loan` (varchar(10)) - 绿色贷款(企业专用)
- `is_tech_ent` (varchar(10)) - 科技型企业(企业专用)
- `loan_term` (varchar(50)) - 贷款期限(企业专用)
> **注意**: SQL 脚本已创建,需手动执行到数据库
### ✅ Entity 类更新
- **文件**: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/LoanPricingWorkflow.java`
- 添加了 6 个新属性
### ✅ DTO 类创建
1. **PersonalLoanPricingCreateDTO.java** - 个人客户发起 DTO
- 包含个人特有字段bizProof、loanLoop
- 字段验证custIsn、guarType、applyAmt 必填
- 担保方式枚举验证
2. **CorporateLoanPricingCreateDTO.java** - 企业客户发起 DTO
- 包含企业特有字段isAgriGuar、isGreenLoan、isTechEnt、isTradeConstruction、loanTerm
- 字段验证custIsn、guarType、applyAmt 必填
- 担保方式枚举验证
### ✅ 转换器工具类
- **文件**: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java`
- 实现 DTO 到 Entity 的转换
- 自动设置 custType 为"个人"或"企业"
### ✅ Service 层更新
- **接口**: `ILoanPricingWorkflowService.java`
- 新增: `createPersonalLoanPricing(PersonalLoanPricingCreateDTO dto)`
- 新增: `createCorporateLoanPricing(CorporateLoanPricingCreateDTO dto)`
- **实现**: `LoanPricingWorkflowServiceImpl.java`
- 实现了两个新方法,复用现有的 createLoanPricing 逻辑
### ✅ Controller 层更新
- **文件**: `LoanPricingWorkflowController.java`
- 新增接口:
- `POST /loanPricing/workflow/create/personal`
- `POST /loanPricing/workflow/create/corporate`
- 保留了原有接口,保持向后兼容
### ✅ HTTP 测试脚本
1. **test_personal_create.http** - 个人客户接口测试
- 成功场景测试
- 3 个缺少必填字段的失败场景
- 担保方式枚举验证测试
- 抵质押信息测试
- 最小必填字段测试
2. **test_corporate_create.http** - 企业客户接口测试
- 成功场景测试
- 3 个缺少必填字段的失败场景
- 担保方式枚举验证测试
- 企业特有字段测试(省农担、绿色贷款、科技企业、贸易建筑)
- 最小必填字段测试
3. **test_backward_compatibility.http** - 向后兼容性测试
- 原有接口个人客户测试
- 原有接口企业客户测试
## 待完成任务
### ⏳ 需要手动执行的步骤
1. **执行数据库迁移脚本**
```bash
# 在 MySQL 中执行
source sql/add_missing_fields.sql
```
2. **执行 HTTP 测试脚本**
- 使用 IntelliJ IDEA 打开 `test_api/` 目录下的 .http 文件
- 逐一执行测试用例
- 验证返回结果
3. **启动应用并验证**
```bash
# 启动后端应用
mvn spring-boot:run
```
- 访问 Swagger UI: `http://localhost:8080/swagger-ui.html`
- 验证新接口文档显示正确
## 接口使用说明
### 个人客户发起接口
**端点**: `POST /loanPricing/workflow/create/personal`
**请求示例**:
```json
{
"custIsn": "TEST001",
"custName": "张三",
"idType": "身份证",
"idNum": "110101199001011234",
"guarType": "信用",
"applyAmt": "500000",
"bizProof": "true",
"loanLoop": "false"
}
```
### 企业客户发起接口
**端点**: `POST /loanPricing/workflow/create/corporate`
**请求示例**:
```json
{
"custIsn": "CORP001",
"custName": "测试科技有限公司",
"idType": "统一社会信用代码",
"idNum": "91110000100000000X",
"guarType": "抵押",
"applyAmt": "1000000",
"loanTerm": "36",
"isAgriGuar": "false",
"isGreenLoan": "true",
"isTechEnt": "true"
}
```
## 验证清单
- [x] 数据库迁移脚本已创建
- [x] Entity 类已更新
- [x] DTO 类已创建
- [x] 转换器已创建
- [x] Service 接口和实现已更新
- [x] Controller 接口已添加
- [x] HTTP 测试脚本已生成
- [ ] 数据库迁移脚本已执行
- [ ] 测试脚本已执行并通过
- [ ] Swagger 文档已验证

12
doc/person.csv Normal file
View File

@@ -0,0 +1,12 @@
中文名,字段名,备注
客户内码,custIsn,
客户类型,custType,个人
担保方式,guarType,"可选值:信用,保证,抵押,质押"
客户名称,custName,
证件类型,idType,
证件号码,idNum,
申请金额,applyAmt,单位:元
是否有经营佐证,bizProof,个人经营性贷款提供的经营佐证包括:借款人或其配偶为法定代表人、实际经营者、股东的企业(个体工商户)营业执照或企查查、企信宝查档资料。
循环功能,loanLoop,贷款合同是否开通循环功能
抵质押类型,collType,
抵质押物是否三方所有,collThirdParty,
1 中文名 字段名 备注
2 客户内码 custIsn
3 客户类型 custType 个人
4 担保方式 guarType 可选值:信用,保证,抵押,质押
5 客户名称 custName
6 证件类型 idType
7 证件号码 idNum
8 申请金额 applyAmt 单位:元
9 是否有经营佐证 bizProof 个人经营性贷款提供的经营佐证包括:借款人或其配偶为法定代表人、实际经营者、股东的企业(个体工商户)营业执照或企查查、企信宝查档资料。
10 循环功能 loanLoop 贷款合同是否开通循环功能
11 抵质押类型 collType
12 抵质押物是否三方所有 collThirdParty

View File

@@ -0,0 +1,854 @@
# 根据客户类型动态展示流程详情实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**目标:** 创建两个独立的详情组件(个人客户和企业客户),根据客户类型动态渲染不同的字段展示。
**架构:** 将现有的单一详情页面拆分为两个独立组件PersonalWorkflowDetail.vue 和 CorporateWorkflowDetail.vue父组件
detail.vue 作为容器负责数据获取和根据 custType 动态分发渲染。
**技术栈:** Vue 2.6.12, Element UI 2.15.14, Vue Router 3.4.9
---
## Task 1: 创建个人客户详情组件 PersonalWorkflowDetail.vue
**文件:**
- 创建: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
**Step 1: 创建组件基础结构**
```vue
<template>
<div class="personal-workflow-detail" v-loading="loading">
<!-- 两栏布局左侧关键信息 + 右侧流程详情+模型输出 -->
<div v-if="!loading && detailData" class="detail-layout">
<!-- 左侧关键信息卡片 -->
<div class="left-panel">
<el-card class="summary-card">
<div slot="header" class="card-header">
<span class="card-title">关键信息</span>
</div>
<el-descriptions :column="1" direction="vertical" border>
<el-descriptions-item label="业务方流水号">{{ detailData.serialNum }}</el-descriptions-item>
<el-descriptions-item label="客户名称">{{ detailData.custName }}</el-descriptions-item>
<el-descriptions-item label="客户类型">{{ detailData.custType }}</el-descriptions-item>
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} </el-descriptions-item>
<el-descriptions-item label="基准利率">
<span class="rate-value">{{ getBaseLoanRate() }}</span> %
</el-descriptions-item>
<el-descriptions-item label="浮动BP">
<span class="total-bp-value">{{ getTotalBp() }}</span>
</el-descriptions-item>
<el-descriptions-item label="测算利率">
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
</el-descriptions-item>
<el-descriptions-item label="执行利率">
<div class="execute-rate-input-wrapper">
<el-input
v-model="executeRateInput"
class="execute-rate-input"
placeholder="请输入执行利率"
>
<template slot="append">%</template>
</el-input>
<el-button
type="primary"
size="small"
@click="handleSetExecuteRate"
>
确定
</el-button>
</div>
</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 流程详情卡片 -->
<el-card class="detail-card">
<div slot="header" class="card-header">
<span class="card-title">流程详情</span>
</div>
<!-- 基本信息组 -->
<div class="info-section">
<h4 class="section-title">基本信息</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="机构编码">{{ detailData.orgCode }}</el-descriptions-item>
<el-descriptions-item label="运行模式">{{ detailData.runType }}</el-descriptions-item>
<el-descriptions-item label="客户内码">{{ detailData.custIsn }}</el-descriptions-item>
<el-descriptions-item label="证件类型">{{ detailData.idType }}</el-descriptions-item>
<el-descriptions-item label="证件号码">{{ detailData.idNum }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
<el-descriptions-item label="创建者">{{ detailData.createBy }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 业务信息组 -->
<div class="info-section">
<h4 class="section-title">业务信息</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} </el-descriptions-item>
<el-descriptions-item label="是否有经营佐证">{{ formatBoolean(detailData.bizProof) }}</el-descriptions-item>
<el-descriptions-item label="循环功能">{{ formatBoolean(detailData.loanLoop) }}</el-descriptions-item>
<el-descriptions-item label="抵质押类型">{{ detailData.collType || '-' }}</el-descriptions-item>
<el-descriptions-item label="抵质押物是否三方所有">{{ formatBoolean(detailData.collThirdParty) }}</el-descriptions-item>
</el-descriptions>
</div>
</el-card>
<!-- 模型输出卡片 -->
<ModelOutputDisplay
:cust-type="detailData.custType"
:retail-output="retailOutput"
:corp-output="null"
/>
<!-- 议价池卡片 -->
<BargainingPoolDisplay
v-if="bargainingPool"
:branch-pool="bargainingPool.branchPool"
:sub-branch-pool="bargainingPool.subBranchPool"
:private-domain-pool="bargainingPool.privateDomainPool"
:excess-profit-share="bargainingPool.excessProfitShare"
/>
</div>
</div>
</div>
</template>
<script>
import { setExecuteRate } from "@/api/loanPricing/workflow"
import ModelOutputDisplay from "./ModelOutputDisplay.vue"
import BargainingPoolDisplay from "./BargainingPoolDisplay.vue"
export default {
name: "PersonalWorkflowDetail",
components: {
ModelOutputDisplay,
BargainingPoolDisplay
},
props: {
detailData: {
type: Object,
required: true
},
retailOutput: {
type: Object,
default: null
},
bargainingPool: {
type: Object,
default: null
}
},
data() {
return {
loading: false,
executeRateInput: ''
}
},
watch: {
'detailData.executeRate': {
handler(newVal) {
this.executeRateInput = newVal || ''
},
immediate: true
}
},
methods: {
/** 格式化布尔值为中文 */
formatBoolean(value) {
if (value === 'true' || value === true) return '是'
if (value === 'false' || value === false) return '否'
return value || '-'
},
/** 获取基准利率 */
getBaseLoanRate() {
return this.retailOutput?.baseLoanRate || '-'
},
/** 获取浮动BP */
getTotalBp() {
return this.retailOutput?.totalBp || '-'
},
/** 获取测算利率 */
getCalculateRate() {
return this.retailOutput?.calculateRate || '-'
},
/** 设定执行利率 */
handleSetExecuteRate() {
const value = this.executeRateInput
if (value === null || value === undefined || value === '') {
this.$modal.msgError("请输入执行利率")
return
}
const numValue = parseFloat(value)
if (isNaN(numValue)) {
this.$modal.msgError("请输入有效的数字")
return
}
if (numValue < 0 || numValue > 100) {
this.$modal.msgError("执行利率必须在 0 到 100 之间")
return
}
this.loading = true
setExecuteRate(this.detailData.serialNum, value.toString()).then(() => {
this.$modal.msgSuccess("执行利率设定成功")
this.$emit('refresh')
this.loading = false
}).catch(error => {
this.$modal.msgError("设定失败:" + (error.msg || error.message || "未知错误"))
this.loading = false
})
}
}
}
</script>
<style lang="scss" scoped>
.personal-workflow-detail {
.detail-layout {
display: flex;
gap: 20px;
align-items: flex-start;
.left-panel {
flex: 0 0 280px;
max-width: 280px;
.summary-card {
::v-deep .el-card__header {
padding: 16px 20px;
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
}
.card-header {
display: flex;
align-items: center;
.card-title {
font-size: 16px;
font-weight: 500;
color: #303133;
}
}
::v-deep .el-card__body {
padding: 16px;
}
.execute-rate-input-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
.execute-rate-input {
width: 100%;
}
}
.rate-value {
color: #67c23a;
font-weight: 500;
}
.total-bp-value {
color: #e6a23c;
font-weight: 600;
}
.calculate-rate {
color: #f56c6c;
font-weight: 600;
font-size: 16px;
}
}
}
.right-panel {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 20px;
.detail-card {
::v-deep .el-card__header {
padding: 16px 20px;
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
}
.card-header {
display: flex;
align-items: center;
.card-title {
font-size: 16px;
font-weight: 500;
color: #303133;
}
}
::v-deep .el-card__body {
padding: 20px;
}
.info-section {
&:not(:last-child) {
margin-bottom: 24px;
}
.section-title {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 500;
color: #606266;
padding-bottom: 8px;
border-bottom: 1px solid #ebeef5;
}
}
}
}
}
}
@media screen and (max-width: 992px) {
.personal-workflow-detail {
.detail-layout {
flex-direction: column;
.left-panel,
.right-panel {
flex: 1 1 100%;
max-width: 100%;
}
}
}
}
</style>
```
**Step 2: 验证组件语法正确**
检查: 在浏览器开发工具中确认无语法错误
---
## Task 2: 创建企业客户详情组件 CorporateWorkflowDetail.vue
**文件:**
- 创建: `ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue`
**Step 1: 创建组件基础结构**
```vue
<template>
<div class="corporate-workflow-detail" v-loading="loading">
<!-- 两栏布局左侧关键信息 + 右侧流程详情+模型输出 -->
<div v-if="!loading && detailData" class="detail-layout">
<!-- 左侧关键信息卡片 -->
<div class="left-panel">
<el-card class="summary-card">
<div slot="header" class="card-header">
<span class="card-title">关键信息</span>
</div>
<el-descriptions :column="1" direction="vertical" border>
<el-descriptions-item label="业务方流水号">{{ detailData.serialNum }}</el-descriptions-item>
<el-descriptions-item label="客户名称">{{ detailData.custName }}</el-descriptions-item>
<el-descriptions-item label="客户类型">{{ detailData.custType }}</el-descriptions-item>
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} </el-descriptions-item>
<el-descriptions-item label="基准利率">
<span class="rate-value">{{ getBaseLoanRate() }}</span> %
</el-descriptions-item>
<el-descriptions-item label="浮动BP">
<span class="total-bp-value">{{ getTotalBp() }}</span>
</el-descriptions-item>
<el-descriptions-item label="测算利率">
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
</el-descriptions-item>
<el-descriptions-item label="执行利率">
<div class="execute-rate-input-wrapper">
<el-input
v-model="executeRateInput"
class="execute-rate-input"
placeholder="请输入执行利率"
>
<template slot="append">%</template>
</el-input>
<el-button
type="primary"
size="small"
@click="handleSetExecuteRate"
>
确定
</el-button>
</div>
</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 流程详情卡片 -->
<el-card class="detail-card">
<div slot="header" class="card-header">
<span class="card-title">流程详情</span>
</div>
<!-- 基本信息组 -->
<div class="info-section">
<h4 class="section-title">基本信息</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="机构编码">{{ detailData.orgCode }}</el-descriptions-item>
<el-descriptions-item label="运行模式">{{ detailData.runType }}</el-descriptions-item>
<el-descriptions-item label="客户内码">{{ detailData.custIsn }}</el-descriptions-item>
<el-descriptions-item label="证件类型">{{ detailData.idType }}</el-descriptions-item>
<el-descriptions-item label="证件号码">{{ detailData.idNum }}</el-descriptions-item>
<el-descriptions-item label="贷款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
<el-descriptions-item label="创建者">{{ detailData.createBy }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 业务信息组 -->
<div class="info-section">
<h4 class="section-title">业务信息</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} </el-descriptions-item>
<el-descriptions-item label="省农担担保贷款">{{ formatBoolean(detailData.isAgriGuar) }}</el-descriptions-item>
<el-descriptions-item label="绿色贷款">{{ formatBoolean(detailData.isGreenLoan) }}</el-descriptions-item>
<el-descriptions-item label="科技型企业">{{ formatBoolean(detailData.isTechEnt) }}</el-descriptions-item>
<el-descriptions-item label="抵质押类型">{{ detailData.collType || '-' }}</el-descriptions-item>
<el-descriptions-item label="抵质押物是否三方所有">{{ formatBoolean(detailData.collThirdParty) }}</el-descriptions-item>
</el-descriptions>
</div>
</el-card>
<!-- 模型输出卡片 -->
<ModelOutputDisplay
:cust-type="detailData.custType"
:retail-output="null"
:corp-output="corpOutput"
/>
<!-- 议价池卡片 -->
<BargainingPoolDisplay
v-if="bargainingPool"
:branch-pool="bargainingPool.branchPool"
:sub-branch-pool="bargainingPool.subBranchPool"
:private-domain-pool="bargainingPool.privateDomainPool"
:excess-profit-share="bargainingPool.excessProfitShare"
/>
</div>
</div>
</div>
</template>
<script>
import { setExecuteRate } from "@/api/loanPricing/workflow"
import ModelOutputDisplay from "./ModelOutputDisplay.vue"
import BargainingPoolDisplay from "./BargainingPoolDisplay.vue"
export default {
name: "CorporateWorkflowDetail",
components: {
ModelOutputDisplay,
BargainingPoolDisplay
},
props: {
detailData: {
type: Object,
required: true
},
corpOutput: {
type: Object,
default: null
},
bargainingPool: {
type: Object,
default: null
}
},
data() {
return {
loading: false,
executeRateInput: ''
}
},
watch: {
'detailData.executeRate': {
handler(newVal) {
this.executeRateInput = newVal || ''
},
immediate: true
}
},
methods: {
/** 格式化布尔值为中文 */
formatBoolean(value) {
if (value === 'true' || value === true) return '是'
if (value === 'false' || value === false) return '否'
return value || '-'
},
/** 获取基准利率 */
getBaseLoanRate() {
return this.corpOutput?.baseLoanRate || '-'
},
/** 获取浮动BP */
getTotalBp() {
return this.corpOutput?.totalBp || '-'
},
/** 获取测算利率 */
getCalculateRate() {
return this.corpOutput?.calculateRate || '-'
},
/** 设定执行利率 */
handleSetExecuteRate() {
const value = this.executeRateInput
if (value === null || value === undefined || value === '') {
this.$modal.msgError("请输入执行利率")
return
}
const numValue = parseFloat(value)
if (isNaN(numValue)) {
this.$modal.msgError("请输入有效的数字")
return
}
if (numValue < 0 || numValue > 100) {
this.$modal.msgError("执行利率必须在 0 到 100 之间")
return
}
this.loading = true
setExecuteRate(this.detailData.serialNum, value.toString()).then(() => {
this.$modal.msgSuccess("执行利率设定成功")
this.$emit('refresh')
this.loading = false
}).catch(error => {
this.$modal.msgError("设定失败:" + (error.msg || error.message || "未知错误"))
this.loading = false
})
}
}
}
</script>
<style lang="scss" scoped>
.corporate-workflow-detail {
.detail-layout {
display: flex;
gap: 20px;
align-items: flex-start;
.left-panel {
flex: 0 0 280px;
max-width: 280px;
.summary-card {
::v-deep .el-card__header {
padding: 16px 20px;
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
}
.card-header {
display: flex;
align-items: center;
.card-title {
font-size: 16px;
font-weight: 500;
color: #303133;
}
}
::v-deep .el-card__body {
padding: 16px;
}
.execute-rate-input-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
.execute-rate-input {
width: 100%;
}
}
.rate-value {
color: #67c23a;
font-weight: 500;
}
.total-bp-value {
color: #e6a23c;
font-weight: 600;
}
.calculate-rate {
color: #f56c6c;
font-weight: 600;
font-size: 16px;
}
}
}
.right-panel {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 20px;
.detail-card {
::v-deep .el-card__header {
padding: 16px 20px;
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
}
.card-header {
display: flex;
align-items: center;
.card-title {
font-size: 16px;
font-weight: 500;
color: #303133;
}
}
::v-deep .el-card__body {
padding: 20px;
}
.info-section {
&:not(:last-child) {
margin-bottom: 24px;
}
.section-title {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 500;
color: #606266;
padding-bottom: 8px;
border-bottom: 1px solid #ebeef5;
}
}
}
}
}
}
@media screen and (max-width: 992px) {
.corporate-workflow-detail {
.detail-layout {
flex-direction: column;
.left-panel,
.right-panel {
flex: 1 1 100%;
max-width: 100%;
}
}
}
}
</style>
```
**Step 2: 验证组件语法正确**
检查: 在浏览器开发工具中确认无语法错误
---
## Task 3: 修改 detail.vue 为容器组件
**文件:**
- 修改: `ruoyi-ui/src/views/loanPricing/workflow/detail.vue`
**Step 1: 替换为容器组件结构**
将整个文件内容替换为:
```vue
<template>
<div class="app-container workflow-detail-container" v-loading="loading">
<!-- 页面头部标题和返回按钮 -->
<div class="page-header">
<h2 class="page-title">流程详情</h2>
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
</div>
<!-- 根据客户类型渲染对应的详情组件 -->
<personal-workflow-detail
v-if="!loading && workflowDetail && workflowDetail.custType === '个人'"
:detail-data="workflowDetail"
:retail-output="retailOutput"
:bargaining-pool="bargainingPool"
@refresh="getDetail"
/>
<corporate-workflow-detail
v-if="!loading && workflowDetail && workflowDetail.custType === '企业'"
:detail-data="workflowDetail"
:corp-output="corpOutput"
:bargaining-pool="bargainingPool"
@refresh="getDetail"
/>
</div>
</template>
<script>
import { getWorkflow } from "@/api/loanPricing/workflow"
import PersonalWorkflowDetail from "./components/PersonalWorkflowDetail.vue"
import CorporateWorkflowDetail from "./components/CorporateWorkflowDetail.vue"
export default {
name: "LoanPricingWorkflowDetail",
components: {
PersonalWorkflowDetail,
CorporateWorkflowDetail
},
data() {
return {
loading: true,
workflowDetail: null,
retailOutput: null,
corpOutput: null,
bargainingPool: null
}
},
created() {
this.getDetail()
},
methods: {
/** 获取流程详情 */
getDetail() {
const serialNum = this.$route.params.serialNum
if (!serialNum) {
this.$modal.msgError("缺少业务方流水号参数")
this.goBack()
return
}
getWorkflow(serialNum).then(response => {
this.workflowDetail = response.data.loanPricingWorkflow
this.retailOutput = response.data.modelRetailOutputFields
this.corpOutput = response.data.modelCorpOutputFields
this.bargainingPool = response.data.bargainingPool
this.loading = false
}).catch(error => {
this.$modal.msgError("获取流程详情失败:" + (error.message || "未知错误"))
this.loading = false
})
},
/** 返回上一页 */
goBack() {
this.$router.go(-1)
}
}
}
</script>
<style lang="scss" scoped>
.workflow-detail-container {
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 0 4px;
.page-title {
margin: 0;
font-size: 20px;
font-weight: 500;
color: #303133;
}
}
}
</style>
```
**Step 2: 验证修改正确**
检查: 在浏览器中访问详情页,确认根据客户类型正确渲染对应组件
---
## Task 4: 测试验证
**Step 1: 测试个人客户详情页**
1. 在列表页点击个人客户记录的"查看"按钮
2. 确认页面正确显示 PersonalWorkflowDetail 组件
3. 确认所有个人客户字段正确显示
4. 确认模型输出正确展示retailOutput
5. 测试执行利率设定功能
**Step 2: 测试企业客户详情页**
1. 在列表页点击企业客户记录的"查看"按钮
2. 确认页面正确显示 CorporateWorkflowDetail 组件
3. 确认所有企业客户字段正确显示
4. 确认模型输出正确展示corpOutput
5. 测试执行利率设定功能
**Step 3: 响应式布局测试**
1. 调整浏览器窗口宽度到小于992px
2. 确认布局自动切换为单列垂直布局
3. 确认所有内容正常显示
---
## Task 5: 提交代码
**Step 1: 添加文件到 Git**
```bash
git add ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue
git add ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue
git add ruoyi-ui/src/views/loanPricing/workflow/detail.vue
```
**Step 2: 提交变更**
```bash
git commit -m "feat: 根据客户类型动态展示流程详情
- 创建 PersonalWorkflowDetail.vue 个人客户详情组件
- 创建 CorporateWorkflowDetail.vue 企业客户详情组件
- 修改 detail.vue 为容器组件,根据 custType 动态渲染
- 个人客户展示:基本信息(客户内码、证件信息)+ 业务信息(担保方式、经营佐证、循环功能等)
- 企业客户展示:基本信息(客户内码、证件信息、贷款期限)+ 业务信息(省农担担保、绿色贷款、科技型企业等)
- 两个组件完全独立,各自实现格式化和计算方法
- 支持响应式布局,小屏幕自动切换为单列布局"
```
---
## 相关文档
- 字段定义参考: `doc/person.csv``doc/corp.csv`
- API 文档: `doc/api/loan-pricing-workflow-api.md`
- 原详情页面: `ruoyi-ui/src/views/loanPricing/workflow/detail.vue` (修改前)

View File

@@ -0,0 +1,216 @@
# 利率定价创建流程前端重构设计文档
## 设计概述
本文档描述利率定价创建流程的前端重构设计,将原有的单一表单拆分为个人客户和企业客户两个独立的创建流程。
## 设计日期
2026-02-02
---
## 一、交互流程
### 用户操作步骤
1. **点击「新增」按钮** → 弹出「选择客户类型」对话框
2. **选择客户类型** → 打开对应的创建表单对话框
3. **填写表单并提交** → 调用对应的 API 接口
4. **成功/失败处理**
- 成功:关闭对话框,刷新列表,显示成功提示
- 失败:保留表单数据,显示后端错误信息
---
## 二、组件结构
### 文件组织
```
ruoyi-ui/src/
├── views/loanPricing/workflow/
│ ├── index.vue (修改)
│ └── components/
│ ├── CustomerTypeSelector.vue (新增)
│ ├── WorkflowCreateDialog.vue (删除)
│ ├── PersonalCreateDialog.vue (新增)
│ └── CorporateCreateDialog.vue (新增)
└── api/loanPricing/
└── workflow.js (修改)
```
### 组件职责
| 组件 | 职责 |
|-----------------------|-----------|
| CustomerTypeSelector | 客户类型选择对话框 |
| PersonalCreateDialog | 个人客户创建表单 |
| CorporateCreateDialog | 企业客户创建表单 |
---
## 三、表单字段设计
### 个人客户表单字段
| 分组 | 字段 | 类型 | 必填 | 说明 |
|-------|----------------|--------|----|------------|
| 基本信息 | custIsn | String | 是 | 客户内码 |
| 基本信息 | custName | String | 是 | 客户名称 |
| 基本信息 | idType | String | 是 | 证件类型 |
| 基本信息 | idNum | String | 是 | 证件号码 |
| 贷款信息 | guarType | String | 是 | 担保方式 |
| 贷款信息 | applyAmt | String | 是 | 申请金额(元) |
| 贷款信息 | bizProof | String | 是 | 是否有经营佐证 |
| 贷款信息 | loanLoop | String | 是 | 循环功能 |
| 抵质押信息 | collType | String | 是 | 抵质押类型 |
| 抵质押信息 | collThirdParty | String | 是 | 抵质押物是否三方所有 |
### 企业客户表单字段
| 分组 | 字段 | 类型 | 必填 | 说明 |
|-------|---------------------|--------|----|------------|
| 基本信息 | custIsn | String | 是 | 客户内码 |
| 基本信息 | custName | String | 是 | 客户名称 |
| 基本信息 | idType | String | 是 | 证件类型 |
| 基本信息 | idNum | String | 是 | 证件号码 |
| 贷款信息 | guarType | String | 是 | 担保方式 |
| 贷款信息 | applyAmt | String | 是 | 申请金额(元) |
| 贷款信息 | loanTerm | String | 是 | 贷款期限(月) |
| 企业标识 | isAgriGuar | String | 是 | 省农担担保贷款 |
| 企业标识 | isGreenLoan | String | 是 | 绿色贷款 |
| 企业标识 | isTechEnt | String | 是 | 科技型企业 |
| 企业标识 | isTradeConstruction | String | 是 | 贸易和建筑业企业 |
| 抵质押信息 | collType | String | 是 | 抵质押类型 |
| 抵质押信息 | collThirdParty | String | 是 | 抵质押物是否三方所有 |
---
## 四、API 接口设计
### 新增接口函数
```javascript
// 创建个人客户利率定价流程
export function createPersonalWorkflow(data) {
return request({
url: '/loanPricing/workflow/create/personal',
method: 'post',
data: data
})
}
// 创建企业客户利率定价流程
export function createCorporateWorkflow(data) {
return request({
url: '/loanPricing/workflow/create/corporate',
method: 'post',
data: data
})
}
```
### 数据转换规则
开关字段在提交时需要转换为字符串 `"true"/"false"`
```javascript
bizProof: this.form.bizProof ? 'true' : 'false'
```
---
## 五、验证规则
### 个人客户验证规则
| 字段 | 验证规则 |
|----------------|------------------------|
| custIsn | 必填,长度 1-50 字符 |
| custName | 必填,长度 1-100 字符 |
| idType | 必填 |
| idNum | 必填,身份证格式 15/18 位 |
| guarType | 必填 |
| applyAmt | 必填,正数,最大值 999999999.99 |
| bizProof | 必填(开关值,默认 "false" |
| loanLoop | 必填(开关值,默认 "false" |
| collType | 必填 |
| collThirdParty | 必填(开关值,默认 "false" |
### 企业客户验证规则
| 字段 | 验证规则 |
|---------------------|------------------------|
| custIsn | 必填,长度 1-50 字符 |
| custName | 必填,长度 1-100 字符 |
| idType | 必填 |
| idNum | 必填,统一社会信用代码 18 位 |
| guarType | 必填 |
| applyAmt | 必填,正数,最大值 999999999.99 |
| loanTerm | 必填,正整数,范围 1-360 |
| isAgriGuar | 必填(开关值,默认 "false" |
| isGreenLoan | 必填(开关值,默认 "false" |
| isTechEnt | 必填(开关值,默认 "false" |
| isTradeConstruction | 必填(开关值,默认 "false" |
| collType | 必填 |
| collThirdParty | 必填(开关值,默认 "false" |
---
## 六、UI 样式规范
### 对话框规范
| 对话框类型 | 宽度 | 说明 |
|--------|-------|------|
| 客户类型选择 | 500px | 居中显示 |
| 表单对话框 | 900px | 两列布局 |
### 表单布局
- 分组:使用 `el-divider` 分隔,带标题
- 字段:两列布局(`el-col :span="12"`
- 必填标识:红色星号
- 按钮:右下角,「确定」(主按钮)、「取消」
### 响应式处理
- 当屏幕宽度 < 768px 时,表单字段改为单列布局
---
## 七、错误处理
| 场景 | 处理方式 |
|--------|--------------------|
| 前端验证失败 | 阻止提交,高亮错误字段 |
| 网络错误 | 显示网络错误提示,保留表单 |
| 后端业务错误 | 显示后端返回的 `msg`,保留表单 |
| 成功 | 关闭对话框,刷新列表,显示成功提示 |
---
## 八、数据字典
### 担保方式 (guarType)
| 值 | 说明 |
|----|------|
| 信用 | 信用贷款 |
| 保证 | 保证贷款 |
| 抵押 | 抵押贷款 |
| 质押 | 质押贷款 |
### 抵质押类型 (collType)
| 值 | 说明 |
|----|------|
| 一线 | 一线抵押 |
| 一类 | 一类抵押 |
| 二类 | 二类抵押 |
### 证件类型 (idType)
- 个人:身份证
- 企业:统一社会信用代码