Files
maintenance_system/src/views/zhinengxunjian/shiyanjilu.vue
2025-09-22 15:42:13 +08:00

975 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div class="operation-inspection">
<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>
<div class="nav-tab" @click="handleInspection4">报修管理</div>
<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 class="tabs-wrapper">
<div style="display: flex; align-items: center; gap: 10px">
<el-button type="primary" @click="handleInspectionManagement1">实验计划</el-button>
<el-button type="primary" @click="handleInspectionManagement2">实验任务</el-button>
<el-button type="primary" @click="handleInspectionManagement3">实验记录</el-button>
</div>
</div>
<!-- 4. 筛选和操作区域 -->
<div class="filter-and-actions">
<div class="filters">
<el-select v-model="filterStatus" placeholder="巡检状态" clearable>
<el-option label="全部状态" value="all"></el-option>
<el-option label="正常" value="normal"></el-option>
<el-option label="需关注" value="attention"></el-option>
<el-option label="有问题" value="problem"></el-option>
</el-select>
<el-select v-model="filterType" placeholder="巡检类型" clearable>
<el-option label="全部类型" value="all"></el-option>
<el-option label="数据库" value="database"></el-option>
<el-option label="服务器" value="server"></el-option>
<el-option label="网络设备" value="network"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
class="date-picker"
></el-date-picker>
<el-button type="primary" class="search-btn"> 搜索 </el-button>
</div>
</div>
<!-- 5. 主内容区 -->
<div class="content-container">
<!-- 5.3 巡检记录根据图片调整 -->
<div v-if="activeTab === 'record'" class="record-container">
<h2 class="section-title">试验记录与报告</h2>
<p class="section-subtitle">截止至 {{ currentDate }}</p>
<!-- 统计卡片 -->
<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>
</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>
</div>
<div class="stat-card">
<p class="stat-label">待分析记录</p>
<p class="stat-value">{{ statData.pendingAnalysis }}<span class="stat-change warning">需要及时处理</span></p>
</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>
</div>
</div>
<!-- 试验记录列表 -->
<div class="test-records">
<!-- 数据库性能巡检记录 -->
<div class="test-record-card passed">
<div class="record-header">
<h3 class="record-title">数据库性能巡检</h3>
<p class="record-date">
{{ testRecords[0].date }} <span class="record-time">耗时: {{ testRecords[0].duration }}</span>
</p>
<span class="status-tag status-passed">通过</span>
</div>
<!-- 试验进度 -->
<div class="test-progress">
<div class="progress-step active">
<div class="step-number">1</div>
<div class="step-name">准备环境</div>
</div>
<div class="progress-line active"></div>
<div class="progress-step active">
<div class="step-number">2</div>
<div class="step-name">50%负载</div>
</div>
<div class="progress-line active"></div>
<div class="progress-step active">
<div class="step-number">3</div>
<div class="step-name">80%负载</div>
</div>
<div class="progress-line active"></div>
<div class="progress-step active">
<div class="step-number">4</div>
<div class="step-name">100%负载</div>
</div>
</div>
<!-- 试验结果 -->
<div class="test-result">
<h4 class="result-title">试验结果</h4>
<p class="result-content">系统在100%负载下稳定运行1小时CPU平均使用率92%内存使用率88%无崩溃或异常重启现象</p>
<p class="result-details">
平均响应时间: {{ testRecords[0].responseTime }} | 错误率: {{ testRecords[0].errorRate }} | 温度值: {{ testRecords[0].temperature }}
</p>
</div>
<div class="record-actions">
<button class="operate-btn view-btn">查看详情</button>
<button class="operate-btn report-btn">生成报告</button>
</div>
</div>
<!-- 1000用户并发测试记录 -->
<div class="test-record-card failed">
<div class="record-header">
<h3 class="record-title">1000用户并发测试</h3>
<p class="record-date">
{{ testRecords[1].date }} <span class="record-time">耗时: {{ testRecords[1].duration }}</span>
</p>
<span class="status-tag status-failed">失败</span>
</div>
<!-- 试验进度 -->
<div class="test-progress">
<div class="progress-step active">
<div class="step-number">1</div>
<div class="step-name">准备环境</div>
</div>
<div class="progress-line active"></div>
<div class="progress-step active">
<div class="step-number">2</div>
<div class="step-name">300用户</div>
</div>
<div class="progress-line active"></div>
<div class="progress-step active">
<div class="step-number">3</div>
<div class="step-name">500用户</div>
</div>
<div class="progress-line failed"></div>
<div class="progress-step failed">
<div class="step-number">4</div>
<div class="step-name">800用户</div>
</div>
</div>
<!-- 失败原因分析 -->
<div class="test-result failure-analysis">
<h4 class="result-title">失败原因分析</h4>
<p class="result-content">当并发用户数达到780人时数据库连接耗尽新用户无法建立数据库连接导致系统响应超时</p>
<!-- 改进建议 -->
<div class="improvement-suggestion">
<i class="fas fa-lightbulb"></i>
<p>建议: 增加数据库连接池最大连接数优化长连接超时时间增加连接复用机制分析评估</p>
</div>
</div>
<div class="record-actions">
<button class="operate-btn view-btn">查看详情</button>
<button class="operate-btn report-btn">生成报告</button>
</div>
</div>
</div>
</div>
<!-- 5.1 巡检计划表格 -->
<div v-if="activeTab === 'plan'" class="table-container">
<el-table :data="planTableData" border>
<el-table-column prop="name" label="计划名称" width="220">
<template #default="scope">
<div class="plan-name">{{ scope.row.name }}</div>
</template>
</el-table-column>
<el-table-column prop="type" label="巡检类型" width="120"></el-table-column>
<el-table-column prop="cycle" label="巡检周期" width="120"></el-table-column>
<el-table-column prop="dateRange" label="执行时间范围"></el-table-column>
<el-table-column 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 prop="status" label="状态" width="100">
<template #default="scope">
<span :class="['status-tag', `status-${scope.row.status}`]">
{{ getStatusText(scope.row.status) }}
</span>
</template>
</el-table-column>
<el-table-column prop="responsible" label="负责人" width="120"></el-table-column>
<el-table-column label="操作" width="220">
<template #default="scope">
<div class="operation-buttons">
<button class="operate-btn edit-btn" v-if="['drafted', 'paused'].includes(scope.row.status)">编辑</button>
<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>
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- 5.2 巡检任务表格 -->
<div v-if="activeTab === 'task'" class="table-container">
<el-table :data="taskTableData" border>
<el-table-column prop="name" label="任务名称" width="220"></el-table-column>
<el-table-column prop="planName" label="所属计划" width="180"></el-table-column>
<el-table-column prop="type" label="巡检类型" width="120"></el-table-column>
<el-table-column prop="target" label="巡检对象" width="150"></el-table-column>
<el-table-column prop="deadline" label="截止时间" width="160"></el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<span :class="['status-tag', `status-${scope.row.status}`]">
{{ getTaskStatusText(scope.row.status) }}
</span>
</template>
</el-table-column>
<el-table-column prop="executor" label="执行人" width="120"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<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>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 6. 分页 -->
<div class="pagination" v-if="activeTab !== 'record'">
<p class="total-records">显示1到{{ pageSize }}{{ totalRecords }}条记录</p>
<el-pagination
layout="prev, pager, next, jumper, sizes"
:total="totalRecords"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[20, 50, 100]"
@current-change="handlePageChange"
@size-change="handleSizeChange"
></el-pagination>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import router from '@/router';
// 1. 选项卡状态管理
const activeTab = ref('record'); // 默认显示"巡检记录"
const showFilter = ref(false);
// 2. 筛选条件
const filterStatus = ref('all');
const filterType = ref('all');
const dateRange = ref([]);
// 4. 当前日期
const currentDate = computed(() => {
const date = new Date();
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')}`;
});
// 5. 统计数据
const statData = ref({
completed: 12,
passRate: 83,
pendingAnalysis: 3,
avgDuration: '42分钟'
});
// 6. 试验记录数据
const testRecords = ref([
{
date: '2025-06-15',
duration: '1小时45分钟',
responseTime: '1.2s',
errorRate: '0%',
temperature: '72°C'
},
{
date: '2025-06-12',
duration: '2小时10分钟'
}
]);
// 9. 方法:切换顶部导航
const handleInspection1 = () => {
router.push('/rili/rili');
};
const handleInspection2 = () => {
router.push('/rili/InspectionManagement');
};
const handleInspection3 = () => {
router.push('/rili/shiyanguanli');
};
const handleInspection4 = () => {
router.push('/rili/baoxiuguanli');
};
const handleInspection5 = () => {
router.push('/rili/qiangxiuguanli');
};
const handleInspection6 = () => {
router.push('/rili/gongdanliebiao');
};
const handleInspection7 = () => {
router.push('/rili/renyuanzhuangtai');
};
const handleInspectionManagement1 = () => {
router.push('/rili/shiyanguanli');
};
const handleInspectionManagement2 = () => {
router.push('/rili/shiyanrenwu');
};
const handleInspectionManagement3 = () => {
router.push('/rili/shiyanjilu');
};
</script>
<style scoped>
/* 1. 基础容器样式 */
.operation-inspection {
padding: 20px;
background-color: #f9fbfd;
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;
}
.nav-tab {
padding: 12px 24px;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 4px;
font-size: 14px;
color: #606266;
border-right: 1px solid #f0f0f0;
flex: 1;
text-align: center;
}
.nav-tab:last-child {
border-right: none;
}
.nav-tab:hover {
color: #409eff;
background-color: #ecf5ff;
}
.nav-tab.active {
background-color: #409eff;
color: #fff;
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
}
/* 选项卡样式 */
.tabs-wrapper {
background-color: #fff;
padding: 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
/* 筛选栏样式 */
.filter-bar {
background-color: #fff;
padding: 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 16px;
}
.filter-item {
flex-shrink: 0;
}
.filter-bar .el-select,
.filter-bar .el-date-picker {
width: 150px;
height: 36px;
}
.filter-actions {
margin-left: auto;
display: flex;
gap: 10px;
}
/* 5. 筛选和操作区域 */
.filter-and-actions {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
background-color: #fff;
border: 1px solid #e5e7eb;
border-radius: 0 0 4px 4px;
margin-bottom: 16px;
flex-wrap: wrap;
gap: 12px;
}
.filters {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.action-buttons {
display: flex;
gap: 12px;
}
.el-select,
.date-picker {
width: 160px;
}
.search-btn,
.export-btn {
height: 36px;
border-radius: 4px;
}
.filter-btn {
background-color: #f3f4f6;
color: #6b7280;
border-color: #e5e7eb;
}
/* 6. 表格容器 */
.table-container {
background-color: #fff;
border-radius: 4px;
border: 1px solid #e5e7eb;
margin-bottom: 16px;
}
.el-table {
width: 100%;
}
.el-table th {
background-color: #f9fafb;
font-weight: 500;
color: #4b5563;
}
.plan-name {
white-space: pre-line;
}
/* 7. 进度条样式 */
.progress-bar {
height: 8px;
background-color: #f3f4f6;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
transition: width 0.3s ease;
}
/* 8. 状态标签样式 */
.status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.status-drafted {
background-color: #e0efff;
color: #165dff;
}
.status-in-progress {
background-color: #e0f2fe;
color: #0284c7;
}
.status-completed {
background-color: #e6ffed;
color: #00b42a;
}
.status-paused {
background-color: #f2f3f5;
color: #86909c;
}
.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;
}
.status-failed {
background-color: #fee2e2;
color: #dc2626;
}
/* 9. 操作按钮样式 */
.operation-buttons {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.operate-btn {
padding: 2px 8px;
font-size: 12px;
border-radius: 4px;
cursor: pointer;
border: none;
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;
}
.view-btn:hover {
background-color: #e8f3ff;
}
.complete-btn {
color: #00b42a;
}
.complete-btn:hover {
background-color: #e6ffed;
}
.accept-btn {
color: #2563eb;
}
.accept-btn:hover {
background-color: #eff6ff;
}
.report-btn {
color: #ff7d00;
}
.report-btn:hover {
background-color: #fff7e0;
}
/* 10. 分页样式 */
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #e5e7eb;
}
.total-records {
font-size: 14px;
color: #6b7280;
margin: 0;
}
.el-pagination {
--el-pagination-item-active-bg-color: #165dff;
}
/* 11. 记录页面样式 */
.record-container {
background-color: #fff;
border-radius: 4px;
border: 1px solid #e5e7eb;
padding: 20px;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: #1f2329;
margin: 0 0 10px 0;
}
.section-subtitle {
font-size: 14px;
color: #6b7280;
margin: 0 0 20px 0;
text-align: right;
}
/* 头部容器 - 替换了固定gap的flex布局 */
.header-container {
display: flex;
justify-content: flex-end;
align-items: center;
}
.header-actions {
display: flex;
gap: 10px;
}
/* 12. 统计卡片样式 */
.stat-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background-color: #f0f7ff;
border-radius: 8px;
padding: 16px;
border-left: 4px solid #165dff;
}
.stat-label {
font-size: 14px;
color: #6b7280;
margin: 0 0 8px 0;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: #1f2329;
margin: 0;
display: flex;
align-items: baseline;
gap: 8px;
}
.stat-change {
font-size: 12px;
padding: 1px 6px;
border-radius: 4px;
white-space: nowrap;
}
.stat-change.up {
background-color: #e6ffed;
color: #00b42a;
}
.stat-change.down {
background-color: #fff1f0;
color: #f5222d;
}
.stat-change.warning {
background-color: #fff7e0;
color: #ff7d00;
}
/* 13. 试验记录样式 */
.test-records {
display: flex;
flex-direction: column;
gap: 20px;
}
.test-record-card {
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
transition: box-shadow 0.2s;
}
.test-record-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.test-record-card.passed {
border-left: 4px solid #00b42a;
}
.test-record-card.failed {
border-left: 4px solid #dc2626;
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
background-color: #f9fafb;
border-bottom: 1px solid #e5e7eb;
}
.record-title {
font-size: 16px;
font-weight: 500;
color: #1f2329;
margin: 0;
}
.record-date {
font-size: 14px;
color: #6b7280;
margin: 0;
}
.record-time {
font-size: 12px;
color: #9ca3af;
margin-left: 8px;
}
/* 14. 试验进度样式 */
.test-progress {
display: flex;
align-items: center;
padding: 20px;
background-color: #fff;
gap: 12px;
}
.progress-step {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
position: relative;
}
.step-number {
width: 32px;
height: 32px;
border-radius: 50%;
background-color: #e5e7eb;
color: #6b7280;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
margin-bottom: 8px;
}
.step-name {
font-size: 12px;
color: #6b7280;
text-align: center;
}
.progress-line {
flex: 1;
height: 0;
border-top: 2px dashed #e5e7eb;
margin: 10px 0;
}
.progress-step.active .step-number {
background-color: #10b981;
color: white;
}
.progress-step.active .step-name {
color: #10b981;
font-weight: 500;
}
.progress-line.active {
border-top-color: #10b981;
}
.progress-step.failed .step-number {
background-color: red;
color: white;
}
.progress-step.failed .step-name {
color: red;
}
.progress-line.failed {
border-top-color: red;
}
/* 15. 试验结果样式 */
.test-result {
padding: 16px 20px;
border-top: 1px solid #e5e7eb;
background-color: #fff;
}
.result-title {
font-size: 14px;
font-weight: 500;
color: #1f2329;
margin: 0 0 12px 0;
}
.result-content {
font-size: 14px;
color: #4b5563;
line-height: 1.5;
margin: 0 0 12px 0;
}
.result-details {
font-size: 13px;
color: #6b7280;
margin: 0;
}
.failure-analysis {
background-color: #fff5f5;
}
.improvement-suggestion {
margin-top: 12px;
padding: 10px 12px;
background-color: #fff8e6;
border-radius: 4px;
display: flex;
align-items: flex-start;
gap: 8px;
}
.improvement-suggestion i {
color: #ff7d00;
margin-top: 2px;
}
.improvement-suggestion p {
font-size: 13px;
color: #6b46c1;
margin: 0;
line-height: 1.5;
}
/* 16. 记录操作按钮 */
.record-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 16px 20px;
background-color: #f9fafb;
border-top: 1px solid #e5e7eb;
}
/* 17. 响应式适配 */
@media (max-width: 1200px) {
.stat-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.stat-grid {
grid-template-columns: 1fr;
}
.test-progress {
flex-direction: column;
align-items: flex-start;
gap: 16px;
padding: 16px;
}
.progress-step {
flex-direction: row;
width: 100%;
gap: 12px;
}
.step-number {
margin-bottom: 0;
}
.progress-line {
width: 2px;
height: 30px;
align-self: center;
}
.filters {
flex-direction: column;
align-items: stretch;
}
.el-select,
.date-picker {
width: 100%;
}
.action-buttons {
width: 100%;
justify-content: space-between;
}
.navigation-tabs {
flex-wrap: wrap;
}
.nav-tab {
flex: 1 1 auto;
min-width: 100px;
}
.record-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
}
</style>