From 15bc7727b58bf9ca0c8f21702fa893daac232b8d Mon Sep 17 00:00:00 2001
From: hyb <kk_huangyangbo@163.com>
Date: Fri, 30 Jan 2026 07:18:51 +0000
Subject: [PATCH] perf: 接口自动化平台优化加载速度

---
 测试组/Test_platform/Interface_automation/frontend/src/pages/reports/ReportList.vue | 1023 +++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 709 insertions(+), 314 deletions(-)

diff --git "a/\346\265\213\350\257\225\347\273\204/Test_platform/Interface_automation/frontend/src/pages/reports/ReportList.vue" "b/\346\265\213\350\257\225\347\273\204/Test_platform/Interface_automation/frontend/src/pages/reports/ReportList.vue"
index d0a85fb..056270e 100644
--- "a/\346\265\213\350\257\225\347\273\204/Test_platform/Interface_automation/frontend/src/pages/reports/ReportList.vue"
+++ "b/\346\265\213\350\257\225\347\273\204/Test_platform/Interface_automation/frontend/src/pages/reports/ReportList.vue"
@@ -1,53 +1,42 @@
 <template>
     <el-container>
-        <el-header style="padding: 10px 0; height: 50px">
+        <el-header style="padding: 20px 24px; height: auto; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);">
             <div class="report__header">
                 <div class="report__header--item">
                     <el-input
                         clearable
-                        size="small"
                         placeholder="请输入报告名称"
                         v-model="search"
-                        style="width: 300px"
+                        style="width: 320px"
+                        prefix-icon="el-icon-search"
+                        size="medium"
                     >
                     </el-input>
                 </div>
                 <div class="report__header--item">
                     <el-dropdown @command="reportTypeChangeHandle">
-                        <el-button type="primary" size="small">
+                        <el-button type="primary" size="medium" style="background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.3); color: white;">
                             类型
                             <i class="el-icon-arrow-down el-icon--right"></i>
                         </el-button>
                         <el-dropdown-menu slot="dropdown">
-                            <el-dropdown-item command="1"
-                                >调试</el-dropdown-item
-                            >
-                            <el-dropdown-item command="2"
-                                >异步</el-dropdown-item
-                            >
-                            <el-dropdown-item command="3"
-                                >定时</el-dropdown-item
-                            >
+                            <el-dropdown-item command="1">调试</el-dropdown-item>
+                            <el-dropdown-item command="2">异步</el-dropdown-item>
+                            <el-dropdown-item command="3">定时</el-dropdown-item>
                             <el-dropdown-item command="">全部</el-dropdown-item>
                         </el-dropdown-menu>
                     </el-dropdown>
                 </div>
                 <div class="report__header--item">
                     <el-dropdown @command="reportStatusChangeHandle">
-                        <el-button type="primary" size="small">
+                        <el-button type="primary" size="medium" style="background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.3); color: white;">
                             结果
                             <i class="el-icon-arrow-down el-icon--right"></i>
                         </el-button>
                         <el-dropdown-menu slot="dropdown">
-                            <el-dropdown-item command="0"
-                                >失败</el-dropdown-item
-                            >
-                            <el-dropdown-item command="1"
-                                >成功</el-dropdown-item
-                            >
-                            <el-dropdown-item command="0"
-                                >全部</el-dropdown-item
-                            >
+                            <el-dropdown-item command="0">失败</el-dropdown-item>
+                            <el-dropdown-item command="1">成功</el-dropdown-item>
+                            <el-dropdown-item command="">全部</el-dropdown-item>
                         </el-dropdown-menu>
                     </el-dropdown>
                 </div>
@@ -55,11 +44,14 @@
                 <div class="report__header--item">
                     <el-button
                         plain
-                        size="small"
+                        size="medium"
                         icon="el-icon-refresh"
                         @click="resetSearch"
-                        >重置</el-button
+                        type="info"
+                        style="background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.3); color: white;"
                     >
+                        重置
+                    </el-button>
                 </div>
 
                 <div class="report__header--item">
@@ -69,24 +61,30 @@
                         v-if="isSuperuser"
                         type="danger"
                         icon="el-icon-delete"
-                        size="small"
+                        size="medium"
                         @click="delSelectionReports"
-                        >批量删除</el-button
+                        style="background: rgba(245, 108, 108, 0.8); border-color: rgba(245, 108, 108, 0.9);"
                     >
+                        批量删除
+                    </el-button>
                 </div>
 
-                <el-switch
-                    style="margin-left: 10px"
-                    v-model="onlyMe"
-                    active-color="#13ce66"
-                    inactive-color="#ff4949"
-                    active-text="只看自己"
-                ></el-switch>
+                <div class="report__header--item">
+                    <el-switch
+                        v-model="onlyMe"
+                        active-color="#67C23A"
+                        inactive-color="#F56C6C"
+                        active-text="只看自己"
+                        inactive-text="查看全部"
+                        size="medium"
+                        style="color: white;"
+                    ></el-switch>
+                </div>
             </div>
         </el-header>
 
         <el-container>
-            <el-main style="padding: 0; margin-left: 10px; margin-top: 10px">
+            <el-main style="padding: 24px; margin: 0; background: linear-gradient(180deg, #f5f7fa 0%, #e8ecf1 100%);">
                 <el-dialog
                     v-if="dialogTableVisible"
                     :visible.sync="dialogTableVisible"
@@ -94,223 +92,142 @@
                 >
                     <report :summary="summary"></report>
                 </el-dialog>
-                <div class="report__body__table">
-                    <el-table
-                        :data="reportData.results"
-                        highlight-current-row
-                        stripe
-                        height="calc(100%)"
-                        @cell-mouse-enter="cellMouseEnter"
-                        @cell-mouse-leave="cellMouseLeave"
-                        @selection-change="handleSelectionChange"
-                        v-loading="loading"
-                        style="margin-top: -10px"
-                        :row-class-name="tableRowClassName"
+                <div class="report__body__list" v-loading="loading">
+                    <div
+                        v-for="(item, index) in reportData.results"
+                        :key="item.id"
+                        class="report-list-item"
+                        :class="{ 'success-item': item.success, 'failure-item': !item.success }"
+                        @mouseenter="handleCardHover(index, true)"
+                        @mouseleave="handleCardHover(index, false)"
                     >
-                        <el-table-column type="selection" width="55">
-                        </el-table-column>
-
-                        <el-table-column label="报告类型" width="100">
-                            <template slot-scope="scope">
-                                <el-tag color="#2C3E50" style="color: white">{{
-                                    scope.row.type
-                                }}</el-tag>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="报告名称" width="250">
-                            <template slot-scope="scope">
-                                <div
-                                    :title="scope.row.name"
-                                    style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
-                                >
-                                    {{ scope.row.name }}
+                        <div class="item-left">
+                            <div class="item-status">
+                                <div class="status-dot" :class="item.success ? 'success' : 'failure'"></div>
+                            </div>
+                            <div class="item-info">
+                                <div class="item-type-badge">
+                                    <i :class="getTypeIcon(item.type)"></i>
+                                    <span>{{ item.type }}</span>
                                 </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="执行结果" width="100">
-                            <template slot-scope="scope">
-                                <el-button
-                                    :type="
-                                        scope.row.success ? 'success' : 'danger'
-                                    "
-                                    size="mini"
-                                >
-                                    {{ scope.row.success ? "成功" : "失败" }}
-                                </el-button>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="耗时" width="100">
-                            <template slot-scope="scope">
-                                <div class="time-display">
-                                    <el-tag
-                                        :type="scope.row.time.duration < 5 ? 'success' : scope.row.time.duration < 10 ? 'warning' : 'danger'"
-                                        size="small"
-                                        effect="dark"
-                                    >
-                                        {{ scope.row.time.duration.toFixed(3) }}s
-                                    </el-tag>
+                                <div class="item-name" :title="item.name">
+                                    {{ item.name }}
                                 </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="总计接口" width="80">
-                            <template slot-scope="scope">
-                                <div class="stat-badge">
-                                    <el-badge :value="scope.row.stat.testsRun" class="item" type="info"></el-badge>
+                                <div class="item-meta-inline">
+                                    <span class="meta-inline">
+                                        <i class="el-icon-user"></i>
+                                        {{ !item.creator ? "机器人" : item.creator }}
+                                    </span>
+                                    <span class="meta-inline">
+                                        <i class="el-icon-date"></i>
+                                        {{ item.time.start_at | timestampToTime }}
+                                    </span>
+                                    <span class="meta-inline">
+                                        <i class="el-icon-time"></i>
+                                        总用时: {{ formatDuration(item.time.duration) }}
+                                    </span>
                                 </div>
-                            </template>
-                        </el-table-column>
+                            </div>
+                        </div>
 
-                        <el-table-column label="通过" width="80">
-                            <template slot-scope="scope">
-                                <div class="stat-badge success">
-                                    <div style="display: flex; align-items: center; gap: 4px;">
-                                        <el-badge :value="scope.row.stat.successes" class="item"></el-badge>
-                                        <i class="el-icon-success"></i>
-                                    </div>
-                                    <el-progress
-                                        :percentage="scope.row.stat.testsRun > 0 ?
-                                            Math.round((scope.row.stat.successes / scope.row.stat.testsRun) * 100) : 0"
-                                        :stroke-width="8"
-                                        :show-text="false"
-                                        color="#67C23A"
-                                        class="mini-progress"
-                                    />
-                                </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="失败" width="80">
-                            <template slot-scope="scope">
-                                <div class="stat-badge danger">
-                                    <div style="display: flex; align-items: center; gap: 4px;">
-                                        <el-badge :value="scope.row.stat.failures" class="item"></el-badge>
-                                        <i class="el-icon-error"></i>
-                                    </div>
-                                    <el-progress
-                                        :percentage="scope.row.stat.testsRun > 0 ?
-                                            Math.round((scope.row.stat.failures / scope.row.stat.testsRun) * 100) : 0"
-                                        :stroke-width="8"
-                                        :show-text="false"
-                                        color="#F56C6C"
-                                        class="mini-progress"
-                                    />
-                                </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="异常" width="80">
-                            <template slot-scope="scope">
-                                <div class="stat-badge warning">
-                                    <div style="display: flex; align-items: center; gap: 4px;">
-                                        <el-badge :value="scope.row.stat.errors" class="item"></el-badge>
-                                        <i class="el-icon-warning"></i>
-                                    </div>
-                                    <el-progress
-                                        :percentage="scope.row.stat.testsRun > 0 ?
-                                            Math.round((scope.row.stat.errors / scope.row.stat.testsRun) * 100) : 0"
-                                        :stroke-width="8"
-                                        :show-text="false"
-                                        color="#E6A23C"
-                                        class="mini-progress"
-                                    />
-                                </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="执行人" width="100">
-                            <template slot-scope="scope">
-                                <div
-                                    :title="scope.row.creator"
-                                    style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
-                                >
-                                    <svg class="icon" aria-hidden="true">
-                                        <use
-                                            :xlink:href="
-                                                !scope.row.creator
-                                                    ? '#icon-jiqiren'
-                                                    : '#icon-sharpicons_user'
-                                            "
-                                        ></use>
-                                    </svg>
-                                    <span>{{
-                                        !scope.row.creator
-                                            ? "机器人"
-                                            : scope.row.creator
-                                    }}</span>
-                                </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="执行时间" width="180">
-                            <template slot-scope="scope">
-                                <div>
-                                    {{
-                                        scope.row.time.start_at
-                                            | timestampToTime
-                                    }}
-                                </div>
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column label="报告操作">
-                            <template slot-scope="scope">
-                                <el-row v-show="currentRow === scope.row">
+                        <div class="item-stats">
+                            <div class="stat-group">
+                            <div class="stat-item">
+                                <span class="stat-label">总用例</span>
+                                <span class="stat-value">{{ item.stat.case_count }}</span>
+                            </div>
+                            <div class="stat-item">
+                                <span class="stat-label">总接口</span>
+                                <span class="stat-value">{{ item.stat.testsRun }}</span>
+                            </div>
+                            <div class="stat-item success">
+                                <span class="stat-label">通过</span>
+                                <span class="stat-value">{{ item.stat.successes }}</span>
+                            </div>
+                            <div class="stat-item failure">
+                                <span class="stat-label">失败</span>
+                                <span class="stat-value">{{ item.stat.failures }}</span>
+                            </div>
+                            <div class="stat-item error">
+                                <span class="stat-label">异常</span>
+                                <span class="stat-value">{{ item.stat.errors }}</span>
+                            </div>
+                            <div class="stat-item skipped" v-if="item.stat.skipped !== undefined">
+                                <span class="stat-label">跳过</span>
+                                <span class="stat-value">{{ item.stat.skipped || 0 }}</span>
+                            </div>
+                        </div>
+                            <div class="progress-bar-container">
+                                <div class="progress-bar">
                                     <div
-                                        style="display: flex; align-items: center;"
-                                    >
-                                        <el-button
-                                            type="info"
-                                            icon="el-icon-refresh-right"
-                                            circle
-                                            size="mini"
-                                            title="重新运行失败用例"
-                                            v-show="handleShowReRun(scope.row)"
-                                            @click="
-                                                handleRunFailCase(scope.row)
-                                            "
-                                        ></el-button>
-                                        <el-button
-                                            type="success"
-                                            icon="el-icon-view"
-                                            circle
-                                            size="mini"
-                                            @click="
-                                                handleWatchReports(scope.row.id)
-                                            "
-                                        ></el-button>
-                                        <el-button
-                                            type="danger"
-                                            icon="el-icon-delete"
-                                            title="删除"
-                                            circle
-                                            size="mini"
-                                            @click="
-                                                handleDelReports(scope.row.id)
-                                            "
-                                        ></el-button>
-                                    </div>
-                                </el-row>
-                            </template>
-                        </el-table-column>
-                    </el-table>
-                    <div class="pagination-container">
-                        <el-pagination
-                            v-show="reportData.count !== 0"
-                            @size-change="handleSizeChange"
-                            @current-change="handleCurrentChange"
-                            :current-page.sync="currentPage"
-                            :page-sizes="[10, 20, 30, 40]"
-                            :page-size="pageSize"
-                            :pager-count="5"
-                            :total="reportData.count"
-                            layout="total, sizes, prev, pager, next, jumper"
-                            background
-                        ></el-pagination>
+                                        class="progress-fill success"
+                                        :style="{ width: calculatePercentage(item.stat.successes, item.stat.testsRun) + '%' }"
+                                    ></div>
+                                    <div
+                                        class="progress-fill failure"
+                                        :style="{ width: calculatePercentage(item.stat.failures, item.stat.testsRun) + '%' }"
+                                    ></div>
+                                    <div
+                                        class="progress-fill error"
+                                        :style="{ width: calculatePercentage(item.stat.errors, item.stat.testsRun) + '%' }"
+                                    ></div>
+                                    <div
+                                        class="progress-fill skipped"
+                                        v-if="item.stat.skipped !== undefined"
+                                        :style="{ width: calculatePercentage(item.stat.skipped || 0, item.stat.testsRun) + '%' }"
+                                    ></div>
+                                </div>
+                                <div class="success-rate">
+                                    成功率: <span :class="getSuccessRateClass(item)">{{ calculateSuccessRate(item) }}%</span>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="item-actions">
+                            <div class="action-buttons">
+                                <el-button
+                                    type="success"
+                                    icon="el-icon-view"
+                                    size="small"
+                                    @click.stop="handleWatchReports(item.id)"
+                                >
+                                    查看
+                                </el-button>
+                                <el-button
+                                    type="danger"
+                                    icon="el-icon-delete"
+                                    size="small"
+                                    plain
+                                    @click.stop="handleDelReports(item.id)"
+                                >
+                                    删除
+                                </el-button>
+                            </div>
+                        </div>
                     </div>
+
+                    <div v-if="reportData.results.length === 0 && !loading" class="empty-state">
+                        <div class="empty-icon">
+                            <i class="el-icon-document"></i>
+                        </div>
+                        <div class="empty-title">暂无测试报告</div>
+                        <div class="empty-description">还没有执行过测试,快去运行你的第一个测试吧!</div>
+                    </div>
+                </div>
+
+                <div class="pagination-container">
+                    <el-pagination
+                        v-show="reportData.count !== 0"
+                        @size-change="handleSizeChange"
+                        @current-change="handleCurrentChange"
+                        :current-page.sync="currentPage"
+                        :page-sizes="[10, 20, 30, 50]"
+                        :page-size="pageSize"
+                        :pager-count="5"
+                        :total="reportData.count"
+                        layout="total, sizes, prev, pager, next, jumper"
+                        background
+                    ></el-pagination>
                 </div>
             </el-main>
         </el-container>
@@ -329,6 +246,7 @@
             search: "",
             searchDebounce: null,
             selectReports: [],
+            selectedReports: {},
             currentRow: "",
             currentPage: 1,
             pageSize: 10,
@@ -347,21 +265,85 @@
         };
     },
     methods: {
-        cellMouseEnter(row) {
-            this.currentRow = row;
-        },
-        cellMouseLeave() {
-            this.currentRow = "";
+        handleCardHover(index, isHovering) {
+            this.currentRow = isHovering ? index : "";
         },
         handleWatchReports(index) {
             let reportUrl =
                 this.$api.baseUrl + this.$store.state.report_path + index;
             window.open(reportUrl);
         },
-        handleSelectionChange(val) {
-            this.selectReports = val;
-            // 更新是否已经选择Report, 依赖这个属性来判断是否禁用批量删除按钮
+        handleSelectionChange() {
+            this.selectReports = this.reportData.results.filter(item => this.selectedReports[item.id]);
             this.isSelectReport = this.selectReports.length > 0;
+        },
+        getTypeIcon(type) {
+            const iconMap = {
+                '调试': 'el-icon-magic-stick',
+                '异步': 'el-icon-video-play',
+                '定时': 'el-icon-alarm-clock',
+                '部署': 'el-icon-upload'
+            };
+            return iconMap[type] || 'el-icon-document';
+        },
+        calculateSuccessRate(item) {
+            if (!item.stat.testsRun || item.stat.testsRun === 0) return 0;
+            return Math.round((item.stat.successes / item.stat.testsRun) * 100);
+        },
+        calculatePercentage(value, total) {
+            if (!total || total === 0) return 0;
+            return Math.round((value / total) * 100);
+        },
+        formatDuration(seconds) {
+            if (seconds < 1) {
+                return (seconds * 1000).toFixed(0) + 'ms';
+            } else if (seconds < 60) {
+                return seconds.toFixed(2) + 's';
+            } else {
+                const minutes = Math.floor(seconds / 60);
+                const remainingSeconds = (seconds % 60).toFixed(0);
+                return `${minutes}m ${remainingSeconds}s`;
+            }
+        },
+        formatPlatform(platform) {
+            if (!platform) return '未知';
+            if (typeof platform === 'string') {
+                return platform;
+            }
+            if (typeof platform === 'object') {
+                return platform.name || platform.type || JSON.stringify(platform);
+            }
+            return String(platform);
+        },
+        getPlatformName(platform) {
+            if (!platform) return '未知平台';
+            if (typeof platform === 'string') {
+                return platform;
+            }
+            if (typeof platform === 'object') {
+                return platform.name || platform.os_name || platform.platform || '未知';
+            }
+            return String(platform);
+        },
+        getPlatformDetail(platform) {
+            if (!platform) return '';
+            if (typeof platform === 'string') {
+                return '';
+            }
+            if (typeof platform === 'object') {
+                const details = [];
+                if (platform.os_version) details.push(platform.os_version);
+                if (platform.browser) details.push(platform.browser);
+                if (platform.python_version) details.push('Python ' + platform.python_version);
+                return details.length > 0 ? `(${details.join(', ')})` : '';
+            }
+            return '';
+        },
+        getSuccessRateClass(item) {
+            const rate = this.calculateSuccessRate(item);
+            if (rate >= 80) return 'rate-high';
+            if (rate >= 60) return 'rate-medium';
+            return 'rate-low';
         },
         reportTypeChangeHandle(command) {
             this.reportType = command;
@@ -380,6 +362,7 @@
             this.reportStatus = "";
             this.currentPage = 1;
             this.onlyMe = false;
+            this.selectedReports = {};
             this.getReportList();
         },
         handleCurrentChange() {
@@ -397,14 +380,13 @@
                 })
                 .then(resp => {
                     this.reportData = resp;
+                    this.selectedReports = {};
                 });
         },
         handleSizeChange(newSize) {
             this.pageSize = newSize;
-            // 计算新的最大页码
             let maxPage = Math.ceil(this.reportData.count / newSize);
             if (this.currentPage > maxPage) {
-                // 如果当前页码超出了范围,请将其设置为最大页面
                 this.currentPage = maxPage;
             }
             this.$api
@@ -421,6 +403,7 @@
                 })
                 .then(resp => {
                     this.reportData = resp;
+                    this.selectedReports = {};
                 });
         },
         handleRunFailCase(row) {
@@ -474,6 +457,7 @@
                         .delAllReports({ data: this.selectReports })
                         .then(resp => {
                             this.$message.success(resp.msg);
+                            this.selectedReports = {};
                             this.getReportList();
                         });
                 });
@@ -497,6 +481,7 @@
                 .then(resp => {
                     this.reportData = resp;
                     this.loading = false;
+                    this.selectedReports = {};
                 });
         },
         handleShowReRun(row) {
@@ -518,9 +503,6 @@
                 this.currentPage = 1;
                 this.getReportList();
             }, 300);
-        },
-        tableRowClassName({ row }) {
-            return row.success === false ? 'warning-row' : ''
         }
     },
     watch: {
@@ -541,85 +523,498 @@
 .report__header {
     display: flex;
     align-items: center;
+    flex-wrap: wrap;
+    gap: 12px;
+    padding: 0;
 }
 
 .report__header--item {
     display: flex;
-    margin-left: 10px;
+    align-items: center;
+    margin: 0;
 }
 
-.report__body__table {
-    position: fixed;
+.report__body__list {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+    padding: 0;
+    min-height: 400px;
+}
+
+.report-list-item {
+    display: grid;
+    grid-template-columns: 240px 1fr 180px;
+    gap: 14px;
+    align-items: center;
+    background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%);
+    border-radius: 12px;
+    padding: 12px 16px;
+    box-shadow: 
+        0 2px 4px rgba(0, 0, 0, 0.04),
+        0 4px 8px rgba(0, 0, 0, 0.06);
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    cursor: pointer;
+    border: 1px solid rgba(0, 0, 0, 0.06);
+    position: relative;
+    overflow: hidden;
+}
+
+.report-list-item::before {
+    content: '';
+    position: absolute;
+    left: 0;
+    top: 0;
     bottom: 0;
-    right: 0;
-    left: 220px;
-    top: 120px;
-    margin-left: -10px;
-    padding-bottom: 60px;
+    width: 4px;
+    background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
+    opacity: 0;
+    transition: opacity 0.3s ease;
 }
-</style>
 
-<style scoped>
-.mini-progress {
-    width: 100%;
-    margin-top: 2px;
-    margin-bottom: -3px;
+.report-list-item:hover {
+    transform: translateX(4px);
+    box-shadow: 
+        0 4px 8px rgba(0, 0, 0, 0.08),
+        0 8px 16px rgba(0, 0, 0, 0.1);
 }
-</style>
 
-<style scoped>
-.stat-badge {
+.report-list-item:hover::before {
+    opacity: 1;
+}
+
+.success-item::before {
+    background: #67C23A;
+}
+
+.failure-item::before {
+    background: #F56C6C;
+}
+
+.item-left {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+}
+
+.item-status {
+    flex-shrink: 0;
+}
+
+.status-dot {
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    animation: pulse 2s infinite;
+}
+
+.status-dot.success {
+    background: #67C23A;
+    box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.2);
+}
+
+.status-dot.failure {
+    background: #F56C6C;
+    box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.2);
+}
+
+@keyframes pulse {
+    0%, 100% {
+        opacity: 1;
+    }
+    50% {
+        opacity: 0.5;
+    }
+}
+
+.item-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+    min-width: 0;
+}
+
+.item-type-badge {
+    display: inline-flex;
+    align-items: center;
+    gap: 6px;
+    padding: 4px 10px;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    border-radius: 6px;
+    color: white;
+    font-size: 11px;
+    font-weight: 600;
+    width: fit-content;
+}
+
+.item-type-badge i {
+    font-size: 12px;
+}
+
+.item-name {
+    font-size: 15px;
+    font-weight: 600;
+    color: #1a1a2e;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    line-height: 1.4;
+}
+
+.item-meta-inline {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    margin-top: 8px;
+    padding-top: 8px;
+    border-top: 1px dashed #e4e7ed;
+}
+
+.meta-inline {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    font-size: 12px;
+    color: #909399;
+}
+
+.meta-inline i {
+    font-size: 13px;
+    color: #909399;
+}
+
+.item-stats {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+}
+
+.stat-group {
+    display: flex;
+    gap: 16px;
+    align-items: center;
+}
+
+.stat-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 4px;
+}
+
+.stat-label {
+    font-size: 11px;
+    color: #909399;
+    font-weight: 500;
+    text-transform: uppercase;
+    letter-spacing: 0.3px;
+}
+
+.stat-value {
+    font-size: 16px;
+    font-weight: 700;
+    color: #303133;
+}
+
+.stat-item.success .stat-value {
+    color: #67C23A;
+}
+
+.stat-item.failure .stat-value {
+    color: #F56C6C;
+}
+
+.stat-item.error .stat-value {
+    color: #E6A23C;
+}
+
+.stat-item.skipped .stat-value {
+    color: #909399;
+}
+
+.progress-bar-container {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+}
+
+.progress-bar {
+    flex: 1;
+    height: 8px;
+    background: #e4e7ed;
+    border-radius: 4px;
+    overflow: hidden;
+    display: flex;
+}
+
+.progress-fill {
+    height: 100%;
+    transition: width 0.3s ease;
+}
+
+.progress-fill.success {
+    background: linear-gradient(90deg, #67C23A 0%, #85ce61 100%);
+}
+
+.progress-fill.failure {
+    background: linear-gradient(90deg, #F56C6C 0%, #f78989 100%);
+}
+
+.progress-fill.error {
+    background: linear-gradient(90deg, #E6A23C 0%, #ebb563 100%);
+}
+
+.progress-fill.skipped {
+    background: linear-gradient(90deg, #909399 0%, #a6a9ad 100%);
+}
+
+.success-rate {
+    font-size: 13px;
+    color: #606266;
+    font-weight: 500;
+    white-space: nowrap;
+}
+
+.success-rate .rate-high {
+    color: #67C23A;
+    font-weight: 700;
+}
+
+.success-rate .rate-medium {
+    color: #E6A23C;
+    font-weight: 700;
+}
+
+.success-rate .rate-low {
+    color: #F56C6C;
+    font-weight: 700;
+}
+
+.item-meta {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+}
+
+.meta-row {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    flex-wrap: nowrap;
+}
+
+.meta-item {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    font-size: 12px;
+    color: #606266;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+    padding: 5px 10px;
+    border-radius: 6px;
+    border: 1px solid #e4e7ed;
+    transition: all 0.3s ease;
+    white-space: nowrap;
+    flex-shrink: 0;
+}
+
+.meta-item:hover {
+    border-color: #409EFF;
+    background: #f0f9ff;
+}
+
+.meta-item i {
+    font-size: 13px;
+    color: #909399;
+}
+
+.ci-link {
+    color: #409EFF;
+    text-decoration: none;
+    font-weight: 600;
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    transition: all 0.3s ease;
+}
+
+.ci-link:hover {
+    color: #66b1ff;
+}
+
+.item-actions {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    min-width: 0;
+}
+
+.action-buttons {
+    display: flex;
+    gap: 6px;
+    flex-shrink: 0;
+}
+
+.action-buttons .el-button {
+    padding: 6px 12px;
+    font-size: 12px;
+}
+
+.empty-state {
+    grid-column: 1 / -1;
     display: flex;
     flex-direction: column;
     align-items: center;
     justify-content: center;
-    padding: 4px 0;
+    padding: 100px 20px;
+    color: #909399;
 }
 
-.stat-badge i {
-    margin: 2px 0;
-    font-size: 16px;
-}
-
-.mini-progress {
-    width: 80%;
-    margin: 2px 0;
-}
-</style>
-
-<style scoped>
-.stat-badge.success {
-    color: #67C23A;
-}
-
-.stat-badge.danger {
-    color: #F56C6C;
-}
-
-.stat-badge.warning {
-    color: #E6A23C;
-}
-
-.stat-badge .item {
-    margin-top: -2px;
-}
-
-.time-display {
+.empty-icon {
+    width: 120px;
+    height: 120px;
+    border-radius: 50%;
+    background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
     display: flex;
-    justify-content: left;
     align-items: center;
-    height: 100%;
+    justify-content: center;
+    margin-bottom: 24px;
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
 }
 
-.time-progress {
-    width: 80%;
-    margin-top: 2px;
+.empty-icon i {
+    font-size: 48px;
+    color: #909399;
+    opacity: 0.6;
 }
 
-.el-table >>> .warning-row td {
-    background-color: #fbbaba !important;
+.empty-title {
+    font-size: 20px;
+    font-weight: 700;
+    color: #303133;
+    margin-bottom: 8px;
 }
-.el-table >>> .warning-row:hover td {
-    background-color: #fba5a5 !important;
+
+.empty-description {
+    font-size: 14px;
+    color: #909399;
+    margin: 0;
+}
+
+.pagination-container {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 32px 0;
+    margin-top: 24px;
+}
+
+.el-button {
+    border-radius: 12px;
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    font-weight: 600;
+    letter-spacing: 0.3px;
+}
+
+.el-button:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.el-tag {
+    border-radius: 8px;
+    font-weight: 600;
+    padding: 6px 14px;
+}
+
+.el-switch {
+    margin-left: 0;
+}
+
+.el-input {
+    border-radius: 12px;
+}
+
+.el-input >>> .el-input__inner {
+    border-radius: 12px;
+    border: 2px solid rgba(255, 255, 255, 0.3);
+    background: rgba(255, 255, 255, 0.15);
+    color: white;
+    transition: all 0.3s ease;
+    font-weight: 500;
+}
+
+.el-input >>> .el-input__inner:focus {
+    background: rgba(255, 255, 255, 0.25);
+    border-color: rgba(255, 255, 255, 0.5);
+    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
+}
+
+.el-input >>> .el-input__inner::placeholder {
+    color: rgba(255, 255, 255, 0.7);
+}
+
+.el-input >>> .el-input__prefix {
+    color: rgba(255, 255, 255, 0.8);
+}
+
+.icon {
+    width: 14px;
+    height: 14px;
+}
+
+@media (max-width: 1600px) {
+    .report-list-item {
+        grid-template-columns: 200px 1fr 160px;
+        gap: 12px;
+    }
+}
+
+@media (max-width: 1200px) {
+    .report-list-item {
+        grid-template-columns: 180px 1fr 150px;
+        gap: 10px;
+    }
+}
+
+@media (max-width: 768px) {
+    .report__header {
+        flex-direction: column;
+        align-items: stretch;
+    }
+
+    .report__header--item {
+        width: 100%;
+    }
+
+    .report-list-item {
+        grid-template-columns: 1fr;
+        gap: 10px;
+        padding: 10px 12px;
+    }
+
+    .item-left {
+        grid-column: 1 / -1;
+        margin-bottom: 8px;
+    }
+
+    .item-stats {
+        grid-column: 1 / -1;
+        margin-bottom: 8px;
+    }
+
+    .stat-group {
+        flex-wrap: wrap;
+        justify-content: center;
+        gap: 8px;
+    }
+
+    .item-actions {
+        grid-column: 1 / -1;
+        justify-content: center;
+        padding-top: 8px;
+        border-top: 1px solid #e4e7ed;
+    }
 }
 </style>

--
Gitblit v1.9.1