Quellcode durchsuchen

feat: 新增 子流程查看功能

lanceJiang vor 1 Jahr
Ursprung
Commit
dc32ed97be

+ 353 - 0
src/views/approve/components/ViewProcessDialog.vue

@@ -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>

+ 15 - 6
src/views/approve/components/approvedContent.vue

@@ -78,10 +78,10 @@
 											<span v-if="active.id" class="text-gray-500 pl-1.5">
 												{{ active.createBy }}
 											</span>
-											<span v-if="active.type === 22" class="text-gray-500">
-												<el-tooltip content="点击查看详情">
-													<a @click="lookSubProcess(active.content.callProcess.split(':')[0])">
-														发起的子流程( {{ active.content.callProcess.split(':')[1] }} )
+											<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>
@@ -214,6 +214,8 @@
 
 		<!-- 回退弹窗 -->
 		<rollback-dialog v-if="rollbackVisible" v-model="rollbackVisible" :task-id="taskId" @success-cb="closeDetailEv"></rollback-dialog>
+		<!-- 查看子流程表单 -->
+		<ViewProcessDialog v-if="processInfo.visible" v-model="processInfo.visible" :processInfo="processInfo" />
 	</div>
 </template>
 
@@ -233,6 +235,7 @@ import ConsentOrRefuseDialog from './consentOrRefuseDialog'
 import DeliverToReviewDialog from './deliverToReviewDialog'
 import LoseSignDialog from './loseSignDialog'
 import RollbackDialog from './rollbackDialog'
+import ViewProcessDialog from './ViewProcessDialog.vue'
 import { storeToRefs } from 'pinia'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { erFormPreview } from '@ER/formEditor'
@@ -478,8 +481,14 @@ const getTaskDetail = () => {
 		})
 }
 
-const lookSubProcess = id => {
-	alert('子流程ID=' + id)
+const processInfo = ref({ name: '', id: '', visible: false })
+const lookSubProcess = item => {
+	const [id, name] = item.content.callProcess.split(':')
+	processInfo.value = {
+		name,
+		id,
+		visible: true
+	}
 }
 
 /**