|
|
@@ -1,218 +1,207 @@
|
|
|
<template>
|
|
|
- <view class="mx-24rpx mt-24rpx overflow-hidden rounded-16rpx bg-white">
|
|
|
- <view class="p-24rpx">
|
|
|
- <view class="mb-16rpx flex">
|
|
|
- <text class="text-28rpx text-[#333] font-bold">审批进度</text>
|
|
|
- </view>
|
|
|
- <!-- 遍历每个审批节点 -->
|
|
|
- <view
|
|
|
- v-for="(activity, index) in activityNodes"
|
|
|
- :key="activity.id || index"
|
|
|
- class="relative pb-24rpx pl-60rpx"
|
|
|
- >
|
|
|
- <!-- 时间线圆点 -->
|
|
|
- <view
|
|
|
- class="absolute left-12rpx top-8rpx h-36rpx w-36rpx flex items-center justify-center rounded-full bg-blue-500"
|
|
|
- >
|
|
|
- <!-- 节点类型图标 -->
|
|
|
- <wd-icon
|
|
|
- :name="getApprovalNodeTypeIcon(activity.nodeType)"
|
|
|
- size="20rpx"
|
|
|
- color="white"
|
|
|
- />
|
|
|
- </view>
|
|
|
+ <!-- 遍历每个审批节点 -->
|
|
|
+ <view
|
|
|
+ v-for="(activity, index) in activityNodes"
|
|
|
+ :key="activity.id || index"
|
|
|
+ class="relative pb-24rpx pl-80rpx"
|
|
|
+ >
|
|
|
+ <!-- 时间线圆点 -->
|
|
|
+ <view
|
|
|
+ class="absolute left-12rpx top-8rpx h-52rpx w-52rpx flex items-center justify-center rounded-full bg-blue-500"
|
|
|
+ >
|
|
|
+ <!-- 节点类型图标 -->
|
|
|
+ <wd-icon
|
|
|
+ :name="getApprovalNodeTypeIcon(activity.nodeType)"
|
|
|
+ size="32rpx"
|
|
|
+ color="white"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 状态小图标 -->
|
|
|
- <view
|
|
|
- v-if="showStatusIcon"
|
|
|
- class="absolute left-38rpx top-32rpx h-12rpx w-12rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
- :style="{ backgroundColor: getApprovalNodeColor(activity.status) }"
|
|
|
- >
|
|
|
- <wd-icon
|
|
|
- :name="getApprovalNodeIcon(activity.status, activity.nodeType)"
|
|
|
- size="12rpx"
|
|
|
- color="white"
|
|
|
- />
|
|
|
+ <!-- 状态小图标 -->
|
|
|
+ <view
|
|
|
+ v-if="showStatusIcon"
|
|
|
+ class="absolute left-48rpx top-44rpx h-16rpx w-16rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
+ :style="{ backgroundColor: getApprovalNodeColor(activity.status) }"
|
|
|
+ >
|
|
|
+ <wd-icon
|
|
|
+ :name="getApprovalNodeIcon(activity.status, activity.nodeType)"
|
|
|
+ size="12rpx"
|
|
|
+ color="white"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 连接线 -->
|
|
|
+ <view
|
|
|
+ v-if="index < activityNodes.length - 1"
|
|
|
+ class="absolute bottom-0 left-38rpx top-64rpx w-2rpx bg-[#e5e5e5]"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 节点内容 -->
|
|
|
+ <view class="ml-8rpx">
|
|
|
+ <!-- 第一行:节点名称、时间 -->
|
|
|
+ <view class="mb-8rpx flex items-center justify-between">
|
|
|
+ <view class="flex items-center">
|
|
|
+ <text class="text-28rpx text-[#333] font-bold">{{ activity.name }}</text>
|
|
|
+ <text v-if="activity.status === BpmTaskStatusEnum.SKIP" class="ml-8rpx text-24rpx text-[#999]">
|
|
|
+ 【跳过】
|
|
|
+ </text>
|
|
|
</view>
|
|
|
+ <text
|
|
|
+ v-if="activity.status !== BpmTaskStatusEnum.NOT_START && getApprovalNodeTime(activity)"
|
|
|
+ class="text-22rpx text-[#999]"
|
|
|
+ >
|
|
|
+ {{ getApprovalNodeTime(activity) }}
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 连接线 -->
|
|
|
- <view
|
|
|
- v-if="index < activityNodes.length - 1"
|
|
|
- class="absolute bottom-0 left-28rpx top-48rpx w-2rpx bg-[#e5e5e5]"
|
|
|
- />
|
|
|
-
|
|
|
- <!-- 节点内容 -->
|
|
|
- <view class="ml-8rpx">
|
|
|
- <!-- 第一行:节点名称、时间 -->
|
|
|
- <view class="mb-8rpx flex items-center justify-between">
|
|
|
- <view class="flex items-center">
|
|
|
- <text class="text-28rpx text-[#333] font-bold">{{ activity.name }}</text>
|
|
|
- <text v-if="activity.status === BpmTaskStatusEnum.SKIP" class="ml-8rpx text-24rpx text-[#999]">
|
|
|
- 【跳过】
|
|
|
- </text>
|
|
|
- </view>
|
|
|
- <text
|
|
|
- v-if="activity.status !== BpmTaskStatusEnum.NOT_START && getApprovalNodeTime(activity)"
|
|
|
- class="text-22rpx text-[#999]"
|
|
|
- >
|
|
|
- {{ getApprovalNodeTime(activity) }}
|
|
|
- </text>
|
|
|
- </view>
|
|
|
+ <!-- 子流程节点 -->
|
|
|
+ <view v-if="activity.nodeType === BpmNodeTypeEnum.CHILD_PROCESS_NODE" class="mb-16rpx">
|
|
|
+ <wd-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ size="small"
|
|
|
+ :disabled="!activity.processInstanceId"
|
|
|
+ @click="handleChildProcess(activity)"
|
|
|
+ >
|
|
|
+ 查看子流程
|
|
|
+ </wd-button>
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 子流程节点 -->
|
|
|
- <view v-if="activity.nodeType === BpmNodeTypeEnum.CHILD_PROCESS_NODE" class="mb-16rpx">
|
|
|
- <wd-button
|
|
|
- type="primary"
|
|
|
- plain
|
|
|
- size="small"
|
|
|
- :disabled="!activity.processInstanceId"
|
|
|
- @click="handleChildProcess(activity)"
|
|
|
+ <!-- 需要自定义选择审批人 -->
|
|
|
+ <view v-if="shouldShowCustomUserSelect(activity)" class="mb-16rpx">
|
|
|
+ <view class="flex flex-wrap items-center">
|
|
|
+ <!-- 添加用户按钮 -->
|
|
|
+ <UserPicker
|
|
|
+ :model-value="getSelectedUserIds(activity.id)"
|
|
|
+ type="checkbox"
|
|
|
+ use-default-slot
|
|
|
+ @confirm="(users) => handleCustomUserSelectConfirm(activity.id, users)"
|
|
|
+ >
|
|
|
+ <view
|
|
|
+ class="mb-8rpx mr-16rpx h-48rpx w-48rpx flex items-center justify-center border-indigo-500 rounded-lg border-solid"
|
|
|
>
|
|
|
- 查看子流程
|
|
|
- </wd-button>
|
|
|
+ <wd-icon name="user-add" size="32rpx" color="blue" />
|
|
|
+ </view>
|
|
|
+ </UserPicker>
|
|
|
+ <!-- 已选择的用户 -->
|
|
|
+ <view
|
|
|
+ v-for="(user, userIndex) in customApproveUsers[activity.id]"
|
|
|
+ :key="user.id || userIndex"
|
|
|
+ class="mb-8rpx mr-16rpx flex items-center rounded-32rpx bg-[#f5f5f5] pr-16rpx"
|
|
|
+ >
|
|
|
+ <view class="mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
+ {{ user.nickname?.[0] || '?' }}
|
|
|
+ </view>
|
|
|
+ <text class="text-24rpx text-[#333]">{{ user.nickname }}</text>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 需要自定义选择审批人 -->
|
|
|
- <view v-if="shouldShowCustomUserSelect(activity)" class="mb-16rpx">
|
|
|
- <view class="flex flex-wrap items-center">
|
|
|
- <!-- 添加用户按钮 -->
|
|
|
- <view
|
|
|
- class="mb-8rpx mr-16rpx h-64rpx w-64rpx flex items-center justify-center rounded-full bg-[#1890ff] text-white"
|
|
|
- @click="handleSelectUser(activity.id, customApproveUsers[activity.id] || [])"
|
|
|
- >
|
|
|
- <wd-icon name="add-outline" size="32rpx" color="white" />
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 已选择的用户 -->
|
|
|
- <view
|
|
|
- v-for="(user, userIndex) in customApproveUsers[activity.id]"
|
|
|
- :key="user.id || userIndex"
|
|
|
- class="mb-8rpx mr-16rpx flex items-center rounded-32rpx bg-[#f5f5f5] pr-16rpx"
|
|
|
- >
|
|
|
- <view class="mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
- {{ user.nickname?.[0] || '?' }}
|
|
|
+ <!-- 审批人员列表 -->
|
|
|
+ <view v-else class="mb-16rpx">
|
|
|
+ <!-- 情况一:遍历每个审批节点下的【进行中】task 任务 -->
|
|
|
+ <view v-if="activity.tasks && activity.tasks.length > 0">
|
|
|
+ <view
|
|
|
+ v-for="(task, taskIndex) in activity.tasks"
|
|
|
+ :key="taskIndex"
|
|
|
+ class="mb-16rpx"
|
|
|
+ >
|
|
|
+ <!-- 审批人信息 -->
|
|
|
+ <view v-if="task.assigneeUser || task.ownerUser" class="mb-8rpx flex items-center">
|
|
|
+ <!-- TODO @jason 显示用户头像 -->
|
|
|
+ <view class="relative mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
+ {{ (task.assigneeUser?.nickname || task.ownerUser?.nickname)?.[0] || '?' }}
|
|
|
+
|
|
|
+ <!-- 任务状态小图标 -->
|
|
|
+ <view
|
|
|
+ v-if="showStatusIcon && shouldShowTaskStatusIcon(task.status)"
|
|
|
+ class="absolute right--4rpx top-36rpx h-16rpx w-16rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
+ :style="{ backgroundColor: getApprovalNodeColor(task.status) }"
|
|
|
+ >
|
|
|
+ <wd-icon
|
|
|
+ :name="getApprovalNodeIcon(task.status, activity.nodeType)"
|
|
|
+ size="12rpx"
|
|
|
+ color="white"
|
|
|
+ />
|
|
|
</view>
|
|
|
- <text class="text-24rpx text-[#333]">{{ user.nickname }}</text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
|
|
|
- <!-- 审批人员列表 -->
|
|
|
- <view v-else class="mb-16rpx">
|
|
|
- <!-- 情况一:遍历每个审批节点下的【进行中】task 任务 -->
|
|
|
- <view v-if="activity.tasks && activity.tasks.length > 0">
|
|
|
- <view
|
|
|
- v-for="(task, taskIndex) in activity.tasks"
|
|
|
- :key="taskIndex"
|
|
|
- class="mb-16rpx"
|
|
|
- >
|
|
|
- <!-- 审批人信息 -->
|
|
|
- <view v-if="task.assigneeUser || task.ownerUser" class="mb-8rpx flex items-center">
|
|
|
- <!-- TODO @jason 显示用户头像 -->
|
|
|
- <view class="relative mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
- {{ (task.assigneeUser?.nickname || task.ownerUser?.nickname)?.[0] || '?' }}
|
|
|
-
|
|
|
- <!-- 任务状态小图标 -->
|
|
|
- <view
|
|
|
- v-if="showStatusIcon && shouldShowTaskStatusIcon(task.status)"
|
|
|
- class="absolute right--4rpx top-36rpx h-16rpx w-16rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
- :style="{ backgroundColor: getApprovalNodeColor(task.status) }"
|
|
|
+ <view class="flex-1">
|
|
|
+ <view class="flex items-center justify-between">
|
|
|
+ <view class="flex items-center">
|
|
|
+ <text class="text-26rpx text-[#333]">
|
|
|
+ {{ task.assigneeUser?.nickname || task.ownerUser?.nickname }}
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ v-if="task.assigneeUser?.deptName || task.ownerUser?.deptName"
|
|
|
+ class="ml-8rpx text-22rpx text-[#999]"
|
|
|
>
|
|
|
- <wd-icon
|
|
|
- :name="getApprovalNodeIcon(task.status, activity.nodeType)"
|
|
|
- size="12rpx"
|
|
|
- color="white"
|
|
|
- />
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="flex-1">
|
|
|
- <view class="flex items-center justify-between">
|
|
|
- <view class="flex items-center">
|
|
|
- <text class="text-26rpx text-[#333]">
|
|
|
- {{ task.assigneeUser?.nickname || task.ownerUser?.nickname }}
|
|
|
- </text>
|
|
|
- <text
|
|
|
- v-if="task.assigneeUser?.deptName || task.ownerUser?.deptName"
|
|
|
- class="ml-8rpx text-22rpx text-[#999]"
|
|
|
- >
|
|
|
- {{ task.assigneeUser?.deptName || task.ownerUser?.deptName }}
|
|
|
- </text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="mt-4rpx flex items-center">
|
|
|
- <text :class="getStatusTextClass(task.status)" class="text-24rpx">
|
|
|
- {{ getStatusText(task.status) }}
|
|
|
- </text>
|
|
|
- </view>
|
|
|
+ {{ task.assigneeUser?.deptName || task.ownerUser?.deptName }}
|
|
|
+ </text>
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <!-- 审批意见 -->
|
|
|
- <view
|
|
|
- v-if="shouldShowApprovalReason(task, activity.nodeType)"
|
|
|
- class="mt-8rpx rounded-8rpx bg-[#f5f5f5] p-16rpx"
|
|
|
- >
|
|
|
- <text class="text-24rpx text-[#666]">审批意见:{{ task.reason }}</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 签名 -->
|
|
|
- <view
|
|
|
- v-if="task.signPicUrl && activity.nodeType === BpmNodeTypeEnum.USER_TASK_NODE"
|
|
|
- class="mt-8rpx rounded-8rpx bg-[#f5f5f5] p-16rpx"
|
|
|
- >
|
|
|
- <text class="text-24rpx text-[#666]">签名:</text>
|
|
|
- <image
|
|
|
- :src="task.signPicUrl"
|
|
|
- class="ml-8rpx h-80rpx w-192rpx"
|
|
|
- mode="aspectFit"
|
|
|
- @click="previewImage(task.signPicUrl)"
|
|
|
- />
|
|
|
+ <view class="mt-4rpx flex items-center">
|
|
|
+ <text :class="getStatusTextClass(task.status)" class="text-24rpx">
|
|
|
+ {{ getStatusText(task.status) }}
|
|
|
+ </text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 情况二:遍历每个审批节点下的【候选的】task 任务 -->
|
|
|
- <view v-if="activity.candidateUsers && activity.candidateUsers.length > 0">
|
|
|
+ <!-- 审批意见 -->
|
|
|
+ <view
|
|
|
+ v-if="shouldShowApprovalReason(task, activity.nodeType)"
|
|
|
+ class="mt-8rpx rounded-8rpx bg-[#f5f5f5] p-16rpx"
|
|
|
+ >
|
|
|
+ <text class="text-24rpx text-[#666]">审批意见:{{ task.reason }}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 签名 -->
|
|
|
+ <view
|
|
|
+ v-if="task.signPicUrl && activity.nodeType === BpmNodeTypeEnum.USER_TASK_NODE"
|
|
|
+ class="mt-8rpx rounded-8rpx bg-[#f5f5f5] p-16rpx"
|
|
|
+ >
|
|
|
+ <text class="text-24rpx text-[#666]">签名:</text>
|
|
|
+ <image
|
|
|
+ :src="task.signPicUrl"
|
|
|
+ class="ml-8rpx h-80rpx w-192rpx"
|
|
|
+ mode="aspectFit"
|
|
|
+ @click="previewImage(task.signPicUrl)"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 情况二:遍历每个审批节点下的【候选的】task 任务 -->
|
|
|
+ <view v-if="activity.candidateUsers && activity.candidateUsers.length > 0">
|
|
|
+ <view
|
|
|
+ v-for="(user, userIndex) in activity.candidateUsers"
|
|
|
+ :key="userIndex"
|
|
|
+ class="mb-8rpx flex items-center"
|
|
|
+ >
|
|
|
+ <view class="relative mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
+ {{ user.nickname?.[0] || '?' }}
|
|
|
+
|
|
|
+ <!-- 候选状态图标 -->
|
|
|
<view
|
|
|
- v-for="(user, userIndex) in activity.candidateUsers"
|
|
|
- :key="userIndex"
|
|
|
- class="mb-8rpx flex items-center"
|
|
|
+ v-if="showStatusIcon"
|
|
|
+ class="absolute right--4rpx top-36rpx h-16rpx w-16rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
+ :style="{ backgroundColor: getApprovalNodeColor(BpmTaskStatusEnum.NOT_START) }"
|
|
|
>
|
|
|
- <view class="relative mr-8rpx h-48rpx w-48rpx flex items-center justify-center rounded-full bg-[#1890ff] text-24rpx text-white">
|
|
|
- {{ user.nickname?.[0] || '?' }}
|
|
|
-
|
|
|
- <!-- 候选状态图标 -->
|
|
|
- <view
|
|
|
- v-if="showStatusIcon"
|
|
|
- class="absolute right--4rpx top-36rpx h-16rpx w-16rpx flex items-center justify-center border-2 border-white rounded-full"
|
|
|
- :style="{ backgroundColor: getApprovalNodeColor(BpmTaskStatusEnum.NOT_START) }"
|
|
|
- >
|
|
|
- <wd-icon name="time" size="12rpx" color="white" />
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="flex-1">
|
|
|
- <text class="text-26rpx text-[#333]">{{ user.nickname }}</text>
|
|
|
- <text v-if="user.deptName" class="ml-8rpx text-22rpx text-[#999]">
|
|
|
- {{ user.deptName }}
|
|
|
- </text>
|
|
|
- </view>
|
|
|
+ <wd-icon name="time" size="12rpx" color="white" />
|
|
|
</view>
|
|
|
</view>
|
|
|
+
|
|
|
+ <view class="flex-1">
|
|
|
+ <text class="text-26rpx text-[#333]">{{ user.nickname }}</text>
|
|
|
+ <text v-if="user.deptName" class="ml-8rpx text-22rpx text-[#999]">
|
|
|
+ {{ user.deptName }}
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <!-- 用户选择弹窗 -->
|
|
|
- <UserPicker
|
|
|
- v-if="showUserPicker"
|
|
|
- v-model="selectedUserIds"
|
|
|
- type="checkbox"
|
|
|
- :visible="showUserPicker"
|
|
|
- @confirm="handleUserSelectConfirm"
|
|
|
- @cancel="handleUserSelectCancel"
|
|
|
- />
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
@@ -243,8 +232,8 @@ const emit = defineEmits<{
|
|
|
const statusIconMap: Record<string, { color: string, icon: string }> = {
|
|
|
'-2': { color: '#909398', icon: 'skip-forward' }, // 跳过
|
|
|
'-1': { color: '#909398', icon: 'time' }, // 审批未开始
|
|
|
- '0': { color: '#ff943e', icon: 'view' }, // 待审批
|
|
|
- '1': { color: '#448ef7', icon: 'view' }, // 审批中
|
|
|
+ '0': { color: '#f59e0b', icon: 'refresh1' }, // 待审批
|
|
|
+ '1': { color: '#f59e0b', icon: 'refresh1' }, // 审批中
|
|
|
'2': { color: '#00b32a', icon: 'check' }, // 审批通过
|
|
|
'3': { color: '#f46b6c', icon: 'close' }, // 审批不通过
|
|
|
'4': { color: '#cccccc', icon: 'delete' }, // 已取消
|
|
|
@@ -355,27 +344,16 @@ function getStatusText(status: number) {
|
|
|
return textMap[status] || '未知'
|
|
|
}
|
|
|
|
|
|
-/** 打开选择用户弹窗 */
|
|
|
-function handleSelectUser(activityId: string, selectedList: any[]) {
|
|
|
- selectedActivityNodeId.value = activityId
|
|
|
- selectedUserIds.value = selectedList.map(item => item.id)
|
|
|
- showUserPicker.value = true
|
|
|
-}
|
|
|
-
|
|
|
-/** 选择用户完成 */
|
|
|
-function handleUserSelectConfirm(userList: any[]) {
|
|
|
- if (!selectedActivityNodeId.value) {
|
|
|
- return
|
|
|
- }
|
|
|
- customApproveUsers.value[selectedActivityNodeId.value] = userList || []
|
|
|
- emit('selectUserConfirm', selectedActivityNodeId.value, userList)
|
|
|
- showUserPicker.value = false
|
|
|
+/** 用户选择确认 */
|
|
|
+function handleCustomUserSelectConfirm(activityId: string, users: any[]) {
|
|
|
+ customApproveUsers.value[activityId] = users || []
|
|
|
+ emit('selectUserConfirm', activityId, users)
|
|
|
}
|
|
|
|
|
|
-/** 取消选择用户 */
|
|
|
-function handleUserSelectCancel() {
|
|
|
- showUserPicker.value = false
|
|
|
- selectedUserIds.value = []
|
|
|
+/** 获取选中的用户ID数组 */
|
|
|
+function getSelectedUserIds(activityId: string): number[] {
|
|
|
+ const users = customApproveUsers.value[activityId] || []
|
|
|
+ return users.map(user => user.id).filter(id => id !== undefined)
|
|
|
}
|
|
|
|
|
|
/** 跳转子流程 */
|