0929
This commit is contained in:
		| @ -83,26 +83,54 @@ | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|           <!-- 横向进度时间线 - 使用Element Plus的Steps组件 --> | ||||
|           <div class="progress-timeline-container"> | ||||
|             <div class="progress-timeline"> | ||||
|               <el-steps direction="horizontal" :active="activeStepIndex" align-center class="custom-steps" :progress-dot="false"> | ||||
|                 <template v-if="trackingSteps.length > 0"> | ||||
|                   <el-step v-for="(step, index) in trackingSteps" :key="step.id" :title="step.name" :status="getStatusByIndex(index)"> | ||||
|                     <template #description> | ||||
|                       <div class="step-description"> | ||||
|                         <div class="step-person-time"> | ||||
|                           {{ step.executor || (step.getOrderPersonVo && step.getOrderPersonVo.userName) || '待分配' }} | ||||
|                         </div> | ||||
|                         <template v-if="step.intendedTime"> | ||||
|                           <div class="step-person-time">预期时间:{{ formatDateTime(step.intendedTime) }}</div> | ||||
|                         </template> | ||||
|                         <div class="step-content">预期目的:{{ step.intendedPurpose || '-' }}</div> | ||||
|                       </div> | ||||
|                     </template> | ||||
|                   </el-step> | ||||
|                 </template> | ||||
|               </el-steps> | ||||
|           <!-- 进度条设计 --> | ||||
|           <div class="tracking-progress-container" v-if="currentTrackedWorkOrder"> | ||||
|             <!-- 进度条整体容器 --> | ||||
|             <div class="progress-bar-wrapper"> | ||||
|               <!-- 进度条背景 --> | ||||
|               <div class="progress-bar-background"></div> | ||||
|               <!-- 进度条填充 --> | ||||
|               <div class="progress-bar-fill" :style="{ width: getProgressPercentage() + '%' }"></div> | ||||
|               <!-- 进度条节点 --> | ||||
|               <div class="progress-bar-nodes"> | ||||
|                 <div | ||||
|                   v-for="(step, index) in trackingSteps" | ||||
|                   :key="step.id" | ||||
|                   class="progress-node" | ||||
|                   :class="getStepStatusClass(index)" | ||||
|                   :style="{ left: getNodePosition(index) + '%' }" | ||||
|                 > | ||||
|                   <div class="node-circle"> | ||||
|                     <div class="node-icon">{{ step.code || index + 1 }}</div> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <!-- 步骤信息显示 --> | ||||
|             <div class="progress-steps-info"> | ||||
|               <div v-for="(step, index) in trackingSteps" :key="step.id" class="step-info-card" :class="getStepStatusClass(index)"> | ||||
|                 <div class="step-header"> | ||||
|                   <div class="step-number">{{ step.code || index + 1 }}</div> | ||||
|                   <div class="step-name">{{ step.name }}</div> | ||||
|                 </div> | ||||
|                 <div class="step-details"> | ||||
|                   <div class="step-person"> | ||||
|                     <i class="el-icon-user"></i> | ||||
|                     {{ step.executor || (step.getOrderPersonVo && step.getOrderPersonVo.userName) || '待分配' }} | ||||
|                   </div> | ||||
|                   <template v-if="step.intendedTime"> | ||||
|                     <div class="step-time"> | ||||
|                       <i class="el-icon-time"></i> | ||||
|                       预期时间:{{ formatDateTime(step.intendedTime) }} | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   <div class="step-purpose"> | ||||
|                     <i class="el-icon-document"></i> | ||||
|                     预期目的:{{ step.intendedPurpose || '-' }} | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
| @ -612,7 +640,8 @@ const updateCurrentTrackedOrder = () => { | ||||
|         intendedTime: node.intendedTime, | ||||
|         finishTime: node.finishTime, | ||||
|         intendedPurpose: node.intendedPurpose || '-', | ||||
|         remark: node.remark || '' | ||||
|         remark: node.remark || '', | ||||
|         status: node.status || '' // 添加status字段 | ||||
|       })); | ||||
|     } else { | ||||
|       // 如果nodes数组为空,创建一些默认的步骤数据 | ||||
| @ -620,13 +649,12 @@ const updateCurrentTrackedOrder = () => { | ||||
|     } | ||||
|  | ||||
|     // 设置当前激活步骤索引 | ||||
|     // 如果有实际的完成时间,我们可以基于此设置激活步骤 | ||||
|     // 否则默认设置为第一个步骤 | ||||
|     // 根据status字段判断,status='1'表示完成,status='2'表示未完成,status='3'表示失败 | ||||
|     activeStepIndex.value = 0; | ||||
|  | ||||
|     // 检查是否有已完成的步骤,如果有,将激活步骤设置为最后一个已完成步骤的下一个 | ||||
|     for (let i = trackingSteps.value.length - 1; i >= 0; i--) { | ||||
|       if (trackingSteps.value[i].finishTime) { | ||||
|       if (trackingSteps.value[i].status === '1') { | ||||
|         activeStepIndex.value = Math.min(i + 1, trackingSteps.value.length - 1); | ||||
|         break; | ||||
|       } | ||||
| @ -796,8 +824,10 @@ const refreshTrackingSteps = async () => { | ||||
|  | ||||
| // 根据索引获取步骤状态 | ||||
| const getStatusByIndex = (index) => { | ||||
|   if (!currentTrackedWorkOrder.value) return 'wait'; | ||||
|  | ||||
|   if (index < activeStepIndex.value) { | ||||
|     return 'success'; | ||||
|     return 'finish'; | ||||
|   } else if (index === activeStepIndex.value) { | ||||
|     return 'process'; | ||||
|   } else { | ||||
| @ -805,6 +835,101 @@ const getStatusByIndex = (index) => { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 试验记录页面步骤条状态判断 - 重点跟踪区域专用 | ||||
| const getStepStatusClass = (index) => { | ||||
|   if (!currentTrackedWorkOrder.value) return 'pending'; | ||||
|  | ||||
|   const step = trackingSteps.value[index]; | ||||
|   if (step) { | ||||
|     // 优先根据status字段判断状态 | ||||
|     const status = step.status?.toString() || ''; | ||||
|  | ||||
|     if (status === '1') { | ||||
|       return 'completed'; // 完成状态 - 绿色 | ||||
|     } else if (status === '3') { | ||||
|       return 'delayed'; // 失败状态 - 红色 | ||||
|     } else if (status === '2') { | ||||
|       return 'pending'; // 未完成状态 - 灰色 | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // fallback到基于索引的判断逻辑 | ||||
|   if (index < activeStepIndex.value) { | ||||
|     return 'completed'; | ||||
|   } else if (index === activeStepIndex.value) { | ||||
|     return 'active'; | ||||
|   } else { | ||||
|     return 'pending'; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 获取进度线状态 - 重点跟踪区域专用 | ||||
| const getLineStatusClass = (index) => { | ||||
|   if (!currentTrackedWorkOrder.value) return 'pending'; | ||||
|  | ||||
|   // 进度线状态与前一个步骤状态保持一致 | ||||
|   const prevStepIndex = index; | ||||
|   const prevStep = trackingSteps.value[prevStepIndex]; | ||||
|  | ||||
|   if (prevStep) { | ||||
|     const status = prevStep.status?.toString() || ''; | ||||
|  | ||||
|     if (status === '1') { | ||||
|       return 'completed'; // 前一步骤已完成 - 绿色 | ||||
|     } else if (status === '3') { | ||||
|       return 'delayed'; // 前一步骤失败 - 红色 | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   //  fallback到基于索引的判断逻辑 | ||||
|   if (index < activeStepIndex.value) { | ||||
|     return 'completed'; | ||||
|   } else { | ||||
|     return 'pending'; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 计算进度百分比 | ||||
| const getProgressPercentage = () => { | ||||
|   if (!currentTrackedWorkOrder.value || trackingSteps.value.length === 0) return 0; | ||||
|  | ||||
|   // 优先使用API返回的progress字段值 | ||||
|   if (currentTrackedWorkOrder.value.progress) { | ||||
|     try { | ||||
|       // 将字符串类型的progress转换为数字 | ||||
|       const progressValue = parseFloat(currentTrackedWorkOrder.value.progress); | ||||
|       // 确保进度值在0-100之间 | ||||
|       return Math.min(Math.max(progressValue, 0), 100); | ||||
|     } catch (error) { | ||||
|       console.warn('解析progress字段失败,使用默认计算逻辑:', error); | ||||
|       // 解析失败时使用原有逻辑 | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // 计算已完成步骤数 | ||||
|   const completedSteps = trackingSteps.value.filter((step) => step.status === '1').length; | ||||
|  | ||||
|   // 如果没有已完成步骤,但有活跃步骤,则使用活跃步骤的位置 | ||||
|   if (completedSteps === 0 && activeStepIndex.value >= 0) { | ||||
|     return (activeStepIndex.value / trackingSteps.value.length) * 100; | ||||
|   } | ||||
|  | ||||
|   // 计算进度百分比 | ||||
|   const percentage = (completedSteps / trackingSteps.value.length) * 100; | ||||
|  | ||||
|   // 确保最大为100% | ||||
|   return Math.min(percentage, 100); | ||||
| }; | ||||
|  | ||||
| // 计算节点位置百分比 | ||||
| const getNodePosition = (index) => { | ||||
|   if (!currentTrackedWorkOrder.value || trackingSteps.value.length <= 1) return 0; | ||||
|  | ||||
|   // 等距分布节点 | ||||
|   const position = (index / (trackingSteps.value.length - 1)) * 100; | ||||
|   return position; | ||||
| }; | ||||
|  | ||||
| // 将状态码转换为可读的状态文本 | ||||
| const getStatusText = (statusCode) => { | ||||
|   const statusMap = { | ||||
| @ -1762,10 +1887,6 @@ const handleCloseDetailDialog = () => { | ||||
|   transition: all 0.3s ease; | ||||
| } | ||||
|  | ||||
| .step-connector.connector-completed { | ||||
|   background: linear-gradient(to bottom, #52c41a, #73d13d); | ||||
| } | ||||
|  | ||||
| /* 动画效果 */ | ||||
| @keyframes fadeInUp { | ||||
|   from { | ||||
| @ -3320,13 +3441,299 @@ const handleCloseDetailDialog = () => { | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| /* 重点跟踪区域进度条样式 */ | ||||
| .tracking-progress-container { | ||||
|   padding: 20px; | ||||
|   background-color: #fff; | ||||
| } | ||||
|  | ||||
| /* 进度条包装器 */ | ||||
| .progress-bar-wrapper { | ||||
|   position: relative; | ||||
|   height: 40px; | ||||
|   margin-bottom: 30px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| /* 进度条背景 */ | ||||
| .progress-bar-background { | ||||
|   position: absolute; | ||||
|   top: 50%; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   height: 6px; | ||||
|   background-color: #e5e7eb; | ||||
|   border-radius: 3px; | ||||
|   transform: translateY(-50%); | ||||
| } | ||||
|  | ||||
| /* 进度条填充 */ | ||||
| .progress-bar-fill { | ||||
|   position: absolute; | ||||
|   top: 50%; | ||||
|   left: 0; | ||||
|   height: 6px; | ||||
|   background: linear-gradient(90deg, #00b42a, #95de64); | ||||
|   border-radius: 3px; | ||||
|   transition: width 0.6s ease; | ||||
|   transform: translateY(-50%); | ||||
| } | ||||
|  | ||||
| /* 进度条节点容器 */ | ||||
| .progress-bar-nodes { | ||||
|   position: relative; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
|  | ||||
| /* 进度条节点 */ | ||||
| .progress-node { | ||||
|   position: absolute; | ||||
|   top: 50%; | ||||
|   transform: translate(-50%, -50%); | ||||
| } | ||||
|  | ||||
| /* 节点圆圈 */ | ||||
| .node-circle { | ||||
|   width: 36px; | ||||
|   height: 36px; | ||||
|   border-radius: 50%; | ||||
|   background-color: #fff; | ||||
|   border: 2px solid #e5e7eb; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | ||||
|   transition: all 0.3s ease; | ||||
|   z-index: 2; | ||||
| } | ||||
|  | ||||
| /* 节点图标/数字 */ | ||||
| .node-icon { | ||||
|   font-size: 14px; | ||||
|   font-weight: 600; | ||||
|   color: #6b7280; | ||||
| } | ||||
|  | ||||
| /* 步骤信息卡片容器 */ | ||||
| .progress-steps-info { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   flex-wrap: wrap; | ||||
|   gap: 12px; | ||||
| } | ||||
|  | ||||
| /* 单个步骤信息卡片 */ | ||||
| .step-info-card { | ||||
|   flex: 1; | ||||
|   min-width: 160px; | ||||
|   max-width: 250px; | ||||
|   padding: 12px; | ||||
|   border-radius: 6px; | ||||
|   border: 1px solid #f0f0f0; | ||||
|   background-color: #fff; | ||||
|   transition: all 0.3s ease; | ||||
| } | ||||
|  | ||||
| /* 步骤卡片头部 */ | ||||
| .step-header { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   margin-bottom: 8px; | ||||
| } | ||||
|  | ||||
| /* 步骤数字 */ | ||||
| .step-info-card .step-number { | ||||
|   width: 20px; | ||||
|   height: 20px; | ||||
|   border-radius: 50%; | ||||
|   background-color: #e5e7eb; | ||||
|   color: #6b7280; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   font-size: 11px; | ||||
|   font-weight: 600; | ||||
|   margin-right: 6px; | ||||
| } | ||||
|  | ||||
| /* 步骤名称 */ | ||||
| .step-name { | ||||
|   font-size: 16px; | ||||
|   font-weight: 600; | ||||
|   color: #1f2937; | ||||
| } | ||||
|  | ||||
| /* 步骤详情 */ | ||||
| .step-details { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   gap: 8px; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| .step-details > div { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   color: #6b7280; | ||||
| } | ||||
|  | ||||
| .step-details i { | ||||
|   margin-right: 6px; | ||||
|   font-size: 14px; | ||||
| } | ||||
|  | ||||
| /* 已完成状态样式 - 绿色 */ | ||||
| .step-info-card.completed { | ||||
|   border-color: #00b42a; | ||||
|   background-color: #f6ffed; | ||||
| } | ||||
|  | ||||
| .step-info-card.completed .step-number { | ||||
|   background-color: #00b42a; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .progress-node.completed .node-circle { | ||||
|   border-color: #00b42a; | ||||
|   background-color: #fff; | ||||
| } | ||||
|  | ||||
| .progress-node.completed .node-icon { | ||||
|   background-color: #00b42a; | ||||
|   color: white; | ||||
|   border-radius: 50%; | ||||
|   width: 28px; | ||||
|   height: 28px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
|  | ||||
| /* 进行中状态样式 */ | ||||
| .step-info-card.active { | ||||
|   border-color: #165dff; | ||||
|   background-color: #f0f7ff; | ||||
|   box-shadow: 0 4px 12px rgba(22, 93, 255, 0.1); | ||||
| } | ||||
|  | ||||
| .step-info-card.active .step-number { | ||||
|   background-color: #165dff; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .progress-node.active .node-circle { | ||||
|   border-color: #165dff; | ||||
|   background-color: #fff; | ||||
|   animation: pulse 2s infinite; | ||||
| } | ||||
|  | ||||
| .progress-node.active .node-icon { | ||||
|   background-color: #165dff; | ||||
|   color: white; | ||||
|   border-radius: 50%; | ||||
|   width: 28px; | ||||
|   height: 28px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
|  | ||||
| /* 待处理状态样式 */ | ||||
| .step-info-card.pending { | ||||
|   border-color: #e5e7eb; | ||||
|   background-color: #fff; | ||||
| } | ||||
|  | ||||
| /* 失败/逾期状态样式 */ | ||||
| .step-info-card.delayed { | ||||
|   border-color: #ff4d4f; | ||||
|   background-color: #fff2f0; | ||||
| } | ||||
|  | ||||
| .step-info-card.delayed .step-number { | ||||
|   background-color: #ff4d4f; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .progress-node.delayed .node-circle { | ||||
|   border-color: #ff4d4f; | ||||
|   background-color: #fff; | ||||
| } | ||||
|  | ||||
| .progress-node.delayed .node-icon { | ||||
|   background-color: #ff4d4f; | ||||
|   color: white; | ||||
|   border-radius: 50%; | ||||
|   width: 28px; | ||||
|   height: 28px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
|  | ||||
| /* 脉冲动画 */ | ||||
| @keyframes pulse { | ||||
|   0% { | ||||
|     box-shadow: 0 0 0 0 rgba(22, 93, 255, 0.4); | ||||
|   } | ||||
|   70% { | ||||
|     box-shadow: 0 0 0 10px rgba(22, 93, 255, 0); | ||||
|   } | ||||
|   100% { | ||||
|     box-shadow: 0 0 0 0 rgba(22, 93, 255, 0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .progress-step.completed .step-number { | ||||
|   background-color: #00b42a; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .progress-line.completed { | ||||
|   background-color: #00b42a; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .progress-step.delayed .step-number { | ||||
|   background-color: #dc2626; | ||||
|   color: white; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .progress-line.delayed { | ||||
|   background-color: #dc2626; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .step-name { | ||||
|   font-size: 14px; | ||||
|   font-weight: 500; | ||||
|   color: #1f2329; | ||||
|   margin-bottom: 8px; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .step-info { | ||||
|   font-size: 12px; | ||||
|   color: #6b7280; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .step-person-time { | ||||
|   margin-bottom: 4px; | ||||
| } | ||||
|  | ||||
| .tracking-progress-timeline .step-purpose { | ||||
|   margin-top: 4px; | ||||
|   line-height: 1.4; | ||||
| } | ||||
|  | ||||
| .custom-step:hover { | ||||
|   transform: translateY(-8px); | ||||
|   filter: brightness(1.03); | ||||
| } | ||||
|  | ||||
| /* 步骤连接线 */ | ||||
| .custom-step:not(:last-child)::after { | ||||
| /* 步骤连接线 - 默认(进行中) */ | ||||
| .custom-step:not(:last-child):not(.is-wait)::after { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   top: 32px; | ||||
| @ -3338,6 +3745,12 @@ const handleCloseDetailDialog = () => { | ||||
|   box-shadow: 0 2px 8px rgba(22, 93, 255, 0.3); | ||||
| } | ||||
|  | ||||
| /* 已完成步骤连接线 */ | ||||
| .custom-step.completed:not(:last-child)::after { | ||||
|   background: linear-gradient(90deg, #00b42a 0%, #95de64 100%); | ||||
|   box-shadow: 0 2px 8px rgba(0, 180, 42, 0.3); | ||||
| } | ||||
|  | ||||
| /* 待处理步骤连接线 */ | ||||
| .custom-step.is-wait:not(:last-child)::after { | ||||
|   background: linear-gradient(90deg, #dcdfe6 0%, #e4e7ed 100%); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user