Commit 371cf0ebe66a054cdf898be8706fe122dde4ccc3

Authored by 刘淇
1 parent dacff5e3

流程节点

pages-sub/problem/work-order-manage/add-maintain-order.vue
... ... @@ -28,7 +28,7 @@
28 28 @click="handleActionSheetOpen('coProcessor'); hideKeyboard()"
29 29 >
30 30 <up-input
31   - v-model="workOrderForm.coProcessorName"
  31 + v-model="workOrderForm.coProcessorNameStr"
32 32 disabled
33 33 disabled-color="#ffffff"
34 34 placeholder="请选择共同处理人"
... ... @@ -203,13 +203,14 @@ const tabList = ref([
203 203 // 共同处理人列表
204 204 const coProcessorList = ref([])
205 205  
206   -// 工单表单数据(仅保留需要的字段
  206 +// 工单表单数据(修改:新增coProcessorName数组,coProcessorNameStr用于页面展示
207 207 const workOrderForm = reactive({
208 208 workerDataId:'',
209 209 taskId:'', // 任务id
210 210 orderNo: '', // 工单编号
211   - coProcessorId: '', // 共同处理人ID数组(多选)
212   - coProcessorName: '', // 共同处理人名称数组(多选)
  211 + coProcessorId: [], // 共同处理人ID数组(多选,改为数组格式)
  212 + coProcessorName: [], // 共同处理人名称数组(多选,数组格式,用于提交)
  213 + coProcessorNameStr: '', // 共同处理人名称字符串(用于页面输入框展示)
213 214 reason: '', // 处理情况描述
214 215 })
215 216  
... ... @@ -313,8 +314,6 @@ onLoad((options) =&gt; {
313 314 duration: 3000
314 315 });
315 316 console.error('onLoad异常:工单缓存数据为空');
316   - // 可选:返回上一页
317   - // setTimeout(() => uni.navigateBack(), 1500);
318 317 return;
319 318 }
320 319  
... ... @@ -352,10 +351,18 @@ onLoad((options) =&gt; {
352 351 workOrderForm.orderNo = orderItem.orderNo || '';
353 352 workOrderForm.reason = orderItem.handleResult || ''; // 处理情况回显
354 353  
355   - // 共同处理人(如有数据则回显
  354 + // 共同处理人(如有数据则回显,转换为数组格式
356 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 366 } else {
360 367 console.warn('工单数据提示:无共同处理人信息');
361 368 }
... ... @@ -418,8 +425,6 @@ onLoad((options) =&gt; {
418 425 duration: 3000
419 426 });
420 427 console.error('获取工单缓存数据失败:', error);
421   - // 可选:返回上一页
422   - // setTimeout(() => uni.navigateBack(), 1500);
423 428 }
424 429 })
425 430  
... ... @@ -551,7 +556,7 @@ const handleActionSheetClose = () =&gt; {
551 556 currentActionSheetData.title = ''
552 557 }
553 558  
554   -// 下拉弹窗选择事件(仅处理共同处理人多选
  559 +// 下拉弹窗选择事件(修改:支持多选,存储名称和ID为数组
555 560 const handleActionSheetSelect = (e) => {
556 561 // 校验事件参数是否有效
557 562 if (!e || !e.name || !e.id) {
... ... @@ -566,10 +571,23 @@ const handleActionSheetSelect = (e) =&gt; {
566 571 }
567 572  
568 573 const { type } = currentActionSheetData
569   - // 多选场景(仅共同处理人
  574 + // 多选场景(仅共同处理人,存储数组格式
570 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 592 if (workOrderFormRef.value) {
575 593 workOrderFormRef.value?.validateField('coProcessorName')
... ... @@ -600,7 +618,7 @@ const hideKeyboard = () =&gt; {
600 618 uni.hideKeyboard()
601 619 }
602 620  
603   -// 提交工单(整合所有图片URL,增加开始/结束图片必填校验
  621 +// 提交工单(修改:将coProcessorName数组一并传入提交参数
604 622 const submitWorkOrder = async () => {
605 623 try {
606 624 // ========== 前置校验:表单实例是否存在 ==========
... ... @@ -696,14 +714,15 @@ const submitWorkOrder = async () =&gt; {
696 714 materialImgs: materialImgs.getSuccessImgUrls() || []
697 715 }
698 716  
699   - // 构造提交参数
  717 + // 构造提交参数(修改:添加coProcessorName数组一并提交)
700 718 const submitData = {
701 719 taskId: workOrderForm.taskId,
702 720 taskKey:'ylWorker',
703 721 operateType: nextStepMap['ylWorker']?.operateTypePass || '', // 兜底:防止nextStepMap缺失属性
704 722 workerDataId: Number(workOrderForm.workerDataId) || '',
705 723 handleResult: workOrderForm.reason.trim(),
706   - coHandlers: workOrderForm.coProcessorId ? [String(workOrderForm.coProcessorId)] : [],
  724 + coHandlers: workOrderForm.coProcessorId || [], // ID数组
  725 + coHandlersName: workOrderForm.coProcessorName || [], // 名称数组(一并提交)
707 726 startImgs: allImgs.startImgs,
708 727 processingImgs: allImgs.processingImgs,
709 728 endImgs: allImgs.endImgs,
... ... @@ -712,6 +731,9 @@ const submitWorkOrder = async () =&gt; {
712 731 problemsImgs:[]
713 732 }
714 733  
  734 + // 打印提交参数,验证coProcessorName是否为数组
  735 + console.log('提交参数(含名称数组):', submitData);
  736 +
715 737 // 校验operateType是否有效
716 738 if (!submitData.operateType) {
717 739 uni.showToast({
... ...
pages-sub/problem/work-order-manage/order-detail.vue
... ... @@ -24,7 +24,6 @@
24 24 ></up-tabs>
25 25 </up-sticky>
26 26  
27   -
28 27 <!-- 顶部Tab内容区 -->
29 28 <view class="tab-content">
30 29 <!-- 1. 工单详情Tab -->
... ... @@ -110,20 +109,18 @@
110 109 ></up-cell>
111 110 </up-cell-group>
112 111  
113   -
114 112 <!-- 图片分类Tabs区块 -->
115 113 <view class="img-tabs-block" v-if="orderDetail.startImgs.length>0">
116 114 <up-cell-group :border="false">
117 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 122 </up-cell>
125 123 </up-cell-group>
126   - <!-- coHandlersName-->
127 124 <up-tabs
128 125 @change='imgTabChange'
129 126 v-model="activeImgTab"
... ... @@ -151,10 +148,68 @@
151 148  
152 149 <!-- 2. 流程节点Tab -->
153 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 213 </view>
159 214 </view>
160 215 </view>
... ... @@ -183,6 +238,14 @@ const topTabList = ref([
183 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 249 const activeTopTabClick = async (item: any) => {
187 250 console.log(item)
188 251 activeTopTab.value = item.index
... ... @@ -192,6 +255,19 @@ const activeTopTabClick = async (item: any) =&gt; {
192 255 }
193 256 const res = await getApprovalDetail(getData)
194 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 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 427 });
322 428  
323 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 432 </script>
332 433  
... ... @@ -371,20 +472,12 @@ onShow(() =&gt; {
371 472 // 图片分类Tabs区块
372 473 .img-tabs-block {
373 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 479 .img-tab-content {
387   - padding: 10rpx 0;
  480 + padding: 20rpx 15px ;
388 481 min-height: 120rpx;
389 482 display: flex;
390 483 align-items: center;
... ... @@ -403,15 +496,83 @@ onShow(() =&gt; {
403 496 }
404 497 }
405 498  
406   -// 流程节点空状态
  499 +// 流程节点区域(完整样式,确保内容可见)
407 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 504 .empty-process {
414 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 578 </style>
418 579 \ No newline at end of file
... ...