|
@@ -0,0 +1,244 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <!-- 流程走向图 -->
|
|
|
+ <el-timeline class="timeline-wrap">
|
|
|
+ <el-timeline-item v-for="(v, index) in processTimelineList" :key="index">
|
|
|
+ <template v-if="v.conditionNodes">
|
|
|
+ <el-radio-group v-model="processChecked[v.nodeKey]" size="small">
|
|
|
+ <el-radio-button v-for="c of v.conditionNodes" :key="c.nodeKey" :label="c.nodeKey">{{ c.nodeName }}</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div style="padding-bottom: 6px">{{ v.nodeName }}</div>
|
|
|
+ <span v-if="v.type === 5" class="text-gray-500">调用子流程 [ {{ v.callProcess.split(':')[1] }} ]</span>
|
|
|
+ <div v-if="assigneeMap[v.nodeKey]" style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap">
|
|
|
+ <template v-if="assigneeMap[v.nodeKey].type === 1">
|
|
|
+ <el-tooltip v-if="!assigneeMap[v.nodeKey].disabled" content="添加用户" placement="left">
|
|
|
+ <el-button style="width: 32px" @click="selectHandler(v.nodeKey, 1)">
|
|
|
+ <svg-icon style="font-size: 18px" icon-class="flow-user-add" />
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <FlowNodeAvatar v-for="(item, index) in assigneeMap[v.nodeKey].assignees" :key="index" :name="item.name" style="margin-top: 5px" />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-tooltip v-if="!assigneeMap[v.nodeKey].disabled" content="添加角色" placement="left">
|
|
|
+ <el-button style="width: 32px" @click="selectHandler(v.nodeKey, 3)">
|
|
|
+ <svg-icon style="font-size: 18px" icon-class="flow-group-add" />
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <FlowNodeAvatar v-for="(item, index) in assigneeMap[v.nodeKey].assignees" :key="index" :name="item.name" style="margin-top: 5px">
|
|
|
+ <template #avatar>
|
|
|
+ <svg-icon icon-class="flow-group" color="#fff" />
|
|
|
+ </template>
|
|
|
+ </FlowNodeAvatar>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="assigneeDesc[v.nodeKey]">
|
|
|
+ <!-- 没有assigneeMap 的情况下 尝试获取描述 -->
|
|
|
+ <el-tag>{{ assigneeDesc[v.nodeKey] }}</el-tag>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-timeline-item>
|
|
|
+ <el-timeline-item>
|
|
|
+ <div style="padding-bottom: 6px">结束</div>
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+
|
|
|
+ <!-- 选择人员/角色-->
|
|
|
+ <use-select ref="useSelectRef" v-bind="active_selectOpts" @update:selected="updateActive_assigneeMap"></use-select>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed, reactive } from 'vue'
|
|
|
+import UseSelect from '@/components/scWorkflow/select.vue'
|
|
|
+import { setTypeOptions_config } from '@/components/scWorkflow/nodes/config'
|
|
|
+import type { ModelContentConfig } from '@/views/approve/components/config.ts'
|
|
|
+type Props = {
|
|
|
+ modelValue: object
|
|
|
+}
|
|
|
+const props = defineProps<Props>()
|
|
|
+const emit = defineEmits<{
|
|
|
+ 'update:modelValue': [value: object] // 具名元组语法
|
|
|
+ update: [value: string]
|
|
|
+}>()
|
|
|
+
|
|
|
+type Assignee = {
|
|
|
+ type: 1 | 3 // 1: 用户 3: 角色
|
|
|
+ assignees: { [key: string]: any /*name, id*/ }
|
|
|
+ disabled?: boolean
|
|
|
+ selectOpts?: any
|
|
|
+ // minSelected?: number
|
|
|
+ // maxSelected?: number
|
|
|
+}
|
|
|
+const useSelectRef = ref()
|
|
|
+const assigneeMap = ref<{
|
|
|
+ [key: string]: Assignee
|
|
|
+}>({})
|
|
|
+const assigneeDesc = ref<{
|
|
|
+ [key: string]: string
|
|
|
+}>({})
|
|
|
+const active_assigneeKey = ref<string>()
|
|
|
+const active_selectOpts = ref({})
|
|
|
+const updateActive_assigneeMap = (assignees: any[]) => {
|
|
|
+ const _cur = assigneeMap.value[active_assigneeKey.value]
|
|
|
+ if (_cur) {
|
|
|
+ _cur.assignees = assignees
|
|
|
+ }
|
|
|
+}
|
|
|
+const selectHandler = (nodeKey: string, type: 1 | 3) => {
|
|
|
+ if (!assigneeMap.value[nodeKey]) {
|
|
|
+ assigneeMap.value[nodeKey] = { assignees: [], type }
|
|
|
+ }
|
|
|
+ const config = assigneeMap.value[nodeKey]
|
|
|
+ active_assigneeKey.value = nodeKey
|
|
|
+ active_selectOpts.value = config.selectOpts || {}
|
|
|
+ useSelectRef.value.open(type, config.assignees)
|
|
|
+}
|
|
|
+
|
|
|
+// 当前form 表单数据字符串
|
|
|
+const processChecked = reactive<{ [nodeKey: string]: any }>({})
|
|
|
+const packageProcess = (data: ModelContentConfig, list = []) => {
|
|
|
+ const new_list = [data]
|
|
|
+ let curData = data
|
|
|
+ while (curData.childNode) {
|
|
|
+ new_list.push(curData.childNode)
|
|
|
+ curData = curData.childNode
|
|
|
+ }
|
|
|
+ return new_list.reduce((_list, config) => {
|
|
|
+ // 条件分支
|
|
|
+ if (Array.isArray(config.conditionNodes)) {
|
|
|
+ // console.log('条件节点', config)
|
|
|
+ _list.push(config)
|
|
|
+ if (Array.isArray(config.conditionNodes) && config.conditionNodes.length) {
|
|
|
+ const _val = processChecked[config.nodeKey]
|
|
|
+ let condition: any = config.conditionNodes[0]
|
|
|
+ if (_val) {
|
|
|
+ config.conditionNodes.some(_condition => {
|
|
|
+ if (_condition.nodeKey === _val) {
|
|
|
+ condition = _condition
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // console.error('else......', condition)
|
|
|
+ processChecked[config.nodeKey] = condition.nodeKey
|
|
|
+ }
|
|
|
+ // console.warn('条件节点', condition.nodeName)
|
|
|
+ if (condition.childNode) {
|
|
|
+ packageProcess(condition.childNode, _list)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // console.log(config.nodeName, 'nodeName 普通节点名称', config, data)
|
|
|
+ // 0,发起人 1,审批人 2,抄送人 3,条件审批 4,条件分支 5,办理子流程 6,定时器在务 7,触发器在务
|
|
|
+ switch (+config.type) {
|
|
|
+ case 0: {
|
|
|
+ // 发起人
|
|
|
+ // console.error('发起人')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 1: {
|
|
|
+ // 审批人
|
|
|
+ // 针对审核人 不同情况控制
|
|
|
+ if (Reflect.has(config, 'setType')) {
|
|
|
+ let disabled = false
|
|
|
+ let selectOpts = {}
|
|
|
+ const user_fn = () => {
|
|
|
+ const _key = config.nodeKey
|
|
|
+ if (!assigneeMap.value[_key]) {
|
|
|
+ assigneeMap.value[_key] = { assignees: config.nodeAssigneeList, type: 1, disabled, selectOpts }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ switch (config.setType) {
|
|
|
+ case 1:
|
|
|
+ // 指定人员 (不允许重新选择) // 但展示
|
|
|
+ disabled = true
|
|
|
+ user_fn()
|
|
|
+ break
|
|
|
+ case 2:
|
|
|
+ // 主管 (不需要选择)
|
|
|
+ assigneeDesc.value[config.nodeKey] = config.examineLevel === 1 ? '直接主管' : `发起人的第${config.examineLevel}级主管`
|
|
|
+ break
|
|
|
+ case 3:
|
|
|
+ // 角色 选择角色 (允许重新选择)
|
|
|
+ const _key = config.nodeKey
|
|
|
+ if (!assigneeMap.value[_key]) {
|
|
|
+ assigneeMap.value[_key] = { assignees: config.nodeAssigneeList, type: 3 }
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 4:
|
|
|
+ // 发起人自选 (1: 选择一个人, 2: 选择多个人)
|
|
|
+ // const isMultiple = config.selectMode === 2
|
|
|
+ if (config.selectMode === 1) {
|
|
|
+ selectOpts = { maxSelected: 1 }
|
|
|
+ }
|
|
|
+ user_fn()
|
|
|
+ break
|
|
|
+ case 5: // 发起人自己 (不能选择)
|
|
|
+ assigneeDesc.value[config.nodeKey] = setTypeOptions_config[5]
|
|
|
+ break
|
|
|
+ case 6:
|
|
|
+ // 连续多级主管 (不能选择)
|
|
|
+ assigneeDesc.value[config.nodeKey] = config.examineLevel === 1 ? '直接主管' : `发起人的第${config.examineLevel}级主管`
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 2: {
|
|
|
+ // 抄送人
|
|
|
+ // 选择人员 & allowSelection 控制 true 允许选择 否则 隐藏
|
|
|
+ const _key = config.nodeKey
|
|
|
+ if (!assigneeMap.value[_key]) {
|
|
|
+ assigneeMap.value[_key] = { assignees: config.nodeAssigneeList, type: 1, disabled: !config.allowSelection }
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ /*case 3: {
|
|
|
+ // 条件审批
|
|
|
+ console.error('条件审批')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 4: {
|
|
|
+ // 条件分支
|
|
|
+ console.error('条件分支')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 5: {
|
|
|
+ // 办理子流程
|
|
|
+ console.error('办理子流程')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 6: {
|
|
|
+ // 定时器在务
|
|
|
+ console.error('定时器在务')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 7: {
|
|
|
+ // 触发器在务
|
|
|
+ console.error('触发器在务')
|
|
|
+ break
|
|
|
+ }*/
|
|
|
+ }
|
|
|
+ _list.push(config)
|
|
|
+ }
|
|
|
+ return _list
|
|
|
+ }, list)
|
|
|
+}
|
|
|
+
|
|
|
+const processTimelineList = computed(() => {
|
|
|
+ return packageProcess(props.modelValue)
|
|
|
+})
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ // 把数据暴露给父节点使用
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.timeline-wrap {
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding-left: 60px;
|
|
|
+}
|
|
|
+</style>
|