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,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,7 +98,7 @@ | ||
| 98 | <view class="u-body-item u-flex u-body-item-btn-wrap common-justify-between common-item-center mt-20"> | 98 | <view class="u-body-item u-flex u-body-item-btn-wrap common-justify-between common-item-center mt-20"> |
| 99 | <!-- <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button>--> | 99 | <!-- <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button>--> |
| 100 | <!-- <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button>--> | 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 | <!-- <up-button type="primary" size="large" @click="handleProcess(item)" v-show="nextStepMap[item.taskKey].ysShow">验收</up-button>--> | 102 | <!-- <up-button type="primary" size="large" @click="handleProcess(item)" v-show="nextStepMap[item.taskKey].ysShow">验收</up-button>--> |
| 103 | 103 | ||
| 104 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> | 104 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> |
| @@ -244,6 +244,9 @@ | @@ -244,6 +244,9 @@ | ||
| 244 | </view> | 244 | </view> |
| 245 | </view> | 245 | </view> |
| 246 | </up-modal> | 246 | </up-modal> |
| 247 | + | ||
| 248 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | ||
| 249 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | ||
| 247 | </view> | 250 | </view> |
| 248 | </template> | 251 | </template> |
| 249 | 252 | ||
| @@ -260,6 +263,7 @@ import { | @@ -260,6 +263,7 @@ import { | ||
| 260 | dcyUniversalApproval, | 263 | dcyUniversalApproval, |
| 261 | aiUniversalApproval | 264 | aiUniversalApproval |
| 262 | } from '@/api/regional-order-manage/regional-order-manage' | 265 | } from '@/api/regional-order-manage/regional-order-manage' |
| 266 | +import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | ||
| 263 | import { useUserStore } from '@/pinia/user'; | 267 | import { useUserStore } from '@/pinia/user'; |
| 264 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' | 268 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 265 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 269 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| @@ -443,6 +447,38 @@ const handleRenew = (item) => { | @@ -443,6 +447,38 @@ const handleRenew = (item) => { | ||
| 443 | navToOrderPage(pageUrl, { isRenew: 1, tempKey }); | 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 | const handleProcess = async (item) => { | 483 | const handleProcess = async (item) => { |
| 448 | if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'}); | 484 | if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'}); |
pages-sub/problem/regional-order-manage/index.vue
| @@ -108,9 +108,9 @@ | @@ -108,9 +108,9 @@ | ||
| 108 | 108 | ||
| 109 | <!-- 待办工单(0) 保留原来的操作按钮区 不变 --> | 109 | <!-- 待办工单(0) 保留原来的操作按钮区 不变 --> |
| 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"> | 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 | <up-button type="success" size="large" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button> | 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 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> | 114 | <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> |
| 115 | </view> | 115 | </view> |
| 116 | 116 | ||
| @@ -205,6 +205,9 @@ | @@ -205,6 +205,9 @@ | ||
| 205 | </view> | 205 | </view> |
| 206 | </view> | 206 | </view> |
| 207 | </up-modal> | 207 | </up-modal> |
| 208 | + | ||
| 209 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | ||
| 210 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | ||
| 208 | </view> | 211 | </view> |
| 209 | </template> | 212 | </template> |
| 210 | 213 | ||
| @@ -221,6 +224,7 @@ import { | @@ -221,6 +224,7 @@ import { | ||
| 221 | dcyUniversalApproval, | 224 | dcyUniversalApproval, |
| 222 | aiUniversalApproval | 225 | aiUniversalApproval |
| 223 | } from '@/api/regional-order-manage/regional-order-manage' | 226 | } from '@/api/regional-order-manage/regional-order-manage' |
| 227 | +import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | ||
| 224 | import { useUserStore } from '@/pinia/user'; | 228 | import { useUserStore } from '@/pinia/user'; |
| 225 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' | 229 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 226 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 230 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| @@ -419,6 +423,55 @@ const handleRenew = (item) => { | @@ -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 | const handleProcess = async (item) => { | 475 | const handleProcess = async (item) => { |
| 423 | if (!item) return showToast('工单信息异常'); | 476 | if (!item) return showToast('工单信息异常'); |
| 424 | const stepName = nextStepMap[item.taskKey]?.name; | 477 | const stepName = nextStepMap[item.taskKey]?.name; |
pages-sub/problem/work-order-manage/index.vue
| @@ -102,7 +102,7 @@ | @@ -102,7 +102,7 @@ | ||
| 102 | </view> | 102 | </view> |
| 103 | <!-- 操作按钮行 --> | 103 | <!-- 操作按钮行 --> |
| 104 | <view class="u-body-item u-body-item-btn-wrap u-flex common-justify-between common-item-center mt-20"> | 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 | v-show="nextStepMap[item.taskKey].backShow">回退 | 106 | v-show="nextStepMap[item.taskKey].backShow">回退 |
| 107 | </up-button> | 107 | </up-button> |
| 108 | 108 | ||
| @@ -110,7 +110,7 @@ | @@ -110,7 +110,7 @@ | ||
| 110 | v-show="nextStepMap[item.taskKey].renewShow">重新提交 | 110 | v-show="nextStepMap[item.taskKey].renewShow">重新提交 |
| 111 | </up-button> | 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 | nextStepMap[item.taskKey].btnText | 114 | nextStepMap[item.taskKey].btnText |
| 115 | }} | 115 | }} |
| 116 | </up-button> | 116 | </up-button> |
| @@ -272,6 +272,9 @@ | @@ -272,6 +272,9 @@ | ||
| 272 | </view> | 272 | </view> |
| 273 | </view> | 273 | </view> |
| 274 | </up-modal> | 274 | </up-modal> |
| 275 | + | ||
| 276 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | ||
| 277 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | ||
| 275 | </view> | 278 | </view> |
| 276 | </template> | 279 | </template> |
| 277 | 280 | ||
| @@ -283,7 +286,8 @@ import { | @@ -283,7 +286,8 @@ import { | ||
| 283 | myBuzSimplePage, | 286 | myBuzSimplePage, |
| 284 | todoBuzSimplePage, | 287 | todoBuzSimplePage, |
| 285 | doneBuzSimplePage, | 288 | doneBuzSimplePage, |
| 286 | - universalApproval | 289 | + universalApproval, |
| 290 | + getTaskActions | ||
| 287 | } from '@/api/work-order-manage/work-order-manage' | 291 | } from '@/api/work-order-manage/work-order-manage' |
| 288 | // 从用户store获取角色信息 | 292 | // 从用户store获取角色信息 |
| 289 | import { useUserStore } from '@/pinia/user'; | 293 | import { useUserStore } from '@/pinia/user'; |
| @@ -486,6 +490,55 @@ const handleRenew = (item) => { | @@ -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 | const handleProcess = async (item) => { | 543 | const handleProcess = async (item) => { |
| 491 | console.log(nextStepMap[item.taskKey].name) | 544 | console.log(nextStepMap[item.taskKey].name) |
pages-sub/problem/work-order-manage/order-detail.vue
| @@ -258,7 +258,7 @@ | @@ -258,7 +258,7 @@ | ||
| 258 | <!-- activeTab==0的时候才出现, 也就是待办 --> | 258 | <!-- activeTab==0的时候才出现, 也就是待办 --> |
| 259 | <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap"> | 259 | <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap"> |
| 260 | <view class="u-body-item u-flex common-justify-between common-item-center "> | 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 | v-show="nextStepMap[orderDetail.taskKey].backShow">回退 | 262 | v-show="nextStepMap[orderDetail.taskKey].backShow">回退 |
| 263 | </up-button> | 263 | </up-button> |
| 264 | 264 | ||
| @@ -266,7 +266,7 @@ | @@ -266,7 +266,7 @@ | ||
| 266 | v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交 | 266 | v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交 |
| 267 | </up-button> | 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 | nextStepMap[orderDetail.taskKey].btnText | 270 | nextStepMap[orderDetail.taskKey].btnText |
| 271 | }} | 271 | }} |
| 272 | </up-button> | 272 | </up-button> |
| @@ -358,6 +358,9 @@ | @@ -358,6 +358,9 @@ | ||
| 358 | </view> | 358 | </view> |
| 359 | </view> | 359 | </view> |
| 360 | </up-modal> | 360 | </up-modal> |
| 361 | + | ||
| 362 | + <!-- 统一节点动作弹窗(工作流配置化,未配置时回退原有逻辑) --> | ||
| 363 | + <unified-task-actions ref="unifiedActionsRef" @success="handleUnifiedSuccess" /> | ||
| 361 | </view> | 364 | </view> |
| 362 | </template> | 365 | </template> |
| 363 | 366 | ||
| @@ -370,7 +373,8 @@ import { | @@ -370,7 +373,8 @@ import { | ||
| 370 | getDoneTaskDetail, | 373 | getDoneTaskDetail, |
| 371 | getTodoTaskDetail, | 374 | getTodoTaskDetail, |
| 372 | getApprovalDetail, | 375 | getApprovalDetail, |
| 373 | - universalApproval | 376 | + universalApproval, |
| 377 | + getTaskActions | ||
| 374 | } from '@/api/work-order-manage/work-order-manage'; | 378 | } from '@/api/work-order-manage/work-order-manage'; |
| 375 | import {nextStepMap, buzStatusMap, calculateFormatTimeDiff} from '@/common/utils/common' | 379 | import {nextStepMap, buzStatusMap, calculateFormatTimeDiff} from '@/common/utils/common' |
| 376 | // 引入图片上传组合式函数 | 380 | // 引入图片上传组合式函数 |
| @@ -697,6 +701,58 @@ const confirmReject = async () => { | @@ -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 | // ========== handleProcess 处理工单 ========== | 756 | // ========== handleProcess 处理工单 ========== |
| 701 | const handleProcess = async (item: any) => { | 757 | const handleProcess = async (item: any) => { |
| 702 | console.log(nextStepMap[item.taskKey]?.name) | 758 | console.log(nextStepMap[item.taskKey]?.name) |