CoolFace
Apppublic

JiuTianDataAgent/NL2SQL

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
index.html899 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4    <meta charset="UTF-8">5    <meta name="viewport" content="width=device-width, initial-scale=1.0">6    <title>NL2SQL Leaderboard</title>7    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">8    <style>9        * {10            margin: 0;11            padding: 0;12            box-sizing: border-box;13            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;14        }15 16        body {17            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);18            min-height: 100vh;19            padding: 20px;20        }21 22        .container {23            max-width: 1400px;24            margin: 0 auto;25            background: #f8fafc;26            border-radius: 20px;27            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);28            overflow: hidden;29        }30 31        header {32            background: linear-gradient(to right, #1e40af, #3b82f6);33            color: white;34            padding: 30px 40px;35            text-align: center;36        }37 38        h1 {39            font-size: 2.8rem;40            margin-bottom: 10px;41            font-weight: 800;42        }43 44        .subtitle {45            font-size: 1.2rem;46            opacity: 0.9;47            margin-bottom: 20px;48        }49 50        .tabs {51            display: flex;52            background: rgba(255, 255, 255, 0.1);53            border-radius: 12px;54            padding: 5px;55            margin: 20px auto 0;56            max-width: 800px;57        }58 59        .tab {60            flex: 1;61            padding: 15px;62            text-align: center;63            font-weight: 600;64            font-size: 1.1rem;65            cursor: pointer;66            border-radius: 8px;67            transition: all 0.3s ease;68        }69 70        .tab:hover {71            background: rgba(255, 255, 255, 0.2);72        }73 74        .tab.active {75            background: white;76            color: #3b82f6;77            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);78        }79 80        main {81            padding: 30px;82        }83 84        .controls {85            background: white;86            padding: 30px;87            border-radius: 12px;88            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);89            margin-bottom: 30px;90        }91 92        .dataset-selector {93            margin-bottom: 30px;94            padding-bottom: 30px;95            border-bottom: 1px solid #e2e8f0;96        }97 98        .metric-selector {99            margin-bottom: 20px;100        }101 102        .dataset-label, .metric-label {103            font-weight: 600;104            color: #475569;105            font-size: 1.1rem;106            margin-bottom: 15px;107            display: flex;108            align-items: center;109            gap: 10px;110        }111 112        .dataset-label i, .metric-label i {113            color: #3b82f6;114        }115 116        .dataset-buttons {117            display: flex;118            flex-wrap: wrap;119            gap: 10px;120        }121 122        .dataset-btn {123            padding: 12px 24px;124            background: white;125            border: 2px solid #cbd5e1;126            border-radius: 8px;127            font-weight: 600;128            color: #475569;129            cursor: pointer;130            transition: all 0.2s ease;131        }132 133        .dataset-btn:hover {134            background: #f1f5f9;135            transform: translateY(-2px);136        }137 138        .dataset-btn.active {139            background: #3b82f6;140            color: white;141            border-color: #3b82f6;142            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);143        }144 145        .metric-checkboxes {146            display: grid;147            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));148            gap: 15px;149            margin-top: 10px;150        }151 152        .metric-checkbox {153            display: flex;154            align-items: center;155            gap: 12px;156            padding: 10px 15px;157            background: #f8fafc;158            border-radius: 8px;159            transition: all 0.2s ease;160            border: 1px solid #e2e8f0;161        }162 163        .metric-checkbox:hover {164            background: #f1f5f9;165            transform: translateY(-2px);166            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);167        }168 169        .metric-checkbox input {170            width: 18px;171            height: 18px;172            cursor: pointer;173            accent-color: #3b82f6;174        }175 176        .metric-checkbox label {177            font-weight: 500;178            color: #475569;179            cursor: pointer;180            font-size: 0.95rem;181            flex: 1;182        }183 184        .metric-hint {185            font-size: 0.85rem;186            color: #64748b;187            margin-top: 5px;188            font-style: italic;189        }190 191        .table-container {192            overflow-x: auto;193            border-radius: 12px;194            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);195            background: white;196            margin-bottom: 30px;197        }198 199        table {200            width: 100%;201            border-collapse: collapse;202            min-width: 1200px;203        }204 205        th {206            background: linear-gradient(to right, #1e40af, #3b82f6);207            color: white;208            text-align: left;209            padding: 18px 20px;210            font-weight: 600;211            font-size: 1.1rem;212            position: sticky;213            top: 0;214        }215 216        th.algorithm-header {217            min-width: 200px;218        }219 220        td {221            padding: 16px 20px;222            border-bottom: 1px solid #e2e8f0;223            font-size: 1rem;224        }225 226        tr:hover {227            background: #f8fafc;228        }229 230        tr:nth-child(even) {231            background: #f8fafc;232        }233 234        tr:nth-child(even):hover {235            background: #f1f5f9;236        }237 238        .algorithm-name {239            font-weight: 600;240            color: #1e293b;241            font-size: 1.1rem;242        }243 244        .metric-value {245            font-weight: 600;246            color: #0f172a;247            text-align: center;248        }249 250        .highlight {251            background: linear-gradient(120deg, #f0f9ff 0%, #e0f2fe 100%);252            border-left: 4px solid #0ea5e9;253        }254 255        .best-value {256            background: linear-gradient(120deg, #dcfce7 0%, #bbf7d0 100%);257            font-weight: 700;258            color: #166534;259            position: relative;260        }261 262        .best-value::after {263            content: "🏆";264            position: absolute;265            right: 5px;266            top: 50%;267            transform: translateY(-50%);268            font-size: 0.9rem;269        }270 271        .no-data {272            text-align: center;273            padding: 60px 20px;274            color: #64748b;275            font-size: 1.2rem;276        }277 278        .no-data i {279            font-size: 3rem;280            margin-bottom: 20px;281            opacity: 0.5;282        }283 284        /* Submit Data Styles */285        .submit-container {286            padding: 40px;287            text-align: center;288            background: white;289            border-radius: 12px;290            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);291            margin-bottom: 30px;292        }293 294        .submit-icon {295            font-size: 5rem;296            color: #cbd5e1;297            margin-bottom: 20px;298        }299 300        .submit-title {301            font-size: 2.5rem;302            color: #64748b;303            margin-bottom: 15px;304        }305 306        .submit-description {307            font-size: 1.2rem;308            color: #94a3b8;309            max-width: 600px;310            margin: 0 auto 30px;311            line-height: 1.6;312        }313 314        .coming-soon {315            display: inline-block;316            background: #f1f5f9;317            color: #64748b;318            padding: 10px 25px;319            border-radius: 30px;320            font-weight: 600;321            font-size: 1.1rem;322        }323 324        .submit-form {325            max-width: 600px;326            margin: 30px auto;327            background: #f8fafc;328            padding: 25px;329            border-radius: 12px;330            text-align: left;331        }332 333        .form-group {334            margin-bottom: 20px;335        }336 337        .form-group label {338            display: block;339            font-weight: 600;340            color: #475569;341            margin-bottom: 8px;342        }343 344        .form-group input,345        .form-group select,346        .form-group textarea {347            width: 100%;348            padding: 12px;349            border: 2px solid #cbd5e1;350            border-radius: 8px;351            font-size: 1rem;352            transition: border-color 0.2s ease;353        }354 355        .form-group input:focus,356        .form-group select:focus,357        .form-group textarea:focus {358            outline: none;359            border-color: #3b82f6;360        }361 362        .submit-btn {363            background: #3b82f6;364            color: white;365            border: none;366            padding: 12px 30px;367            border-radius: 8px;368            font-weight: 600;369            font-size: 1.1rem;370            cursor: pointer;371            transition: all 0.2s ease;372        }373 374        .submit-btn:hover {375            background: #2563eb;376            transform: translateY(-2px);377            box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);378        }379 380        /* Footer */381        footer {382            text-align: center;383            padding: 20px;384            color: #94a3b8;385            font-size: 0.9rem;386            border-top: 1px solid #e2e8f0;387            margin-top: 30px;388        }389 390        .controls-header {391            display: flex;392            justify-content: space-between;393            align-items: center;394            margin-bottom: 25px;395            padding-bottom: 15px;396            border-bottom: 1px solid #e2e8f0;397        }398 399        .controls-title {400            font-size: 1.4rem;401            color: #1e293b;402            font-weight: 700;403        }404 405        .reset-btn {406            background: #f1f5f9;407            color: #475569;408            border: 1px solid #cbd5e1;409            padding: 8px 16px;410            border-radius: 6px;411            font-weight: 500;412            cursor: pointer;413            transition: all 0.2s ease;414        }415 416        .reset-btn:hover {417            background: #e2e8f0;418        }419 420        /* Responsive */421        @media (max-width: 768px) {422            .container {423                border-radius: 10px;424            }425            426            h1 {427                font-size: 2rem;428            }429            430            .tab {431                padding: 12px 15px;432                font-size: 1rem;433            }434            435            .controls {436                padding: 20px;437            }438            439            .metric-checkboxes {440                grid-template-columns: 1fr;441            }442            443            th, td {444                padding: 12px 15px;445            }446            447            .submit-container {448                padding: 20px;449            }450            451            .submit-title {452                font-size: 1.8rem;453            }454            455            .controls-header {456                flex-direction: column;457                align-items: flex-start;458                gap: 10px;459            }460            461            .reset-btn {462                align-self: flex-end;463            }464        }465    </style>466</head>467<body>468    <div class="container">469        <header>470            <h1>NL2SQL Leaderboard</h1>471            <div class="subtitle">Benchmarking Natural Language to SQL Conversion Models and Agents</div>472            <div class="tabs">473                <div class="tab active" onclick="switchTab('leaderboard')">Leaderboard</div>474                <div class="tab" onclick="switchTab('submit')">Submit Results</div>475                <div class="tab" onclick="switchTab('about')">About</div>476            </div>477        </header>478 479        <main id="leaderboard-content">480            <div class="controls">481                <div class="controls-header">482                    <div class="controls-title">483                        <i class="fas fa-sliders-h"></i> Filter Controls484                    </div>485                    <button class="reset-btn" onclick="resetFilters()">486                        <i class="fas fa-redo"></i> Reset Filters487                    </button>488                </div>489                490                <div class="dataset-selector">491                    <div class="dataset-label">492                        <i class="fas fa-database"></i>493                        Select Dataset494                    </div>495                    <div class="dataset-buttons" id="dataset-buttons">496                        <!-- 动态生成数据集按钮 -->497                    </div>498                </div>499                500                <div class="metric-selector">501                    <div class="metric-label">502                        <i class="fas fa-chart-bar"></i>503                        Select Metrics to Display504                    </div>505                    <div class="metric-checkboxes" id="metric-checkboxes">506                        <!-- 动态生成指标复选框 -->507                    </div>508                    <div class="metric-hint">509                        Tip: Click on metrics to show/hide them in the table. The best value for each metric is highlighted with a 🏆. Metrics Precision/Recall/F1 are calculated only among the sqls that are executable.510                    </div>511                </div>512            </div>513 514            <div class="table-container">515                <table id="leaderboard-table">516                    <thead>517                        <tr id="table-header">518                            <!-- 动态生成表头 -->519                        </tr>520                    </thead>521                    <tbody id="table-body">522                        <!-- 动态生成表格内容 -->523                    </tbody>524                </table>525            </div>526 527            <div class="no-data" id="no-data-message" style="display: none;">528                <i class="fas fa-database"></i>529                <h3>No Data Available</h3>530                <p>This dataset currently has no results. Be the first to submit!</p>531                <button class="submit-btn" onclick="switchTab('submit')" style="margin-top: 20px;">532                    <i class="fas fa-cloud-upload-alt"></i> Submit Results533                </button>534            </div>535        </main>536 537        <div id="submit-content" style="display: none;">538            <div class="submit-container">539                <div class="submit-icon">540                    <i class="fas fa-cloud-upload-alt"></i>541                </div>542                <h2 class="submit-title">Submit Your Results</h2>543                <p class="submit-description">544                    Submit your NL2SQL model results to be included in the leaderboard. 545                    Please ensure your results follow the required format.546                </p>547                <div class="coming-soon">548                    <i class="fas fa-tools"></i> Submission Portal Under Development549                </div>550 551                <div class="submit-form">552                    <div class="form-group">553                        <label for="dataset-select"><i class="fas fa-database"></i> Select Dataset</label>554                        <select id="dataset-select" class="dataset-select">555                            <option value="birddev">BIRD Dev</option>556                            <option value="birdtrain">BIRD Train</option>557                            <option value="spider">Spider</option>558                        </select>559                    </div>560 561                    <div class="form-group">562                        <label for="algorithm-name"><i class="fas fa-robot"></i> Algorithm/Model Name</label>563                        <input type="text" id="algorithm-name" placeholder="Enter your algorithm name">564                    </div>565 566                    <div class="form-group">567                        <label for="results-json"><i class="fas fa-code"></i> Results (JSON format)</label>568                        <textarea id="results-json" rows="6" placeholder='{569  "execution_ability": 0.85,570  "correct_rate": 0.78,571  "execution_efficiency_on_executable_sql": 0.92,572  "table_precision_on_executable_sql": 0.87,573  "table_recall_on_executable_sql": 0.85,574  "table_f1_on_executable_sql": 0.86,575  "column_precision_on_executable_sql": 0.83,576  "column_recall_on_executable_sql": 0.80,577  "column_f1_on_executable_sql": 0.815578}'></textarea>579                    </div>580 581                    <div style="text-align: center;">582                        <button class="submit-btn" onclick="submitResults()">583                            <i class="fas fa-paper-plane"></i> Submit Results (Demo)584                        </button>585                    </div>586                </div>587            </div>588        </div>589 590        <div id="about-content" style="display: none;">591            <div class="submit-container">592                <h2 class="submit-title">About NL2SQL Leaderboard</h2>593                <p class="submit-description">594                    This leaderboard tracks the performance of various Natural Language to SQL (NL2SQL) models 595                    across multiple benchmarks and metrics.596                </p>597                598                <div style="text-align: left; max-width: 800px; margin: 40px auto;">599                    <h3 style="color: #475569; margin-bottom: 15px;">Metrics Explained:</h3>600                    <ul style="list-style-type: none; padding: 0;">601                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">602                            <strong>Execution Ability:</strong> Ability to generate executable SQL queries603                        </li>604                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">605                            <strong>Correct Rate:</strong> Percentage of correctly generated SQL queries606                        </li>607                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">608                            <strong>Execution Efficiency:</strong> Performance on executable SQL queries609                        </li>610                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">611                            <strong>Table/Column Precision/Recall/F1:</strong> Accuracy in selecting correct tables and columns when the sql is executable612                        </li>613                    </ul>614                    615                    <h3 style="color: #475569; margin-top: 30px; margin-bottom: 15px;">Supported Datasets:</h3>616                    <ul style="list-style-type: none; padding: 0;">617                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">618                            <strong>BIRD Dev:</strong> Benchmark for large-scale database tasks in the real world619                        </li>620                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">621                            <strong>BIRD Train:</strong> Training set for BIRD benchmark622                        </li>623                        <li style="margin-bottom: 10px; padding: 10px; background: #f1f5f9; border-radius: 6px;">624                            <strong>Spider:</strong> Large-scale complex and cross-domain semantic parsing and text-to-SQL dataset625                        </li>626                    </ul>627                </div>628            </div>629        </div>630 631        <footer>632            <p>NL2SQL Leaderboard • Last Updated: <span id="last-updated">Loading...</span></p>633            <p style="margin-top: 5px; font-size: 0.8rem;">634                <i class="fas fa-code"></i> Built with FastAPI &bull; 635                <i class="fas fa-server"></i> Docker Ready &bull; 636                <i class="fas fa-chart-line"></i> Real-time Updates637            </p>638        </footer>639    </div>640 641    <script>642        let currentDataset = 'birddev';643        let datasets = {};644        let selectedMetrics = new Set();645        let datasetMetrics = {};646 647        // 页面加载时获取数据648        document.addEventListener('DOMContentLoaded', async function() {649            await loadLeaderboardData();650            updateLastUpdated();651        });652 653        // 加载排行榜数据654        async function loadLeaderboardData() {655            try {656                const response = await fetch('/api/data');657                const data = await response.json();658                datasets = data.datasets;659                660                // 为每个数据集存储指标661                for (const [key, dataset] of Object.entries(datasets)) {662                    datasetMetrics[key] = dataset.metrics;663                }664                665                // 初始化数据集选择按钮666                initDatasetButtons();667                668                // 显示当前数据集669                showDataset(currentDataset);670                671            } catch (error) {672                console.error('Error loading data:', error);673                alert('Failed to load leaderboard data');674            }675        }676 677        // 更新最后更新时间678        async function updateLastUpdated() {679            try {680                const response = await fetch('/api/data');681                const data = await response.json();682                const date = new Date(data.last_updated);683                document.getElementById('last-updated').textContent = 684                    date.toLocaleString();685            } catch (error) {686                console.error('Error updating timestamp:', error);687            }688        }689 690        // 初始化数据集按钮691        function initDatasetButtons() {692            const container = document.getElementById('dataset-buttons');693            container.innerHTML = '';694            695            for (const [key, dataset] of Object.entries(datasets)) {696                const button = document.createElement('button');697                button.className = `dataset-btn ${key === currentDataset ? 'active' : ''}`;698                button.textContent = dataset.name;  // 移除了图标699                button.onclick = () => {700                    currentDataset = key;701                    showDataset(key);702                    updateDatasetButtons();703                };704                container.appendChild(button);705            }706        }707 708        // 更新数据集按钮状态709        function updateDatasetButtons() {710            const buttons = document.querySelectorAll('.dataset-btn');711            buttons.forEach(button => {712                // 移除所有active类713                button.classList.remove('active');714                715                // 找到对应的数据集键716                const buttonText = button.textContent.toLowerCase().replace(/\s+/g, '');717                let foundKey = null;718                for (const [key, dataset] of Object.entries(datasets)) {719                    if (dataset.name.toLowerCase().replace(/\s+/g, '').includes(buttonText.replace(/icon/g, ''))) {720                        foundKey = key;721                        break;722                    }723                }724                if (foundKey === currentDataset) {725                    button.classList.add('active');726                }727            });728        }729 730        // 显示指定数据集731        function showDataset(datasetKey) {732            const dataset = datasets[datasetKey];733            if (!dataset) return;734            735            // 初始化指标选择(如果这是第一次显示这个数据集)736            if (!selectedMetrics.size || !Array.from(selectedMetrics).some(m => dataset.metrics.includes(m))) {737                // 重置为选中所有指标738                selectedMetrics = new Set(dataset.metrics || []);739            }740            741            // 更新指标复选框742            updateMetricCheckboxes(datasetKey);743            744            // 更新表格745            updateTable(dataset);746            747            // 显示/隐藏无数据消息748            const noDataMsg = document.getElementById('no-data-message');749            if (dataset.algorithms.length === 0) {750                noDataMsg.style.display = 'block';751                document.querySelector('.table-container').style.display = 'none';752            } else {753                noDataMsg.style.display = 'none';754                document.querySelector('.table-container').style.display = 'block';755            }756        }757 758        // 更新指标复选框759        function updateMetricCheckboxes(datasetKey) {760            const container = document.getElementById('metric-checkboxes');761            container.innerHTML = '';762            763            const metrics = datasetMetrics[datasetKey] || [];764            765            metrics.forEach(metric => {766                const checkbox = document.createElement('div');767                checkbox.className = 'metric-checkbox';768                769                const inputId = `metric-${metric}`;770                const isChecked = selectedMetrics.has(metric);771                772                checkbox.innerHTML = `773                    <input type="checkbox" id="${inputId}" ${isChecked ? 'checked' : ''} 774                        onchange="toggleMetric('${metric}', this.checked)">775                    <label for="${inputId}">${formatMetricName(metric)}</label>  <!-- 移除了图标 -->776                `;777                container.appendChild(checkbox);778            });779        }780 781        // 切换指标显示782        function toggleMetric(metric, isChecked) {783            if (isChecked) {784                selectedMetrics.add(metric);785            } else {786                selectedMetrics.delete(metric);787            }788            789            // 重新显示当前数据集(会刷新表格)790            showDataset(currentDataset);791        }792 793        // 格式化指标名称794        function formatMetricName(metric) {795            // 先替换_on_executable_sql796            let name = metric.replace(/_on_executable_sql/g, '');797            // 将下划线替换为空格798            name = name.replace(/_/g, ' ');799            // 首字母大写800            name = name.replace(/\b\w/g, l => l.toUpperCase());801            return name;802        }803 804        // 重置过滤器805        function resetFilters() {806            // 重置为默认数据集807            currentDataset = 'birddev';808            809            // 重置指标选择810            const dataset = datasets[currentDataset];811            selectedMetrics = new Set(dataset.metrics || []);812            813            // 更新显示814            showDataset(currentDataset);815            updateDatasetButtons();816        }817 818        // 切换标签页(如果需要,也可以移除控制面板标题中的图标)819        function switchTab(tabName) {820            // 更新标签页状态821            document.querySelectorAll('.tab').forEach(tab => {822                tab.classList.toggle('active', tab.textContent.toLowerCase().includes(tabName));823            });824            825            // 显示对应的内容826            document.getElementById('leaderboard-content').style.display = 827                tabName === 'leaderboard' ? 'block' : 'none';828            document.getElementById('submit-content').style.display = 829                tabName === 'submit' ? 'block' : 'none';830            document.getElementById('about-content').style.display = 831                tabName === 'about' ? 'block' : 'none';832        }833 834        // 更新表格835        function updateTable(dataset) {836            const algorithms = dataset.algorithms;837            const metrics = dataset.metrics || [];838            839            // 更新表头840            updateTableHeader(metrics);841            842            // 更新表格内容843            updateTableBody(algorithms, metrics);844        }845 846        // 更新表头847        function updateTableHeader(metrics) {848            const header = document.getElementById('table-header');849            header.innerHTML = '<th class="algorithm-header">Algorithm</th>';  // 移除了图标850            851            // 只添加选中的指标852            metrics.forEach(metric => {853                if (selectedMetrics.has(metric)) {854                    header.innerHTML += `<th>${formatMetricName(metric)}</th>`;  // 移除了图标855                }856            });857        }858 859        // 更新表格内容860        function updateTableBody(algorithms, metrics) {861            const tbody = document.getElementById('table-body');862            tbody.innerHTML = '';863            864            if (algorithms.length === 0) return;865            866            // 计算每个指标的最佳值867            const bestValues = {};868            metrics.forEach(metric => {869                if (selectedMetrics.has(metric)) {870                    const values = algorithms.map(a => a[metric] || 0);871                    bestValues[metric] = Math.max(...values);872                }873            });874            875            // 填充数据行876            algorithms.forEach(algorithm => {877                const row = document.createElement('tr');878                let rowHtml = `<td class="algorithm-name">${algorithm.name}</td>`;  // 移除了图标879                880                metrics.forEach(metric => {881                    if (selectedMetrics.has(metric)) {882                        const value = algorithm[metric];883                        const isBest = value === bestValues[metric] && value !== undefined;884                        const cellClass = isBest ? 'metric-value best-value' : 'metric-value';885                        const displayValue = value !== undefined ? value.toFixed(3) : 'N/A';886                        rowHtml += `<td class="${cellClass}">${displayValue}</td>`;887                    }888                });889                890                row.innerHTML = rowHtml;891                tbody.appendChild(row);892            });893        }894 895        // 每30秒刷新数据896        setInterval(updateLastUpdated, 30000);897    </script>898</body>899</html>