You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cz-hjjc-budget/docs/第三步-事前流程字段交互逻辑.md

436 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
name: 第三步-事前流程字段交互逻辑
overview: 详细说明非直接支付页面第三步中 pre_approval_flow 字段(事前流程选择)的交互逻辑、组件使用情况、数据流和预览展示。
status: 已完成
---
# 第三步 - 事前流程字段交互逻辑
## 一、组件使用
### 1.1 组件引入
**文件**: `czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue`
```vue
import PreApprovalFlowPicker from '@/components/PreApprovalFlowPicker.vue'
```
### 1.2 组件渲染
**位置**: 第三步动态表单渲染部分(约第 296 行)
**触发条件**: 当字段的 `element_type === 'oa_custom_model'`
```vue
<!-- 事前流程实例 -->
<PreApprovalFlowPicker
v-else-if="field.element_type === 'oa_custom_model'"
v-model="formData[field.key]"
:model-id="field.model_id"
:placeholder="`请选择${field.label}`"
/>
```
### 1.3 组件属性说明
| 属性 | 类型 | 说明 | 来源 |
|------|------|------|------|
| `v-model` | `Number\|String\|null` | 双向绑定存储选中的流程ID | `formData[field.key]` |
| `model-id` | `Number\|String` | 流程模型ID用于查询对应的流程实例 | `field.model_id`(从模板配置获取) |
| `placeholder` | `String` | 占位符文本 | `请选择${field.label}` |
## 二、交互流程
### 2.1 交互流程图
```mermaid
flowchart TD
A[用户进入第三步] --> B{字段类型是否为 oa_custom_model?}
B -->|是| C[渲染 PreApprovalFlowPicker 组件]
B -->|否| D[渲染其他字段类型]
C --> E{是否已选择流程?}
E -->|否| F[显示"选择事前流程"按钮]
E -->|是| G[显示已选择的流程信息]
F --> H[用户点击按钮]
H --> I[打开选择对话框]
I --> J[加载流程实例列表]
J --> K[用户搜索/浏览流程]
K --> L[用户点击表格行选择]
L --> M[点击确定按钮]
M --> N[更新 v-model 值]
N --> O[关闭对话框]
O --> G
G --> P[显示流程编号和标题]
P --> Q[显示流程状态标签]
Q --> R[提供清除按钮]
R --> S{用户点击清除?}
S -->|是| T[清空选择]
T --> F
S -->|否| U[保持选择状态]
```
### 2.2 详细交互步骤
#### 步骤1初始状态
- **未选择时**:显示"选择事前流程"按钮
- **已选择时**:显示已选择的流程信息卡片,包含:
- 流程编号和标题
- 流程状态标签(带颜色)
- 清除按钮(右上角)
#### 步骤2打开选择对话框
- 点击"选择事前流程"按钮
- 打开宽度为 80% 的对话框
- 对话框标题:"选择事前流程实例"
#### 步骤3加载流程列表
**API 调用**: `GET /api/budget/planned-expenditures/oa-flow-instances`
**请求参数**:
```javascript
{
custom_model_id: field.model_id, // 流程模型ID
page: 1, // 当前页码
page_size: 15, // 每页数量
keyword: '' // 搜索关键词(可选)
}
```
**响应数据结构**:
```javascript
{
code: 0,
data: {
data: [ // 流程实例列表
{
id: 1,
no: 'FLOW-20250101-001',
title: '流程标题',
status: 'approved',
status_text: '已通过',
created_at: '2025-01-01 10:00:00',
related_count: 2, // 已关联数量
list_field_values: {}, // 列表字段值
list_fields: [] // 列表字段定义
}
],
total: 100, // 总记录数
list_columns: [ // 动态列定义show_in_list=1的字段
{
label: '字段标签',
name: 'field_name',
type: 'text'
}
]
}
}
```
#### 步骤4流程列表展示
**表格列**:
1. **序号列** (#) - 宽度 60px
2. **流程编号** (no) - 宽度 150px支持溢出提示
3. **流程标题** (title) - 最小宽度 250px支持溢出提示
4. **动态列** - 根据 `list_columns` 动态生成,显示 `show_in_list=1` 的字段
5. **流程状态** - 宽度 120px显示状态标签
6. **创建时间** - 宽度 180px
7. **关联信息** - 宽度 200px显示已关联数量
8. **操作** - 宽度 120px固定右侧包含"查看详情"按钮
**交互特性**:
- 支持行点击选择(高亮当前行)
- 支持搜索功能(搜索流程编号、标题、列表字段)
- 支持分页15/30/50/100 条每页)
- 支持查看详情(打开详情对话框)
#### 步骤5选择流程
- 用户点击表格行(高亮显示)
- 点击"确定"按钮
- 更新 `v-model` 绑定的值流程ID
- 关闭对话框
- 更新显示区域,展示已选择的流程信息
#### 步骤6清除选择
- 点击已选择流程卡片右上角的清除按钮(红色链接按钮)
- 清空 `v-model` 绑定的值
- 恢复显示"选择事前流程"按钮
## 三、数据存储
### 3.1 表单数据存储
**存储位置**: `formData[field.key]`
**存储格式**:
- 未选择:`null` 或 `undefined`
- 已选择流程ID`Number` 或 `String`
**示例**:
```javascript
formData = {
'element_123': 456, // 字段key: 流程ID
// ... 其他字段
}
```
### 3.2 后端存储
**表**: `budget_planned_expenditure_element_values`
**字段映射**:
- `planned_expenditure_id`: 支出记录ID
- `element_id`: 模板元素ID
- `field_key`: 字段键名(如 `element_123`
- `element_type`: `'oa_custom_model'`
- `value_text`: 流程ID字符串格式
- `flow_custom_model_id`: 流程模型ID冗余字段
- `flow_instance_id`: 流程实例ID冗余字段
- `flow_display_name`: 流程显示名称(冗余字段)
**存储逻辑**: 通过 `PlannedExpenditureController::persistElementValues()` 方法保存
## 四、预览页面展示
### 4.1 流程详情加载
**触发时机**: 进入预览页面第6步
**函数**: `loadFlowDetailsForPreview()`
**逻辑**:
1. 遍历模板配置,收集所有 `oa_custom_model` 类型的字段
2. 提取这些字段的值流程ID
3. 去重后调用批量查询接口
**API 调用**: `GET /api/budget/planned-expenditures/oa-flow-details`
**请求参数**:
```javascript
{
flow_ids: [1, 2, 3, ...] // 流程ID数组
}
```
**响应数据**:
```javascript
{
code: 0,
data: {
'1': { // key为流程ID
id: 1,
no: 'FLOW-001',
title: '流程标题',
display_name: 'FLOW-001 - 流程标题',
status: 'approved',
list_fields: [], // 列表字段
all_fields: [] // 全部字段(用于详情)
},
// ...
}
}
```
**存储位置**: `flowDetailsMap.value`(流程详情映射表)
### 4.2 预览格式化
**函数**: `formatPreviewFieldValue()`
**处理逻辑**:
```javascript
if (elementType === 'oa_custom_model') {
if (!value) {
return '未选择流程'
}
const flowDetail = flowDetailsMap.value[value]
if (flowDetail) {
// 生成HTML包含
// 1. 流程基础信息(编号-标题)
// 2. 列表字段表格(如果有)
return html
}
return `流程ID: ${value}`
}
```
**展示内容**:
- **基础信息**: 流程编号和标题(加粗显示)
- **列表字段表格**: 显示所有 `show_in_list=1` 的字段及其值
**HTML 结构**:
```html
<div class="flow-preview">
<div class="flow-basic-info">
<strong>FLOW-001 - 流程标题</strong>
</div>
<table class="flow-list-fields">
<tr>
<td class="field-label">字段1:</td>
<td class="field-value">值1</td>
</tr>
<!-- ... 更多字段 ... -->
</table>
</div>
```
## 五、组件内部实现
### 5.1 核心状态
```javascript
const selectedFlow = ref(null) // 当前选中的流程对象
const showSelectDialog = ref(false) // 选择对话框显示状态
const showDetailDialog = ref(false) // 详情对话框显示状态
const flowList = ref([]) // 流程列表数据
const listColumns = ref([]) // 动态列定义
const searchKeyword = ref('') // 搜索关键词
const loading = ref(false) // 加载状态
```
### 5.2 核心方法
#### `loadFlowInstances()`
- 加载流程实例列表
- 支持分页和搜索
- 处理动态列定义
#### `handleSelectFlow(row)`
- 处理表格行点击
- 设置 `selectedRow`
#### `confirmSelect()`
- 确认选择
- 更新 `v-model`
- 关闭对话框
#### `clearSelection()`
- 清除选择
- 重置 `v-model``null`
#### `viewFlowDetail(row)`
- 查看流程详情
- 打开详情对话框
#### `loadFlowDetail(flowId)`
- 加载流程详情
- 使用缓存机制
- 优先从列表获取其次从API加载
### 5.3 计算属性
```javascript
const detailFields = computed(() => {
// 优先使用 all_fields否则使用 list_fields
const flow = detailFlow.value
if (!flow) return []
if (Array.isArray(flow.all_fields) && flow.all_fields.length > 0) {
return flow.all_fields
}
if (Array.isArray(flow.list_fields) && flow.list_fields.length > 0) {
return flow.list_fields
}
return []
})
```
## 六、样式和UI
### 6.1 已选择状态展示
```css
.selected-item {
display: flex;
align-items: center;
padding: 12px;
background: #f5f7fa;
border-radius: 4px;
border: 1px solid #e4e7ed;
}
.item-icon {
color: #409eff;
font-size: 20px;
margin-right: 12px;
}
.item-content {
flex: 1;
}
.item-title {
font-weight: 500;
color: #303133;
}
.item-meta {
margin-top: 4px;
}
.item-remove {
margin-left: 12px;
}
```
### 6.2 选择对话框
- 宽度80%
- 支持搜索
- 表格支持行点击高亮
- 分页控件在底部
- 操作按钮在对话框底部
### 6.3 详情对话框
- 宽度70%
- 使用 `el-descriptions` 展示基础信息
- 使用 `el-table` 展示列表字段
- 支持关联信息展示(如果有)
## 七、注意事项
### 7.1 数据一致性
- 流程ID必须与 `model_id` 对应的流程模型匹配
- 选择流程后,流程详情会在预览页面加载
- 如果流程不存在,预览页面会显示"流程ID: xxx"
### 7.2 性能优化
- 流程列表支持分页,避免一次性加载过多数据
- 流程详情使用缓存机制,避免重复请求
- 预览页面批量加载所有流程详情,减少请求次数
### 7.3 错误处理
- API 请求失败时显示错误提示
- 流程不存在时显示友好的提示信息
- 网络错误时不影响其他功能
## 八、相关文件
### 8.1 组件文件
- `czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue`
### 8.2 页面文件
- `czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue`
### 8.3 API 文件
- `czemc-budget-execution-frontend/src/utils/api.js`
- `plannedExpenditureAPI.getOaFlowInstances()` - 获取流程实例列表
- `plannedExpenditureAPI.getOaFlowDetails()` - 批量获取流程详情
### 8.4 后端接口
- `GET /api/budget/planned-expenditures/oa-flow-instances` - 获取流程实例列表
- `GET /api/budget/planned-expenditures/oa-flow-details` - 批量获取流程详情