dengzedong
5 天以前 3357b5f0f0919f7a52cd32a6d6de0acb14e0daaf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<template>
 
  <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
  <ContentWrap v-if="!selectProcessDefinition" v-loading="loading">
    <el-tabs tab-position="left" v-model="categoryActive">
      <el-tab-pane
        :label="category.name"
        :name="category.code"
        :key="category.code"
        v-for="category in categoryList"
      >
        <el-row :gutter="20">
          <el-col
            :lg="6"
            :sm="12"
            :xs="24"
            v-for="definition in categoryProcessDefinitionList"
            :key="definition.id"
          >
            <el-card
              shadow="hover"
              class="mb-20px cursor-pointer"
              @click="handleSelect(definition)"
            >
              <template #default>
                <div class="flex">
                  <el-image :src="definition.icon" class="w-32px h-32px" />
                  <el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
                </div>
              </template>
            </el-card>
          </el-col>
        </el-row>
      </el-tab-pane>
    </el-tabs>
  </ContentWrap>
 
  <!-- 第二步,填写表单,进行流程的提交 -->
  <ContentWrap v-else>
    <el-card class="box-card">
      <div class="clearfix">
        <span class="el-icon-document">申请信息【{{ selectProcessDefinition.name }}】</span>
        <el-button style="float: right" type="primary" @click="selectProcessDefinition = undefined">
          <Icon icon="ep:delete" /> 选择其它流程
        </el-button>
      </div>
      <el-col :span="16" :offset="6" style="margin-top: 20px">
        <form-create
          :rule="detailForm.rule"
          v-model:api="fApi"
          v-model="detailForm.value"
          :option="detailForm.option"
          @submit="submitForm"
        >
          <template #type-startUserSelect>
            <el-col :span="24">
              <el-card class="mb-10px">
                <template #header>指定审批人</template>
                <el-form
                  :model="startUserSelectAssignees"
                  :rules="startUserSelectAssigneesFormRules"
                  ref="startUserSelectAssigneesFormRef"
                >
                  <el-form-item
                    v-for="userTask in startUserSelectTasks"
                    :key="userTask.id"
                    :label="`任务【${userTask.name}】`"
                    :prop="userTask.id"
                  >
                    <el-select
                      v-model="startUserSelectAssignees[userTask.id]"
                      multiple
                      placeholder="请选择审批人"
                    >
                      <el-option
                        v-for="user in userList"
                        :key="user.id"
                        :label="user.nickname"
                        :value="user.id"
                      />
                    </el-select>
                  </el-form-item>
                </el-form>
              </el-card>
            </el-col>
          </template>
        </form-create>
      </el-col>
    </el-card>
    <!-- 流程图预览 -->
    <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
  </ContentWrap>
</template>
<script lang="ts" setup>
import * as DefinitionApi from '@/api/bpm/definition'
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
import type { ApiAttrs } from '@form-create/element-ui/types/config'
import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
import { CategoryApi } from '@/api/bpm/category'
import { useTagsViewStore } from '@/store/modules/tagsView'
import * as UserApi from '@/api/system/user'
 
defineOptions({ name: 'BpmProcessInstanceCreate' })
 
const route = useRoute() // 路由
const { push, currentRoute } = useRouter() // 路由
const message = useMessage() // 消息
const { delView } = useTagsViewStore() // 视图操作
 
const processInstanceId = route.query.processInstanceId
const loading = ref(true) // 加载中
const categoryList = ref([]) // 分类的列表
const categoryActive = ref('') // 选中的分类
const processDefinitionList = ref([]) // 流程定义的列表
 
/** 查询列表 */
const getList = async () => {
  loading.value = true
  try {
    // 流程分类
    categoryList.value = await CategoryApi.getCategorySimpleList()
    if (categoryList.value.length > 0) {
      categoryActive.value = categoryList.value[0].code
    }
    // 流程定义
    processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
      suspensionState: 1
    })
 
    // 如果 processInstanceId 非空,说明是重新发起
    if (processInstanceId?.length > 0) {
      const processInstance = await ProcessInstanceApi.getProcessInstance(processInstanceId)
      if (!processInstance) {
        message.error('重新发起流程失败,原因:流程实例不存在')
        return
      }
      const processDefinition = processDefinitionList.value.find(
        (item) => item.key == processInstance.processDefinition?.key
      )
      if (!processDefinition) {
        message.error('重新发起流程失败,原因:流程定义不存在')
        return
      }
      await handleSelect(processDefinition, processInstance.formVariables)
    }
  } finally {
    loading.value = false
  }
}
 
/** 选中分类对应的流程定义列表 */
const categoryProcessDefinitionList = computed(() => {
  return processDefinitionList.value.filter((item) => item.category == categoryActive.value)
})
 
// ========== 表单相关 ==========
const fApi = ref<ApiAttrs>()
const detailForm = ref({
  rule: [],
  option: {},
  value: {}
}) // 流程表单详情
const selectProcessDefinition = ref() // 选择的流程定义
 
// 指定审批人
const bpmnXML = ref(null) // BPMN 数据
const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
const userList = ref<any[]>([]) // 用户列表
 
/** 处理选择流程的按钮操作 **/
const handleSelect = async (row, formVariables) => {
  // 设置选择的流程
  selectProcessDefinition.value = row
 
  // 重置指定审批人
  startUserSelectTasks.value = []
  startUserSelectAssignees.value = {}
  startUserSelectAssigneesFormRules.value = {}
 
  // 情况一:流程表单
  if (row.formType == 10) {
    // 设置表单
    // 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
    // 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
    //        这样,就可能导致一个流程被审批不通过后,重新发起时,会直接后端报错!!!
    const allowedFields = decodeFields(row.formFields).map((fieldObj: any) => fieldObj.field)
    for (const key in formVariables) {
      if (!allowedFields.includes(key)) {
        delete formVariables[key]
      }
    }
    setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
 
    // 加载流程图
    const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
    if (processDefinitionDetail) {
      bpmnXML.value = processDefinitionDetail.bpmnXml
      startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
 
      // 设置指定审批人
      if (startUserSelectTasks.value?.length > 0) {
        detailForm.value.rule.push({
          type: 'startUserSelect',
          props: {
            title: '指定审批人'
          }
        })
        // 设置校验规则
        for (const userTask of startUserSelectTasks.value) {
          startUserSelectAssignees.value[userTask.id] = []
          startUserSelectAssigneesFormRules.value[userTask.id] = [
            { required: true, message: '请选择审批人', trigger: 'blur' }
          ]
        }
        // 加载用户列表
        userList.value = await UserApi.getSimpleUserList()
      }
    }
    // 情况二:业务表单
  } else if (row.formCustomCreatePath) {
    await push({
      path: row.formCustomCreatePath
    })
    // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
  }
}
 
/** 提交按钮 */
const submitForm = async (formData) => {
  if (!fApi.value || !selectProcessDefinition.value) {
    return
  }
  // 如果有指定审批人,需要校验
  if (startUserSelectTasks.value?.length > 0) {
    await startUserSelectAssigneesFormRef.value.validate()
  }
 
  // 提交请求
  fApi.value.btn.loading(true)
  try {
    await ProcessInstanceApi.createProcessInstance({
      processDefinitionId: selectProcessDefinition.value.id,
      variables: formData,
      startUserSelectAssignees: startUserSelectAssignees.value
    })
    // 提示
    message.success('发起流程成功')
    // 跳转回去
    delView(unref(currentRoute))
    await push({
      name: 'BpmProcessInstanceMy'
    })
  } finally {
    fApi.value.btn.loading(false)
  }
}
 
/** 初始化 */
onMounted(() => {
  getList()
})
</script>