Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
commit
397c09a195
|
@ -162,9 +162,11 @@ const getApprovalDetail = async (row: any) => {
|
||||||
startUserSelectTasks.value = data.activityNodes?.filter(
|
startUserSelectTasks.value = data.activityNodes?.filter(
|
||||||
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
|
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
|
||||||
)
|
)
|
||||||
|
if (startUserSelectTasks.value?.length > 0) {
|
||||||
for (const node of startUserSelectTasks.value) {
|
for (const node of startUserSelectTasks.value) {
|
||||||
startUserSelectAssignees.value[node.id] = []
|
startUserSelectAssignees.value[node.id] = []
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取审批节点,显示 Timeline 的数据
|
// 获取审批节点,显示 Timeline 的数据
|
||||||
activityNodes.value = data.activityNodes
|
activityNodes.value = data.activityNodes
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
v-for="category in categoryList"
|
v-for="category in availableCategories"
|
||||||
:key="category.code"
|
:key="category.code"
|
||||||
class="flex items-center p-10px cursor-pointer text-14px rounded-md"
|
class="flex items-center p-10px cursor-pointer text-14px rounded-md"
|
||||||
:class="categoryActive.code === category.code ? 'text-#3e7bff bg-#e8eeff' : ''"
|
:class="categoryActive.code === category.code ? 'text-#3e7bff bg-#e8eeff' : ''"
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="19">
|
<el-col :span="19">
|
||||||
<el-scrollbar ref="scrollWrapper" height="700">
|
<el-scrollbar ref="scrollWrapper" height="700" @scroll="handleScroll">
|
||||||
<div
|
<div
|
||||||
class="mb-20px pl-10px"
|
class="mb-20px pl-10px"
|
||||||
v-for="(definitions, categoryCode) in processDefinitionGroup"
|
v-for="(definitions, categoryCode) in processDefinitionGroup"
|
||||||
|
@ -137,10 +137,6 @@ const getCategoryList = async () => {
|
||||||
try {
|
try {
|
||||||
// 流程分类
|
// 流程分类
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||||
// 选中首个分类
|
|
||||||
if (categoryList.value.length > 0) {
|
|
||||||
categoryActive.value = categoryList.value[0]
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +150,11 @@ const getProcessDefinitionList = async () => {
|
||||||
})
|
})
|
||||||
// 初始化过滤列表为全部流程定义
|
// 初始化过滤列表为全部流程定义
|
||||||
filteredProcessDefinitionList.value = processDefinitionList.value
|
filteredProcessDefinitionList.value = processDefinitionList.value
|
||||||
|
|
||||||
|
// 在获取完所有数据后,设置第一个有效分类为激活状态
|
||||||
|
if (availableCategories.value.length > 0 && !categoryActive.value?.code) {
|
||||||
|
categoryActive.value = availableCategories.value[0]
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,10 +221,62 @@ const handleSelect = async (row, formVariables?) => {
|
||||||
processDefinitionDetailRef.value?.initProcessInfo(row, formVariables)
|
processDefinitionDetailRef.value?.initProcessInfo(row, formVariables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 处理滚动事件 */
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
// 直接使用事件对象获取滚动位置
|
||||||
|
const scrollTop = e.scrollTop
|
||||||
|
|
||||||
|
// 获取所有分类区域的位置信息
|
||||||
|
const categoryPositions = categoryList.value
|
||||||
|
.map((category) => {
|
||||||
|
const categoryRef = proxy.$refs[`category-${category.code}`]
|
||||||
|
if (categoryRef?.[0]) {
|
||||||
|
return {
|
||||||
|
code: category.code,
|
||||||
|
offsetTop: categoryRef[0].offsetTop,
|
||||||
|
height: categoryRef[0].offsetHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
// 查找当前滚动位置对应的分类
|
||||||
|
let currentCategory = categoryPositions[0]
|
||||||
|
for (const position of categoryPositions) {
|
||||||
|
// 为了更好的用户体验,可以添加一个缓冲区域(比如 50px)
|
||||||
|
if (scrollTop >= position.offsetTop - 50) {
|
||||||
|
currentCategory = position
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新当前 active 的分类
|
||||||
|
if (currentCategory && categoryActive.value.code !== currentCategory.code) {
|
||||||
|
categoryActive.value = categoryList.value.find((c) => c.code === currentCategory.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 过滤出有流程的分类列表 */
|
||||||
|
const availableCategories = computed(() => {
|
||||||
|
if (!categoryList.value?.length || !processDefinitionGroup.value) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有有流程的分类代码
|
||||||
|
const availableCategoryCodes = Object.keys(processDefinitionGroup.value)
|
||||||
|
|
||||||
|
// 过滤出有流程的分类
|
||||||
|
return categoryList.value.filter(category =>
|
||||||
|
availableCategoryCodes.includes(category.code)
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -40,14 +40,22 @@ watch(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.box-card {
|
.box-card {
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 0;
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.process-viewer) {
|
:deep(.el-card__body) {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.process-viewer) {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
min-height: 500px;
|
min-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" class="mb-20px">
|
<div v-loading="loading" class="process-viewer-container">
|
||||||
<SimpleProcessViewer
|
<SimpleProcessViewer
|
||||||
:flow-node="simpleModel"
|
:flow-node="simpleModel"
|
||||||
:tasks="tasks"
|
:tasks="tasks"
|
||||||
|
@ -154,8 +154,15 @@ const setSimpleModelNodeTaskStatus = (
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.process-viewer) {
|
.process-viewer-container {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.process-viewer) {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
min-height: 500px;
|
min-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
<img class="w-full h-full" :src="getApprovalNodeImg(activity.nodeType)" alt="" />
|
<img class="w-full h-full" :src="getApprovalNodeImg(activity.nodeType)" alt="" />
|
||||||
<div
|
<div
|
||||||
v-if="showStatusIcon"
|
v-if="showStatusIcon"
|
||||||
class="position-absolute top-17px left-17px rounded-full flex items-center p-2px"
|
class="position-absolute top-17px left-17px rounded-full flex items-center p-1px border-2 border-white border-solid"
|
||||||
:style="{ backgroundColor: getApprovalNodeColor(activity.status) }"
|
:style="{ backgroundColor: getApprovalNodeColor(activity.status) }"
|
||||||
>
|
>
|
||||||
<el-icon :size="12" color="#fff">
|
<el-icon :size="11" color="#fff">
|
||||||
<component :is="getApprovalNodeIcon(activity.status, activity.nodeType)" />
|
<component :is="getApprovalNodeIcon(activity.status, activity.nodeType)" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,10 +106,10 @@
|
||||||
<!-- 信息:任务 ICON -->
|
<!-- 信息:任务 ICON -->
|
||||||
<div
|
<div
|
||||||
v-if="showStatusIcon && onlyStatusIconShow.includes(task.status)"
|
v-if="showStatusIcon && onlyStatusIconShow.includes(task.status)"
|
||||||
class="position-absolute top-19px left-23px rounded-full flex items-center p-2px"
|
class="position-absolute top-19px left-23px rounded-full flex items-center p-1px border-2 border-white border-solid"
|
||||||
:style="{ backgroundColor: statusIconMap2[task.status]?.color }"
|
:style="{ backgroundColor: statusIconMap2[task.status]?.color }"
|
||||||
>
|
>
|
||||||
<Icon :size="12" :icon="statusIconMap2[task.status]?.icon" color="#FFFFFF" />
|
<Icon :size="11" :icon="statusIconMap2[task.status]?.icon" color="#FFFFFF" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,10 +140,10 @@
|
||||||
<!-- 信息:任务 ICON -->
|
<!-- 信息:任务 ICON -->
|
||||||
<div
|
<div
|
||||||
v-if="showStatusIcon"
|
v-if="showStatusIcon"
|
||||||
class="position-absolute top-19px left-23px rounded-full flex items-center p-2px"
|
class="position-absolute top-20px left-24px rounded-full flex items-center p-1px border-2 border-white border-solid"
|
||||||
:style="{ backgroundColor: statusIconMap2['-1']?.color }"
|
:style="{ backgroundColor: statusIconMap2['-1']?.color }"
|
||||||
>
|
>
|
||||||
<Icon :size="12" :icon="statusIconMap2['-1']?.icon" color="#FFFFFF" />
|
<Icon :size="11" :icon="statusIconMap2['-1']?.icon" color="#FFFFFF" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -128,6 +128,7 @@ import { formatDate } from '@/utils/formatTime'
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { BpmModelType } from '@/utils/constants'
|
import { BpmModelType } from '@/utils/constants'
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||||
|
import { registerComponent } from '@/utils/routerHelper'
|
||||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
|
@ -228,6 +229,9 @@ const getApprovalDetail = async () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
// 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
|
||||||
|
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取审批节点,显示 Timeline 的数据
|
// 获取审批节点,显示 Timeline 的数据
|
||||||
|
@ -319,9 +323,12 @@ $process-header-height: 194px;
|
||||||
$process-header-height - 40px
|
$process-header-height - 40px
|
||||||
);
|
);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
:deep(.box-card) {
|
:deep(.box-card) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
.el-card__body {
|
.el-card__body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
Loading…
Reference in New Issue