|
@@ -0,0 +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 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 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>
|