123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- <!-- 流程详情 -->
- <template>
- <div class="flow-detail-content">
- <div v-loading="validateForm.loading" class="flow-detail-container">
- <!-- 值为空 -->
- <div v-if="!currentTaskRow.instanceId" class="flow-empty-detail-box">
- <el-empty description="暂无数据" />
- </div>
- <!-- 值不为空 -->
- <div class="column-page-wrap" v-show="currentTaskRow.instanceId">
- <!-- 1、头部信息 -->
- <div class="flow-status-stamp">
- <div class="flow-stamp-container">
- <FlowStatusStamp :status="currentTaskRow.instanceState" />
- </div>
- </div>
- <div class="flow-header-box">
- <div class="flow-no">编号:{{ currentTaskRow.instanceId }}</div>
- <div class="action-area">
- <div class="action-item" @click="openPrintModal">
- <el-tooltip effect="dark" content="打印" placement="bottom">
- <el-icon :style="{ color: 'var(--el-color-primary)' }" :size="16"><Printer /> </el-icon>
- </el-tooltip>
- </div>
- </div>
- </div>
- <!-- 2、内容体 -->
- <div class="flow-detail-box">
- <!--头部-->
- <div class="header-box">
- <div class="summary-info">
- <div class="title">{{ currentTaskRow.modelContent?.name }}</div>
- <FlowStatusTag :status="currentTaskRow.instanceState" />
- </div>
- <div class="initiator-info">
- <FlowNodeAvatar :name="currentTaskRow.createBy" />
- <div class="begin-time">{{ formatTimestamp(currentTaskRow.createTime) }} 提交</div>
- </div>
- </div>
- <el-tabs v-model="tabName">
- <el-tab-pane v-for="v of tabList" :key="v.value" :label="v.label" :name="v.value" />
- </el-tabs>
- <!-- 审批信息 -->
- <div v-show="tabName === 'ApprovalInfo'" class="scroll-wrap">
- <!-- 系统表单-->
- <template v-if="currentTaskRow.processType === 'business' && currentObj?.formTemplate.type === 1">
- <component :is="dyVueComponent" ref="dyVueComponentRef" @form-valid="openComment('consentOrRefuseVisible', 'agree')"></component>
- </template>
- <!-- 表单设计 -->
- <template v-else>
- <div class="form-wrap self-Everright-formEditor">
- <er-form-preview
- v-show="validateForm.rule.length"
- ref="EReditorRef"
- :file-upload-u-r-i="uploadFileApi"
- :is-show-complete-button="false"
- />
- <LeNoData v-if="!validateForm.rule.length" message="表单无数据" />
- </div>
- </template>
- <div class="area-divider"></div>
- <!--审批流-->
- <el-timeline style="margin-left: 50px">
- <el-timeline-item v-for="active in activeData" :key="active.id" hollow :timestamp="active.local_timestamp">
- <template #dot>
- <FlowTypeDot :status="active.id ? 0 : 1" :type="active.type" :name="active.createBy" />
- </template>
- <div v-show="active.type === 0" class="timeline-box flex-1">
- <span style="color: #86909c; display: block; margin-bottom: 3px; padding-left: 4px">评论</span>
- <div class="flex flex-align-center">
- <div class="timeline-box-user flex-1">
- <span style="padding-left: 4px">{{ active.createBy }}</span>
- <div v-if="active.local_content" class="comment">
- <div class="comment-content">{{ active.local_content }}</div>
- </div>
- </div>
- <span class="timeline-box-date">{{ formatTimestamp(active.finishTime) }}</span>
- </div>
- </div>
- <div v-show="active.type !== 0" class="timeline-box flex-1">
- <span style="display: block" class="pl-1.5 mb-0">{{ active.taskName }}</span>
- <div class="flex flex-align-center">
- <div class="timeline-box-user flex-1">
- <span v-if="active.id" class="text-gray-500 pl-1.5">
- {{ active.createBy }}
- </span>
- <span v-if="active.type === 22">
- 发起的子流程<el-tooltip content="点击查看详情">
- <a class="el-button el-button--primary is-link" style="padding-top: 0" @click="lookSubProcess(active)">
- ({{ active.content.callProcess.split(':')[1] }})
- </a>
- </el-tooltip>
- </span>
- <div class="mt-[3px] flex flex-wrap gap-[6px]">
- <FlowNodeAvatar v-for="nodeUser in active.local_nodeUserList" :key="nodeUser.id" :name="nodeUser.name" />
- <FlowNodeAvatar v-for="nodeRole in active.local_nodeRoleList" :key="nodeRole.id" :name="nodeRole.name">
- <template #avatar>
- <svg-icon icon-class="flow-group" color="#fff" />
- </template>
- </FlowNodeAvatar>
- </div>
- <div v-if="active.local_content" class="comment">
- <div class="comment-content">{{ active.local_content }}</div>
- </div>
- </div>
- <span class="timeline-box-date">{{ formatTimestamp(active.finishTime) }}</span>
- </div>
- </div>
- </el-timeline-item>
- </el-timeline>
- </div>
- <!-- 流程图 -->
- <div v-show="tabName === 'FlowChart'" class="scroll-wrap">
- <div class="tags-desc">
- <!--0: 已执行 1正在执行-->
- <el-tag style="margin-left: 4px" type="success" size="small">已执行</el-tag>
- <el-tag style="margin-left: 4px" type="warning" size="small">执行中</el-tag>
- <el-tag style="margin-left: 4px" type="info" size="small">未执行</el-tag>
- </div>
- <ScWorkflow style="overflow-x: auto" :model-value="modelContentConfig" disabled />
- </div>
- </div>
- </div>
- </div>
- <!-- 打印审批流 -->
- <printer-dialog v-if="printerVisible" v-model="printerVisible" :currentTaskRow="currentTaskRow" :opts="printOpts"></printer-dialog>
- </div>
- </template>
- <script setup>
- import path from 'path'
- import useTaskProcessStore from '@/store/modules/taskProcess'
- import { computed, ref, nextTick, watch, markRaw, defineAsyncComponent, provide, onMounted } from 'vue'
- import FlowStatusStamp from '@/components/Flow/FlowStatusStamp.vue'
- import FlowStatusTag from '@/components/Flow/FlowStatusTag.vue'
- import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
- import FlowTypeDot from '@/components/Flow/FlowTypeDot.vue'
- import { ChatLineSquare, Check, Close, Switch, DArrowLeft, Plus, Minus, More, Printer } from '@element-plus/icons-vue'
- import { processTaskApprovalInfo, processClaimTaskApi } from '@/api/flow/processTask'
- import { formatTimestamp } from '@/utils/datetime'
- // import ReviewDialog from './reviewDialog'
- // import AddSignDialog from './addSignDialog'
- // import ConsentOrRefuseDialog from './consentOrRefuseDialog'
- // import DeliverToReviewDialog from './deliverToReviewDialog'
- // import LoseSignDialog from './loseSignDialog'
- // import RollbackDialog from './rollbackDialog'
- // import ViewProcessDialog from './ViewProcessDialog.vue'
- import PrinterDialog from '@/views/approve/components/printer.vue'
- // import PrinterDialog from './printer.vue'
- import { storeToRefs } from 'pinia'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { erFormPreview } from '@ER/formEditor'
- const modules = import.meta.glob('@/views/**/*.vue')
- const { VITE_APP_BASE_API } = import.meta.env
- const uploadFileApi = ref(`${VITE_APP_BASE_API}/v1/oss/upload`)
- import { package_modelContentConfig } from '../components/config.ts'
- import { useRoute } from 'vue-router'
- const route = useRoute()
- const props = defineProps({
- /**
- * pendingApproval 待审批
- * myApplication 我的申请
- * myReceived 我收到的
- * pendingClaim 认领任务
- * approved 已审批
- */
- currentTaskType: {
- type: String,
- default: ''
- }
- })
- const tabName = ref('ApprovalInfo')
- const tabList = computed(() => {
- const list = [
- {
- label: '审批信息',
- value: 'ApprovalInfo'
- },
- {
- label: '流程图',
- value: 'FlowChart'
- }
- ]
- /*// 待审批 去掉流程图 todo?
- if (props.currentTaskType === 'pendingApproval') {
- list.splice(1, 1)
- }*/
- return list
- })
- const currentTaskRow = ref({})
- // store值
- // const taskProcessInfo = useTaskProcessStore()
- // const { currentTaskRow } = storeToRefs(taskProcessInfo)
- // 各种操作弹窗显示隐藏 start
- const reviewVisible = ref(false)
- const addSignVisible = ref(false)
- const consentOrRefuseVisible = ref(false)
- const deliverToReviewVisible = ref(false)
- const loseSignVisible = ref(false)
- const rollbackVisible = ref(false)
- const printerVisible = ref(false)
- const printOpts = ref({})
- const allowReject = ref(false)
- const allowRevocation = ref(false)
- // 各种操作弹窗显示隐藏 end
- const activeData = ref([])
- const currentType = ref(null)
- const currentFormData = ref({})
- // 当前form 表单数据字符串
- let cur_processForm_str = ''
- let cur_formStructure = {}
- let cur_formData = {}
- const openPrintModal = () => {
- // innerTable.value = document.querySelector('.self-Everright-formEditor').innerHTML
- printOpts.value = {
- formStructure: cur_formStructure,
- formData: cur_formData
- }
- printerVisible.value = true
- }
- const EReditorRef = ref()
- const taskId = computed(() => {
- return currentTaskRow.value.taskId || ''
- })
- const validateForm = ref({
- rule: [],
- loading: false
- })
- // 允许转交
- const allowTransfer = ref(true)
- // 允许加减签
- const allowAppendNode = ref(true)
- // 允许回退
- const allowRollback = ref(true)
- const modelContentConfig = ref({})
- // 系统表单
- const dyVueComponent = ref(undefined)
- const dyVueComponentRef = ref()
- const dyVueForm = ref({})
- const currentObj = ref()
- provide('dyVueForm', dyVueForm) // 这里主要是存放动态的form的属性值
- /**
- * 详情按钮各个操作弹窗
- * @param visibleType 评论 拒绝 同意等
- */
- const openComment = async (visibleType, item) => {
- switch (visibleType) {
- case 'reviewVisible':
- currentType.value = item
- reviewVisible.value = !reviewVisible.value
- break
- case 'addSignVisible':
- addSignVisible.value = !addSignVisible.value
- break
- case 'consentOrRefuseVisible':
- currentFormData.value = {}
- if (item === 'agree') {
- const { processType } = currentTaskRow.value
- const flag = processType === 'business' && currentObj.value.formTemplate.type === 1 // 系统表单
- if (flag) {
- const formData = dyVueComponentRef.value.getComponentData()
- const saveData = {
- // 这里要调整真实的URL
- // formStructure: currentObj.value.formTemplate.pcUrl,
- formStructure: '@/views/flow/test/test1.vue',
- formData
- }
- currentFormData.value = { processForm: JSON.stringify(saveData) }
- } else {
- const formData = EReditorRef.value.getData()
- let processForm = JSON.parse(cur_processForm_str)
- processForm = { ...processForm, formData }
- currentFormData.value = { processForm: JSON.stringify(processForm) }
- }
- }
- currentType.value = item
- consentOrRefuseVisible.value = !consentOrRefuseVisible.value
- break
- case 'deliverToReviewVisible':
- deliverToReviewVisible.value = !deliverToReviewVisible.value
- break
- case 'loseSignVisible':
- loseSignVisible.value = !loseSignVisible.value
- break
- case 'rollbackVisible':
- rollbackVisible.value = !rollbackVisible.value
- break
- }
- }
- /**
- * 获取taskId对应的详情
- * @param taskId
- * 1、获取流程数据
- * 2、获取表单数据
- * 2.1 主流程、业务流程(type=0 表单设计)、子流程 使用EverightForm表单来渲染表单值
- * 2.2 业务流程(type=1 使用系统表单来渲染表单值)
- */
- const getTaskDetail = () => {
- // const cur = currentTaskRow.value || {}
- const { id } = route.query
- validateForm.value.loading = true
- processTaskApprovalInfo({
- // taskId: cur.taskId,
- // instanceId: cur.instanceId,
- // instanceState: cur.instanceState
- instanceId: id,
- })
- .then(data => {
- const activeList = data.processApprovals
- activeList.forEach(v => {
- v.local_timestamp = v.id && formatTimestamp(v.createTime)
- const _content = v.content
- v.local_nodeUserList = _content?.nodeUserList || []
- v.local_nodeRoleList = _content?.nodeRoleList || []
- v.local_content = _content?.opinion
- })
- activeData.value = activeList
- try {
- const modelContent = JSON.parse(data.modelContent || '{}')
- data.modelContent = modelContent
- const modelContent_config = modelContent.nodeConfig // ?? modelContent.childNode
- if (modelContent_config) {
- package_modelContentConfig(modelContent_config, data.renderNodes || {})
- }
- modelContentConfig.value = modelContent_config || {}
- window.modelContentConfig = modelContentConfig
- /* 允许转交 允许加减签 允许回退 start */
- if (props.currentTaskType === 'pendingApproval') {
- allowTransfer.value = data?.allowTransfer
- allowAppendNode.value = data?.allowAppendNode
- allowRollback.value = data?.allowRollback
- }
- if (data?.taskType !== 0) {
- allowReject.value = true
- }
- if (props.currentTaskType === 'myApplication') {
- allowRevocation.value = data.processSetting.allowRevocation
- }
- /* 允许转交 允许加减签 允许回退 end */
- /* 这里主要是表单设计的逻辑 start */
- let formContent = null
- const { processType } = currentTaskRow.value
- if (processType === 'business') {
- currentObj.value = data
- if (data?.formTemplate.type === 1) {
- dyVueForm.value = {
- dy: {
- name: '测试名字',
- region: 1,
- delivery: true,
- type: ['1'],
- resource: 'Sponsor',
- desc: '啦啦啦'
- }
- }
- // dyVueComponent.value = markRaw(defineAsyncComponent(() => import('@/views/flow/test/test1.vue')))
- if (data.formTemplate.pcUrl && modules[data.formTemplate.pcUrl]) {
- dyVueComponent.value = defineAsyncComponent(async () => await /* @vite-ignore */ modules[data.formTemplate.pcUrl]())
- }
- return
- } else {
- formContent = `{"formStructure":${data.formTemplate.content}}` || '{}'
- }
- } else {
- formContent = data.formContent
- }
- cur_processForm_str = formContent
- const { formStructure, formData } = JSON.parse(cur_processForm_str)
- const formConfig = data.formConfig || []
- cur_formStructure = formStructure // 存储一份
- let newFields = cur_formStructure.fields
- let newFieldsList = cur_formStructure.list
- // pendingApproval, 除了[待审批]这个状态, 其余只能读取
- // if (props.currentTaskType !== 'pendingApproval') {
- newFields.map(item1 => (item1.options.disabled = true))
- // }
- /* else {
- // data.formConfig
- newFields.forEach(item => {
- const matchingFormItem = formConfig.find(formItem => formItem.id == item.id)
- // 如果找到匹配的对象,且opera为'2',则删除newFields中的对象
- if (matchingFormItem && matchingFormItem.opera === '2') {
- newFields = newFields.filter(t => t.id !== item.id)
- } else if (matchingFormItem) {
- // 否则,将opera属性添加到newFields中的对象
- item.opera = matchingFormItem.opera
- // '0': 只读 '1': 编辑 '2': 隐藏
- item.options.disabled = ['0', '1'].indexOf(matchingFormItem.opera) > -1 ? !Number(matchingFormItem.opera) : false
- }
- })
- newFieldsList.forEach(item => {
- const matchingFormItem = formConfig.find(formItem => formItem.id == item.id)
- // 如果找到匹配的对象,且opera为'2',则删除newFields中的对象
- if (matchingFormItem && matchingFormItem.opera === '2') {
- newFieldsList = newFieldsList.filter(t => t.id !== item.id)
- }
- })
- }*/
- cur_formStructure = {
- ...cur_formStructure,
- fields: newFields,
- list: newFieldsList
- }
- cur_formData = formData
- console.log(cur_formStructure, '=/////====newFormStructure')
- console.log(formData, '=/////====newFormStructure')
- EReditorRef.value.setData(cur_formStructure, formData)
- validateForm.value.rule = newFields
- /* 这里主要是表单设计的逻辑 end */
- } catch (e) {
- console.error('解析 descItems 数据出现问题', e)
- // descItemsData.value.list = []
- validateForm.value.rule = []
- }
- currentTaskRow.value = data
- })
- .finally(() => {
- validateForm.value.loading = false
- })
- }
- onMounted(getTaskDetail)
- const processInfo = ref({ name: '', id: '', instanceId: '', visible: false })
- const lookSubProcess = item => {
- const [id, name, instanceId] = item.content.callProcess.split(':')
- processInfo.value = {
- name,
- id,
- instanceId,
- visible: true
- }
- }
- </script>
- <style scoped lang="scss">
- .column-page-wrap {
- background: transparent;
- }
- .flow-detail-content {
- height: 100%;
- overflow: hidden;
- background: var(--el-bg-color);
- border-radius: 6px;
- flex: 1;
- .flow-detail-container {
- display: flex;
- flex-direction: column;
- position: relative;
- z-index: 0;
- height: 100%;
- overflow: hidden;
- }
- // 通过、不通过样式
- .flow-status-stamp {
- position: absolute;
- right: 60px;
- top: 10px;
- z-index: 99;
- }
- // 头部
- .flow-header-box {
- font-weight: 400;
- font-size: 13px;
- border-bottom: 1px solid var(--el-border-color);
- padding: 0 20px;
- height: 39px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- color: var(--el-color-info);
- .action-area {
- display: flex;
- gap: 4px;
- .action-item {
- cursor: pointer;
- display: flex;
- align-items: center;
- }
- }
- }
- // 内容体
- .flow-detail-box {
- //height: calc(100% - 92px);
- //overflow: hidden;
- //overflow-y: auto;
- flex: 1;
- min-height: 0;
- padding: 0 20px;
- display: flex;
- flex-direction: column;
- height: 100%;
- .header-box {
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding-bottom: 10px;
- // padding-top: 20px;
- .summary-info {
- display: flex;
- align-items: center;
- padding-top: 10px;
- font-size: 24px;
- .title {
- font-family: PingFangSC-Semibold, PingFang SC;
- color: var(--el-text-color-primary);
- }
- }
- .initiator-info {
- display: flex;
- align-items: center;
- margin-top: 16px;
- .begin-time {
- margin-left: 16px;
- font-weight: 350;
- color: var(--el-text-color-placeholder);
- font-size: 13px;
- -webkit-user-select: none;
- user-select: none;
- }
- }
- }
- .area-divider {
- border-bottom: 1px solid var(--el-border-color);
- margin: 20px 0;
- //position: relative;
- }
- .scroll-wrap {
- position: relative;
- overflow: hidden;
- overflow-y: auto;
- flex: 1;
- .tags-desc {
- position: absolute;
- left: 6px;
- top: 10px;
- z-index: 1;
- }
- :deep(.zoom-scale) {
- position: absolute;
- top: 16px;
- right: 16px;
- }
- }
- }
- // 底部
- .flow-actions {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- height: 52px;
- //border-top: 1px solid var(--color-neutral-3);
- border-top: 1px solid var(--el-border-color-light);
- background: var(--el-bg-color);
- padding: 0 20px;
- }
- }
- .comment-content {
- user-select: none;
- margin-top: 4px;
- padding: 8px;
- border-radius: 4px;
- background-color: var(--el-bg-color-page);
- }
- .timeline-box {
- margin-left: 5px;
- }
- :deep(.el-timeline-item__timestamp) {
- margin-left: 6px;
- }
- // 修改everright 表单的样式
- .self-Everright-formEditor {
- :deep(.Everright-formEditor-selectElement) {
- padding: 0px 16px;
- }
- }
- </style>
|