Commit 6853427faf5613784424d515aed2f036cbec1084
1 parent
5f7ce1e4
业务线 小程序不用写死,从后端接口获取
Showing
9 changed files
with
560 additions
and
122 deletions
common/utils/common.js
| @@ -102,6 +102,28 @@ export const buzStatusMap = { | @@ -102,6 +102,28 @@ export const buzStatusMap = { | ||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | /** | 104 | /** |
| 105 | + * 工作流节点动作按钮颜色(与统一动作弹窗保持一致) | ||
| 106 | + * @param {string} code - 动作编码,如 ASSIGN/REJECT/RETURN_PREV | ||
| 107 | + */ | ||
| 108 | +export const getActionButtonType = (code) => { | ||
| 109 | + switch (code) { | ||
| 110 | + case 'APPROVE': | ||
| 111 | + case 'HANDLE': | ||
| 112 | + case 'COMPLETE': | ||
| 113 | + return 'success' | ||
| 114 | + case 'REJECT': | ||
| 115 | + return 'error' | ||
| 116 | + case 'RETURN': | ||
| 117 | + case 'RETURN_PREV': | ||
| 118 | + return 'warning' | ||
| 119 | + case 'WITHDRAW': | ||
| 120 | + return 'info' | ||
| 121 | + default: | ||
| 122 | + return 'primary' | ||
| 123 | + } | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +/** | ||
| 105 | * 计算两个时间的时间差,返回格式化字符串(天/小时/分钟/秒,按需显示,无无效单位) | 127 | * 计算两个时间的时间差,返回格式化字符串(天/小时/分钟/秒,按需显示,无无效单位) |
| 106 | * @param {string | Date | number} startTime - 开始时间(时间字符串/Date对象/10位/13位时间戳) | 128 | * @param {string | Date | number} startTime - 开始时间(时间字符串/Date对象/10位/13位时间戳) |
| 107 | * @param {string | Date | number} endTime - 结束时间(时间字符串/Date对象/10位/13位时间戳) | 129 | * @param {string | Date | number} endTime - 结束时间(时间字符串/Date对象/10位/13位时间戳) |
components/unified-task-actions/unified-task-actions.vue
| @@ -175,7 +175,7 @@ const getButtonType = (code) => { | @@ -175,7 +175,7 @@ const getButtonType = (code) => { | ||
| 175 | case 'COMPLETE': | 175 | case 'COMPLETE': |
| 176 | return 'success' | 176 | return 'success' |
| 177 | case 'REJECT': | 177 | case 'REJECT': |
| 178 | - return 'danger' | 178 | + return 'error' |
| 179 | case 'RETURN': | 179 | case 'RETURN': |
| 180 | case 'RETURN_PREV': | 180 | case 'RETURN_PREV': |
| 181 | return 'warning' | 181 | return 'warning' |
pages-sub/daily/maintain-manage/type-list-page.vue
| @@ -33,8 +33,8 @@ | @@ -33,8 +33,8 @@ | ||
| 33 | <view class="info-text u-line-1">有效期:{{ timeFormat(item.beginTime, 'yyyy-mm-dd') }}—{{ | 33 | <view class="info-text u-line-1">有效期:{{ timeFormat(item.beginTime, 'yyyy-mm-dd') }}—{{ |
| 34 | timeFormat(item.endTime, 'yyyy-mm-dd') }} | 34 | timeFormat(item.endTime, 'yyyy-mm-dd') }} |
| 35 | </view> | 35 | </view> |
| 36 | - <!-- 仅待完成且存在进行中的本次养护时才显示结束任务(draftCommitId 由后端按 commit_state=1 回填) --> | ||
| 37 | - <view v-if="activeStatus === '1' && item.draftCommitId" class="mini-btn" | 36 | + <!-- 仅待完成且存在进行中的本次养护时才显示结束任务(commitId 由后端按 commit_state=1 回填) --> |
| 37 | + <view v-if="activeStatus === '1' && item.commitId" class="mini-btn" | ||
| 38 | @click.stop="handleEndTask(item)">结束任务</view> | 38 | @click.stop="handleEndTask(item)">结束任务</view> |
| 39 | </view> | 39 | </view> |
| 40 | </view> | 40 | </view> |
| @@ -255,18 +255,18 @@ const handleEndTaskModalCancel = () => { | @@ -255,18 +255,18 @@ const handleEndTaskModalCancel = () => { | ||
| 255 | // 确认结束任务,真正调用接口【新增】 | 255 | // 确认结束任务,真正调用接口【新增】 |
| 256 | const confirmEndTask = async () => { | 256 | const confirmEndTask = async () => { |
| 257 | const item = currentEndTaskItem.value | 257 | const item = currentEndTaskItem.value |
| 258 | - // draftCommitId 由明细列表接口回填(commit_state=1),按钮只在有值时渲染 | ||
| 259 | - if (!item || !item.draftCommitId) return | 258 | + // commitId 由明细列表接口回填(commit_state=1),按钮只在有值时渲染 |
| 259 | + if (!item || !item.commitId) return | ||
| 260 | endTaskModalShow.value = false | 260 | endTaskModalShow.value = false |
| 261 | 261 | ||
| 262 | uni.showLoading({ title: '提交中...', mask: true }) | 262 | uni.showLoading({ title: '提交中...', mask: true }) |
| 263 | try { | 263 | try { |
| 264 | // 后端 /finish 只认 commitId,且要求本次养护至少有一条有效子上报 | 264 | // 后端 /finish 只认 commitId,且要求本次养护至少有一条有效子上报 |
| 265 | - await finishPlan({ commitId: item.draftCommitId }) | 265 | + await finishPlan({ commitId: item.commitId }) |
| 266 | // 先关 loading 再弹提示:微信端 showLoading/showToast 共用同一个 toast,顺序反了会把成功提示顶掉 | 266 | // 先关 loading 再弹提示:微信端 showLoading/showToast 共用同一个 toast,顺序反了会把成功提示顶掉 |
| 267 | uni.hideLoading() | 267 | uni.hideLoading() |
| 268 | uni.showToast({ title: '结束任务成功', icon: 'success' }) | 268 | uni.showToast({ title: '结束任务成功', icon: 'success' }) |
| 269 | - // 操作成功,重新加载数据,刷新列表(draftCommitId 随之清空,按钮消失) | 269 | + // 操作成功,重新加载数据,刷新列表(commitId 随之清空,按钮消失) |
| 270 | await loadData() | 270 | await loadData() |
| 271 | pagingRef.value?.reload() | 271 | pagingRef.value?.reload() |
| 272 | } catch (err) { | 272 | } catch (err) { |
pages-sub/problem/ai-manage/index.vue
| @@ -94,16 +94,25 @@ | @@ -94,16 +94,25 @@ | ||
| 94 | <view class="u-body-item-title">提交时间:</view> | 94 | <view class="u-body-item-title">提交时间:</view> |
| 95 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> | 95 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> |
| 96 | </view> | 96 | </view> |
| 97 | - <!-- 操作按钮行 --> | ||
| 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="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button>--> | ||
| 100 | - <up-button type="warning" size="large" @click="handleUnifiedReject(item)" | ||
| 101 | - v-show="showRejectButton(item)">回退 | ||
| 102 | - </up-button> | ||
| 103 | - <up-button type="primary" size="large" @click="handleUnifiedProcess(item)">{{ getProcessButtonText(item) }}</up-button> | ||
| 104 | -<!-- <up-button type="primary" size="large" @click="handleProcess(item)" v-show="nextStepMap[item.taskKey].ysShow">验收</up-button>--> | ||
| 105 | - | ||
| 106 | - <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> | 97 | + <!-- 操作按钮行:已配置工作流动作时按配置逐个渲染按钮,点击直接执行对应动作 --> |
| 98 | + <view class="u-body-item task-btn-row mt-20"> | ||
| 99 | + <template v-if="hasConfiguredActions(item)"> | ||
| 100 | + <view | ||
| 101 | + v-for="action in item.configuredActions" | ||
| 102 | + :key="action.code" | ||
| 103 | + :class="['task-btn', 'task-btn--' + getActionButtonType(action.code)]" | ||
| 104 | + @click.stop="handleUnifiedProcess(item, action)" | ||
| 105 | + >{{ action.name }}</view> | ||
| 106 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 107 | + </template> | ||
| 108 | + <!-- 未配置:沿用原有节点按钮 --> | ||
| 109 | + <template v-else> | ||
| 110 | + <view v-if="showRejectButton(item)" class="task-btn task-btn--warning" | ||
| 111 | + @click.stop="handleUnifiedReject(item)">回退</view> | ||
| 112 | + <view class="task-btn task-btn--primary" | ||
| 113 | + @click.stop="handleUnifiedProcess(item)">{{ getProcessButtonText(item) }}</view> | ||
| 114 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 115 | + </template> | ||
| 107 | </view> | 116 | </view> |
| 108 | </view> | 117 | </view> |
| 109 | </template> | 118 | </template> |
| @@ -267,7 +276,7 @@ import { | @@ -267,7 +276,7 @@ import { | ||
| 267 | } from '@/api/regional-order-manage/regional-order-manage' | 276 | } from '@/api/regional-order-manage/regional-order-manage' |
| 268 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | 277 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' |
| 269 | import { useUserStore } from '@/pinia/user'; | 278 | import { useUserStore } from '@/pinia/user'; |
| 270 | -import { nextStepMap, buzStatusMap } from '@/common/utils/common' | 279 | +import { nextStepMap, buzStatusMap, getActionButtonType } from '@/common/utils/common' |
| 271 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 280 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 272 | 281 | ||
| 273 | // ========== 全局实例 & 常量 ========== | 282 | // ========== 全局实例 & 常量 ========== |
| @@ -497,14 +506,18 @@ const getProcessButtonText = (item) => { | @@ -497,14 +506,18 @@ const getProcessButtonText = (item) => { | ||
| 497 | return nextStepMap[item.taskKey]?.btnText || '处理' | 506 | return nextStepMap[item.taskKey]?.btnText || '处理' |
| 498 | } | 507 | } |
| 499 | 508 | ||
| 500 | -/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | ||
| 501 | -const handleUnifiedProcess = async (item) => { | 509 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ |
| 510 | +const hasConfiguredActions = (item) => | ||
| 511 | + Array.isArray(item?.configuredActions) && item.configuredActions.length > 0 | ||
| 512 | + | ||
| 513 | +/** 待办-处理工单:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ | ||
| 514 | +const handleUnifiedProcess = async (item, action = null) => { | ||
| 502 | const actions = Array.isArray(item?.configuredActions) | 515 | const actions = Array.isArray(item?.configuredActions) |
| 503 | ? item.configuredActions | 516 | ? item.configuredActions |
| 504 | : await queryUnifiedActions(item) | 517 | : await queryUnifiedActions(item) |
| 505 | if (actions) { | 518 | if (actions) { |
| 506 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 507 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 519 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 520 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 508 | return | 521 | return |
| 509 | } | 522 | } |
| 510 | handleProcess(item) | 523 | handleProcess(item) |
| @@ -730,8 +743,44 @@ onShow(() => { | @@ -730,8 +743,44 @@ onShow(() => { | ||
| 730 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); | 743 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
| 731 | } | 744 | } |
| 732 | 745 | ||
| 733 | -.u-body-item-btn-wrap :deep(.u-button--large){ | ||
| 734 | - height: 28px; | 746 | +// 卡片操作按钮:自动换行、右对齐,动作多时每行 3~4 个,不再整行铺满 |
| 747 | +.task-btn-row { | ||
| 748 | + display: flex; | ||
| 749 | + flex-wrap: wrap; | ||
| 750 | + justify-content: flex-end; | ||
| 751 | + margin-bottom: -10rpx; | ||
| 752 | +} | ||
| 753 | + | ||
| 754 | +.task-btn { | ||
| 755 | + box-sizing: border-box; | ||
| 756 | + height: 56rpx; | ||
| 757 | + line-height: 56rpx; | ||
| 758 | + padding: 0 24rpx; | ||
| 759 | + margin: 0 0 10rpx 12rpx; | ||
| 760 | + border-radius: 8rpx; | ||
| 761 | + font-size: 24rpx; | ||
| 762 | + color: #fff; | ||
| 763 | + white-space: nowrap; | ||
| 764 | +} | ||
| 765 | + | ||
| 766 | +.task-btn--primary { | ||
| 767 | + background-color: #3c9cff; | ||
| 768 | +} | ||
| 769 | + | ||
| 770 | +.task-btn--success { | ||
| 771 | + background-color: #5ac725; | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +.task-btn--warning { | ||
| 775 | + background-color: #f9ae3d; | ||
| 776 | +} | ||
| 777 | + | ||
| 778 | +.task-btn--error { | ||
| 779 | + background-color: #f56c6c; | ||
| 780 | +} | ||
| 781 | + | ||
| 782 | +.task-btn--info { | ||
| 783 | + background-color: #909399; | ||
| 735 | } | 784 | } |
| 736 | 785 | ||
| 737 | 786 |
pages-sub/problem/regional-order-manage/index.vue
| @@ -106,12 +106,27 @@ | @@ -106,12 +106,27 @@ | ||
| 106 | <view class="u-line-1 u-body-value">{{ item.createTime ? timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') : '-' }}</view> | 106 | <view class="u-line-1 u-body-value">{{ item.createTime ? timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') : '-' }}</view> |
| 107 | </view> | 107 | </view> |
| 108 | 108 | ||
| 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"> | ||
| 111 | - <up-button type="warning" size="large" @click="handleUnifiedReject(item)" v-show="showRejectButton(item)">回退</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="handleUnifiedProcess(item)">{{ getProcessButtonText(item) }}</up-button> | ||
| 114 | - <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> | 109 | + <!-- 待办工单(0):已配置工作流动作时按配置逐个渲染按钮,点击直接执行对应动作 --> |
| 110 | + <view v-if="activeTab === 0" class="u-body-item task-btn-row mt-20"> | ||
| 111 | + <template v-if="hasConfiguredActions(item)"> | ||
| 112 | + <view | ||
| 113 | + v-for="action in item.configuredActions" | ||
| 114 | + :key="action.code" | ||
| 115 | + :class="['task-btn', 'task-btn--' + getActionButtonType(action.code)]" | ||
| 116 | + @click.stop="handleUnifiedProcess(item, action)" | ||
| 117 | + >{{ action.name }}</view> | ||
| 118 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 119 | + </template> | ||
| 120 | + <!-- 未配置:沿用原有节点按钮 --> | ||
| 121 | + <template v-else> | ||
| 122 | + <view v-if="showRejectButton(item)" class="task-btn task-btn--warning" | ||
| 123 | + @click.stop="handleUnifiedReject(item)">回退</view> | ||
| 124 | + <view v-if="nextStepMap[item.taskKey]?.renewShow" class="task-btn task-btn--success" | ||
| 125 | + @click.stop="handleRenew(item)">重新提交</view> | ||
| 126 | + <view class="task-btn task-btn--primary" | ||
| 127 | + @click.stop="handleUnifiedProcess(item)">{{ getProcessButtonText(item) }}</view> | ||
| 128 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 129 | + </template> | ||
| 115 | </view> | 130 | </view> |
| 116 | 131 | ||
| 117 | <!-- 已删除:原来单独的【我发起的/已办】按钮区,不需要了 --> | 132 | <!-- 已删除:原来单独的【我发起的/已办】按钮区,不需要了 --> |
| @@ -226,7 +241,7 @@ import { | @@ -226,7 +241,7 @@ import { | ||
| 226 | } from '@/api/regional-order-manage/regional-order-manage' | 241 | } from '@/api/regional-order-manage/regional-order-manage' |
| 227 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | 242 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' |
| 228 | import { useUserStore } from '@/pinia/user'; | 243 | import { useUserStore } from '@/pinia/user'; |
| 229 | -import { nextStepMap, buzStatusMap } from '@/common/utils/common' | 244 | +import { nextStepMap, buzStatusMap, getActionButtonType } from '@/common/utils/common' |
| 230 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 245 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 231 | 246 | ||
| 232 | // ========== 全局实例 & 常量 ========== | 247 | // ========== 全局实例 & 常量 ========== |
| @@ -472,14 +487,18 @@ const getProcessButtonText = (item) => { | @@ -472,14 +487,18 @@ const getProcessButtonText = (item) => { | ||
| 472 | return nextStepMap[item.taskKey]?.btnText | 487 | return nextStepMap[item.taskKey]?.btnText |
| 473 | } | 488 | } |
| 474 | 489 | ||
| 475 | -/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | ||
| 476 | -const handleUnifiedProcess = async (item) => { | 490 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ |
| 491 | +const hasConfiguredActions = (item) => | ||
| 492 | + Array.isArray(item?.configuredActions) && item.configuredActions.length > 0 | ||
| 493 | + | ||
| 494 | +/** 待办-处理工单:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ | ||
| 495 | +const handleUnifiedProcess = async (item, action = null) => { | ||
| 477 | const actions = Array.isArray(item?.configuredActions) | 496 | const actions = Array.isArray(item?.configuredActions) |
| 478 | ? item.configuredActions | 497 | ? item.configuredActions |
| 479 | : await queryUnifiedActions(item) | 498 | : await queryUnifiedActions(item) |
| 480 | if (actions) { | 499 | if (actions) { |
| 481 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 482 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 500 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 501 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 483 | return | 502 | return |
| 484 | } | 503 | } |
| 485 | handleProcess(item) | 504 | handleProcess(item) |
| @@ -720,8 +739,44 @@ onLoad(() => { | @@ -720,8 +739,44 @@ onLoad(() => { | ||
| 720 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); | 739 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
| 721 | } | 740 | } |
| 722 | 741 | ||
| 723 | -.u-body-item-btn-wrap :deep(.u-button--large){ | ||
| 724 | - height: 28px; | 742 | +// 卡片操作按钮:自动换行、右对齐,动作多时每行 3~4 个,不再整行铺满 |
| 743 | +.task-btn-row { | ||
| 744 | + display: flex; | ||
| 745 | + flex-wrap: wrap; | ||
| 746 | + justify-content: flex-end; | ||
| 747 | + margin-bottom: -10rpx; | ||
| 748 | +} | ||
| 749 | + | ||
| 750 | +.task-btn { | ||
| 751 | + box-sizing: border-box; | ||
| 752 | + height: 56rpx; | ||
| 753 | + line-height: 56rpx; | ||
| 754 | + padding: 0 24rpx; | ||
| 755 | + margin: 0 0 10rpx 12rpx; | ||
| 756 | + border-radius: 8rpx; | ||
| 757 | + font-size: 24rpx; | ||
| 758 | + color: #fff; | ||
| 759 | + white-space: nowrap; | ||
| 760 | +} | ||
| 761 | + | ||
| 762 | +.task-btn--primary { | ||
| 763 | + background-color: #3c9cff; | ||
| 764 | +} | ||
| 765 | + | ||
| 766 | +.task-btn--success { | ||
| 767 | + background-color: #5ac725; | ||
| 768 | +} | ||
| 769 | + | ||
| 770 | +.task-btn--warning { | ||
| 771 | + background-color: #f9ae3d; | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +.task-btn--error { | ||
| 775 | + background-color: #f56c6c; | ||
| 776 | +} | ||
| 777 | + | ||
| 778 | +.task-btn--info { | ||
| 779 | + background-color: #909399; | ||
| 725 | } | 780 | } |
| 726 | 781 | ||
| 727 | 782 |
pages-sub/problem/regional-order-manage/list-detail.vue
| @@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
| 8 | color="#3c9cff" | 8 | color="#3c9cff" |
| 9 | ></up-loading-page> | 9 | ></up-loading-page> |
| 10 | 10 | ||
| 11 | - <view v-else class="main-content"> | 11 | + <view v-else class="main-content" :class="'pb-rows-' + actionBarRows"> |
| 12 | <!-- 顶部标签栏 --> | 12 | <!-- 顶部标签栏 --> |
| 13 | <up-sticky :bgColor="'#fff'"> | 13 | <up-sticky :bgColor="'#fff'"> |
| 14 | <up-tabs | 14 | <up-tabs |
| @@ -223,23 +223,25 @@ | @@ -223,23 +223,25 @@ | ||
| 223 | </view> | 223 | </view> |
| 224 | 224 | ||
| 225 | <!-- 底部操作按钮 --> | 225 | <!-- 底部操作按钮 --> |
| 226 | - <view v-if="activeTab === '0' && (configuredActions || nextStepMap[orderDetail.taskKey])" class="fixed-bottom-btn-wrap"> | ||
| 227 | - <view class="u-body-item u-flex common-justify-between common-item-center"> | ||
| 228 | - <up-button | ||
| 229 | - type="warning" | ||
| 230 | - size="normal" | ||
| 231 | - @click="handleUnifiedReject(orderDetail)" | ||
| 232 | - v-show="showRejectButton" | ||
| 233 | - >回退</up-button> | ||
| 234 | - <up-button | ||
| 235 | - type="success" | ||
| 236 | - size="normal" | ||
| 237 | - @click="handleRenew(orderDetail)" | ||
| 238 | - v-show="nextStepMap[orderDetail.taskKey]?.renewShow" | ||
| 239 | - >重新提交</up-button> | ||
| 240 | - <up-button type="primary" size="normal" @click="handleUnifiedProcess(orderDetail)"> | ||
| 241 | - {{ processButtonText }} | ||
| 242 | - </up-button> | 226 | + <view v-if="activeTab === '0' && (configuredActions || nextStepMap[orderDetail.taskKey])" |
| 227 | + class="fixed-bottom-btn-wrap order-action-bar"> | ||
| 228 | + <!-- 已配置工作流动作:每行最多 3 个、超出自动换行,点击直接执行对应动作,不再弹「请选择操作」 --> | ||
| 229 | + <view v-if="hasConfiguredActions" class="order-action-row"> | ||
| 230 | + <view | ||
| 231 | + v-for="action in configuredActions" | ||
| 232 | + :key="action.code" | ||
| 233 | + :class="['order-action-btn', 'order-action-btn--' + getActionButtonType(action.code)]" | ||
| 234 | + @click.stop="handleUnifiedProcess(orderDetail, action)" | ||
| 235 | + >{{ action.name }}</view> | ||
| 236 | + </view> | ||
| 237 | + <!-- 未配置:沿用原有节点按钮(整行展示) --> | ||
| 238 | + <view v-else class="order-action-row order-action-row--single"> | ||
| 239 | + <view v-if="showRejectButton" class="order-action-btn order-action-btn--warning" | ||
| 240 | + @click.stop="handleUnifiedReject(orderDetail)">回退</view> | ||
| 241 | + <view v-if="nextStepMap[orderDetail.taskKey]?.renewShow" class="order-action-btn order-action-btn--success" | ||
| 242 | + @click.stop="handleRenew(orderDetail)">重新提交</view> | ||
| 243 | + <view class="order-action-btn order-action-btn--primary" | ||
| 244 | + @click.stop="handleUnifiedProcess(orderDetail)">{{ processButtonText }}</view> | ||
| 243 | </view> | 245 | </view> |
| 244 | </view> | 246 | </view> |
| 245 | 247 | ||
| @@ -335,7 +337,7 @@ import { timeFormat } from '@/uni_modules/uview-plus' | @@ -335,7 +337,7 @@ import { timeFormat } from '@/uni_modules/uview-plus' | ||
| 335 | import { getApprovalDetail,workOrderDetailByID } from '@/api/regional-order-manage/regional-order-manage' | 337 | import { getApprovalDetail,workOrderDetailByID } from '@/api/regional-order-manage/regional-order-manage' |
| 336 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' | 338 | import { getTaskActions } from '@/api/work-order-manage/work-order-manage' |
| 337 | 339 | ||
| 338 | -import { nextStepMap, calculateFormatTimeDiff } from '@/common/utils/common' | 340 | +import { nextStepMap, calculateFormatTimeDiff, getActionButtonType } from '@/common/utils/common' |
| 339 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 341 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 340 | import { useUserStore } from '@/pinia/user' | 342 | import { useUserStore } from '@/pinia/user' |
| 341 | 343 | ||
| @@ -517,6 +519,21 @@ const showRejectButton = computed(() => { | @@ -517,6 +519,21 @@ const showRejectButton = computed(() => { | ||
| 517 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow | 519 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow |
| 518 | }) | 520 | }) |
| 519 | 521 | ||
| 522 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ | ||
| 523 | +const hasConfiguredActions = computed(() => | ||
| 524 | + Array.isArray(configuredActions.value) && configuredActions.value.length > 0) | ||
| 525 | + | ||
| 526 | +/** 底部操作栏行数:详情内容据此预留底部间距,避免按钮遮挡内容 */ | ||
| 527 | +const actionBarRows = computed(() => { | ||
| 528 | + if (hasConfiguredActions.value) { | ||
| 529 | + return Math.ceil(configuredActions.value.length / 3) | ||
| 530 | + } | ||
| 531 | + let rows = 1 | ||
| 532 | + if (showRejectButton.value) rows++ | ||
| 533 | + if (nextStepMap[orderDetail.value.taskKey]?.renewShow) rows++ | ||
| 534 | + return rows | ||
| 535 | +}) | ||
| 536 | + | ||
| 520 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ | 537 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ |
| 521 | const processButtonText = computed(() => { | 538 | const processButtonText = computed(() => { |
| 522 | const actions = configuredActions.value | 539 | const actions = configuredActions.value |
| @@ -529,14 +546,14 @@ const processButtonText = computed(() => { | @@ -529,14 +546,14 @@ const processButtonText = computed(() => { | ||
| 529 | /** 统一动作弹窗 */ | 546 | /** 统一动作弹窗 */ |
| 530 | const unifiedActionsRef = ref<any>(null) | 547 | const unifiedActionsRef = ref<any>(null) |
| 531 | 548 | ||
| 532 | -/** 处理:已配置统一动作时走统一接口,否则回退原有处理 */ | ||
| 533 | -const handleUnifiedProcess = async (item: any) => { | 549 | +/** 处理:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ |
| 550 | +const handleUnifiedProcess = async (item: any, action: any = null) => { | ||
| 534 | const actions = Array.isArray(configuredActions.value) | 551 | const actions = Array.isArray(configuredActions.value) |
| 535 | ? configuredActions.value | 552 | ? configuredActions.value |
| 536 | : await queryUnifiedActions(item?.taskId) | 553 | : await queryUnifiedActions(item?.taskId) |
| 537 | if (actions) { | 554 | if (actions) { |
| 538 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 539 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 555 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 556 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 540 | return | 557 | return |
| 541 | } | 558 | } |
| 542 | handleProcess(item) | 559 | handleProcess(item) |
| @@ -1001,4 +1018,74 @@ onLoad((options: any) => { | @@ -1001,4 +1018,74 @@ onLoad((options: any) => { | ||
| 1001 | width: 70px !important; | 1018 | width: 70px !important; |
| 1002 | height: 70px !important; | 1019 | height: 70px !important; |
| 1003 | } | 1020 | } |
| 1021 | + | ||
| 1022 | +// 底部动作按钮:动作多时自动换行 | ||
| 1023 | +// 底部操作栏:白底 + 顶部投影,按钮每行最多 3 个,避免多动作时整屏遮挡工单详情 | ||
| 1024 | +.order-action-bar { | ||
| 1025 | + background-color: #fff; | ||
| 1026 | + box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06); | ||
| 1027 | + padding: 10rpx 14rpx; | ||
| 1028 | + padding-bottom: calc(10rpx + constant(safe-area-inset-bottom)); | ||
| 1029 | + padding-bottom: calc(10rpx + env(safe-area-inset-bottom)); | ||
| 1030 | +} | ||
| 1031 | + | ||
| 1032 | +.order-action-row { | ||
| 1033 | + display: flex; | ||
| 1034 | + flex-wrap: wrap; | ||
| 1035 | + margin: -6rpx; | ||
| 1036 | +} | ||
| 1037 | + | ||
| 1038 | +.order-action-btn { | ||
| 1039 | + box-sizing: border-box; | ||
| 1040 | + width: calc((100% - 36rpx) / 3); | ||
| 1041 | + height: 68rpx; | ||
| 1042 | + line-height: 68rpx; | ||
| 1043 | + margin: 6rpx; | ||
| 1044 | + padding: 0 8rpx; | ||
| 1045 | + border-radius: 8rpx; | ||
| 1046 | + font-size: 26rpx; | ||
| 1047 | + text-align: center; | ||
| 1048 | + color: #fff; | ||
| 1049 | + white-space: nowrap; | ||
| 1050 | + overflow: hidden; | ||
| 1051 | + text-overflow: ellipsis; | ||
| 1052 | +} | ||
| 1053 | + | ||
| 1054 | +// 未配置统一动作时按钮整行展示,保持原有观感 | ||
| 1055 | +.order-action-row--single .order-action-btn { | ||
| 1056 | + width: 100%; | ||
| 1057 | +} | ||
| 1058 | + | ||
| 1059 | +.order-action-btn--primary { | ||
| 1060 | + background-color: #3c9cff; | ||
| 1061 | +} | ||
| 1062 | + | ||
| 1063 | +.order-action-btn--success { | ||
| 1064 | + background-color: #5ac725; | ||
| 1065 | +} | ||
| 1066 | + | ||
| 1067 | +.order-action-btn--warning { | ||
| 1068 | + background-color: #f9ae3d; | ||
| 1069 | +} | ||
| 1070 | + | ||
| 1071 | +.order-action-btn--error { | ||
| 1072 | + background-color: #f56c6c; | ||
| 1073 | +} | ||
| 1074 | + | ||
| 1075 | +.order-action-btn--info { | ||
| 1076 | + background-color: #909399; | ||
| 1077 | +} | ||
| 1078 | + | ||
| 1079 | +// 按操作栏行数给内容预留底部间距 | ||
| 1080 | +.main-content.pb-rows-1 { | ||
| 1081 | + padding-bottom: 190rpx; | ||
| 1082 | +} | ||
| 1083 | + | ||
| 1084 | +.main-content.pb-rows-2 { | ||
| 1085 | + padding-bottom: 290rpx; | ||
| 1086 | +} | ||
| 1087 | + | ||
| 1088 | +.main-content.pb-rows-3 { | ||
| 1089 | + padding-bottom: 390rpx; | ||
| 1090 | +} | ||
| 1004 | </style> | 1091 | </style> |
| 1005 | \ No newline at end of file | 1092 | \ No newline at end of file |
pages-sub/problem/regional-order-manage/order-detail.vue
| @@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
| 9 | ></up-loading-page> | 9 | ></up-loading-page> |
| 10 | 10 | ||
| 11 | <!-- 主内容容器 --> | 11 | <!-- 主内容容器 --> |
| 12 | - <view v-else class="main-content"> | 12 | + <view v-else class="main-content" :class="'pb-rows-' + actionBarRows"> |
| 13 | <!-- 顶部固定Tabs --> | 13 | <!-- 顶部固定Tabs --> |
| 14 | <up-sticky :bgColor="'#fff'"> | 14 | <up-sticky :bgColor="'#fff'"> |
| 15 | <up-tabs | 15 | <up-tabs |
| @@ -253,21 +253,25 @@ | @@ -253,21 +253,25 @@ | ||
| 253 | </view> | 253 | </view> |
| 254 | 254 | ||
| 255 | <!-- activeTab==0的时候才出现, 也就是待办 --> | 255 | <!-- activeTab==0的时候才出现, 也就是待办 --> |
| 256 | - <view v-if="activeTab==0&&(configuredActions||nextStepMap[orderDetail.taskKey])" class="fixed-bottom-btn-wrap"> | ||
| 257 | - <view class="u-body-item u-flex common-justify-between common-item-center "> | ||
| 258 | - <up-button type="warning" size="normal" @click="handleUnifiedReject(orderDetail)" | ||
| 259 | - v-show="showRejectButton">回退 | ||
| 260 | - </up-button> | ||
| 261 | - | ||
| 262 | - <up-button type="success" size="normal" @click="handleRenew(orderDetail)" | ||
| 263 | - v-show="nextStepMap[orderDetail.taskKey]?.renewShow">重新提交 | ||
| 264 | - </up-button> | ||
| 265 | - | ||
| 266 | - <up-button type="primary" size="normal" @click="handleUnifiedProcess(orderDetail)">{{ | ||
| 267 | - processButtonText | ||
| 268 | - }} | ||
| 269 | - </up-button> | ||
| 270 | - | 256 | + <view v-if="activeTab==0&&(configuredActions||nextStepMap[orderDetail.taskKey])" |
| 257 | + class="fixed-bottom-btn-wrap order-action-bar"> | ||
| 258 | + <!-- 已配置工作流动作:每行最多 3 个、超出自动换行,点击直接执行对应动作,不再弹「请选择操作」 --> | ||
| 259 | + <view v-if="hasConfiguredActions" class="order-action-row"> | ||
| 260 | + <view | ||
| 261 | + v-for="action in configuredActions" | ||
| 262 | + :key="action.code" | ||
| 263 | + :class="['order-action-btn', 'order-action-btn--' + getActionButtonType(action.code)]" | ||
| 264 | + @click.stop="handleUnifiedProcess(orderDetail, action)" | ||
| 265 | + >{{ action.name }}</view> | ||
| 266 | + </view> | ||
| 267 | + <!-- 未配置:沿用原有节点按钮(整行展示) --> | ||
| 268 | + <view v-else class="order-action-row order-action-row--single"> | ||
| 269 | + <view v-if="showRejectButton" class="order-action-btn order-action-btn--warning" | ||
| 270 | + @click.stop="handleUnifiedReject(orderDetail)">回退</view> | ||
| 271 | + <view v-if="nextStepMap[orderDetail.taskKey]?.renewShow" class="order-action-btn order-action-btn--success" | ||
| 272 | + @click.stop="handleRenew(orderDetail)">重新提交</view> | ||
| 273 | + <view class="order-action-btn order-action-btn--primary" | ||
| 274 | + @click.stop="handleUnifiedProcess(orderDetail)">{{ processButtonText }}</view> | ||
| 271 | </view> | 275 | </view> |
| 272 | </view> | 276 | </view> |
| 273 | 277 | ||
| @@ -377,7 +381,7 @@ import { | @@ -377,7 +381,7 @@ import { | ||
| 377 | dcyUniversalApproval, | 381 | dcyUniversalApproval, |
| 378 | aiUniversalApproval | 382 | aiUniversalApproval |
| 379 | } from '@/api/regional-order-manage/regional-order-manage'; | 383 | } from '@/api/regional-order-manage/regional-order-manage'; |
| 380 | -import {nextStepMap, buzStatusMap, calculateFormatTimeDiff} from '@/common/utils/common' | 384 | +import {nextStepMap, buzStatusMap, calculateFormatTimeDiff, getActionButtonType} from '@/common/utils/common' |
| 381 | import {getTaskActions} from '@/api/work-order-manage/work-order-manage'; | 385 | import {getTaskActions} from '@/api/work-order-manage/work-order-manage'; |
| 382 | import {useUploadImgs} from '@/common/utils/useUploadImgs' | 386 | import {useUploadImgs} from '@/common/utils/useUploadImgs' |
| 383 | import { useUserStore } from '@/pinia/user'; | 387 | import { useUserStore } from '@/pinia/user'; |
| @@ -585,6 +589,21 @@ const showRejectButton = computed(() => { | @@ -585,6 +589,21 @@ const showRejectButton = computed(() => { | ||
| 585 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow | 589 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow |
| 586 | }) | 590 | }) |
| 587 | 591 | ||
| 592 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ | ||
| 593 | +const hasConfiguredActions = computed(() => | ||
| 594 | + Array.isArray(configuredActions.value) && configuredActions.value.length > 0) | ||
| 595 | + | ||
| 596 | +/** 底部操作栏行数:详情内容据此预留底部间距,避免按钮遮挡内容 */ | ||
| 597 | +const actionBarRows = computed(() => { | ||
| 598 | + if (hasConfiguredActions.value) { | ||
| 599 | + return Math.ceil(configuredActions.value.length / 3) | ||
| 600 | + } | ||
| 601 | + let rows = 1 | ||
| 602 | + if (showRejectButton.value) rows++ | ||
| 603 | + if (nextStepMap[orderDetail.value.taskKey]?.renewShow) rows++ | ||
| 604 | + return rows | ||
| 605 | +}) | ||
| 606 | + | ||
| 588 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ | 607 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ |
| 589 | const processButtonText = computed(() => { | 608 | const processButtonText = computed(() => { |
| 590 | const actions = configuredActions.value | 609 | const actions = configuredActions.value |
| @@ -597,14 +616,14 @@ const processButtonText = computed(() => { | @@ -597,14 +616,14 @@ const processButtonText = computed(() => { | ||
| 597 | /** 统一动作弹窗 */ | 616 | /** 统一动作弹窗 */ |
| 598 | const unifiedActionsRef = ref<any>(null) | 617 | const unifiedActionsRef = ref<any>(null) |
| 599 | 618 | ||
| 600 | -/** 处理:已配置统一动作时走统一接口,否则回退原有处理 */ | ||
| 601 | -const handleUnifiedProcess = async (item: any) => { | 619 | +/** 处理:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ |
| 620 | +const handleUnifiedProcess = async (item: any, action: any = null) => { | ||
| 602 | const actions = Array.isArray(configuredActions.value) | 621 | const actions = Array.isArray(configuredActions.value) |
| 603 | ? configuredActions.value | 622 | ? configuredActions.value |
| 604 | : await queryUnifiedActions(item?.taskId) | 623 | : await queryUnifiedActions(item?.taskId) |
| 605 | if (actions) { | 624 | if (actions) { |
| 606 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 607 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 625 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 626 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 608 | return | 627 | return |
| 609 | } | 628 | } |
| 610 | handleProcess(item) | 629 | handleProcess(item) |
| @@ -1146,4 +1165,74 @@ onShow(() => {}); | @@ -1146,4 +1165,74 @@ onShow(() => {}); | ||
| 1146 | width: 70px !important; | 1165 | width: 70px !important; |
| 1147 | height: 70px !important; | 1166 | height: 70px !important; |
| 1148 | } | 1167 | } |
| 1168 | + | ||
| 1169 | +// 底部动作按钮:动作多时自动换行 | ||
| 1170 | +// 底部操作栏:白底 + 顶部投影,按钮每行最多 3 个,避免多动作时整屏遮挡工单详情 | ||
| 1171 | +.order-action-bar { | ||
| 1172 | + background-color: #fff; | ||
| 1173 | + box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06); | ||
| 1174 | + padding: 10rpx 14rpx; | ||
| 1175 | + padding-bottom: calc(10rpx + constant(safe-area-inset-bottom)); | ||
| 1176 | + padding-bottom: calc(10rpx + env(safe-area-inset-bottom)); | ||
| 1177 | +} | ||
| 1178 | + | ||
| 1179 | +.order-action-row { | ||
| 1180 | + display: flex; | ||
| 1181 | + flex-wrap: wrap; | ||
| 1182 | + margin: -6rpx; | ||
| 1183 | +} | ||
| 1184 | + | ||
| 1185 | +.order-action-btn { | ||
| 1186 | + box-sizing: border-box; | ||
| 1187 | + width: calc((100% - 36rpx) / 3); | ||
| 1188 | + height: 68rpx; | ||
| 1189 | + line-height: 68rpx; | ||
| 1190 | + margin: 6rpx; | ||
| 1191 | + padding: 0 8rpx; | ||
| 1192 | + border-radius: 8rpx; | ||
| 1193 | + font-size: 26rpx; | ||
| 1194 | + text-align: center; | ||
| 1195 | + color: #fff; | ||
| 1196 | + white-space: nowrap; | ||
| 1197 | + overflow: hidden; | ||
| 1198 | + text-overflow: ellipsis; | ||
| 1199 | +} | ||
| 1200 | + | ||
| 1201 | +// 未配置统一动作时按钮整行展示,保持原有观感 | ||
| 1202 | +.order-action-row--single .order-action-btn { | ||
| 1203 | + width: 100%; | ||
| 1204 | +} | ||
| 1205 | + | ||
| 1206 | +.order-action-btn--primary { | ||
| 1207 | + background-color: #3c9cff; | ||
| 1208 | +} | ||
| 1209 | + | ||
| 1210 | +.order-action-btn--success { | ||
| 1211 | + background-color: #5ac725; | ||
| 1212 | +} | ||
| 1213 | + | ||
| 1214 | +.order-action-btn--warning { | ||
| 1215 | + background-color: #f9ae3d; | ||
| 1216 | +} | ||
| 1217 | + | ||
| 1218 | +.order-action-btn--error { | ||
| 1219 | + background-color: #f56c6c; | ||
| 1220 | +} | ||
| 1221 | + | ||
| 1222 | +.order-action-btn--info { | ||
| 1223 | + background-color: #909399; | ||
| 1224 | +} | ||
| 1225 | + | ||
| 1226 | +// 按操作栏行数给内容预留底部间距 | ||
| 1227 | +.main-content.pb-rows-1 { | ||
| 1228 | + padding-bottom: 190rpx; | ||
| 1229 | +} | ||
| 1230 | + | ||
| 1231 | +.main-content.pb-rows-2 { | ||
| 1232 | + padding-bottom: 290rpx; | ||
| 1233 | +} | ||
| 1234 | + | ||
| 1235 | +.main-content.pb-rows-3 { | ||
| 1236 | + padding-bottom: 390rpx; | ||
| 1237 | +} | ||
| 1149 | </style> | 1238 | </style> |
| 1150 | \ No newline at end of file | 1239 | \ No newline at end of file |
pages-sub/problem/work-order-manage/index.vue
| @@ -101,20 +101,27 @@ | @@ -101,20 +101,27 @@ | ||
| 101 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> | 101 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> |
| 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"> | ||
| 105 | - <up-button type="warning" size="large" @click="handleUnifiedReject(item)" | ||
| 106 | - v-show="showRejectButton(item)">回退 | ||
| 107 | - </up-button> | ||
| 108 | - | ||
| 109 | - <up-button type="success" size="large" @click="handleRenew(item)" | ||
| 110 | - v-show="nextStepMap[item.taskKey]?.renewShow">重新提交 | ||
| 111 | - </up-button> | ||
| 112 | - | ||
| 113 | - <up-button type="primary" size="large" @click="handleUnifiedProcess(item)">{{ | ||
| 114 | - getProcessButtonText(item) | ||
| 115 | - }} | ||
| 116 | - </up-button> | ||
| 117 | - <up-button type="info" size="large" @click="handleDetail(item)">详情</up-button> | 104 | + <view class="u-body-item task-btn-row mt-20"> |
| 105 | + <!-- 已配置工作流动作:按配置逐个渲染按钮,点击直接执行对应动作,不再弹「请选择操作」 --> | ||
| 106 | + <template v-if="hasConfiguredActions(item)"> | ||
| 107 | + <view | ||
| 108 | + v-for="action in item.configuredActions" | ||
| 109 | + :key="action.code" | ||
| 110 | + :class="['task-btn', 'task-btn--' + getActionButtonType(action.code)]" | ||
| 111 | + @click.stop="handleUnifiedProcess(item, action)" | ||
| 112 | + >{{ action.name }}</view> | ||
| 113 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 114 | + </template> | ||
| 115 | + <!-- 未配置:沿用原有节点按钮 --> | ||
| 116 | + <template v-else> | ||
| 117 | + <view v-if="showRejectButton(item)" class="task-btn task-btn--warning" | ||
| 118 | + @click.stop="handleUnifiedReject(item)">回退</view> | ||
| 119 | + <view v-if="nextStepMap[item.taskKey]?.renewShow" class="task-btn task-btn--success" | ||
| 120 | + @click.stop="handleRenew(item)">重新提交</view> | ||
| 121 | + <view class="task-btn task-btn--primary" | ||
| 122 | + @click.stop="handleUnifiedProcess(item)">{{ getProcessButtonText(item) }}</view> | ||
| 123 | + <view class="task-btn task-btn--info" @click.stop="handleDetail(item)">详情</view> | ||
| 124 | + </template> | ||
| 118 | </view> | 125 | </view> |
| 119 | </view> | 126 | </view> |
| 120 | </template> | 127 | </template> |
| @@ -291,7 +298,7 @@ import { | @@ -291,7 +298,7 @@ import { | ||
| 291 | } from '@/api/work-order-manage/work-order-manage' | 298 | } from '@/api/work-order-manage/work-order-manage' |
| 292 | // 从用户store获取角色信息 | 299 | // 从用户store获取角色信息 |
| 293 | import { useUserStore } from '@/pinia/user'; | 300 | import { useUserStore } from '@/pinia/user'; |
| 294 | -import { nextStepMap, buzStatusMap } from '@/common/utils/common' | 301 | +import { nextStepMap, buzStatusMap, getActionButtonType } from '@/common/utils/common' |
| 295 | // 引入图片上传组合式函数(与参考页面一致) | 302 | // 引入图片上传组合式函数(与参考页面一致) |
| 296 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 303 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 297 | 304 | ||
| @@ -539,14 +546,18 @@ const getProcessButtonText = (item) => { | @@ -539,14 +546,18 @@ const getProcessButtonText = (item) => { | ||
| 539 | return nextStepMap[item.taskKey]?.btnText | 546 | return nextStepMap[item.taskKey]?.btnText |
| 540 | } | 547 | } |
| 541 | 548 | ||
| 542 | -/** 待办-处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | ||
| 543 | -const handleUnifiedProcess = async (item) => { | 549 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ |
| 550 | +const hasConfiguredActions = (item) => | ||
| 551 | + Array.isArray(item?.configuredActions) && item.configuredActions.length > 0 | ||
| 552 | + | ||
| 553 | +/** 待办-处理工单:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ | ||
| 554 | +const handleUnifiedProcess = async (item, action = null) => { | ||
| 544 | const actions = Array.isArray(item?.configuredActions) | 555 | const actions = Array.isArray(item?.configuredActions) |
| 545 | ? item.configuredActions | 556 | ? item.configuredActions |
| 546 | : await queryUnifiedActions(item) | 557 | : await queryUnifiedActions(item) |
| 547 | if (actions) { | 558 | if (actions) { |
| 548 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 549 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 559 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 560 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 550 | return | 561 | return |
| 551 | } | 562 | } |
| 552 | handleProcess(item) | 563 | handleProcess(item) |
| @@ -842,9 +853,44 @@ onUnload(() => { | @@ -842,9 +853,44 @@ onUnload(() => { | ||
| 842 | border-radius: 12rpx; | 853 | border-radius: 12rpx; |
| 843 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); | 854 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
| 844 | } | 855 | } |
| 845 | -.u-body-item-btn-wrap :deep(.u-button--large){ | 856 | +// 卡片操作按钮:自动换行、右对齐,动作多时每行 3~4 个,不再整行铺满 |
| 857 | +.task-btn-row { | ||
| 858 | + display: flex; | ||
| 859 | + flex-wrap: wrap; | ||
| 860 | + justify-content: flex-end; | ||
| 861 | + margin-bottom: -10rpx; | ||
| 862 | +} | ||
| 863 | + | ||
| 864 | +.task-btn { | ||
| 865 | + box-sizing: border-box; | ||
| 866 | + height: 56rpx; | ||
| 867 | + line-height: 56rpx; | ||
| 868 | + padding: 0 24rpx; | ||
| 869 | + margin: 0 0 10rpx 12rpx; | ||
| 870 | + border-radius: 8rpx; | ||
| 871 | + font-size: 24rpx; | ||
| 872 | + color: #fff; | ||
| 873 | + white-space: nowrap; | ||
| 874 | +} | ||
| 875 | + | ||
| 876 | +.task-btn--primary { | ||
| 877 | + background-color: #3c9cff; | ||
| 878 | +} | ||
| 879 | + | ||
| 880 | +.task-btn--success { | ||
| 881 | + background-color: #5ac725; | ||
| 882 | +} | ||
| 883 | + | ||
| 884 | +.task-btn--warning { | ||
| 885 | + background-color: #f9ae3d; | ||
| 886 | +} | ||
| 887 | + | ||
| 888 | +.task-btn--error { | ||
| 889 | + background-color: #f56c6c; | ||
| 890 | +} | ||
| 846 | 891 | ||
| 847 | - height: 28px; | 892 | +.task-btn--info { |
| 893 | + background-color: #909399; | ||
| 848 | } | 894 | } |
| 849 | 895 | ||
| 850 | 896 |
pages-sub/problem/work-order-manage/order-detail.vue
| @@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
| 9 | ></up-loading-page> | 9 | ></up-loading-page> |
| 10 | 10 | ||
| 11 | <!-- 主内容容器 --> | 11 | <!-- 主内容容器 --> |
| 12 | - <view v-else class="main-content"> | 12 | + <view v-else class="main-content" :class="'pb-rows-' + actionBarRows"> |
| 13 | <!-- 顶部固定Tabs --> | 13 | <!-- 顶部固定Tabs --> |
| 14 | <up-sticky :bgColor="'#fff'"> | 14 | <up-sticky :bgColor="'#fff'"> |
| 15 | <up-tabs | 15 | <up-tabs |
| @@ -256,20 +256,25 @@ | @@ -256,20 +256,25 @@ | ||
| 256 | </view> | 256 | </view> |
| 257 | 257 | ||
| 258 | <!-- activeTab==0的时候才出现, 也就是待办 --> | 258 | <!-- activeTab==0的时候才出现, 也就是待办 --> |
| 259 | - <view v-if="activeTab==0&&(configuredActions||nextStepMap[orderDetail.taskKey])" class="fixed-bottom-btn-wrap"> | ||
| 260 | - <view class="u-body-item u-flex common-justify-between common-item-center "> | ||
| 261 | - <up-button type="warning" size="normal" @click="handleUnifiedReject(orderDetail)" | ||
| 262 | - v-show="showRejectButton">回退 | ||
| 263 | - </up-button> | ||
| 264 | - | ||
| 265 | - <up-button type="success" size="normal" @click="handleRenew(orderDetail)" | ||
| 266 | - v-show="nextStepMap[orderDetail.taskKey]?.renewShow">重新提交 | ||
| 267 | - </up-button> | ||
| 268 | - | ||
| 269 | - <up-button type="primary" size="normal" @click="handleUnifiedProcess(orderDetail)">{{ | ||
| 270 | - processButtonText | ||
| 271 | - }} | ||
| 272 | - </up-button> | 259 | + <view v-if="activeTab==0&&(configuredActions||nextStepMap[orderDetail.taskKey])" |
| 260 | + class="fixed-bottom-btn-wrap order-action-bar"> | ||
| 261 | + <!-- 已配置工作流动作:每行最多 3 个、超出自动换行,点击直接执行对应动作,不再弹「请选择操作」 --> | ||
| 262 | + <view v-if="hasConfiguredActions" class="order-action-row"> | ||
| 263 | + <view | ||
| 264 | + v-for="action in configuredActions" | ||
| 265 | + :key="action.code" | ||
| 266 | + :class="['order-action-btn', 'order-action-btn--' + getActionButtonType(action.code)]" | ||
| 267 | + @click.stop="handleUnifiedProcess(orderDetail, action)" | ||
| 268 | + >{{ action.name }}</view> | ||
| 269 | + </view> | ||
| 270 | + <!-- 未配置:沿用原有节点按钮(整行展示) --> | ||
| 271 | + <view v-else class="order-action-row order-action-row--single"> | ||
| 272 | + <view v-if="showRejectButton" class="order-action-btn order-action-btn--warning" | ||
| 273 | + @click.stop="handleUnifiedReject(orderDetail)">回退</view> | ||
| 274 | + <view v-if="nextStepMap[orderDetail.taskKey]?.renewShow" class="order-action-btn order-action-btn--success" | ||
| 275 | + @click.stop="handleRenew(orderDetail)">重新提交</view> | ||
| 276 | + <view class="order-action-btn order-action-btn--primary" | ||
| 277 | + @click.stop="handleUnifiedProcess(orderDetail)">{{ processButtonText }}</view> | ||
| 273 | </view> | 278 | </view> |
| 274 | </view> | 279 | </view> |
| 275 | 280 | ||
| @@ -376,7 +381,7 @@ import { | @@ -376,7 +381,7 @@ import { | ||
| 376 | universalApproval, | 381 | universalApproval, |
| 377 | getTaskActions | 382 | getTaskActions |
| 378 | } from '@/api/work-order-manage/work-order-manage'; | 383 | } from '@/api/work-order-manage/work-order-manage'; |
| 379 | -import {nextStepMap, buzStatusMap, calculateFormatTimeDiff} from '@/common/utils/common' | 384 | +import {nextStepMap, buzStatusMap, calculateFormatTimeDiff, getActionButtonType} from '@/common/utils/common' |
| 380 | // 引入图片上传组合式函数 | 385 | // 引入图片上传组合式函数 |
| 381 | import {useUploadImgs} from '@/common/utils/useUploadImgs' | 386 | import {useUploadImgs} from '@/common/utils/useUploadImgs' |
| 382 | 387 | ||
| @@ -722,6 +727,21 @@ const showRejectButton = computed(() => { | @@ -722,6 +727,21 @@ const showRejectButton = computed(() => { | ||
| 722 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow | 727 | return !!nextStepMap[orderDetail.value.taskKey]?.backShow |
| 723 | }) | 728 | }) |
| 724 | 729 | ||
| 730 | +/** 是否已配置统一动作(配置后按配置逐个渲染动作按钮) */ | ||
| 731 | +const hasConfiguredActions = computed(() => | ||
| 732 | + Array.isArray(configuredActions.value) && configuredActions.value.length > 0) | ||
| 733 | + | ||
| 734 | +/** 底部操作栏行数:详情内容据此预留底部间距,避免按钮遮挡内容 */ | ||
| 735 | +const actionBarRows = computed(() => { | ||
| 736 | + if (hasConfiguredActions.value) { | ||
| 737 | + return Math.ceil(configuredActions.value.length / 3) | ||
| 738 | + } | ||
| 739 | + let rows = 1 | ||
| 740 | + if (showRejectButton.value) rows++ | ||
| 741 | + if (nextStepMap[orderDetail.value.taskKey]?.renewShow) rows++ | ||
| 742 | + return rows | ||
| 743 | +}) | ||
| 744 | + | ||
| 725 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ | 745 | /** 主按钮文案:已配置统一动作时取配置的动作名称,未配置时沿用原有节点规则 */ |
| 726 | const processButtonText = computed(() => { | 746 | const processButtonText = computed(() => { |
| 727 | const actions = configuredActions.value | 747 | const actions = configuredActions.value |
| @@ -745,14 +765,14 @@ const queryUnifiedActions = async (item: any) => { | @@ -745,14 +765,14 @@ const queryUnifiedActions = async (item: any) => { | ||
| 745 | return null | 765 | return null |
| 746 | } | 766 | } |
| 747 | 767 | ||
| 748 | -/** 处理工单:已配置统一动作时走新逻辑,否则回退原有处理 */ | ||
| 749 | -const handleUnifiedProcess = async (item: any) => { | 768 | +/** 处理工单:已配置统一动作时直接执行点击的配置动作,否则回退原有处理 */ |
| 769 | +const handleUnifiedProcess = async (item: any, action: any = null) => { | ||
| 750 | const actions = Array.isArray(configuredActions.value) | 770 | const actions = Array.isArray(configuredActions.value) |
| 751 | ? configuredActions.value | 771 | ? configuredActions.value |
| 752 | : await queryUnifiedActions(item) | 772 | : await queryUnifiedActions(item) |
| 753 | if (actions) { | 773 | if (actions) { |
| 754 | - // 主按钮文案就是第一个动作,直接执行该动作,不再弹「请选择操作」让用户二次选择 | ||
| 755 | - unifiedActionsRef.value?.open(item, actions.slice(0, 1)) | 774 | + // 传入的动作即用户点击的配置按钮,直接执行该动作,不再弹「请选择操作」二次选择 |
| 775 | + unifiedActionsRef.value?.open(item, [action || actions[0]]) | ||
| 756 | return | 776 | return |
| 757 | } | 777 | } |
| 758 | handleProcess(item) | 778 | handleProcess(item) |
| @@ -1151,4 +1171,74 @@ onShow(() => { | @@ -1151,4 +1171,74 @@ onShow(() => { | ||
| 1151 | width: 70px !important; // 与多图保持一致 | 1171 | width: 70px !important; // 与多图保持一致 |
| 1152 | height: 70px !important; | 1172 | height: 70px !important; |
| 1153 | } | 1173 | } |
| 1174 | + | ||
| 1175 | +// 底部动作按钮:动作多时自动换行 | ||
| 1176 | +// 底部操作栏:白底 + 顶部投影,按钮每行最多 3 个,避免多动作时整屏遮挡工单详情 | ||
| 1177 | +.order-action-bar { | ||
| 1178 | + background-color: #fff; | ||
| 1179 | + box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06); | ||
| 1180 | + padding: 10rpx 14rpx; | ||
| 1181 | + padding-bottom: calc(10rpx + constant(safe-area-inset-bottom)); | ||
| 1182 | + padding-bottom: calc(10rpx + env(safe-area-inset-bottom)); | ||
| 1183 | +} | ||
| 1184 | + | ||
| 1185 | +.order-action-row { | ||
| 1186 | + display: flex; | ||
| 1187 | + flex-wrap: wrap; | ||
| 1188 | + margin: -6rpx; | ||
| 1189 | +} | ||
| 1190 | + | ||
| 1191 | +.order-action-btn { | ||
| 1192 | + box-sizing: border-box; | ||
| 1193 | + width: calc((100% - 36rpx) / 3); | ||
| 1194 | + height: 68rpx; | ||
| 1195 | + line-height: 68rpx; | ||
| 1196 | + margin: 6rpx; | ||
| 1197 | + padding: 0 8rpx; | ||
| 1198 | + border-radius: 8rpx; | ||
| 1199 | + font-size: 26rpx; | ||
| 1200 | + text-align: center; | ||
| 1201 | + color: #fff; | ||
| 1202 | + white-space: nowrap; | ||
| 1203 | + overflow: hidden; | ||
| 1204 | + text-overflow: ellipsis; | ||
| 1205 | +} | ||
| 1206 | + | ||
| 1207 | +// 未配置统一动作时按钮整行展示,保持原有观感 | ||
| 1208 | +.order-action-row--single .order-action-btn { | ||
| 1209 | + width: 100%; | ||
| 1210 | +} | ||
| 1211 | + | ||
| 1212 | +.order-action-btn--primary { | ||
| 1213 | + background-color: #3c9cff; | ||
| 1214 | +} | ||
| 1215 | + | ||
| 1216 | +.order-action-btn--success { | ||
| 1217 | + background-color: #5ac725; | ||
| 1218 | +} | ||
| 1219 | + | ||
| 1220 | +.order-action-btn--warning { | ||
| 1221 | + background-color: #f9ae3d; | ||
| 1222 | +} | ||
| 1223 | + | ||
| 1224 | +.order-action-btn--error { | ||
| 1225 | + background-color: #f56c6c; | ||
| 1226 | +} | ||
| 1227 | + | ||
| 1228 | +.order-action-btn--info { | ||
| 1229 | + background-color: #909399; | ||
| 1230 | +} | ||
| 1231 | + | ||
| 1232 | +// 按操作栏行数给内容预留底部间距 | ||
| 1233 | +.main-content.pb-rows-1 { | ||
| 1234 | + padding-bottom: 190rpx; | ||
| 1235 | +} | ||
| 1236 | + | ||
| 1237 | +.main-content.pb-rows-2 { | ||
| 1238 | + padding-bottom: 290rpx; | ||
| 1239 | +} | ||
| 1240 | + | ||
| 1241 | +.main-content.pb-rows-3 { | ||
| 1242 | + padding-bottom: 390rpx; | ||
| 1243 | +} | ||
| 1154 | </style> | 1244 | </style> |
| 1155 | \ No newline at end of file | 1245 | \ No newline at end of file |