Parcourir la source

feat: 审批列表UI调整 - 右侧详情

luoyali il y a 1 an
Parent
commit
03c1bb7104

+ 13 - 4
src/api/flow/processTask.ts

@@ -9,9 +9,9 @@ const api = {
 	pageMyApplication: '/v1/process-task/page-my-application',
 	pageApproved: '/v1/process-task/page-approved',
 	listHisTask: '/v1/process-task/list-his-task', // todo废弃了
-	consentTask: '/v1/process-task/approval-consent', // 审批同意
-	rejectionTask: '/v1/process-task/approval-rejection', // 审批拒绝
-	reviewTask: '/v1/process-task/approval-comment', // 评论审批
+	consentTask: '/v1/process-task/consent', // 审批同意
+	rejectionTask: '/v1/process-task/rejection', // 审批拒绝
+	reviewTask: '/v1/process-task/comment', // 评论审批
 	approvalInfo: '/v1/process-task/approval-info' // 审批详情
 }
 
@@ -103,6 +103,14 @@ export function processApprovalInfoApi(taskId: string): AxiosPromise {
 	})
 }
 
+// 查看审批信息
+export function processTaskApprovalInfo(taskId: string): AxiosPromise {
+	return request({
+		url: `/v1/process-task/approval-info-${taskId}`,
+		method: 'post'
+	})
+}
+
 export default {
 	processTaskPageMyApplicationApi,
 	processTaskPageApprovedApi,
@@ -110,5 +118,6 @@ export default {
 	processConsentTaskApi,
 	processRejectionTaskApi,
 	processReviewTaskApi,
-	processApprovalInfoApi
+	processApprovalInfoApi,
+	processTaskApprovalInfo
 }

+ 386 - 4
src/views/approve/components/approvedContent.vue

@@ -1,13 +1,395 @@
 <template>
+	<div class="flow-detail-content">
+		<div class="flow-detail-container">
+			<!-- 值为空 -->
+			<div v-if="false" class="flow-empty-detail-box">
+				<el-empty description="暂无数据" />
+			</div>
 
+			<!-- 值不为空 -->
+			<template v-if="true">
+				<!-- 1、头部信息 -->
+				<div class="flow-status-stamp">
+					<div class="flow-stamp-container">
+						<FlowStatusStamp :status="0" />
+					</div>
+				</div>
+
+				<div class="flow-header-box">
+					<div class="flow-no">编号:1759387444983226369</div>
+					<div class="action-area">
+						<div class="action-item"></div>
+					</div>
+				</div>
+				<!-- 2、内容体 -->
+				<div class="flow-detail-box">
+					<!--头部-->
+					<div class="header-box">
+						<div class="summary-info">
+							<div class="title">{{ taskObj.processName }}</div>
+							<FlowStatusTag :status="0" />
+						</div>
+						<div class="initiator-info">
+							<FlowNodeAvatar id="1" />
+							<div class="begin-time">{{ taskObj.createTime }} 提交</div>
+						</div>
+					</div>
+					<div class="area-divider"></div>
+
+					<!-- 表单 -->
+					<div v-loading="validateForm.loading" class="form-wrap">
+						<FormCreate v-show="validateForm.rule.length" v-model:api="validateForm.api" :option="validateForm.option" :rule="validateForm.rule" />
+						<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.taskName" placement="top">
+							<template #dot>
+								<FlowTypeDot :status="active.taskState" :type="active.taskType" />
+							</template>
+
+							<div class="timeline-box flex-1">
+								<div class="flex flex-align-center">
+									<div class="timeline-box-user flex-1">
+										<FlowNodeAvatar id="1" />
+										<div class="comment">
+											<div class="comment-content">{{ active.duration }}</div>
+										</div>
+									</div>
+									<span class="timeline-box-date">{{ formatTimestamp(active.finishTime) }}</span>
+								</div>
+							</div>
+						</el-timeline-item>
+					</el-timeline>
+				</div>
+
+				<!-- 3、底部操作按钮 -->
+				<div class="flow-actions">
+					<el-button :icon="ChatLineSquare" @click="openComment('reviewVisible')">评论</el-button>
+					<el-button :icon="Check" type="success" @click="openComment('consentOrRefuseVisible', 'agree')">同意</el-button>
+					<el-button :icon="Close" type="danger" @click="openComment('consentOrRefuseVisible', 'reject')">拒绝</el-button>
+					<el-dropdown style="margin-left: 12px">
+						<el-button :icon="More">更多</el-button>
+						<template #dropdown>
+							<el-dropdown-menu>
+								<el-dropdown-item @click.native="openComment('deliverToReviewVisible')">
+									<el-icon><DArrowLeft /></el-icon>
+									转交
+								</el-dropdown-item>
+								<el-dropdown-item @click.native="openComment('rollbackVisible')">
+									<el-icon><Switch /></el-icon>
+									回退
+								</el-dropdown-item>
+								<el-dropdown-item>
+									<el-icon><Plus /></el-icon>
+									加签
+								</el-dropdown-item>
+								<el-dropdown-item @click.native="openComment('loseSignVisible')">
+									<el-icon><Minus /></el-icon>
+									减签
+								</el-dropdown-item>
+							</el-dropdown-menu>
+						</template>
+					</el-dropdown>
+				</div>
+			</template>
+		</div>
+
+		<!-- 评论弹窗-->
+		<review-dialog v-if="reviewVisible" v-model="reviewVisible" :task-id="taskId"></review-dialog>
+
+		<!-- 加签弹窗 -->
+		<add-sign-dialog v-if="addSignVisible" v-model="addSignVisible" :task-id="taskId"></add-sign-dialog>
+
+		<!-- 同意或拒绝弹窗 -->
+		<consent-or-refuse-dialog
+			v-if="consentOrRefuseVisible"
+			v-model="consentOrRefuseVisible"
+			:task-id="taskId"
+			:current-type="currentDialog"
+		></consent-or-refuse-dialog>
+
+		<!-- 转交审批弹窗 -->
+		<deliver-to-review-dialog v-if="deliverToReviewVisible" v-model="deliverToReviewVisible" :task-id="taskId"></deliver-to-review-dialog>
+
+		<!-- 减签弹窗 -->
+		<lose-sign-dialog v-if="loseSignVisible" v-model="loseSignVisible" :task-id="taskId"></lose-sign-dialog>
+
+		<!-- 回退弹窗 -->
+		<rollback-dialog v-if="rollbackVisible" v-model="rollbackVisible" :task-id="taskId"></rollback-dialog>
+	</div>
 </template>
 
-<script>
-export default {
-	name: "approvedContent.vue"
+<script setup>
+import { computed, ref, onMounted, nextTick } 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 } from '@element-plus/icons-vue'
+import { processApprovalInfoApi, processTaskApprovalInfo } 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 viewForm from '@/utils/form'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	taskObj: {
+		type: Object,
+		default: () => ({})
+	}
+})
+const emit = defineEmits(['update:modelValue', 'successFn'])
+
+// 各种操作弹窗显示隐藏 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)
+// 各种操作弹窗显示隐藏 end
+const activeData = ref([])
+const currentDialog = ref(null)
+
+const visibleDialog = computed({
+	get() {
+		return props.modelValue
+	},
+	set(val) {
+		emit('update:modelValue', val)
+	}
+})
+
+const taskId = computed(() => {
+	return props.taskObj.taskId || ''
+})
+const FormCreate = viewForm.$form()
+const validateForm = ref({
+	api: {},
+	option: {
+		submitBtn: false
+	},
+	rule: [],
+	loading: false
+})
+
+// 关闭按钮
+const closeDrawer = () => {
+	emit('successFn')
+	emit('update:modelValue', false)
+}
+
+const handleCancel = () => {
+	closeDrawer()
+}
+
+// 操作按钮
+const openComment = (type, item) => {
+	switch (type) {
+		case 'reviewVisible':
+			reviewVisible.value = !reviewVisible.value
+			break
+		case 'addSignVisible':
+			addSignVisible.value = !addSignVisible.value
+			break
+		case 'consentOrRefuseVisible':
+			// 点击同意
+			if (item === 'agree') {
+				debugger
+				// 验证表单 todo...
+				const api = validateForm.value.api
+				api.validate((valid, fail) => {
+					if (valid) {
+						const values = api.formData()
+						console.warn(values, 'values')
+						//todo 表单验证通过
+					} else {
+						//todo 表单验证未通过
+					}
+				})
+				// return
+			}
+			currentDialog.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
+	}
 }
+
+nextTick(() => {
+	const cur = props.taskObj || {}
+	processApprovalInfoApi(cur.taskId).then(data => {
+		// console.log(JSON.stringify(data))
+		activeData.value = data
+	})
+	// 提交的表单 数据展示
+	validateForm.value.loading = true
+	processTaskApprovalInfo(cur.taskId)
+		.then(data => {
+			console.log(data, 'data.......')
+			// validateForm.value.origin = data
+			try {
+				/*descItemsData.value.list = JSON.parse(data.formContent).map(item => {
+					const showLabel = item.title
+					let showValue = item.local_value
+					const options = item.options
+					if (Array.isArray(options) && showValue !== undefined) {
+						if (Array.isArray(showValue)) {
+							showValue = showValue.reduce(val => {
+								const cur = options.find(option => option.value === val)
+								return cur?.label || val
+							}, [])
+						} else {
+							const cur = options.find(option => option.value === showValue)
+							showValue = cur?.label || showValue
+						}
+					}
+					return {
+						showLabel,
+						showValue
+					}
+				})*/
+				const forms = JSON.parse(data.formContent)
+				if (Array.isArray(forms)) {
+					validateForm.value.rule = forms
+					const api = validateForm.value.api
+					api.setValue(
+						forms.reduce((obj, item) => {
+							obj[item.field] = item.local_value
+							return obj
+						}, {})
+					)
+					api.nextTick(() => {
+						// 是否有编辑权限 操作
+						api.disabled(false /*true*/)
+					})
+				}
+			} catch (e) {
+				console.error('解析 descItems 数据出现问题', e)
+				// descItemsData.value.list = []
+				validateForm.value.rule = []
+			}
+		})
+		.finally(() => {
+			validateForm.value.loading = false
+		})
+})
 </script>
 
-<style scoped>
+<style scoped lang="scss">
+.flow-detail-content {
+	height: 100%;
+	overflow: hidden;
+	background: #fff;
+	border-radius: 6px;
+	flex: auto;
+	.flow-detail-container {
+		height: 100%;
+		overflow: hidden;
+		position: relative;
+	}
+
+	// 通过、不通过样式
+	.flow-status-stamp {
+		position: absolute;
+		right: 30px;
+		top: 30px;
+	}
+
+	// 头部
+	.flow-header-box {
+		font-weight: 400;
+		font-size: 13px;
+		border-bottom: 1px solid #e5e6ec;
+		padding: 0 20px;
+		height: 39px;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		color: #86909c;
+		.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;
+		padding: 0 20px;
+
+		.header-box {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			// padding-top: 20px;
+			.summary-info {
+				display: flex;
+				align-items: center;
+				.title {
+					font-size: 24px;
+					font-family: PingFangSC-Semibold, PingFang SC;
+					color: #1d2129;
+				}
+			}
+			.initiator-info {
+				display: flex;
+				align-items: center;
+				margin-top: 16px;
+				.begin-time {
+					margin-left: 16px;
+					font-weight: 350;
+					color: #86909c;
+					font-size: 13px;
+					-webkit-user-select: none;
+					user-select: none;
+				}
+			}
+		}
+		.area-divider {
+			border-bottom: 1px solid rgba(229, 230, 235, 1);
+			margin: 20px 0;
+			position: relative;
+		}
+	}
+
+	// 底部
+	.flow-actions {
+		display: flex;
+		align-items: center;
+		justify-content: end;
+		height: 52px;
+		border-top: 1px solid var(--color-neutral-3);
+		padding: 0 20px;
+	}
+}
 </style>

+ 49 - 3
src/views/approve/components/approvedItem.vue

@@ -2,13 +2,30 @@
 	<!-- 左侧列表 -->
 	<div class="flow-content">
 		<!-- 搜索条件 -->
+		<div class="search-box">
+			<div class="search-segment" style="flex: 1 1 0%">
+				<el-input v-model="input3" placeholder="流程名称">
+					<template #append>
+						<el-button :icon="Search" />
+					</template>
+				</el-input>
+			</div>
+			<div class="search-segment">
+				<el-tooltip effect="dark" content="重置" placement="top">
+					<el-button :icon="Refresh" />
+				</el-tooltip>
+			</div>
+			<div class="search-segment">
+				<el-button :icon="Filter" />
+			</div>
+		</div>
 		<!-- 内容值 -->
-		<div class="flow-list-box">
-			<div class="item-box">
+		<div v-infinite-scroll="load" class="flow-list-box">
+			<div v-for="i in count" :key="i" class="item-box" :class="[i === 2 ? 'item-box-choosed' : '']">
 				<div class="flow-card-box flow-card-box-hoverable">
 					<!--头部-->
 					<div class="header">
-						<el-text tag="b">我是Bold</el-text>
+						<el-text tag="b">我是Bold {{ i }}</el-text>
 						<el-tag type="primary">待审核</el-tag>
 					</div>
 					<!--操作类容-->
@@ -30,6 +47,13 @@
 
 <script setup>
 import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
+import { Search, Filter, Refresh } from '@element-plus/icons-vue'
+
+import { ref } from 'vue'
+const count = ref(0)
+const load = () => {
+	count.value += 2
+}
 </script>
 
 <style scoped lang="scss">
@@ -41,6 +65,23 @@ import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
 	background: var(--el-bg-color);
 	border-radius: 6px;
 
+	// 搜搜条件
+	.search-box {
+		display: flex;
+		align-items: center;
+		height: 56px;
+		padding: 0 12px;
+
+		.search-segment + .search-segment {
+			margin-left: 4px;
+		}
+		//> .el-button {
+		//	padding: 0;
+		//	width: 32px;
+		//	background-color: var(--el-fill-color-light);
+		//}
+	}
+
 	.flow-list-box {
 		background-color: #fff;
 		padding: 0 12px;
@@ -50,6 +91,11 @@ import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
 		will-change: scroll-position;
 		.item-box {
 			margin-bottom: 12px;
+			&.item-box-choosed {
+				.flow-card-box {
+					border-color: var(--el-color-primary);
+				}
+			}
 		}
 		.flow-card-box {
 			-webkit-user-select: none;

+ 1 - 1
src/views/approve/pendingClaim/index.vue

@@ -31,6 +31,6 @@ import ApprovedContent from '../components/approvedContent.vue'
 
 .warp-right {
 	flex: 1; /*这里设置为占比1,填充满剩余空间*/
-	border: 1px solid #ff4400;
+	//border: 1px solid #ff4400;
 }
 </style>