This commit is contained in:
dhr
2025-09-15 20:02:32 +08:00
parent 91162f1dc4
commit cdf5dfcdae

View File

@ -55,7 +55,7 @@
></el-date-picker> ></el-date-picker>
</div> </div>
<div class="filter-actions"> <div class="filter-actions">
<el-button type="primary" class="search-btn">搜索</el-button> <el-button type="primary" class="search-btn" @click="fetchDashboardData">搜索</el-button>
</div> </div>
</div> </div>
@ -97,19 +97,19 @@
<div class="grid grid-cols-2 md:grid-cols-4 gap-4"> <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="stat-card"> <div class="stat-card">
<p class="stat-label">本月完成巡检</p> <p class="stat-label">本月完成巡检</p>
<p class="stat-value">42</p> <p class="stat-value">{{ completedInspections }}</p>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<p class="stat-label">发现问题数</p> <p class="stat-label">发现问题数</p>
<p class="stat-value">7</p> <p class="stat-value">{{ totalProblems }}</p>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<p class="stat-label">已解决问题</p> <p class="stat-label">已解决问题</p>
<p class="stat-value">5</p> <p class="stat-value">{{ solvedProblems }}</p>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<p class="stat-label">平均完成时间</p> <p class="stat-label">平均完成时间</p>
<p class="stat-value">45分钟</p> <p class="stat-value">{{ avgCompletionTime }}</p>
</div> </div>
</div> </div>
@ -117,54 +117,87 @@
<!-- 图表区域 --> <!-- 图表区域 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 py-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-6 py-4">
<!-- 饼图 - 本月巡检完成情况 --> <!-- 饼图 - 进度条对比可视化 -->
<div class="md:col-span-1"> <div class="md:col-span-1">
<p class="chart-title">本月完成巡检</p> <p class="chart-title">进度指标对比</p>
<div class="relative w-32 h-32 mx-auto"> <div class="relative w-40 h-40 mx-auto">
<!-- 饼图使用SVG绘制 - 显示本月巡检完成情况 --> <!-- 饼图使用SVG绘制 - 显示三个进度条的占比对比 -->
<svg class="w-full h-full" viewBox="0 0 100 100"> <svg class="w-full h-full" viewBox="0 0 100 100">
<!-- 背景圆环 --> <!-- 背景圆环 -->
<circle cx="50" cy="50" r="45" fill="none" stroke="#f3f4f6" stroke-width="10" /> <circle cx="50" cy="50" r="40" fill="none" stroke="#f3f4f6" stroke-width="12" />
<!-- 已完成部分 (72%) -->
<!-- 完成率部分 -->
<circle <circle
cx="50" cx="50"
cy="50" cy="50"
r="45" r="40"
fill="none"
stroke="#f5222d"
stroke-width="12"
stroke-linecap="round"
:stroke-dasharray="completionLength"
:stroke-dashoffset="completionOffset"
transform="rotate(-90 50 50)"
style="transition: all 1.5s ease-in-out"
/>
<!-- 解决率部分 -->
<circle
cx="50"
cy="50"
r="40"
fill="none" fill="none"
stroke="#10b981" stroke="#10b981"
stroke-width="10" stroke-width="12"
stroke-dasharray="282.74" stroke-linecap="round"
stroke-dashoffset="113.1" :stroke-dasharray="resolutionLength"
transform="rotate(-90 50 50)" :stroke-dashoffset="resolutionOffset"
transform="rotate(-90 + completionRotation 50 50)"
style="transition: all 1.5s ease-in-out"
/> />
<!-- 未完成部分 (28%) -->
<!-- 及时率部分 -->
<circle <circle
cx="50" cx="50"
cy="50" cy="50"
r="45" r="40"
fill="none" fill="none"
stroke="#f97316" stroke="#6b7280"
stroke-width="10" stroke-width="12"
stroke-dasharray="113.1" stroke-linecap="round"
stroke-dashoffset="0" :stroke-dasharray="timelinessLength"
transform="rotate(-90 50 50)" :stroke-dashoffset="timelinessOffset"
transform="rotate(-90 + totalRotation 50 50)"
style="transition: all 1.5s ease-in-out"
/> />
</svg> </svg>
<!-- 饼图中心显示 -->
<!-- 饼图中心显示 - 总完成度 -->
<div class="absolute inset-0 flex flex-col items-center justify-center"> <div class="absolute inset-0 flex flex-col items-center justify-center">
<p class="text-sm text-gray-500">已解决问题</p> <div class="w-24 h-24 rounded-full bg-white flex items-center justify-center shadow-lg">
<p class="text-lg font-bold text-gray-800">72%</p> <div class="text-center">
<p class="text-xs font-medium text-gray-500">平均完成度</p>
<p class="text-xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-purple-500">
{{ averageRate.toFixed(1) }}%
</p>
</div> </div>
</div> </div>
</div>
</div>
<!-- 饼图图例 --> <!-- 饼图图例 -->
<div class="mt-3 flex justify-center space-x-4"> <div class="mt-6 flex flex-col items-center space-y-3">
<div class="flex items-center"> <div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-green-500 mr-1"></div> <div class="w-4 h-4 rounded-full bg-red-500 mr-2 shadow-sm"></div>
<span class="text-xs text-gray-600">已解决</span> <span class="text-sm text-gray-700">完成率 {{ completionRate }}%</span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-orange-500 mr-1"></div> <div class="w-4 h-4 rounded-full bg-green-500 mr-2 shadow-sm"></div>
<span class="text-xs text-gray-600">解决</span> <span class="text-sm text-gray-700">解决 {{ resolutionRate }}%</span>
</div>
<div class="flex items-center">
<div class="w-4 h-4 rounded-full bg-gray-500 mr-2 shadow-sm"></div>
<span class="text-sm text-gray-700">及时率 {{ timelinessRate }}%</span>
</div> </div>
</div> </div>
</div> </div>
@ -175,28 +208,28 @@
<div> <div>
<div class="flex justify-between text-sm mb-1"> <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">68%</span> <span class="font-medium text-gray-800">{{ completionRate }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-red-500 h-2 rounded-full" style="width: 68%"></div> <div class="bg-red-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: completionRate + '%' }"></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <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">72%</span> <span class="font-medium text-gray-800">{{ resolutionRate }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-green-500 h-2 rounded-full" style="width: 72%"></div> <div class="bg-green-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: resolutionRate + '%' }"></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <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">60%</span> <span class="font-medium text-gray-800">{{ timelinessRate }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-gray-500 h-2 rounded-full" style="width: 60%"></div> <div class="bg-gray-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: timelinessRate + '%' }"></div>
</div> </div>
</div> </div>
</div> </div>
@ -212,46 +245,58 @@
<div> <div>
<div class="flex justify-between text-sm mb-1"> <div class="flex justify-between text-sm mb-1">
<span class="text-gray-600">温度异常率</span> <span class="text-gray-600">温度异常率</span>
<span class="text-gray-500">85%</span> <span class="text-gray-500">{{ problemTypes.temperature }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-blue-500 h-2 rounded-full" style="width: 85%"></div> <div
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
:style="{ width: problemTypes.temperature + '%' }"
></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <div class="flex justify-between text-sm mb-1">
<span class="text-gray-600">内存使用率</span> <span class="text-gray-600">内存使用率</span>
<span class="text-gray-500">62%</span> <span class="text-gray-500">{{ problemTypes.memory }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-blue-500 h-2 rounded-full" style="width: 62%"></div> <div
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
:style="{ width: problemTypes.memory + '%' }"
></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <div class="flex justify-between text-sm mb-1">
<span class="text-gray-600">CPU负载</span> <span class="text-gray-600">CPU负载</span>
<span class="text-gray-500">45%</span> <span class="text-gray-500">{{ problemTypes.cpu }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-blue-500 h-2 rounded-full" style="width: 45%"></div> <div class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out" :style="{ width: problemTypes.cpu + '%' }"></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <div class="flex justify-between text-sm mb-1">
<span class="text-gray-600">响应时间</span> <span class="text-gray-600">响应时间</span>
<span class="text-gray-500">30%</span> <span class="text-gray-500">{{ problemTypes.responseTime }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-blue-500 h-2 rounded-full" style="width: 30%"></div> <div
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
:style="{ width: problemTypes.responseTime + '%' }"
></div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex justify-between text-sm mb-1"> <div class="flex justify-between text-sm mb-1">
<span class="text-gray-600">磁盘空间状态</span> <span class="text-gray-600">磁盘空间状态</span>
<span class="text-gray-500">15%</span> <span class="text-gray-500">{{ problemTypes.diskSpace }}%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2"> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
<div class="bg-blue-500 h-2 rounded-full" style="width: 15%"></div> <div
class="bg-blue-500 h-2 rounded-full transition-all duration-1500 ease-out"
:style="{ width: problemTypes.diskSpace + '%' }"
></div>
</div> </div>
</div> </div>
</div> </div>
@ -271,7 +316,7 @@
<div class="card-body flex-1 overflow-y-auto scrollbar-thin"> <div class="card-body flex-1 overflow-y-auto scrollbar-thin">
<div class="inspection-results space-y-4"> <div class="inspection-results space-y-4">
<!-- 结果1正常 --> <!-- 结果1正常 -->
<div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm"> <div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-4"> <div class="flex justify-between items-start mb-4">
<h3 class="text-lg font-medium text-gray-800">数据库性能巡检</h3> <h3 class="text-lg font-medium text-gray-800">数据库性能巡检</h3>
<span class="status-tag status-normal px-3 py-1 text-xs">正常</span> <span class="status-tag status-normal px-3 py-1 text-xs">正常</span>
@ -308,7 +353,7 @@
</div> </div>
<!-- 结果2需关注 --> <!-- 结果2需关注 -->
<div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm"> <div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-4"> <div class="flex justify-between items-start mb-4">
<h3 class="text-lg font-medium text-gray-800">生产服务器日常巡检</h3> <h3 class="text-lg font-medium text-gray-800">生产服务器日常巡检</h3>
<span class="status-tag status-attention px-3 py-1 text-xs">需关注</span> <span class="status-tag status-attention px-3 py-1 text-xs">需关注</span>
@ -352,7 +397,7 @@
</div> </div>
<!-- 结果3有问题 --> <!-- 结果3有问题 -->
<div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm"> <div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-4"> <div class="flex justify-between items-start mb-4">
<h3 class="text-lg font-medium text-gray-800">网络设备安全巡检</h3> <h3 class="text-lg font-medium text-gray-800">网络设备安全巡检</h3>
<span class="status-tag status-problem px-3 py-1 text-xs">有问题</span> <span class="status-tag status-problem px-3 py-1 text-xs">有问题</span>
@ -396,7 +441,7 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref, onMounted, computed } from 'vue';
import router from '@/router'; import router from '@/router';
import TitleComponent from '@/views/demo/components/TitleComponent.vue'; import TitleComponent from '@/views/demo/components/TitleComponent.vue';
@ -408,13 +453,141 @@ const dateRange = ref([]);
// 时间范围选择 // 时间范围选择
const timeRange = ref('month'); // 默认选中"月" const timeRange = ref('month'); // 默认选中"月"
// 进度指标数据
const completionRate = ref(68); // 完成率
const resolutionRate = ref(72); // 解决率
const timelinessRate = ref(60); // 及时率
// 统计数据
const completedInspections = ref(42);
const totalProblems = ref(7);
const solvedProblems = ref(5);
const avgCompletionTime = ref('45分钟');
// 问题类型数据
const problemTypes = ref({
temperature: 85, // 温度异常率
memory: 62, // 内存使用率
cpu: 45, // CPU负载
responseTime: 30, // 响应时间
diskSpace: 15 // 磁盘空间状态
});
// 计算饼图相关参数
const totalCircumference = 2 * Math.PI * 40; // 圆环总周长r=40
// 各进度条在饼图中的长度
const completionLength = computed(() => (completionRate.value / 100) * totalCircumference);
const resolutionLength = computed(() => (resolutionRate.value / 100) * totalCircumference);
const timelinessLength = computed(() => (timelinessRate.value / 100) * totalCircumference);
// 各进度条在饼图中的偏移量
const completionOffset = ref(0);
const resolutionOffset = ref(0);
const timelinessOffset = ref(0);
// 各进度条的旋转角度(用于连续显示)
const completionRotation = computed(() => (completionRate.value / 100) * 360);
const totalRotation = computed(() => completionRotation.value + (resolutionRate.value / 100) * 360);
// 计算平均完成度
const averageRate = computed(() => (completionRate.value + resolutionRate.value + timelinessRate.value) / 3);
// 时间范围切换函数 // 时间范围切换函数
const handleTimeRangeChange = (range) => { const handleTimeRangeChange = (range) => {
timeRange.value = range; timeRange.value = range;
// 在实际应用中,这里应该根据选择的时间范围重新获取数据 fetchDashboardData(); // 切换时间范围重新获取数据
console.log(`切换到${range}视图`);
}; };
// 获取仪表盘数据(模拟)
const fetchDashboardData = () => {
// 模拟加载状态
completionRate.value = 0;
resolutionRate.value = 0;
timelinessRate.value = 0;
// 模拟API请求延迟
setTimeout(() => {
// 根据时间范围返回不同数据
let mockData;
if (timeRange.value === 'month') {
mockData = {
completionRate: 68,
resolutionRate: 72,
timelinessRate: 60,
completedInspections: 42,
totalProblems: 7,
solvedProblems: 5,
avgCompletionTime: '45分钟',
problemTypes: {
temperature: 85,
memory: 62,
cpu: 45,
responseTime: 30,
diskSpace: 15
}
};
} else if (timeRange.value === 'week') {
mockData = {
completionRate: 75,
resolutionRate: 80,
timelinessRate: 65,
completedInspections: 12,
totalProblems: 2,
solvedProblems: 2,
avgCompletionTime: '35分钟',
problemTypes: {
temperature: 70,
memory: 55,
cpu: 40,
responseTime: 25,
diskSpace: 10
}
};
} else {
// day
mockData = {
completionRate: 90,
resolutionRate: 100,
timelinessRate: 95,
completedInspections: 2,
totalProblems: 0,
solvedProblems: 0,
avgCompletionTime: '25分钟',
problemTypes: {
temperature: 30,
memory: 45,
cpu: 25,
responseTime: 10,
diskSpace: 5
}
};
}
// 应用筛选条件(这里仅做简单演示)
if (filterStatus.value === 'problem') {
mockData.totalProblems = Math.round(mockData.totalProblems * 1.5);
mockData.solvedProblems = Math.round(mockData.solvedProblems * 0.7);
mockData.resolutionRate = Math.round(mockData.resolutionRate * 0.8);
}
// 更新数据
completionRate.value = mockData.completionRate;
resolutionRate.value = mockData.resolutionRate;
timelinessRate.value = mockData.timelinessRate;
completedInspections.value = mockData.completedInspections;
totalProblems.value = mockData.totalProblems;
solvedProblems.value = mockData.solvedProblems;
avgCompletionTime.value = mockData.avgCompletionTime;
problemTypes.value = mockData.problemTypes;
}, 800); // 模拟网络延迟
};
// 页面加载时获取数据
onMounted(() => {
fetchDashboardData();
});
// 导航方法 // 导航方法
const handleInspection1 = () => { const handleInspection1 = () => {
router.push('/rili/rili'); router.push('/rili/rili');
@ -456,11 +629,12 @@ const handleInspectionManagement3 = () => {
min-height: 100vh; min-height: 100vh;
} }
/* 头部容器 - 替换了固定gap的flex布局 */ /* 头部容器 */
.header-container { .header-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px;
} }
.header-actions { .header-actions {
@ -477,6 +651,12 @@ const handleInspectionManagement3 = () => {
border-radius: 4px; border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
padding: 2px; padding: 2px;
overflow-x: auto;
scrollbar-width: none;
}
.navigation-tabs::-webkit-scrollbar {
display: none;
} }
.nav-tab { .nav-tab {
@ -487,8 +667,7 @@ const handleInspectionManagement3 = () => {
font-size: 14px; font-size: 14px;
color: #606266; color: #606266;
border-right: 1px solid #f0f0f0; border-right: 1px solid #f0f0f0;
flex: 1; white-space: nowrap;
text-align: center;
} }
.nav-tab:last-child { .nav-tab:last-child {
@ -603,7 +782,7 @@ const handleInspectionManagement3 = () => {
} }
.card-body { .card-body {
padding: 0 20px; padding: 20px;
flex: 1; /* 内容区域占满剩余空间 */ flex: 1; /* 内容区域占满剩余空间 */
} }
@ -612,6 +791,14 @@ const handleInspectionManagement3 = () => {
background-color: #f5f7fa; background-color: #f5f7fa;
border-radius: 6px; border-radius: 6px;
padding: 16px; padding: 16px;
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
} }
.stat-label { .stat-label {
@ -639,6 +826,7 @@ const handleInspectionManagement3 = () => {
font-size: 14px; font-size: 14px;
color: #606266; color: #606266;
margin: 0 0 8px 0; margin: 0 0 8px 0;
text-align: center;
} }
/* 区域标题 */ /* 区域标题 */
@ -750,4 +938,25 @@ const handleInspectionManagement3 = () => {
justify-content: flex-end; justify-content: flex-end;
} }
} }
@media (max-width: 768px) {
.grid-cols-2 {
grid-template-columns: 1fr;
}
.md\:col-span-1,
.md\:col-span-2,
.md\:col-span-3 {
grid-column: span 1;
}
.md\:grid-cols-4,
.md\:grid-cols-3 {
grid-template-columns: 1fr 1fr;
}
.navigation-tabs {
justify-content: flex-start;
}
}
</style> </style>