|
@@ -0,0 +1,353 @@
|
|
|
|
+<template>
|
|
|
|
+ <!-- 查看流程表单 查看 -->
|
|
|
|
+ <el-dialog v-model="localVisible" class="le-dialog view-process-dialog local-view-process-dialog" :title="`${processInfo.name}`" width="800">
|
|
|
|
+ <div class="flow-detail-content">
|
|
|
|
+ <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">
|
|
|
|
+ <!-- 表单 -->
|
|
|
|
+ <div v-loading="validateForm.loading" class="form-wrap self-Everright-formEditor">
|
|
|
|
+ <erFormPreview v-show="validateForm.rule.length" ref="EReditorRef" :is-show-complete-button="false" />
|
|
|
|
+ <!-- :file-upload-u-r-i="uploadFileApi"-->
|
|
|
|
+ <LeNoData v-if="!validateForm.rule.length" message="表单无数据" />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <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" @click="lookSubProcess(active)">
|
|
|
|
+ ({{ active.content.callProcess.split(':')[1] }})
|
|
|
|
+ </a>
|
|
|
|
+ </el-tooltip>
|
|
|
|
+ </span>
|
|
|
|
+ <div style="display: flex; gap: 6px; margin-top: 3px">
|
|
|
|
+ <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="danger" 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>
|
|
|
|
+ <template #footer>
|
|
|
|
+ <el-button @click="closeDialog">关 闭</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import { computed, onMounted, reactive, ref } from 'vue'
|
|
|
|
+import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
|
|
|
|
+import FlowTypeDot from '@/components/Flow/FlowTypeDot.vue'
|
|
|
|
+import { erFormPreview } from '@ER/formEditor'
|
|
|
|
+import { processTaskApprovalInfo } from '@/api/flow/processTask'
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
+import { formatTimestamp } from '@/utils/datetime.ts'
|
|
|
|
+import { package_modelContentConfig } from './config.ts'
|
|
|
|
+
|
|
|
|
+const props = defineProps({
|
|
|
|
+ modelValue: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: false
|
|
|
|
+ },
|
|
|
|
+ processInfo: {
|
|
|
|
+ type: Object as { name: string; id: string },
|
|
|
|
+ default: () => ({})
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+const tabList = [
|
|
|
|
+ {
|
|
|
|
+ label: '审批信息',
|
|
|
|
+ value: 'ApprovalInfo'
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ label: '流程图',
|
|
|
|
+ value: 'FlowChart'
|
|
|
|
+ }
|
|
|
|
+]
|
|
|
|
+const tabName = ref('ApprovalInfo')
|
|
|
|
+const validateForm = ref({
|
|
|
|
+ rule: [],
|
|
|
|
+ loading: false
|
|
|
|
+})
|
|
|
|
+const EReditorRef = ref()
|
|
|
|
+const modelContentConfig = ref({})
|
|
|
|
+const activeData = ref([])
|
|
|
|
+
|
|
|
|
+const $myEmit = defineEmits(['update:modelValue'])
|
|
|
|
+
|
|
|
|
+const queryDetail = () => {
|
|
|
|
+ // processTaskApprovalInfo({ instanceId: props.processInfo.id })
|
|
|
|
+ processTaskApprovalInfo({ instanceId: '1803659392853921794' })
|
|
|
|
+ .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 || '{}')
|
|
|
|
+ const modelContent_config = modelContent.nodeConfig // ?? modelContent.childNode
|
|
|
|
+ if (modelContent_config) {
|
|
|
|
+ package_modelContentConfig(modelContent_config, data.renderNodes || {})
|
|
|
|
+ }
|
|
|
|
+ modelContentConfig.value = modelContent_config || {}
|
|
|
|
+ // 当前form 表单数据字符串
|
|
|
|
+ const cur_processForm_str = data.formContent || '{}'
|
|
|
|
+ const { formStructure, formData } = JSON.parse(cur_processForm_str)
|
|
|
|
+ EReditorRef.value.setData(formStructure, formData)
|
|
|
|
+ validateForm.value.rule = formStructure.fields
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.error('解析 modelContent 数据出现问题', e)
|
|
|
|
+ validateForm.value.rule = []
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ .finally(() => {
|
|
|
|
+ validateForm.value.loading = false
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+window.test_validateForm = validateForm
|
|
|
|
+onMounted(queryDetail)
|
|
|
|
+const closeDialog = () => {
|
|
|
|
+ $myEmit('update:modelValue', false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const localVisible = computed({
|
|
|
|
+ get() {
|
|
|
|
+ return props.modelValue
|
|
|
|
+ },
|
|
|
|
+ set(val) {
|
|
|
|
+ $myEmit('update:modelValue', val)
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
+.flow-detail-content {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ height: 100%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
+ //border-radius: 6px;
|
|
|
|
+ flex: auto;
|
|
|
|
+ .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-detail-container {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ position: relative;
|
|
|
|
+ height: 100%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ }*/
|
|
|
|
+ /*!/ 通过、不通过样式
|
|
|
|
+ .flow-status-stamp {
|
|
|
|
+ position: absolute;
|
|
|
|
+ right: 10px;
|
|
|
|
+ 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;
|
|
|
|
+ padding: 4px;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ width: fit-content;
|
|
|
|
+ height: fit-content;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 内容体
|
|
|
|
+ .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>
|
|
|
|
+<style lang="scss">
|
|
|
|
+// 全局样式
|
|
|
|
+.local-view-process-dialog {
|
|
|
|
+ .el-dialog__body {
|
|
|
|
+ height: calc(100vh - 160px);
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|