Commit 6853427faf5613784424d515aed2f036cbec1084

Authored by 王彪总
1 parent 5f7ce1e4

业务线 小程序不用写死,从后端接口获取

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 = () =&gt; { @@ -255,18 +255,18 @@ const handleEndTaskModalCancel = () =&gt; {
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) =&gt; { @@ -497,14 +506,18 @@ const getProcessButtonText = (item) =&gt; {
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(() =&gt; { @@ -730,8 +743,44 @@ onShow(() =&gt; {
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) =&gt; { @@ -472,14 +487,18 @@ const getProcessButtonText = (item) =&gt; {
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(() =&gt; { @@ -720,8 +739,44 @@ onLoad(() =&gt; {
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 &#39;@/uni_modules/uview-plus&#39; @@ -335,7 +337,7 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;
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(() =&gt; { @@ -517,6 +519,21 @@ const showRejectButton = computed(() =&gt; {
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(() =&gt; { @@ -529,14 +546,14 @@ const processButtonText = computed(() =&gt; {
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) =&gt; { @@ -1001,4 +1018,74 @@ onLoad((options: any) =&gt; {
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(() =&gt; { @@ -585,6 +589,21 @@ const showRejectButton = computed(() =&gt; {
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(() =&gt; { @@ -597,14 +616,14 @@ const processButtonText = computed(() =&gt; {
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(() =&gt; {}); @@ -1146,4 +1165,74 @@ onShow(() =&gt; {});
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) =&gt; { @@ -539,14 +546,18 @@ const getProcessButtonText = (item) =&gt; {
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(() =&gt; { @@ -842,9 +853,44 @@ onUnload(() =&gt; {
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(() =&gt; { @@ -722,6 +727,21 @@ const showRejectButton = computed(() =&gt; {
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) =&gt; { @@ -745,14 +765,14 @@ const queryUnifiedActions = async (item: any) =&gt; {
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(() =&gt; { @@ -1151,4 +1171,74 @@ onShow(() =&gt; {
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