# 间接支付页面 - 第三步"选择事前流程"按钮逻辑说明 ## 页面信息 - **页面路径**: `http://localhost:3000/#/payment/indirect-payment` - **页面组件**: `czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue` - **步骤**: 第三步 - 填写信息 - **相关组件**: `czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue` ## 一、按钮出现逻辑 ### 1.1 渲染条件 **位置**: `IndirectPayment.vue` 第296-301行 ```296:301:czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue ``` **触发条件**: 1. 当前字段的 `element_type === 'oa_custom_model'`(事前流程类型) 2. 字段必须存在于模板配置中(从 `templateConfig` 中获取) 3. 字段的 `visible !== false`(字段可见) 4. 字段通过显示条件检查(`checkFieldDisplayConditions(f)`) ### 1.2 按钮显示/隐藏逻辑 **位置**: `PreApprovalFlowPicker.vue` 第4-40行 ```4:40:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue
{{ getFlowDisplayName(selectedFlow) }}
{{ getStatusText(selectedFlow.status) }}
选择事前流程
``` **显示逻辑**: - **显示"选择事前流程"按钮**的条件: 1. `readonly === false`(非只读模式) 2. `selectedFlow === null` 或 `selectedFlow === undefined`(未选择流程) 3. `disabled === false`(按钮未禁用) - **显示已选择流程卡片**的条件: 1. `readonly === false`(非只读模式) 2. `selectedFlow !== null` 且 `selectedFlow !== undefined`(已选择流程) ### 1.3 状态判断流程 ```mermaid flowchart TD A[字段类型检查] --> B{是否为 oa_custom_model?} B -->|否| C[不渲染组件] B -->|是| D{字段是否可见?} D -->|否| C D -->|是| E{是否通过显示条件?} E -->|否| C E -->|是| F[渲染 PreApprovalFlowPicker] F --> G{readonly?} G -->|是| H[显示只读状态] G -->|否| I{selectedFlow?} I -->|有值| J[显示已选择流程卡片] I -->|无值| K[显示"选择事前流程"按钮] K --> L{disabled?} L -->|是| M[按钮禁用状态] L -->|否| N[按钮可点击] ``` ## 二、点击按钮后的操作步骤 ### 2.1 打开选择对话框 **触发**: 点击"选择事前流程"按钮 **操作**: 设置 `showSelectDialog.value = true` **位置**: `PreApprovalFlowPicker.vue` 第34行 ```34:34:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue @click="showSelectDialog = true" ``` ### 2.2 对话框打开事件处理 **函数**: `handleDialogOpened()`(第623-631行) ```623:631:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 对话框打开时先加载年份列表,然后加载流程列表 const handleDialogOpened = async () => { // 先加载年份列表 await loadAvailableYears() // 年份加载完成后,如果有选中的年份,再加载流程列表 if (selectedYear.value) { loadFlowInstances() } } ``` **执行顺序**: 1. 调用 `loadAvailableYears()` 加载可用年份列表 2. 自动选定年份(优先当年,否则最新年份) 3. 如果有选中的年份,调用 `loadFlowInstances()` 加载流程列表 ### 2.3 加载年份列表 **函数**: `loadAvailableYears()`(第416-453行) **API调用**: `GET /api/budget/planned-expenditures/oa-flow-years` **请求参数**: ```javascript { custom_model_id: props.modelId // 流程模型ID } ``` **响应处理**: - 如果返回年份数组,设置 `availableYears.value` - 自动选定年份: - 优先选择当前年份(如果存在) - 否则选择数组中的第一个年份(最新年份) - 如果无数据或出错,默认使用当前年份 **关键代码**: ```432:444:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 自动选定当年 const currentYear = new Date().getFullYear() if (availableYears.value.includes(currentYear)) { selectedYear.value = currentYear } else if (availableYears.value.length > 0) { // 如果没有当年,选择最新的年份 selectedYear.value = availableYears.value[0] } ``` ### 2.4 加载流程实例列表 **函数**: `loadFlowInstances()`(第456-522行) **触发时机**: 1. 对话框打开后,年份加载完成且有选中年份 2. 年份切换时 3. 搜索关键词变化时 4. 分页参数变化时 **API调用**: `GET /api/budget/planned-expenditures/oa-flow-instances` **请求参数**: ```javascript { custom_model_id: props.modelId, // 流程模型ID(必填) year: selectedYear.value, // 年份(必填) page: pagination.value.currentPage, // 当前页码 page_size: pagination.value.pageSize, // 每页数量 keyword: searchKeyword.value // 搜索关键词(可选) } ``` **响应数据结构**: ```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' } ] } } ``` **处理逻辑**: 1. 提取 `list_columns` 作为动态表格列 2. 提取 `data` 作为流程列表数据 3. 更新分页信息(`total`) 4. 设置加载状态 ### 2.5 流程列表展示 **位置**: 对话框中的表格(第107-166行) **表格列结构**: 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 条每页) - 支持查看详情(打开详情对话框) ### 2.6 年份切换 **函数**: `handleYearChange()`(第608-614行) **触发**: 用户切换年份单选按钮 **操作**: 1. 重置分页到第一页 2. 清空搜索关键词 3. 清空已选中的行 4. 重新加载流程列表 ```608:614:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 年份切换 const handleYearChange = () => { pagination.value.currentPage = 1 searchKeyword.value = '' selectedRow.value = null loadFlowInstances() } ``` ### 2.7 搜索功能 **函数**: `handleSearch()`(第616-620行) **触发**: 搜索输入框内容变化(`@input` 事件) **操作**: 1. 重置分页到第一页 2. 重新加载流程列表(带搜索关键词) ```616:620:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 搜索 const handleSearch = () => { pagination.value.currentPage = 1 loadFlowInstances() } ``` ### 2.8 选择流程 **函数**: `handleSelectFlow(row)`(第572-575行) **触发**: 点击表格行 **操作**: 设置 `selectedRow.value = row`(高亮当前行) ```572:575:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 处理选择行 const handleSelectFlow = (row) => { selectedRow.value = row } ``` ### 2.9 确认选择 **函数**: `confirmSelect()`(第577-592行) **触发**: 点击对话框底部的"确定"按钮 **前置条件**: `selectedRow.value !== null`(必须已选择一行) **操作步骤**: 1. 验证是否已选择流程 2. 设置 `selectedFlow.value = selectedRow.value` 3. 触发 `update:modelValue` 事件,传递流程ID 4. 触发 `change` 事件,传递流程ID 5. 加载流程详情用于展示 6. 关闭对话框 ```577:592:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 确认选择 const confirmSelect = () => { if (!selectedRow.value) { ElMessage.warning('请选择一条流程') return } selectedFlow.value = selectedRow.value emit('update:modelValue', selectedRow.value.id) emit('change', selectedRow.value.id) // 加载详情用于展示 loadFlowDetail(selectedRow.value.id) showSelectDialog.value = false } ``` ### 2.10 加载流程详情 **函数**: `loadFlowDetail(flowId)`(第524-570行) **触发时机**: 1. 确认选择后,加载选中流程的详情 2. 查看详情时,加载指定流程的详情 3. 组件初始化时,如果有初始值,加载详情 **缓存机制**: 1. 先检查缓存 `flowDetailsCache.value[flowId]` 2. 如果缓存存在,直接使用 3. 否则从列表数据中查找 4. 如果列表中没有,调用API加载 **API调用**: `GET /api/budget/planned-expenditures/oa-flow-details` **请求参数**: ```javascript { flow_ids: [flowId] // 流程ID数组 } ``` **响应数据**: ```javascript { code: 0, data: { 'flowId': { // key为流程ID id: 1, no: 'FLOW-001', title: '流程标题', display_name: 'FLOW-001 - 流程标题', status: 'approved', list_fields: [], // 列表字段 all_fields: [] // 全部字段(用于详情) } } } ``` ### 2.11 更新显示状态 **位置**: 组件模板第6-27行 **显示内容**: - 流程图标(DocumentChecked) - 流程显示名称(编号 - 标题) - 流程状态标签(带颜色) - 清除按钮(右上角) **样式**: 绿色背景卡片,带边框 ### 2.12 清除选择 **函数**: `clearSelection()`(第594-600行) **触发**: 点击已选择流程卡片右上角的清除按钮 **操作**: 1. 清空 `selectedFlow.value` 2. 清空 `selectedRow.value` 3. 触发 `update:modelValue` 事件,传递 `null` 4. 触发 `change` 事件,传递 `null` **结果**: 恢复显示"选择事前流程"按钮 ```594:600:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 清除选择 const clearSelection = () => { selectedFlow.value = null selectedRow.value = null emit('update:modelValue', null) emit('change', null) } ``` ## 三、完整操作流程图 ```mermaid sequenceDiagram participant U as 用户 participant C as PreApprovalFlowPicker组件 participant API as 后端API U->>C: 点击"选择事前流程"按钮 C->>C: showSelectDialog = true C->>C: handleDialogOpened() C->>API: GET /oa-flow-years (加载年份列表) API-->>C: 返回年份数组 C->>C: 自动选定年份(优先当年) C->>API: GET /oa-flow-instances (加载流程列表) API-->>C: 返回流程列表和动态列 C->>U: 显示流程列表对话框 alt 用户切换年份 U->>C: 切换年份 C->>C: handleYearChange() C->>C: 重置分页和搜索 C->>API: GET /oa-flow-instances (重新加载) API-->>C: 返回新年份的流程列表 end alt 用户搜索 U->>C: 输入搜索关键词 C->>C: handleSearch() C->>API: GET /oa-flow-instances (带关键词) API-->>C: 返回搜索结果 end U->>C: 点击表格行选择流程 C->>C: handleSelectFlow(row) C->>C: selectedRow = row (高亮行) U->>C: 点击"确定"按钮 C->>C: confirmSelect() C->>C: selectedFlow = selectedRow C->>C: emit('update:modelValue', flowId) C->>API: GET /oa-flow-details (加载详情) API-->>C: 返回流程详情 C->>C: 缓存详情数据 C->>C: showSelectDialog = false C->>U: 显示已选择流程卡片 alt 用户清除选择 U->>C: 点击清除按钮 C->>C: clearSelection() C->>C: emit('update:modelValue', null) C->>U: 恢复显示"选择事前流程"按钮 end ``` ## 四、数据流 ### 4.1 数据绑定 **父组件** (`IndirectPayment.vue`): - `v-model="formData[field.key]"` - 双向绑定流程ID - `:model-id="field.model_id"` - 传递流程模型ID **子组件** (`PreApprovalFlowPicker.vue`): - `props.modelValue` - 接收流程ID - `props.modelId` - 接收流程模型ID - `emit('update:modelValue', flowId)` - 更新流程ID ### 4.2 数据存储 **表单数据**: ```javascript formData = { 'element_123': 456, // 字段key: 流程ID // ... 其他字段 } ``` **组件内部状态**: ```javascript selectedFlow.value = { id: 456, no: 'FLOW-001', title: '流程标题', status: 'approved', // ... 其他字段 } ``` ### 4.3 监听数据变化 **监听 modelValue 变化**(第634-647行): - 当外部传入的 `modelValue` 变化时 - 如果有值且与当前选中的流程ID不同,重新加载详情 - 如果值为空,清空选中状态 ```634:647:czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue // 监听 modelValue 变化,加载详情 watch(() => props.modelValue, (newValue) => { if (newValue) { // 如果有值但没有选中流程,需要加载 if (!selectedFlow.value || selectedFlow.value.id !== newValue) { loadFlowDetail(newValue).then(() => { if (detailFlow.value) { selectedFlow.value = detailFlow.value } }) } } else { selectedFlow.value = null } }, { immediate: true }) ``` ## 五、关键配置和参数 ### 5.1 组件属性 | 属性 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `modelValue` | Number/String/null | 否 | null | 双向绑定,流程ID | | `modelId` | Number/String | 是 | - | 流程模型ID | | `placeholder` | String | 否 | '请选择事前流程实例' | 占位符文本 | | `disabled` | Boolean | 否 | false | 是否禁用 | | `readonly` | Boolean | 否 | false | 是否只读 | | `autoLoad` | Boolean | 否 | false | 是否自动加载 | ### 5.2 对话框配置 - **宽度**: 80% - **标题**: "选择事前流程实例" - **关闭方式**: 点击遮罩不关闭(`:close-on-click-modal="false"`) ### 5.3 分页配置 - **默认每页**: 15条 - **可选每页**: 15/30/50/100条 - **布局**: total, sizes, prev, pager, next ## 六、注意事项 ### 6.1 必填参数 - `modelId` 必须提供,否则无法加载流程列表 - 年份必须选择,否则不加载流程列表 ### 6.2 数据一致性 - 流程ID必须与 `modelId` 对应的流程模型匹配 - 选择流程后,流程详情会在确认时加载并缓存 - 如果流程不存在,会显示友好的错误信息 ### 6.3 性能优化 - 流程列表支持分页,避免一次性加载过多数据 - 流程详情使用缓存机制,避免重复请求 - 年份列表只在对话框打开时加载一次 ### 6.4 错误处理 - API 请求失败时显示错误提示 - 流程不存在时显示友好的提示信息 - 网络错误时不影响其他功能 - 年份加载失败时默认使用当前年份 ### 6.5 用户体验 - 自动选定当前年份,减少用户操作 - 支持搜索,快速定位流程 - 支持查看详情,了解流程完整信息 - 已选择状态清晰展示,带状态标签 - 支持清除选择,方便重新选择 ## 七、相关文件 ### 7.1 组件文件 - `czemc-budget-execution-frontend/src/components/PreApprovalFlowPicker.vue` ### 7.2 页面文件 - `czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue` ### 7.3 API 文件 - `czemc-budget-execution-frontend/src/utils/api.js` - `plannedExpenditureAPI.getOaFlowYears()` - 获取可用年份列表 - `plannedExpenditureAPI.getOaFlowInstances()` - 获取流程实例列表 - `plannedExpenditureAPI.getOaFlowDetails()` - 批量获取流程详情 ### 7.4 后端接口 - `GET /api/budget/planned-expenditures/oa-flow-years` - 获取可用年份列表 - `GET /api/budget/planned-expenditures/oa-flow-instances` - 获取流程实例列表 - `GET /api/budget/planned-expenditures/oa-flow-details` - 批量获取流程详情