|
|
@@ -0,0 +1,373 @@
|
|
|
+<template>
|
|
|
+ <!-- 顶部栏 -->
|
|
|
+ <div
|
|
|
+ style="
|
|
|
+ height: 64px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 16px;
|
|
|
+ justify-content: space-between;
|
|
|
+ background: #fff;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div style="display: flex; align-items: center; gap: 12px">
|
|
|
+ <div style="font-weight: 700; font-size: 18px">统计</div>
|
|
|
+ <div style="color: #888; font-size: 13px">所有项目</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="display: flex; align-items: center; gap: 12px">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="dateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ format="YYYY年MM月DD日"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="padding: 16px">
|
|
|
+ <!-- 统计卡片 -->
|
|
|
+ <el-card shadow="never" style="padding: 18px; background-color: #fff">
|
|
|
+ <el-row :gutter="24" justify="start">
|
|
|
+ <el-col :span="4" v-for="(card, idx) in statsData" :key="idx">
|
|
|
+ <div
|
|
|
+ @click="selectCard(idx)"
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ selectedCardIdx === idx
|
|
|
+ ? 'linear-gradient(135deg, #e6f7ff 0%, #f0f5ff 100%)'
|
|
|
+ : 'linear-gradient(135deg, #fff 0%, #fafafa 100%)',
|
|
|
+ padding: '20px 16px',
|
|
|
+ borderRadius: '8px',
|
|
|
+ boxShadow:
|
|
|
+ selectedCardIdx === idx
|
|
|
+ ? '0 4px 12px rgba(0, 118, 255, 0.15)'
|
|
|
+ : '0 2px 8px rgba(0, 0, 0, 0.08)',
|
|
|
+ border: selectedCardIdx === idx ? '1px solid #0076ff' : '1px solid #f0f0f0',
|
|
|
+ textAlign: 'center',
|
|
|
+ cursor: 'pointer',
|
|
|
+ transition: 'all 0.3s ease'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <div style="font-size: 12px; color: #999; margin-bottom: 8px">{{ card.title }}</div>
|
|
|
+ <div style="font-size: 32px; font-weight: 600; color: #262626">{{ card.value }}</div>
|
|
|
+ <div style="font-size: 12px; color: #bbb; margin-top: 8px">{{ card.subtitle }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 执行统计图表 -->
|
|
|
+ <el-card style="margin-top: 16px; padding: 18px">
|
|
|
+ <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px">
|
|
|
+ <div style="font-weight: 600">{{ currentChartTitle }}</div>
|
|
|
+ <div style="display: flex; gap: 8px">
|
|
|
+ <div style="display: flex; align-items: center; gap: 4px">
|
|
|
+ <div style="width: 12px; height: 12px; background: #13c2c2; border-radius: 2px"></div>
|
|
|
+ <span style="font-size: 12px; color: #666">Successful</span>
|
|
|
+ </div>
|
|
|
+ <div style="display: flex; align-items: center; gap: 4px">
|
|
|
+ <div style="width: 12px; height: 12px; background: #ff6b6b; border-radius: 2px"></div>
|
|
|
+ <span style="font-size: 12px; color: #666">Failed</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <ExecutionChart :data="currentChartData" />
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 执行统计表格 -->
|
|
|
+ <el-card style="margin-top: 16px; padding: 18px">
|
|
|
+ <div style="font-weight: 600; margin-bottom: 16px; font-size: 14px">
|
|
|
+ {{ currentTableTitle }}
|
|
|
+ </div>
|
|
|
+ <el-table :data="filteredTableData" style="width: 100%" stripe border>
|
|
|
+ <el-table-column type="selection" width="50" />
|
|
|
+ <el-table-column prop="name" label="姓名" width="120" />
|
|
|
+ <el-table-column prop="totalExecutions" label="生产执行次数↓" width="140" />
|
|
|
+ <el-table-column prop="failedExecutions" label="生产环境执行失败数" width="160" />
|
|
|
+ <el-table-column prop="failureRate" label="故障率" width="100" />
|
|
|
+ <el-table-column prop="timeSaved" label="节省时间" width="100" />
|
|
|
+ <el-table-column prop="avgRuntime" label="运行时间(平均)" width="140" />
|
|
|
+ <el-table-column prop="projectName" label="项目名称" min-width="180" />
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页器 -->
|
|
|
+ <div
|
|
|
+ style="
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ gap: 12px;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ :page-size="pageSize"
|
|
|
+ :current-page="currentPage"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
+ @update:current-page="currentPage = $event"
|
|
|
+ @update:page-size="pageSize = $event"
|
|
|
+ :total="filteredTableData.length"
|
|
|
+ layout="total, prev, pager, next, sizes"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed } from 'vue'
|
|
|
+import ExecutionChart from '@/components/Chart/ExecutionChart.vue'
|
|
|
+
|
|
|
+// 日期范围
|
|
|
+const dateRange = ref<[Date, Date] | null>([new Date('2026-01-20'), new Date('2026-01-27')])
|
|
|
+
|
|
|
+// 当前选中的卡片索引
|
|
|
+const selectedCardIdx = ref(0)
|
|
|
+
|
|
|
+// 统计数据
|
|
|
+const statsData = [
|
|
|
+ { title: '生产执行', value: 12, subtitle: '过去7天' },
|
|
|
+ { title: '生产环境执行失败', value: 3, subtitle: '过去7天' },
|
|
|
+ { title: '故障率', value: '25%', subtitle: '过去7天' },
|
|
|
+ { title: '节省时间', value: '8.2h', subtitle: '过去7天' },
|
|
|
+ { title: '运行时间(平均)', value: '5.1s', subtitle: '过去7天' }
|
|
|
+]
|
|
|
+
|
|
|
+// 不同卡片对应的图表和表格数据
|
|
|
+const cardDataMap = [
|
|
|
+ {
|
|
|
+ // 生产执行
|
|
|
+ chartTitle: '生产执行 - 按时间统计',
|
|
|
+ chartData: {
|
|
|
+ categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
|
|
+ successful: [8, 12, 15, 24, 18, 10, 5],
|
|
|
+ failed: [1, 2, 1, 3, 2, 1, 0]
|
|
|
+ },
|
|
|
+ tableTitle: '生产执行明细',
|
|
|
+ tableData: [
|
|
|
+ {
|
|
|
+ name: '项目1',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.1s',
|
|
|
+ projectName: '数据处理项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目2',
|
|
|
+ totalExecutions: 18,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '5.6%',
|
|
|
+ timeSaved: '4.2h',
|
|
|
+ avgRuntime: '4.8s',
|
|
|
+ projectName: 'AI 分析项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目3',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.3s',
|
|
|
+ projectName: '图像识别项目'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ // 生产环境执行失败
|
|
|
+ chartTitle: '生产环境执行失败 - 按时间统计',
|
|
|
+ chartData: {
|
|
|
+ categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
|
|
+ successful: [2, 3, 1, 2, 1, 0, 0],
|
|
|
+ failed: [1, 2, 1, 3, 2, 1, 0]
|
|
|
+ },
|
|
|
+ tableTitle: '生产环境失败执行明细',
|
|
|
+ tableData: [
|
|
|
+ {
|
|
|
+ name: '项目1',
|
|
|
+ totalExecutions: 1,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '100%',
|
|
|
+ timeSaved: '0h',
|
|
|
+ avgRuntime: '2.1s',
|
|
|
+ projectName: '数据处理项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目2',
|
|
|
+ totalExecutions: 1,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '100%',
|
|
|
+ timeSaved: '0h',
|
|
|
+ avgRuntime: '1.8s',
|
|
|
+ projectName: 'AI 分析项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目3',
|
|
|
+ totalExecutions: 1,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '100%',
|
|
|
+ timeSaved: '0h',
|
|
|
+ avgRuntime: '3.3s',
|
|
|
+ projectName: '图像识别项目'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ // 故障率
|
|
|
+ chartTitle: '故障率 - 按时间统计',
|
|
|
+ chartData: {
|
|
|
+ categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
|
|
+ successful: [5, 8, 12, 18, 15, 8, 4],
|
|
|
+ failed: [1, 2, 1, 3, 2, 1, 0]
|
|
|
+ },
|
|
|
+ tableTitle: '故障率统计明细',
|
|
|
+ tableData: [
|
|
|
+ {
|
|
|
+ name: '项目1',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.1s',
|
|
|
+ projectName: '数据处理项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目2',
|
|
|
+ totalExecutions: 18,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '5.6%',
|
|
|
+ timeSaved: '4.2h',
|
|
|
+ avgRuntime: '4.8s',
|
|
|
+ projectName: 'AI 分析项目'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ // 节省时间
|
|
|
+ chartTitle: '节省时间 - 按时间统计',
|
|
|
+ chartData: {
|
|
|
+ categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
|
|
+ successful: [12, 18, 20, 28, 22, 15, 8],
|
|
|
+ failed: [0, 1, 0, 2, 1, 0, 0]
|
|
|
+ },
|
|
|
+ tableTitle: '节省时间统计明细',
|
|
|
+ tableData: [
|
|
|
+ {
|
|
|
+ name: '项目1',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.1s',
|
|
|
+ projectName: '数据处理项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目3',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.3s',
|
|
|
+ projectName: '图像识别项目'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ // 运行时间(平均)
|
|
|
+ chartTitle: '运行时间(平均) - 按时间统计',
|
|
|
+ chartData: {
|
|
|
+ categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
|
|
+ successful: [6, 10, 14, 22, 18, 9, 4],
|
|
|
+ failed: [1, 1, 0, 2, 1, 1, 0]
|
|
|
+ },
|
|
|
+ tableTitle: '运行时间统计明细',
|
|
|
+ tableData: [
|
|
|
+ {
|
|
|
+ name: '项目2',
|
|
|
+ totalExecutions: 18,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '5.6%',
|
|
|
+ timeSaved: '4.2h',
|
|
|
+ avgRuntime: '4.8s',
|
|
|
+ projectName: 'AI 分析项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目3',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.3s',
|
|
|
+ projectName: '图像识别项目'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '项目1',
|
|
|
+ totalExecutions: 12,
|
|
|
+ failedExecutions: 1,
|
|
|
+ failureRate: '8.3%',
|
|
|
+ timeSaved: '2.5h',
|
|
|
+ avgRuntime: '5.1s',
|
|
|
+ projectName: '数据处理项目'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+]
|
|
|
+
|
|
|
+// 当前选中卡片的图表标题
|
|
|
+const currentChartTitle = computed(() => {
|
|
|
+ return cardDataMap[selectedCardIdx.value]?.chartTitle || '统计图表'
|
|
|
+})
|
|
|
+
|
|
|
+// 当前选中卡片的图表数据
|
|
|
+const currentChartData = computed(() => {
|
|
|
+ return cardDataMap[selectedCardIdx.value]?.chartData || cardDataMap[0].chartData
|
|
|
+})
|
|
|
+
|
|
|
+// 当前选中卡片的表格标题
|
|
|
+const currentTableTitle = computed(() => {
|
|
|
+ return cardDataMap[selectedCardIdx.value]?.tableTitle || '统计明细'
|
|
|
+})
|
|
|
+
|
|
|
+// 当前选中卡片的表格数据
|
|
|
+const filteredTableData = computed(() => {
|
|
|
+ return cardDataMap[selectedCardIdx.value]?.tableData || []
|
|
|
+})
|
|
|
+
|
|
|
+// 点击卡片
|
|
|
+const selectCard = (idx: number) => {
|
|
|
+ selectedCardIdx.value = idx
|
|
|
+ currentPage.value = 1
|
|
|
+}
|
|
|
+
|
|
|
+// 分页
|
|
|
+const pageSize = ref(10)
|
|
|
+const currentPage = ref(1)
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+:deep(.el-date-picker) {
|
|
|
+ width: 280px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-card) {
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-table) {
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-table__header-wrapper) {
|
|
|
+ background-color: #fafafa;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-pagination) {
|
|
|
+ margin-top: 16px;
|
|
|
+}
|
|
|
+</style>
|