<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>性能测试平台</title>
    <link href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="https://unpkg.com/echarts/dist/echarts.min.js"></script>
    <style>
        /* ── Reset & Base ── */
        *, *::before, *::after { box-sizing: border-box; }
        html, body { margin: 0; padding: 0; height: 100%; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #f0f2f5; }

        /* ── Layout ── */
        #app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }

        /* ── Header ── */
        .app-header {
            height: 56px;
            background: linear-gradient(135deg, #1a2a4a 0%, #2d4a8a 100%);
            display: flex;
            align-items: center;
            padding: 0 24px;
            box-shadow: 0 2px 12px rgba(0,0,0,.25);
            flex-shrink: 0;
            z-index: 100;
        }
        .app-header .logo-wrap {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .app-header .logo-icon {
            width: 32px;
            height: 32px;
        }
        .app-header .app-title {
            font-size: 17px;
            font-weight: 600;
            color: #fff;
            letter-spacing: .5px;
        }
        .app-header .header-right {
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 16px;
        }
        .app-header .status-dot {
            width: 8px; height: 8px;
            border-radius: 50%;
            background: #52c41a;
            box-shadow: 0 0 6px #52c41a;
            display: inline-block;
        }
        .app-header .status-text {
            font-size: 12px;
            color: rgba(255,255,255,.7);
        }

        /* ── Body ── */
        .app-body {
            display: flex;
            flex: 1;
            overflow: hidden;
        }

        /* ── Sidebar ── */
        .sidebar {
            width: 200px;
            background: #fff;
            border-right: 1px solid #e8ecf0;
            display: flex;
            flex-direction: column;
            flex-shrink: 0;
            overflow-y: auto;
        }
        .sidebar-section {
            padding: 16px 12px 8px;
            font-size: 11px;
            font-weight: 600;
            color: #aab0bc;
            text-transform: uppercase;
            letter-spacing: .8px;
        }
        .nav-item {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 10px 16px;
            margin: 2px 8px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            color: #4a5568;
            transition: all .18s;
            user-select: none;
        }
        .nav-item:hover { background: #f0f4ff; color: #3b6ef0; }
        .nav-item.active { background: linear-gradient(135deg, #e8eeff, #dce8ff); color: #3b6ef0; font-weight: 600; }
        .nav-item .nav-icon { font-size: 16px; width: 20px; text-align: center; }
        .nav-badge {
            margin-left: auto;
            background: #3b6ef0;
            color: #fff;
            font-size: 11px;
            padding: 1px 6px;
            border-radius: 10px;
            min-width: 18px;
            text-align: center;
        }

        /* ── Main Content ── */
        .main-content {
            flex: 1;
            overflow-y: auto;
            padding: 24px;
        }

        /* ── Page Header ── */
        .page-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 20px;
        }
        .page-title {
            font-size: 20px;
            font-weight: 700;
            color: #1a2a4a;
        }
        .page-subtitle {
            font-size: 13px;
            color: #8a94a6;
            margin-top: 2px;
        }

        /* ── Cards ── */
        .card {
            background: #fff;
            border-radius: 12px;
            box-shadow: 0 1px 4px rgba(0,0,0,.06), 0 4px 16px rgba(0,0,0,.04);
            overflow: hidden;
            margin-bottom: 20px;
        }
        .card-header {
            padding: 16px 20px;
            border-bottom: 1px solid #f0f2f5;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        .card-title {
            font-size: 15px;
            font-weight: 600;
            color: #1a2a4a;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .card-title .card-icon {
            width: 28px; height: 28px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 14px;
        }
        .card-body { padding: 20px; }

        /* ── Status Tags ── */
        .status-tag {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            padding: 3px 10px;
            border-radius: 20px;
            font-size: 12px;
            font-weight: 500;
        }
        .status-running { background: #fff7e6; color: #d46b08; }
        .status-done    { background: #f6ffed; color: #389e0d; }
        .status-idle    { background: #f5f5f5; color: #8c8c8c; }
        .status-dot-sm  { width: 6px; height: 6px; border-radius: 50%; }
        .status-running .status-dot-sm { background: #fa8c16; animation: pulse 1.2s infinite; }
        .status-done    .status-dot-sm { background: #52c41a; }
        .status-idle    .status-dot-sm { background: #bfbfbf; }
        @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.4} }

        /* ── Platform Tags ── */
        .platform-tag {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 2px 8px;
            border-radius: 6px;
            font-size: 11px;
            font-weight: 600;
        }
        .platform-ios     { background: #f0f5ff; color: #2f54eb; }
        .platform-android { background: #f6ffed; color: #389e0d; }
        .platform-pc      { background: #f9f0ff; color: #722ed1; }

        /* ── Table ── */
        .perf-table { width: 100%; border-collapse: collapse; }
        .perf-table th {
            background: #f8fafc;
            padding: 11px 14px;
            text-align: left;
            font-size: 12px;
            font-weight: 600;
            color: #8a94a6;
            border-bottom: 1px solid #edf0f5;
            white-space: nowrap;
        }
        .perf-table td {
            padding: 12px 14px;
            font-size: 13px;
            color: #2d3748;
            border-bottom: 1px solid #f5f7fa;
            vertical-align: middle;
        }
        .perf-table tr:last-child td { border-bottom: none; }
        .perf-table tr:hover td { background: #fafbff; }
        .task-name-input { border: none; background: transparent; font-size: 13px; color: #2d3748; width: 100%; outline: none; cursor: pointer; }
        .task-name-input:focus { background: #f0f4ff; border-radius: 4px; padding: 2px 6px; cursor: text; }

        /* ── Action Buttons ── */
        .btn {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 5px 12px;
            border-radius: 6px;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            border: none;
            transition: all .15s;
            white-space: nowrap;
        }
        .btn:disabled { opacity: .5; cursor: not-allowed; }
        .btn-primary   { background: #3b6ef0; color: #fff; }
        .btn-primary:hover:not(:disabled)   { background: #2d5de0; }
        .btn-default   { background: #f0f2f5; color: #4a5568; border: 1px solid #e2e8f0; }
        .btn-default:hover:not(:disabled)   { background: #e8ecf4; }
        .btn-danger    { background: #fff1f0; color: #cf1322; border: 1px solid #ffa39e; }
        .btn-danger:hover:not(:disabled)    { background: #ffe7e6; }
        .btn-warning   { background: #fffbe6; color: #d46b08; border: 1px solid #ffe58f; }
        .btn-warning:hover:not(:disabled)   { background: #fff3cc; }
        .btn-success   { background: #f6ffed; color: #389e0d; border: 1px solid #b7eb8f; }
        .btn-success:hover:not(:disabled)   { background: #edfde0; }
        .btn-sm { padding: 3px 9px; font-size: 11px; }
        .btn-lg { padding: 9px 20px; font-size: 14px; border-radius: 8px; }
        .btn-group { display: flex; gap: 6px; flex-wrap: wrap; }

        /* ── Form ── */
        .form-section { margin-bottom: 24px; }
        .form-label {
            display: block;
            font-size: 13px;
            font-weight: 600;
            color: #4a5568;
            margin-bottom: 6px;
        }
        .form-hint { font-size: 11px; color: #aab0bc; margin-top: 4px; }

        /* ── Device Cards ── */
        .device-grid { display: flex; flex-wrap: wrap; gap: 10px; }
        .device-card {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 10px 14px;
            border: 2px solid #e8ecf0;
            border-radius: 10px;
            cursor: pointer;
            transition: all .18s;
            background: #fff;
            min-width: 160px;
        }
        .device-card:hover { border-color: #3b6ef0; background: #f0f4ff; }
        .device-card.selected { border-color: #3b6ef0; background: #f0f4ff; box-shadow: 0 0 0 3px rgba(59,110,240,.12); }
        .device-card .device-icon { font-size: 22px; }
        .device-card .device-name { font-size: 13px; font-weight: 600; color: #2d3748; }
        .device-card .device-type { font-size: 11px; color: #8a94a6; }

        /* ── Info Grid ── */
        .info-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
        .info-item { background: #f8fafc; border-radius: 8px; padding: 12px 14px; }
        .info-item .info-label { font-size: 11px; color: #8a94a6; margin-bottom: 4px; }
        .info-item .info-value { font-size: 14px; font-weight: 600; color: #1a2a4a; }

        /* ── Chart Container ── */
        .chart-grid { display: flex; flex-wrap: wrap; gap: 16px; }
        .chart-card {
            background: #fff;
            border-radius: 12px;
            box-shadow: 0 1px 4px rgba(0,0,0,.06);
            overflow: hidden;
            flex: 1 1 calc(50% - 8px);
            min-width: 300px;
        }
        .chart-card.full-width { flex: 1 1 100%; }
        .chart-card-header {
            padding: 12px 16px;
            border-bottom: 1px solid #f0f2f5;
            font-size: 13px;
            font-weight: 600;
            color: #2d3748;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .chart-card-header .metric-dot {
            width: 8px; height: 8px;
            border-radius: 50%;
        }
        .chart-body { padding: 8px; height: 280px; }
        .echarts-chart { width: 100%; height: 100%; }

        /* ── Screenshot Panel ── */
        .screenshot-panel {
            background: #f8fafc;
            border-radius: 10px;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 200px;
        }
        .screenshot-panel img { max-width: 100%; max-height: 400px; object-fit: contain; }
        .screenshot-placeholder { color: #aab0bc; font-size: 13px; text-align: center; padding: 40px; }

        /* ── Compare ── */
        .compare-chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
        .compare-chart-item { background: #fff; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,.06); overflow: hidden; }
        .compare-chart-item .chart-label { padding: 10px 14px; font-size: 13px; font-weight: 600; color: #2d3748; border-bottom: 1px solid #f0f2f5; }
        .compare-chart-item .chart-area { height: 240px; padding: 8px; }

        /* ── Empty State ── */
        .empty-state { text-align: center; padding: 60px 20px; color: #aab0bc; }
        .empty-state .empty-icon { font-size: 48px; margin-bottom: 12px; }
        .empty-state .empty-text { font-size: 14px; }

        /* ── Scrollbar ── */
        ::-webkit-scrollbar { width: 6px; height: 6px; }
        ::-webkit-scrollbar-track { background: transparent; }
        ::-webkit-scrollbar-thumb { background: #d0d7e3; border-radius: 3px; }
        ::-webkit-scrollbar-thumb:hover { background: #b0bac8; }

        /* ── Dialog Override ── */
        .el-dialog { border-radius: 14px !important; overflow: hidden; }
        .el-dialog__header { background: #f8fafc; border-bottom: 1px solid #edf0f5; padding: 16px 20px !important; }
        .el-dialog__title { font-size: 16px !important; font-weight: 700 !important; color: #1a2a4a !important; }
        .el-dialog__body { padding: 20px !important; }
        .el-dialog__footer { border-top: 1px solid #edf0f5; padding: 14px 20px !important; }

        /* ── Misc ── */
        .divider { height: 1px; background: #f0f2f5; margin: 20px 0; }
        .text-muted { color: #8a94a6; font-size: 12px; }
        .better { color: #389e0d; font-weight: 600; }
        .worse  { color: #cf1322; font-weight: 600; }
        .tag-running { animation: pulse 1.2s infinite; }

        /* ── Process Tree ── */
        .el-tree { background: #f8fafc; border-radius: 8px; padding: 8px; max-height: 45vh; overflow-y: auto; }

        /* ── Responsive ── */
        @media (max-width: 768px) {
            .sidebar { display: none; }
            .compare-chart-grid { grid-template-columns: 1fr; }
            .chart-card { flex: 1 1 100%; }
        }
    </style>
</head>
<body>
<div id="app">

    <!-- ══ Header ══ -->
    <header class="app-header">
        <div class="logo-wrap">
            <svg class="logo-icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
                <path d="M467 672L110 840l348 160c25 11 44 16 57 16s33-5 59-16l349-160-357-168a105 105 0 0 0-90 0z" fill="#797979"/>
                <path d="M110 792v55c203 92 319 143 348 154 44 16 58 26 130-5 47-20 156-70 325-150v-54c-201 93-309 143-326 149-24 10-102 19-139 0-25-12-137-62-338-149z" fill="#282F9A"/>
                <path d="M467 624L110 792l348 159c25 11 44 16 57 16s33-5 59-16l349-159-357-168a105 105 0 0 0-90 0z" fill="#727AFF"/>
                <path d="M635 43L439 446l160 14-111 333 269-472-132-32z" fill="#FFCB28"/>
                <path d="M439 458l-101 81L595 82l39-39z" fill="#FF9D00"/>
                <path d="M339 538h158l112-79-164-12z" fill="#CD7E00"/>
                <path d="M497 538L456 812l33-19 117-333z" fill="#FF9D00"/>
            </svg>
            <span class="app-title">全平台性能测试平台</span>
        </div>
        <div class="header-right">
            <span class="status-dot"></span>
            <span class="status-text">服务运行中</span>
        </div>
    </header>

    <!-- ══ Body ══ -->
    <div class="app-body">

        <!-- ── Sidebar ── -->
        <nav class="sidebar">
            <div class="sidebar-section">导航</div>
            <div class="nav-item" :class="{active: currentView==='taskList'}" @click="switchView('taskList')">
                <span class="nav-icon">📋</span> 任务列表
                <span class="nav-badge" v-if="tasks.length">{{ tasks.length }}</span>
            </div>
            <div class="nav-item" :class="{active: currentView==='createTask'}" @click="switchView('createTask')">
                <span class="nav-icon">➕</span> 创建任务
            </div>
            <div class="nav-item" :class="{active: currentView==='compareTask'}" @click="switchView('compareTask')">
                <span class="nav-icon">⚖️</span> 对比任务
            </div>
            <div class="nav-item" :class="{active: currentView==='reportManagement'}" @click="switchView('reportManagement')">
                <span class="nav-icon">📊</span> 报告管理
            </div>
        </nav>

        <!-- ── Main ── -->
        <main class="main-content">

            <!-- ════ 任务列表 ════ -->
            <template v-if="currentView === 'taskList'">
                <div class="page-header">
                    <div>
                        <div class="page-title">任务列表</div>
                        <div class="page-subtitle">管理所有性能采集任务</div>
                    </div>
                    <button class="btn btn-primary btn-lg" @click="switchView('createTask')">
                        ＋ 新建任务
                    </button>
                </div>

                <div class="card">
                    <div class="card-body" style="padding:0;">
                        <table class="perf-table">
                            <thead>
                                <tr>
                                    <th>任务名称</th>
                                    <th>设备</th>
                                    <th>进程</th>
                                    <th>版本</th>
                                    <th>平台</th>
                                    <th>状态</th>
                                    <th>开始时间</th>
                                    <th>操作</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-if="tasks.length === 0">
                                    <td colspan="8">
                                        <div class="empty-state">
                                            <div class="empty-icon">📭</div>
                                            <div class="empty-text">暂无任务，点击「新建任务」开始采集</div>
                                        </div>
                                    </td>
                                </tr>
                                <tr v-for="row in tasks" :key="row.id">
                                    <td style="min-width:140px;">
                                        <input class="task-name-input"
                                            v-model="row.name"
                                            @blur="saveEdit(row)"
                                            :title="row.name" />
                                    </td>
                                    <td class="text-muted">{{ row.serialno || '本机' }}</td>
                                    <td class="text-muted">{{ row.target_pid_name || row.target_pid }}</td>
                                    <td>
                                        <input class="task-name-input"
                                            v-model="row.version"
                                            placeholder="—"
                                            @blur="saveVersion(row)"
                                            style="width:80px;" />
                                    </td>
                                    <td>
                                        <span :class="['platform-tag', 'platform-' + (row.device_type || 'pc')]">
                                            {{ (row.platform || row.device_type || 'PC').toUpperCase() }}
                                        </span>
                                    </td>
                                    <td>
                                        <span v-if="row.status==1" class="status-tag status-running">
                                            <span class="status-dot-sm"></span> 采集中
                                        </span>
                                        <span v-else-if="row.status==2" class="status-tag status-done">
                                            <span class="status-dot-sm"></span> 已完成
                                        </span>
                                        <span v-else class="status-tag status-idle">
                                            <span class="status-dot-sm"></span> 未开始
                                        </span>
                                    </td>
                                    <td class="text-muted" style="white-space:nowrap;">{{ row.start_time }}</td>
                                    <td>
                                        <div class="btn-group">
                                            <button v-if="row.status==1" class="btn btn-warning btn-sm" @click="stop_task(row.id)">停止</button>
                                            <button class="btn btn-primary btn-sm" @click="curtaskid=row.id; result(row.id)">查看</button>
                                            <button class="btn btn-default btn-sm" @click="exportExcel(row.id)">导出</button>
                                            <button class="btn btn-danger btn-sm" @click="delete_task(row.id)">删除</button>
                                        </div>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </template>

            <!-- ════ 创建任务 ════ -->
            <template v-else-if="currentView === 'createTask'">
                <div class="page-header">
                    <div>
                        <div class="page-title">创建任务</div>
                        <div class="page-subtitle">选择设备和目标进程，开始性能采集</div>
                    </div>
                </div>

                <!-- 设备选择 -->
                <div class="card">
                    <div class="card-header">
                        <div class="card-title">
                            <span class="card-icon" style="background:#e8f4ff;">📱</span>
                            选择测试设备
                        </div>
                        <button class="btn btn-default btn-sm" @click="refreshDevices" :disabled="isRefreshingDevices">
                            {{ isRefreshingDevices ? '刷新中…' : '🔄 刷新' }}
                        </button>
                    </div>
                    <div class="card-body">
                        <div v-if="connectedDevices.length === 0" class="empty-state" style="padding:30px;">
                            <div class="empty-icon">🔌</div>
                            <div class="empty-text">未检测到设备，请连接后点击刷新</div>
                        </div>
                        <div v-else class="device-grid">
                            <div v-for="d in connectedDevices" :key="d._key"
                                class="device-card"
                                :class="{selected: selectedDeviceKey === d._key}"
                                @click="selectedDeviceKey=d._key; onDeviceChange(d._key)">
                                <span class="device-icon">{{ d.device_type==='ios' ? '📱' : d.device_type==='android' ? '🤖' : '🖥️' }}</span>
                                <div>
                                    <div class="device-name">{{ d.name || d.model || d.serial || d.udid }}</div>
                                    <div class="device-type">{{ d.device_type.toUpperCase() }}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 设备信息 -->
                <div class="card">
                    <div class="card-header">
                        <div class="card-title">
                            <span class="card-icon" style="background:#f0fff4;">💻</span>
                            设备信息
                        </div>
                    </div>
                    <div class="card-body">
                        <div v-if="currentSystemInfo" class="info-grid">
                            <div class="info-item">
                                <div class="info-label">平台</div>
                                <div class="info-value">{{ currentSystemInfo.platform }}</div>
                            </div>
                            <div class="info-item">
                                <div class="info-label">设备名称</div>
                                <div class="info-value">{{ currentSystemInfo.computer_name }}</div>
                            </div>
                            <div class="info-item">
                                <div class="info-label">CPU 核心数</div>
                                <div class="info-value">{{ currentSystemInfo.cpu_cores }}</div>
                            </div>
                            <div class="info-item">
                                <div class="info-label">RAM</div>
                                <div class="info-value">{{ currentSystemInfo.ram }}</div>
                            </div>
                            <div class="info-item">
                                <div class="info-label">ROM</div>
                                <div class="info-value">{{ currentSystemInfo.rom }}</div>
                            </div>
                            <div class="info-item" v-if="currentSystemInfo.android_version">
                                <div class="info-label">Android 版本</div>
                                <div class="info-value">{{ currentSystemInfo.android_version }}</div>
                            </div>
                            <div class="info-item" v-if="currentSystemInfo.ios_version">
                                <div class="info-label">iOS 版本</div>
                                <div class="info-value">{{ currentSystemInfo.ios_version }}</div>
                            </div>
                        </div>
                        <div v-else>
                            <el-skeleton :rows="2" animated />
                        </div>
                    </div>
                </div>

                <!-- PC 进程选择 -->
                <template v-if="currentDeviceType === 'pc'">
                    <div class="card">
                        <div class="card-header">
                            <div class="card-title">
                                <span class="card-icon" style="background:#fff7e6;">⚙️</span>
                                选择目标进程
                            </div>
                            <div style="display:flex;gap:8px;align-items:center;">
                                <button class="btn btn-default btn-sm"
                                    :class="{active: chose_radio==='独立进程'}"
                                    @click="chose_radio='独立进程'"
                                    style="border-radius:6px 0 0 6px;">独立进程</button>
                                <button class="btn btn-default btn-sm"
                                    :class="{active: chose_radio==='树形进程'}"
                                    @click="chose_radio='树形进程'; getProcessTree()"
                                    style="border-radius:0 6px 6px 0;margin-left:-1px;">树形进程</button>
                            </div>
                        </div>
                        <div class="card-body">
                            <!-- 独立进程 -->
                            <div v-if="chose_radio==='独立进程'" class="form-section">
                                <label class="form-label">选择 PID</label>
                                <el-select v-model="newTask.pid" filterable placeholder="搜索进程名称或 PID"
                                    @visible-change="get_pids" @change="getPidCmd" style="width:100%;">
                                    <el-option v-for="p in pids" :key="p.pid"
                                        :label="p.name + ' — ' + p.pid" :value="p.pid">
                                    </el-option>
                                </el-select>
                            </div>
                            <!-- 树形进程 -->
                            <div v-if="chose_radio==='树形进程'" class="form-section">
                                <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
                                    <label class="form-label" style="margin:0;">进程树</label>
                                    <button class="btn btn-default btn-sm" @click="refreshProcessTree" :disabled="isRefreshingProcessTree">
                                        {{ isRefreshingProcessTree ? '刷新中…' : '🔄 刷新' }}
                                    </button>
                                </div>
                                <el-tree ref="processTreeRef" :data="processTree" node-key="pid"
                                    :default-expand-all="true" :props="treeProps"
                                    :highlight-current="true" @node-click="handleNodeClick">
                                </el-tree>
                            </div>

                            <div v-if="cmd" class="info-item" style="margin-bottom:12px;">
                                <div class="info-label">命令行</div>
                                <div class="info-value" style="font-size:12px;word-break:break-all;font-weight:400;">{{ cmd }}</div>
                            </div>

                            <div v-if="pid_img" style="margin-bottom:12px;">
                                <label class="form-label">进程截图</label>
                                <div class="screenshot-panel">
                                    <img :src="pid_img" alt="进程截图" />
                                </div>
                            </div>

                            <div class="form-section">
                                <label class="form-label">监控范围</label>
                                <div style="display:flex;gap:8px;">
                                    <button class="btn" :class="include_child ? 'btn-default' : 'btn-primary'"
                                        @click="include_child=false">仅当前进程</button>
                                    <button class="btn" :class="include_child ? 'btn-primary' : 'btn-default'"
                                        @click="include_child=true">含子进程</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </template>

                <!-- 移动端应用选择 -->
                <template v-if="currentDeviceType === 'android' || currentDeviceType === 'ios'">
                    <div class="card">
                        <div class="card-header">
                            <div class="card-title">
                                <span class="card-icon" style="background:#fff0f6;">📦</span>
                                选择目标应用
                            </div>
                        </div>
                        <div class="card-body">
                            <div class="form-section">
                                <label class="form-label">应用列表</label>
                                <el-select v-model="selectedApp" filterable placeholder="搜索应用名称"
                                    @visible-change="refreshDeviceApps" value-key="package_name || bundle_id"
                                    style="width:100%;">
                                    <el-option v-for="app in deviceApps"
                                        :key="app.package_name || app.bundle_id"
                                        :label="app.name + (app.running ? ' (运行中)' : '')"
                                        :value="app">
                                        <span>{{ app.name }}</span>
                                        <span style="float:right;color:#aab0bc;font-size:12px;">
                                            {{ app.package_name || app.bundle_id }}
                                            <el-tag v-if="app.running" size="mini" type="success" style="margin-left:4px;">运行中</el-tag>
                                        </span>
                                    </el-option>
                                </el-select>
                            </div>

                            <div v-if="selectedApp" class="info-grid" style="margin-bottom:16px;">
                                <div class="info-item">
                                    <div class="info-label">包名</div>
                                    <div class="info-value" style="font-size:12px;font-weight:400;">{{ selectedApp.package_name || selectedApp.bundle_id }}</div>
                                </div>
                                <div class="info-item" v-if="selectedApp.version">
                                    <div class="info-label">版本</div>
                                    <div class="info-value">{{ selectedApp.version }}</div>
                                </div>
                                <div class="info-item" v-if="selectedApp.pid">
                                    <div class="info-label">PID</div>
                                    <div class="info-value">{{ selectedApp.pid }}</div>
                                </div>
                            </div>

                            <div v-if="selectedApp">
                                <button class="btn btn-default btn-sm" @click="getMobileScreenshot" style="margin-bottom:10px;">📷 获取截图</button>
                                <div v-if="pid_img" class="screenshot-panel">
                                    <img :src="pid_img" alt="设备截图" />
                                </div>
                            </div>
                        </div>
                    </div>
                </template>

                <!-- 任务名称 & 提交 -->
                <div class="card">
                    <div class="card-body">
                        <div class="form-section">
                            <label class="form-label">任务名称</label>
                            <el-input v-model="newTask.name" placeholder="请输入任务名称" clearable></el-input>
                        </div>
                        <div style="display:flex;gap:12px;">
                            <button class="btn btn-primary btn-lg" @click="createTask">🚀 开始采集</button>
                            <button class="btn btn-default btn-lg" @click="switchView('taskList')">取消</button>
                        </div>
                    </div>
                </div>
            </template>

            <!-- ════ 对比任务 ════ -->
            <template v-else-if="currentView === 'compareTask'">
                <div class="page-header">
                    <div>
                        <div class="page-title">任务对比</div>
                        <div class="page-subtitle">支持全量对比或按区间标签精确对比</div>
                    </div>
                    <!-- 模式切换 -->
                    <div style="display:flex;gap:0;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;">
                        <button class="btn" style="border-radius:0;border:none;"
                            :style="compareMode==='task' ? 'background:#3b6ef0;color:#fff;' : 'background:#fff;color:#4a5568;'"
                            @click="compareMode='task'">📋 全量对比</button>
                        <button class="btn" style="border-radius:0;border:none;border-left:1px solid #e2e8f0;"
                            :style="compareMode==='label' ? 'background:#3b6ef0;color:#fff;' : 'background:#fff;color:#4a5568;'"
                            @click="compareMode='label'">🏷️ 标签对比</button>
                    </div>
                </div>

                <!-- ── 全量对比模式 ── -->
                <template v-if="compareMode==='task'">
                    <div class="card">
                        <div class="card-header">
                            <div class="card-title">
                                <span class="card-icon" style="background:#f0f5ff;">📌</span>
                                选择对比任务
                            </div>
                            <span class="text-muted">已选 {{ selectedTasksForCompare.length }} 个</span>
                        </div>
                        <div class="card-body" style="padding:0;">
                            <table class="perf-table">
                                <thead>
                                    <tr>
                                        <th style="width:48px;"></th>
                                        <th>任务名称</th>
                                        <th>版本</th>
                                        <th>进程名称</th>
                                        <th>平台</th>
                                        <th>状态</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr v-for="row in tasks" :key="row.id"
                                        style="cursor:pointer;"
                                        :style="isTaskSelected(row) ? 'background:#f0f4ff;' : ''"
                                        @click="toggleTaskSelection(row)">
                                        <td style="text-align:center;">
                                            <span style="font-size:16px;">{{ isTaskSelected(row) ? '✅' : '⬜' }}</span>
                                        </td>
                                        <td>{{ row.name }}</td>
                                        <td class="text-muted">{{ row.version || '—' }}</td>
                                        <td class="text-muted">{{ row.target_pid_name }}</td>
                                        <td>
                                            <span :class="['platform-tag', 'platform-' + (row.device_type || 'pc')]">
                                                {{ (row.platform || row.device_type || 'PC').toUpperCase() }}
                                            </span>
                                        </td>
                                        <td>
                                            <span v-if="row.status==2" class="status-tag status-done"><span class="status-dot-sm"></span>已完成</span>
                                            <span v-else-if="row.status==1" class="status-tag status-running"><span class="status-dot-sm"></span>采集中</span>
                                            <span v-else class="status-tag status-idle"><span class="status-dot-sm"></span>未开始</span>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                    <div class="card" v-if="selectedTasksForCompare.length >= 2">
                        <div class="card-body">
                            <div class="form-section">
                                <label class="form-label">基准任务</label>
                                <el-select v-model="baseTaskId" placeholder="请选择基准任务" style="width:100%;max-width:400px;">
                                    <el-option v-for="t in selectedTasksForCompare" :key="t.id"
                                        :label="t.name + (t.version ? ' (' + t.version + ')' : '')"
                                        :value="t.id">
                                    </el-option>
                                </el-select>
                            </div>
                            <div class="btn-group">
                                <button class="btn btn-primary btn-lg" @click="compareTasks">⚖️ 开始对比</button>
                                <button class="btn btn-default btn-lg" @click="selectedTasksForCompare=[]">清除选择</button>
                            </div>
                        </div>
                    </div>
                </template>

                <!-- ── 标签对比模式 ── -->
                <template v-else>
                    <div class="card">
                        <div class="card-header">
                            <div class="card-title">
                                <span class="card-icon" style="background:#fff7e6;">🏷️</span>
                                选择对比标签
                            </div>
                            <span class="text-muted">已选 {{ selectedLabelsForCompare.length }} 个标签</span>
                        </div>
                        <div class="card-body">
                            <div v-if="allLabelsWithTask.length === 0" class="empty-state" style="padding:30px;">
                                <div class="empty-icon">🏷️</div>
                                <div class="empty-text">暂无标签，请先在任务详情中拖拽选区并保存标签</div>
                            </div>
                            <div v-else>
                                <!-- 按任务分组展示标签 -->
                                <div v-for="group in labelGroups" :key="group.task_id" style="margin-bottom:16px;">
                                    <div style="font-size:13px;font-weight:600;color:#4a5568;margin-bottom:8px;display:flex;align-items:center;gap:8px;">
                                        <span>{{ group.task_name }}</span>
                                        <span class="text-muted" style="font-weight:400;">{{ group.task_version ? 'v'+group.task_version : '' }}</span>
                                    </div>
                                    <div style="display:flex;flex-wrap:wrap;gap:8px;">
                                        <div v-for="lb in group.labels" :key="lb.id"
                                            @click="toggleLabelSelection(lb)"
                                            style="display:flex;align-items:center;gap:6px;padding:7px 12px;border-radius:8px;border:2px solid;cursor:pointer;transition:all .15s;"
                                            :style="{
                                                borderColor: lb.color,
                                                background: isLabelSelected(lb) ? lb.color : lb.color+'18',
                                                color: isLabelSelected(lb) ? '#fff' : '#2d3748'
                                            }">
                                            <span style="font-size:15px;">{{ isLabelSelected(lb) ? '✅' : '🏷️' }}</span>
                                            <div>
                                                <div style="font-weight:600;font-size:13px;">{{ lb.name }}</div>
                                                <div style="font-size:11px;opacity:.8;">{{ num2time(lb.start_ts) }} ~ {{ num2time(lb.end_ts) }}</div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="card" v-if="selectedLabelsForCompare.length >= 2">
                        <div class="card-body">
                            <div style="margin-bottom:12px;">
                                <div class="form-label" style="margin-bottom:8px;">已选标签（第一个为基准）</div>
                                <div style="display:flex;flex-wrap:wrap;gap:8px;">
                                    <div v-for="(lb, idx) in selectedLabelsForCompare" :key="lb.id"
                                        style="display:flex;align-items:center;gap:6px;padding:5px 10px;border-radius:8px;font-size:12px;border:1.5px solid;"
                                        :style="{borderColor: lb.color, background: lb.color+'18'}">
                                        <span style="background:#1a2a4a;color:#fff;border-radius:50%;width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;">{{ idx+1 }}</span>
                                        <span :style="{color:lb.color,fontWeight:600}">{{ lb.name }}</span>
                                        <span class="text-muted">{{ lb._task_name }}</span>
                                        <span style="cursor:pointer;color:#cf1322;" @click="selectedLabelsForCompare.splice(idx,1)">✕</span>
                                    </div>
                                </div>
                            </div>
                            <div class="btn-group">
                                <button class="btn btn-primary btn-lg" @click="compareLabelsTasks">🏷️ 开始标签对比</button>
                                <button class="btn btn-default btn-lg" @click="selectedLabelsForCompare=[]">清除选择</button>
                            </div>
                        </div>
                    </div>
                </template>
            </template>

            <!-- ════ 报告管理 ════ -->
            <template v-else-if="currentView === 'reportManagement'">
                <div class="page-header">
                    <div>
                        <div class="page-title">报告管理</div>
                        <div class="page-subtitle">查看和管理历史对比报告</div>
                    </div>
                    <button class="btn btn-default btn-lg" @click="getComparisonReports">🔄 刷新</button>
                </div>

                <div class="card">
                    <div class="card-body" style="padding:0;">
                        <table class="perf-table">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>报告名称</th>
                                    <th>创建时间</th>
                                    <th>类型</th>
                                    <th>操作</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-if="comparisonReports.length === 0">
                                    <td colspan="5">
                                        <div class="empty-state">
                                            <div class="empty-icon">📂</div>
                                            <div class="empty-text">暂无报告</div>
                                        </div>
                                    </td>
                                </tr>
                                <tr v-for="r in comparisonReports" :key="r.id">
                                    <td class="text-muted">{{ r.id }}</td>
                                    <td>{{ r.name }}</td>
                                    <td class="text-muted">{{ r.create_time }}</td>
                                    <td><span class="platform-tag platform-pc">普通报告</span></td>
                                    <td>
                                        <div class="btn-group">
                                            <button class="btn btn-primary btn-sm" @click="openReportFile(r.report_path)">查看</button>
                                            <button class="btn btn-danger btn-sm" @click="deleteReport(r.id)">删除</button>
                                        </div>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </template>

        </main>
    </div><!-- end app-body -->

    <!-- ══════════════════════════════════════
         Dialog: 任务详情图表
    ══════════════════════════════════════ -->
    <el-dialog :visible.sync="dialogVisible" width="96%" fullscreen
        @open="handleDialogVisibleChange"
        @closed="onDetailDialogClosed">
        <div slot="title" style="font-size:16px;font-weight:700;color:#1a2a4a;display:flex;align-items:center;gap:16px;">
            <span>📈 性能数据详情</span>
            <!-- 区间标签工具栏 -->
            <div style="display:flex;align-items:center;gap:8px;margin-left:8px;">
                <button class="btn btn-default btn-sm" @click="labelPanelVisible=!labelPanelVisible">
                    🏷️ 标签管理 <span v-if="currentLabels.length" style="background:#3b6ef0;color:#fff;border-radius:10px;padding:0 6px;font-size:11px;margin-left:4px;">{{ currentLabels.length }}</span>
                </button>
                <button class="btn btn-success btn-sm" :disabled="!brushRange.start_ts"
                    @click="showSaveLabelDialog">
                    ✂️ 保存选区
                </button>
                <span v-if="brushRange.start_ts" class="text-muted" style="font-size:12px;">
                    已选 {{ num2time(brushRange.start_ts) }} ~ {{ num2time(brushRange.end_ts) }}
                </span>
            </div>
        </div>

        <!-- 标签面板 -->
        <div v-if="labelPanelVisible" style="background:#f8fafc;border-radius:10px;padding:14px 16px;margin-bottom:14px;border:1px solid #e8ecf0;">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;">
                <span style="font-size:13px;font-weight:600;color:#1a2a4a;">🏷️ 区间标签</span>
                <span class="text-muted">在图表上拖拽选区后点击「保存选区」</span>
            </div>
            <div v-if="currentLabels.length === 0" class="text-muted" style="text-align:center;padding:16px;">暂无标签</div>
            <div v-else style="display:flex;flex-wrap:wrap;gap:8px;">
                <div v-for="lb in currentLabels" :key="lb.id"
                    style="display:flex;align-items:center;gap:6px;padding:5px 10px;border-radius:8px;border:1.5px solid;font-size:12px;"
                    :style="{borderColor: lb.color, background: lb.color + '18'}">
                    <span :style="{color: lb.color, fontWeight:600}">{{ lb.name }}</span>
                    <span class="text-muted">{{ num2time(lb.start_ts) }}~{{ num2time(lb.end_ts) }}</span>
                    <span style="cursor:pointer;color:#cf1322;margin-left:2px;" @click="deleteLabel(lb.id)">✕</span>
                </div>
            </div>
        </div>

        <div class="chart-grid">
            <!-- 截图 -->
            <div class="chart-card" style="flex:0 0 260px;min-width:220px;">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#8b5cf6;"></span>
                    {{ num2time(clicktime) || '—' }} 截图
                </div>
                <div class="chart-body" style="height:320px;padding:0;">
                    <div class="screenshot-panel" style="height:100%;border-radius:0;">
                        <img v-if="clicktime"
                            :src="'/static/' + opentaskid + '/screenshot/' + clicktime + '.png'"
                            alt="截图" style="max-height:100%;max-width:100%;object-fit:contain;" />
                        <div v-else class="screenshot-placeholder">点击图表时间轴<br>查看对应截图</div>
                    </div>
                </div>
            </div>
            <!-- CPU -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#3b6ef0;"></span> CPU 使用率 (%)
                </div>
                <div class="chart-body"><div id="cpuChart" class="echarts-chart"></div></div>
            </div>
            <!-- Memory -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#10b981;"></span> 内存使用量 (MB)
                </div>
                <div class="chart-body"><div id="memoryChart" class="echarts-chart"></div></div>
            </div>
            <!-- FPS -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#f59e0b;"></span> FPS (帧/秒)
                </div>
                <div class="chart-body"><div id="fpsChart" class="echarts-chart"></div></div>
            </div>
            <!-- GPU -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#ef4444;"></span> GPU 使用率 (%)
                </div>
                <div class="chart-body"><div id="gpuChart" class="echarts-chart"></div></div>
            </div>
            <!-- Process Info -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#8b5cf6;"></span> 线程数 / 句柄数
                </div>
                <div class="chart-body"><div id="processInfoChart" class="echarts-chart"></div></div>
            </div>
            <!-- Disk IO -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#06b6d4;"></span> 磁盘 I/O (MB/s)
                </div>
                <div class="chart-body"><div id="diskIOChart" class="echarts-chart"></div></div>
            </div>
            <!-- Network IO -->
            <div class="chart-card">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#f97316;"></span> 网络 I/O (MB/s)
                </div>
                <div class="chart-body"><div id="networkIOChart" class="echarts-chart"></div></div>
            </div>
            <!-- Battery (mobile) -->
            <div class="chart-card" id="batteryChartContainer" style="display:none;">
                <div class="chart-card-header">
                    <span class="metric-dot" style="background:#52c41a;"></span> 🔋 电池信息
                </div>
                <div class="chart-body"><div id="batteryChart" class="echarts-chart"></div></div>
            </div>
        </div>
    </el-dialog>

    <!-- ══════════════════════════════════════
         Dialog: 保存区间标签
    ══════════════════════════════════════ -->
    <el-dialog title="保存区间标签" :visible.sync="saveLabelDialogVisible" width="420px" append-to-body>
        <div style="padding:4px 0;">
            <div class="form-section">
                <label class="form-label">标签名称</label>
                <el-input v-model="newLabelForm.name" placeholder="如：冷启动、滑动列表…" clearable></el-input>
            </div>
            <div class="form-section">
                <label class="form-label">备注（可选）</label>
                <el-input v-model="newLabelForm.note" placeholder="描述这段区间的场景" clearable></el-input>
            </div>
            <div class="form-section">
                <label class="form-label">颜色</label>
                <div style="display:flex;gap:8px;flex-wrap:wrap;">
                    <span v-for="c in labelColors" :key="c"
                        @click="newLabelForm.color=c"
                        :style="{
                            width:'24px',height:'24px',borderRadius:'50%',background:c,cursor:'pointer',
                            boxShadow: newLabelForm.color===c ? '0 0 0 3px '+c+'66' : 'none',
                            border: newLabelForm.color===c ? '2px solid #fff' : '2px solid transparent'
                        }">
                    </span>
                </div>
            </div>
            <div class="info-grid" style="margin-top:4px;">
                <div class="info-item">
                    <div class="info-label">开始时间</div>
                    <div class="info-value" style="font-size:13px;">{{ num2time(brushRange.start_ts) }}</div>
                </div>
                <div class="info-item">
                    <div class="info-label">结束时间</div>
                    <div class="info-value" style="font-size:13px;">{{ num2time(brushRange.end_ts) }}</div>
                </div>
            </div>
        </div>
        <span slot="footer">
            <button class="btn btn-primary" @click="saveLabel" :disabled="!newLabelForm.name">保存</button>
            <button class="btn btn-default" style="margin-left:8px;" @click="saveLabelDialogVisible=false">取消</button>
        </span>
    </el-dialog>

    <!-- ══════════════════════════════════════
         Dialog: 对比结果
    ══════════════════════════════════════ -->
    <el-dialog title="任务对比结果" :visible.sync="compareDialogVisible"
        width="92%" fullscreen :before-close="()=>{compareDialogVisible=false}">
        <div v-if="comparisonResult">
            <!-- 概览 -->
            <div class="card" style="margin-bottom:16px;">
                <div class="card-body">
                    <div class="info-grid">
                        <div class="info-item">
                            <div class="info-label">基准任务</div>
                            <div class="info-value">{{ comparisonResult.base_task ? comparisonResult.base_task.name : '—' }}</div>
                        </div>
                        <div class="info-item">
                            <div class="info-label">对比任务数</div>
                            <div class="info-value">{{ comparisonResult.tasks ? comparisonResult.tasks.length : 0 }}</div>
                        </div>
                        <div class="info-item">
                            <div class="info-label">创建时间</div>
                            <div class="info-value" style="font-size:12px;font-weight:400;">{{ new Date().toLocaleString() }}</div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 对比图表 -->
            <div class="compare-chart-grid">
                <div class="compare-chart-item" v-for="m in compareMetrics" :key="m.key">
                    <div class="chart-label">{{ m.label }}</div>
                    <div class="chart-area"><div :id="'compare_' + m.key + '_chart'" style="width:100%;height:100%;"></div></div>
                </div>
            </div>

            <!-- 汇总表 -->
            <div class="divider"></div>
            <div class="card">
                <div class="card-header"><div class="card-title">对比详情</div></div>
                <div class="card-body" style="padding:0;">
                    <table class="perf-table">
                        <thead>
                            <tr>
                                <th>任务名称</th>
                                <th>版本</th>
                                <th>基准</th>
                                <th>CPU 均值</th>
                                <th>内存均值</th>
                                <th>FPS 均值</th>
                                <th>GPU 均值</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr v-for="t in comparisonResult.tasks" :key="t.id">
                                <td>{{ t.name }}</td>
                                <td class="text-muted">{{ t.version || '—' }}</td>
                                <td>
                                    <span v-if="t.id === comparisonResult.base_task.id" class="status-tag status-done">基准</span>
                                    <span v-else class="text-muted">—</span>
                                </td>
                                <td>{{ t.avg && t.avg.cpu_avg ? t.avg.cpu_avg.toFixed(2) + '%' : '—' }}</td>
                                <td>{{ t.avg && t.avg.memory_avg ? t.avg.memory_avg.toFixed(2) + ' MB' : '—' }}</td>
                                <td>{{ t.avg && t.avg.fps_avg ? t.avg.fps_avg.toFixed(2) : '—' }}</td>
                                <td>{{ t.avg && t.avg.gpu_avg ? t.avg.gpu_avg.toFixed(2) + '%' : '—' }}</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
        <span slot="footer">
            <button class="btn btn-primary" @click="exportComparisonExcel">📥 导出报表</button>
            <button class="btn btn-default" style="margin-left:8px;" @click="compareDialogVisible=false">关闭</button>
        </span>
    </el-dialog>

    <!-- ══════════════════════════════════════
         Dialog: 高级分析
    ══════════════════════════════════════ -->
    <el-dialog title="高级性能分析" :visible.sync="advancedDialogVisible"
        width="92%" fullscreen :before-close="()=>{advancedDialogVisible=false}"
        @open="handleAdvancedDialogOpen">
        <div v-if="advancedAnalysisResult">
            <div class="card" style="margin-bottom:16px;">
                <div class="card-body">
                    <div class="info-grid">
                        <div class="info-item">
                            <div class="info-label">基准任务</div>
                            <div class="info-value">{{ advancedAnalysisResult.base_task ? advancedAnalysisResult.base_task.name : '—' }}</div>
                        </div>
                        <div class="info-item">
                            <div class="info-label">对比任务</div>
                            <div class="info-value">{{ advancedAnalysisResult.compare_task ? advancedAnalysisResult.compare_task.name : '—' }}</div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="card" style="margin-bottom:16px;">
                <div class="card-header"><div class="card-title">统计显著性分析</div></div>
                <div class="card-body" style="height:360px;padding:8px;">
                    <div id="advanced_significance_chart" style="width:100%;height:100%;"></div>
                </div>
            </div>

            <div class="card" style="margin-bottom:16px;">
                <div class="card-header"><div class="card-title">性能瓶颈分析</div></div>
                <div class="card-body" style="height:360px;padding:8px;">
                    <div id="advanced_bottlenecks_chart" style="width:100%;height:100%;"></div>
                </div>
            </div>

            <div class="card">
                <div class="card-header"><div class="card-title">异常值检测</div></div>
                <div class="card-body">
                    <el-collapse v-if="advancedAnalysisResult.advanced_analysis && advancedAnalysisResult.advanced_analysis.outliers">
                        <el-collapse-item
                            v-for="(outlier, metricName) in advancedAnalysisResult.advanced_analysis.outliers"
                            :key="metricName"
                            :title="getMetricDisplayName(metricName) + ' 异常值分析'">
                            <table class="perf-table">
                                <thead>
                                    <tr><th>异常值</th><th>Z 分数</th><th>时间点</th><th>类型</th></tr>
                                </thead>
                                <tbody>
                                    <tr v-for="(o, i) in outlier.outliers" :key="i">
                                        <td>{{ o.value.toFixed(2) }}</td>
                                        <td>{{ o.z_score.toFixed(2) }}</td>
                                        <td class="text-muted">{{ new Date(o.timestamp * 1000).toLocaleString() }}</td>
                                        <td>
                                            <span :class="['status-tag', o.is_high ? 'status-running' : 'status-idle']">
                                                {{ o.is_high ? '高值异常' : '低值异常' }}
                                            </span>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                            <div style="margin-top:12px;font-size:13px;color:#4a5568;">
                                <strong>总结：</strong>{{ outlier.summary }}
                            </div>
                        </el-collapse-item>
                    </el-collapse>
                    <div v-else class="empty-state"><div class="empty-icon">✅</div><div class="empty-text">未检测到异常值</div></div>
                </div>
            </div>
        </div>
        <span slot="footer">
            <button class="btn btn-primary" @click="exportAdvancedAnalysisExcel">📥 导出报表</button>
            <button class="btn btn-default" style="margin-left:8px;" @click="advancedDialogVisible=false">关闭</button>
        </span>
    </el-dialog>

</div><!-- end #app -->
<script>
new Vue({
    el: '#app',
    data() {
        return {
            // ── 视图 ──
            currentView: 'taskList',

            // ── 任务列表 ──
            tasks: [],

            // ── 创建任务 ──
            chose_radio: '树形进程',
            include_child: true,
            newTask: { name: '新任务', pid: null },
            pids: [],
            cmd: '',
            pid_name: '',
            pid_img: '',
            processTree: [],
            isRefreshingProcessTree: false,
            treeProps: { children: 'child_p', label: 'name' },

            // ── 设备 ──
            connectedDevices: [],
            selectedDeviceKey: 'pc_local',
            currentDeviceType: 'pc',
            currentDeviceId: null,
            isRefreshingDevices: false,
            deviceApps: [],
            selectedApp: null,
            currentSystemInfo: null,

            // ── 图表对话框 ──
            dialogVisible: false,
            resultOption: null,
            clicktime: null,
            opentaskid: null,
            curtaskid: null,
            intervalId: null,
            isloadres: false,
            cpuChart: null, memoryChart: null, fpsChart: null,
            gpuChart: null, processInfoChart: null,
            diskIOChart: null, networkIOChart: null, batteryChart: null,

            // ── 对比 ──
            compareMode: 'task',                // 'task' | 'label'
            selectedTasksForCompare: [],
            baseTaskId: null,
            compareDialogVisible: false,
            comparisonResult: null,
            comparisonCharts: {},
            resizeComparisonCharts: null,
            compareMetrics: [
                { key: 'cpu',       label: 'CPU 使用率 (%)' },
                { key: 'memory',    label: '内存使用量 (MB)' },
                { key: 'fps',       label: 'FPS (帧/秒)' },
                { key: 'gpu',       label: 'GPU 使用率 (%)' },
                { key: 'threads',   label: '线程数 (个)' },
                { key: 'handles',   label: '句柄数 (个)' },
                { key: 'disk_read', label: '磁盘读取 (MB/s)' },
                { key: 'disk_write',label: '磁盘写入 (MB/s)' },
                { key: 'net_sent',  label: '网络发送 (MB/s)' },
                { key: 'net_recv',  label: '网络接收 (MB/s)' },
            ],
            // 标签对比
            allLabelsWithTask: [],              // [{...label, _task_name, _task_version}]
            selectedLabelsForCompare: [],       // 已选标签列表

            // ── 图表对话框 - 标签 ──
            brushRange: { start_ts: null, end_ts: null },
            currentLabels: [],
            labelPanelVisible: false,
            saveLabelDialogVisible: false,
            newLabelForm: { name: '', note: '', color: '#3b6ef0' },
            labelColors: ['#3b6ef0','#52c41a','#fa8c16','#f5222d','#722ed1','#13c2c2','#eb2f96','#fadb14'],

            // ── 高级分析 ──
            advancedDialogVisible: false,
            advancedAnalysisResult: null,
            advancedCharts: {},

            // ── 报告管理 ──
            comparisonReports: [],
        };
    },

    computed: {
        runningTaskCount() {
            return this.tasks.filter(t => t.status == 1).length;
        },
        // 按任务分组的标签列表（用于标签对比页）
        labelGroups() {
            const map = {};
            for (const lb of this.allLabelsWithTask) {
                const tid = lb.task_id;
                if (!map[tid]) {
                    map[tid] = {
                        task_id: tid,
                        task_name: lb._task_name || '未知任务',
                        task_version: lb._task_version || '',
                        labels: []
                    };
                }
                map[tid].labels.push(lb);
            }
            return Object.values(map);
        }
    },

    watch: {
        compareDialogVisible(val) {
            if (!val) {
                Object.values(this.comparisonCharts).forEach(c => c && c.dispose && c.dispose());
                this.comparisonCharts = {};
                if (this.resizeComparisonCharts) window.removeEventListener('resize', this.resizeComparisonCharts);
            }
        },
        advancedDialogVisible(val) {
            if (!val) {
                Object.values(this.advancedCharts).forEach(c => c && c.dispose && c.dispose());
                this.advancedCharts = {};
                window.removeEventListener('resize', this.resizeAdvancedCharts);
            }
        }
    },

    methods: {
        // ══════════════════════════════════════
        //  视图切换
        // ══════════════════════════════════════
        switchView(view) {
            this.currentView = view;
            switch (view) {
                case 'taskList':
                    this.getAllTask();
                    break;
                case 'createTask':
                    this.refreshDevices();
                    this.get_system_info();
                    if (this.currentDeviceType === 'pc') {
                        this.get_pids();
                        this.getProcessTree();
                    }
                    this.newTask = { name: '任务-' + this.getCurrentTimestamp(), pid: null };
                    this.cmd = '';
                    this.pid_img = '';
                    this.selectedApp = null;
                    break;
                case 'compareTask':
                    this.getAllTask();
                    this.loadAllLabels();
                    break;
                case 'reportManagement':
                    this.getComparisonReports();
                    break;
            }
        },

        getCurrentTimestamp() {
            const d = new Date();
            const pad = n => String(n).padStart(2, '0');
            return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}-${pad(d.getHours())}-${pad(d.getMinutes())}-${pad(d.getSeconds())}`;
        },

        // ══════════════════════════════════════
        //  设备管理
        // ══════════════════════════════════════
        async refreshDevices() {
            this.isRefreshingDevices = true;
            try {
                const res = await axios.get('/get_devices/');
                if (res.data && res.data.msg) {
                    this.connectedDevices = res.data.msg.map(d => {
                        d._key = d.device_type + '_' + (d.serial || d.udid || 'local');
                        return d;
                    });
                    if (!this.connectedDevices.find(d => d._key === this.selectedDeviceKey)) {
                        if (this.connectedDevices.length > 0) {
                            this.selectedDeviceKey = this.connectedDevices[0]._key;
                            this.onDeviceChange(this.selectedDeviceKey);
                        }
                    }
                }
            } catch (e) {
                console.error('获取设备列表失败:', e);
            } finally {
                this.isRefreshingDevices = false;
            }
        },

        onDeviceChange(deviceKey) {
            const device = this.connectedDevices.find(d => d._key === deviceKey);
            if (!device) return;
            this.currentDeviceType = device.device_type;
            this.currentDeviceId = device.serial || device.udid || null;
            this.selectedApp = null;
            this.deviceApps = [];
            this.cmd = '';
            this.pid_img = '';
            this.get_system_info();
            if (this.currentDeviceType === 'pc') {
                this.get_pids();
                this.getProcessTree();
            } else {
                this.refreshDeviceApps();
            }
        },

        async refreshDeviceApps() {
            if (this.currentDeviceType === 'pc') return;
            try {
                const res = await axios.get(`/get_device_apps/?device_type=${this.currentDeviceType}&device_id=${encodeURIComponent(this.currentDeviceId)}`);
                if (res.data && res.data.msg) this.deviceApps = res.data.msg;
            } catch (e) {
                this.$message.error('获取应用列表失败');
            }
        },

        async getMobileScreenshot() {
            try {
                const res = await axios.get(`/pid_img/?device_type=${this.currentDeviceType}&device_id=${encodeURIComponent(this.currentDeviceId)}`);
                if (res.data) this.pid_img = 'data:image/png;base64,' + res.data;
            } catch (e) {
                this.$message.error('获取截图失败');
            }
        },

        // ══════════════════════════════════════
        //  进程管理
        // ══════════════════════════════════════
        async get_pids() {
            const res = await axios.get('/get_pids/');
            this.pids = res.data.msg;
        },

        async get_system_info() {
            try {
                let url = '/system_info/?device_type=' + this.currentDeviceType;
                if (this.currentDeviceId) url += '&device_id=' + encodeURIComponent(this.currentDeviceId);
                const res = await axios.get(url);
                if (res.data && res.data.msg) this.currentSystemInfo = res.data.msg;
            } catch (e) {
                console.error('获取系统信息失败:', e);
            }
        },

        async getProcessTree() {
            const res = await axios.get('/get_pids/?is_print_tree=true');
            this.processTree = res.data.msg;
        },

        async refreshProcessTree() {
            this.isRefreshingProcessTree = true;
            try {
                await Promise.all([this.get_pids(), this.getProcessTree()]);
                this.$message.success('进程树刷新成功');
            } catch (e) {
                this.$message.error('刷新进程树失败');
            } finally {
                this.isRefreshingProcessTree = false;
            }
        },

        async handleNodeClick(data) {
            await this.get_pids();
            this.newTask.pid = data.pid;
            this.pid_name = data.name || '';
            await this.getPidCmd();
        },

        async getPidCmd() {
            if (!this.pid_name) {
                const found = this.pids.find(p => p.pid === this.newTask.pid);
                if (found) { this.cmd = found.cmd.join(' '); this.pid_name = found.name; }
            } else {
                const found = this.pids.find(p => p.pid === this.newTask.pid);
                if (found) this.cmd = found.cmd.join(' ');
            }
            await this.getPidImg();
        },

        async getPidImg() {
            const res = await axios.get('/pid_img/?pid=' + this.newTask.pid);
            this.pid_img = 'data:image/png;base64,' + res.data;
        },

        isTaskSelected(task) {
            return this.selectedTasksForCompare.some(t => t.id === task.id);
        },

        // ══════════════════════════════════════
        //  任务 CRUD
        // ══════════════════════════════════════
        async getAllTask() {
            const res = await axios.get('/get_all_task/');
            this.tasks = res.data.msg;
        },

        async createTask() {
            if (this.currentDeviceType === 'android' || this.currentDeviceType === 'ios') {
                if (!this.selectedApp) { this.$message.error('请先选择要测试的应用'); return; }
                try {
                    const pkg = this.selectedApp.package_name || this.selectedApp.bundle_id;
                    const url = `/run_task/?pid=${this.selectedApp.pid||0}&task_name=${encodeURIComponent(this.newTask.name)}&pid_name=${encodeURIComponent(this.selectedApp.name||pkg)}&include_child=false&device_type=${this.currentDeviceType}&device_id=${encodeURIComponent(this.currentDeviceId)}&package_name=${encodeURIComponent(pkg)}`;
                    const res = await axios.get(url);
                    this.print_res(res);
                    await this.getAllTask();
                    this.switchView('taskList');
                } catch (e) {
                    this.$message.error('创建任务失败: ' + (e.response?.data?.msg || e.message));
                }
            } else if (this.newTask.pid) {
                try {
                    const url = `/run_task/?pid=${this.newTask.pid}&task_name=${encodeURIComponent(this.newTask.name)}&pid_name=${encodeURIComponent(this.pid_name)}&include_child=${this.include_child}&device_type=pc`;
                    const res = await axios.get(url);
                    this.print_res(res);
                    await this.getAllTask();
                    this.switchView('taskList');
                } catch (e) {
                    this.$message.error('创建任务失败: ' + (e.response?.data?.msg || e.message));
                }
            } else {
                this.$message.error('未选择 PID');
            }
        },

        async stop_task(taskId) {
            const res = await axios.get('/stop_task/?task_id=' + taskId);
            this.print_res(res);
            await this.getAllTask();
        },

        async task_status(taskId) {
            const res = await axios.get('/task_status/?task_id=' + taskId);
            return parseInt(res.data.msg);
        },

        async delete_task(taskId) {
            try {
                await this.$confirm('确定要删除此任务吗？此操作不可撤销。', '警告', {
                    confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
                });
                const res = await axios.get('/delete_task/?task_id=' + taskId);
                this.print_res(res);
                await this.getAllTask();
            } catch (e) {
                if (e !== 'cancel' && e !== 'close') console.error(e);
            }
        },

        async saveEdit(row) {
            try {
                const res = await axios.get('/change_task_name/?task_id=' + row.id + '&new_name=' + row.name);
                this.print_res(res);
            } catch (e) { console.error(e); }
        },

        async saveVersion(row) {
            try {
                const res = await axios.get(`/set_task_version/?task_id=${row.id}&version=${encodeURIComponent(row.version||'')}`);
                this.print_res(res);
            } catch (e) { this.$message.error('设置版本失败'); }
        },

        async exportExcel(taskId) {
            window.open(`/export_excel/?task_id=${taskId}`, '_blank');
            this.$message.success('正在导出 Excel 报表…');
        },

        print_res(res) {
            if (res.data.code == 200) {
                this.$message({ showClose: true, message: res.data.msg, type: 'success' });
            } else {
                this.$message.error(res.data.msg);
            }
        },

        // ══════════════════════════════════════
        //  图表数据处理
        // ══════════════════════════════════════
        num2time(ts) {
            if (!ts) return '';
            return new Date(ts * 1000).toLocaleTimeString();
        },

        _baseChartOption() {
            return {
                legend: { show: true },
                tooltip: {
                    trigger: 'axis',
                    axisPointer: { type: 'cross', crossStyle: { color: '#999' } }
                },
                toolbox: {
                    feature: {
                        brush: { type: ['lineX', 'clear'], title: { lineX: '选区', clear: '清除' } }
                    },
                    right: 60
                },
                xAxis: {
                    type: 'category',
                    boundaryGap: false,
                    data: [],
                    axisLabel: { formatter: ts => new Date(ts * 1000).toLocaleTimeString() }
                },
                yAxis: { type: 'value' },
                series: [],
                brush: {
                    xAxisIndex: 'all', brushLink: 'all',
                    brushStyle: { borderWidth: 2, color: 'rgba(59,110,240,.15)', borderColor: '#3b6ef0' }
                },
                dataZoom: { type: 'slider', start: 0, end: 100, height: 14 },
                showSymbol: false,
                lineStyle: { width: 1.5 },
            };
        },

        async result(taskId, silent) {
            const res = await axios.get('/result/?task_id=' + taskId);
            this.opentaskid = taskId;
            const allRes = {};
            res.data.msg.forEach(x => {
                const opt = JSON.parse(JSON.stringify(this._baseChartOption()));
                opt.xAxis.axisLabel.formatter = ts => new Date(ts * 1000).toLocaleTimeString();
                const tl = [], cpu = [], cpuAll = [], fps = [], gpu = [], mem = [],
                      threads = [], handles = [], dr = [], dw = [], ns = [], nr = [],
                      bl = [], bt = [];
                x.value.forEach(v => {
                    const t = v['time'];
                    if (x.name === 'cpu') {
                        tl.push(t);
                        cpu.push(v['cpu_usage(%)'] ?? '-');
                        cpuAll.push(v['cpu_usage_all(%)'] ?? '-');
                    } else if (x.name === 'fps') {
                        tl.push(t); fps.push(v['fps(帧)'] ?? '-');
                    } else if (x.name === 'gpu') {
                        tl.push(t); gpu.push(v['gpu(%)'] ?? '-');
                    } else if (x.name === 'memory') {
                        tl.push(t); mem.push(v['process_memory_usage(M)'] ?? '-');
                    } else if (x.name === 'process_info') {
                        tl.push(t);
                        handles.push(v['num_handles(个)'] ?? '-');
                        threads.push(v['num_threads(个)'] ?? '-');
                    } else if (x.name === 'disk_io') {
                        tl.push(t);
                        dr.push(v['disk_read_rate(MB/s)'] ?? '-');
                        dw.push(v['disk_write_rate(MB/s)'] ?? '-');
                    } else if (x.name === 'network_io') {
                        tl.push(t);
                        ns.push(v['net_sent_rate(MB/s)'] ?? '-');
                        nr.push(v['net_recv_rate(MB/s)'] ?? '-');
                    } else if (x.name === 'battery') {
                        tl.push(t);
                        bl.push(v['battery_level(%)'] ?? '-');
                        bt.push(v['battery_temperature(℃)'] ?? '-');
                    }
                });
                opt.xAxis.data = tl;
                if (x.name === 'cpu') {
                    opt.series.push({ name: 'CPU(%)', type: 'line', data: cpu, connectNulls: true, areaStyle: { opacity: .2 } });
                    opt.series.push({ name: 'CPU全核(%)', type: 'line', data: cpuAll, connectNulls: true, areaStyle: { opacity: .1 } });
                } else if (x.name === 'fps') {
                    opt.series.push({ name: 'FPS', type: 'line', data: fps, connectNulls: true });
                } else if (x.name === 'gpu') {
                    opt.series.push({ name: 'GPU(%)', type: 'line', data: gpu, connectNulls: true, areaStyle: { opacity: .2 } });
                } else if (x.name === 'memory') {
                    opt.series.push({ name: '内存(MB)', type: 'line', data: mem, connectNulls: true, areaStyle: { opacity: .2 } });
                } else if (x.name === 'process_info') {
                    opt.series.push({ name: '句柄数', type: 'line', data: handles, connectNulls: true });
                    opt.series.push({ name: '线程数', type: 'line', data: threads, connectNulls: true });
                } else if (x.name === 'disk_io') {
                    opt.series.push({ name: '读取(MB/s)', type: 'line', data: dr, connectNulls: true, areaStyle: { opacity: .15 } });
                    opt.series.push({ name: '写入(MB/s)', type: 'line', data: dw, connectNulls: true, areaStyle: { opacity: .15 } });
                } else if (x.name === 'network_io') {
                    opt.series.push({ name: '发送(MB/s)', type: 'line', data: ns, connectNulls: true, areaStyle: { opacity: .15 } });
                    opt.series.push({ name: '接收(MB/s)', type: 'line', data: nr, connectNulls: true, areaStyle: { opacity: .15 } });
                } else if (x.name === 'battery') {
                    opt.yAxis = [
                        { type: 'value', name: '电量(%)', min: 0, max: 100 },
                        { type: 'value', name: '温度(℃)', position: 'right' }
                    ];
                    opt.series.push({ name: '电量(%)', type: 'line', yAxisIndex: 0, data: bl, connectNulls: true, areaStyle: { opacity: .2 }, itemStyle: { color: '#52c41a' } });
                    opt.series.push({ name: '温度(℃)', type: 'line', yAxisIndex: 1, data: bt, connectNulls: true, itemStyle: { color: '#fa8c16' } });
                }
                allRes[x.name] = opt;
            });
            this.resultOption = allRes;
            if (!silent) this.dialogVisible = true;
        },

        // ══════════════════════════════════════
        //  图表渲染 & 联动
        // ══════════════════════════════════════
        handleDialogVisibleChange() {
            this.$nextTick(() => {
                const allRes = this.resultOption;
                const ids = ['cpuChart','memoryChart','fpsChart','gpuChart','processInfoChart','diskIOChart','networkIOChart'];
                const names = ['cpu','memory','fps','gpu','process_info','disk_io','network_io'];
                const charts = {};
                ids.forEach((id, i) => {
                    const el = document.getElementById(id);
                    if (el) {
                        const c = echarts.init(el);
                        if (allRes[names[i]]) c.setOption(allRes[names[i]]);
                        charts[names[i]] = c;
                        this[id.replace('Chart','') + 'Chart'] = c;
                    }
                });
                // 直接赋值实例
                this.cpuChart = charts['cpu'];
                this.memoryChart = charts['memory'];
                this.fpsChart = charts['fps'];
                this.gpuChart = charts['gpu'];
                this.processInfoChart = charts['process_info'];
                this.diskIOChart = charts['disk_io'];
                this.networkIOChart = charts['network_io'];

                // 电池（移动端）
                const batteryContainer = document.getElementById('batteryChartContainer');
                if (allRes['battery'] && allRes['battery'].series && allRes['battery'].series.length > 0) {
                    batteryContainer.style.display = '';
                    this.batteryChart = echarts.init(document.getElementById('batteryChart'));
                    this.batteryChart.setOption(allRes['battery']);
                } else {
                    batteryContainer.style.display = 'none';
                    this.batteryChart = null;
                }

                // 联动：点击时间轴同步截图
                const allChartList = Object.values(charts).filter(Boolean);
                if (this.batteryChart) allChartList.push(this.batteryChart);
                allChartList.forEach(c => this.syncDataZoom(c, allChartList.filter(x => x !== c)));

                // brush 区间选择：监听第一个有效图表的 brushEnd 事件
                const firstChart = allChartList[0];
                if (firstChart) {
                    firstChart.on('brushEnd', params => {
                        const areas = params.areas;
                        if (!areas || areas.length === 0) {
                            this.brushRange = { start_ts: null, end_ts: null };
                            return;
                        }
                        const area = areas[0];
                        if (!area.coordRange || area.coordRange.length < 2) return;
                        // coordRange 对于 category xAxis 是 index 范围
                        const xData = firstChart.getOption().xAxis[0].data;
                        if (!xData) return;
                        const i0 = Math.round(area.coordRange[0]);
                        const i1 = Math.round(area.coordRange[1]);
                        const s = xData[Math.max(0, Math.min(i0, xData.length - 1))];
                        const e = xData[Math.max(0, Math.min(i1, xData.length - 1))];
                        this.brushRange = { start_ts: s, end_ts: e };
                    });
                    // 同步 brush 到其他图表
                    firstChart.on('brush', params => {
                        allChartList.slice(1).forEach(c => {
                            c && c.dispatchAction({ type: 'brush', areas: params.areas });
                        });
                    });
                }
            });

            // 加载当前任务标签
            this.loadLabels();

            this.intervalId = setInterval(() => {
                this.task_status(this.curtaskid).then(s => {
                    if (s !== 2 && s !== -1 && !this.isloadres) this.reloadchart();
                });
            }, 3000);
        },

        syncDataZoom(chartInstance, otherCharts) {
            chartInstance.getZr().on('click', params => {
                const pt = [params.offsetX, params.offsetY];
                if (!chartInstance.containPixel('grid', pt)) return;
                const xIdx = chartInstance.convertFromPixel({ seriesIndex: 0 }, pt)[0];
                const xData = chartInstance.getOption().xAxis[0].data;
                this.clicktime = xData[xIdx];
                const brushAction = {
                    type: 'brush',
                    areas: [{ brushType: 'lineX', coordRange: [xIdx, xIdx], xAxisIndex: 0, transformable: false }]
                };
                chartInstance.dispatchAction(brushAction);
                otherCharts.forEach(c => c && c.dispatchAction(brushAction));
            });
        },

        onDetailDialogClosed() {
            // 重置标签相关状态
            this.brushRange = { start_ts: null, end_ts: null };
            this.currentLabels = [];
            this.labelPanelVisible = false;
            this.saveLabelDialogVisible = false;
            // 清除轮询
            if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; }
        },

        async reloadchart() {
            this.isloadres = true;
            await this.result(this.curtaskid, true);
            const allRes = this.resultOption;
            if (this.cpuChart) this.cpuChart.setOption(allRes['cpu']);
            if (this.memoryChart) this.memoryChart.setOption(allRes['memory']);
            if (this.fpsChart) this.fpsChart.setOption(allRes['fps']);
            if (this.gpuChart) this.gpuChart.setOption(allRes['gpu']);
            if (this.processInfoChart) this.processInfoChart.setOption(allRes['process_info']);
            if (this.diskIOChart) this.diskIOChart.setOption(allRes['disk_io']);
            if (this.networkIOChart) this.networkIOChart.setOption(allRes['network_io']);
            const batteryContainer = document.getElementById('batteryChartContainer');
            if (allRes['battery'] && allRes['battery'].series && allRes['battery'].series.length > 0) {
                batteryContainer.style.display = '';
                if (!this.batteryChart) this.batteryChart = echarts.init(document.getElementById('batteryChart'));
                this.batteryChart.setOption(allRes['battery']);
            } else {
                batteryContainer.style.display = 'none';
            }
            this.isloadres = false;
        },

        // ══════════════════════════════════════
        //  区间标签
        // ══════════════════════════════════════

        /** 加载当前图表对话框任务的标签 */
        async loadLabels() {
            if (!this.opentaskid) return;
            try {
                const res = await axios.get(`/get_labels/?task_id=${this.opentaskid}`);
                if (res.data.code === 200) {
                    this.currentLabels = res.data.msg || [];
                }
            } catch (e) { /* ignore */ }
        },

        /** 加载所有任务的标签（用于对比页） */
        async loadAllLabels() {
            try {
                const res = await axios.get('/get_labels/');
                if (res.data.code === 200) {
                    const labels = res.data.msg || [];
                    // 附加任务名称
                    this.allLabelsWithTask = labels.map(lb => {
                        const task = this.tasks.find(t => t.id === lb.task_id);
                        return {
                            ...lb,
                            _task_name: task ? task.name : `任务 #${lb.task_id}`,
                            _task_version: task ? task.version : ''
                        };
                    });
                }
            } catch (e) { /* ignore */ }
        },

        showSaveLabelDialog() {
            if (!this.brushRange.start_ts) {
                this.$message.warning('请先在图表上拖拽选择区间');
                return;
            }
            this.newLabelForm = { name: '', note: '', color: '#3b6ef0' };
            this.saveLabelDialogVisible = true;
        },

        async saveLabel() {
            if (!this.newLabelForm.name) { this.$message.warning('请输入标签名称'); return; }
            try {
                const res = await axios.post('/create_label/', {
                    task_id: this.opentaskid,
                    name: this.newLabelForm.name,
                    note: this.newLabelForm.note,
                    color: this.newLabelForm.color,
                    start_ts: this.brushRange.start_ts,
                    end_ts: this.brushRange.end_ts
                });
                if (res.data.code === 200) {
                    this.$message.success('标签已保存');
                    this.saveLabelDialogVisible = false;
                    this.brushRange = { start_ts: null, end_ts: null };
                    await this.loadLabels();
                } else {
                    this.$message.error(res.data.msg || '保存失败');
                }
            } catch (e) {
                this.$message.error('保存标签失败');
            }
        },

        async deleteLabel(lb) {
            try {
                await this.$confirm(`确认删除标签「${lb.name}」？`, '提示', { type: 'warning' });
                const res = await axios.delete(`/delete_label/?label_id=${lb.id}`);
                if (res.data.code === 200) {
                    this.$message.success('已删除');
                    await this.loadLabels();
                }
            } catch (e) { /* cancel or error */ }
        },

        /** 标签对比页：切换选中状态 */
        toggleLabelSelection(lb) {
            const idx = this.selectedLabelsForCompare.findIndex(l => l.id === lb.id);
            if (idx > -1) {
                this.selectedLabelsForCompare.splice(idx, 1);
            } else {
                this.selectedLabelsForCompare.push(lb);
            }
        },

        isLabelSelected(lb) {
            return this.selectedLabelsForCompare.some(l => l.id === lb.id);
        },

        /** 发起标签对比 */
        async compareLabelsTasks() {
            if (this.selectedLabelsForCompare.length < 2) {
                this.$message.error('请至少选择两个标签');
                return;
            }
            try {
                const ids = this.selectedLabelsForCompare.map(l => l.id).join(',');
                const res = await axios.get(`/compare_labels/?label_ids=${ids}`);
                if (res.data.code === 200) {
                    Object.values(this.comparisonCharts).forEach(c => c && c.dispose && c.dispose());
                    this.comparisonCharts = {};
                    this.comparisonResult = res.data.msg;
                    // 将标签的详细数据添加到 selectedLabelsForCompare 中
                    if (this.comparisonResult.tasks) {
                        this.comparisonResult.tasks.forEach(task => {
                            const label = this.selectedLabelsForCompare.find(l => l.id === task.label_id);
                            if (label) {
                                label.data = task.data;
                                label._task_name = task.name;
                            }
                        });
                    }
                    this.compareDialogVisible = true;
                    this.$nextTick(() => this.renderComparisonCharts());
                } else {
                    this.$message.error(res.data.msg || '标签对比失败');
                }
            } catch (e) {
                this.$message.error('标签对比请求失败');
            }
        },

        // ══════════════════════════════════════
        //  对比任务
        // ══════════════════════════════════════
        toggleTaskSelection(task) {
            const idx = this.selectedTasksForCompare.findIndex(t => t.id === task.id);
            if (idx > -1) {
                this.selectedTasksForCompare.splice(idx, 1);
            } else {
                this.selectedTasksForCompare.push(task);
            }
            this.baseTaskId = this.selectedTasksForCompare.length > 0 ? this.selectedTasksForCompare[0].id : null;
        },

        async compareTasks() {
            if (this.selectedTasksForCompare.length < 2) { this.$message.error('请至少选择两个任务'); return; }
            try {
                const ids = this.selectedTasksForCompare.map(t => t.id).join(',');
                const res = await axios.get(`/compare_tasks/?task_ids=${ids}&base_task_id=${this.baseTaskId}`);
                if (res.data.code === 200) {
                    Object.values(this.comparisonCharts).forEach(c => c && c.dispose && c.dispose());
                    this.comparisonCharts = {};
                    this.comparisonResult = res.data.msg;
                    this.compareDialogVisible = true;
                    this.$nextTick(() => this.renderComparisonCharts());
                } else {
                    this.$message.error(res.data.msg || '对比失败');
                }
            } catch (e) {
                this.$message.error('对比任务失败');
            }
        },

        renderComparisonCharts() {
            const baseTaskId = (this.comparisonResult.base_task || {}).id;
            window.removeEventListener('resize', this.resizeComparisonCharts);
            this.resizeComparisonCharts = () => {
                Object.values(this.comparisonCharts).forEach(c => c && !c.isDisposed() && c.resize());
            };
            window.addEventListener('resize', this.resizeComparisonCharts);

            this.compareMetrics.forEach(m => {
                this.$nextTick(() => {
                    const el = document.getElementById('compare_' + m.key + '_chart');
                    if (!el) return;
                    if (this.comparisonCharts[m.key]) this.comparisonCharts[m.key].dispose();
                    const chart = echarts.init(el);
                    this.comparisonCharts[m.key] = chart;

                    // 检查是否是标签对比
                    const isLabelCompare = this.selectedLabelsForCompare.length > 0;
                    
                    if (isLabelCompare) {
                        // 标签对比：展示详细数据曲线
                        const labels = this.selectedLabelsForCompare;
                        
                        // 检查是否有有效的标签数据
                        const validLabels = labels.filter(lb => lb.data && lb.data.timestamps && lb.data.timestamps.length > 0);
                        
                        if (validLabels.length === 0) {
                            // 没有有效数据，显示提示信息
                            chart.setOption({
                                title: {
                                    text: '暂无标签数据',
                                    left: 'center',
                                    top: 'center'
                                }
                            });
                            return;
                        }
                        
                        // 生成统一的时间轴数据
                        let allTimestamps = [];
                        validLabels.forEach(lb => {
                            if (lb.data && lb.data.timestamps) {
                                allTimestamps = allTimestamps.concat(lb.data.timestamps);
                            }
                        });
                        // 去重并排序
                        allTimestamps = [...new Set(allTimestamps)].sort((a, b) => a - b);
                        
                        const series = validLabels.map((lb, index) => {
                            const task = this.tasks.find(t => t.id === lb.task_id);
                            const taskName = task ? task.name : `任务 #${lb.task_id}`;
                            const labelName = `${taskName} - ${lb.name}`;
                            
                            // 获取标签的详细数据
                            const labelData = (lb.data && lb.data[m.key]) || [];
                            
                            // 确保数据点长度与时间戳长度匹配
                            const data = [];
                            for (let i = 0; i < allTimestamps.length; i++) {
                                const timestamp = allTimestamps[i];
                                const idx = lb.data.timestamps.indexOf(timestamp);
                                if (idx !== -1 && idx < labelData.length) {
                                    data.push(labelData[idx]);
                                } else {
                                    data.push(null);
                                }
                            }
                            
                            return {
                                name: labelName,
                                type: 'line',
                                data: data,
                                smooth: true,
                                symbol: 'none',
                                lineStyle: { width: 2 },
                                itemStyle: {
                                    color: lb.color || this.labelColors[index % this.labelColors.length]
                                },
                                areaStyle: {
                                    opacity: 0.1
                                }
                            };
                        });

                        chart.setOption({
                            tooltip: { 
                                trigger: 'axis',
                                axisPointer: { type: 'cross' }
                            },
                            legend: { bottom: 0, type: 'scroll' },
                            grid: { left: '5%', right: '5%', bottom: '25%', top: '10%', containLabel: true },
                            xAxis: {
                                type: 'category',
                                data: allTimestamps,
                                axisLabel: { 
                                    formatter: ts => new Date(ts * 1000).toLocaleTimeString()
                                }
                            },
                            yAxis: { type: 'value' },
                            series
                        });
                    } else {
                        // 任务对比：展示详细数据曲线
                        const tasks = [...(this.comparisonResult.tasks || [])].sort((a, b) =>
                            a.id === baseTaskId ? -1 : b.id === baseTaskId ? 1 : 0);

                        // 检查是否有有效的任务数据
                        const validTasks = tasks.filter(t => t.data && t.data[m.key] && t.data[m.key].length > 0);

                        if (validTasks.length === 0) {
                            // 没有有效数据，显示提示信息
                            chart.setOption({
                                title: {
                                    text: '暂无任务数据',
                                    left: 'center',
                                    top: 'center'
                                }
                            });
                            return;
                        }

                        // 生成统一的时间轴数据
                        let allTimestamps = [];
                        validTasks.forEach(task => {
                            if (task.data && task.data[m.key]) {
                                const taskData = task.data[m.key];
                                if (taskData.length > 0) {
                                    // 提取时间戳
                                    const timestamps = taskData.map(item => item.time);
                                    allTimestamps = allTimestamps.concat(timestamps);
                                }
                            }
                        });
                        // 去重并排序
                        allTimestamps = [...new Set(allTimestamps)].sort((a, b) => a - b);

                        const series = validTasks.map((task, index) => {
                            const taskName = task.id === baseTaskId ? `${task.name} ${task.version||''} (基准)` : `${task.name} ${task.version||''}`;

                            // 获取任务的详细数据
                            const taskData = (task.data && task.data[m.key]) || [];

                            // 确保数据点长度与时间戳长度匹配
                            const data = [];
                            for (let i = 0; i < allTimestamps.length; i++) {
                                const timestamp = allTimestamps[i];
                                const dataPoint = taskData.find(item => item.time === timestamp);
                                if (dataPoint) {
                                    // 直接使用标准化的 value 字段
                                    data.push(dataPoint.value);
                                } else {
                                    data.push(null);
                                }
                            }

                            return {
                                name: taskName,
                                type: 'line',
                                data: data,
                                smooth: true,
                                symbol: 'none',
                                lineStyle: { width: 2 },
                                areaStyle: {
                                    opacity: 0.1
                                },
                                itemStyle: {
                                    color: task.id === baseTaskId ? '#3b6ef0' : `hsl(${index * 60}, 70%, 60%)`
                                }
                            };
                        });

                        chart.setOption({
                            tooltip: { 
                                trigger: 'axis',
                                axisPointer: { type: 'cross' }
                            },
                            legend: { bottom: 0, type: 'scroll' },
                            grid: { left: '5%', right: '5%', bottom: '25%', top: '10%', containLabel: true },
                            xAxis: {
                                type: 'category',
                                data: allTimestamps,
                                axisLabel: { 
                                    formatter: ts => new Date(ts * 1000).toLocaleTimeString()
                                }
                            },
                            yAxis: { type: 'value' },
                            series
                        });
                    }
                    
                    setTimeout(() => chart && !chart.isDisposed() && chart.resize(), 200);
                });
            });
        },

        async exportComparisonExcel() {
            // 检查是否是标签对比
            const isLabelCompare = this.selectedLabelsForCompare.length > 0;
            
            if (isLabelCompare) {
                // 标签对比导出
                const labelIds = this.selectedLabelsForCompare.map(l => l.id).join(',');
                window.open(`/export_label_comparison_excel/?label_ids=${labelIds}`, '_blank');
                this.$message.success('正在导出标签对比报表…');
            } else {
                // 任务对比导出
                const ids = this.selectedTasksForCompare.map(t => t.id).join(',');
                window.open(`/export_comparison_excel/?task_ids=${ids}&base_task_id=${this.baseTaskId}`, '_blank');
                this.$message.success('正在导出对比报表…');
            }
        },

        // ══════════════════════════════════════
        //  高级分析
        // ══════════════════════════════════════
        async performAdvancedAnalysis() {
            if (this.selectedTasksForCompare.length !== 2) { this.$message.error('高级分析需要选择两个任务'); return; }
            try {
                this.$message.info('正在进行高级分析，请稍候…');
                const ids = this.selectedTasksForCompare.map(t => t.id).join(',');
                const res = await axios.get(`/advanced_compare_tasks/?task_ids=${ids}&base_task_id=${this.baseTaskId}`);
                if (res.data.code === 200) {
                    this.advancedAnalysisResult = res.data.msg;
                    this.advancedDialogVisible = true;
                    this.$nextTick(() => this.renderAdvancedAnalysisCharts());
                } else {
                    this.$message.error(res.data.msg || '高级分析失败');
                }
            } catch (e) {
                this.$message.error('高级分析失败: ' + (e.message || '未知错误'));
            }
        },

        handleAdvancedDialogOpen() {
            if (this.advancedAnalysisResult && this.advancedAnalysisResult.advanced_analysis) {
                setTimeout(() => this.renderAdvancedAnalysisCharts(), 300);
            }
        },

        renderAdvancedAnalysisCharts() {
            if (!this.advancedAnalysisResult || !this.advancedAnalysisResult.advanced_analysis) return;
            Object.values(this.advancedCharts).forEach(c => c && c.dispose && c.dispose());
            this.advancedCharts = {};
            window.removeEventListener('resize', this.resizeAdvancedCharts);
            this.resizeAdvancedCharts = () => {
                Object.values(this.advancedCharts).forEach(c => c && !c.isDisposed() && c.resize());
            };
            window.addEventListener('resize', this.resizeAdvancedCharts);
            this.renderSignificanceChart();
            this.renderBottlenecksChart();
        },

        renderSignificanceChart() {
            const sig = this.advancedAnalysisResult.advanced_analysis.statistical_significance;
            if (!sig) return;
            const el = document.getElementById('advanced_significance_chart');
            if (!el) return;
            const chart = echarts.init(el);
            this.advancedCharts.significance = chart;
            const metrics = Object.keys(sig);
            chart.setOption({
                tooltip: {
                    trigger: 'axis',
                    formatter: params => {
                        const m = metrics[params[0].dataIndex];
                        const d = sig[m];
                        return `<b>${params[0].name}</b><br>置信度: ${d.confidence.toFixed(2)}%<br>p值: ${d.p_value.toFixed(4)}<br>显著: ${d.is_significant ? '✅' : '❌'}`;
                    }
                },
                grid: { left: '5%', right: '5%', bottom: '15%', top: '10%', containLabel: true },
                xAxis: { type: 'category', data: metrics.map(m => this.getMetricDisplayName(m)), axisLabel: { interval: 0, rotate: 30 } },
                yAxis: { type: 'value', name: '置信度(%)', max: 100 },
                series: [{
                    type: 'bar',
                    data: metrics.map(m => sig[m].confidence || 0),
                    itemStyle: { color: p => p.value > 95 ? '#52c41a' : '#3b6ef0' },
                    markLine: { data: [{ yAxis: 95, lineStyle: { color: '#ef4444', type: 'dashed' }, label: { formatter: '显著性阈值' } }] }
                }]
            });
            setTimeout(() => chart && !chart.isDisposed() && chart.resize(), 200);
        },

        renderBottlenecksChart() {
            const bn = this.advancedAnalysisResult.advanced_analysis.bottleneck_analysis;
            if (!bn || !bn.potential_bottlenecks || !bn.potential_bottlenecks.length) return;
            const el = document.getElementById('advanced_bottlenecks_chart');
            if (!el) return;
            const chart = echarts.init(el);
            this.advancedCharts.bottlenecks = chart;
            const items = bn.potential_bottlenecks;
            chart.setOption({
                tooltip: { trigger: 'axis' },
                grid: { left: '5%', right: '5%', bottom: '15%', top: '10%', containLabel: true },
                xAxis: { type: 'category', data: items.map(b => this.getMetricDisplayName(b.metric)), axisLabel: { interval: 0, rotate: 30 } },
                yAxis: { type: 'value', name: '变化率(%)' },
                series: [{
                    type: 'bar',
                    data: items.map(b => b.percent_change),
                    itemStyle: {
                        color: p => {
                            const m = items[p.dataIndex].metric;
                            const smaller = ['cpu','memory','disk_write','disk_read','net_sent','net_recv'].includes(m);
                            return (smaller && p.value < 0) || (!smaller && p.value > 0) ? '#52c41a' : '#ef4444';
                        }
                    }
                }]
            });
            setTimeout(() => chart && !chart.isDisposed() && chart.resize(), 200);
        },

        getMetricDisplayName(key) {
            const map = {
                cpu: 'CPU使用率', memory: '内存', fps: 'FPS', gpu: 'GPU',
                threads: '线程数', handles: '句柄数',
                disk_read: '磁盘读取', disk_write: '磁盘写入',
                net_sent: '网络发送', net_recv: '网络接收'
            };
            return map[key] || key;
        },

        async exportAdvancedAnalysisExcel() {
            if (!this.advancedAnalysisResult) { this.$message.error('请先进行高级分析'); return; }
            const ids = this.selectedTasksForCompare.map(t => t.id).join(',');
            window.open(`/export_advanced_comparison_excel/?task_ids=${ids}&base_task_id=${this.baseTaskId}`, '_blank');
            this.$message.success('正在导出高级分析报表…');
        },

        // ══════════════════════════════════════
        //  报告管理
        // ══════════════════════════════════════
        async getComparisonReports() {
            try {
                const res = await axios.get('/get_comparison_reports/');
                if (res.data.code === 200) this.comparisonReports = res.data.msg || [];
                else this.$message.error(res.data.msg || '获取报告列表失败');
            } catch (e) {
                this.$message.error('获取报告列表失败');
            }
        },

        async deleteReport(reportId) {
            try {
                await this.$confirm('确定要删除此报告吗？', '警告', {
                    confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
                });
                const res = await axios.get(`/delete_comparison_report/?report_id=${reportId}`);
                if (res.data.code === 200) {
                    this.$message.success('报告已删除');
                    await this.getComparisonReports();
                } else {
                    this.$message.error(res.data.msg || '删除失败');
                }
            } catch (e) {
                if (e !== 'cancel' && e !== 'close') this.$message.error('删除报告失败');
            }
        },

        openReportFile(reportPath) {
            if (!reportPath) { this.$message.error('报告路径不存在'); return; }
            const fileName = reportPath.split(/[\/\\]/).pop();
            window.open(`/static/comparison_reports/${fileName}`, '_blank');
        },
    },

    // ══════════════════════════════════════
    //  生命周期
    // ══════════════════════════════════════
    async mounted() {
        await this.getAllTask();
        await this.refreshDevices();
        await this.get_system_info();
        await this.get_pids();
        await this.getProcessTree();

        // 每 5 秒刷新任务列表
        this.intervalId = setInterval(() => this.getAllTask(), 5000);
    },

    beforeDestroy() {
        if (this.intervalId) clearInterval(this.intervalId);
    },
});
</script>
</body>
</html>
