Commit b6e4a0183c5e96681f5e60264987096143a7bd32
1 parent
40ba5f9a
业务线 小程序不用写死,从后端接口获取
Showing
6 changed files
with
514 additions
and
9 deletions
api/work-order-manage/work-order-manage.js
| ... | ... | @@ -112,3 +112,20 @@ export const workorderCreate = (data) => { |
| 112 | 112 | |
| 113 | 113 | |
| 114 | 114 | |
| 115 | + | |
| 116 | + | |
| 117 | +/** | |
| 118 | + * 获得当前任务可用的统一节点动作(工作流配置化) | |
| 119 | + * @returns {Promise} | |
| 120 | + */ | |
| 121 | +export const getTaskActions = (taskId) => { | |
| 122 | + return get(`/app-api/workorder/task/${taskId}/actions`); | |
| 123 | +}; | |
| 124 | + | |
| 125 | +/** | |
| 126 | + * 执行统一节点动作(工作流配置化) | |
| 127 | + * @returns {Promise} | |
| 128 | + */ | |
| 129 | +export const executeTaskAction = (data) => { | |
| 130 | + return post('/app-api/workorder/task/action', data); | |
| 131 | +}; | ... | ... |
components/unified-task-actions/unified-task-actions.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <up-popup | |
| 3 | + :show="visible" | |
| 4 | + mode="bottom" | |
| 5 | + round | |
| 6 | + closeable | |
| 7 | + :safe-area-inset-bottom="true" | |
| 8 | + @close="close" | |
| 9 | + > | |
| 10 | + <view class="unified-action-popup"> | |
| 11 | + <view class="unified-action-title">{{ current ? current.name : '请选择操作' }}</view> | |
| 12 | + | |
| 13 | + <!-- 第一步:动作列表 --> | |
| 14 | + <view v-if="!current" class="unified-action-list"> | |
| 15 | + <up-button | |
| 16 | + v-for="action in actions" | |
| 17 | + :key="action.code" | |
| 18 | + :type="getButtonType(action.code)" | |
| 19 | + size="large" | |
| 20 | + :customStyle="{ marginBottom: '20rpx' }" | |
| 21 | + @click="selectAction(action)" | |
| 22 | + > | |
| 23 | + {{ action.name }} | |
| 24 | + </up-button> | |
| 25 | + <view v-if="!actions.length" class="unified-action-tip">当前节点暂未配置可执行动作</view> | |
| 26 | + </view> | |
| 27 | + | |
| 28 | + <!-- 第二步:动作表单 --> | |
| 29 | + <view v-else class="unified-action-form"> | |
| 30 | + <view v-if="current.requireAssignee" class="form-item"> | |
| 31 | + <view class="form-label">处理人</view> | |
| 32 | + <up-radio-group v-if="workers.length" v-model="form.nextAssignee" class="worker-list"> | |
| 33 | + <up-radio | |
| 34 | + v-for="worker in workers" | |
| 35 | + :key="worker.id" | |
| 36 | + :name="String(worker.id)" | |
| 37 | + :label="worker.nickname" | |
| 38 | + /> | |
| 39 | + </up-radio-group> | |
| 40 | + <view v-else class="unified-action-tip"> | |
| 41 | + {{ loadingWorkers ? '加载处理人中...' : '暂无可选处理人' }} | |
| 42 | + </view> | |
| 43 | + </view> | |
| 44 | + | |
| 45 | + <view class="form-item"> | |
| 46 | + <view class="form-label">{{ reasonRequired ? '原因/备注(必填)' : '原因/备注' }}</view> | |
| 47 | + <up-textarea | |
| 48 | + v-model.trim="form.reason" | |
| 49 | + :placeholder="reasonRequired ? '请输入原因/备注(必填)' : '请输入原因/备注(选填)'" | |
| 50 | + :count="200" | |
| 51 | + maxlength="200" | |
| 52 | + height="120" | |
| 53 | + /> | |
| 54 | + </view> | |
| 55 | + | |
| 56 | + <view v-if="current.requireAttachment" class="form-item"> | |
| 57 | + <view class="form-label">附件(必填)</view> | |
| 58 | + <up-upload | |
| 59 | + :file-list="attachImgs.rawImgList.value || []" | |
| 60 | + @after-read="attachImgs.uploadImgs" | |
| 61 | + @delete="attachImgs.deleteImg" | |
| 62 | + multiple | |
| 63 | + width="70" | |
| 64 | + height="70" | |
| 65 | + :max-count="attachImgs.uploadConfig.maxCount" | |
| 66 | + :upload-text="attachImgs.uploadConfig.uploadText" | |
| 67 | + :size-type="attachImgs.uploadConfig.sizeType" | |
| 68 | + /> | |
| 69 | + </view> | |
| 70 | + | |
| 71 | + <view class="unified-action-btns"> | |
| 72 | + <up-button type="info" size="large" @click="backToList">返回</up-button> | |
| 73 | + <up-button type="primary" size="large" :loading="submitting" @click="submit">确定</up-button> | |
| 74 | + </view> | |
| 75 | + </view> | |
| 76 | + </view> | |
| 77 | + </up-popup> | |
| 78 | +</template> | |
| 79 | + | |
| 80 | +<script setup> | |
| 81 | +import { ref, reactive, computed } from 'vue' | |
| 82 | +import { | |
| 83 | + executeTaskAction, | |
| 84 | + getYlWorkersPage | |
| 85 | +} from '@/api/work-order-manage/work-order-manage' | |
| 86 | +import { useUploadImgs } from '@/common/utils/useUploadImgs' | |
| 87 | + | |
| 88 | +const emit = defineEmits(['success']) | |
| 89 | + | |
| 90 | +const visible = ref(false) | |
| 91 | +const actions = ref([]) | |
| 92 | +const current = ref(null) | |
| 93 | +const currentItem = ref(null) | |
| 94 | +const submitting = ref(false) | |
| 95 | +const workers = ref([]) | |
| 96 | +const loadingWorkers = ref(false) | |
| 97 | + | |
| 98 | +const form = reactive({ | |
| 99 | + nextAssignee: '', | |
| 100 | + reason: '' | |
| 101 | +}) | |
| 102 | + | |
| 103 | +const attachImgs = useUploadImgs({ | |
| 104 | + maxCount: 5, | |
| 105 | + uploadText: '选择附件' | |
| 106 | +}) | |
| 107 | + | |
| 108 | +const reasonRequired = computed(() => { | |
| 109 | + if (!current.value) return false | |
| 110 | + return ( | |
| 111 | + current.value.requireReason === true || | |
| 112 | + ['REJECT', 'RETURN', 'RETURN_PREV', 'TRANSFER', 'DELEGATE'].includes(current.value.code) | |
| 113 | + ) | |
| 114 | +}) | |
| 115 | + | |
| 116 | +/** 打开动作弹窗 */ | |
| 117 | +const open = async (item, actionList) => { | |
| 118 | + currentItem.value = item | |
| 119 | + actions.value = actionList || [] | |
| 120 | + current.value = null | |
| 121 | + form.nextAssignee = '' | |
| 122 | + form.reason = '' | |
| 123 | + attachImgs.clearImgs() | |
| 124 | + visible.value = true | |
| 125 | +} | |
| 126 | + | |
| 127 | +const close = () => { | |
| 128 | + visible.value = false | |
| 129 | + current.value = null | |
| 130 | + submitting.value = false | |
| 131 | +} | |
| 132 | + | |
| 133 | +const backToList = () => { | |
| 134 | + current.value = null | |
| 135 | +} | |
| 136 | + | |
| 137 | +/** 动作按钮颜色 */ | |
| 138 | +const getButtonType = (code) => { | |
| 139 | + switch (code) { | |
| 140 | + case 'APPROVE': | |
| 141 | + case 'HANDLE': | |
| 142 | + case 'COMPLETE': | |
| 143 | + return 'success' | |
| 144 | + case 'REJECT': | |
| 145 | + return 'danger' | |
| 146 | + case 'RETURN': | |
| 147 | + case 'RETURN_PREV': | |
| 148 | + return 'warning' | |
| 149 | + case 'WITHDRAW': | |
| 150 | + return 'info' | |
| 151 | + default: | |
| 152 | + return 'primary' | |
| 153 | + } | |
| 154 | +} | |
| 155 | + | |
| 156 | +/** 选择动作,按需加载处理人 */ | |
| 157 | +const selectAction = async (action) => { | |
| 158 | + // 退回动作必须在配置中指定目标节点(小程序端不提供节点选择) | |
| 159 | + if (action.code === 'RETURN' && !action.targetNodeKey) { | |
| 160 | + uni.showToast({ title: '该退回动作未配置目标节点,请在PC端配置', icon: 'none' }) | |
| 161 | + return | |
| 162 | + } | |
| 163 | + current.value = action | |
| 164 | + form.nextAssignee = '' | |
| 165 | + form.reason = '' | |
| 166 | + attachImgs.clearImgs() | |
| 167 | + if (action.requireAssignee) { | |
| 168 | + loadWorkers() | |
| 169 | + } | |
| 170 | +} | |
| 171 | + | |
| 172 | +/** 加载处理人 */ | |
| 173 | +const loadWorkers = async () => { | |
| 174 | + if (workers.value.length > 0) return | |
| 175 | + loadingWorkers.value = true | |
| 176 | + try { | |
| 177 | + const res = await getYlWorkersPage({ | |
| 178 | + busiLine: currentItem.value?.busiLine, | |
| 179 | + pageNo: 1, | |
| 180 | + pageSize: 100 | |
| 181 | + }) | |
| 182 | + workers.value = res?.list || [] | |
| 183 | + } catch (e) { | |
| 184 | + workers.value = [] | |
| 185 | + } finally { | |
| 186 | + loadingWorkers.value = false | |
| 187 | + } | |
| 188 | +} | |
| 189 | + | |
| 190 | +/** 提交动作 */ | |
| 191 | +const submit = async () => { | |
| 192 | + if (!current.value || !currentItem.value) return | |
| 193 | + const action = current.value | |
| 194 | + const reason = form.reason | |
| 195 | + if (reasonRequired.value && !reason) { | |
| 196 | + uni.showToast({ title: '请填写原因/备注', icon: 'none' }) | |
| 197 | + return | |
| 198 | + } | |
| 199 | + if (action.requireAssignee && !form.nextAssignee) { | |
| 200 | + uni.showToast({ title: '请选择处理人', icon: 'none' }) | |
| 201 | + return | |
| 202 | + } | |
| 203 | + const attachments = attachImgs.getSuccessImgUrls() | |
| 204 | + if (action.requireAttachment && attachments.length === 0) { | |
| 205 | + uni.showToast({ title: '请上传附件', icon: 'none' }) | |
| 206 | + return | |
| 207 | + } | |
| 208 | + | |
| 209 | + submitting.value = true | |
| 210 | + uni.showLoading({ title: '提交中...', mask: true }) | |
| 211 | + try { | |
| 212 | + const data = { | |
| 213 | + taskId: currentItem.value.taskId, | |
| 214 | + actionCode: action.code, | |
| 215 | + reason: reason || undefined, | |
| 216 | + targetNodeKey: action.targetNodeKey || undefined | |
| 217 | + } | |
| 218 | + if (action.requireAssignee) { | |
| 219 | + data.nextAssignee = Number(form.nextAssignee) | |
| 220 | + } | |
| 221 | + if (attachments.length > 0) { | |
| 222 | + data.attachments = attachments | |
| 223 | + } | |
| 224 | + await executeTaskAction(data) | |
| 225 | + uni.hideLoading() | |
| 226 | + uni.showToast({ title: '操作成功', icon: 'success', duration: 1000 }) | |
| 227 | + visible.value = false | |
| 228 | + emit('success', currentItem.value) | |
| 229 | + } catch (e) { | |
| 230 | + uni.hideLoading() | |
| 231 | + // 请求层已统一提示错误信息 | |
| 232 | + } finally { | |
| 233 | + submitting.value = false | |
| 234 | + } | |
| 235 | +} | |
| 236 | + | |
| 237 | +defineExpose({ open }) | |
| 238 | +</script> | |
| 239 | + | |
| 240 | +<style lang="scss" scoped> | |
| 241 | +.unified-action-popup { | |
| 242 | + padding: 40rpx 40rpx calc(40rpx + env(safe-area-inset-bottom)); | |
| 243 | + max-height: 80vh; | |
| 244 | + overflow-y: auto; | |
| 245 | +} | |
| 246 | + | |
| 247 | +.unified-action-title { | |
| 248 | + font-size: 34rpx; | |
| 249 | + font-weight: bold; | |
| 250 | + text-align: center; | |
| 251 | + margin-bottom: 40rpx; | |
| 252 | +} | |
| 253 | + | |
| 254 | +.unified-action-list { | |
| 255 | + display: flex; | |
| 256 | + flex-direction: column; | |
| 257 | +} | |
| 258 | + | |
| 259 | +.unified-action-tip { | |
| 260 | + color: #909399; | |
| 261 | + font-size: 26rpx; | |
| 262 | + padding: 20rpx 0; | |
| 263 | +} | |
| 264 | + | |
| 265 | +.unified-action-form .form-item { | |
| 266 | + margin-bottom: 30rpx; | |
| 267 | +} | |
| 268 | + | |
| 269 | +.form-label { | |
| 270 | + font-size: 28rpx; | |
| 271 | + color: #303133; | |
| 272 | + margin-bottom: 16rpx; | |
| 273 | +} | |
| 274 | + | |
| 275 | +.worker-list { | |
| 276 | + display: flex; | |
| 277 | + flex-wrap: wrap; | |
| 278 | + gap: 16rpx; | |
| 279 | +} | |
| 280 | + | |
| 281 | +.unified-action-btns { | |
| 282 | + display: flex; | |
| 283 | + gap: 24rpx; | |
| 284 | + margin-top: 20rpx; | |
| 285 | + | |
| 286 | + ::v-deep .u-button { | |
| 287 | + flex: 1; | |
| 288 | + } | |
| 289 | +} | |
| 290 | +</style> | ... | ... |
pages-sub/problem/ai-manage/index.vue
| ... | ... | @@ -98,7 +98,7 @@ |
| 98 | 98 | <view class="u-body-item u-flex u-body-item-btn-wrap common-justify-between common-item-center mt-20"> |
| 99 | 99 | <!-- <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button>--> |
| 100 | 100 | <!-- <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button>--> |
| 101 | - <up-button type="primary" size="large" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 101 | + <up-button type="primary" size="large" @click="handleUnifiedProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 102 | 102 | <!-- <up-button type="primary" size="large" @click="handleProcess(item)" v-show="nextStepMap[item.taskKey].ysShow">验收</up-button>--> |
| 103 | 103 | |
| 104 | 104 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> |
| ... | ... | @@ -244,6 +244,9 @@ |
| 244 | 244 | </view> |
| 245 | 245 | </view> |
| 246 | 246 | </up-modal> |
| 247 | + | |
| 248 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | |
| 249 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | |
| 247 | 250 | </view> |
| 248 | 251 | </template> |
| 249 | 252 | |
| ... | ... | @@ -260,6 +263,7 @@ import { |
| 260 | 263 | dcyUniversalApproval, |
| 261 | 264 | aiUniversalApproval |
| 262 | 265 | } from '@/api/regional-order-manage/regional-order-manage' |
| 266 | +import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | |
| 263 | 267 | import { useUserStore } from '@/pinia/user'; |
| 264 | 268 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 265 | 269 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| ... | ... | @@ -443,6 +447,38 @@ const handleRenew = (item) => { |
| 443 | 447 | navToOrderPage(pageUrl, { isRenew: 1, tempKey }); |
| 444 | 448 | }; |
| 445 | 449 | |
| 450 | +// ========== 统一节点动作(工作流配置化) ========== | |
| 451 | +const unifiedActionsRef = ref(null) | |
| 452 | + | |
| 453 | +/** 查询当前任务是否已配置统一动作;未配置或接口异常时返回 null,回退原有逻辑 */ | |
| 454 | +const queryUnifiedActions = async (item) => { | |
| 455 | + if (!item || !item.taskId) return null | |
| 456 | + try { | |
| 457 | + const data = await getTaskActions(item.taskId) | |
| 458 | + if (data && data.configured && Array.isArray(data.actions) && data.actions.length > 0) { | |
| 459 | + return data.actions | |
| 460 | + } | |
| 461 | + } catch (e) { | |
| 462 | + console.warn('查询统一节点动作失败,回退原有逻辑', e) | |
| 463 | + } | |
| 464 | + return null | |
| 465 | +} | |
| 466 | + | |
| 467 | +/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | |
| 468 | +const handleUnifiedProcess = async (item) => { | |
| 469 | + const actions = await queryUnifiedActions(item) | |
| 470 | + if (actions) { | |
| 471 | + unifiedActionsRef.value?.open(item, actions) | |
| 472 | + return | |
| 473 | + } | |
| 474 | + handleProcess(item) | |
| 475 | +} | |
| 476 | + | |
| 477 | +/** 统一动作执行成功后刷新列表 */ | |
| 478 | +const handleUnifiedSuccess = () => { | |
| 479 | + pagingRef.value && pagingRef.value.reload() | |
| 480 | +} | |
| 481 | + | |
| 446 | 482 | // 处理工单核心逻辑 |
| 447 | 483 | const handleProcess = async (item) => { |
| 448 | 484 | if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'}); | ... | ... |
pages-sub/problem/regional-order-manage/index.vue
| ... | ... | @@ -108,9 +108,9 @@ |
| 108 | 108 | |
| 109 | 109 | <!-- 待办工单(0) 保留原来的操作按钮区 不变 --> |
| 110 | 110 | <view v-if="activeTab === 0" class="u-body-item u-body-item-btn-wrap u-flex common-justify-between common-item-center mt-20"> |
| 111 | - <up-button type="warning" size="large" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button> | |
| 111 | + <up-button type="warning" size="large" @click="handleUnifiedReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button> | |
| 112 | 112 | <up-button type="success" size="large" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button> |
| 113 | - <up-button type="primary" size="large" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 113 | + <up-button type="primary" size="large" @click="handleUnifiedProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 114 | 114 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> |
| 115 | 115 | </view> |
| 116 | 116 | |
| ... | ... | @@ -205,6 +205,9 @@ |
| 205 | 205 | </view> |
| 206 | 206 | </view> |
| 207 | 207 | </up-modal> |
| 208 | + | |
| 209 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | |
| 210 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | |
| 208 | 211 | </view> |
| 209 | 212 | </template> |
| 210 | 213 | |
| ... | ... | @@ -221,6 +224,7 @@ import { |
| 221 | 224 | dcyUniversalApproval, |
| 222 | 225 | aiUniversalApproval |
| 223 | 226 | } from '@/api/regional-order-manage/regional-order-manage' |
| 227 | +import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | |
| 224 | 228 | import { useUserStore } from '@/pinia/user'; |
| 225 | 229 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 226 | 230 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| ... | ... | @@ -419,6 +423,55 @@ const handleRenew = (item) => { |
| 419 | 423 | }; |
| 420 | 424 | |
| 421 | 425 | // 处理工单核心逻辑 |
| 426 | +// ========== 统一节点动作(工作流配置化) ========== | |
| 427 | +const unifiedActionsRef = ref(null) | |
| 428 | + | |
| 429 | +/** 查询当前任务是否已配置统一动作;未配置或接口异常时返回 null,回退原有逻辑 */ | |
| 430 | +const queryUnifiedActions = async (item) => { | |
| 431 | + if (!item || !item.taskId) return null | |
| 432 | + try { | |
| 433 | + const data = await getTaskActions(item.taskId) | |
| 434 | + if (data && data.configured && Array.isArray(data.actions) && data.actions.length > 0) { | |
| 435 | + return data.actions | |
| 436 | + } | |
| 437 | + } catch (e) { | |
| 438 | + console.warn('查询统一节点动作失败,回退原有逻辑', e) | |
| 439 | + } | |
| 440 | + return null | |
| 441 | +} | |
| 442 | + | |
| 443 | +/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | |
| 444 | +const handleUnifiedProcess = async (item) => { | |
| 445 | + const actions = await queryUnifiedActions(item) | |
| 446 | + if (actions) { | |
| 447 | + unifiedActionsRef.value?.open(item, actions) | |
| 448 | + return | |
| 449 | + } | |
| 450 | + handleProcess(item) | |
| 451 | +} | |
| 452 | + | |
| 453 | +/** 待办-回退:已配置统一动作时走新逻辑,否则回退原有弹窗 */ | |
| 454 | +const handleUnifiedReject = async (item) => { | |
| 455 | + const actions = await queryUnifiedActions(item) | |
| 456 | + if (actions) { | |
| 457 | + const rejectActions = actions.filter((action) => | |
| 458 | + ['REJECT', 'RETURN', 'RETURN_PREV'].includes(action.code) | |
| 459 | + ) | |
| 460 | + if (rejectActions.length > 0) { | |
| 461 | + unifiedActionsRef.value?.open(item, rejectActions) | |
| 462 | + } else { | |
| 463 | + uni.showToast({ title: '当前节点未配置回退动作', icon: 'none' }) | |
| 464 | + } | |
| 465 | + return | |
| 466 | + } | |
| 467 | + handleReject(item) | |
| 468 | +} | |
| 469 | + | |
| 470 | +/** 统一动作执行成功后刷新列表 */ | |
| 471 | +const handleUnifiedSuccess = () => { | |
| 472 | + pagingHandle('reload') | |
| 473 | +} | |
| 474 | + | |
| 422 | 475 | const handleProcess = async (item) => { |
| 423 | 476 | if (!item) return showToast('工单信息异常'); |
| 424 | 477 | const stepName = nextStepMap[item.taskKey]?.name; | ... | ... |
pages-sub/problem/work-order-manage/index.vue
| ... | ... | @@ -102,7 +102,7 @@ |
| 102 | 102 | </view> |
| 103 | 103 | <!-- 操作按钮行 --> |
| 104 | 104 | <view class="u-body-item u-body-item-btn-wrap u-flex common-justify-between common-item-center mt-20"> |
| 105 | - <up-button type="warning" size="large" @click="handleReject(item)" | |
| 105 | + <up-button type="warning" size="large" @click="handleUnifiedReject(item)" | |
| 106 | 106 | v-show="nextStepMap[item.taskKey].backShow">回退 |
| 107 | 107 | </up-button> |
| 108 | 108 | |
| ... | ... | @@ -110,7 +110,7 @@ |
| 110 | 110 | v-show="nextStepMap[item.taskKey].renewShow">重新提交 |
| 111 | 111 | </up-button> |
| 112 | 112 | |
| 113 | - <up-button type="primary" size="large" @click="handleProcess(item)">{{ | |
| 113 | + <up-button type="primary" size="large" @click="handleUnifiedProcess(item)">{{ | |
| 114 | 114 | nextStepMap[item.taskKey].btnText |
| 115 | 115 | }} |
| 116 | 116 | </up-button> |
| ... | ... | @@ -272,6 +272,9 @@ |
| 272 | 272 | </view> |
| 273 | 273 | </view> |
| 274 | 274 | </up-modal> |
| 275 | + | |
| 276 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | |
| 277 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | |
| 275 | 278 | </view> |
| 276 | 279 | </template> |
| 277 | 280 | |
| ... | ... | @@ -283,7 +286,8 @@ import { |
| 283 | 286 | myBuzSimplePage, |
| 284 | 287 | todoBuzSimplePage, |
| 285 | 288 | doneBuzSimplePage, |
| 286 | - universalApproval | |
| 289 | + universalApproval, | |
| 290 | + getTaskActions | |
| 287 | 291 | } from '@/api/work-order-manage/work-order-manage' |
| 288 | 292 | // 从用户store获取角色信息 |
| 289 | 293 | import { useUserStore } from '@/pinia/user'; |
| ... | ... | @@ -486,6 +490,55 @@ const handleRenew = (item) => { |
| 486 | 490 | }); |
| 487 | 491 | }; |
| 488 | 492 | |
| 493 | +// ========== 统一节点动作(工作流配置化) ========== | |
| 494 | +const unifiedActionsRef = ref(null) | |
| 495 | + | |
| 496 | +/** 查询当前任务是否已配置统一动作;未配置或接口异常时返回 null,回退原有逻辑 */ | |
| 497 | +const queryUnifiedActions = async (item) => { | |
| 498 | + if (!item || !item.taskId) return null | |
| 499 | + try { | |
| 500 | + const data = await getTaskActions(item.taskId) | |
| 501 | + if (data && data.configured && Array.isArray(data.actions) && data.actions.length > 0) { | |
| 502 | + return data.actions | |
| 503 | + } | |
| 504 | + } catch (e) { | |
| 505 | + console.warn('查询统一节点动作失败,回退原有逻辑', e) | |
| 506 | + } | |
| 507 | + return null | |
| 508 | +} | |
| 509 | + | |
| 510 | +/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | |
| 511 | +const handleUnifiedProcess = async (item) => { | |
| 512 | + const actions = await queryUnifiedActions(item) | |
| 513 | + if (actions) { | |
| 514 | + unifiedActionsRef.value?.open(item, actions) | |
| 515 | + return | |
| 516 | + } | |
| 517 | + handleProcess(item) | |
| 518 | +} | |
| 519 | + | |
| 520 | +/** 待办-回退:已配置统一动作时走新逻辑,否则回退原有弹窗 */ | |
| 521 | +const handleUnifiedReject = async (item) => { | |
| 522 | + const actions = await queryUnifiedActions(item) | |
| 523 | + if (actions) { | |
| 524 | + const rejectActions = actions.filter((action) => | |
| 525 | + ['REJECT', 'RETURN', 'RETURN_PREV'].includes(action.code) | |
| 526 | + ) | |
| 527 | + if (rejectActions.length > 0) { | |
| 528 | + unifiedActionsRef.value?.open(item, rejectActions) | |
| 529 | + } else { | |
| 530 | + uni.showToast({ title: '当前节点未配置回退动作', icon: 'none' }) | |
| 531 | + } | |
| 532 | + return | |
| 533 | + } | |
| 534 | + handleReject(item) | |
| 535 | +} | |
| 536 | + | |
| 537 | +/** 统一动作执行成功后刷新列表 */ | |
| 538 | +const handleUnifiedSuccess = () => { | |
| 539 | + paging.value?.reload() | |
| 540 | +} | |
| 541 | + | |
| 489 | 542 | // 待办-处理工单 |
| 490 | 543 | const handleProcess = async (item) => { |
| 491 | 544 | console.log(nextStepMap[item.taskKey].name) | ... | ... |
pages-sub/problem/work-order-manage/order-detail.vue
| ... | ... | @@ -258,7 +258,7 @@ |
| 258 | 258 | <!-- activeTab==0的时候才出现, 也就是待办 --> |
| 259 | 259 | <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap"> |
| 260 | 260 | <view class="u-body-item u-flex common-justify-between common-item-center "> |
| 261 | - <up-button type="warning" size="normal" @click="handleReject(orderDetail)" | |
| 261 | + <up-button type="warning" size="normal" @click="handleUnifiedReject(orderDetail)" | |
| 262 | 262 | v-show="nextStepMap[orderDetail.taskKey].backShow">回退 |
| 263 | 263 | </up-button> |
| 264 | 264 | |
| ... | ... | @@ -266,7 +266,7 @@ |
| 266 | 266 | v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交 |
| 267 | 267 | </up-button> |
| 268 | 268 | |
| 269 | - <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{ | |
| 269 | + <up-button type="primary" size="normal" @click="handleUnifiedProcess(orderDetail)">{{ | |
| 270 | 270 | nextStepMap[orderDetail.taskKey].btnText |
| 271 | 271 | }} |
| 272 | 272 | </up-button> |
| ... | ... | @@ -358,6 +358,9 @@ |
| 358 | 358 | </view> |
| 359 | 359 | </view> |
| 360 | 360 | </up-modal> |
| 361 | + | |
| 362 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | |
| 363 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | |
| 361 | 364 | </view> |
| 362 | 365 | </template> |
| 363 | 366 | |
| ... | ... | @@ -370,7 +373,8 @@ import { |
| 370 | 373 | getDoneTaskDetail, |
| 371 | 374 | getTodoTaskDetail, |
| 372 | 375 | getApprovalDetail, |
| 373 | - universalApproval | |
| 376 | + universalApproval, | |
| 377 | + getTaskActions | |
| 374 | 378 | } from '@/api/work-order-manage/work-order-manage'; |
| 375 | 379 | import {nextStepMap, buzStatusMap, calculateFormatTimeDiff} from '@/common/utils/common' |
| 376 | 380 | // 引入图片上传组合式函数 |
| ... | ... | @@ -697,6 +701,58 @@ const confirmReject = async () => { |
| 697 | 701 | } |
| 698 | 702 | }; |
| 699 | 703 | |
| 704 | +// ========== 统一节点动作(工作流配置化) ========== | |
| 705 | +const unifiedActionsRef = ref(null) | |
| 706 | + | |
| 707 | +/** 查询当前任务是否已配置统一动作;未配置或接口异常时返回 null,回退原有逻辑 */ | |
| 708 | +const queryUnifiedActions = async (item: any) => { | |
| 709 | + if (!item || !item.taskId) return null | |
| 710 | + try { | |
| 711 | + const data = await getTaskActions(item.taskId) | |
| 712 | + if (data && data.configured && Array.isArray(data.actions) && data.actions.length > 0) { | |
| 713 | + return data.actions | |
| 714 | + } | |
| 715 | + } catch (e) { | |
| 716 | + console.warn('查询统一节点动作失败,回退原有逻辑', e) | |
| 717 | + } | |
| 718 | + return null | |
| 719 | +} | |
| 720 | + | |
| 721 | +/** 处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | |
| 722 | +const handleUnifiedProcess = async (item: any) => { | |
| 723 | + const actions = await queryUnifiedActions(item) | |
| 724 | + if (actions) { | |
| 725 | + unifiedActionsRef.value?.open(item, actions) | |
| 726 | + return | |
| 727 | + } | |
| 728 | + handleProcess(item) | |
| 729 | +} | |
| 730 | + | |
| 731 | +/** 回退:已配置统一动作时走新逻辑,否则回退原有弹窗 */ | |
| 732 | +const handleUnifiedReject = async (item: any) => { | |
| 733 | + const actions = await queryUnifiedActions(item) | |
| 734 | + if (actions) { | |
| 735 | + const rejectActions = actions.filter((action: any) => | |
| 736 | + ['REJECT', 'RETURN', 'RETURN_PREV'].includes(action.code) | |
| 737 | + ) | |
| 738 | + if (rejectActions.length > 0) { | |
| 739 | + unifiedActionsRef.value?.open(item, rejectActions) | |
| 740 | + } else { | |
| 741 | + uni.showToast({ title: '当前节点未配置回退动作', icon: 'none' }) | |
| 742 | + } | |
| 743 | + return | |
| 744 | + } | |
| 745 | + handleReject(item) | |
| 746 | +} | |
| 747 | + | |
| 748 | +/** 统一动作执行成功后通知列表刷新并返回 */ | |
| 749 | +const handleUnifiedSuccess = () => { | |
| 750 | + if (eventChannel.value) { | |
| 751 | + eventChannel.value.emit('needRefresh') | |
| 752 | + } | |
| 753 | + uni.navigateBack({delta: 1}) | |
| 754 | +} | |
| 755 | + | |
| 700 | 756 | // ========== handleProcess 处理工单 ========== |
| 701 | 757 | const handleProcess = async (item: any) => { |
| 702 | 758 | console.log(nextStepMap[item.taskKey]?.name) | ... | ... |