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

11 KiB

name overview status
第三步-事前流程字段交互逻辑 详细说明非直接支付页面第三步中 pre_approval_flow 字段(事前流程选择)的交互逻辑、组件使用情况、数据流和预览展示。 已完成

第三步 - 事前流程字段交互逻辑

一、组件使用

1.1 组件引入

文件: czemc-budget-execution-frontend/src/views/payment/IndirectPayment.vue

import PreApprovalFlowPicker from '@/components/PreApprovalFlowPicker.vue'

1.2 组件渲染

位置: 第三步动态表单渲染部分(约第 296 行)

触发条件: 当字段的 element_type === 'oa_custom_model'

<!-- 事前流程实例 -->
<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 交互流程图

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

请求参数:

{
  custom_model_id: field.model_id,  // 流程模型ID
  page: 1,                           // 当前页码
  page_size: 15,                     // 每页数量
  keyword: ''                        // 搜索关键词(可选)
}

响应数据结构:

{
  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]

存储格式:

  • 未选择:nullundefined
  • 已选择流程IDNumberString

示例:

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

请求参数:

{
  flow_ids: [1, 2, 3, ...]  // 流程ID数组
}

响应数据:

{
  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()

处理逻辑:

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 结构:

<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 核心状态

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-modelnull

viewFlowDetail(row)

  • 查看流程详情
  • 打开详情对话框

loadFlowDetail(flowId)

  • 加载流程详情
  • 使用缓存机制
  • 优先从列表获取其次从API加载

5.3 计算属性

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 已选择状态展示

.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 - 批量获取流程详情