123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- <template>
- <el-drawer
- :close-on-click-modal="false"
- class="local-launch_drawer-wrap"
- :title="record.processName"
- :model-value="modelValue"
- size="760px"
- @update:model-value="updateModelValue"
- >
- <div v-if="validateForm.loading" v-loading="true" class="local_loading"></div>
- <div class="info-wrap">
- <el-divider content-position="left">{{ record.processName }}表单</el-divider>
- <FormCreate v-if="false" v-model:api="validateForm.api" class="form-wrap" :option="validateForm.option" :rule="validateForm.rule" />
- <er-form-preview ref="EReditorRef" :is-show-complete-button="false" />
- <el-divider content-position="left">审批流程</el-divider>
- <el-timeline class="timeline-wrap">
- <el-timeline-item v-for="(v, index) in processTimelineList" :key="index">
- <template v-if="v.conditionNode === 1">
- <el-radio-group v-model="processChecked[v.name]" size="small">
- <el-radio-button v-for="c of v.conditionNodeList" :key="c.name" :label="c.name" />
- </el-radio-group>
- </template>
- <template v-else>
- <div style="padding-bottom: 6px">{{ v.name }}</div>
- <div v-if="assigneeMap[v.name]" style="display: flex; align-items: center; gap: 6px">
- <template v-if="assigneeMap[v.name].type === 1">
- <el-tooltip v-if="!assigneeMap[v.name].disabled" content="添加用户" placement="left">
- <el-button style="width: 32px" @click="selectHandler(v.name, 1)">
- <svg-icon style="font-size: 18px" icon-class="flow-user-add" />
- </el-button>
- </el-tooltip>
- <FlowNodeAvatar v-for="(item, index) in assigneeMap[v.name].assignees" :key="index" :name="item.name" style="margin-top: 5px" />
- </template>
- <template v-else>
- <el-tooltip v-if="!assigneeMap[v.name].disabled" content="添加角色" placement="left">
- <el-button style="width: 32px" @click="selectHandler(v.name, 2)">
- <svg-icon style="font-size: 18px" icon-class="flow-group-add" />
- </el-button>
- </el-tooltip>
- <FlowNodeAvatar v-for="(item, index) in assigneeMap[v.name].assignees" :key="index" :name="item.name" style="margin-top: 5px">
- <template #avatar>
- <svg-icon icon-class="flow-group" color="#fff" />
- </template>
- </FlowNodeAvatar>
- </template>
- </div>
- </template>
- </el-timeline-item>
- <el-timeline-item>
- <div style="padding-bottom: 6px">结束</div>
- </el-timeline-item>
- </el-timeline>
- <el-divider></el-divider>
- </div>
- <template #footer>
- <el-button @click="updateModelValue(false)">{{ $t('le.btn.cancel') }}</el-button>
- <el-button :disabled="validateForm.loading" type="primary" style="margin-left: 8px" @click="onSubmit">{{ $t('le.btn.confirm') }}</el-button>
- </template>
- </el-drawer>
- <!-- 选择人员/角色-->
- <use-select ref="useSelectRef" v-bind="active_selectOpts" @update:selected="updateActive_assigneeMap"></use-select>
- </template>
- <script setup lang="ts">
- import model from '@/api/flow/process'
- import viewForm from '@/utils/form'
- import { erFormPreview } from 'everright-formeditor'
- import 'Everright-formEditor/dist/style.css'
- import { ref, shallowRef, computed, reactive, watchEffect } from 'vue'
- import { ElMessage } from 'element-plus'
- import UseSelect from '@/components/scWorkflow/select.vue'
- import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
- import { approveTypeOptions_config, selectModeOptions_config, setTypeOptions_config } from '@/components/scWorkflow/nodes/config'
- const EReditorRef = ref()
- type Props = {
- modelValue: boolean
- record: { processId: string; processName: string; [key: string]: any }
- }
- const props = defineProps<Props>()
- const emit = defineEmits<{
- 'update:modelValue': [bool: boolean] // 具名元组语法
- update: [value: string]
- }>()
- const updateModelValue = (bool: boolean) => emit('update:modelValue', bool)
- type Assignee = {
- type: 1 | 2 // 1: 用户 2: 角色
- assignees: { [key: string]: any /*name, id*/ }
- disabled?: boolean
- selectOpts?: any
- // minSelected?: number
- // maxSelected?: number
- }
- const useSelectRef = ref()
- const assigneeMap = ref<{
- [key: string]: Assignee
- }>({})
- const active_assigneeKey = ref<string>()
- const active_selectOpts = ref({})
- // const userMap = ref(new Map())
- // const active_assigneeMap = ref<Assignee>({})
- window.assigneeMap = assigneeMap
- const updateActive_assigneeMap = (assignees: any[]) => {
- // active_assigneeMap todo... { assignees: [], type }
- const _cur = assigneeMap.value[active_assigneeKey.value]
- if (_cur) {
- _cur.assignees = assignees
- }
- // assigneeMap.value[active_assigneeKey.value].assignees = assignees
- // Object.assign()
- }
- const selectHandler = (name: string, type: number) => {
- /*if (!userMap.value.get(name)) {
- userMap.value.set(name, { assignees: [], type })
- }
- useSelectRef.value.open(type, userMap.value.get(name).assignees)
- */
- if (!assigneeMap.value[name]) {
- assigneeMap.value[name] = { assignees: [], type }
- }
- // active_assigneeMap.value = assigneeMap.value[name]
- const config = assigneeMap.value[name]
- active_assigneeKey.value = name
- active_selectOpts.value = config.selectOpts || {}
- useSelectRef.value.open(type, config.assignees)
- }
- const FormCreate = viewForm.$form()
- const validateForm = ref({
- api: {},
- option: {
- submitBtn: false
- },
- rule: [],
- loading: false
- })
- const onSubmit = async () => {
- // todo 这里还需要做表单校验
- const formData = EReditorRef.value.getData()
- // console.log(formData)
- const api = validateForm.value.api
- // await api.validate()
- // const values = api.formData()
- // console.warn(values, 'values')
- const processId = props.record.processId
- validateForm.value.loading = true
- let processForm = JSON.parse(cur_processForm_str)
- processForm = { ...processForm, formData }
- // processForm.forEach(v => {
- // // 填写的数据存储(local_: 本地数据处理标识)
- // v.local_value = values[v.field]
- // })
- console.log(processForm, '===========')
- const _assigneeMap = assigneeMap.value
- const assigneeMap_ = Object.keys(_assigneeMap).reduce((obj, key) => {
- const _o = _assigneeMap[key]
- obj[key] = {
- assigneeList: _o.assignees,
- type: _o.type
- }
- return obj
- }, {})
- model
- .processLaunchApi({
- processId, // 流程ID
- processForm: JSON.stringify(processForm), // 流程表单JSON内容 & local_value 保存
- assigneeMap: assigneeMap_
- })
- .then(res => {
- ElMessage.success('提交成功')
- updateModelValue(false)
- })
- .finally(() => {
- validateForm.value.loading = false
- })
- }
- // 当前form 表单数据字符串
- let cur_processForm_str = '{}'
- const processChecked = reactive<{ [key: string]: any }>({
- /*'local_条件分支_期限': '短期'*/
- })
- const localProcessData = ref<any[]>([])
- let c_idx = 0
- const packageProcess = (data, list = []) => {
- return data.reduce((_list, config) => {
- if (config.conditionNode === 0) {
- console.log(config.name, 'name 普通节点名称', config, data)
- /* // todo...
- // 默认用户
- let type = 1
- let assignees = config.nodeUserList
- if (config.nodeRoleList) {
- // 存在设置角色
- type = 2
- assignees = config.nodeRoleList
- }
- // userMap.value.set(config.name, { assignees, type })
- // 流程名称
- assigneeMap.value[config.name] = { assignees, type }*/
- // 0,发起人 1,审批人 2,抄送人 3,条件审批 4,条件分支 5,办理子流程 6,定时器在务 7,触发器在务
- switch (+config.type) {
- case 0: {
- // 发起人
- console.error(1)
- break
- }
- case 1: {
- // 审批人
- const handleName = setTypeOptions_config[config.setType]
- // if (config.setType) {
- // 针对审核人 不同情况控制
- if (Reflect.has(config, 'setType')) {
- console.log(handleName, 'handleName', config)
- let disabled = false
- let selectOpts = {}
- switch (config.setType) {
- case 1:
- // 指定人员 (不允许重新选择) // 但展示
- disabled = true
- case 2:
- // 主管 (不需要选择)
- break
- case 3:
- // 角色 选择角色 (允许重新选择)
- assigneeMap.value[config.name] = { assignees: config.nodeRoleList, type: 2 }
- break
- case 4:
- // 发起人自选 (1: 选择一个人, 2: 选择多个人)
- // const isMultiple = config.selectMode === 2
- if (config.selectMode === 1) {
- selectOpts.maxSelected = 1
- }
- break
- case 5:
- // 发起人自己 (不能选择)
- // break
- case 6:
- // 连续多级主管 (不能选择)
- break
- default:
- assigneeMap.value[config.name] = { assignees: config.nodeUserList, type: 1, disabled: false, selectOpts }
- // 发起的时候不需要选择
- }
- }
- break
- }
- case 2: {
- // 抄送人
- // 选择人员 & allowSelection 控制 true 允许选择 否则 隐藏
- assigneeMap.value[config.name] = { assignees: config.nodeUserList, type: 1, disabled: !config.allowSelection }
- break
- }
- /*case 3: {
- // 条件审批
- console.error(1)
- break
- }
- case 4: {
- // 条件分支
- console.error(1)
- break
- }
- case 5: {
- // 办理子流程
- console.error(1)
- break
- }
- case 6: {
- // 定时器在务
- console.error(1)
- break
- }
- case 7: {
- // 触发器在务
- console.error(1)
- break
- }*/
- }
- _list.push(config)
- } else if (config.conditionNode === 1) {
- // 自定义标识key
- if (!config.name) {
- config.name = `local_${c_idx++}`
- }
- // console.log('条件节点', config)
- _list.push(config)
- if (Array.isArray(config.conditionNodeList) && config.conditionNodeList.length) {
- const _val = processChecked[config.name]
- let condition = config.conditionNodeList[0]
- if (_val) {
- config.conditionNodeList.some(_condition => {
- if (_condition.name === _val) {
- condition = _condition
- return true
- }
- })
- } else {
- // console.error('else......', condition)
- processChecked[config.name] = condition.name
- }
- // console.warn('条件节点', condition.name)
- if (Array.isArray(condition.childNode) && condition.childNode.length) {
- packageProcess(condition.childNode, _list)
- }
- }
- }
- return _list
- }, list)
- }
- const processTimelineList = computed(() => {
- return packageProcess(localProcessData.value)
- })
- watchEffect(() => {
- console.error(processTimelineList.value, 'processTimelineList todo')
- })
- validateForm.value.loading = true
- Promise.all([
- model.processListNodeMapApi(props.record.processId).then((res: any) => {
- localProcessData.value = res
- }),
- model.processDetailApi(props.record.processId).then(res => {
- /*let local_workflow = {}
- try {
- console.log(JSON.parse(res.modelContent))
- const modelContent = JSON.parse(res.modelContent)
- local_workflow = modelContent.nodeConfig ?? modelContent.childNode
- } catch (e) {}*/
- cur_processForm_str = res.processForm || '{}'
- // const x = JSON.parse(cur_processForm_str)
- // x[0].validate = [{ required: true, message: '请输入商品简介', trigger: 'blur' }]
- // processForm 动态表单
- // validateForm.value.rule = [...JSON.parse(cur_processForm_str) /*, { ...workflowItem, value: local_workflow }*/ /*, { type: 'input', field: 'test_3' }*/]
- const { formStructure } = JSON.parse(cur_processForm_str)
- EReditorRef.value.setData(formStructure)
- // validateForm.value.rule = JSON.parse(cur_processForm_str)
- })
- ]).finally(() => {
- validateForm.value.loading = false
- })
- </script>
- <style lang="scss">
- .local-launch_drawer-wrap {
- .el-drawer__header {
- display: flex;
- padding: 16px 24px;
- align-items: center;
- justify-content: space-between;
- background-color: var(--el-color-info-light-9);
- text-align: left;
- /* margin-right: 0; */
- margin-bottom: 0;
- }
- .el-drawer__close-btn {
- padding: 0;
- margin-right: -12px;
- }
- .el-drawer__body {
- position: relative;
- display: flex;
- flex-direction: column;
- }
- .el-drawer__footer {
- border-top: 1px solid var(--el-border-color-lighter);
- padding: 12px 24px;
- }
- .local_loading {
- position: absolute;
- left: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- z-index: 999;
- background: rgba(0, 0, 0, 0.05);
- }
- }
- </style>
- <style scoped lang="scss">
- .info-wrap {
- //display: flex;
- .form-wrap {
- flex: 1;
- }
- .timeline-wrap {
- //margin-left: 8px;
- flex-shrink: 0;
- padding-left: 60px;
- //padding: 0;
- //width: 360px;
- }
- }
- </style>
|