Commit 7a96cf5063e3d3a58ac990c89873850b222f24cc
1 parent
1f1f236c
重新提交
Showing
3 changed files
with
357 additions
and
85 deletions
common/utils/common.js
| 1 | 1 | export const nextStepMap = { |
| 2 | 2 | ylTeamLeader: { |
| 3 | 3 | name: '养护组长分配', |
| 4 | - btnText:'分配', | |
| 4 | + btnText: '分配', | |
| 5 | 5 | operateTypePass: 110, |
| 6 | - backShow:true | |
| 6 | + operateTypeNoPass: 210, //养护组长退回:210 | |
| 7 | + backShow: true, | |
| 8 | + renewShow: false | |
| 7 | 9 | }, |
| 8 | 10 | ylWorker: { |
| 9 | 11 | name: '养护员待实施', |
| 10 | - btnText:'实施', | |
| 12 | + btnText: '实施', | |
| 11 | 13 | operateTypePass: 120, |
| 12 | - backShow:true | |
| 14 | + backShow: true, | |
| 15 | + renewShow: false | |
| 13 | 16 | }, |
| 14 | 17 | ylTeamLeaderConfirm: { |
| 15 | 18 | name: '养护组长验收', |
| 16 | - btnText:'验收', | |
| 19 | + btnText: '验收', | |
| 17 | 20 | operateTypePass: 130, //养护组长验收通过: 130 |
| 18 | 21 | operateTypeNoPass: 230, // 养护组长验收不通过:230 |
| 19 | - backShow:false | |
| 22 | + backShow: false, | |
| 23 | + renewShow: false | |
| 20 | 24 | }, |
| 21 | - ylInspector:{ | |
| 25 | + ylInspector: { | |
| 22 | 26 | name: '巡查员验收', |
| 23 | - btnText:'验收', | |
| 27 | + btnText: '验收', | |
| 24 | 28 | operateTypePass: 140, //巡查员验收通过: 140 |
| 25 | 29 | operateTypeNoPass: 240, // 巡查员验收不通过:230 |
| 26 | - backShow:false | |
| 27 | - } | |
| 30 | + backShow: false, | |
| 31 | + renewShow: false | |
| 32 | + }, | |
| 33 | + ylInspectorStart: { | |
| 34 | + name: '发起人确认', | |
| 35 | + btnText: '结束工单', | |
| 36 | + operateTypePass: 200, //巡查员结束工单:200 | |
| 37 | + operateTypeNoPass: 240, // 巡查员验收不通过:230 | |
| 38 | + operateTypeRenew: 100, //巡查员重新发起:100 | |
| 39 | + backShow: false, | |
| 40 | + renewShow: true | |
| 41 | + }, | |
| 28 | 42 | } |
| 29 | 43 | \ No newline at end of file | ... | ... |
pages-sub/problem/work-order-manage/add-order.vue
| ... | ... | @@ -38,7 +38,6 @@ |
| 38 | 38 | disabled-color="#ffffff" |
| 39 | 39 | placeholder="请先选择工单位置" |
| 40 | 40 | border="none" |
| 41 | - | |
| 42 | 41 | ></up-input> |
| 43 | 42 | <template #right> |
| 44 | 43 | <up-icon name="arrow-right" size="16" ></up-icon> |
| ... | ... | @@ -167,12 +166,13 @@ |
| 167 | 166 | |
| 168 | 167 | <script setup> |
| 169 | 168 | import { ref, reactive, watch } from 'vue' |
| 170 | -import { onReady, onShow } from '@dcloudio/uni-app'; | |
| 169 | +import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; | |
| 171 | 170 | import { useUploadImgs } from '@/common/utils/useUploadImgs' // 引入改造后的上传逻辑 |
| 172 | 171 | import { getRoadListByLatLng } from '@/api/common' |
| 173 | -import { universalApproval } from '@/api/work-order-manage/work-order-manage' | |
| 172 | +import { universalApproval, workorderCreate } from '@/api/work-order-manage/work-order-manage' | |
| 174 | 173 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 175 | 174 | import { nextStepMap } from '@/common/utils/common' |
| 175 | + | |
| 176 | 176 | // ========== 表单Ref ========== |
| 177 | 177 | const workOrderFormRef = ref(null) |
| 178 | 178 | |
| ... | ... | @@ -203,6 +203,10 @@ const currentActionSheetData = reactive({ |
| 203 | 203 | const show = ref(false) |
| 204 | 204 | const finishDate = ref(Date.now()) |
| 205 | 205 | |
| 206 | +// ========== 重新提交相关状态(核心:本地存储读取) ========== | |
| 207 | +const isRenew = ref(false); // 是否为重新提交状态 | |
| 208 | +const renewOrderData = ref(null); // 重新提交的原有工单数据 | |
| 209 | + | |
| 206 | 210 | // ========== 下拉列表数据 ========== |
| 207 | 211 | const roadNameList = ref([]) |
| 208 | 212 | const orderNameList = ref([]) |
| ... | ... | @@ -244,6 +248,43 @@ const workOrderFormRules = reactive({ |
| 244 | 248 | }) |
| 245 | 249 | |
| 246 | 250 | // ========== 生命周期 ========== |
| 251 | +// 页面加载:读取本地存储的工单数据(核心改造) | |
| 252 | +onLoad((options) => { | |
| 253 | + // 判断是否为重新提交状态 | |
| 254 | + console.log('434') | |
| 255 | + console.log(options) | |
| 256 | + if (options.isRenew ==1 && options.tempKey) { | |
| 257 | + isRenew.value = true; | |
| 258 | + const tempKey = options.tempKey; | |
| 259 | + | |
| 260 | + // 1. 从本地同步存储中读取完整工单数据 | |
| 261 | + try { | |
| 262 | + const orderData = uni.getStorageSync(tempKey); | |
| 263 | + if (orderData && typeof orderData === 'object') { | |
| 264 | + renewOrderData.value = orderData; | |
| 265 | + | |
| 266 | + console.log('123213') | |
| 267 | + console.log(orderData) | |
| 268 | + // 2. 回显工单数据到表单 | |
| 269 | + echoOrderData(renewOrderData.value); | |
| 270 | + } else { | |
| 271 | + uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' }); | |
| 272 | + // 跳转回列表页 | |
| 273 | + setTimeout(() => uni.navigateBack(), 1000); | |
| 274 | + return; | |
| 275 | + } | |
| 276 | + } catch (error) { | |
| 277 | + console.error('读取工单数据失败:', error); | |
| 278 | + uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' }); | |
| 279 | + setTimeout(() => uni.navigateBack(), 1000); | |
| 280 | + return; | |
| 281 | + } finally { | |
| 282 | + // 3. 关键:用完即删,避免本地冗余存储和数据泄露 | |
| 283 | + uni.removeStorageSync(tempKey); | |
| 284 | + } | |
| 285 | + } | |
| 286 | +}); | |
| 287 | + | |
| 247 | 288 | onReady(() => { |
| 248 | 289 | // 设置表单校验规则 |
| 249 | 290 | if (workOrderFormRef.value) { |
| ... | ... | @@ -265,6 +306,49 @@ onShow(() => { |
| 265 | 306 | console.log('紧急程度列表:', pressingTypeList.value) |
| 266 | 307 | }) |
| 267 | 308 | |
| 309 | +// ========== 核心方法:工单数据回显 ========== | |
| 310 | +const echoOrderData = (orderItem) => { | |
| 311 | + // 1. 基础表单字段回显(适配工单字段与表单字段映射) | |
| 312 | + workOrderForm.roadId = orderItem.roadId || 0; | |
| 313 | + workOrderForm.roadName = orderItem.roadName || ''; | |
| 314 | + workOrderForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || ''; | |
| 315 | + workOrderForm.orderName = orderItem.orderName || ''; | |
| 316 | + workOrderForm.pressingType = orderItem.pressingType || ''; | |
| 317 | + workOrderForm.pressingTypeName = uni.$dict.getDictLabel('workorder_pressing_type', orderItem.pressingType) || ''; | |
| 318 | + workOrderForm.problemDesc = orderItem.remark || ''; | |
| 319 | + workOrderForm.lat = orderItem.lat || 0; | |
| 320 | + workOrderForm.lon = orderItem.lon || 0; | |
| 321 | + workOrderForm.finishDate = orderItem.finishDate || timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss'); | |
| 322 | + | |
| 323 | + // 2. 上传图片回显(兼容useUploadImgs格式) | |
| 324 | + if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { | |
| 325 | + const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({ | |
| 326 | + url: imgUrl, | |
| 327 | + name: `renew_img_${index}`, | |
| 328 | + status: 'success' // 标记为已上传状态 | |
| 329 | + })); | |
| 330 | + problemImgs.imgList = imgList; | |
| 331 | + problemImgs.rawImgList.value = imgList; | |
| 332 | + } | |
| 333 | + | |
| 334 | + // 3. 自动获取道路列表(保证下拉框正常使用) | |
| 335 | + if (orderItem.lat && orderItem.lon) { | |
| 336 | + getRoadListByLatLng({ | |
| 337 | + companyCode: 'sls', | |
| 338 | + latitude: orderItem.lat, | |
| 339 | + longitude: orderItem.lon | |
| 340 | + }).then((roadRes) => { | |
| 341 | + if (Array.isArray(roadRes)) { | |
| 342 | + roadNameList.value = roadRes.map((item) => ({ | |
| 343 | + name: item.roadName || '', | |
| 344 | + code: item.roadCode || '', | |
| 345 | + id: item.roadId || 0 | |
| 346 | + })); | |
| 347 | + } | |
| 348 | + }).catch(err => console.error('回显道路列表失败:', err)); | |
| 349 | + } | |
| 350 | +}; | |
| 351 | + | |
| 268 | 352 | // ========== 方法定义 ========== |
| 269 | 353 | /** |
| 270 | 354 | * 打开通用下拉弹窗 |
| ... | ... | @@ -327,9 +411,9 @@ const handleActionSheetSelect = (e) => { |
| 327 | 411 | break |
| 328 | 412 | case 'pressingType': |
| 329 | 413 | console.log(e) |
| 330 | - workOrderForm.pressingType =e.value | |
| 414 | + workOrderForm.pressingType = e.value | |
| 331 | 415 | workOrderForm.pressingTypeName = e.name |
| 332 | - workOrderFormRef.value?.validateField('pressingType') | |
| 416 | + workOrderFormRef.value?.validateField('pressingTypeName') | |
| 333 | 417 | break |
| 334 | 418 | } |
| 335 | 419 | // 关闭弹窗 |
| ... | ... | @@ -410,14 +494,15 @@ const hideKeyboard = () => { |
| 410 | 494 | } |
| 411 | 495 | |
| 412 | 496 | /** |
| 413 | - * 提交工单 | |
| 497 | + * 提交工单(区分新增/重新提交,接口隔离) | |
| 414 | 498 | */ |
| 415 | 499 | const submitWorkOrder = async () => { |
| 416 | 500 | try { |
| 417 | 501 | // 先执行表单校验 |
| 418 | 502 | await workOrderFormRef.value.validate() |
| 419 | 503 | |
| 420 | - const submitData = { | |
| 504 | + // 构建公共提交数据 | |
| 505 | + const commonSubmitData = { | |
| 421 | 506 | roadId: workOrderForm.roadId, |
| 422 | 507 | roadName: workOrderForm.roadName, |
| 423 | 508 | imgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法 |
| ... | ... | @@ -436,18 +521,36 @@ const submitWorkOrder = async () => { |
| 436 | 521 | |
| 437 | 522 | // 显示加载中 |
| 438 | 523 | uni.showLoading({ title: '提交中...' }) |
| 439 | - | |
| 440 | - // 调用提交接口 | |
| 441 | - const res = await universalApproval(submitData) | |
| 524 | + let res | |
| 525 | + | |
| 526 | + // 核心:根据状态区分接口调用 | |
| 527 | + if (isRenew.value) { | |
| 528 | + // 重新提交:调用 universalApproval 接口 | |
| 529 | + const renewSubmitData = { | |
| 530 | + // 原有工单必要参数 | |
| 531 | + workerDataId: renewOrderData.value.id, | |
| 532 | + taskKey: renewOrderData.value.taskKey, | |
| 533 | + taskId: renewOrderData.value.taskId, | |
| 534 | + operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | |
| 535 | + agree: 0, | |
| 536 | + reason: '重新提交工单', | |
| 537 | + // 新编辑的工单数据 | |
| 538 | + ...commonSubmitData | |
| 539 | + } | |
| 540 | + res = await universalApproval(renewSubmitData) | |
| 541 | + } else { | |
| 542 | + // 新增工单:调用原有 workorderCreate 接口(不影响原有功能) | |
| 543 | + res = await workorderCreate(commonSubmitData) | |
| 544 | + } | |
| 442 | 545 | |
| 443 | 546 | uni.hideLoading() |
| 444 | 547 | uni.showToast({ |
| 445 | - title: '工单提交成功', | |
| 548 | + title: isRenew.value ? '重新提交成功' : '工单提交成功', | |
| 446 | 549 | icon: 'success', |
| 447 | 550 | duration: 1000 |
| 448 | 551 | }) |
| 449 | 552 | |
| 450 | - // 延迟跳转 | |
| 553 | + // 延迟跳转回列表页 | |
| 451 | 554 | setTimeout(() => { |
| 452 | 555 | uni.redirectTo({ |
| 453 | 556 | url: '/pages-sub/problem/work-order-manage/index' |
| ... | ... | @@ -460,9 +563,9 @@ const submitWorkOrder = async () => { |
| 460 | 563 | // 区分是表单校验失败还是接口调用失败 |
| 461 | 564 | if (!Array.isArray(error)) { |
| 462 | 565 | // 接口调用失败 |
| 463 | - console.error('工单提交失败:', error) | |
| 566 | + console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error) | |
| 464 | 567 | uni.showToast({ |
| 465 | - title: '提交失败,请重试', | |
| 568 | + title: isRenew.value ? '重新提交失败,请重试' : '提交失败,请重试', | |
| 466 | 569 | icon: 'none', |
| 467 | 570 | duration: 2000 |
| 468 | 571 | }) | ... | ... |
pages-sub/problem/work-order-manage/index.vue
| ... | ... | @@ -100,6 +100,11 @@ |
| 100 | 100 | <up-button type="warning" size="mini" @click="handleReject(item)" |
| 101 | 101 | v-show="nextStepMap[item.taskKey].backShow">回退 |
| 102 | 102 | </up-button> |
| 103 | + | |
| 104 | + <up-button type="success" size="mini" @click="handleRenew(item)" | |
| 105 | + v-show="nextStepMap[item.taskKey].renewShow">重新提交 | |
| 106 | + </up-button> | |
| 107 | + | |
| 103 | 108 | <up-button type="primary" size="mini" @click="handleProcess(item)">{{ |
| 104 | 109 | nextStepMap[item.taskKey].btnText |
| 105 | 110 | }} |
| ... | ... | @@ -139,7 +144,6 @@ |
| 139 | 144 | <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> |
| 140 | 145 | </view> |
| 141 | 146 | |
| 142 | - | |
| 143 | 147 | <view class="u-body-item u-flex common-justify-between common-item-center"> |
| 144 | 148 | <view class="u-body-item-title">紧急程度: |
| 145 | 149 | {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} |
| ... | ... | @@ -165,18 +169,31 @@ |
| 165 | 169 | </up-button> |
| 166 | 170 | </view> |
| 167 | 171 | |
| 168 | - <!-- 回退原因弹窗 --> | |
| 169 | - <up-popup v-model="rejectPopupShow" mode="center" :close-on-click-overlay="false"> | |
| 170 | - <view class="reject-popup"> | |
| 171 | - <view class="popup-title">回退原因</view> | |
| 172 | + <!-- 回退原因弹窗:替换为up-modal(核心修改) --> | |
| 173 | + <up-modal | |
| 174 | + :show="rejectModalShow" | |
| 175 | + title="回退原因" | |
| 176 | + :closeOnClickOverlay="false" | |
| 177 | + :showConfirmButton="true" | |
| 178 | + :showCancelButton="true" | |
| 179 | + @cancel="handleRejectModalCancel" | |
| 180 | + @confirm="confirmReject" | |
| 181 | + > | |
| 182 | + <view class="reject-modal-content"> | |
| 183 | + <!-- 回退原因 必填textarea --> | |
| 184 | + <!-- <view class="textarea-label">--> | |
| 185 | + <!-- 回退原因 <text class="required-mark">*</text>--> | |
| 186 | + <!-- </view>--> | |
| 172 | 187 | <up-textarea |
| 173 | - v-model="rejectReason" | |
| 188 | + v-model.trim="rejectReason" | |
| 174 | 189 | placeholder="请输入回退原因(必填)" |
| 175 | - :required="true" | |
| 176 | - maxlength="-1" | |
| 190 | + | |
| 177 | 191 | rows="6" |
| 178 | - class="mt-20" | |
| 192 | + :count="200" | |
| 193 | + maxlength="200" | |
| 194 | + class="reject-textarea" | |
| 179 | 195 | /> |
| 196 | + <!-- 上传图片(选填) --> | |
| 180 | 197 | <view class="upload-wrap mt-20"> |
| 181 | 198 | <view class="upload-title">上传图片(选填)</view> |
| 182 | 199 | <up-upload |
| ... | ... | @@ -188,14 +205,10 @@ |
| 188 | 205 | max-count="3" |
| 189 | 206 | /> |
| 190 | 207 | </view> |
| 191 | - <view class="popup-btn-wrap mt-40"> | |
| 192 | - <up-button type="default" size="medium" @click="rejectPopupShow = false" class="mr-20">取消</up-button> | |
| 193 | - <up-button type="primary" size="medium" @click="confirmReject">确认提交</up-button> | |
| 194 | - </view> | |
| 195 | 208 | </view> |
| 196 | - </up-popup> | |
| 209 | + </up-modal> | |
| 197 | 210 | |
| 198 | - <!-- 新增:养护组长验收弹窗 up-modal --> | |
| 211 | + <!-- 养护组长验收弹窗 up-modal --> | |
| 199 | 212 | <up-modal |
| 200 | 213 | :show="acceptModalShow" |
| 201 | 214 | title="验收" |
| ... | ... | @@ -225,8 +238,6 @@ |
| 225 | 238 | count |
| 226 | 239 | /> |
| 227 | 240 | </view> |
| 228 | - | |
| 229 | - | |
| 230 | 241 | </view> |
| 231 | 242 | </up-modal> |
| 232 | 243 | </view> |
| ... | ... | @@ -245,6 +256,7 @@ import { |
| 245 | 256 | // 假设从用户store获取角色信息 |
| 246 | 257 | import { useUserStore } from '@/pinia/user'; |
| 247 | 258 | import { nextStepMap } from '@/common/utils/common' |
| 259 | +import { workorderCreate } from "../../../api/work-order-manage/work-order-manage"; | |
| 248 | 260 | // ========== 状态管理 ========== |
| 249 | 261 | const userStore = useUserStore(); |
| 250 | 262 | // 标签页切换 |
| ... | ... | @@ -269,14 +281,14 @@ const paging = ref(null); |
| 269 | 281 | const orderList = ref([]); |
| 270 | 282 | // 角色控制(巡查员显示新增按钮) |
| 271 | 283 | const isInspector = computed(() => { |
| 272 | - // 假设用户角色字段为role,巡查员标识为inspector | |
| 273 | - return userStore.userInfo.roles.includes('yl_inspector') | |
| 284 | + // 增加可选链,避免用户信息不存在报错 | |
| 285 | + return userStore.userInfo?.roles?.includes('yl_inspector') || false; | |
| 274 | 286 | }); |
| 275 | -// 回退弹窗相关 | |
| 276 | -const rejectPopupShow = ref(false); | |
| 277 | -const rejectReason = ref(''); | |
| 278 | -const rejectFileList = ref([]); | |
| 279 | -const currentRejectItem = ref(null); | |
| 287 | +// 回退弹窗相关(核心修改:将rejectPopupShow改为rejectModalShow) | |
| 288 | +const rejectModalShow = ref(false); // 回退modal显示开关 | |
| 289 | +const rejectReason = ref(''); // 回退原因 | |
| 290 | +const rejectFileList = ref([]); // 回退上传图片列表 | |
| 291 | +const currentRejectItem = ref(null); // 当前回退工单 | |
| 280 | 292 | // 上传地址(根据实际接口配置) |
| 281 | 293 | const uploadUrl = ref('https://xxx.com/upload'); |
| 282 | 294 | // ========== 新增:养护组长验收弹窗相关状态 ========== |
| ... | ... | @@ -337,73 +349,163 @@ const handleDetail = (item) => { |
| 337 | 349 | url: `/pages-sub/problem/work-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}` |
| 338 | 350 | }); |
| 339 | 351 | }; |
| 352 | + | |
| 353 | +// 待办-重新提交工单(跳转到新增页面并携带工单数据) | |
| 354 | + | |
| 355 | +const generateTempKey = () => { | |
| 356 | + return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000); | |
| 357 | +}; | |
| 358 | + | |
| 359 | +// 待办-重新提交工单(改造后:大数据存本地,仅传唯一标识) | |
| 360 | +const handleRenew = (item) => { | |
| 361 | + // 校验工单有效性 | |
| 362 | + if (!item || !item.id) { | |
| 363 | + uni.showToast({title: '工单信息异常,无法重新提交', icon: 'none'}); | |
| 364 | + return; | |
| 365 | + } | |
| 366 | + | |
| 367 | + // 1. 生成唯一临时标识 | |
| 368 | + const tempKey = generateTempKey(); | |
| 369 | + | |
| 370 | + // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效) | |
| 371 | + try { | |
| 372 | + uni.setStorageSync(tempKey, item); | |
| 373 | + } catch (error) { | |
| 374 | + console.error('存储工单数据失败:', error); | |
| 375 | + uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'}); | |
| 376 | + return; | |
| 377 | + } | |
| 378 | + | |
| 379 | + // 3. URL 仅传递「唯一标识」和「重新提交标记」(数据量极小,无长度问题) | |
| 380 | + uni.navigateTo({ | |
| 381 | + url: `/pages-sub/problem/work-order-manage/add-order?isRenew=1&tempKey=${tempKey}` | |
| 382 | + }); | |
| 383 | +}; | |
| 384 | + | |
| 340 | 385 | // 待办-处理工单 |
| 341 | 386 | const handleProcess = async (item) => { |
| 342 | 387 | console.log(nextStepMap[item.taskKey].name) |
| 343 | 388 | try { |
| 344 | - if (nextStepMap[item.taskKey].name == '养护组长分配') { | |
| 389 | + if (nextStepMap[item.taskKey]?.name == '养护组长分配') { | |
| 345 | 390 | uni.navigateTo({ |
| 346 | 391 | url: `/pages-sub/problem/work-order-manage/distribution-order?taskId=${item.taskId}&orderNo=${item.orderNo}` |
| 347 | 392 | }) |
| 348 | 393 | } |
| 349 | - if (nextStepMap[item.taskKey].name == '养护员待实施') { | |
| 394 | + if (nextStepMap[item.taskKey]?.name == '养护员待实施') { | |
| 350 | 395 | uni.navigateTo({ |
| 351 | 396 | url: `/pages-sub/problem/work-order-manage/add-maintain-order?taskId=${item.taskId}&id=${item.id}&orderNo=${item.orderNo}` |
| 352 | 397 | }) |
| 353 | 398 | } |
| 354 | 399 | // 养护组长验收 - 打开弹窗 |
| 355 | - if (nextStepMap[item.taskKey].name == '养护组长验收') { | |
| 356 | - console.log('123') | |
| 400 | + if (nextStepMap[item.taskKey]?.name == '养护组长验收') { | |
| 357 | 401 | currentAcceptItem.value = item; // 存储当前工单信息 |
| 358 | 402 | acceptReason.value = ''; // 清空上次的验收原因 |
| 359 | 403 | acceptRadioValue.value = '0'; // 重置默认选中“通过” |
| 360 | 404 | acceptModalShow.value = true; // 显示验收弹窗 |
| 361 | 405 | } |
| 362 | - | |
| 363 | 406 | // 巡查员验收 - 打开弹窗 |
| 364 | - if (nextStepMap[item.taskKey].name == '巡查员验收') { | |
| 365 | - console.log('456') | |
| 407 | + if (nextStepMap[item.taskKey]?.name == '巡查员验收') { | |
| 366 | 408 | currentAcceptItem.value = item; // 存储当前工单信息 |
| 367 | 409 | acceptReason.value = ''; // 清空上次的验收原因 |
| 368 | 410 | acceptRadioValue.value = '0'; // 重置默认选中“通过” |
| 369 | 411 | acceptModalShow.value = true; // 显示验收弹窗 |
| 370 | 412 | } |
| 371 | 413 | |
| 414 | + // 发起人确认 | |
| 415 | + if (nextStepMap[item.taskKey]?.name == '发起人确认') { | |
| 416 | + console.log(item) | |
| 417 | + // currentAcceptItem.value = item; // 存储当前工单信息 | |
| 418 | + uni.showModal({ | |
| 419 | + title: "结束工单", | |
| 420 | + content: "请确定是否结束工单?", | |
| 421 | + success: async function (res) { | |
| 422 | + if (res.confirm) { | |
| 423 | + // 构建请求参数 | |
| 424 | + const requestData = { | |
| 425 | + // fileUrls: rejectFileList.value.map(file => file.url || ''), | |
| 426 | + "workerDataId": item.id, | |
| 427 | + "taskKey": item.taskKey, | |
| 428 | + "taskId": item.taskId, | |
| 429 | + "operateType": nextStepMap[item.taskKey].operateTypeNoPass, | |
| 430 | + "agree": 1, | |
| 431 | + "reason": '结束工单' | |
| 432 | + }; | |
| 433 | + // 调用回退工单接口 | |
| 434 | + const res = await universalApproval(requestData); | |
| 435 | + uni.showToast({title: '结束成功', icon: 'success', duration: 1000}); | |
| 436 | + rejectModalShow.value = false; | |
| 437 | + paging.value?.reload(); // 刷新列表 | |
| 438 | + } else if (res.cancel) { | |
| 439 | + console.log("用户点击取消"); | |
| 440 | + } | |
| 441 | + }, | |
| 442 | + }); | |
| 443 | + } | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 372 | 447 | } catch (error) { |
| 373 | 448 | console.error('处理工单失败:', error); |
| 374 | 449 | uni.showToast({title: '处理失败,请重试', icon: 'none'}); |
| 375 | 450 | } |
| 376 | 451 | }; |
| 377 | -// 待办-回退工单 | |
| 452 | +// 待办-回退工单(打开回退modal) | |
| 378 | 453 | const handleReject = (item) => { |
| 454 | + console.log('123213') | |
| 455 | + // 校验工单有效性 | |
| 456 | + if (!item || !item.id) { | |
| 457 | + uni.showToast({title: '工单信息异常,无法回退', icon: 'none'}); | |
| 458 | + return; | |
| 459 | + } | |
| 379 | 460 | currentRejectItem.value = item; |
| 461 | + rejectReason.value = ''; // 清空上次输入 | |
| 462 | + rejectFileList.value = []; // 清空上次上传图片 | |
| 463 | + rejectModalShow.value = true; // 显示回退modal | |
| 464 | +}; | |
| 465 | +// 回退modal - 取消按钮 | |
| 466 | +const handleRejectModalCancel = () => { | |
| 467 | + rejectModalShow.value = false; | |
| 380 | 468 | rejectReason.value = ''; |
| 381 | 469 | rejectFileList.value = []; |
| 382 | - rejectPopupShow.value = true; | |
| 383 | 470 | }; |
| 384 | 471 | // 确认回退工单 |
| 385 | 472 | const confirmReject = async () => { |
| 386 | - if (!rejectReason.value.trim()) { | |
| 387 | - uni.showToast({title: '请填写回退原因', icon: 'none'}); | |
| 473 | + // 严格校验回退原因(去除首尾空格) | |
| 474 | + const rejectReasonTrim = rejectReason.value.trim(); | |
| 475 | + if (!rejectReasonTrim) { | |
| 476 | + uni.showToast({title: '请填写回退原因', icon: 'none', duration: 2000}); | |
| 477 | + return; | |
| 478 | + } | |
| 479 | + // 校验当前工单有效性 | |
| 480 | + if (!currentRejectItem.value || !currentRejectItem.value.id) { | |
| 481 | + uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 2000}); | |
| 482 | + rejectModalShow.value = false; | |
| 388 | 483 | return; |
| 389 | 484 | } |
| 390 | 485 | try { |
| 486 | + // 显示加载中,防止重复提交 | |
| 487 | + uni.showLoading({title: '提交中...', mask: true}); | |
| 488 | + // 构建请求参数 | |
| 489 | + const requestData = { | |
| 490 | + // fileUrls: rejectFileList.value.map(file => file.url || ''), | |
| 491 | + "workerDataId": currentRejectItem.value.id, | |
| 492 | + "taskKey": currentRejectItem.value.taskKey, | |
| 493 | + "taskId": currentRejectItem.value.taskId, | |
| 494 | + "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass, | |
| 495 | + "agree": 1, | |
| 496 | + "reason": rejectReasonTrim | |
| 497 | + }; | |
| 391 | 498 | // 调用回退工单接口 |
| 392 | - await uni.request({ | |
| 393 | - url: '/api/order/reject', | |
| 394 | - method: 'POST', | |
| 395 | - data: { | |
| 396 | - orderId: currentRejectItem.value.id, | |
| 397 | - reason: rejectReason.value, | |
| 398 | - fileUrls: rejectFileList.value.map(file => file.url) | |
| 399 | - } | |
| 400 | - }); | |
| 401 | - uni.showToast({title: '回退成功', icon: 'success'}); | |
| 402 | - rejectPopupShow.value = false; | |
| 499 | + const res = await universalApproval(requestData); | |
| 500 | + uni.showToast({title: '回退成功', icon: 'success', duration: 1000}); | |
| 501 | + rejectModalShow.value = false; | |
| 403 | 502 | paging.value?.reload(); // 刷新列表 |
| 404 | 503 | } catch (error) { |
| 405 | 504 | console.error('回退工单失败:', error); |
| 406 | - uni.showToast({title: '回退失败,请重试', icon: 'none'}); | |
| 505 | + uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000}); | |
| 506 | + } finally { | |
| 507 | + // 隐藏加载中 | |
| 508 | + uni.hideLoading(); | |
| 407 | 509 | } |
| 408 | 510 | }; |
| 409 | 511 | // 新增工单 |
| ... | ... | @@ -412,20 +514,20 @@ const handleAddOrder = () => { |
| 412 | 514 | url: '/pages-sub/problem/work-order-manage/add-order' |
| 413 | 515 | }); |
| 414 | 516 | }; |
| 415 | -// 上传图片-读取后 | |
| 517 | +// 上传图片-读取后(避免重复添加) | |
| 416 | 518 | const handleAfterRead = (file) => { |
| 417 | - rejectFileList.value.push(file); | |
| 519 | + if (!file) return; | |
| 520 | + const isExist = rejectFileList.value.some(item => item.url === file.url); | |
| 521 | + if (!isExist && rejectFileList.value.length < 3) { | |
| 522 | + rejectFileList.value.push(file); | |
| 523 | + } | |
| 418 | 524 | }; |
| 419 | -// 上传图片-删除 | |
| 525 | +// 上传图片-删除(安全删除,避免索引越界) | |
| 420 | 526 | const handleDeleteFile = (index) => { |
| 527 | + if (index < 0 || index >= rejectFileList.value.length) return; | |
| 421 | 528 | rejectFileList.value.splice(index, 1); |
| 422 | 529 | }; |
| 423 | 530 | // ========== 新增:养护组长验收弹窗事件 ========== |
| 424 | -// 验收弹窗 - 取消按钮 | |
| 425 | -const handleAcceptModalCancel = () => { | |
| 426 | - acceptModalShow.value = false; | |
| 427 | - acceptReason.value = ''; // 清空验收原因 | |
| 428 | -}; | |
| 429 | 531 | // 验收弹窗 - 确定按钮(含表单校验) |
| 430 | 532 | const handleAcceptModalConfirm = async () => { |
| 431 | 533 | // 1. 校验验收原因是否为空 |
| ... | ... | @@ -433,7 +535,7 @@ const handleAcceptModalConfirm = async () => { |
| 433 | 535 | uni.showToast({title: '请填写验收原因', icon: 'none', duration: 2000}); |
| 434 | 536 | return; |
| 435 | 537 | } |
| 436 | - // 2. 校验验收原因长度(虽textarea已限制maxlength,此处做兜底校验) | |
| 538 | + // 2. 校验验收原因长度 | |
| 437 | 539 | if (acceptReason.value.length > 200) { |
| 438 | 540 | uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 2000}); |
| 439 | 541 | return; |
| ... | ... | @@ -442,7 +544,7 @@ const handleAcceptModalConfirm = async () => { |
| 442 | 544 | // 3. 调用验收接口 |
| 443 | 545 | console.log(currentAcceptItem.value) |
| 444 | 546 | let postData = {} |
| 445 | - if(currentAcceptItem.value.taskKey == 'ylTeamLeaderConfirm'){ // 养护组长验收 | |
| 547 | + if (currentAcceptItem.value?.taskKey == 'ylTeamLeaderConfirm') { // 养护组长验收 | |
| 446 | 548 | postData = { |
| 447 | 549 | "taskKey": "ylTeamLeaderConfirm", |
| 448 | 550 | "taskId": currentAcceptItem.value.taskId, |
| ... | ... | @@ -450,16 +552,15 @@ const handleAcceptModalConfirm = async () => { |
| 450 | 552 | "reason": acceptReason.value.trim() |
| 451 | 553 | } |
| 452 | 554 | } |
| 453 | - if(currentAcceptItem.value.taskKey == 'ylInspector'){ // 巡查员验收 | |
| 555 | + if (currentAcceptItem.value?.taskKey == 'ylInspector') { // 巡查员验收 | |
| 454 | 556 | postData = { |
| 455 | 557 | "taskKey": "ylTeamLeaderConfirm", |
| 456 | 558 | "taskId": currentAcceptItem.value.taskId, |
| 457 | 559 | "operateType": acceptRadioValue.value == 0 ? nextStepMap['ylTeamLeaderConfirm'].operateTypePass : nextStepMap['ylTeamLeaderConfirm'].operateTypeNoPass, |
| 458 | 560 | "reason": acceptReason.value.trim(), |
| 459 | - "agree":acceptRadioValue.value | |
| 561 | + "agree": acceptRadioValue.value | |
| 460 | 562 | } |
| 461 | 563 | } |
| 462 | - | |
| 463 | 564 | const acceptRes = await universalApproval(postData); |
| 464 | 565 | // 4. 操作成功处理 |
| 465 | 566 | uni.showToast({title: '提交成功', icon: 'success', duration: 1500}); |
| ... | ... | @@ -482,6 +583,7 @@ onShow(() => { |
| 482 | 583 | <style scoped lang="scss"> |
| 483 | 584 | .page-container { |
| 484 | 585 | min-height: 100vh; |
| 586 | + background-color: #fafafa; | |
| 485 | 587 | } |
| 486 | 588 | |
| 487 | 589 | // 顶部固定区域 |
| ... | ... | @@ -516,7 +618,59 @@ onShow(() => { |
| 516 | 618 | } |
| 517 | 619 | } |
| 518 | 620 | |
| 519 | -// 新增:养护组长验收弹窗样式 | |
| 621 | +// 工单卡片样式 | |
| 622 | +.order-card { | |
| 623 | + margin: 0 20rpx 20rpx; | |
| 624 | + background: #fff; | |
| 625 | + border-radius: 12rpx; | |
| 626 | + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); | |
| 627 | +} | |
| 628 | + | |
| 629 | +.card-body { | |
| 630 | + | |
| 631 | +} | |
| 632 | + | |
| 633 | + | |
| 634 | +// 回退modal样式 | |
| 635 | +.reject-modal-content { | |
| 636 | + width: 100%; | |
| 637 | + box-sizing: border-box; | |
| 638 | + padding: 10rpx 0; | |
| 639 | +} | |
| 640 | + | |
| 641 | +.textarea-label { | |
| 642 | + font-size: 28rpx; | |
| 643 | + color: #333; | |
| 644 | + margin-bottom: 10rpx; | |
| 645 | + | |
| 646 | + .required-mark { | |
| 647 | + color: #f56c6c; | |
| 648 | + margin-left: 4rpx; | |
| 649 | + } | |
| 650 | +} | |
| 651 | + | |
| 652 | +.reject-textarea { | |
| 653 | + font-size: 28rpx; | |
| 654 | + padding: 16rpx; | |
| 655 | + border: 1rpx solid #e4e7ed; | |
| 656 | + border-radius: 8rpx; | |
| 657 | +} | |
| 658 | + | |
| 659 | +.upload-wrap { | |
| 660 | + margin-top: 20rpx; | |
| 661 | + | |
| 662 | + .upload-title { | |
| 663 | + font-size: 28rpx; | |
| 664 | + color: #333; | |
| 665 | + margin-bottom: 10rpx; | |
| 666 | + } | |
| 667 | +} | |
| 668 | + | |
| 669 | +.mt-20 { | |
| 670 | + margin-top: 20rpx; | |
| 671 | +} | |
| 672 | + | |
| 673 | +// 养护组长验收弹窗样式 | |
| 520 | 674 | .accept-modal-content { |
| 521 | 675 | width: 100%; |
| 522 | 676 | box-sizing: border-box; |
| ... | ... | @@ -532,6 +686,7 @@ onShow(() => { |
| 532 | 686 | |
| 533 | 687 | .textarea-wrap { |
| 534 | 688 | width: 100%; |
| 689 | + margin-top: 30rpx; | |
| 535 | 690 | } |
| 536 | 691 | |
| 537 | 692 | .modal-btn-wrap { | ... | ... |