Compare commits
18 Commits
eb58634edf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e3d3708c6 | |||
| 5ba58bafa6 | |||
| 25e9fac1b9 | |||
| b8626db4dd | |||
| 519d955679 | |||
| 7f6cf8797d | |||
| e0ef7f9788 | |||
| c8715d2b74 | |||
| 8b82f479c4 | |||
| bce3f26424 | |||
| 4b06c16ff5 | |||
| 3be949f95b | |||
| 2fb1a3b23d | |||
| cf843557a6 | |||
| 13f2940a3a | |||
| 8718429ad1 | |||
| b737fe660f | |||
| da757b3b21 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -31,7 +31,6 @@ src/types
|
|||||||
# 单独把这个文件排除掉,用以解决部分电脑生成的 auto-import.d.ts 的API不完整导致类型提示报错问题
|
# 单独把这个文件排除掉,用以解决部分电脑生成的 auto-import.d.ts 的API不完整导致类型提示报错问题
|
||||||
!src/types/auto-import.d.ts
|
!src/types/auto-import.d.ts
|
||||||
src/manifest.json
|
src/manifest.json
|
||||||
src/pages.json
|
|
||||||
|
|
||||||
# 2025-10-15 by 菲鸽: lock 文件还是需要加入版本管理,今天又遇到版本不一致导致无法运行的问题了。
|
# 2025-10-15 by 菲鸽: lock 文件还是需要加入版本管理,今天又遇到版本不一致导致无法运行的问题了。
|
||||||
# pnpm-lock.yaml
|
# pnpm-lock.yaml
|
||||||
@@ -46,3 +45,4 @@ src/pages.json
|
|||||||
|
|
||||||
# 更新 uni-app 官方版本
|
# 更新 uni-app 官方版本
|
||||||
# npx @dcloudio/uvm@latest
|
# npx @dcloudio/uvm@latest
|
||||||
|
src/pages.json
|
||||||
|
|||||||
89
interaction-flow.md
Normal file
89
interaction-flow.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# 应结账款页面交互流程图
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
## 用户操作流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[用户进入应结账款页面] --> B[查看商户列表]
|
||||||
|
B --> C[点击商户头部的"消账"按钮]
|
||||||
|
C --> D[进入选择模式]
|
||||||
|
|
||||||
|
D --> E{用户操作}
|
||||||
|
E -->|点击订单| F[选择/取消选择订单]
|
||||||
|
E -->|点击全选| G[全选/取消全选]
|
||||||
|
E -->|点击取消| H[退出选择模式]
|
||||||
|
E -->|选择订单后点击进行消账| I[打开消账弹窗]
|
||||||
|
|
||||||
|
F --> J[更新已选数量和金额]
|
||||||
|
G --> J
|
||||||
|
J --> K{是否有选中订单}
|
||||||
|
K -->|有| L[显示"进行消账(X)"按钮]
|
||||||
|
K -->|无| M[显示"取消"按钮]
|
||||||
|
|
||||||
|
L --> N[点击"进行消账"]
|
||||||
|
N --> I
|
||||||
|
I --> O[填写消账信息]
|
||||||
|
O --> P[提交消账申请]
|
||||||
|
P --> Q[消账成功]
|
||||||
|
Q --> R[退出选择模式]
|
||||||
|
R --> S[刷新列表]
|
||||||
|
|
||||||
|
H --> T[返回正常模式]
|
||||||
|
M --> T
|
||||||
|
T --> B
|
||||||
|
S --> B
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据流图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[用户点击消账] --> B[selectionMode[merchantId] = true]
|
||||||
|
B --> C[显示选择界面]
|
||||||
|
|
||||||
|
C --> D[用户选择订单]
|
||||||
|
D --> E[selectedOrders[merchantId] 更新]
|
||||||
|
E --> F[计算选中数量和金额]
|
||||||
|
F --> G[更新按钮状态]
|
||||||
|
|
||||||
|
G --> H[点击进行消账]
|
||||||
|
H --> I[获取选中订单数据]
|
||||||
|
I --> J[调用消账API]
|
||||||
|
J --> K[消账完成]
|
||||||
|
K --> L[selectionMode[merchantId] = false]
|
||||||
|
L --> M[刷新页面数据]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 组件状态管理
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> NormalMode
|
||||||
|
NormalMode --> SelectionMode: 点击消账按钮
|
||||||
|
SelectionMode --> NormalMode: 点击取消
|
||||||
|
SelectionMode --> NormalMode: 消账完成
|
||||||
|
SelectionMode --> SelectionMode: 选择/取消选择订单
|
||||||
|
SelectionMode --> SelectionMode: 全选/取消全选
|
||||||
|
|
||||||
|
state SelectionMode {
|
||||||
|
[*] --> NoSelection
|
||||||
|
NoSelection --> HasSelection: 选择订单
|
||||||
|
HasSelection --> NoSelection: 取消选择所有订单
|
||||||
|
HasSelection --> HasSelection: 继续选择订单
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 条件判断逻辑
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[订单点击事件] --> B{是否在选择模式?}
|
||||||
|
B -->|否| C[忽略点击]
|
||||||
|
B -->|是| D{订单状态是否为未结或逾期?}
|
||||||
|
D -->|否| E[忽略点击]
|
||||||
|
D -->|是| F{订单是否已选中?}
|
||||||
|
F -->|是| G[取消选中订单]
|
||||||
|
F -->|否| H[选中订单]
|
||||||
|
G --> I[更新选中列表]
|
||||||
|
H --> I
|
||||||
|
I --> J[更新UI显示]
|
||||||
@@ -18,7 +18,7 @@ const pages = {
|
|||||||
type: 'home',
|
type: 'home',
|
||||||
style: {
|
style: {
|
||||||
navigationStyle: 'custom',
|
navigationStyle: 'custom',
|
||||||
navigationBarTitleText: '首页',
|
navigationBarTitleText: '数字广东',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
342
settlement-redesign-plan.md
Normal file
342
settlement-redesign-plan.md
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
# 应结账款页面重构方案
|
||||||
|
|
||||||
|
## 需求概述
|
||||||
|
|
||||||
|
1. 将商户卡片中的"批量消账"按钮改为"消账"
|
||||||
|
2. 移除每个订单的"申请消账"按钮
|
||||||
|
3. 点击"消账"按钮后,进入选择模式,可以选择该商户下的多个订单
|
||||||
|
4. 选择订单后,按钮文本变为"进行消账(X)",X为选择的订单数量
|
||||||
|
5. 添加全选/取消全选功能
|
||||||
|
6. 显示已选订单的汇总金额
|
||||||
|
7. 限制只能选择未结和逾期状态的订单
|
||||||
|
8. 点击"进行消账"后,执行批量消账逻辑
|
||||||
|
|
||||||
|
## 数据结构设计
|
||||||
|
|
||||||
|
### 新增状态变量
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 选择模式状态
|
||||||
|
const selectionMode = ref<Record<string, boolean>>({}) // 记录每个商户是否处于选择模式
|
||||||
|
const selectedOrders = ref<Record<string, string[]>>({}) // 记录每个商户选中的订单ID列表
|
||||||
|
```
|
||||||
|
|
||||||
|
### 计算属性
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 计算每个商户选中的订单数量
|
||||||
|
const selectedCount = computed(() => {
|
||||||
|
return (merchantId: string) => selectedOrders.value[merchantId]?.length || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算每个商户选中的订单总金额
|
||||||
|
const selectedAmount = computed(() => {
|
||||||
|
return (merchantId: string) => {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return 0
|
||||||
|
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
return group.settlements
|
||||||
|
.filter(item => selectedIds.includes(item.id))
|
||||||
|
.reduce((sum, item) => sum + item.amount, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算每个商户是否全选
|
||||||
|
const isAllSelected = computed(() => {
|
||||||
|
return (merchantId: string) => {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return false
|
||||||
|
|
||||||
|
const selectableOrders = group.settlements.filter(
|
||||||
|
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
|
||||||
|
)
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
|
||||||
|
return selectableOrders.length > 0 && selectableOrders.every(item => selectedIds.includes(item.id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## UI 设计
|
||||||
|
|
||||||
|
### 1. 商户头部修改
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- 商户头部 -->
|
||||||
|
<view class="merchant-header">
|
||||||
|
<!-- 原有商户信息保持不变 -->
|
||||||
|
|
||||||
|
<!-- 头部操作区域 -->
|
||||||
|
<view v-if="currentTab === 0" class="header-action">
|
||||||
|
<!-- 非选择模式 -->
|
||||||
|
<view v-if="!selectionMode[group.merchantId]" class="batch-btn-small" @click.stop="enterSelectionMode(group.merchantId)">
|
||||||
|
<text class="i-carbon-checkmark-outline" />
|
||||||
|
<text>消账</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选择模式 -->
|
||||||
|
<template v-else>
|
||||||
|
<view class="selection-controls">
|
||||||
|
<view class="select-all-btn" @click.stop="toggleSelectAll(group.merchantId)">
|
||||||
|
<text class="i-carbon-checkmark-filled" v-if="isAllSelected(group.merchantId)" />
|
||||||
|
<text class="i-carbon-checkmark-outline" v-else />
|
||||||
|
<text>全选</text>
|
||||||
|
</view>
|
||||||
|
<view class="selected-info">
|
||||||
|
<text>已选 {{ selectedCount(group.merchantId) }} 单</text>
|
||||||
|
<text class="amount">¥{{ selectedAmount(group.merchantId).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="selectedCount(group.merchantId) > 0"
|
||||||
|
class="batch-btn-small active"
|
||||||
|
@click.stop="handleBatchWriteOff(group.merchantId)"
|
||||||
|
>
|
||||||
|
<text class="i-carbon-checkmark-outline" />
|
||||||
|
<text>进行消账({{ selectedCount(group.merchantId) }})</text>
|
||||||
|
</view>
|
||||||
|
<view v-else class="cancel-btn" @click.stop="exitSelectionMode(group.merchantId)">
|
||||||
|
<text>取消</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 订单列表修改
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- 订单列表 -->
|
||||||
|
<view class="order-list">
|
||||||
|
<view
|
||||||
|
v-for="item in group.settlements"
|
||||||
|
:key="item.id"
|
||||||
|
class="order-item"
|
||||||
|
:class="{
|
||||||
|
'selection-mode': selectionMode[group.merchantId],
|
||||||
|
'selected': selectedOrders[group.merchantId]?.includes(item.id),
|
||||||
|
'disabled': item.status !== SettlementStatus.UNSETTLED && item.status !== SettlementStatus.OVERDUE
|
||||||
|
}"
|
||||||
|
@click="handleOrderClick(group.merchantId, item.id, item.status)"
|
||||||
|
>
|
||||||
|
<!-- 选择框 -->
|
||||||
|
<view v-if="selectionMode[group.merchantId]" class="checkbox">
|
||||||
|
<text class="i-carbon-checkmark-filled" v-if="selectedOrders[group.merchantId]?.includes(item.id)" />
|
||||||
|
<text class="i-carbon-checkmark-outline" v-else />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 订单内容 -->
|
||||||
|
<view class="order-content">
|
||||||
|
<!-- 原有订单头部和内容保持不变 -->
|
||||||
|
<!-- 移除订单操作区域 -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能实现
|
||||||
|
|
||||||
|
### 1. 进入选择模式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function enterSelectionMode(merchantId: string) {
|
||||||
|
selectionMode.value[merchantId] = true
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 退出选择模式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function exitSelectionMode(merchantId: string) {
|
||||||
|
selectionMode.value[merchantId] = false
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 处理订单点击
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function handleOrderClick(merchantId: string, orderId: string, status: SettlementStatus) {
|
||||||
|
// 只有在选择模式下且订单状态为未结或逾期时才能选择
|
||||||
|
if (!selectionMode.value[merchantId]) return
|
||||||
|
if (status !== SettlementStatus.UNSETTLED && status !== SettlementStatus.OVERDUE) return
|
||||||
|
|
||||||
|
const selected = selectedOrders.value[merchantId] || []
|
||||||
|
const index = selected.indexOf(orderId)
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
// 取消选择
|
||||||
|
selected.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
// 添加选择
|
||||||
|
selected.push(orderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedOrders.value[merchantId] = selected
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 全选/取消全选
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function toggleSelectAll(merchantId: string) {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return
|
||||||
|
|
||||||
|
const selectableOrders = group.settlements.filter(
|
||||||
|
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isAllSelected.value(merchantId)) {
|
||||||
|
// 取消全选
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
} else {
|
||||||
|
// 全选
|
||||||
|
selectedOrders.value[merchantId] = selectableOrders.map(item => item.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 批量消账
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function handleBatchWriteOff(merchantId: string) {
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
if (selectedIds.length === 0) return
|
||||||
|
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return
|
||||||
|
|
||||||
|
// 获取选中的订单
|
||||||
|
const selectedSettlements = group.settlements.filter(item => selectedIds.includes(item.id))
|
||||||
|
|
||||||
|
currentSettlement.value = null
|
||||||
|
currentMerchantSettlements.value = selectedSettlements
|
||||||
|
isBatchMode.value = true
|
||||||
|
writeOffVisible.value = true
|
||||||
|
|
||||||
|
// 退出选择模式
|
||||||
|
exitSelectionMode(merchantId)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 样式设计
|
||||||
|
|
||||||
|
### 1. 选择模式样式
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.order-item {
|
||||||
|
&.selection-mode {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #ddd;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.i-carbon-checkmark-outline {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.i-carbon-checkmark-filled {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected .checkbox {
|
||||||
|
background: rgba($primary, 0.1);
|
||||||
|
border-color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 选择控制区域样式
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.selection-controls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
.select-all-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
background: rgba($primary, 0.05);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
color: $primary;
|
||||||
|
font-size: 22rpx;
|
||||||
|
|
||||||
|
.i-carbon-checkmark-outline,
|
||||||
|
.i-carbon-checkmark-filled {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-info {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 22rpx;
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
background: rgba($text-2, 0.1);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
color: $text-2;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-btn-small.active {
|
||||||
|
background: $primary;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 交互流程
|
||||||
|
|
||||||
|
1. 用户点击商户头部的"消账"按钮
|
||||||
|
2. 进入选择模式,显示选择框和全选按钮
|
||||||
|
3. 用户可以选择/取消选择符合条件的订单
|
||||||
|
4. 选择订单后,显示已选数量和汇总金额
|
||||||
|
5. 点击"进行消账"按钮,打开消账弹窗
|
||||||
|
6. 消账完成后退出选择模式,刷新列表
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 只有未结和逾期状态的订单才能被选择
|
||||||
|
2. 选择模式下,其他商户的卡片保持正常状态
|
||||||
|
3. 消账弹窗需要支持批量处理多个订单
|
||||||
|
4. 需要处理选择状态的响应式更新
|
||||||
@@ -13,8 +13,8 @@ export function login(data: { phone: string, code?: string, password?: string })
|
|||||||
const user: User = {
|
const user: User = {
|
||||||
id: 'user_001',
|
id: 'user_001',
|
||||||
username: data.phone,
|
username: data.phone,
|
||||||
nickname: `用户${data.phone.slice(-4)}`,
|
nickname: `王明阳`, // ${data.phone.slice(-4)}
|
||||||
avatar: 'https://picsum.photos/200/200?random=avatar',
|
avatar: '/static/images/avatar.jpg',
|
||||||
phone: data.phone,
|
phone: data.phone,
|
||||||
creditLimits: [],
|
creditLimits: [],
|
||||||
member: mockMember,
|
member: mockMember,
|
||||||
|
|||||||
@@ -93,12 +93,12 @@ function goToDetail() {
|
|||||||
// border-radius: 16rpx;
|
// border-radius: 16rpx;
|
||||||
border-bottom: 1rpx solid #f5f5f5;
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
position: relative; // 移到嵌套规则之前
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
@@ -129,16 +129,21 @@ function goToDetail() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
/* 添加右边距避免与删除按钮重叠 */
|
||||||
|
padding-right: 80rpx; /* 根据删除按钮大小调整 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
display: -webkit-box;
|
/* 移除固定行数限制,让内容自然换行 */
|
||||||
-webkit-box-orient: vertical;
|
display: block;
|
||||||
-webkit-line-clamp: 2;
|
overflow: visible;
|
||||||
overflow: hidden;
|
white-space: normal;
|
||||||
|
word-wrap: break-word;
|
||||||
|
/* 调整最小高度确保有足够空间 */
|
||||||
|
min-height: 80rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.specs {
|
.specs {
|
||||||
|
|||||||
390
src/components/finance/WriteOffRecord.vue
Normal file
390
src/components/finance/WriteOffRecord.vue
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
<template>
|
||||||
|
<view class="write-off-record-popup" :class="{ show: visible }">
|
||||||
|
<view class="mask" @click="handleClose"></view>
|
||||||
|
<view class="content">
|
||||||
|
<view class="header">
|
||||||
|
<text class="title">消账记录</text>
|
||||||
|
<text class="close-btn i-carbon-close" @click="handleClose"></text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<scroll-view scroll-y class="body">
|
||||||
|
<view v-if="writeOffRecords.length === 0" class="empty-state">
|
||||||
|
<text class="empty-text">暂无消账记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="record-list">
|
||||||
|
<view
|
||||||
|
v-for="(record, index) in writeOffRecords"
|
||||||
|
:key="record.id"
|
||||||
|
class="record-item"
|
||||||
|
>
|
||||||
|
<!-- 记录头部 -->
|
||||||
|
<view class="record-header">
|
||||||
|
<view class="record-info">
|
||||||
|
<text class="record-id">记录编号: {{ record.id }}</text>
|
||||||
|
<view class="status-badge" :class="getStatusClass(record.status)">
|
||||||
|
{{ getStatusText(record.status) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="record-amount">
|
||||||
|
<text class="amount-label">消账金额</text>
|
||||||
|
<text class="amount-value">¥{{ record.amount.toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 记录内容 -->
|
||||||
|
<view class="record-content">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">提交时间</text>
|
||||||
|
<text class="info-value">{{ record.submitTime }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="record.remark" class="info-row">
|
||||||
|
<text class="info-label">备注说明</text>
|
||||||
|
<text class="info-value">{{ record.remark }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 凭证图片 -->
|
||||||
|
<view v-if="record.proof && record.proof.length > 0" class="proof-section">
|
||||||
|
<view class="proof-label">凭证图片</view>
|
||||||
|
<view class="proof-list">
|
||||||
|
<view
|
||||||
|
v-for="(img, imgIndex) in record.proof"
|
||||||
|
:key="imgIndex"
|
||||||
|
class="proof-item"
|
||||||
|
@click="previewImage(img, record.proof)"
|
||||||
|
>
|
||||||
|
<image :src="img" mode="aspectFill" class="proof-image" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<view class="btn close-btn" @click="handleClose">关闭</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { WriteOff, WriteOffStatus } from '@/typings/mall'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean
|
||||||
|
writeOffRecords: WriteOff[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
writeOffRecords: () => [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:visible': [visible: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 状态文本
|
||||||
|
function getStatusText(status: WriteOffStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case 'pending':
|
||||||
|
return '待审核'
|
||||||
|
case 'approved':
|
||||||
|
return '已通过'
|
||||||
|
case 'rejected':
|
||||||
|
return '已拒绝'
|
||||||
|
default:
|
||||||
|
return '未知'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态样式类
|
||||||
|
function getStatusClass(status: WriteOffStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case 'pending':
|
||||||
|
return 'warning'
|
||||||
|
case 'approved':
|
||||||
|
return 'success'
|
||||||
|
case 'rejected':
|
||||||
|
return 'danger'
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
function handleClose() {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
function previewImage(current: string, urls: string[]) {
|
||||||
|
uni.previewImage({
|
||||||
|
current,
|
||||||
|
urls,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// 设计令牌
|
||||||
|
$primary: #4d80f0;
|
||||||
|
$danger: #fa4350;
|
||||||
|
$warning: #ff8f0d;
|
||||||
|
$success: #00c05a;
|
||||||
|
|
||||||
|
$text-1: #262626;
|
||||||
|
$text-2: #909399;
|
||||||
|
$text-3: #c0c4cc;
|
||||||
|
|
||||||
|
$bg-page: #f4f4f4;
|
||||||
|
$bg-card: #ffffff;
|
||||||
|
|
||||||
|
.write-off-record-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 999;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: visibility 0.3s;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
visibility: visible;
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 30rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: $text-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: 30rpx;
|
||||||
|
max-height: 600rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $text-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-item {
|
||||||
|
background: $bg-card;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.record-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
|
||||||
|
.record-id {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-2;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: $success;
|
||||||
|
background: rgba($success, 0.1);
|
||||||
|
}
|
||||||
|
&.warning {
|
||||||
|
color: $warning;
|
||||||
|
background: rgba($warning, 0.1);
|
||||||
|
}
|
||||||
|
&.danger {
|
||||||
|
color: $danger;
|
||||||
|
background: rgba($danger, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-amount {
|
||||||
|
text-align: right;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.amount-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: $text-2;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-value {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $danger;
|
||||||
|
font-family: 'DIN Alternate', sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-content {
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-2;
|
||||||
|
min-width: 120rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-1;
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-section {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
|
||||||
|
.proof-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-2;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
|
||||||
|
.proof-item {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.proof-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||||
|
border-top: 1rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
height: 88rpx;
|
||||||
|
background: $bg-page;
|
||||||
|
color: $text-1;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,3 +8,4 @@ export * from './finance'
|
|||||||
export * from './member'
|
export * from './member'
|
||||||
export * from './banner'
|
export * from './banner'
|
||||||
export * from './address'
|
export * from './address'
|
||||||
|
export * from './loan-application'
|
||||||
|
|||||||
141
src/mock/loan-application.ts
Normal file
141
src/mock/loan-application.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import { LoanApplicationStatus } from '@/typings/mall'
|
||||||
|
import type { LoanApplicationRecord } from '@/typings/mall'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 助贷申请记录模拟数据
|
||||||
|
*/
|
||||||
|
export const mockLoanApplicationRecords: LoanApplicationRecord[] = [
|
||||||
|
{
|
||||||
|
applicationId: "AP20230815001",
|
||||||
|
loanType: "ENTERPRISE",
|
||||||
|
loanTitle: "经营贷",
|
||||||
|
dateLabel: "申请时间",
|
||||||
|
dateValue: "2023-08-15",
|
||||||
|
status: LoanApplicationStatus.PROCESSING,
|
||||||
|
statusText: "处理中",
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
steps: ["提交申请", "资料审核", "风险评估", "审批完成"],
|
||||||
|
currentStepIndex: 2,
|
||||||
|
stepStatus: "active"
|
||||||
|
},
|
||||||
|
alertInfo: {
|
||||||
|
show: true,
|
||||||
|
type: "info",
|
||||||
|
content: "您的申请正在风控部门审核中,预计还需要1-2个工作日完成评估"
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
code: "VIEW_DETAIL",
|
||||||
|
text: "查看详情",
|
||||||
|
style: "primary-blue"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
applicationId: "AP20230810002",
|
||||||
|
loanType: "ENTERPRISE",
|
||||||
|
loanTitle: "经营贷",
|
||||||
|
dateLabel: "申请时间",
|
||||||
|
dateValue: "2023-08-10",
|
||||||
|
status: LoanApplicationStatus.COMPLETED,
|
||||||
|
statusText: "已完成",
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
code: "DOWNLOAD_CONTRACT",
|
||||||
|
text: "下载合同",
|
||||||
|
style: "text-link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "VIEW_RESULT",
|
||||||
|
text: "查看结果",
|
||||||
|
style: "primary-green"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
applicationId: "AP20230805003",
|
||||||
|
loanType: "REVITALIZATION",
|
||||||
|
loanTitle: "振兴贷",
|
||||||
|
dateLabel: "创建时间",
|
||||||
|
dateValue: "2023-08-05",
|
||||||
|
status: LoanApplicationStatus.PENDING,
|
||||||
|
statusText: "待提交",
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
steps: ["提交申请", "资料审核", "风险评估", "审批完成"],
|
||||||
|
currentStepIndex: 0,
|
||||||
|
stepStatus: "active"
|
||||||
|
},
|
||||||
|
alertInfo: {
|
||||||
|
show: true,
|
||||||
|
type: "warning",
|
||||||
|
content: "您的申请资料尚未完整,请尽快完善资料后提交"
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
code: "DELETE",
|
||||||
|
text: "删除",
|
||||||
|
style: "text-link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "CONTINUE_FILL",
|
||||||
|
text: "继续填写",
|
||||||
|
style: "primary-yellow"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
applicationId: "AP20230728004",
|
||||||
|
loanType: "HOUSING",
|
||||||
|
loanTitle: "安居贷",
|
||||||
|
dateLabel: "申请时间",
|
||||||
|
dateValue: "2023-07-28",
|
||||||
|
status: LoanApplicationStatus.PROCESSING,
|
||||||
|
statusText: "处理中",
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
steps: ["提交申请", "资料审核", "风险评估", "审批完成"],
|
||||||
|
currentStepIndex: 1,
|
||||||
|
stepStatus: "active"
|
||||||
|
},
|
||||||
|
alertInfo: {
|
||||||
|
show: true,
|
||||||
|
type: "info",
|
||||||
|
content: "您的申请已进入资料审核阶段,请耐心等待"
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
code: "CANCEL",
|
||||||
|
text: "取消申请",
|
||||||
|
style: "text-link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "VIEW_DETAIL",
|
||||||
|
text: "查看详情",
|
||||||
|
style: "primary-blue"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
applicationId: "AP20230720005",
|
||||||
|
loanType: "ENTERPRISE",
|
||||||
|
loanTitle: "经营贷",
|
||||||
|
dateLabel: "申请时间",
|
||||||
|
dateValue: "2023-07-20",
|
||||||
|
status: LoanApplicationStatus.COMPLETED,
|
||||||
|
statusText: "已完成",
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
code: "DOWNLOAD_CONTRACT",
|
||||||
|
text: "下载合同",
|
||||||
|
style: "text-link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "VIEW_RESULT",
|
||||||
|
text: "查看结果",
|
||||||
|
style: "primary-green"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Settlement } from '@/typings/mall'
|
import type { Settlement } from '@/typings/mall'
|
||||||
|
import type { WriteOff } from '@/typings/mall'
|
||||||
import SettlementItem from '@/components/finance/SettlementItem.vue'
|
import SettlementItem from '@/components/finance/SettlementItem.vue'
|
||||||
import WriteOffForm from '@/components/finance/WriteOffForm.vue'
|
import WriteOffForm from '@/components/finance/WriteOffForm.vue'
|
||||||
|
import WriteOffRecord from '@/components/finance/WriteOffRecord.vue'
|
||||||
import { useFinanceStore } from '@/store/finance'
|
import { useFinanceStore } from '@/store/finance'
|
||||||
import { SettlementStatus } from '@/typings/mall'
|
import { SettlementStatus } from '@/typings/mall'
|
||||||
|
|
||||||
@@ -36,6 +38,14 @@ const currentSettlement = ref<Settlement | null>(null)
|
|||||||
const currentMerchantSettlements = ref<Settlement[]>([]) // 批量消账时的商户所有账款
|
const currentMerchantSettlements = ref<Settlement[]>([]) // 批量消账时的商户所有账款
|
||||||
const isBatchMode = ref(false) // 是否批量消账模式
|
const isBatchMode = ref(false) // 是否批量消账模式
|
||||||
|
|
||||||
|
// 消账记录弹窗相关
|
||||||
|
const writeOffRecordVisible = ref(false)
|
||||||
|
const currentWriteOffRecords = ref<WriteOff[]>([]) // 当前订单的消账记录
|
||||||
|
|
||||||
|
// 选择模式状态
|
||||||
|
const selectionMode = ref<Record<string, boolean>>({}) // 记录每个商户是否处于选择模式
|
||||||
|
const selectedOrders = ref<Record<string, string[]>>({}) // 记录每个商户选中的订单ID列表
|
||||||
|
|
||||||
// 按商户分组
|
// 按商户分组
|
||||||
const groupedByMerchant = computed(() => {
|
const groupedByMerchant = computed(() => {
|
||||||
const groups: Record<string, {
|
const groups: Record<string, {
|
||||||
@@ -72,6 +82,39 @@ const groupedByMerchant = computed(() => {
|
|||||||
return Object.values(groups)
|
return Object.values(groups)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 计算每个商户选中的订单数量
|
||||||
|
const selectedCount = computed(() => {
|
||||||
|
return (merchantId: string) => selectedOrders.value[merchantId]?.length || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算每个商户选中的订单总金额
|
||||||
|
const selectedAmount = computed(() => {
|
||||||
|
return (merchantId: string) => {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return 0
|
||||||
|
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
return group.settlements
|
||||||
|
.filter(item => selectedIds.includes(item.id))
|
||||||
|
.reduce((sum, item) => sum + item.amount, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算每个商户是否全选
|
||||||
|
const isAllSelected = computed(() => {
|
||||||
|
return (merchantId: string) => {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return false
|
||||||
|
|
||||||
|
const selectableOrders = group.settlements.filter(
|
||||||
|
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
|
||||||
|
)
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
|
||||||
|
return selectableOrders.length > 0 && selectableOrders.every(item => selectedIds.includes(item.id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 页面显示
|
// 页面显示
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
loadData()
|
loadData()
|
||||||
@@ -120,7 +163,97 @@ function handleOpenWriteOff(item: Settlement) {
|
|||||||
writeOffVisible.value = true
|
writeOffVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开商户批量消账弹窗
|
// 进入选择模式
|
||||||
|
function enterSelectionMode(merchantId: string) {
|
||||||
|
selectionMode.value[merchantId] = true
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出选择模式
|
||||||
|
function exitSelectionMode(merchantId: string) {
|
||||||
|
selectionMode.value[merchantId] = false
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理订单点击
|
||||||
|
async function handleOrderClick(merchantId: string, orderId: string, status: SettlementStatus) {
|
||||||
|
// 已结订单点击显示消账记录
|
||||||
|
if (status === SettlementStatus.SETTLED) {
|
||||||
|
await showWriteOffRecords(orderId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有在选择模式下且订单状态为未结或逾期时才能选择
|
||||||
|
if (!selectionMode.value[merchantId]) return
|
||||||
|
if (status !== SettlementStatus.UNSETTLED && status !== SettlementStatus.OVERDUE) return
|
||||||
|
|
||||||
|
const selected = selectedOrders.value[merchantId] || []
|
||||||
|
const index = selected.indexOf(orderId)
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
// 取消选择
|
||||||
|
selected.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
// 添加选择
|
||||||
|
selected.push(orderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedOrders.value[merchantId] = selected
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示消账记录
|
||||||
|
async function showWriteOffRecords(settlementId: string) {
|
||||||
|
try {
|
||||||
|
uni.showLoading({ title: '加载中...' })
|
||||||
|
await financeStore.fetchWriteOffList(settlementId)
|
||||||
|
currentWriteOffRecords.value = financeStore.writeOffList
|
||||||
|
writeOffRecordVisible.value = true
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({ title: '获取消账记录失败', icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全选/取消全选
|
||||||
|
function toggleSelectAll(merchantId: string) {
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return
|
||||||
|
|
||||||
|
const selectableOrders = group.settlements.filter(
|
||||||
|
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isAllSelected.value(merchantId)) {
|
||||||
|
// 取消全选
|
||||||
|
selectedOrders.value[merchantId] = []
|
||||||
|
} else {
|
||||||
|
// 全选
|
||||||
|
selectedOrders.value[merchantId] = selectableOrders.map(item => item.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量消账
|
||||||
|
function handleBatchWriteOff(merchantId: string) {
|
||||||
|
const selectedIds = selectedOrders.value[merchantId] || []
|
||||||
|
if (selectedIds.length === 0) return
|
||||||
|
|
||||||
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
|
if (!group) return
|
||||||
|
|
||||||
|
// 获取选中的订单
|
||||||
|
const selectedSettlements = group.settlements.filter(item => selectedIds.includes(item.id))
|
||||||
|
|
||||||
|
currentSettlement.value = null
|
||||||
|
currentMerchantSettlements.value = selectedSettlements
|
||||||
|
isBatchMode.value = true
|
||||||
|
writeOffVisible.value = true
|
||||||
|
|
||||||
|
// 退出选择模式
|
||||||
|
exitSelectionMode(merchantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开商户批量消账弹窗(保留原有功能作为备用)
|
||||||
function handleOpenBatchWriteOff(merchantId: string) {
|
function handleOpenBatchWriteOff(merchantId: string) {
|
||||||
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
|
||||||
if (!group)
|
if (!group)
|
||||||
@@ -249,7 +382,7 @@ function getDaysUntilDue(dateStr: string) {
|
|||||||
<text class="i-carbon-warning-filled alert-icon" />
|
<text class="i-carbon-warning-filled alert-icon" />
|
||||||
<text class="alert-title">近期到期提醒</text>
|
<text class="alert-title">近期到期提醒</text>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-x class="due-list" show-scrollbar="false">
|
<scroll-view scroll-x class="due-list" :show-scrollbar="false">
|
||||||
<view
|
<view
|
||||||
v-for="item in financeStore.dueOrders"
|
v-for="item in financeStore.dueOrders"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@@ -305,51 +438,86 @@ function getDaysUntilDue(dateStr: string) {
|
|||||||
|
|
||||||
<!-- 头部批量操作 -->
|
<!-- 头部批量操作 -->
|
||||||
<view v-if="currentTab === 0" class="header-action">
|
<view v-if="currentTab === 0" class="header-action">
|
||||||
<view class="batch-btn-small" @click.stop="handleOpenBatchWriteOff(group.merchantId)">
|
<!-- 非选择模式 -->
|
||||||
|
<view v-if="!selectionMode[group.merchantId]" class="batch-btn-small" @click.stop="enterSelectionMode(group.merchantId)">
|
||||||
<text class="i-carbon-checkmark-outline" />
|
<text class="i-carbon-checkmark-outline" />
|
||||||
<text>批量消账</text>
|
<text>消账</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 选择模式 -->
|
||||||
|
<template v-else>
|
||||||
|
<view class="selection-controls">
|
||||||
|
<view class="select-all-btn" @click.stop="toggleSelectAll(group.merchantId)">
|
||||||
|
<text class="i-carbon-checkmark-filled" v-if="isAllSelected(group.merchantId)" />
|
||||||
|
<text class="i-carbon-checkmark-outline" v-else />
|
||||||
|
<text>全选</text>
|
||||||
|
</view>
|
||||||
|
<view class="selected-info">
|
||||||
|
<text>已选 {{ selectedCount(group.merchantId) }} 单</text>
|
||||||
|
<text class="amount">¥{{ selectedAmount(group.merchantId).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="selectedCount(group.merchantId) > 0"
|
||||||
|
class="batch-btn-small active"
|
||||||
|
@click.stop="handleBatchWriteOff(group.merchantId)"
|
||||||
|
>
|
||||||
|
<text class="i-carbon-checkmark-outline" />
|
||||||
|
<text>进行消账({{ selectedCount(group.merchantId) }})</text>
|
||||||
|
</view>
|
||||||
|
<view v-else class="cancel-btn" @click.stop="exitSelectionMode(group.merchantId)">
|
||||||
|
<text>取消</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单列表 -->
|
<!-- 订单列表 -->
|
||||||
<view class="order-list">
|
<view class="order-list">
|
||||||
<view v-for="item in group.settlements" :key="item.id" class="order-item">
|
<view
|
||||||
<!-- 订单头部 -->
|
v-for="item in group.settlements"
|
||||||
<view class="order-header">
|
:key="item.id"
|
||||||
<view class="order-no-wrapper">
|
class="order-item"
|
||||||
<text class="order-label">订单号</text>
|
:class="{
|
||||||
<text class="order-no">{{ item.orderNo }}</text>
|
'selection-mode': selectionMode[group.merchantId],
|
||||||
</view>
|
'selected': selectedOrders[group.merchantId]?.includes(item.id),
|
||||||
<view class="status-badge" :class="getStatusClass(item.status)">
|
'disabled': item.status !== SettlementStatus.UNSETTLED && item.status !== SettlementStatus.OVERDUE
|
||||||
{{ getStatusText(item.status) }}
|
}"
|
||||||
</view>
|
@click="handleOrderClick(group.merchantId, item.id, item.status)"
|
||||||
|
>
|
||||||
|
<!-- 选择框 -->
|
||||||
|
<view v-if="selectionMode[group.merchantId]" class="checkbox">
|
||||||
|
<text class="i-carbon-checkmark-filled" v-if="selectedOrders[group.merchantId]?.includes(item.id)" />
|
||||||
|
<text class="i-carbon-checkmark-outline" v-else />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单内容 -->
|
<!-- 订单内容 -->
|
||||||
<view class="order-body">
|
<view class="order-content">
|
||||||
<view class="info-row">
|
<!-- 订单头部 -->
|
||||||
<text class="info-label">到期日期</text>
|
<view class="order-header">
|
||||||
<text class="info-value">{{ item.dueDate }}</text>
|
<view class="order-no-wrapper">
|
||||||
|
<text class="order-label">订单号</text>
|
||||||
|
<text class="order-no">{{ item.orderNo }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="status-badge" :class="getStatusClass(item.status)">
|
||||||
|
{{ getStatusText(item.status) }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.settlementDate" class="info-row">
|
|
||||||
<text class="info-label">结算日期</text>
|
|
||||||
<text class="info-value">{{ item.settlementDate }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="info-label">应结金额</text>
|
|
||||||
<text class="info-value amount">¥{{ item.amount.toFixed(2) }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 订单操作 -->
|
<!-- 订单内容 -->
|
||||||
<view
|
<view class="order-body">
|
||||||
v-if="item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE"
|
<view class="info-row">
|
||||||
class="order-footer"
|
<text class="info-label">到期日期</text>
|
||||||
>
|
<text class="info-value">{{ item.dueDate }}</text>
|
||||||
<view class="action-btn" @click="handleOpenWriteOff(item)">
|
</view>
|
||||||
<text class="i-carbon-document-tasks" />
|
<view v-if="item.settlementDate" class="info-row">
|
||||||
<text>申请消账</text>
|
<text class="info-label">结算日期</text>
|
||||||
|
<text class="info-value">{{ item.settlementDate }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">应结金额</text>
|
||||||
|
<text class="info-value amount">¥{{ item.amount.toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -367,6 +535,12 @@ function getDaysUntilDue(dateStr: string) {
|
|||||||
:default-amount="currentWriteOffAmount"
|
:default-amount="currentWriteOffAmount"
|
||||||
@submit="handleSubmitWriteOff"
|
@submit="handleSubmitWriteOff"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 消账记录弹窗 -->
|
||||||
|
<WriteOffRecord
|
||||||
|
v-model:visible="writeOffRecordVisible"
|
||||||
|
:write-off-records="currentWriteOffRecords"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -610,6 +784,54 @@ $bg-card: #ffffff;
|
|||||||
.i-carbon-checkmark-outline {
|
.i-carbon-checkmark-outline {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: $primary;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-controls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
.select-all-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
background: rgba($primary, 0.05);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
color: $primary;
|
||||||
|
font-size: 22rpx;
|
||||||
|
|
||||||
|
.i-carbon-checkmark-outline,
|
||||||
|
.i-carbon-checkmark-filled {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-info {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 22rpx;
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
background: rgba($text-2, 0.1);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
color: $text-2;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -627,6 +849,49 @@ $bg-card: #ffffff;
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.selection-mode {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #ddd;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.i-carbon-checkmark-outline {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.i-carbon-checkmark-filled {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected .checkbox {
|
||||||
|
background: rgba($primary, 0.1);
|
||||||
|
border-color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 订单头部
|
// 订单头部
|
||||||
.order-header {
|
.order-header {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useCartStore } from '@/store/cart'
|
|||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
navigationBarTitleText: '首页',
|
navigationBarTitleText: '数字广东',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,10 @@ onUnload(() => {
|
|||||||
<view class="login-page">
|
<view class="login-page">
|
||||||
<view class="logo-wrapper">
|
<view class="logo-wrapper">
|
||||||
<view class="logo">
|
<view class="logo">
|
||||||
<text class="i-carbon-store icon"></text>
|
<image src="/static/logo4.png" class="logo-image"></image>
|
||||||
|
<!-- <text class="i-carbon-store icon"></text> -->
|
||||||
</view>
|
</view>
|
||||||
<text class="app-name">商城+金融</text>
|
<!-- <text class="app-name">商城+金融</text> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form">
|
<view class="form">
|
||||||
@@ -101,7 +102,7 @@ onUnload(() => {
|
|||||||
class="input"
|
class="input"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="请输入手机号"
|
placeholder="请输入手机号"
|
||||||
maxlength="11"
|
:maxlength="11"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ onUnload(() => {
|
|||||||
class="input"
|
class="input"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
maxlength="6"
|
:maxlength="6"
|
||||||
/>
|
/>
|
||||||
<view
|
<view
|
||||||
class="code-btn"
|
class="code-btn"
|
||||||
@@ -122,7 +123,7 @@ onUnload(() => {
|
|||||||
{{ countdown > 0 ? `${countdown}s后重发` : '获取验证码' }}
|
{{ countdown > 0 ? `${countdown}s后重发` : '获取验证码' }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<wd-notice-bar text="演示使用,直接点击登录~" prefix="warn-bold" custom-class="space" color="#34D19D" background-color="#f0f9eb" />
|
||||||
<view class="submit-btn" @click="handleLogin">
|
<view class="submit-btn" @click="handleLogin">
|
||||||
<text v-if="!loading">登录</text>
|
<text v-if="!loading">登录</text>
|
||||||
<text v-else>登录中...</text>
|
<text v-else>登录中...</text>
|
||||||
@@ -130,12 +131,18 @@ onUnload(() => {
|
|||||||
|
|
||||||
<view class="tips">
|
<view class="tips">
|
||||||
<text>未注册手机号验证后自动创建账号</text>
|
<text>未注册手机号验证后自动创建账号</text>
|
||||||
|
<p> </p>
|
||||||
|
<p><text>©2025 数字广东网络建设有限公司</text></p>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.logo-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 78px;
|
||||||
|
}
|
||||||
.login-page {
|
.login-page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -148,19 +155,19 @@ onUnload(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 100rpx;
|
margin-top: 120rpx;
|
||||||
margin-bottom: 100rpx;
|
margin-bottom: 116rpx;
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 160rpx;
|
width: 320rpx;
|
||||||
height: 160rpx;
|
height: 116rpx;
|
||||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff4d4f 100%);
|
// background: linear-gradient(135deg, #ff6b6b 0%, #ff4d4f 100%);
|
||||||
border-radius: 40rpx;
|
border-radius: 40rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
box-shadow: 0 8rpx 24rpx rgba(255, 77, 79, 0.3);
|
// box-shadow: 0 8rpx 24rpx rgba(255, 77, 79, 0.3);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 80rpx;
|
font-size: 80rpx;
|
||||||
@@ -199,7 +206,7 @@ onUnload(() => {
|
|||||||
|
|
||||||
.code-btn {
|
.code-btn {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #ff4d4f;
|
color: #008ef7;
|
||||||
padding-left: 20rpx;
|
padding-left: 20rpx;
|
||||||
border-left: 1rpx solid #ddd;
|
border-left: 1rpx solid #ddd;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@@ -212,7 +219,7 @@ onUnload(() => {
|
|||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff4d4f 100%);
|
background: linear-gradient(135deg, #0060ef 0%, #0060ef 100%);
|
||||||
border-radius: 50rpx;
|
border-radius: 50rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
654
src/pages/me/loan-application-records.vue
Normal file
654
src/pages/me/loan-application-records.vue
Normal file
@@ -0,0 +1,654 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { mockLoanApplicationRecords } from '@/mock/loan-application'
|
||||||
|
import type { LoanApplicationRecord, LoanApplicationStatus } from '@/typings/mall'
|
||||||
|
|
||||||
|
definePage({
|
||||||
|
style: {
|
||||||
|
navigationBarTitleText: '助贷申请记录',
|
||||||
|
navigationBarBackgroundColor: '#fff',
|
||||||
|
navigationBarTextStyle: 'black',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 申请记录列表
|
||||||
|
const applicationRecords = ref<LoanApplicationRecord[]>(mockLoanApplicationRecords)
|
||||||
|
|
||||||
|
// 详情弹窗相关
|
||||||
|
const showDetailModal = ref(false)
|
||||||
|
const selectedRecord = ref<LoanApplicationRecord | null>(null)
|
||||||
|
|
||||||
|
// 打开详情弹窗
|
||||||
|
function openDetailModal(record: LoanApplicationRecord) {
|
||||||
|
selectedRecord.value = record
|
||||||
|
showDetailModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情弹窗
|
||||||
|
function closeDetailModal() {
|
||||||
|
showDetailModal.value = false
|
||||||
|
selectedRecord.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理按钮点击
|
||||||
|
function handleActionClick(record: LoanApplicationRecord, action: any) {
|
||||||
|
if (action.code === 'VIEW_DETAIL' || action.code === 'VIEW_RESULT' || action.code === 'CONTINUE_FILL') {
|
||||||
|
openDetailModal(record)
|
||||||
|
} else if (action.code === 'DOWNLOAD_CONTRACT') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '合同下载中...',
|
||||||
|
icon: 'loading'
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '合同已下载',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
} else if (action.code === 'CANCEL') {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要取消该申请吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '申请已取消',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (action.code === 'DELETE') {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要删除该申请记录吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '记录已删除',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态颜色
|
||||||
|
function getStatusColor(status: LoanApplicationStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case 'PROCESSING':
|
||||||
|
return {
|
||||||
|
bg: 'rgba(59, 130, 246, 0.1)',
|
||||||
|
text: '#3B82F6'
|
||||||
|
}
|
||||||
|
case 'COMPLETED':
|
||||||
|
return {
|
||||||
|
bg: 'rgba(16, 185, 129, 0.1)',
|
||||||
|
text: '#10B981'
|
||||||
|
}
|
||||||
|
case 'PENDING':
|
||||||
|
return {
|
||||||
|
bg: 'rgba(245, 158, 11, 0.1)',
|
||||||
|
text: '#F59E0B'
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
bg: '#f5f5f5',
|
||||||
|
text: '#999'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取按钮样式
|
||||||
|
function getButtonStyle(style: string) {
|
||||||
|
switch (style) {
|
||||||
|
case 'primary-blue':
|
||||||
|
return 'background: #3B82F6; color: #fff;'
|
||||||
|
case 'primary-green':
|
||||||
|
return 'background: #10B981; color: #fff;'
|
||||||
|
case 'primary-yellow':
|
||||||
|
return 'background: #F59E0B; color: #fff;'
|
||||||
|
case 'text-link':
|
||||||
|
return 'background: transparent; color: #666; border: none;'
|
||||||
|
default:
|
||||||
|
return 'background: #f5f5f5; color: #333;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="loan-application-records-page">
|
||||||
|
<!-- 申请记录列表 -->
|
||||||
|
<view class="records-list">
|
||||||
|
<view
|
||||||
|
v-for="record in applicationRecords"
|
||||||
|
:key="record.applicationId"
|
||||||
|
class="record-card"
|
||||||
|
>
|
||||||
|
<!-- 头部区域 -->
|
||||||
|
<view class="card-header">
|
||||||
|
<view class="title-section">
|
||||||
|
<text class="title">{{ record.loanTitle }}</text>
|
||||||
|
<view
|
||||||
|
class="status-badge"
|
||||||
|
:style="{
|
||||||
|
background: getStatusColor(record.status).bg,
|
||||||
|
color: getStatusColor(record.status).text
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ record.statusText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="info-section">
|
||||||
|
<text class="application-id">申请编号:{{ record.applicationId }}</text>
|
||||||
|
<text class="date-info">{{ record.dateLabel }}:{{ record.dateValue }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 进度条区域 -->
|
||||||
|
<view v-if="record.progress && record.progress.show" class="progress-section">
|
||||||
|
<view class="steps-container">
|
||||||
|
<view
|
||||||
|
v-for="(step, index) in record.progress.steps"
|
||||||
|
:key="index"
|
||||||
|
class="step-item"
|
||||||
|
:class="{
|
||||||
|
'completed': index < record.progress.currentStepIndex,
|
||||||
|
'current': index === record.progress.currentStepIndex,
|
||||||
|
'future': index > record.progress.currentStepIndex
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<view class="step-circle">
|
||||||
|
<text v-if="index < record.progress.currentStepIndex" class="i-carbon-checkmark"></text>
|
||||||
|
<text v-else>{{ index + 1 }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="step-text">{{ step }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提示信息区域 -->
|
||||||
|
<view v-if="record.alertInfo && record.alertInfo.show" class="alert-section">
|
||||||
|
<view
|
||||||
|
class="alert-box"
|
||||||
|
:class="record.alertInfo.type"
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
v-if="record.alertInfo.type === 'info'"
|
||||||
|
class="i-carbon-information alert-icon"
|
||||||
|
></text>
|
||||||
|
<text
|
||||||
|
v-else-if="record.alertInfo.type === 'warning'"
|
||||||
|
class="i-carbon-warning-alt alert-icon"
|
||||||
|
></text>
|
||||||
|
<text class="alert-content">{{ record.alertInfo.content }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作区 -->
|
||||||
|
<view class="actions-section">
|
||||||
|
<view
|
||||||
|
v-for="action in record.actions"
|
||||||
|
:key="action.code"
|
||||||
|
class="action-btn"
|
||||||
|
:style="getButtonStyle(action.style)"
|
||||||
|
@click="handleActionClick(record, action)"
|
||||||
|
>
|
||||||
|
{{ action.text }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<view v-if="showDetailModal" class="modal-overlay" @click="closeDetailModal">
|
||||||
|
<view class="modal-content" @click.stop>
|
||||||
|
<view class="modal-header">
|
||||||
|
<text class="modal-title">申请详情</text>
|
||||||
|
<text class="i-carbon-close modal-close" @click="closeDetailModal"></text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="selectedRecord" class="modal-body">
|
||||||
|
<view class="detail-item">
|
||||||
|
<text class="detail-label">申请编号</text>
|
||||||
|
<text class="detail-value">{{ selectedRecord.applicationId }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-item">
|
||||||
|
<text class="detail-label">贷款类型</text>
|
||||||
|
<text class="detail-value">{{ selectedRecord.loanTitle }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-item">
|
||||||
|
<text class="detail-label">申请状态</text>
|
||||||
|
<text
|
||||||
|
class="detail-value status-text"
|
||||||
|
:style="{ color: getStatusColor(selectedRecord.status).text }"
|
||||||
|
>
|
||||||
|
{{ selectedRecord.statusText }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-item">
|
||||||
|
<text class="detail-label">{{ selectedRecord.dateLabel }}</text>
|
||||||
|
<text class="detail-value">{{ selectedRecord.dateValue }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 进度详情 -->
|
||||||
|
<view v-if="selectedRecord.progress && selectedRecord.progress.show" class="progress-detail">
|
||||||
|
<text class="detail-label">申请进度</text>
|
||||||
|
<view class="progress-steps">
|
||||||
|
<view
|
||||||
|
v-for="(step, index) in selectedRecord.progress.steps"
|
||||||
|
:key="index"
|
||||||
|
class="progress-step"
|
||||||
|
:class="{
|
||||||
|
'completed': index < selectedRecord.progress.currentStepIndex,
|
||||||
|
'current': index === selectedRecord.progress.currentStepIndex,
|
||||||
|
'future': index > selectedRecord.progress.currentStepIndex
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<view class="step-dot"></view>
|
||||||
|
<text class="step-name">{{ step }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提示信息 -->
|
||||||
|
<view v-if="selectedRecord.alertInfo && selectedRecord.alertInfo.show" class="alert-detail">
|
||||||
|
<view
|
||||||
|
class="alert-detail-box"
|
||||||
|
:class="selectedRecord.alertInfo.type"
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
v-if="selectedRecord.alertInfo.type === 'info'"
|
||||||
|
class="i-carbon-information alert-icon"
|
||||||
|
></text>
|
||||||
|
<text
|
||||||
|
v-else-if="selectedRecord.alertInfo.type === 'warning'"
|
||||||
|
class="i-carbon-warning-alt alert-icon"
|
||||||
|
></text>
|
||||||
|
<text>{{ selectedRecord.alertInfo.content }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loan-application-records-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.records-list {
|
||||||
|
.record-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.title-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
|
||||||
|
.application-id, .date-info {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-section {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.steps-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 30rpx;
|
||||||
|
left: 50rpx;
|
||||||
|
right: 50rpx;
|
||||||
|
height: 2rpx;
|
||||||
|
background: #e5e5e5;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.step-circle {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
.step-circle {
|
||||||
|
background: #10B981;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.step-text {
|
||||||
|
color: #10B981;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current {
|
||||||
|
.step-circle {
|
||||||
|
background: #3B82F6;
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
.step-text {
|
||||||
|
color: #3B82F6;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.future {
|
||||||
|
.step-circle {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.step-text {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-section {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.alert-box {
|
||||||
|
padding: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-top: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1rpx solid #e5e5e5;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗样式
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
padding: 40rpx;
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600rpx;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-close {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 30rpx;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.status-text {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-detail {
|
||||||
|
padding: 20rpx 0;
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-steps {
|
||||||
|
.progress-step {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-dot {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
.step-dot {
|
||||||
|
background: #10B981;
|
||||||
|
}
|
||||||
|
.step-name {
|
||||||
|
color: #10B981;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current {
|
||||||
|
.step-dot {
|
||||||
|
background: #3B82F6;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
.step-name {
|
||||||
|
color: #3B82F6;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.future {
|
||||||
|
.step-dot {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 2rpx solid #e5e5e5;
|
||||||
|
}
|
||||||
|
.step-name {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-detail {
|
||||||
|
padding: 20rpx 0;
|
||||||
|
|
||||||
|
.alert-detail-box {
|
||||||
|
padding: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: #3B82F6;
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
color: #F59E0B;
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-top: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
942
src/pages/me/loan-application.vue
Normal file
942
src/pages/me/loan-application.vue
Normal file
@@ -0,0 +1,942 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
definePage({
|
||||||
|
style: {
|
||||||
|
navigationBarTitleText: '贷款申请',
|
||||||
|
navigationBarBackgroundColor: '#fff',
|
||||||
|
navigationBarTextStyle: 'black',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
// 个人信息
|
||||||
|
personalInfo: {
|
||||||
|
name: '',
|
||||||
|
phone: '',
|
||||||
|
idCard: '',
|
||||||
|
region: ['', '', ''], // 省、市、区
|
||||||
|
detailAddress: '',
|
||||||
|
},
|
||||||
|
// 经营信息
|
||||||
|
businessInfo: {
|
||||||
|
businessProject: '',
|
||||||
|
businessTime: '',
|
||||||
|
annualIncome: '',
|
||||||
|
hasDebt: 'no', // 'no' 或 'yes'
|
||||||
|
debtAmount: '',
|
||||||
|
loanDemand: '',
|
||||||
|
assets: [] as string[], // 家庭主要资产
|
||||||
|
},
|
||||||
|
// 证件信息
|
||||||
|
documentInfo: {
|
||||||
|
businessLicense: '', // 营业执照或租赁合同
|
||||||
|
otherMaterials: [] as string[], // 其他辅助材料
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证错误
|
||||||
|
const formErrors = ref({
|
||||||
|
personalInfo: {
|
||||||
|
name: '',
|
||||||
|
phone: '',
|
||||||
|
idCard: '',
|
||||||
|
region: '',
|
||||||
|
detailAddress: '',
|
||||||
|
},
|
||||||
|
businessInfo: {
|
||||||
|
businessProject: '',
|
||||||
|
businessTime: '',
|
||||||
|
annualIncome: '',
|
||||||
|
debtAmount: '',
|
||||||
|
loanDemand: '',
|
||||||
|
assets: '',
|
||||||
|
},
|
||||||
|
documentInfo: {
|
||||||
|
businessLicense: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 省市区选项(模拟数据)
|
||||||
|
const regionOptions = ref([
|
||||||
|
{
|
||||||
|
value: '110000',
|
||||||
|
label: '北京市',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: '110100',
|
||||||
|
label: '北京市',
|
||||||
|
children: [
|
||||||
|
{ value: '110101', label: '东城区' },
|
||||||
|
{ value: '110102', label: '西城区' },
|
||||||
|
{ value: '110105', label: '朝阳区' },
|
||||||
|
{ value: '110106', label: '丰台区' },
|
||||||
|
{ value: '110107', label: '石景山区' },
|
||||||
|
{ value: '110108', label: '海淀区' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '310000',
|
||||||
|
label: '上海市',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: '310100',
|
||||||
|
label: '上海市',
|
||||||
|
children: [
|
||||||
|
{ value: '310101', label: '黄浦区' },
|
||||||
|
{ value: '310104', label: '徐汇区' },
|
||||||
|
{ value: '310105', label: '长宁区' },
|
||||||
|
{ value: '310106', label: '静安区' },
|
||||||
|
{ value: '310107', label: '普陀区' },
|
||||||
|
{ value: '310109', label: '虹口区' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '440000',
|
||||||
|
label: '广东省',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: '440100',
|
||||||
|
label: '广州市',
|
||||||
|
children: [
|
||||||
|
{ value: '440103', label: '荔湾区' },
|
||||||
|
{ value: '440104', label: '越秀区' },
|
||||||
|
{ value: '440105', label: '海珠区' },
|
||||||
|
{ value: '440106', label: '天河区' },
|
||||||
|
{ value: '440111', label: '白云区' },
|
||||||
|
{ value: '440112', label: '黄埔区' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '440300',
|
||||||
|
label: '深圳市',
|
||||||
|
children: [
|
||||||
|
{ value: '440303', label: '罗湖区' },
|
||||||
|
{ value: '440304', label: '福田区' },
|
||||||
|
{ value: '440305', label: '南山区' },
|
||||||
|
{ value: '440306', label: '宝安区' },
|
||||||
|
{ value: '440307', label: '龙岗区' },
|
||||||
|
{ value: '440308', label: '龙华区' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 家庭主要资产选项
|
||||||
|
const assetOptions = [
|
||||||
|
{ value: 'house', label: '商品房' },
|
||||||
|
{ value: 'selfBuiltState', label: '自建房 (国有)' },
|
||||||
|
{ value: 'selfBuiltCollective', label: '自建房 (集体)' },
|
||||||
|
{ value: 'shop', label: '商铺' },
|
||||||
|
{ value: 'land', label: '土地' },
|
||||||
|
{ value: 'car', label: '车辆' },
|
||||||
|
{ value: 'other', label: '其他' },
|
||||||
|
{ value: 'none', label: '无' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 选择省市区
|
||||||
|
function handleRegionPicker() {
|
||||||
|
// 简化的省市区选择器,使用 uni.showActionSheet
|
||||||
|
const provinces = regionOptions.value.map(item => item.label)
|
||||||
|
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: provinces,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.tapIndex !== undefined) {
|
||||||
|
const selectedProvince = regionOptions.value[res.tapIndex]
|
||||||
|
formData.value.personalInfo.region[0] = selectedProvince.value
|
||||||
|
|
||||||
|
// 选择市
|
||||||
|
const cities = selectedProvince.children.map((item: any) => item.label)
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: cities,
|
||||||
|
success: (cityRes) => {
|
||||||
|
if (cityRes.tapIndex !== undefined) {
|
||||||
|
const selectedCity = selectedProvince.children[cityRes.tapIndex]
|
||||||
|
formData.value.personalInfo.region[1] = selectedCity.value
|
||||||
|
|
||||||
|
// 选择区
|
||||||
|
const districts = selectedCity.children.map((item: any) => item.label)
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: districts,
|
||||||
|
success: (districtRes) => {
|
||||||
|
if (districtRes.tapIndex !== undefined) {
|
||||||
|
const selectedDistrict = selectedCity.children[districtRes.tapIndex]
|
||||||
|
formData.value.personalInfo.region[2] = selectedDistrict.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理资产选择
|
||||||
|
function handleAssetChange(value: string) {
|
||||||
|
const { assets } = formData.value.businessInfo
|
||||||
|
|
||||||
|
// 如果选择"无",则取消其他所有选项
|
||||||
|
if (value === 'none') {
|
||||||
|
formData.value.businessInfo.assets = ['none']
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果选择了其他选项,则取消"无"选项
|
||||||
|
if (assets.includes('none')) {
|
||||||
|
const index = assets.indexOf('none')
|
||||||
|
assets.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换选项
|
||||||
|
const index = assets.indexOf(value)
|
||||||
|
if (index > -1) {
|
||||||
|
assets.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
assets.push(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传营业执照
|
||||||
|
function handleUploadBusinessLicense() {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: (res) => {
|
||||||
|
formData.value.documentInfo.businessLicense = res.tempFilePaths[0]
|
||||||
|
formErrors.value.documentInfo.businessLicense = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传其他材料
|
||||||
|
function handleUploadOtherMaterials() {
|
||||||
|
const currentCount = formData.value.documentInfo.otherMaterials.length
|
||||||
|
const maxCount = 5
|
||||||
|
|
||||||
|
if (currentCount >= maxCount) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `最多上传${maxCount}张图片`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.chooseImage({
|
||||||
|
count: maxCount - currentCount,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: (res) => {
|
||||||
|
if (Array.isArray(res.tempFilePaths)) {
|
||||||
|
formData.value.documentInfo.otherMaterials.push(...res.tempFilePaths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除其他材料
|
||||||
|
function handleRemoveOtherMaterial(index: number) {
|
||||||
|
formData.value.documentInfo.otherMaterials.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
function handlePreviewImage(url: string) {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [url]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
function validateForm() {
|
||||||
|
let isValid = true
|
||||||
|
const errors = formErrors.value
|
||||||
|
|
||||||
|
// 重置错误信息
|
||||||
|
Object.keys(errors.personalInfo).forEach(key => {
|
||||||
|
errors.personalInfo[key as keyof typeof errors.personalInfo] = ''
|
||||||
|
})
|
||||||
|
Object.keys(errors.businessInfo).forEach(key => {
|
||||||
|
errors.businessInfo[key as keyof typeof errors.businessInfo] = ''
|
||||||
|
})
|
||||||
|
errors.documentInfo.businessLicense = ''
|
||||||
|
|
||||||
|
// 验证个人信息
|
||||||
|
if (!formData.value.personalInfo.name.trim()) {
|
||||||
|
errors.personalInfo.name = '请输入姓名'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.personalInfo.phone.trim()) {
|
||||||
|
errors.personalInfo.phone = '请输入联系方式'
|
||||||
|
isValid = false
|
||||||
|
} else if (!/^1[3-9]\d{9}$/.test(formData.value.personalInfo.phone)) {
|
||||||
|
errors.personalInfo.phone = '请输入正确的手机号'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.personalInfo.idCard.trim()) {
|
||||||
|
errors.personalInfo.idCard = '请输入身份证号码'
|
||||||
|
isValid = false
|
||||||
|
} else if (!/^\d{17}[\dXx]$/.test(formData.value.personalInfo.idCard)) {
|
||||||
|
errors.personalInfo.idCard = '请输入正确的身份证号码'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.personalInfo.region.some(item => !item)) {
|
||||||
|
errors.personalInfo.region = '请选择省市区'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.personalInfo.detailAddress.trim()) {
|
||||||
|
errors.personalInfo.detailAddress = '请输入详细地址'
|
||||||
|
isValid = false
|
||||||
|
} else if (formData.value.personalInfo.detailAddress.trim().length < 5 || formData.value.personalInfo.detailAddress.trim().length > 100) {
|
||||||
|
errors.personalInfo.detailAddress = '详细地址长度应在5-100字之间'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证经营信息
|
||||||
|
if (!formData.value.businessInfo.businessProject.trim()) {
|
||||||
|
errors.businessInfo.businessProject = '请输入经营项目'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.businessInfo.businessTime.trim()) {
|
||||||
|
errors.businessInfo.businessTime = '请输入经营时间'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.businessInfo.annualIncome.trim()) {
|
||||||
|
errors.businessInfo.annualIncome = '请输入最近1年经营收入'
|
||||||
|
isValid = false
|
||||||
|
} else if (isNaN(Number(formData.value.businessInfo.annualIncome)) || Number(formData.value.businessInfo.annualIncome) <= 0) {
|
||||||
|
errors.businessInfo.annualIncome = '请输入正确的金额'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.businessInfo.hasDebt === 'yes' && !formData.value.businessInfo.debtAmount.trim()) {
|
||||||
|
errors.businessInfo.debtAmount = '请输入负债金额'
|
||||||
|
isValid = false
|
||||||
|
} else if (formData.value.businessInfo.hasDebt === 'yes' && (isNaN(Number(formData.value.businessInfo.debtAmount)) || Number(formData.value.businessInfo.debtAmount) <= 0)) {
|
||||||
|
errors.businessInfo.debtAmount = '请输入正确的金额'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.businessInfo.loanDemand.trim()) {
|
||||||
|
errors.businessInfo.loanDemand = '请输入贷款需求'
|
||||||
|
isValid = false
|
||||||
|
} else if (isNaN(Number(formData.value.businessInfo.loanDemand)) || Number(formData.value.businessInfo.loanDemand) <= 0) {
|
||||||
|
errors.businessInfo.loanDemand = '请输入正确的金额'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.businessInfo.assets.length === 0) {
|
||||||
|
errors.businessInfo.assets = '请至少选择一项家庭主要资产'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证证件信息
|
||||||
|
if (!formData.value.documentInfo.businessLicense) {
|
||||||
|
errors.documentInfo.businessLicense = '请上传营业执照或租赁合同'
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
function handleSubmit() {
|
||||||
|
// 取消表单校验,直接提交
|
||||||
|
|
||||||
|
uni.showLoading({
|
||||||
|
title: '提交中...'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 模拟提交
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '提交成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 延迟返回
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
function handleBack() {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="loan-application-page">
|
||||||
|
<!-- 个人信息模块 -->
|
||||||
|
<view class="form-card">
|
||||||
|
<view class="card-title">
|
||||||
|
<view class="title-bar"></view>
|
||||||
|
<text class="title-text">个人信息</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-content">
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">姓名</view>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.personalInfo.name"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
:maxlength="20"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.personalInfo.name" class="error-text">{{ formErrors.personalInfo.name }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">联系方式</view>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.personalInfo.phone"
|
||||||
|
placeholder="请输入联系方式"
|
||||||
|
type="number"
|
||||||
|
:maxlength="11"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.personalInfo.phone" class="error-text">{{ formErrors.personalInfo.phone }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">身份证号码</view>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.personalInfo.idCard"
|
||||||
|
placeholder="请输入身份证号码"
|
||||||
|
:maxlength="18"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.personalInfo.idCard" class="error-text">{{ formErrors.personalInfo.idCard }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item" @click="handleRegionPicker">
|
||||||
|
<view class="label">经营地址</view>
|
||||||
|
<view class="picker-input">
|
||||||
|
<text v-if="formData.personalInfo.region.some(item => item)" class="picker-text">
|
||||||
|
{{ regionOptions.find(p => p.value === formData.personalInfo.region[0])?.label }}
|
||||||
|
{{ regionOptions.find(p => p.value === formData.personalInfo.region[0])?.children?.find(c => c.value === formData.personalInfo.region[1])?.label }}
|
||||||
|
{{ regionOptions.find(p => p.value === formData.personalInfo.region[0])?.children?.find(c => c.value === formData.personalInfo.region[1])?.children?.find(d => d.value === formData.personalInfo.region[2])?.label }}
|
||||||
|
</text>
|
||||||
|
<text v-else class="picker-placeholder">选择省,市,区</text>
|
||||||
|
<text class="i-carbon-chevron-right picker-arrow"></text>
|
||||||
|
</view>
|
||||||
|
<text v-if="formErrors.personalInfo.region" class="error-text">{{ formErrors.personalInfo.region }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">详细地址</view>
|
||||||
|
<textarea
|
||||||
|
class="textarea"
|
||||||
|
v-model="formData.personalInfo.detailAddress"
|
||||||
|
placeholder="请输入街道小区楼牌号等"
|
||||||
|
:maxlength="100"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.personalInfo.detailAddress" class="error-text">{{ formErrors.personalInfo.detailAddress }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 经营信息模块 -->
|
||||||
|
<view class="form-card">
|
||||||
|
<view class="card-title">
|
||||||
|
<view class="title-bar"></view>
|
||||||
|
<text class="title-text">经营信息</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-content">
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">经营项目</view>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.businessInfo.businessProject"
|
||||||
|
placeholder="请输入经营项目"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.businessInfo.businessProject" class="error-text">{{ formErrors.businessInfo.businessProject }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">经营时间</view>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.businessInfo.businessTime"
|
||||||
|
placeholder="请输入经营时间"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.businessInfo.businessTime" class="error-text">{{ formErrors.businessInfo.businessTime }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">最近1年经营收入</view>
|
||||||
|
<view class="input-with-suffix">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.businessInfo.annualIncome"
|
||||||
|
placeholder="请输入金额"
|
||||||
|
type="digit"
|
||||||
|
/>
|
||||||
|
<text class="suffix">万元</text>
|
||||||
|
</view>
|
||||||
|
<text v-if="formErrors.businessInfo.annualIncome" class="error-text">{{ formErrors.businessInfo.annualIncome }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">负债情况</view>
|
||||||
|
<view class="radio-group">
|
||||||
|
<view
|
||||||
|
class="radio-item"
|
||||||
|
:class="{ active: formData.businessInfo.hasDebt === 'no' }"
|
||||||
|
@click="formData.businessInfo.hasDebt = 'no'"
|
||||||
|
>
|
||||||
|
<text class="radio-dot"></text>
|
||||||
|
<text>无负债</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="radio-item"
|
||||||
|
:class="{ active: formData.businessInfo.hasDebt === 'yes' }"
|
||||||
|
@click="formData.businessInfo.hasDebt = 'yes'"
|
||||||
|
>
|
||||||
|
<text class="radio-dot"></text>
|
||||||
|
<text>有负债</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="formData.businessInfo.hasDebt === 'yes'" class="debt-amount">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.businessInfo.debtAmount"
|
||||||
|
placeholder="请输入负债金额"
|
||||||
|
type="digit"
|
||||||
|
/>
|
||||||
|
<text v-if="formErrors.businessInfo.debtAmount" class="error-text">{{ formErrors.businessInfo.debtAmount }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">贷款需求</view>
|
||||||
|
<view class="input-with-suffix">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="formData.businessInfo.loanDemand"
|
||||||
|
placeholder="请输入金额"
|
||||||
|
type="digit"
|
||||||
|
/>
|
||||||
|
<text class="suffix">万元</text>
|
||||||
|
</view>
|
||||||
|
<text v-if="formErrors.businessInfo.loanDemand" class="error-text">{{ formErrors.businessInfo.loanDemand }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">家庭主要资产</view>
|
||||||
|
<view class="checkbox-group">
|
||||||
|
<view
|
||||||
|
v-for="option in assetOptions"
|
||||||
|
:key="option.value"
|
||||||
|
class="checkbox-item"
|
||||||
|
:class="{ active: formData.businessInfo.assets.includes(option.value) }"
|
||||||
|
@click="handleAssetChange(option.value)"
|
||||||
|
>
|
||||||
|
<text class="checkbox-icon"></text>
|
||||||
|
<text>{{ option.label }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text v-if="formErrors.businessInfo.assets" class="error-text">{{ formErrors.businessInfo.assets }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 证件信息模块 -->
|
||||||
|
<view class="form-card">
|
||||||
|
<view class="card-title">
|
||||||
|
<view class="title-bar"></view>
|
||||||
|
<text class="title-text">证件信息</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-content">
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">营业执照(租赁合同)</view>
|
||||||
|
<view class="upload-area" @click="handleUploadBusinessLicense">
|
||||||
|
<image v-if="formData.documentInfo.businessLicense" :src="formData.documentInfo.businessLicense" class="uploaded-image" @click.stop="handlePreviewImage(formData.documentInfo.businessLicense)" />
|
||||||
|
<view v-else class="upload-placeholder">
|
||||||
|
<text class="i-carbon-camera upload-icon"></text>
|
||||||
|
<text class="upload-text">点击上传图片</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text v-if="formErrors.documentInfo.businessLicense" class="error-text">{{ formErrors.documentInfo.businessLicense }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="label">其他辅助材料</view>
|
||||||
|
<view class="upload-list">
|
||||||
|
<view
|
||||||
|
v-for="(image, index) in formData.documentInfo.otherMaterials"
|
||||||
|
:key="index"
|
||||||
|
class="upload-item"
|
||||||
|
>
|
||||||
|
<image :src="image" class="uploaded-image" @click="handlePreviewImage(image)" />
|
||||||
|
<view class="delete-btn" @click="handleRemoveOtherMaterial(index)">
|
||||||
|
<text class="i-carbon-close"></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-if="formData.documentInfo.otherMaterials.length < 5"
|
||||||
|
class="upload-area small"
|
||||||
|
@click="handleUploadOtherMaterials"
|
||||||
|
>
|
||||||
|
<text class="i-carbon-add upload-icon"></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部提交按钮 -->
|
||||||
|
<view class="submit-bar">
|
||||||
|
<button class="submit-btn" @click="handleSubmit">提交资料</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loan-application-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #F5F7FA;
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-card {
|
||||||
|
background: #FFFFFF;
|
||||||
|
margin: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
border-bottom: 1rpx solid #F0F0F0;
|
||||||
|
|
||||||
|
.title-bar {
|
||||||
|
width: 6rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
background: #FF6600;
|
||||||
|
border-radius: 3rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-content {
|
||||||
|
padding: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.picker-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-placeholder {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-arrow {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-suffix {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suffix {
|
||||||
|
height: 80rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
background: #F0F0F0;
|
||||||
|
border-top-right-radius: 8rpx;
|
||||||
|
border-bottom-right-radius: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 40rpx;
|
||||||
|
|
||||||
|
.radio-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.radio-dot {
|
||||||
|
width: 36rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #DDDDDD;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 18rpx;
|
||||||
|
height: 18rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #28C445;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.radio-dot {
|
||||||
|
border-color: #28C445;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.debt-amount {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.checkbox-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
.checkbox-icon {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
border: 2rpx solid #DDDDDD;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 16rpx;
|
||||||
|
height: 16rpx;
|
||||||
|
background: #28C445;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.checkbox-icon {
|
||||||
|
border-color: #28C445;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area {
|
||||||
|
width: 100%;
|
||||||
|
height: 200rpx;
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
|
||||||
|
.upload-icon {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploaded-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.upload-item {
|
||||||
|
position: relative;
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
|
||||||
|
.uploaded-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: -10rpx;
|
||||||
|
right: -10rpx;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.i-carbon-close {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
display: block;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D4F;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: #28C445;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -40,6 +40,13 @@ function navigateTo(url: string) {
|
|||||||
uni.navigateTo({ url })
|
uni.navigateTo({ url })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 跳转到贷款申请页面
|
||||||
|
function goToLoanApplication() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/me/loan-application'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
@@ -162,7 +169,7 @@ function handleLogout() {
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<view class="apply-btn">立即申请</view>
|
<view class="apply-btn" @click="goToLoanApplication">立即申请</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -189,7 +196,7 @@ function handleLogout() {
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<view class="apply-btn">立即申请</view>
|
<view class="apply-btn" @click="goToLoanApplication">立即申请</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -198,6 +205,11 @@ function handleLogout() {
|
|||||||
<!-- 常用功能 -->
|
<!-- 常用功能 -->
|
||||||
<view class="section-card">
|
<view class="section-card">
|
||||||
<view class="cell-group">
|
<view class="cell-group">
|
||||||
|
<view class="cell" @click="navigateTo('/pages/me/loan-application-records')">
|
||||||
|
<text class="i-carbon-document-attachment icon"></text>
|
||||||
|
<text class="label">助贷申请进度</text>
|
||||||
|
<text class="i-carbon-chevron-right arrow"></text>
|
||||||
|
</view>
|
||||||
<view class="cell" @click="navigateTo('/pages/member/index')">
|
<view class="cell" @click="navigateTo('/pages/member/index')">
|
||||||
<text class="i-carbon-user-favorite icon"></text>
|
<text class="i-carbon-user-favorite icon"></text>
|
||||||
<text class="label">会员中心</text>
|
<text class="label">会员中心</text>
|
||||||
|
|||||||
@@ -351,9 +351,12 @@ function handleCancel() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
|
padding-left: calc(24rpx + env(safe-area-inset-left));
|
||||||
|
padding-right: calc(24rpx + env(safe-area-inset-right));
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||||
gap: 20rpx;
|
gap: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
padding: 16rpx 40rpx;
|
padding: 16rpx 40rpx;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 263 KiB |
BIN
src/static/logo1.png
Normal file
BIN
src/static/logo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/static/logo2.png
Normal file
BIN
src/static/logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/static/logo3.png
Normal file
BIN
src/static/logo3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/static/logo4.png
Normal file
BIN
src/static/logo4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
@@ -7,7 +7,7 @@ export const useUserStore = defineStore('user', {
|
|||||||
userInfo: {
|
userInfo: {
|
||||||
id: 'user_001',
|
id: 'user_001',
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
nickname: '测试用户',
|
nickname: '王明阳',
|
||||||
avatar: '/static/images/avatar.jpg',
|
avatar: '/static/images/avatar.jpg',
|
||||||
phone: '13800138000',
|
phone: '13800138000',
|
||||||
creditLimits: [], // 实际应从 financeStore 获取或关联
|
creditLimits: [], // 实际应从 financeStore 获取或关联
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const nativeTabbarList: NativeTabBarItem[] = [
|
|||||||
iconPath: 'static/tabbar/home.png',
|
iconPath: 'static/tabbar/home.png',
|
||||||
selectedIconPath: 'static/tabbar/homeHL.png',
|
selectedIconPath: 'static/tabbar/homeHL.png',
|
||||||
pagePath: 'pages/index/index',
|
pagePath: 'pages/index/index',
|
||||||
text: '首页',
|
text: '数字广东',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
iconPath: 'static/tabbar/personal.png',
|
iconPath: 'static/tabbar/personal.png',
|
||||||
@@ -43,15 +43,15 @@ export const nativeTabbarList: NativeTabBarItem[] = [
|
|||||||
// 如果需要配置鼓包,需要在 'tabbar/store.ts' 里面设置,最后在 `tabbar/index.vue` 里面更改鼓包的图片
|
// 如果需要配置鼓包,需要在 'tabbar/store.ts' 里面设置,最后在 `tabbar/index.vue` 里面更改鼓包的图片
|
||||||
export const customTabbarList: CustomTabBarItem[] = [
|
export const customTabbarList: CustomTabBarItem[] = [
|
||||||
{
|
{
|
||||||
text: '首页',
|
text: '数字广东',
|
||||||
pagePath: 'pages/index/index',
|
pagePath: 'pages/index/index',
|
||||||
// 注意 unocss 图标需要如下处理:(二选一)
|
// 注意 unocss 图标需要如下处理:(二选一)
|
||||||
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
||||||
// 2)配置到 unocss.config.ts 的 safelist 中
|
// 2)配置到 unocss.config.ts 的 safelist 中
|
||||||
iconType: 'unocss',
|
iconType: 'image',
|
||||||
icon: 'i-carbon-home',
|
icon: '/static/logo2.png',
|
||||||
|
iconActive: '/static/logo1.png',
|
||||||
// badge: 'dot',
|
// badge: 'dot',
|
||||||
navigationStyle: 'custom',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pagePath: 'pages/sort/index',
|
pagePath: 'pages/sort/index',
|
||||||
|
|||||||
@@ -205,3 +205,50 @@ export interface Banner {
|
|||||||
link?: string // 跳转链接
|
link?: string // 跳转链接
|
||||||
goodsId?: string // 关联商品ID
|
goodsId?: string // 关联商品ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 助贷申请记录相关类型定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 助贷申请状态
|
||||||
|
export enum LoanApplicationStatus {
|
||||||
|
PROCESSING = 'PROCESSING', // 处理中
|
||||||
|
COMPLETED = 'COMPLETED', // 已完成
|
||||||
|
PENDING = 'PENDING', // 待提交
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进度条数据
|
||||||
|
export interface LoanApplicationProgress {
|
||||||
|
show: boolean
|
||||||
|
steps: string[] // 步骤名称数组
|
||||||
|
currentStepIndex: number // 当前步骤索引 (从0开始)
|
||||||
|
stepStatus: string // 当前步骤状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提示信息框
|
||||||
|
export interface LoanApplicationAlertInfo {
|
||||||
|
show: boolean
|
||||||
|
type: 'info' | 'warning' // info(蓝), warning(黄)
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部按钮配置
|
||||||
|
export interface LoanApplicationAction {
|
||||||
|
code: string
|
||||||
|
text: string
|
||||||
|
style: string // text-link, primary-blue, primary-green, primary-yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
// 助贷申请记录
|
||||||
|
export interface LoanApplicationRecord {
|
||||||
|
applicationId: string // 申请编号
|
||||||
|
loanType: string // 贷款类型:ENTERPRISE, 其他
|
||||||
|
loanTitle: string // 显示标题
|
||||||
|
dateLabel: string // 动态标签:申请时间 / 创建时间
|
||||||
|
dateValue: string // 日期值
|
||||||
|
status: LoanApplicationStatus // 状态枚举
|
||||||
|
statusText: string // 状态中文文案
|
||||||
|
progress?: LoanApplicationProgress // 进度条数据 (仅Processing和Pending状态需要)
|
||||||
|
alertInfo?: LoanApplicationAlertInfo // 提示信息框 (可空)
|
||||||
|
actions: LoanApplicationAction[] // 底部按钮配置
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default defineConfig({
|
|||||||
// 动态图标需要在这里配置,或者写在vue页面中注释掉
|
// 动态图标需要在这里配置,或者写在vue页面中注释掉
|
||||||
safelist: ['i-carbon-code', 'i-carbon-home', 'i-carbon-user','i-carbon-shopping-cart','i-carbon-view-mode-2','i-carbon-webhook',
|
safelist: ['i-carbon-code', 'i-carbon-home', 'i-carbon-user','i-carbon-shopping-cart','i-carbon-view-mode-2','i-carbon-webhook',
|
||||||
'i-carbon-restaurant','i-carbon-crop','i-carbon-ibm-deployable-architecture',
|
'i-carbon-restaurant','i-carbon-crop','i-carbon-ibm-deployable-architecture',
|
||||||
'i-carbon-edge-device','i-carbon-ibm-cloud-direct-link-1-dedicated-hosting','i-carbon-face-satisfied'],
|
'i-carbon-edge-device','i-carbon-ibm-cloud-direct-link-1-dedicated-hosting','i-carbon-face-satisfied','i-carbon-document-attachment'],
|
||||||
rules: [
|
rules: [
|
||||||
[
|
[
|
||||||
'p-safe',
|
'p-safe',
|
||||||
|
|||||||
Reference in New Issue
Block a user