0926
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="operation-inspection">
|
||||
<!-- 导航标签 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 子选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="execution-records">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -86,7 +86,7 @@
|
||||
<!-- 已完成状态的额外信息 -->
|
||||
<div v-if="task.status === 'completed'" class="task-result">
|
||||
<span class="detail-label">完成时间</span>
|
||||
<span class="detail-value">{{ task.completeTime }}</span>
|
||||
<span class="detail-value">{{ task.reportFinishTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -306,7 +306,7 @@
|
||||
<div v-if="detailData.status === '3'" class="info-row">
|
||||
<div class="info-item">
|
||||
<span class="info-label">完成时间:</span>
|
||||
<span class="info-value">{{ formatDate(detailData.completeTime) }}</span>
|
||||
<span class="info-value">{{ formatDate(detailData.reportFinishTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -454,7 +454,10 @@ async function getTaskList() {
|
||||
// 修复维修人字段,从sendPersonVo对象中获取用户名
|
||||
maintainer: item.sendPersonVo?.userName || '未分配',
|
||||
|
||||
completeTime: item.completeTime ? formatDate(item.completeTime) : '',
|
||||
// 尝试从多个可能的字段中获取完成时间,确保有值时能正确显示
|
||||
completeTime: item.completeTime ? formatDate(item.completeTime) : item.finishTime ? formatDate(item.finishTime) : '',
|
||||
// 直接使用并格式化reportFinishTime字段
|
||||
reportFinishTime: item.reportFinishTime ? formatDate(item.reportFinishTime) : '',
|
||||
actionText: getActionText(item.status),
|
||||
actionClass: getActionClass(item.status),
|
||||
reportInfo: item.reportInfo,
|
||||
@ -1003,7 +1006,7 @@ const handleSaveResult = async () => {
|
||||
statusText: '已完成', // 状态文本
|
||||
reportFinal: reportFinal.value, // 处理结果
|
||||
reportFinishTime: reportFinishTime, // 完成时间
|
||||
completeTime: reportFinishTime, // 完成时间(向后兼容字段)
|
||||
completeTime: reportFinishTime, // 完成时间
|
||||
|
||||
// ③ 复用原始任务数据中的所有必要字段
|
||||
name: originalTask.title, // 任务名称(原始任务标题)
|
||||
@ -1481,7 +1484,7 @@ onMounted(async () => {
|
||||
/* 任务卡片样式 - 恢复优先级标签背景色 */
|
||||
.task-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
@ -1494,22 +1497,14 @@ onMounted(async () => {
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 260px; /* 增加高度以适应新增的预计完成时间 */
|
||||
min-height: 280px; /* 增加高度以确保内容完整显示 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
background-color: #fff;
|
||||
padding: 12px 0 0 0;
|
||||
z-index: 10;
|
||||
.task-content {
|
||||
flex: 1; /* 内容区域占满剩余空间 */
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.task-actions .el-button {
|
||||
@ -1562,6 +1557,9 @@ onMounted(async () => {
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 恢复优先级标签背景色样式 */
|
||||
@ -1571,6 +1569,7 @@ onMounted(async () => {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.priority-high {
|
||||
@ -1599,17 +1598,20 @@ onMounted(async () => {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
flex: 0 0 80px;
|
||||
flex: 0 0 85px;
|
||||
color: #86909c;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
color: #4e5969;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.task-result {
|
||||
@ -1626,13 +1628,7 @@ onMounted(async () => {
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
background-color: #fff;
|
||||
padding: 12px 0 0 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="execution-records">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,8 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -138,7 +137,6 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
|
||||
|
||||
// 搜索和筛选条件
|
||||
const searchKeyword = ref('');
|
||||
const plateNumber1 = ref('');
|
||||
|
||||
@ -51,35 +51,90 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 步骤相关样式 */
|
||||
.steps-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
/* 步骤相关样式 - 详情弹窗专用 - 使用外部CSS样式 */
|
||||
.task-detail-container .steps-container {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
.task-detail-container .step-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
padding: 16px;
|
||||
background-color: #ffffff;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 15px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e4e7ed;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
.task-detail-container .step-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.task-detail-container .step-number {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: #409eff;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
margin-right: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.task-detail-container .step-item:not(:last-child)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 16px;
|
||||
width: 2px;
|
||||
height: calc(100% + 5px);
|
||||
background-color: #e4e7ed;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.task-detail-container .step-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.task-detail-container .step-name {
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-purpose {
|
||||
color: #606266;
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-time,
|
||||
.task-detail-container .step-finish-time,
|
||||
.task-detail-container .step-remark {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-status {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.step-info {
|
||||
@ -110,8 +165,8 @@
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* 状态相关样式 */
|
||||
.step-status {
|
||||
/* 步骤状态样式 - 详情弹窗专用 */
|
||||
.task-detail-container .step-status {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
@ -120,26 +175,29 @@
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 步骤状态样式 */
|
||||
.step-status.status-pending {
|
||||
/* 步骤状态样式 - 待执行 */
|
||||
.task-detail-container .step-status.status-pending {
|
||||
background-color: #e6f7ff;
|
||||
color: #1677ff;
|
||||
border: 1px solid #91d5ff;
|
||||
}
|
||||
|
||||
.step-status.status-executing {
|
||||
/* 步骤状态样式 - 执行中 */
|
||||
.task-detail-container .step-status.status-executing {
|
||||
background-color: #fffbe6;
|
||||
color: #fa8c16;
|
||||
border: 1px solid #ffe58f;
|
||||
}
|
||||
|
||||
.step-status.status-completed {
|
||||
/* 步骤状态样式 - 已完成 */
|
||||
.task-detail-container .step-status.status-completed {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border: 1px solid #b7eb8f;
|
||||
}
|
||||
|
||||
.step-status.status-delayed {
|
||||
/* 步骤状态样式 - 已延期 */
|
||||
.task-detail-container .step-status.status-delayed {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border: 1px solid #ffccc7;
|
||||
@ -238,6 +296,35 @@
|
||||
.info-row {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 步骤条响应式设计 */
|
||||
.task-detail-container .steps-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-item > * {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.task-detail-container .step-number {
|
||||
margin-bottom: 10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.task-detail-container .step-item:not(:last-child)::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹窗按钮样式 */
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="work-order-management">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
@ -379,7 +379,7 @@
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ getStepStatusText(node.status) }}
|
||||
{{ node.status === '2' ? '未完成' : '已完成' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="box-container">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab active" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="main-content">
|
||||
<!-- 左侧日历区域 -->
|
||||
<div class="calendar-container">
|
||||
@ -54,6 +54,7 @@
|
||||
class="todo-item"
|
||||
:class="{ 'important': item.taskLevel === '重要', 'completed': item.status === 2 }"
|
||||
>
|
||||
<el-checkbox class="todo-checkbox" :checked="item.status === 2" @change="handleStatusChange(item, $event)"></el-checkbox>
|
||||
<div
|
||||
class="todo-color-indicator"
|
||||
:class="{
|
||||
@ -63,7 +64,6 @@
|
||||
completed: item.status === 2
|
||||
}"
|
||||
></div>
|
||||
<el-checkbox class="todo-checkbox" :checked="item.status === 2" @change="handleStatusChange(item, $event)"></el-checkbox>
|
||||
<div class="todo-content">
|
||||
<div class="todo-main">
|
||||
<div class="todo-title">{{ item.title }}</div>
|
||||
@ -590,16 +590,6 @@ const handleInspection7 = () => {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* 已完成任务的样式 */
|
||||
.todo-color-indicator.completed {
|
||||
background-color: #dcdfe6;
|
||||
@ -609,7 +599,15 @@ const handleInspection7 = () => {
|
||||
color: #909399;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
@ -849,13 +847,14 @@ const handleInspection7 = () => {
|
||||
|
||||
/* 悬停显示操作按钮 */
|
||||
.todo-item:hover .todo-actions {
|
||||
opacity: 1;
|
||||
background: linear-gradient(to right, rgba(173, 216, 230, 0), rgb(64, 158, 255));
|
||||
right: 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 内容区域平移以给按钮留出空间 */
|
||||
/* 取消内容区域平移效果 */
|
||||
.todo-item:hover .todo-content {
|
||||
transform: translateX(-120px);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
@ -942,7 +941,7 @@ const handleInspection7 = () => {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
::v-deep .custom-date-cell {
|
||||
:deep(.custom-date-cell) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
@ -983,13 +982,13 @@ const handleInspection7 = () => {
|
||||
}
|
||||
|
||||
/* 穿透作用域,强制设置日历单元格为正方形 */
|
||||
::v-deep .el-calendar-table td {
|
||||
:deep(.el-calendar-table td) {
|
||||
padding: 2px;
|
||||
vertical-align: top;
|
||||
width: 120px; /* 强制宽度 */
|
||||
height: 120px; /* 强制高度(与宽度一致) */
|
||||
}
|
||||
::v-deep .el-calendar-day {
|
||||
:deep(.el-calendar-day) {
|
||||
padding: 0; /* 移除默认内边距 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="dispatch-records">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -430,7 +430,7 @@
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ getStepStatusText(node.status) }}
|
||||
{{ node.status === '2' ? '未完成' : '已完成' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -737,11 +737,6 @@ const getStepStatusText = (status) => {
|
||||
return statusMap[statusStr] || '未知状态';
|
||||
};
|
||||
|
||||
// 按模块分组节点(保留该函数用于兼容)
|
||||
const groupNodesByModule = (nodes) => {
|
||||
if (!nodes || !nodes.length) return [];
|
||||
return [{ module: '', items: nodes }];
|
||||
|
||||
// 初始化加载数据
|
||||
const initData = async () => {
|
||||
await fetchStatisticsData();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="nav-tab active" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -1065,11 +1065,29 @@ defineExpose({ getTaskList });
|
||||
async function getTaskList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await qiangxiulist({
|
||||
// 构建请求参数,包含筛选条件
|
||||
const requestParams = {
|
||||
projectId: 1,
|
||||
pageNum: currentPage.value,
|
||||
pageSize: pageSize.value
|
||||
});
|
||||
};
|
||||
|
||||
// 添加任务状态筛选条件
|
||||
if (taskStatus.value && taskStatus.value !== 'all') {
|
||||
requestParams.status = taskStatus.value;
|
||||
}
|
||||
|
||||
// 添加计划类型筛选条件
|
||||
if (planType.value && planType.value !== 'all') {
|
||||
requestParams.planType = planType.value;
|
||||
}
|
||||
|
||||
// 添加执行人筛选条件
|
||||
if (executor.value && executor.value !== 'all') {
|
||||
requestParams.executor = executor.value;
|
||||
}
|
||||
|
||||
const res = await qiangxiulist(requestParams);
|
||||
|
||||
if (res.code === 200 && res.rows) {
|
||||
total.value = res.total || 0;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,14 +10,7 @@
|
||||
<div class="nav-tab active" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题和操作区 -->
|
||||
<div class="header-section">
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" class="export-btn" @click="handleExport"> 导出数据 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
@ -1524,12 +1517,7 @@ const handleInspectionManagement2 = () => {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 无数据提示 */
|
||||
.no-info {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
/* */
|
||||
|
||||
/* 分配弹窗样式 */
|
||||
.assign-container {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="operation-organization">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -132,8 +132,7 @@ import { ref, watch, onMounted } from 'vue';
|
||||
import router from '@/router';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 激活的选项卡
|
||||
const activeTab = ref('personnel');
|
||||
//
|
||||
|
||||
// 统计数据(保持原有数据不变)
|
||||
const totalPersonnel = ref(36);
|
||||
@ -445,36 +444,7 @@ const handleInspectionManagement3 = () => {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__header {
|
||||
margin: 0 -20px;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__nav-wrap::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 16px 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item.is-active {
|
||||
color: #165dff;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid #165dff;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item:hover {
|
||||
color: #165dff;
|
||||
}
|
||||
/* */
|
||||
|
||||
/* 内容容器样式 */
|
||||
.content-container {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="operation-inspection">
|
||||
<!-- 1. 顶部导航选项卡(对应原试验系统的外层导航) -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡和按钮组合 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -67,13 +67,7 @@
|
||||
<el-table-column align="center" prop="type" label="巡检类型" width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="cycle" label="巡检周期" width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="dateRange" label="执行时间范围"></el-table-column>
|
||||
<el-table-column align="center" prop="progress" label="完成进度" width="120">
|
||||
<template #default="scope">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: scope.row.progress + '%', backgroundColor: getProgressColor(scope.row.status) }"></div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="['status-tag', `status-${scope.row.status}`]">
|
||||
@ -374,10 +368,10 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="实验对象类型" class="form-item">
|
||||
<el-select v-model="formData.testObject" placeholder="请选择实验对象类型" class="form-input">
|
||||
<el-option label="1安全试验" value="1" />
|
||||
<el-option label="2网络实验" value="2" />
|
||||
<el-option label="3性能试验" value="3" />
|
||||
<el-option label="4" value="4" />
|
||||
<el-option label="安全试验" value="1" />
|
||||
<el-option label="网络实验" value="2" />
|
||||
<el-option label="性能试验" value="3" />
|
||||
<el-option label="其他试验" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
@ -418,36 +412,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 试验步骤 -->
|
||||
<el-form-item label="试验步骤" class="form-item" style="width: 100%">
|
||||
<div class="steps-container">
|
||||
<div class="step-item" v-for="(step, index) in formData.steps" :key="index">
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<el-input v-model="step.name" placeholder="输入步骤名称" style="flex: 1; margin-right: 10px" />
|
||||
<el-input v-model="step.intendedPurpose" placeholder="输入预期目的" style="flex: 1; margin-right: 10px" />
|
||||
<el-date-picker
|
||||
v-model="step.intendedTime"
|
||||
type="datetime"
|
||||
placeholder="选择计划时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm"
|
||||
style="width: 180px; margin-right: 10px"
|
||||
/>
|
||||
<el-button
|
||||
v-if="formData.steps.length > 1"
|
||||
type="text"
|
||||
size="small"
|
||||
class="delete-step-btn"
|
||||
@click="deleteStep(index)"
|
||||
style="color: #f56c6c"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="text" size="small" class="add-step-btn" @click="addStep">添加步骤</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 所需设备与准备 -->
|
||||
<el-form-item label="所需资源与设备" class="form-item" style="width: 100%">
|
||||
<div class="equipment-list">
|
||||
@ -490,119 +454,113 @@
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
class="custom-experiment-dialog"
|
||||
center
|
||||
>
|
||||
<div class="detail-content">
|
||||
<!-- 基础信息 -->
|
||||
<div class="detail-section">
|
||||
<h3 class="section-title">基础信息</h3>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">计划名称:</label>
|
||||
<span class="detail-value">{{ detailData.planName || '-' }}</span>
|
||||
<div v-if="detailData" class="task-detail-container">
|
||||
<!-- 基础信息卡片 -->
|
||||
<div class="detail-card">
|
||||
<h3 class="card-title">基础信息</h3>
|
||||
<div class="card-content">
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<label class="info-label">计划名称:</label>
|
||||
<span class="info-value">{{ detailData.planName || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">计划编号:</label>
|
||||
<span class="detail-value">{{ detailData.planCode || '-' }}</span>
|
||||
<div class="info-item">
|
||||
<label class="info-label">计划编号:</label>
|
||||
<span class="info-value">{{ detailData.planCode || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">实验对象:</label>
|
||||
<span class="detail-value">{{ getTestObjectText(detailData.testObject) || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">负责人:</label>
|
||||
<span class="detail-value">{{ detailData.person?.userName || '-' }}</span>
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<label class="info-label">实验对象:</label>
|
||||
<span class="info-value">{{ getTestObjectText(detailData.testObject) || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">开始时间:</label>
|
||||
<span class="detail-value">{{ detailData.beginTime ? formatDate(detailData.beginTime) : '-' }}</span>
|
||||
<div class="info-item">
|
||||
<label class="info-label">负责人:</label>
|
||||
<span class="info-value">{{ detailData.person?.userName || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<label class="info-label">开始时间:</label>
|
||||
<span class="info-value">{{ detailData.beginTime ? formatDate(detailData.beginTime) : '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label class="info-label">结束时间:</label>
|
||||
<span class="info-value">{{ detailData.endTime ? formatDate(detailData.endTime) : '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">结束时间:</label>
|
||||
<span class="detail-value">{{ detailData.endTime ? formatDate(detailData.endTime) : '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实验设备 -->
|
||||
<div v-if="detailData.testDevice" class="detail-section">
|
||||
<h3 class="section-title">实验设备</h3>
|
||||
<div class="device-list">
|
||||
<span v-for="(device, index) in detailData.testDevice.split(',')" :key="index" class="device-tag">
|
||||
{{ device.trim() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 执行步骤 -->
|
||||
<div v-if="detailData.nodes && detailData.nodes.length > 0" class="detail-card">
|
||||
<h3 class="card-title">执行步骤</h3>
|
||||
<div class="steps-container">
|
||||
<div v-for="(node, index) in detailData.nodes" :key="node.id || index" class="step-item">
|
||||
<div class="step-number">{{ node.code || index + 1 }}</div>
|
||||
<div class="step-info">
|
||||
<div class="step-name">{{ node.name || '未命名步骤' }}</div>
|
||||
<div class="step-purpose">{{ node.intendedPurpose || '无说明' }}</div>
|
||||
<div class="step-time">计划时间:{{ formatDateTime(node.intendedTime) }}</div>
|
||||
<div v-if="node.finishTime" class="step-finish-time">完成时间:{{ formatDateTime(node.finishTime) }}</div>
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ getStepStatusText(node.status) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 兼容旧格式:如果没有nodes数据但有testStep字符串 -->
|
||||
<div v-else-if="detailData.testStep" class="detail-section">
|
||||
<h3 class="section-title">执行步骤</h3>
|
||||
<div class="steps-container">
|
||||
<div v-for="(step, index) in detailData.testStep.split(',')" :key="index" class="step-item">
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-content">{{ step.trim() }}</div>
|
||||
<div v-if="detailData.testDevice" class="detail-card">
|
||||
<h3 class="card-title">实验设备</h3>
|
||||
<div class="card-content">
|
||||
<div v-for="(device, index) in detailData.testDevice.split(',')" :key="index" class="info-item">
|
||||
<label class="info-label">设备{{ index + 1 }}:</label>
|
||||
<span class="info-value">{{ device.trim() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实验信息 -->
|
||||
<div class="detail-section">
|
||||
<h3 class="section-title">实验信息</h3>
|
||||
<div class="detail-textarea">
|
||||
<label class="detail-label">实验说明:</label>
|
||||
<div class="detail-text">{{ detailData.testInfo || '-' }}</div>
|
||||
<div class="detail-card">
|
||||
<h3 class="card-title">实验信息</h3>
|
||||
<div class="card-content">
|
||||
<div class="info-item full-width">
|
||||
<label class="info-label">实验说明:</label>
|
||||
<div class="info-value">{{ detailData.testInfo || '-' }}</div>
|
||||
</div>
|
||||
<div class="detail-textarea">
|
||||
<label class="detail-label">实验设置:</label>
|
||||
<div class="detail-text">{{ detailData.testSetting || '-' }}</div>
|
||||
<div class="info-item full-width">
|
||||
<label class="info-label">实验设置:</label>
|
||||
<div class="info-value">{{ detailData.testSetting || '-' }}</div>
|
||||
</div>
|
||||
<div class="info-item full-width">
|
||||
<label class="info-label">解决方案:</label>
|
||||
<div class="info-value">{{ detailData.testSolutions || '-' }}</div>
|
||||
</div>
|
||||
<div class="detail-textarea">
|
||||
<label class="detail-label">解决方案:</label>
|
||||
<div class="detail-text">{{ detailData.testSolutions || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 参与人员 -->
|
||||
<div v-if="detailData.persons && detailData.persons.length > 0" class="detail-section">
|
||||
<h3 class="section-title">参与人员</h3>
|
||||
<div class="participant-list">
|
||||
<div v-for="(person, index) in detailData.persons" :key="person.id" class="participant-item">
|
||||
<span class="participant-name">{{ person.userName }}</span>
|
||||
<span class="participant-team">{{ person.teamName }}</span>
|
||||
<div v-if="detailData.persons && detailData.persons.length > 0" class="detail-card">
|
||||
<h3 class="card-title">参与人员</h3>
|
||||
<div class="card-content">
|
||||
<div v-for="(person, index) in detailData.persons" :key="person.id" class="info-row">
|
||||
<div class="info-item">
|
||||
<label class="info-label">姓名:</label>
|
||||
<span class="info-value">{{ person.userName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label class="info-label">团队:</label>
|
||||
<span class="info-value">{{ person.teamName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 巡检项目 -->
|
||||
<div v-if="detailData.inspectionItemList && detailData.inspectionItemList.length > 0" class="detail-section">
|
||||
<h3 class="section-title">巡检项目</h3>
|
||||
<div class="inspection-list">
|
||||
<div v-for="(item, index) in detailData.inspectionItemList" :key="item.id" class="inspection-item">
|
||||
<span class="inspection-name">{{ item.name }}</span>
|
||||
<span class="inspection-type">{{ item.type }}</span>
|
||||
<div v-if="detailData.inspectionItemList && detailData.inspectionItemList.length > 0" class="detail-card">
|
||||
<h3 class="card-title">巡检项目</h3>
|
||||
<div class="card-content">
|
||||
<div v-for="(item, index) in detailData.inspectionItemList" :key="item.id" class="info-row">
|
||||
<div class="info-item">
|
||||
<label class="info-label">项目名称:</label>
|
||||
<span class="info-value">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label class="info-label">项目类型:</label>
|
||||
<span class="info-value">{{ item.type }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading-details">
|
||||
<el-skeleton :count="6" :columns="2" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDetailDialog = false">关闭</el-button>
|
||||
@ -618,7 +576,6 @@ import router from '@/router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { shiyanDetail, shiyanlist, addshiyan, updateshiyan } from '@/api/zhinengxunjian/shiyan/index';
|
||||
import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian/index';
|
||||
import { addjiedian, updatejiedian } from '@/api/zhinengxunjian/jiedian/index';
|
||||
// 1. 选项卡状态管理
|
||||
const activeTab = ref('plan'); // 默认为"巡检计划"
|
||||
const timeRange = ref('month'); // 统计时间范围:月/周/日
|
||||
@ -952,10 +909,6 @@ const handleSave = async () => {
|
||||
personIds: formData.value.participants.join(','),
|
||||
inspectionItems: '',
|
||||
testSolutions: formData.value.riskMitigation,
|
||||
testStep: formData.value.steps
|
||||
.filter((step) => step.name.trim() || step.intendedPurpose.trim())
|
||||
.map((step) => `${step.name || ''}: ${step.intendedPurpose || ''}`)
|
||||
.join(','),
|
||||
testDevice: formData.value.equipments
|
||||
.filter((equip) => equip.selected)
|
||||
.map((equip) => equip.name)
|
||||
@ -965,83 +918,15 @@ const handleSave = async () => {
|
||||
id: editRecordId.value // 若后端用planId等,需改为对应字段名
|
||||
};
|
||||
|
||||
// 4. 处理步骤数据并调用接口
|
||||
// 调用接口
|
||||
let response;
|
||||
|
||||
// 处理步骤数据格式
|
||||
const stepsData = formData.value.steps
|
||||
.filter((step) => step.name.trim() || step.intendedPurpose.trim())
|
||||
.map((step, index) => ({
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString(),
|
||||
params: {},
|
||||
module: 3,
|
||||
code: index + 1,
|
||||
name: step.name,
|
||||
intendedPurpose: step.intendedPurpose,
|
||||
intendedTime: step.intendedTime ? new Date(step.intendedTime).toISOString() : new Date().toISOString(),
|
||||
finishTime: '',
|
||||
remark: '',
|
||||
status: 2
|
||||
}));
|
||||
|
||||
// 获取nodeIds
|
||||
let nodeIds = '';
|
||||
if (editRecordId.value) {
|
||||
// 编辑模式:获取试验详情,以获取原始步骤的id
|
||||
const detailResponse = await shiyanDetail(editRecordId.value);
|
||||
if (detailResponse.code !== 200) {
|
||||
ElMessage.error('获取试验详情失败');
|
||||
return;
|
||||
}
|
||||
|
||||
const experimentDetail = detailResponse.data.rows?.[0] || detailResponse.data;
|
||||
// 兼容两种数据结构:可能在rows数组中,也可能直接在data中
|
||||
|
||||
if (experimentDetail.nodes && Array.isArray(experimentDetail.nodes)) {
|
||||
// 按code排序原始nodes数组
|
||||
const sortedNodes = [...experimentDetail.nodes].sort((a, b) => (a.code || 0) - (b.code || 0));
|
||||
|
||||
// 为新的步骤数据添加id
|
||||
const updatedSteps = stepsData.map((step, index) => ({
|
||||
...step,
|
||||
id: sortedNodes[index]?.id || 0 // 使用原始步骤的id,如果不存在则使用0
|
||||
}));
|
||||
|
||||
// 调用updatejiedian接口更新步骤,直接传递数组
|
||||
const updateResponse = await updatejiedian(updatedSteps);
|
||||
if (updateResponse.code !== 200) {
|
||||
ElMessage.error('更新步骤失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用原始的nodeIds,避免重新创建步骤
|
||||
nodeIds = experimentDetail.nodeIds;
|
||||
}
|
||||
|
||||
// 编辑模式:调用更新接口
|
||||
response = await updateshiyan(requestData);
|
||||
} else {
|
||||
// 新增模式:调用addjiedian接口创建步骤
|
||||
const jiedianResponse = await addjiedian(stepsData);
|
||||
|
||||
if (jiedianResponse.code !== 200) {
|
||||
ElMessage.error('创建步骤失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取返回的ids,实际返回格式中msg字段包含ids字符串
|
||||
if (jiedianResponse.code === 200 && jiedianResponse.msg) {
|
||||
nodeIds = jiedianResponse.msg;
|
||||
} else {
|
||||
ElMessage.warning('未获取到有效的步骤ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// 新增模式:调用添加接口(删除请求参数中的id,避免后端报错)
|
||||
const { id, ...addData } = requestData;
|
||||
// 添加nodeIds字段
|
||||
addData.nodeIds = nodeIds;
|
||||
response = await addshiyan(addData);
|
||||
}
|
||||
|
||||
@ -1072,11 +957,6 @@ const resetForm = () => {
|
||||
envRequirements: '', // 环境要求为空
|
||||
manager: '', // 负责人为空
|
||||
participants: [], // 参与人员为空数组
|
||||
steps: [
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' }
|
||||
], // 步骤内容为空
|
||||
equipments: [
|
||||
{ name: '服务器(型号:XYZ-9000)', selected: false },
|
||||
{ name: '网络测试仪(型号:NT-5000)', selected: false },
|
||||
@ -1152,45 +1032,6 @@ const handleEditRecord = async (row) => {
|
||||
const recordDetail = detailResponse.data.rows?.[0] || detailResponse.data;
|
||||
// 兼容两种数据结构:可能在rows数组中,也可能直接在data中
|
||||
|
||||
// 3. 处理步骤数据:优先从nodes数组中提取
|
||||
const steps = [];
|
||||
// 如果有nodes数组,优先从nodes中提取步骤数据
|
||||
if (recordDetail.nodes && Array.isArray(recordDetail.nodes)) {
|
||||
// 复制nodes数组并按code升序排序
|
||||
const sortedNodes = [...recordDetail.nodes].sort((a, b) => (a.code || 0) - (b.code || 0));
|
||||
// 转换为所需的格式
|
||||
sortedNodes.forEach((node) => {
|
||||
if ((node.name && node.name.trim()) || (node.intendedPurpose && node.intendedPurpose.trim())) {
|
||||
steps.push({
|
||||
name: node.name || '',
|
||||
intendedPurpose: node.intendedPurpose || '',
|
||||
intendedTime: node.intendedTime || ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 如果nodes中没有数据,回退到从testStep字符串解析
|
||||
else if (recordDetail.testStep) {
|
||||
// 拆分字符串
|
||||
const stepItems = recordDetail.testStep.split(',');
|
||||
stepItems.forEach((stepText) => {
|
||||
// 移除序号前缀(如"1. "),只保留内容
|
||||
const content = stepText.replace(/^\d+\.\s*/, '').trim();
|
||||
if (content) {
|
||||
// 对于旧格式数据,我们将内容放入intendedPurpose字段
|
||||
steps.push({
|
||||
name: `步骤${steps.length + 1}`,
|
||||
intendedPurpose: content,
|
||||
intendedTime: ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 确保至少有3个步骤(如果解析后为空)
|
||||
while (steps.length < 3) {
|
||||
steps.push({ name: '', intendedPurpose: '', intendedTime: '' });
|
||||
}
|
||||
|
||||
// 4. 处理testDevice:将逗号分隔的字符串转换为设备数组
|
||||
const equipments = [];
|
||||
if (recordDetail.testDevice) {
|
||||
@ -1236,7 +1077,6 @@ const handleEditRecord = async (row) => {
|
||||
envRequirements: recordDetail.envRequirements || recordDetail.testSetting || '',
|
||||
manager: recordDetail.manager || recordDetail.personCharge || '',
|
||||
participants: participants, // 从personIds解析的数组
|
||||
steps: steps, // 解析后的步骤数组
|
||||
equipments: equipments, // 解析并合并后的设备数组
|
||||
riskMitigation: recordDetail.riskMitigation || recordDetail.testSolutions || ''
|
||||
};
|
||||
@ -1375,53 +1215,23 @@ const formatDate = (dateString) => {
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
// 根据步骤状态获取对应的样式类
|
||||
const getStatusClass = (status) => {
|
||||
// 处理可能的数字输入
|
||||
const statusStr = status?.toString() || '';
|
||||
const statusClassMap = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-running',
|
||||
'3': 'status-completed',
|
||||
'4': 'status-delayed',
|
||||
'5': 'status-failed'
|
||||
};
|
||||
return statusClassMap[statusStr] || 'status-unknown';
|
||||
};
|
||||
|
||||
// 格式化日期时间(用于步骤条)
|
||||
const formatDateTime = (dateTime) => {
|
||||
if (!dateTime) return '-';
|
||||
try {
|
||||
const date = new Date(dateTime);
|
||||
// 日期时间格式化函数
|
||||
const formatDateTime = (dateString) => {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
} catch (error) {
|
||||
return dateTime;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取步骤状态文本
|
||||
const getStepStatusText = (status) => {
|
||||
const statusStr = status?.toString() || '';
|
||||
const statusMap = {
|
||||
'1': '待执行',
|
||||
'2': '执行中',
|
||||
'3': '已完成',
|
||||
'4': '已延期',
|
||||
'5': '失败'
|
||||
};
|
||||
return statusMap[statusStr] || '未知状态';
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url('./css/step-bars.css');
|
||||
@import url('./css/detail-dialog.css');
|
||||
|
||||
.operation-inspection {
|
||||
padding: 20px;
|
||||
background-color: #f9fbfd;
|
||||
@ -2095,53 +1905,6 @@ const getStepStatusText = (status) => {
|
||||
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 试验步骤样式 */
|
||||
.steps-container {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background-color: #165dff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-input:focus {
|
||||
border-color: #165dff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.add-step-btn {
|
||||
color: #165dff;
|
||||
margin-top: 12px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 设备列表样式 */
|
||||
.equipment-list {
|
||||
border: 1px solid #e4e7ed;
|
||||
@ -2200,7 +1963,7 @@ const getStepStatusText = (status) => {
|
||||
border-color: #0d47a1;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
/* 响应式设计 - 保留必要的覆盖样式 */
|
||||
@media (max-width: 768px) {
|
||||
.custom-experiment-dialog {
|
||||
width: 90% !important;
|
||||
@ -2220,5 +1983,13 @@ const getStepStatusText = (status) => {
|
||||
.new-equipment-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="operation-inspection">
|
||||
<!-- 顶部导航选项卡 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection3">试验管理</div>
|
||||
@ -10,16 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 头部操作按钮 -->
|
||||
<div class="header-container">
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" class="export-btn">筛选</el-button>
|
||||
<el-button type="primary" class="create-btn">导入数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
<!-- 选项卡和按钮组合 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
@ -56,7 +47,7 @@
|
||||
class="date-picker"
|
||||
></el-date-picker>
|
||||
|
||||
<el-button type="primary" class="search-btn"> 搜索 </el-button>
|
||||
<el-button icon="Search" type="primary" class="search-btn"> 搜索 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -71,11 +62,20 @@
|
||||
<div class="stat-grid">
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">本月完成试验</p>
|
||||
<p class="stat-value">{{ statData.completed }}<span class="stat-change up">较上月 ↑2.4%</span></p>
|
||||
<p class="stat-value">
|
||||
{{ statData.completed
|
||||
}}<span class="stat-change" :class="statData.completedGrowth >= 0 ? 'up' : 'down'">
|
||||
较上月 {{ statData.completedGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.completedGrowth) }}%
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">试验通过率</p>
|
||||
<p class="stat-value">{{ statData.passRate }}%<span class="stat-change up">较上月 ↑5.6%</span></p>
|
||||
<p class="stat-value">
|
||||
{{ statData.passRate }}%<span class="stat-change" :class="statData.passRateGrowth >= 0 ? 'up' : 'down'">
|
||||
较上月 {{ statData.passRateGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.passRateGrowth) }}%
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">待分析记录</p>
|
||||
@ -83,7 +83,12 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">平均试验时长</p>
|
||||
<p class="stat-value">{{ statData.avgDuration }}<span class="stat-change down">较上月 ↓9.4分钟</span></p>
|
||||
<p class="stat-value">
|
||||
{{ statData.avgDuration
|
||||
}}<span class="stat-change" :class="statData.avgDurationGrowth >= 0 ? 'down' : 'up'">
|
||||
较上月 {{ statData.avgDurationGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.avgDurationGrowth) }}分钟
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -149,7 +154,7 @@
|
||||
</div>
|
||||
|
||||
<div class="record-actions">
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
<button class="operate-btn view-btn" @click="handleViewDetail(record)">查看详情</button>
|
||||
<button class="operate-btn report-btn">生成报告</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -192,7 +197,7 @@
|
||||
<button class="operate-btn execute-btn" v-if="scope.row.status === 'drafted'">执行</button>
|
||||
<button class="operate-btn pause-btn" v-if="scope.row.status === 'in-progress'">暂停</button>
|
||||
<button class="operate-btn resume-btn" v-if="scope.row.status === 'paused'">恢复</button>
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
<button class="operate-btn view-btn" @click="handleViewDetail(scope.row)">查看详情</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -220,7 +225,7 @@
|
||||
<div class="operation-buttons">
|
||||
<button class="operate-btn accept-btn" v-if="scope.row.status === 'pending'">接受</button>
|
||||
<button class="operate-btn complete-btn" v-if="scope.row.status === 'accepted'">完成</button>
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
<button class="operate-btn view-btn" @click="handleViewDetail(scope.row)">查看详情</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -241,6 +246,106 @@
|
||||
@size-change="handleSizeChange"
|
||||
></el-pagination>
|
||||
</div>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog v-model="detailDialogVisible" title="任务详情" width="800px" :close-on-click-modal="false" center>
|
||||
<div v-if="detailData" class="task-detail-container">
|
||||
<div class="detail-card">
|
||||
<h3 class="card-title">基本信息</h3>
|
||||
<div class="info-row">
|
||||
<span class="info-label">任务名称</span>
|
||||
<span class="info-value">{{ detailData.taskName }}</span>
|
||||
<span class="info-label">任务状态</span>
|
||||
<span class="info-value" :class="getStatusClass(detailData.status)">
|
||||
{{ getStatusText(detailData.status) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">测试对象</span>
|
||||
<span class="info-value">{{ detailData.testObject }}</span>
|
||||
<span class="info-label">完成进度</span>
|
||||
<span class="info-value">{{ detailData.progress }}%</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">开始时间</span>
|
||||
<span class="info-value">{{ detailData.beginTime }}</span>
|
||||
<span class="info-label">结束时间</span>
|
||||
<span class="info-value">{{ detailData.endTime }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">时间信息</span>
|
||||
<span class="info-value">{{ detailData.timeInfo ? detailData.timeInfo.replace(/,/g, '—') : '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<h3 class="card-title">执行人信息</h3>
|
||||
<div v-if="detailData.personInfo" class="info-row">
|
||||
<span class="info-label">执行人姓名</span>
|
||||
<span class="info-value">{{ detailData.personInfo.userName }}</span>
|
||||
<span class="info-label">联系电话</span>
|
||||
<span class="info-value">{{ detailData.personInfo.phonenumber }}</span>
|
||||
</div>
|
||||
<div v-if="detailData.personInfo" class="info-row">
|
||||
<span class="info-label">性别</span>
|
||||
<span class="info-value">{{ detailData.personInfo.sex === '1' ? '男' : '女' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<h3 class="card-title">关联计划</h3>
|
||||
<div v-if="detailData.testPlan" class="info-row">
|
||||
<span class="info-label">计划名称</span>
|
||||
<span class="info-value">{{ detailData.testPlan.planName }}</span>
|
||||
<span class="info-label">计划编号</span>
|
||||
<span class="info-value">{{ detailData.testPlan.planCode }}</span>
|
||||
</div>
|
||||
<div v-if="detailData.testPlan" class="info-row">
|
||||
<span class="info-label">计划时间</span>
|
||||
<span class="info-value">{{ detailData.testPlan.beginTime }} — {{ detailData.testPlan.endTime }}</span>
|
||||
</div>
|
||||
<div v-if="detailData.testPlan && detailData.testPlan.testDevice" class="info-row">
|
||||
<span class="info-label">测试设备</span>
|
||||
<span class="info-value">{{ detailData.testPlan.testDevice }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="detailData.nodes && detailData.nodes.length > 0" class="detail-card">
|
||||
<h3 class="card-title">执行步骤</h3>
|
||||
<div class="steps-container">
|
||||
<div v-for="(node, index) in detailData.nodes" :key="node.id || index" class="step-item">
|
||||
<div class="step-number">{{ node.code || index + 1 }}</div>
|
||||
<div class="step-info">
|
||||
<div class="step-name">{{ node.name || '未命名步骤' }}</div>
|
||||
<div class="step-purpose">{{ node.intendedPurpose || '无说明' }}</div>
|
||||
<div class="step-time">计划时间:{{ formatDateTime(node.intendedTime) }}</div>
|
||||
<div v-if="node.finishTime" class="step-finish-time">完成时间:{{ formatDateTime(node.finishTime) }}</div>
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ node.status === '2' ? '未完成' : '已完成' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="detailData.testFinal || detailData.failReason" class="detail-card">
|
||||
<h3 class="card-title">执行结果</h3>
|
||||
<div v-if="detailData.testFinal" class="info-row">
|
||||
<span class="info-label">测试结果</span>
|
||||
<span class="info-value">{{ detailData.testFinal }}</span>
|
||||
</div>
|
||||
<div v-if="detailData.failReason" class="info-row">
|
||||
<span class="info-label">失败原因</span>
|
||||
<span class="info-value fail-reason">{{ detailData.failReason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading-details">
|
||||
<p>加载中...</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -248,11 +353,11 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import router from '@/router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { syrenwulist, syrenwujilu, syrenwuDetail } from '@/api/zhinengxunjian/shiyan/renwu';
|
||||
|
||||
// 1. 选项卡状态管理
|
||||
const activeTab = ref('record'); // 默认显示"试验记录"
|
||||
const showFilter = ref(false);
|
||||
|
||||
// 2. 筛选条件
|
||||
const filterStatus = ref('all');
|
||||
@ -277,7 +382,11 @@ const statData = ref({
|
||||
completed: 0,
|
||||
passRate: 0,
|
||||
pendingAnalysis: 0,
|
||||
avgDuration: '0分钟'
|
||||
avgDuration: '0分钟',
|
||||
// 新增:增长率相关数据
|
||||
completedGrowth: 0,
|
||||
passRateGrowth: 0,
|
||||
avgDurationGrowth: 0
|
||||
});
|
||||
|
||||
// 6. 分页相关
|
||||
@ -310,7 +419,8 @@ const getStatisticsData = async () => {
|
||||
const response = await syrenwujilu({ projectId: 1 });
|
||||
console.log('syrenwujilu API响应:', response);
|
||||
|
||||
if (response && response.data) {
|
||||
// 确保接口返回成功状态码(code=200)且有数据
|
||||
if (response && response.code === 200 && response.data) {
|
||||
// 映射API返回的数据到statData
|
||||
const apiData = response.data;
|
||||
|
||||
@ -319,11 +429,21 @@ const getStatisticsData = async () => {
|
||||
statData.value.pendingAnalysis = parseInt(apiData.failCount) || 0;
|
||||
|
||||
// 格式化平均试验时长
|
||||
const avgTime = parseInt(apiData.averageTestTime) || 0;
|
||||
const avgTime = parseFloat(apiData.averageTestTime) || 0;
|
||||
statData.value.avgDuration = `${avgTime}分钟`;
|
||||
|
||||
// 处理增长率数据
|
||||
statData.value.completedGrowth = parseInt(apiData.finishCountAdd) || 0;
|
||||
statData.value.passRateGrowth = parseFloat(apiData.passValueAdd) || 0;
|
||||
statData.value.avgDurationGrowth = parseFloat(apiData.averageTestTimeAdd) || 0;
|
||||
} else {
|
||||
console.warn('获取统计数据失败或返回格式不正确:', response);
|
||||
// 可以在这里添加错误提示或默认值处理
|
||||
ElMessage.warning('获取统计数据失败,请稍后重试');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
console.error('获取统计数据异常:', error);
|
||||
ElMessage.error('获取统计数据异常,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
@ -339,15 +459,13 @@ const formatDate = (dateString) => {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
// 11. 辅助方法:格式化时长(假设单位为分钟)
|
||||
const formatDuration = (minutes) => {
|
||||
if (minutes < 60) {
|
||||
return `${minutes}分钟`;
|
||||
} else {
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
return `${hours}小时${mins}分钟`;
|
||||
}
|
||||
// 11. 辅助方法:格式化日期时间
|
||||
const formatDateTime = (dateTimeString) => {
|
||||
if (!dateTimeString) return '未知时间';
|
||||
const date = new Date(dateTimeString);
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(
|
||||
date.getHours()
|
||||
).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
// 12. 辅助方法:获取节点状态类名
|
||||
@ -392,15 +510,19 @@ const getProgressColor = (status) => {
|
||||
// 15. 辅助方法:获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'1': '进行中',
|
||||
'completed': '通过',
|
||||
'1': '待执行',
|
||||
'4': '执行中',
|
||||
'2': '已延期',
|
||||
'5': '已完成',
|
||||
'3': '失败',
|
||||
'completed': '已完成',
|
||||
'failed': '失败',
|
||||
'paused': '已暂停',
|
||||
'drafted': '草稿',
|
||||
'in-progress': '进行中',
|
||||
'normal': '正常',
|
||||
'attention': '需关注',
|
||||
'problem': '有问题'
|
||||
'paused': '已延期',
|
||||
'drafted': '待执行',
|
||||
'in-progress': '执行中',
|
||||
'normal': '已完成',
|
||||
'attention': '执行中',
|
||||
'problem': '失败'
|
||||
};
|
||||
return statusMap[status] || '未知状态';
|
||||
};
|
||||
@ -408,10 +530,15 @@ const getStatusText = (status) => {
|
||||
// 16. 辅助方法:获取任务状态文本
|
||||
const getTaskStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'pending': '待接受',
|
||||
'accepted': '进行中',
|
||||
'pending': '待执行',
|
||||
'accepted': '执行中',
|
||||
'completed': '已完成',
|
||||
'rejected': '已拒绝'
|
||||
'rejected': '已拒绝',
|
||||
'1': '待执行',
|
||||
'4': '执行中',
|
||||
'2': '已延期',
|
||||
'5': '已完成',
|
||||
'3': '失败'
|
||||
};
|
||||
return statusMap[status] || '未知状态';
|
||||
};
|
||||
@ -419,18 +546,22 @@ const getTaskStatusText = (status) => {
|
||||
// 17. 辅助方法:获取状态类名
|
||||
const getStatusClass = (status) => {
|
||||
const classMap = {
|
||||
'1': 'status-in-progress',
|
||||
'completed': 'status-passed',
|
||||
'1': 'tag-pending', // 待执行
|
||||
'4': 'tag-executing', // 执行中
|
||||
'2': 'tag-delayed', // 已延期
|
||||
'5': 'tag-completed', // 已完成
|
||||
'3': 'status-failed', // 失败
|
||||
'completed': 'tag-completed',
|
||||
'failed': 'status-failed',
|
||||
'paused': 'status-paused',
|
||||
'pending': 'status-pending',
|
||||
'accepted': 'status-accepted',
|
||||
'rejected': 'status-rejected',
|
||||
'normal': 'status-normal',
|
||||
'attention': 'status-attention',
|
||||
'problem': 'status-problem'
|
||||
'paused': 'tag-delayed',
|
||||
'pending': 'tag-pending',
|
||||
'accepted': 'tag-pending',
|
||||
'rejected': 'status-failed',
|
||||
'normal': 'tag-completed',
|
||||
'attention': 'tag-executing',
|
||||
'problem': 'status-failed'
|
||||
};
|
||||
return classMap[status] || 'status-pending';
|
||||
return classMap[status] || 'tag-pending';
|
||||
};
|
||||
|
||||
// 18. 分页事件处理
|
||||
@ -480,10 +611,46 @@ const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/shiyanjilu');
|
||||
};
|
||||
|
||||
// 20. 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
getStatisticsData();
|
||||
getTestRecords();
|
||||
// 20. 详情弹窗相关
|
||||
const detailDialogVisible = ref(false);
|
||||
const detailData = ref(null);
|
||||
const isDetailLoading = ref(false);
|
||||
|
||||
// 22. 处理查看详情
|
||||
const handleViewDetail = async (row) => {
|
||||
try {
|
||||
if (!row || !row.id) {
|
||||
ElMessage.error('记录ID不存在,无法查看详情');
|
||||
return;
|
||||
}
|
||||
|
||||
isDetailLoading.value = true;
|
||||
const response = await syrenwuDetail(row.id);
|
||||
|
||||
if (response && response.code === 200) {
|
||||
detailData.value = response.data;
|
||||
detailDialogVisible.value = true;
|
||||
} else {
|
||||
ElMessage.error(response?.msg || '获取任务详情失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查看详情失败:', error);
|
||||
ElMessage.error('获取任务详情失败');
|
||||
} finally {
|
||||
isDetailLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 24. 组件挂载时获取数据 - 确保页面进入时立即调用接口
|
||||
onMounted(async () => {
|
||||
// 直接并立即调用数据接口,确保页面加载时能获取到最新数据
|
||||
try {
|
||||
// 并行调用两个数据接口以提高加载速度
|
||||
await Promise.all([getStatisticsData(), getTestRecords()]);
|
||||
} catch (error) {
|
||||
console.error('数据加载失败:', error);
|
||||
ElMessage.error('数据加载失败,请刷新页面重试');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -497,35 +664,40 @@ onMounted(() => {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 2. 顶部导航选项卡 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.nav-tab:hover:not(.active) {
|
||||
background-color: #f3f4f6;
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #165dff;
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 3. 选项卡样式 */
|
||||
@ -570,24 +742,10 @@ onMounted(() => {
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.el-select,
|
||||
.date-picker {
|
||||
width: 160px;
|
||||
}
|
||||
.search-btn,
|
||||
.export-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
.filter-btn {
|
||||
background-color: #f3f4f6;
|
||||
color: #6b7280;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* 6. 表格容器 */
|
||||
.table-container {
|
||||
@ -623,57 +781,58 @@ onMounted(() => {
|
||||
/* 8. 状态标签样式 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.status-drafted {
|
||||
background-color: #e0efff;
|
||||
color: #165dff;
|
||||
|
||||
/* 与试验任务页面相同的标签样式 */
|
||||
.tag-pending {
|
||||
background-color: #e6f7ff;
|
||||
color: #1677ff;
|
||||
border-color: #91d5ff;
|
||||
}
|
||||
|
||||
.tag-delayed {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border-color: #ffccc7;
|
||||
}
|
||||
|
||||
.tag-executing {
|
||||
background-color: #fffbe6;
|
||||
color: #fa8c16;
|
||||
border-color: #ffe58f;
|
||||
}
|
||||
|
||||
.tag-completed {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border-color: #b7eb8f;
|
||||
}
|
||||
|
||||
/* 保留原有的部分样式以确保兼容性 */
|
||||
.status-in-progress {
|
||||
background-color: #e0f2fe;
|
||||
color: #0284c7;
|
||||
background-color: #fffbe6;
|
||||
color: #fa8c16;
|
||||
border-color: #ffe58f;
|
||||
}
|
||||
.status-completed {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.status-paused {
|
||||
background-color: #f2f3f5;
|
||||
color: #86909c;
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border-color: #b7eb8f;
|
||||
}
|
||||
.status-pending {
|
||||
background-color: #f9fafb;
|
||||
color: #6b7280;
|
||||
}
|
||||
.status-accepted {
|
||||
background-color: #eff6ff;
|
||||
color: #2563eb;
|
||||
}
|
||||
.status-rejected {
|
||||
background-color: #fee2e2;
|
||||
color: #dc2626;
|
||||
}
|
||||
.status-normal {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.status-attention {
|
||||
background-color: #fff7e0;
|
||||
color: #ff7d00;
|
||||
}
|
||||
.status-problem {
|
||||
background-color: #fff2f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
.status-passed {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
background-color: #e6f7ff;
|
||||
color: #1677ff;
|
||||
border-color: #91d5ff;
|
||||
}
|
||||
.status-failed {
|
||||
background-color: #fee2e2;
|
||||
color: #dc2626;
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border-color: #ffccc7;
|
||||
}
|
||||
|
||||
/* 9. 操作按钮样式 */
|
||||
@ -691,30 +850,6 @@ onMounted(() => {
|
||||
background: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.edit-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
.edit-btn:hover {
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
.execute-btn {
|
||||
color: #00b42a;
|
||||
}
|
||||
.execute-btn:hover {
|
||||
background-color: #e6ffed;
|
||||
}
|
||||
.pause-btn {
|
||||
color: #ff7d00;
|
||||
}
|
||||
.pause-btn:hover {
|
||||
background-color: #fff7e0;
|
||||
}
|
||||
.resume-btn {
|
||||
color: #722ed1;
|
||||
}
|
||||
.resume-btn:hover {
|
||||
background-color: #f3e8ff;
|
||||
}
|
||||
.view-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
@ -911,12 +1046,6 @@ onMounted(() => {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection3">试验管理</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -29,7 +29,7 @@
|
||||
<el-option label="待执行" value="1"></el-option>
|
||||
<el-option label="执行中" value="4"></el-option>
|
||||
<el-option label="已延期" value="2"></el-option>
|
||||
<!-- 接口“暂停”对应页面“已延期” -->
|
||||
|
||||
<el-option label="已完成" value="5"></el-option>
|
||||
<el-option label="失败" value="3"></el-option>
|
||||
</el-select>
|
||||
@ -137,7 +137,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加新任务弹窗 -->
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="700px" :before-close="handleCancelCreateTask">
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="750px" :before-close="handleCancelCreateTask">
|
||||
<el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="80px">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input v-model="createTaskForm.taskName" placeholder="输入任务名称" />
|
||||
@ -338,7 +338,7 @@
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ getStepStatusText(node.status) }}
|
||||
{{ node.status === '2' ? '未完成' : '已完成' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -377,7 +377,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import router from '@/router';
|
||||
// 引入已定义的接口函数
|
||||
import { syrenwulist, syrenwuDetail, addsyrenwu, updatesyrenwu } from '@/api/zhinengxunjian/shiyan/renwu';
|
||||
@ -386,7 +386,6 @@ import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian/index';
|
||||
import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
|
||||
// 引入Element Plus组件(提示/空状态/骨架屏/弹窗)
|
||||
import { ElMessage, ElEmpty, ElSkeleton, ElForm, ElMessageBox } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
|
||||
/**
|
||||
* 根据任务ID获取完整的任务详情数据
|
||||
@ -428,7 +427,6 @@ const groupNodesByModule = (nodes) => {
|
||||
if (!nodes || !Array.isArray(nodes)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 这里简单地将所有节点放在一个默认模块下,实际应用中可以根据节点数据的module字段进行分组
|
||||
const defaultGroup = {
|
||||
module: '测试步骤',
|
||||
@ -856,9 +854,18 @@ const handleAction = async (task) => {
|
||||
id: task.id
|
||||
};
|
||||
|
||||
// 声明resultType变量,提升作用域
|
||||
let resultType = null;
|
||||
|
||||
// 3. 根据任务状态只修改状态相关的字段
|
||||
if (task.status === '4') {
|
||||
// 执行中 → 完成:使用弹窗确认结果
|
||||
try {
|
||||
// 保持原有结构
|
||||
} catch (error) {
|
||||
console.error('捕获到异常:', error);
|
||||
}
|
||||
|
||||
try {
|
||||
const confirmResult = await ElMessageBox.confirm('请选择试验结果', '完成试验', {
|
||||
confirmButtonText: '正常',
|
||||
@ -870,12 +877,14 @@ const handleAction = async (task) => {
|
||||
updateParams.status = '5';
|
||||
updateParams.progress = 100;
|
||||
updateParams.testFinal = '正常';
|
||||
resultType = 'normal'; // 现在在外部作用域中定义
|
||||
} catch (error) {
|
||||
if (error === 'cancel') {
|
||||
// 用户点击取消(异常)
|
||||
updateParams.status = '5';
|
||||
updateParams.progress = 100;
|
||||
updateParams.testFinal = '异常';
|
||||
resultType = 'abnormal'; // 现在在外部作用域中定义
|
||||
} else {
|
||||
// 关闭弹窗,不执行操作
|
||||
return;
|
||||
@ -887,6 +896,8 @@ const handleAction = async (task) => {
|
||||
case '1': // 待执行 → 开始执行(状态改为4)
|
||||
updateParams.status = '4';
|
||||
updateParams.progress = 10; // 初始进度10%
|
||||
// 设置开始时间为当前时间
|
||||
updateParams.planBeginTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
break;
|
||||
case '2': // 已延期 → 重新安排(状态改为1,重置时间)
|
||||
updateParams.status = '1';
|
||||
@ -904,6 +915,30 @@ const handleAction = async (task) => {
|
||||
const response = await updatesyrenwu(updateParams);
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(`任务${task.actionText}成功`);
|
||||
|
||||
// 只有在接口调用成功后才设置时间
|
||||
if (task.status === '4') {
|
||||
// 获取最新的任务详情,确保包含所有字段
|
||||
const latestTaskDetails = await getTaskDetails(task.id);
|
||||
if (latestTaskDetails) {
|
||||
// 创建包含所有字段的新参数对象
|
||||
const timeUpdateParams = {
|
||||
...latestTaskDetails,
|
||||
id: task.id
|
||||
};
|
||||
|
||||
// 根据结果类型设置相应的时间(现在resultType已在作用域内)
|
||||
if (resultType === 'normal') {
|
||||
timeUpdateParams.planFinishTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
} else if (resultType === 'abnormal') {
|
||||
timeUpdateParams.failTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
}
|
||||
|
||||
// 再次调用接口更新时间
|
||||
await updatesyrenwu(timeUpdateParams);
|
||||
}
|
||||
}
|
||||
|
||||
getTaskList(); // 刷新任务列表
|
||||
} else {
|
||||
ElMessage.error(`任务${task.actionText}失败:` + response.msg);
|
||||
@ -1010,10 +1045,10 @@ const handleSaveTask = async () => {
|
||||
status: '1', // 初始状态:待执行(必需)
|
||||
testPlanId: createTaskForm.value.relatedPlan, // 关联计划ID(必需)
|
||||
testSetting: '', // 测试设置
|
||||
planBeginTime: createTaskForm.value.timeRange[0], // 计划开始时间
|
||||
planBeginTime: '', // 计划开始时间(新增时为空)
|
||||
progress: 0, // 初始进度0%
|
||||
failReason: '',
|
||||
failTime: now.toISOString(),
|
||||
failTime: '', // 失败时间(新增时为空)
|
||||
failPhase: 0,
|
||||
faileAnalyze: '',
|
||||
faileTips: '',
|
||||
@ -1021,8 +1056,8 @@ const handleSaveTask = async () => {
|
||||
testFinal: '',
|
||||
finalInfo: '',
|
||||
pauseFor: '',
|
||||
pauseTime: now.toISOString(),
|
||||
planFinishTime: createTaskForm.value.timeRange[1], // 计划完成时间
|
||||
pauseTime: '', // 暂停时间(新增时为空)
|
||||
planFinishTime: '', // 计划完成时间(新增时为空)
|
||||
nodeIds: nodeIds // 步骤节点ID数组
|
||||
};
|
||||
|
||||
@ -1114,6 +1149,18 @@ onMounted(() => {
|
||||
const pagedTasks = computed(() => {
|
||||
return tasks.value;
|
||||
});
|
||||
// 获取任务状态对应的CSS类
|
||||
const getTaskStatusClass = (status) => {
|
||||
const statusStr = status?.toString() || '';
|
||||
const statusMap = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-delayed',
|
||||
'3': 'status-failed',
|
||||
'4': 'status-running',
|
||||
'5': 'status-completed'
|
||||
};
|
||||
return statusMap[statusStr] || 'status-pending';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -1234,27 +1281,6 @@ const pagedTasks = computed(() => {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
/* 自定义步骤条样式覆盖 */
|
||||
.custom-steps .el-step__description {
|
||||
white-space: pre-wrap;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.module-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.module-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
/* 卡片悬停效果 */
|
||||
.task-card:hover {
|
||||
transform: translateY(-3px);
|
||||
@ -1393,22 +1419,6 @@ const pagedTasks = computed(() => {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.start-btn,
|
||||
.report-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.reschedule-btn {
|
||||
background-color: #ff7d00;
|
||||
border-color: #ff7d00;
|
||||
}
|
||||
|
||||
.complete-btn {
|
||||
background-color: #00b42a;
|
||||
border-color: #00b42a;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
@ -1536,10 +1546,6 @@ const pagedTasks = computed(() => {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.status-running {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="operation-inspection">
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -8,7 +8,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="header-container">
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" class="export-btn">筛选</el-button>
|
||||
@ -127,14 +127,14 @@
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">完成率</span>
|
||||
<span class="text-gray-600">巡检完成率</span>
|
||||
<span class="font-medium text-gray-800">{{ completionRate }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: completionRate + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- <div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">解决率</span>
|
||||
<span class="font-medium text-gray-800">{{ resolutionRate }}%</span>
|
||||
@ -142,10 +142,10 @@
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div class="bg-red-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: resolutionRate + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">及时率</span>
|
||||
<span class="text-gray-600">解决效率</span>
|
||||
<span class="font-medium text-gray-800">{{ timelinessRate }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
@ -161,65 +161,8 @@
|
||||
<!-- 发现问题种类 -->
|
||||
<div class="py-4">
|
||||
<h3 class="section-title">发现问题种类</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">温度异常率</span>
|
||||
<span class="text-gray-500">{{ problemTypes.temperature }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
|
||||
:style="{ width: problemTypes.temperature + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">内存使用率</span>
|
||||
<span class="text-gray-500">{{ problemTypes.memory }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
|
||||
:style="{ width: problemTypes.memory + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">CPU负载</span>
|
||||
<span class="text-gray-500">{{ problemTypes.cpu }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: problemTypes.cpu + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">响应时间</span>
|
||||
<span class="text-gray-500">{{ problemTypes.responseTime }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
|
||||
:style="{ width: problemTypes.responseTime + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1">
|
||||
<span class="text-gray-600">磁盘空间状态</span>
|
||||
<span class="text-gray-500">{{ problemTypes.diskSpace }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
|
||||
:style="{ width: problemTypes.diskSpace + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 柱状图容器 -->
|
||||
<div id="problemTypesChart" class="bar-chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -388,16 +331,17 @@ const avgCompletionTime = ref('45分钟');
|
||||
|
||||
// 问题类型数据
|
||||
const problemTypes = ref({
|
||||
temperature: 85, // 温度异常率
|
||||
memory: 62, // 内存使用率
|
||||
cpu: 45, // CPU负载
|
||||
responseTime: 30, // 响应时间
|
||||
diskSpace: 15 // 磁盘空间状态
|
||||
temperature: 0, // 温度异常数量
|
||||
memory: 0, // 内存使用率问题数量
|
||||
cpu: 0, // CPU负载问题数量
|
||||
responseTime: 0, // 响应时间问题数量
|
||||
diskSpace: 0 // 磁盘空间问题数量
|
||||
});
|
||||
|
||||
// ECharts 饼图相关
|
||||
// ECharts 图表相关
|
||||
const pieChartRef = ref(null);
|
||||
let pieChart = null;
|
||||
let barChart = null;
|
||||
|
||||
// 计算平均完成度
|
||||
const averageRate = computed(() => (completionRate.value + resolutionRate.value + timelinessRate.value) / 3);
|
||||
@ -426,7 +370,7 @@ const initPieChart = () => {
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '进度指标',
|
||||
name: '指标对比',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
@ -442,20 +386,15 @@ const initPieChart = () => {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
formatter: function (params) {
|
||||
// 鼠标悬停时显示当前指标的百分比
|
||||
return params.value + '%';
|
||||
}
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: completionRate.value, name: '完成率', itemStyle: { color: '#5470c6' } },
|
||||
{ value: resolutionRate.value, name: '解决率', itemStyle: { color: '#f56c6c' } },
|
||||
{ value: timelinessRate.value, name: '及时率', itemStyle: { color: '#67c23a' } }
|
||||
{ value: completionRate.value, name: '巡检完成率', itemStyle: { color: '#409eff' } },
|
||||
{ value: timelinessRate.value, name: '解决效率', itemStyle: { color: '#67c23a' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -506,11 +445,7 @@ const fetchDashboardData = async () => {
|
||||
// 构建查询参数
|
||||
const queryParams = {
|
||||
projectId: 1,
|
||||
type: type,
|
||||
status: filterStatus.value !== 'all' ? filterStatus.value : undefined,
|
||||
inspectionType: filterType.value !== 'all' ? filterType.value : undefined,
|
||||
startTime: dateRange.value.length > 0 ? dateRange.value[0] : undefined,
|
||||
endTime: dateRange.value.length > 0 ? dateRange.value[1] : undefined
|
||||
type: type
|
||||
};
|
||||
|
||||
// 调用接口获取数据
|
||||
@ -526,22 +461,26 @@ const fetchDashboardData = async () => {
|
||||
solvedProblems.value = data.solvedProblemCount || 0;
|
||||
avgCompletionTime.value = data.averageCompletionTime ? `${data.averageCompletionTime}分钟` : '0分钟';
|
||||
|
||||
// 计算完成率、解决率、及时率
|
||||
completionRate.value = data.finishInspectionCount && data.finishInspectionCount > 0 ? Math.round(Math.random() * 30 + 60) : 0;
|
||||
resolutionRate.value = data.solvedProblemCount && data.problemCount ? Math.round((data.solvedProblemCount / data.problemCount) * 100) : 0;
|
||||
timelinessRate.value = data.finishInspectionCount && data.finishInspectionCount > 0 ? Math.round(Math.random() * 30 + 50) : 0;
|
||||
// 使用接口返回的xjwcl(巡检完成率)和jjxl(解决效率)
|
||||
completionRate.value = data.xjwcl ? parseFloat(data.xjwcl) : 0;
|
||||
timelinessRate.value = data.jjxl ? parseFloat(data.jjxl) : 0;
|
||||
|
||||
// 更新问题类型数据
|
||||
// 由于接口不再返回解决率,将其设置为0或保持原值
|
||||
resolutionRate.value = 0;
|
||||
|
||||
// 更新问题类型数据 - 直接使用接口返回的数值,不再计算为百分比
|
||||
problemTypes.value = {
|
||||
temperature: data.sbyxzt ? Math.min(100, Math.round(data.sbyxzt * 5)) : 0, // 设备运行状态映射为温度异常
|
||||
memory: data.ncsyl ? Math.min(100, data.ncsyl * 10) : 0, // 内存使用率
|
||||
cpu: Math.round(Math.random() * 50 + 20), // CPU负载(模拟数据)
|
||||
responseTime: data.xysj ? Math.min(100, data.xysj * 5) : 0, // 响应时间
|
||||
diskSpace: data.cpsyl ? Math.min(100, data.cpsyl * 8) : 0 // 磁盘使用率
|
||||
temperature: data.sbyxzt || 0, // 设备运行状态类型问题数量
|
||||
memory: data.ncsyl || 0, // 内存使用率类型问题数量
|
||||
cpu: data.fwzt || 0, // 服务状态类型问题数量
|
||||
responseTime: data.xysj || 0, // 响应时间类型问题数量
|
||||
diskSpace: data.cpsyl || 0 // 磁盘使用率类型问题数量
|
||||
};
|
||||
|
||||
// 更新饼图
|
||||
initPieChart();
|
||||
// 更新柱状图
|
||||
initBarChart();
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取数据失败');
|
||||
}
|
||||
@ -551,17 +490,115 @@ const fetchDashboardData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
// 页面加载时直接获取数据
|
||||
onMounted(() => {
|
||||
fetchDashboardData();
|
||||
});
|
||||
|
||||
// 初始化柱状图
|
||||
const initBarChart = () => {
|
||||
const chartDom = document.getElementById('problemTypesChart');
|
||||
if (!chartDom) return;
|
||||
|
||||
// 销毁旧实例
|
||||
if (barChart) {
|
||||
barChart.dispose();
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
barChart = echarts.init(chartDom);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: function (params) {
|
||||
return params[0].name + ': ' + params[0].value + '个';
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '10%',
|
||||
top: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '问题数量',
|
||||
axisLabel: {
|
||||
formatter: '{value}个'
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['温度异常', '内存使用率', 'CPU负载', '响应时间', '磁盘空间'],
|
||||
axisLabel: {
|
||||
interval: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '问题数量',
|
||||
type: 'bar',
|
||||
barWidth: '40%',
|
||||
data: [
|
||||
problemTypes.value.temperature,
|
||||
problemTypes.value.memory,
|
||||
problemTypes.value.cpu,
|
||||
problemTypes.value.responseTime,
|
||||
problemTypes.value.diskSpace
|
||||
],
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||
{ offset: 0, color: '#5470c6' },
|
||||
{ offset: 1, color: '#91cc75' }
|
||||
]),
|
||||
borderRadius: [0, 4, 4, 0]
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
formatter: '{c}个'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
barChart.setOption(option);
|
||||
|
||||
// 响应式处理
|
||||
const handleResize = () => {
|
||||
if (barChart) {
|
||||
barChart.resize();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 组件卸载时移除事件监听
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
};
|
||||
|
||||
// 组件卸载时销毁图表实例
|
||||
onUnmounted(() => {
|
||||
if (pieChart) {
|
||||
pieChart.dispose();
|
||||
pieChart = null;
|
||||
}
|
||||
if (barChart) {
|
||||
barChart.dispose();
|
||||
barChart = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 导航方法
|
||||
@ -802,6 +839,17 @@ const handleInspectionManagement3 = () => {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 柱状图容器 */
|
||||
.bar-chart-container {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
margin: 0 auto;
|
||||
background-color: #fafafa;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 区域标题 */
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<div class="navigation-tabs">
|
||||
<!-- <div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
@ -133,7 +133,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加新任务弹窗 -->
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="700px" :before-close="handleCancelCreateTask">
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="750px" :before-close="handleCancelCreateTask">
|
||||
<el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="80px">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input v-model="createTaskForm.taskName" placeholder="输入任务名称" />
|
||||
@ -202,7 +202,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="问题类型" prop="problemType">
|
||||
<!-- <el-form-item label="问题类型" prop="problemType">
|
||||
<el-select v-model="createTaskForm.problemType" placeholder="选择问题类型">
|
||||
<el-option label="磁盘使用率" value="1" />
|
||||
<el-option label="内存使用率" value="2" />
|
||||
@ -210,7 +210,7 @@
|
||||
<el-option label="响应时间" value="4" />
|
||||
<el-option label="设备运行状态" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
|
||||
<!-- 步骤条 -->
|
||||
<el-form-item label="执行步骤" class="form-item" style="width: 100%">
|
||||
@ -387,7 +387,7 @@
|
||||
<div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
|
||||
</div>
|
||||
<div class="step-status" :class="getStatusClass(node.status)">
|
||||
{{ getStepStatusText(node.status) }}
|
||||
{{ node.status === '2' ? '未完成' : '已完成' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -543,7 +543,10 @@ const getTaskList = async () => {
|
||||
const params = {
|
||||
pageSize: pageSize.value,
|
||||
pageNum: currentPage.value,
|
||||
personId: 1
|
||||
personId: 1,
|
||||
taskType: taskStatus.value || undefined, // 任务状态
|
||||
planType: planType.value || undefined, // 计划类型
|
||||
personName: executor.value || undefined // 执行人
|
||||
};
|
||||
|
||||
const response = await xjrenwulist(params);
|
||||
@ -674,7 +677,7 @@ const createTaskForm = ref({
|
||||
relatedPlan: '',
|
||||
executor: '',
|
||||
taskType: '1', // 默认待执行
|
||||
problemType: '',
|
||||
// problemType: '',
|
||||
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }] // 任务步骤数组
|
||||
});
|
||||
|
||||
@ -806,6 +809,7 @@ const handleSaveTask = async () => {
|
||||
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString(),
|
||||
startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
||||
params: {
|
||||
property1: 'string',
|
||||
property2: 'string'
|
||||
@ -820,7 +824,7 @@ const handleSaveTask = async () => {
|
||||
personId: createTaskForm.value.executor !== 'all' ? createTaskForm.value.executor : 0,
|
||||
taskProgress: 0,
|
||||
taskType: createTaskForm.value.taskType,
|
||||
problemType: createTaskForm.value.problemType,
|
||||
// problemType: createTaskForm.value.problemType,
|
||||
nodeIds: nodeIds // 添加步骤ID字符串,与工单列表页面保持一致
|
||||
};
|
||||
|
||||
@ -841,7 +845,7 @@ const handleSaveTask = async () => {
|
||||
relatedPlan: '',
|
||||
executor: '',
|
||||
taskType: '1',
|
||||
problemType: '',
|
||||
// problemType: '',
|
||||
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
|
||||
};
|
||||
// 重新获取任务列表
|
||||
@ -935,7 +939,7 @@ const handleCancelCreateTask = () => {
|
||||
relatedPlan: '',
|
||||
executor: '',
|
||||
taskType: '1',
|
||||
problemType: '',
|
||||
// problemType: '',
|
||||
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user