Commit 371cf0ebe66a054cdf898be8706fe122dde4ccc3

Authored by 刘淇
1 parent dacff5e3

流程节点

pages-sub/problem/work-order-manage/add-maintain-order.vue
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 @click="handleActionSheetOpen('coProcessor'); hideKeyboard()" 28 @click="handleActionSheetOpen('coProcessor'); hideKeyboard()"
29 > 29 >
30 <up-input 30 <up-input
31 - v-model="workOrderForm.coProcessorName" 31 + v-model="workOrderForm.coProcessorNameStr"
32 disabled 32 disabled
33 disabled-color="#ffffff" 33 disabled-color="#ffffff"
34 placeholder="请选择共同处理人" 34 placeholder="请选择共同处理人"
@@ -203,13 +203,14 @@ const tabList = ref([ @@ -203,13 +203,14 @@ const tabList = ref([
203 // 共同处理人列表 203 // 共同处理人列表
204 const coProcessorList = ref([]) 204 const coProcessorList = ref([])
205 205
206 -// 工单表单数据(仅保留需要的字段 206 +// 工单表单数据(修改:新增coProcessorName数组,coProcessorNameStr用于页面展示
207 const workOrderForm = reactive({ 207 const workOrderForm = reactive({
208 workerDataId:'', 208 workerDataId:'',
209 taskId:'', // 任务id 209 taskId:'', // 任务id
210 orderNo: '', // 工单编号 210 orderNo: '', // 工单编号
211 - coProcessorId: '', // 共同处理人ID数组(多选)  
212 - coProcessorName: '', // 共同处理人名称数组(多选) 211 + coProcessorId: [], // 共同处理人ID数组(多选,改为数组格式)
  212 + coProcessorName: [], // 共同处理人名称数组(多选,数组格式,用于提交)
  213 + coProcessorNameStr: '', // 共同处理人名称字符串(用于页面输入框展示)
213 reason: '', // 处理情况描述 214 reason: '', // 处理情况描述
214 }) 215 })
215 216
@@ -313,8 +314,6 @@ onLoad((options) =&gt; { @@ -313,8 +314,6 @@ onLoad((options) =&gt; {
313 duration: 3000 314 duration: 3000
314 }); 315 });
315 console.error('onLoad异常:工单缓存数据为空'); 316 console.error('onLoad异常:工单缓存数据为空');
316 - // 可选:返回上一页  
317 - // setTimeout(() => uni.navigateBack(), 1500);  
318 return; 317 return;
319 } 318 }
320 319
@@ -352,10 +351,18 @@ onLoad((options) =&gt; { @@ -352,10 +351,18 @@ onLoad((options) =&gt; {
352 workOrderForm.orderNo = orderItem.orderNo || ''; 351 workOrderForm.orderNo = orderItem.orderNo || '';
353 workOrderForm.reason = orderItem.handleResult || ''; // 处理情况回显 352 workOrderForm.reason = orderItem.handleResult || ''; // 处理情况回显
354 353
355 - // 共同处理人(如有数据则回显 354 + // 共同处理人(如有数据则回显,转换为数组格式
356 if (orderItem.coHandlers && orderItem.coHandlersName) { 355 if (orderItem.coHandlers && orderItem.coHandlersName) {
357 - workOrderForm.coProcessorId = orderItem.coHandlers;  
358 - workOrderForm.coProcessorName = orderItem.coHandlersName; 356 + // 处理ID:如果是字符串(逗号分隔),转为数组;本身是数组则直接赋值
  357 + workOrderForm.coProcessorId = Array.isArray(orderItem.coHandlers)
  358 + ? orderItem.coHandlers
  359 + : (orderItem.coHandlers.split(',').filter(Boolean) || []);
  360 + // 处理名称:同理,转为数组格式(用于提交),并拼接为字符串(用于展示)
  361 + workOrderForm.coProcessorName = Array.isArray(orderItem.coHandlersName)
  362 + ? orderItem.coHandlersName
  363 + : (orderItem.coHandlersName.split(',').filter(Boolean) || []);
  364 + // 拼接名称为字符串,用于输入框展示
  365 + workOrderForm.coProcessorNameStr = workOrderForm.coProcessorName.join(',');
359 } else { 366 } else {
360 console.warn('工单数据提示:无共同处理人信息'); 367 console.warn('工单数据提示:无共同处理人信息');
361 } 368 }
@@ -418,8 +425,6 @@ onLoad((options) =&gt; { @@ -418,8 +425,6 @@ onLoad((options) =&gt; {
418 duration: 3000 425 duration: 3000
419 }); 426 });
420 console.error('获取工单缓存数据失败:', error); 427 console.error('获取工单缓存数据失败:', error);
421 - // 可选:返回上一页  
422 - // setTimeout(() => uni.navigateBack(), 1500);  
423 } 428 }
424 }) 429 })
425 430
@@ -551,7 +556,7 @@ const handleActionSheetClose = () =&gt; { @@ -551,7 +556,7 @@ const handleActionSheetClose = () =&gt; {
551 currentActionSheetData.title = '' 556 currentActionSheetData.title = ''
552 } 557 }
553 558
554 -// 下拉弹窗选择事件(仅处理共同处理人多选 559 +// 下拉弹窗选择事件(修改:支持多选,存储名称和ID为数组
555 const handleActionSheetSelect = (e) => { 560 const handleActionSheetSelect = (e) => {
556 // 校验事件参数是否有效 561 // 校验事件参数是否有效
557 if (!e || !e.name || !e.id) { 562 if (!e || !e.name || !e.id) {
@@ -566,10 +571,23 @@ const handleActionSheetSelect = (e) =&gt; { @@ -566,10 +571,23 @@ const handleActionSheetSelect = (e) =&gt; {
566 } 571 }
567 572
568 const { type } = currentActionSheetData 573 const { type } = currentActionSheetData
569 - // 多选场景(仅共同处理人 574 + // 多选场景(仅共同处理人,存储数组格式
570 if (type === 'coProcessor') { 575 if (type === 'coProcessor') {
571 - workOrderForm.coProcessorName = e.name  
572 - workOrderForm.coProcessorId = e.id 576 + // 若已选中该处理人,移除(实现切换选择);未选中则添加
  577 + const idIndex = workOrderForm.coProcessorId.findIndex(id => id === e.id);
  578 + if (idIndex > -1) {
  579 + // 移除已选中的ID和名称
  580 + workOrderForm.coProcessorId.splice(idIndex, 1);
  581 + workOrderForm.coProcessorName.splice(idIndex, 1);
  582 + } else {
  583 + // 添加新选中的ID和名称(数组格式)
  584 + workOrderForm.coProcessorId.push(e.id);
  585 + workOrderForm.coProcessorName.push(e.name);
  586 + }
  587 +
  588 + // 拼接名称数组为字符串,用于输入框展示
  589 + workOrderForm.coProcessorNameStr = workOrderForm.coProcessorName.join(',');
  590 +
573 // 校验表单实例是否存在,再执行字段校验 591 // 校验表单实例是否存在,再执行字段校验
574 if (workOrderFormRef.value) { 592 if (workOrderFormRef.value) {
575 workOrderFormRef.value?.validateField('coProcessorName') 593 workOrderFormRef.value?.validateField('coProcessorName')
@@ -600,7 +618,7 @@ const hideKeyboard = () =&gt; { @@ -600,7 +618,7 @@ const hideKeyboard = () =&gt; {
600 uni.hideKeyboard() 618 uni.hideKeyboard()
601 } 619 }
602 620
603 -// 提交工单(整合所有图片URL,增加开始/结束图片必填校验 621 +// 提交工单(修改:将coProcessorName数组一并传入提交参数
604 const submitWorkOrder = async () => { 622 const submitWorkOrder = async () => {
605 try { 623 try {
606 // ========== 前置校验:表单实例是否存在 ========== 624 // ========== 前置校验:表单实例是否存在 ==========
@@ -696,14 +714,15 @@ const submitWorkOrder = async () =&gt; { @@ -696,14 +714,15 @@ const submitWorkOrder = async () =&gt; {
696 materialImgs: materialImgs.getSuccessImgUrls() || [] 714 materialImgs: materialImgs.getSuccessImgUrls() || []
697 } 715 }
698 716
699 - // 构造提交参数 717 + // 构造提交参数(修改:添加coProcessorName数组一并提交)
700 const submitData = { 718 const submitData = {
701 taskId: workOrderForm.taskId, 719 taskId: workOrderForm.taskId,
702 taskKey:'ylWorker', 720 taskKey:'ylWorker',
703 operateType: nextStepMap['ylWorker']?.operateTypePass || '', // 兜底:防止nextStepMap缺失属性 721 operateType: nextStepMap['ylWorker']?.operateTypePass || '', // 兜底:防止nextStepMap缺失属性
704 workerDataId: Number(workOrderForm.workerDataId) || '', 722 workerDataId: Number(workOrderForm.workerDataId) || '',
705 handleResult: workOrderForm.reason.trim(), 723 handleResult: workOrderForm.reason.trim(),
706 - coHandlers: workOrderForm.coProcessorId ? [String(workOrderForm.coProcessorId)] : [], 724 + coHandlers: workOrderForm.coProcessorId || [], // ID数组
  725 + coHandlersName: workOrderForm.coProcessorName || [], // 名称数组(一并提交)
707 startImgs: allImgs.startImgs, 726 startImgs: allImgs.startImgs,
708 processingImgs: allImgs.processingImgs, 727 processingImgs: allImgs.processingImgs,
709 endImgs: allImgs.endImgs, 728 endImgs: allImgs.endImgs,
@@ -712,6 +731,9 @@ const submitWorkOrder = async () =&gt; { @@ -712,6 +731,9 @@ const submitWorkOrder = async () =&gt; {
712 problemsImgs:[] 731 problemsImgs:[]
713 } 732 }
714 733
  734 + // 打印提交参数,验证coProcessorName是否为数组
  735 + console.log('提交参数(含名称数组):', submitData);
  736 +
715 // 校验operateType是否有效 737 // 校验operateType是否有效
716 if (!submitData.operateType) { 738 if (!submitData.operateType) {
717 uni.showToast({ 739 uni.showToast({
pages-sub/problem/work-order-manage/order-detail.vue
@@ -24,7 +24,6 @@ @@ -24,7 +24,6 @@
24 ></up-tabs> 24 ></up-tabs>
25 </up-sticky> 25 </up-sticky>
26 26
27 -  
28 <!-- 顶部Tab内容区 --> 27 <!-- 顶部Tab内容区 -->
29 <view class="tab-content"> 28 <view class="tab-content">
30 <!-- 1. 工单详情Tab --> 29 <!-- 1. 工单详情Tab -->
@@ -110,20 +109,18 @@ @@ -110,20 +109,18 @@
110 ></up-cell> 109 ></up-cell>
111 </up-cell-group> 110 </up-cell-group>
112 111
113 -  
114 <!-- 图片分类Tabs区块 --> 112 <!-- 图片分类Tabs区块 -->
115 <view class="img-tabs-block" v-if="orderDetail.startImgs.length>0"> 113 <view class="img-tabs-block" v-if="orderDetail.startImgs.length>0">
116 <up-cell-group :border="false"> 114 <up-cell-group :border="false">
117 <up-cell> 115 <up-cell>
118 - <template #title>  
119 - <view style="min-width: 200rpx">共同处理人</view>  
120 - </template>  
121 - <template #value>  
122 - <view class="common-text-color up-line-1">{{ orderDetail.coHandlersName || '--' }}</view>  
123 - </template> 116 + <template #title>
  117 + <view style="min-width: 200rpx">共同处理人</view>
  118 + </template>
  119 + <template #value>
  120 + <view class="common-text-color up-line-1">{{ orderDetail.coHandlersName || '--' }}</view>
  121 + </template>
124 </up-cell> 122 </up-cell>
125 </up-cell-group> 123 </up-cell-group>
126 - <!-- coHandlersName-->  
127 <up-tabs 124 <up-tabs
128 @change='imgTabChange' 125 @change='imgTabChange'
129 v-model="activeImgTab" 126 v-model="activeImgTab"
@@ -151,10 +148,68 @@ @@ -151,10 +148,68 @@
151 148
152 <!-- 2. 流程节点Tab --> 149 <!-- 2. 流程节点Tab -->
153 <view v-show="activeTopTab == 1" class="process-content"> 150 <view v-show="activeTopTab == 1" class="process-content">
154 - <!-- <up-empty-->  
155 - <!-- mode="data"-->  
156 - <!-- ></up-empty>-->  
157 - <!-- &lt;!&ndash; 可根据实际需求补充流程节点展示逻辑 &ndash;&gt;--> 151 + <!-- 竖向步骤条:动态绑定current属性 -->
  152 + <up-steps
  153 + v-if="processData.activityNodes && processData.activityNodes.length"
  154 + :list="processData.activityNodes"
  155 + :current="getCurrentStepIndex()"
  156 + direction="column"
  157 + active-color="#3c9cff"
  158 + inactive-color="#999"
  159 + class="vertical-steps"
  160 + >
  161 + <up-steps-item
  162 + v-for="(item, index) in processData.activityNodes"
  163 + :key="`${item.id}_${index}`"
  164 + >
  165 + <!-- 唯一自定义模板:content,包含标题、描述、相册所有内容 -->
  166 + <template #content>
  167 + <view class="step-content-wrap">
  168 + <!-- 1. 原标题内容:节点名称 + 操作人 -->
  169 + <view class="step-title">
  170 + {{ item.name }}
  171 + <text class="operator-name">
  172 + {{ item.tasks && item.tasks[0]?.assigneeUser?.nickname ? '(' + item.tasks[0].assigneeUser.nickname + ')' : '(未知操作人)' }}
  173 + </text>
  174 + </view>
  175 +
  176 + <!-- 2. 原描述内容:时间 + 处理说明(最多200字) -->
  177 + <view class="step-desc">
  178 + <!-- 时间行 -->
  179 + <view class="time-line">
  180 + 处理时间:{{ timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss') }}
  181 + <text v-if="item.endTime"> - {{ timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss') }}</text>
  182 + <text v-else class="processing-tag">(处理中)</text>
  183 + </view>
  184 + <!-- 原因行 -->
  185 + <view class="reason-line up-line-2" v-if="item.tasks && item.tasks[0]?.reason">
  186 + 描述:{{ getLimitReason(item.tasks && item.tasks[0]?.reason) }}
  187 + </view>
  188 + </view>
  189 +
  190 + <!-- 3. 原相册内容:预留up-album -->
  191 + <view class="step-album-wrap">
  192 + <up-album
  193 + v-if="item.tasks && item.tasks[0]?.signPicUrl && item.tasks[0].signPicUrl.length"
  194 + :urls="item.tasks[0].signPicUrl.slice(0, 3)"
  195 + singleSize="60"
  196 + multipleSize="60"
  197 + :preview-full-image="true"
  198 + class="step-album"
  199 + ></up-album>
  200 + </view>
  201 + </view>
  202 + </template>
  203 + </up-steps-item>
  204 + </up-steps>
  205 +
  206 + <!-- 流程节点为空时的提示 -->
  207 + <up-empty
  208 + v-else
  209 + mode="data"
  210 + title="暂无流程节点数据"
  211 + class="empty-process"
  212 + ></up-empty>
158 </view> 213 </view>
159 </view> 214 </view>
160 </view> 215 </view>
@@ -183,6 +238,14 @@ const topTabList = ref([ @@ -183,6 +238,14 @@ const topTabList = ref([
183 {name: '流程节点'} 238 {name: '流程节点'}
184 ]); 239 ]);
185 240
  241 +// 流程节点数据(初始化适配接口格式)
  242 +const processData = ref<any>({
  243 + status: 2,
  244 + activityNodes: [],
  245 + formFieldsPermission: null,
  246 + todoTask: null
  247 +});
  248 +
186 const activeTopTabClick = async (item: any) => { 249 const activeTopTabClick = async (item: any) => {
187 console.log(item) 250 console.log(item)
188 activeTopTab.value = item.index 251 activeTopTab.value = item.index
@@ -192,6 +255,19 @@ const activeTopTabClick = async (item: any) =&gt; { @@ -192,6 +255,19 @@ const activeTopTabClick = async (item: any) =&gt; {
192 } 255 }
193 const res = await getApprovalDetail(getData) 256 const res = await getApprovalDetail(getData)
194 console.log(res) 257 console.log(res)
  258 + // 关键:格式化数据,补充up-steps要求的title字段
  259 + if (res && res.activityNodes && res.activityNodes.length) {
  260 + const formatActivityNodes = res.activityNodes.map(node => ({
  261 + ...node,
  262 + title: node.name // 补充强制字段,满足3.3.48版本组件要求
  263 + }));
  264 + processData.value = {
  265 + ...res,
  266 + activityNodes: formatActivityNodes
  267 + };
  268 + } else {
  269 + processData.value = res;
  270 + }
195 } 271 }
196 } 272 }
197 273
@@ -270,6 +346,36 @@ const imgTabChange = (({index}) =&gt; { @@ -270,6 +346,36 @@ const imgTabChange = (({index}) =&gt; {
270 currentImgList.value = orderDetail.value[currentKey] 346 currentImgList.value = orderDetail.value[currentKey]
271 }) 347 })
272 348
  349 +/**
  350 + * 截取reason最多200字
  351 + * @param reason 处理说明
  352 + * @returns 截取后的字符串
  353 + */
  354 +const getLimitReason = (reason: string | null | undefined) => {
  355 + if (!reason) return '无处理说明';
  356 + if (reason.length <= 200) return reason;
  357 + return reason.substring(0, 200) + '...';
  358 +}
  359 +
  360 +/**
  361 + * 动态获取当前步骤索引(核心:根据接口数据的status字段判断)
  362 + * status=1:当前步骤(处理中)
  363 + * status=2:已完成步骤
  364 + * @returns {number} 当前激活的步骤索引(从0开始)
  365 + */
  366 +const getCurrentStepIndex = () => {
  367 + const { activityNodes } = processData.value;
  368 + if (!activityNodes || !activityNodes.length) return 0;
  369 +
  370 + // 1. 查找第一个状态为1(处理中)的节点,即为当前步骤
  371 + const processingNodeIndex = activityNodes.findIndex(node => node.status === 1);
  372 + if (processingNodeIndex !== -1) {
  373 + return processingNodeIndex;
  374 + }
  375 +
  376 + // 2. 若没有处理中的节点(全部已完成),则激活最后一个节点
  377 + return activityNodes.length - 1;
  378 +}
273 379
274 /** 380 /**
275 * 获取工单详情 381 * 获取工单详情
@@ -321,12 +427,7 @@ onLoad((options: any) =&gt; { @@ -321,12 +427,7 @@ onLoad((options: any) =&gt; {
321 }); 427 });
322 428
323 onShow(() => { 429 onShow(() => {
324 - // if (taskId.value) {  
325 - // DetailQuery(taskId.value);  
326 - // } else {  
327 - // loading.value = false;  
328 - // uni.showToast({title: '缺少工单ID参数', icon: 'none'});  
329 - // } 430 + // 注释原有逻辑,避免重复加载
330 }); 431 });
331 </script> 432 </script>
332 433
@@ -371,20 +472,12 @@ onShow(() =&gt; { @@ -371,20 +472,12 @@ onShow(() =&gt; {
371 // 图片分类Tabs区块 472 // 图片分类Tabs区块
372 .img-tabs-block { 473 .img-tabs-block {
373 background-color: #fff; 474 background-color: #fff;
374 - border-radius: 12rpx;  
375 - padding: 16rpx;  
376 -  
377 - //// 图片Tabs样式(平均分配空间)  
378 - //.img-tabs {  
379 - // --u-tabs-item-flex: 1; // 平均分配空间  
380 - // --u-tabs-item-font-size: 26rpx;  
381 - // --u-tabs-item-height: 72rpx;  
382 - // margin-bottom: 16rpx;  
383 - //} 475 + //border-radius: 12rpx;
  476 + //padding: 16rpx;
384 477
385 // 图片内容区 478 // 图片内容区
386 .img-tab-content { 479 .img-tab-content {
387 - padding: 10rpx 0; 480 + padding: 20rpx 15px ;
388 min-height: 120rpx; 481 min-height: 120rpx;
389 display: flex; 482 display: flex;
390 align-items: center; 483 align-items: center;
@@ -403,15 +496,83 @@ onShow(() =&gt; { @@ -403,15 +496,83 @@ onShow(() =&gt; {
403 } 496 }
404 } 497 }
405 498
406 -// 流程节点空状态 499 +// 流程节点区域(完整样式,确保内容可见)
407 .process-content { 500 .process-content {
408 - display: flex;  
409 - justify-content: center;  
410 - align-items: center;  
411 - min-height: 400rpx; 501 + //padding: 16rpx;
  502 + //min-height: 400rpx;
412 503
413 .empty-process { 504 .empty-process {
414 margin-top: 100rpx; 505 margin-top: 100rpx;
415 } 506 }
  507 +
  508 + // 竖向步骤条容器样式
  509 + .vertical-steps {
  510 + //width: 100%;
  511 + background-color: #fff;
  512 + padding: 20rpx;
  513 + border-radius: 12rpx;
  514 + display: flex;
  515 + flex-direction: column;
  516 + gap: 20rpx; // 步骤项间距
  517 + }
  518 +
  519 + // 自定义内容容器样式
  520 + .step-content-wrap {
  521 + width: 100%;
  522 + //padding: 10rpx 0;
  523 + box-sizing: border-box;
  524 +
  525 + // 节点标题 + 操作人样式
  526 + .step-title {
  527 + font-size: 30rpx;
  528 + font-weight: 600;
  529 + color: #333;
  530 + margin-bottom: 12rpx;
  531 +
  532 + .operator-name {
  533 + font-size: 30rpx;
  534 + font-weight: 600;
  535 + color: #333;
  536 + margin-left: 20rpx;
  537 + }
  538 + }
  539 +
  540 + // 描述(时间 + 处理说明)样式
  541 + .step-desc {
  542 + font-size: 24rpx;
  543 + color: #666;
  544 + line-height: 1.6;
  545 + margin-bottom: 12rpx;
  546 +
  547 + .time-line {
  548 + margin-bottom: 8rpx;
  549 +
  550 + .processing-tag {
  551 + color: #f59e0b;
  552 + margin-left: 10rpx;
  553 + }
  554 + }
  555 +
  556 + .reason-line {
  557 + word-break: break-all; // 长文本自动换行
  558 + }
  559 + }
  560 +
  561 + // 相册容器样式
  562 + .step-album-wrap {
  563 + padding: 10rpx 0;
  564 +
  565 + .step-album {
  566 + width: 100%;
  567 + max-width: 300rpx;
  568 + }
  569 +
  570 + .no-img-tip {
  571 + font-size: 24rpx;
  572 + color: #999;
  573 + margin-top: 10rpx;
  574 + }
  575 + }
  576 + }
416 } 577 }
417 </style> 578 </style>
418 \ No newline at end of file 579 \ No newline at end of file