Commit 7a96cf5063e3d3a58ac990c89873850b222f24cc

Authored by 刘淇
1 parent 1f1f236c

重新提交

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(() =&gt; {
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) =&gt; {
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 = () =&gt; {
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 () =&gt; {
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 () =&gt; {
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) =&gt; {
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 = () =&gt; {
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 () =&gt; {
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 () =&gt; {
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 () =&gt; {
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(() =&gt; {
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(() =&gt; {
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(() =&gt; {
532 686  
533 687 .textarea-wrap {
534 688 width: 100%;
  689 + margin-top: 30rpx;
535 690 }
536 691  
537 692 .modal-btn-wrap {
... ...