Commit 2f1e317685883e87c449af0e779743548d5fb3f2

Authored by 刘淇
1 parent 4fa8cf12

图片参数处理

common/utils/common.js
@@ -42,16 +42,6 @@ export const nextStepMap = { @@ -42,16 +42,6 @@ export const nextStepMap = {
42 }, 42 },
43 } 43 }
44 44
45 -// ylTeamLeader 养护组长退回:210  
46 -// ylTeamLeader 养护组长分配:110  
47 -// ylInspectorStart 巡查员结束工单:200  
48 -// ylInspectorStart 巡查员重新发起:100  
49 -// ylWorker 养护员退回 :220  
50 -// ylWorker 养护员实施 :120  
51 -// ylTeamLeaderConfirm 养护组长验收通过: 130  
52 -// ylTeamLeaderConfirm 养护组长验收不通过:230  
53 -// ylInspector 巡查员验收通过:140  
54 -// ylInspector 巡查员验收不通过:240  
55 45
56 export const buzStatusMap = { 46 export const buzStatusMap = {
57 '000' :'巡查员发起', 47 '000' :'巡查员发起',
pages-sub/daily/maintain-manage/add-record.vue
@@ -116,38 +116,14 @@ export default { @@ -116,38 +116,14 @@ export default {
116 } 116 }
117 } 117 }
118 ], 118 ],
119 - // progress: [  
120 - // {  
121 - //  
122 - // required: true,  
123 - // message: '请设置完成进度',  
124 - // trigger: ['change'],  
125 - // validator: (rule, value, callback) => {  
126 - // // 第一步:校验是否为空/0  
127 - // if (!value && value !== 0) {  
128 - // callback(new Error('请设置完成进度'))  
129 - // }  
130 - // // 第二步:校验是否大于初始进度  
131 - // else if (value <= this.initProgress) {  
132 - // callback(new Error(`完成进度必须大于${this.initProgress}%`))  
133 - // }  
134 - // // 校验通过  
135 - // else {  
136 - // callback()  
137 - // }  
138 - // }  
139 - // }  
140 - // ] 119 +
141 } 120 }
142 } 121 }
143 }, 122 },
144 onLoad(option) { 123 onLoad(option) {
145 console.log('页面参数:', option) 124 console.log('页面参数:', option)
146 this.paramsOptins = option 125 this.paramsOptins = option
147 - // // 初始化初始进度  
148 - // this.initProgress = option.finishPercent ? Number(option.finishPercent)+1 : 0  
149 - // // 关键修复:初始进度值设为 初始进度+1,避免刚进入就触发校验失败  
150 - // this.inspectForm.progress = this.initProgress 126 +
151 127
152 }, 128 },
153 onReady() { 129 onReady() {
@@ -158,30 +134,7 @@ export default { @@ -158,30 +134,7 @@ export default {
158 /** 134 /**
159 * 进度滑块变化处理 - 核心优化:实时触发校验 + 状态更新 135 * 进度滑块变化处理 - 核心优化:实时触发校验 + 状态更新
160 */ 136 */
161 - handleProgressChange(value) {  
162 - // // 1. 强制修正非法值(兜底)  
163 - // console.log(value)  
164 - // if (value <= this.initProgress) {  
165 - // console.log('123')  
166 - // this.inspectForm.progress = this.initProgress + 1  
167 - // uni.showToast({  
168 - // title: `进度不能低于${this.initProgress}%`,  
169 - // icon: 'none',  
170 - // duration: 1500  
171 - // })  
172 - // }  
173 - //  
174 - // // 2. 关键:手动触发progress字段的校验,实时更新提示状态  
175 - // this.$nextTick(async () => {  
176 - // try {  
177 - // // 触发单个字段校验  
178 - // await this.$refs.inspectFormRef.validateField('progress')  
179 - // } catch (err) {  
180 - // // 校验失败时uView会自动显示提示,此处无需额外处理  
181 - // console.log('进度校验失败:', err)  
182 - // }  
183 - // })  
184 - }, 137 +
185 138
186 /** 139 /**
187 * 删除图片 140 * 删除图片
pages-sub/daily/quick-order/add-order.vue
@@ -445,8 +445,8 @@ export default { @@ -445,8 +445,8 @@ export default {
445 const submitData = { 445 const submitData = {
446 roadId: this.workOrderForm.roadId, 446 roadId: this.workOrderForm.roadId,
447 roadName: this.workOrderForm.roadName, 447 roadName: this.workOrderForm.roadName,
448 - imgs: this.getImgUrlList(this.problemImgsList),  
449 - longRangeImgList: this.getImgUrlList(this.completeImgsList), 448 + problemsImgs: this.getImgUrlList(this.problemImgsList),
  449 + endImgs : this.getImgUrlList(this.completeImgsList),
450 remark: this.workOrderForm.problemDesc.trim(), 450 remark: this.workOrderForm.problemDesc.trim(),
451 handleResult: this.workOrderForm.handleResult.trim(), 451 handleResult: this.workOrderForm.handleResult.trim(),
452 latLonType: 2, 452 latLonType: 2,
pages-sub/daily/quick-order/index.vue
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <up-sticky> 4 <up-sticky>
5 <view class="search-header"> 5 <view class="search-header">
6 <!-- 左侧下拉框:替换为uView Plus的Select组件 --> 6 <!-- 左侧下拉框:替换为uView Plus的Select组件 -->
7 - <view class="select-wrap"> 7 + <view class="select-wrap common-text-color">
8 <up-select 8 <up-select
9 v-model:current="selectedSortValue" 9 v-model:current="selectedSortValue"
10 :options="sortOptions" 10 :options="sortOptions"
@@ -173,8 +173,7 @@ const handleAddOrder = () =&gt; { @@ -173,8 +173,7 @@ const handleAddOrder = () =&gt; {
173 173
174 // 下拉选择框容器 174 // 下拉选择框容器
175 .select-wrap { 175 .select-wrap {
176 - width: 80px;  
177 - color: #333; 176 + width: 60px;
178 margin-right: 20rpx; 177 margin-right: 20rpx;
179 // 适配up-select样式 178 // 适配up-select样式
180 :deep(.u-select) { 179 :deep(.u-select) {
pages-sub/problem/work-order-manage/add-order.vue
@@ -118,11 +118,11 @@ @@ -118,11 +118,11 @@
118 <!-- 完成时间 --> 118 <!-- 完成时间 -->
119 <up-form-item 119 <up-form-item
120 label="希望完成时间" 120 label="希望完成时间"
121 - prop="finishDate" 121 + prop="expectedFinishDate"
122 @click="show=true;hideKeyboard()" 122 @click="show=true;hideKeyboard()"
123 > 123 >
124 <up-input 124 <up-input
125 - v-model="workOrderForm.finishDate" 125 + v-model="workOrderForm.expectedFinishDate"
126 border="none" 126 border="none"
127 readonly 127 readonly
128 placeholder="点击选择时间" 128 placeholder="点击选择时间"
@@ -155,11 +155,11 @@ @@ -155,11 +155,11 @@
155 <!-- 完成时间选择器 --> 155 <!-- 完成时间选择器 -->
156 <up-datetime-picker 156 <up-datetime-picker
157 :show="show" 157 :show="show"
158 - v-model="finishDate" 158 + v-model="expectedFinishDate"
159 mode="datetime" 159 mode="datetime"
160 :min-date="new Date()" 160 :min-date="new Date()"
161 @cancel="show = false" 161 @cancel="show = false"
162 - @confirm="finishDateConfirm" 162 + @confirm="expectedFinishDateConfirm"
163 ></up-datetime-picker> 163 ></up-datetime-picker>
164 </view> 164 </view>
165 </template> 165 </template>
@@ -201,7 +201,7 @@ const currentActionSheetData = reactive({ @@ -201,7 +201,7 @@ const currentActionSheetData = reactive({
201 }) 201 })
202 // 完成时间选择器控制 202 // 完成时间选择器控制
203 const show = ref(false) 203 const show = ref(false)
204 -const finishDate = ref(Date.now()) 204 +const expectedFinishDate = ref(Date.now())
205 205
206 // ========== 重新提交相关状态(核心:本地存储读取) ========== 206 // ========== 重新提交相关状态(核心:本地存储读取) ==========
207 const isRenew = ref(false); // 是否为重新提交状态 207 const isRenew = ref(false); // 是否为重新提交状态
@@ -223,7 +223,7 @@ const workOrderForm = reactive({ @@ -223,7 +223,7 @@ const workOrderForm = reactive({
223 problemDesc: '', // 情况描述 223 problemDesc: '', // 情况描述
224 lat: 0, // 纬度 224 lat: 0, // 纬度
225 lon: 0, // 经度 225 lon: 0, // 经度
226 - finishDate: '', // 完成时间 226 + expectedFinishDate: '', // 希望完成时间
227 }) 227 })
228 228
229 // ========== 表单校验规则 ========== 229 // ========== 表单校验规则 ==========
@@ -318,7 +318,7 @@ const echoOrderData = (orderItem) =&gt; { @@ -318,7 +318,7 @@ const echoOrderData = (orderItem) =&gt; {
318 workOrderForm.problemDesc = orderItem.remark || ''; 318 workOrderForm.problemDesc = orderItem.remark || '';
319 workOrderForm.lat = orderItem.lat || 0; 319 workOrderForm.lat = orderItem.lat || 0;
320 workOrderForm.lon = orderItem.lon || 0; 320 workOrderForm.lon = orderItem.lon || 0;
321 - workOrderForm.finishDate = orderItem.finishDate || timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss'); 321 + workOrderForm.expectedFinishDate = orderItem.expectedFinishDate || timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss');
322 322
323 // 2. 上传图片回显(兼容useUploadImgs格式) 323 // 2. 上传图片回显(兼容useUploadImgs格式)
324 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { 324 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) {
@@ -480,9 +480,9 @@ const chooseWorkLocation = () =&gt; { @@ -480,9 +480,9 @@ const chooseWorkLocation = () =&gt; {
480 /** 480 /**
481 * 完成时间确认 481 * 完成时间确认
482 */ 482 */
483 -const finishDateConfirm = (e) => { 483 +const expectedFinishDateConfirm = (e) => {
484 console.log('选择的完成时间:', e) 484 console.log('选择的完成时间:', e)
485 - workOrderForm.finishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss') 485 + workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
486 show.value = false 486 show.value = false
487 } 487 }
488 488
@@ -505,7 +505,7 @@ const submitWorkOrder = async () =&gt; { @@ -505,7 +505,7 @@ const submitWorkOrder = async () =&gt; {
505 const commonSubmitData = { 505 const commonSubmitData = {
506 roadId: workOrderForm.roadId, 506 roadId: workOrderForm.roadId,
507 roadName: workOrderForm.roadName, 507 roadName: workOrderForm.roadName,
508 - imgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法 508 + problemsImgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法
509 remark: workOrderForm.problemDesc.trim(), 509 remark: workOrderForm.problemDesc.trim(),
510 latLonType: 2, 510 latLonType: 2,
511 lat: workOrderForm.lat, 511 lat: workOrderForm.lat,
@@ -513,7 +513,7 @@ const submitWorkOrder = async () =&gt; { @@ -513,7 +513,7 @@ const submitWorkOrder = async () =&gt; {
513 lonLatAddress: workOrderForm.workLocation, 513 lonLatAddress: workOrderForm.workLocation,
514 pressingType: workOrderForm.pressingType, 514 pressingType: workOrderForm.pressingType,
515 orderName: workOrderForm.orderName, 515 orderName: workOrderForm.orderName,
516 - finishDate: workOrderForm.finishDate, 516 + expectedFinishDate: workOrderForm.expectedFinishDate,
517 sourceId: 1, 517 sourceId: 1,
518 sourceName: '园林', 518 sourceName: '园林',
519 busiLine: 'yl' 519 busiLine: 'yl'
pages-sub/problem/work-order-manage/index.vue
@@ -510,12 +510,12 @@ const confirmReject = async () =&gt; { @@ -510,12 +510,12 @@ const confirmReject = async () =&gt; {
510 // 严格校验回退原因(去除首尾空格) 510 // 严格校验回退原因(去除首尾空格)
511 const rejectReasonTrim = rejectReason.value.trim(); 511 const rejectReasonTrim = rejectReason.value.trim();
512 if (!rejectReasonTrim) { 512 if (!rejectReasonTrim) {
513 - uni.showToast({title: '请填写回退原因', icon: 'none', duration: 2000}); 513 + uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000});
514 return; 514 return;
515 } 515 }
516 // 校验当前工单有效性 516 // 校验当前工单有效性
517 if (!currentRejectItem.value || !currentRejectItem.value.id) { 517 if (!currentRejectItem.value || !currentRejectItem.value.id) {
518 - uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 2000}); 518 + uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration:1000});
519 rejectModalShow.value = false; 519 rejectModalShow.value = false;
520 return; 520 return;
521 } 521 }
@@ -559,12 +559,12 @@ const handleAddOrder = () =&gt; { @@ -559,12 +559,12 @@ const handleAddOrder = () =&gt; {
559 const handleAcceptModalConfirm = async () => { 559 const handleAcceptModalConfirm = async () => {
560 // 1. 校验验收原因是否为空 560 // 1. 校验验收原因是否为空
561 if (!acceptReason.value.trim()) { 561 if (!acceptReason.value.trim()) {
562 - uni.showToast({title: '请填写验收原因', icon: 'none', duration: 2000}); 562 + uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000});
563 return; 563 return;
564 } 564 }
565 // 2. 校验验收原因长度 565 // 2. 校验验收原因长度
566 if (acceptReason.value.length > 200) { 566 if (acceptReason.value.length > 200) {
567 - uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 2000}); 567 + uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000});
568 return; 568 return;
569 } 569 }
570 try { 570 try {
@@ -592,14 +592,14 @@ const handleAcceptModalConfirm = async () =&gt; { @@ -592,14 +592,14 @@ const handleAcceptModalConfirm = async () =&gt; {
592 } 592 }
593 const acceptRes = await universalApproval(postData); 593 const acceptRes = await universalApproval(postData);
594 // 4. 操作成功处理 594 // 4. 操作成功处理
595 - uni.showToast({title: '提交成功', icon: 'success', duration: 1500}); 595 + uni.showToast({title: '提交成功', icon: 'success', duration: 1000});
596 acceptModalShow.value = false; 596 acceptModalShow.value = false;
597 acceptReason.value = ''; // 清空验收原因 597 acceptReason.value = ''; // 清空验收原因
598 paging.value?.reload(); // 刷新工单列表 598 paging.value?.reload(); // 刷新工单列表
599 } catch (error) { 599 } catch (error) {
600 // 5. 操作失败处理 600 // 5. 操作失败处理
601 console.error('养护组长验收失败:', error); 601 console.error('养护组长验收失败:', error);
602 - uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 2000}); 602 + uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000});
603 } 603 }
604 }; 604 };
605 605
@@ -679,12 +679,6 @@ onShow(() =&gt; { @@ -679,12 +679,6 @@ onShow(() =&gt; {
679 } 679 }
680 } 680 }
681 681
682 -.reject-textarea {  
683 - font-size: 28rpx;  
684 - padding: 16rpx;  
685 - border: 1rpx solid #e4e7ed;  
686 - border-radius: 8rpx;  
687 -}  
688 682
689 .upload-wrap { 683 .upload-wrap {
690 margin-top: 20rpx; 684 margin-top: 20rpx;
pages-sub/problem/work-order-manage/order-detail.vue
@@ -110,7 +110,7 @@ @@ -110,7 +110,7 @@
110 <!-- 希望完成时间 --> 110 <!-- 希望完成时间 -->
111 <up-cell 111 <up-cell
112 title="希望完成时间" 112 title="希望完成时间"
113 - :value="orderDetail.finishDate === 0 ? '未设置' : timeFormat(orderDetail.finishDate, 'yyyy-mm-dd hh:MM:ss')" 113 + :value="orderDetail.expectedFinishDate === 0 ? '未设置' : timeFormat(orderDetail.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss')"
114 align="middle" 114 align="middle"
115 :border="false" 115 :border="false"
116 ></up-cell> 116 ></up-cell>
@@ -131,7 +131,9 @@ @@ -131,7 +131,9 @@
131 <view style="min-width: 200rpx">共同处理人</view> 131 <view style="min-width: 200rpx">共同处理人</view>
132 </template> 132 </template>
133 <template #value> 133 <template #value>
134 - <view class="common-text-color up-line-1">{{ orderDetail.coHandlersName.join(',') || '--' }}</view> 134 + <view class="common-text-color up-line-1">
  135 + {{ Array.isArray(orderDetail.coHandlersName) && orderDetail.coHandlersName.length > 0 ? orderDetail.coHandlersName.join(',') : '--' }}
  136 + </view>
135 </template> 137 </template>
136 </up-cell> 138 </up-cell>
137 </up-cell-group> 139 </up-cell-group>
@@ -167,10 +169,10 @@ @@ -167,10 +169,10 @@
167 v-if="processData.activityNodes && processData.activityNodes.length" 169 v-if="processData.activityNodes && processData.activityNodes.length"
168 :list="processData.activityNodes" 170 :list="processData.activityNodes"
169 :current="getCurrentStepIndex()" 171 :current="getCurrentStepIndex()"
170 - direction="column"  
171 - active-color="#3c9cff"  
172 - inactive-color="#999"  
173 - class="vertical-steps" 172 + direction="column"
  173 + active-color="#3c9cff"
  174 + inactive-color="#999"
  175 + class="vertical-steps"
174 > 176 >
175 <template > 177 <template >
176 <up-steps-item 178 <up-steps-item
@@ -193,7 +195,7 @@ @@ -193,7 +195,7 @@
193 <!-- 时间行 --> 195 <!-- 时间行 -->
194 <view class="time-line"> 196 <view class="time-line">
195 处理时间:{{ timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss') }} 197 处理时间:{{ timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss') }}
196 - <text v-if="item.endTime"> - {{ timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss') }}</text> 198 + <text v-if="item.endTime"> {{ timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss') }}</text>
197 <text v-else class="processing-tag">(处理中)</text> 199 <text v-else class="processing-tag">(处理中)</text>
198 </view> 200 </view>
199 <!-- 原因行 --> 201 <!-- 原因行 -->
@@ -230,19 +232,98 @@ @@ -230,19 +232,98 @@
230 </view> 232 </view>
231 </view> 233 </view>
232 </view> 234 </view>
  235 +
  236 + <!-- activeTab==0的时候才出现, 也就是待办 -->
  237 + <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap">
  238 + <view class="u-body-item u-flex common-justify-between common-item-center ">
  239 + <up-button type="warning" size="normal" @click="handleReject(orderDetail)"
  240 + v-show="nextStepMap[orderDetail.taskKey].backShow">回退
  241 + </up-button>
  242 +
  243 + <up-button type="success" size="normal" @click="handleRenew(orderDetail)"
  244 + v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交
  245 + </up-button>
  246 +
  247 + <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{
  248 + nextStepMap[orderDetail.taskKey].btnText
  249 + }}
  250 + </up-button>
  251 +
  252 + </view>
  253 + </view>
  254 +
  255 + <!-- 回退原因弹窗 -->
  256 + <up-modal
  257 + :show="rejectModalShow"
  258 + title="回退原因"
  259 + :closeOnClickOverlay="false"
  260 + :showConfirmButton="true"
  261 + :showCancelButton="true"
  262 + @cancel="handleRejectModalCancel"
  263 + @confirm="confirmReject"
  264 + >
  265 + <view class="reject-modal-content">
  266 + <up-textarea
  267 + v-model.trim="rejectReason"
  268 + placeholder="请输入回退原因(必填)"
  269 + rows="6"
  270 + :count="200"
  271 + maxlength="200"
  272 + class="reject-textarea"
  273 + />
  274 + </view>
  275 + </up-modal>
  276 +
  277 + <!-- 验收弹窗 -->
  278 + <up-modal
  279 + :show="acceptModalShow"
  280 + title="验收"
  281 + :closeOnClickOverlay="false"
  282 + :showConfirmButton="true"
  283 + :showCancelButton="true"
  284 + @cancel="acceptModalShow=false"
  285 + @confirm="handleAcceptModalConfirm"
  286 + >
  287 + <view class="accept-modal-content">
  288 + <!-- 第一行:单选框(通过/不通过,默认通过) -->
  289 + <view class="radio-group-wrap">
  290 + <up-radio-group v-model="acceptRadioValue">
  291 + <up-radio name="0" label="通过"></up-radio>
  292 + <up-radio name="1" label="不通过"></up-radio>
  293 + </up-radio-group>
  294 + </view>
  295 +
  296 + <!-- 第二行:必填textarea,最多200字 -->
  297 + <view class="textarea-wrap mt-30">
  298 + <up-textarea
  299 + v-model.trim="acceptReason"
  300 + placeholder="请输入验收原因(必填,最多200字)"
  301 + :required="true"
  302 + maxlength="200"
  303 + rows="5"
  304 + count
  305 + />
  306 + </view>
  307 + </view>
  308 + </up-modal>
  309 +
233 </view> 310 </view>
234 </template> 311 </template>
235 312
236 <script setup lang="ts"> 313 <script setup lang="ts">
237 -import {ref, computed} from 'vue'; 314 +import {ref, watch} from 'vue';
238 import {onLoad, onShow} from '@dcloudio/uni-app'; 315 import {onLoad, onShow} from '@dcloudio/uni-app';
239 import {timeFormat} from '@/uni_modules/uview-plus'; 316 import {timeFormat} from '@/uni_modules/uview-plus';
240 import { 317 import {
241 getMyTaskDetail, 318 getMyTaskDetail,
242 getDoneTaskDetail, 319 getDoneTaskDetail,
243 getTodoTaskDetail, 320 getTodoTaskDetail,
244 - getApprovalDetail 321 + getApprovalDetail,
  322 + universalApproval
245 } from '@/api/work-order-manage/work-order-manage'; 323 } from '@/api/work-order-manage/work-order-manage';
  324 +import { nextStepMap, buzStatusMap } from '@/common/utils/common'
  325 +// 引入图片上传组合式函数
  326 +import { useUploadImgs } from '@/common/utils/useUploadImgs'
246 327
247 // 状态管理 328 // 状态管理
248 const loading = ref(true); 329 const loading = ref(true);
@@ -263,36 +344,6 @@ const processData = ref&lt;any&gt;({ @@ -263,36 +344,6 @@ const processData = ref&lt;any&gt;({
263 todoTask: null 344 todoTask: null
264 }); 345 });
265 346
266 -const activeTopTabClick = async (item: any) => {  
267 - console.log(item)  
268 - activeTopTab.value = item.index  
269 - if (activeTopTab.value == 1) {  
270 - let getData = {  
271 - processInstanceId: processInstanceId.value,  
272 - }  
273 - const res = await getApprovalDetail(getData)  
274 - console.log(res)  
275 - // 关键:格式化数据,补充up-steps要求的title字段  
276 - if (res && res.activityNodes && res.activityNodes.length) {  
277 - // 1. 先过滤:剔除 name 为 "结束" 的节点  
278 - const filteredActivityNodes = res.activityNodes.filter(node => {  
279 - // 返回 true 保留节点,返回 false 剔除节点  
280 - return node.name !== '结束';  
281 - });  
282 - const formatActivityNodes = filteredActivityNodes.map(node => ({  
283 - ...node,  
284 - title: node.name // 补充强制字段,满足3.3.48版本组件要求  
285 - }));  
286 - processData.value = {  
287 - ...res,  
288 - activityNodes: formatActivityNodes  
289 - };  
290 - } else {  
291 - processData.value = res;  
292 - }  
293 - }  
294 -}  
295 -  
296 // 图片分类Tab列表(带角标配置) 347 // 图片分类Tab列表(带角标配置)
297 const imgTabList = ref([ 348 const imgTabList = ref([
298 {name: '开始', badge: {isDot: true}}, 349 {name: '开始', badge: {isDot: true}},
@@ -318,6 +369,7 @@ const orderDetail = ref&lt;any&gt;({ @@ -318,6 +369,7 @@ const orderDetail = ref&lt;any&gt;({
318 curingLevelName: '', 369 curingLevelName: '',
319 commitDate: 0, 370 commitDate: 0,
320 finishDate: 0, 371 finishDate: 0,
  372 + expectedFinishDate:0,
321 pressingType: 0, 373 pressingType: 0,
322 userId: 0, 374 userId: 0,
323 userName: '', 375 userName: '',
@@ -360,7 +412,7 @@ const orderDetail = ref&lt;any&gt;({ @@ -360,7 +412,7 @@ const orderDetail = ref&lt;any&gt;({
360 const currentImgList = ref([]) 412 const currentImgList = ref([])
361 413
362 const tabKeyMap = ['startImgs', 'processingImgs', 'endImgs', 'personImgs', 'materialImgs']; 414 const tabKeyMap = ['startImgs', 'processingImgs', 'endImgs', 'personImgs', 'materialImgs'];
363 -const imgTabChange = (({index}) => { 415 +const imgTabChange = (({index}: {index: number}) => {
364 console.log(index) 416 console.log(index)
365 const currentKey = tabKeyMap[index] 417 const currentKey = tabKeyMap[index]
366 console.log(currentKey) 418 console.log(currentKey)
@@ -389,16 +441,6 @@ const getCurrentStepIndex = () =&gt; { @@ -389,16 +441,6 @@ const getCurrentStepIndex = () =&gt; {
389 const { activityNodes } = processData.value; 441 const { activityNodes } = processData.value;
390 if (!activityNodes || !activityNodes.length) return 0; 442 if (!activityNodes || !activityNodes.length) return 0;
391 443
392 - // item.tasks && item.tasks[0]?.assigneeUser?.nickname  
393 - // 1. 查找第一个状态为1(处理中)的节点,即为当前步骤  
394 - // const processingNodeIndex = activityNodes.findIndex(node => node.name === '结束');  
395 - // console.log(processingNodeIndex)  
396 - // if (processingNodeIndex !== -1) {  
397 - // return activityNodes.length - 2  
398 - // }else{  
399 - // return activityNodes.length - 1  
400 - // }  
401 -  
402 // 2. 若没有处理中的节点(全部已完成),则激活最后一个节点 444 // 2. 若没有处理中的节点(全部已完成),则激活最后一个节点
403 return activityNodes.length - 1; 445 return activityNodes.length - 1;
404 } 446 }
@@ -442,6 +484,281 @@ const DetailQuery = async (taskIdStr: string) =&gt; { @@ -442,6 +484,281 @@ const DetailQuery = async (taskIdStr: string) =&gt; {
442 const taskId = ref('') 484 const taskId = ref('')
443 const activeTab = ref('') 485 const activeTab = ref('')
444 const processInstanceId = ref('') 486 const processInstanceId = ref('')
  487 +
  488 +const activeTopTabClick = async (item: any) => {
  489 + console.log(item)
  490 + activeTopTab.value = item.index
  491 + if (activeTopTab.value == 1) {
  492 + let getData = {
  493 + processInstanceId: processInstanceId.value,
  494 + }
  495 + const res = await getApprovalDetail(getData)
  496 + console.log(res)
  497 + // 关键:格式化数据,补充up-steps要求的title字段
  498 + if (res && res.activityNodes && res.activityNodes.length) {
  499 + // 1. 先过滤:剔除 name 为 "结束" 的节点
  500 + const filteredActivityNodes = res.activityNodes.filter(node => {
  501 + // 返回 true 保留节点,返回 false 剔除节点
  502 + return node.name !== '结束';
  503 + });
  504 + const formatActivityNodes = filteredActivityNodes.map(node => ({
  505 + ...node,
  506 + title: node.name // 补充强制字段,满足3.3.48版本组件要求
  507 + }));
  508 + processData.value = {
  509 + ...res,
  510 + activityNodes: formatActivityNodes
  511 + };
  512 + } else {
  513 + processData.value = res;
  514 + }
  515 + }
  516 +}
  517 +
  518 +
  519 +// ========== 新增:回退弹窗相关状态 ==========
  520 +const rejectModalShow = ref(false); // 回退modal显示开关
  521 +const rejectReason = ref(''); // 回退原因
  522 +const currentRejectItem = ref<any>(null); // 当前回退工单
  523 +// 回退图片上传配置
  524 +const rejectImgs = useUploadImgs({
  525 + maxCount: 3,
  526 + uploadText: '选择回退图片',
  527 + sizeType: ['compressed'],
  528 + formRef: null,
  529 + fieldName: 'rejectImgs'
  530 +})
  531 +// 监听上传实例响应式变化
  532 +watch(() => rejectImgs.rawImgList.value, (newVal) => {
  533 + rejectImgs.imgList = newVal
  534 +}, { deep: true })
  535 +
  536 +// ========== 新增:验收弹窗相关状态 ==========
  537 +const acceptModalShow = ref(false); // 验收弹窗显示开关
  538 +const acceptRadioValue = ref('0'); // 单选框值,默认0(通过)
  539 +const acceptReason = ref(''); // 验收原因
  540 +const currentAcceptItem = ref<any>(null); // 当前验收的工单项
  541 +
  542 +// ========== 新增:生成临时key ==========
  543 +const generateTempKey = () => {
  544 + return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000);
  545 +};
  546 +
  547 +// ========== 新增:handleRenew 重新提交工单 ==========
  548 +const handleRenew = (item: any) => {
  549 + // 校验工单有效性
  550 + if (!item || !item.id) {
  551 + uni.showToast({title: '工单信息异常,无法重新提交', icon: 'none'});
  552 + return;
  553 + }
  554 +
  555 + // 1. 生成唯一临时标识
  556 + const tempKey = generateTempKey();
  557 +
  558 + // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效)
  559 + try {
  560 + uni.setStorageSync(tempKey, item);
  561 + } catch (error) {
  562 + console.error('存储工单数据失败:', error);
  563 + uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'});
  564 + return;
  565 + }
  566 +
  567 + // 3. URL 仅传递「唯一标识」和「重新提交标记」
  568 + uni.navigateTo({
  569 + url: `/pages-sub/problem/work-order-manage/add-order?isRenew=1&tempKey=${tempKey}`
  570 + });
  571 +};
  572 +
  573 +// ========== 新增:handleReject 打开回退弹窗 ==========
  574 +const handleReject = (item: any) => {
  575 + // 校验工单有效性
  576 + if (!item || !item.id) {
  577 + uni.showToast({title: '工单信息异常,无法回退', icon: 'none'});
  578 + return;
  579 + }
  580 + currentRejectItem.value = item;
  581 + rejectReason.value = ''; // 清空上次输入
  582 + rejectImgs.clearImgs(); // 清空上传图片
  583 + rejectModalShow.value = true; // 显示回退modal
  584 +};
  585 +
  586 +// ========== 新增:回退弹窗取消按钮 ==========
  587 +const handleRejectModalCancel = () => {
  588 + rejectModalShow.value = false;
  589 + rejectReason.value = '';
  590 + rejectImgs.clearImgs(); // 清空上传图片
  591 +};
  592 +
  593 +// ========== 新增:确认回退工单 ==========
  594 +const confirmReject = async () => {
  595 + // 严格校验回退原因(去除首尾空格)
  596 + const rejectReasonTrim = rejectReason.value.trim();
  597 + if (!rejectReasonTrim) {
  598 + uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000});
  599 + return;
  600 + }
  601 + // 校验当前工单有效性
  602 + if (!currentRejectItem.value || !currentRejectItem.value.id) {
  603 + uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 1000});
  604 + rejectModalShow.value = false;
  605 + return;
  606 + }
  607 + try {
  608 + // 显示加载中,防止重复提交
  609 + uni.showLoading({title: '提交中...', mask: true});
  610 +
  611 + // 构建请求参数
  612 + const requestData = {
  613 + "returnImgs": rejectImgs.getSuccessImgUrls(),
  614 + "workerDataId": currentRejectItem.value.id,
  615 + "taskKey": currentRejectItem.value.taskKey,
  616 + "taskId": currentRejectItem.value.taskId,
  617 + "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass,
  618 + "agree": 1,
  619 + "reason": rejectReasonTrim
  620 + };
  621 + // 调用回退工单接口
  622 + const res = await universalApproval(requestData);
  623 + uni.showToast({title: '回退成功', icon: 'success', duration: 1000});
  624 + rejectModalShow.value = false;
  625 + // 重新获取工单详情,刷新页面
  626 + await DetailQuery(taskId.value);
  627 + } catch (error) {
  628 + console.error('回退工单失败:', error);
  629 + uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000});
  630 + } finally {
  631 + // 隐藏加载中
  632 + uni.hideLoading();
  633 + }
  634 +};
  635 +
  636 +// ========== 新增:handleProcess 处理工单 ==========
  637 +const handleProcess = async (item: any) => {
  638 + console.log(nextStepMap[item.taskKey]?.name)
  639 + try {
  640 + if (nextStepMap[item.taskKey]?.name == '养护组长分配') {
  641 + uni.navigateTo({
  642 + url: `/pages-sub/problem/work-order-manage/distribution-order?taskId=${item.taskId}&orderNo=${item.orderNo}&id=${item.id}`
  643 + })
  644 + }
  645 + if (nextStepMap[item.taskKey]?.name == '养护员待实施') {
  646 + // ① 生成唯一临时key
  647 + const tempKey = `maintain_order_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
  648 +
  649 + // ② 存储完整item到本地缓存
  650 + try {
  651 + uni.setStorageSync(tempKey, item);
  652 + } catch (error) {
  653 + console.error('存储养护工单数据失败:', error);
  654 + uni.showToast({title: '数据存储异常,无法跳转', icon: 'none'});
  655 + return;
  656 + }
  657 +
  658 + // ③ URL仅传递临时key
  659 + uni.navigateTo({
  660 + url: `/pages-sub/problem/work-order-manage/add-maintain-order?tempKey=${tempKey}`
  661 + })
  662 + }
  663 + // 养护组长验收 - 打开弹窗
  664 + if (nextStepMap[item.taskKey]?.name == '养护组长验收') {
  665 + currentAcceptItem.value = item; // 存储当前工单信息
  666 + acceptReason.value = ''; // 清空上次的验收原因
  667 + acceptRadioValue.value = '0'; // 重置默认选中“通过”
  668 + acceptModalShow.value = true; // 显示验收弹窗
  669 + }
  670 + // 巡查员验收 - 打开弹窗
  671 + if (nextStepMap[item.taskKey]?.name == '巡查员验收') {
  672 + currentAcceptItem.value = item; // 存储当前工单信息
  673 + acceptReason.value = ''; // 清空上次的验收原因
  674 + acceptRadioValue.value = '0'; // 重置默认选中“通过”
  675 + acceptModalShow.value = true; // 显示验收弹窗
  676 + }
  677 +
  678 + // 发起人确认
  679 + if (nextStepMap[item.taskKey]?.name == '发起人确认') {
  680 + console.log(item)
  681 + uni.showModal({
  682 + title: "结束工单",
  683 + content: "请确定是否结束工单?",
  684 + success: async function (res) {
  685 + if (res.confirm) {
  686 + // 构建请求参数
  687 + const requestData = {
  688 + "returnImgs": rejectImgs.getSuccessImgUrls(),
  689 + "workerDataId": item.id,
  690 + "taskKey":'ylInspectorStart',
  691 + "taskId": item.taskId,
  692 + "operateType": 200,
  693 + "agree": 1,
  694 + "reason": '结束工单'
  695 + };
  696 + // 调用回退工单接口
  697 + const res = await universalApproval(requestData);
  698 + uni.showToast({title: '结束成功', icon: 'success', duration: 1000});
  699 + // 重新获取工单详情,刷新页面
  700 + await DetailQuery(taskId.value);
  701 + } else if (res.cancel) {
  702 + console.log("用户点击取消");
  703 + }
  704 + },
  705 + });
  706 + }
  707 + } catch (error) {
  708 + console.error('处理工单失败:', error);
  709 + uni.showToast({title: '处理失败,请重试', icon: 'none'});
  710 + }
  711 +};
  712 +
  713 +// ========== 新增:验收弹窗确认按钮 ==========
  714 +const handleAcceptModalConfirm = async () => {
  715 + // 1. 校验验收原因是否为空
  716 + if (!acceptReason.value.trim()) {
  717 + uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000});
  718 + return;
  719 + }
  720 + // 2. 校验验收原因长度
  721 + if (acceptReason.value.length > 200) {
  722 + uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000});
  723 + return;
  724 + }
  725 + try {
  726 + // 3. 调用验收接口
  727 + console.log(currentAcceptItem.value)
  728 + let postData: any = {}
  729 + if (currentAcceptItem.value?.taskKey == 'ylTeamLeaderConfirm') { // 养护组长验收
  730 + postData = {
  731 + "taskKey": currentAcceptItem.value.taskKey, // ylTeamLeaderConfirm
  732 + "workerDataId": currentAcceptItem.value.id,
  733 + "taskId": currentAcceptItem.value.taskId,
  734 + "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,
  735 + "reason": acceptReason.value.trim()
  736 + }
  737 + }
  738 + if (currentAcceptItem.value?.taskKey == 'ylInspector') { // 巡查员验收
  739 + postData = {
  740 + "taskKey": currentAcceptItem.value.taskKey, //ylInspector
  741 + "taskId": currentAcceptItem.value.taskId,
  742 + "workerDataId": currentAcceptItem.value.id,
  743 + "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,
  744 + "reason": acceptReason.value.trim(),
  745 + "agree": acceptRadioValue.value
  746 + }
  747 + }
  748 + const acceptRes = await universalApproval(postData);
  749 + // 4. 操作成功处理
  750 + uni.showToast({title: '提交成功', icon: 'success', duration: 1000});
  751 + acceptModalShow.value = false;
  752 + acceptReason.value = ''; // 清空验收原因
  753 + // 重新获取工单详情,刷新页面
  754 + await DetailQuery(taskId.value);
  755 + } catch (error) {
  756 + // 5. 操作失败处理
  757 + console.error('验收失败:', error);
  758 + uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000});
  759 + }
  760 +};
  761 +
445 onLoad((options: any) => { 762 onLoad((options: any) => {
446 console.log('页面入参:', options) 763 console.log('页面入参:', options)
447 const {taskId: taskIdOpt, activeTab: activeTabOpt, processInstanceId: processInstanceIdOpt} = options; 764 const {taskId: taskIdOpt, activeTab: activeTabOpt, processInstanceId: processInstanceIdOpt} = options;
@@ -465,7 +782,7 @@ onShow(() =&gt; { @@ -465,7 +782,7 @@ onShow(() =&gt; {
465 782
466 // 主内容容器 783 // 主内容容器
467 .main-content { 784 .main-content {
468 - padding-bottom: 20rpx; 785 + padding-bottom: 80rpx;
469 } 786 }
470 787
471 // 顶部Tabs 788 // 顶部Tabs
@@ -601,4 +918,38 @@ onShow(() =&gt; { @@ -601,4 +918,38 @@ onShow(() =&gt; {
601 } 918 }
602 } 919 }
603 } 920 }
  921 +
  922 +
  923 +
  924 +// 回退弹窗样式
  925 +.reject-modal-content {
  926 + width: 100%;
  927 + box-sizing: border-box;
  928 + padding: 10rpx 0;
  929 +}
  930 +
  931 +
  932 +// 验收弹窗样式
  933 +.accept-modal-content {
  934 + width: 100%;
  935 + box-sizing: border-box;
  936 +}
  937 +
  938 +.radio-group-wrap {
  939 + display: flex;
  940 + align-items: center;
  941 + gap: 40rpx;
  942 + font-size: 28rpx;
  943 + margin-bottom: 20rpx;
  944 +}
  945 +
  946 +.textarea-wrap {
  947 + width: 100%;
  948 + margin-top: 30rpx;
  949 +}
  950 +
  951 +
  952 +.mt-30 {
  953 + margin-top: 30rpx;
  954 +}
604 </style> 955 </style>
605 \ No newline at end of file 956 \ No newline at end of file