/** * 测试页面基础框架 - 通用函数 * 提供API调用、图表渲染、UI交互等公共功能 */ // ==================== 配置 ==================== const API_BASE_URL = 'http://localhost:8000/api/v1'; // ==================== API 调用函数 ==================== /** * 发送 API 请求 * @param {string} endpoint - 接口端点 * @param {object} data - 请求数据 * @param {string} method - HTTP 方法 * @returns {Promise} 响应数据 */ async function apiRequest(endpoint, data, method = 'POST') { try { const response = await fetch(`${API_BASE_URL}${endpoint}`, { method: method, headers: { 'Content-Type': 'application/json', }, body: method === 'POST' ? JSON.stringify(data) : null, }); const result = await response.json(); if (result.success) { return result; } else { throw new Error(result.message || '请求失败'); } } catch (error) { console.error('API 请求错误:', error); throw error; } } /** * 显示加载状态 * @param {string} elementId - 元素ID */ function showLoading(elementId) { const element = document.getElementById(elementId); if (element) { element.innerHTML = `

加载中...

`; } } /** * 隐藏加载状态 * @param {string} elementId - 元素ID * @param {string} content - 新内容 */ function hideLoading(elementId, content = '') { const element = document.getElementById(elementId); if (element) { element.innerHTML = content; } } /** * 显示错误信息 * @param {string} elementId - 元素ID * @param {string} message - 错误消息 */ function showError(elementId, message) { const element = document.getElementById(elementId); if (element) { element.innerHTML = `
⚠️
${message}
`; } } /** * 显示成功消息 * @param {string} elementId - 元素ID * @param {string} message - 成功消息 */ function showSuccess(elementId, message) { const element = document.getElementById(elementId); if (element) { element.innerHTML = `
${message}
`; } } // ==================== 图表渲染函数 ==================== /** * 渲染柱状图(使用纯CSS/HTML) * @param {string} containerId - 容器ID * @param {Array} data - 数据数组 [{label, value, color}] * @param {string} title - 图表标题 */ function renderBarChart(containerId, data, title = '') { const container = document.getElementById(containerId); if (!container) return; const maxValue = Math.max(...data.map(d => d.value)); let html = `
${title ? `

${title}

` : ''}
`; data.forEach((item, index) => { const percentage = (item.value / maxValue) * 100; const color = item.color || getBarColor(index); html += `
${item.label}
${item.value}
`; }); html += `
`; container.innerHTML = html; } /** * 渲染饼图(使用CSS) * @param {string} containerId - 容器ID * @param {Array} data - 数据数组 [{label, value, color}] * @param {string} title - 图表标题 */ function renderPieChart(containerId, data, title = '') { const container = document.getElementById(containerId); if (!container) return; const total = data.reduce((sum, item) => sum + item.value, 0); let html = `
${title ? `

${title}

` : ''}
`; let currentAngle = 0; data.forEach((item, index) => { const percentage = (item.value / total) * 100; const angle = (item.value / total) * 360; const color = item.color || getBarColor(index); html += `
`; currentAngle += angle; }); html += `
`; data.forEach((item, index) => { const percentage = ((item.value / total) * 100).toFixed(1); const color = item.color || getBarColor(index); html += `
${item.label}: ${item.value} (${percentage}%)
`; }); html += `
`; container.innerHTML = html; } /** * 渲染卡片列表 * @param {string} containerId - 容器ID * @param {Array} items - 卡片项目数组 * @param {string} title - 标题 */ function renderCardList(containerId, items, title = '') { const container = document.getElementById(containerId); if (!container) return; let html = `
${title ? `

${title}

` : ''}
`; items.forEach((item, index) => { html += `
${item.title || item.name || item.id || `项目 ${index + 1}`}
${item.badge ? `
${item.badge}
` : ''}
${item.content || item.description || ''}
${item.details ? `
${renderKeyValueList(item.details)}
` : ''}
`; }); html += `
`; container.innerHTML = html; } /** * 渲染键值列表 * @param {object} data - 数据对象 * @returns {string} HTML字符串 */ function renderKeyValueList(data) { if (!data || typeof data !== 'object') return ''; let html = '
'; for (const [key, value] of Object.entries(data)) { html += `
${key}: ${value}
`; } html += '
'; return html; } /** * 渲染表格数据 * @param {string} containerId - 容器ID * @param {Array} columns - 列定义 [{key, label, width}] * @param {Array} data - 数据数组 * @param {string} title - 表格标题 */ function renderTable(containerId, columns, data, title = '') { const container = document.getElementById(containerId); if (!container) return; let html = `
${title ? `

${title}

` : ''}
`; columns.forEach(col => { html += ``; }); html += ` `; data.forEach(row => { html += ''; columns.forEach(col => { const value = row[col.key]; html += ``; }); html += ''; }); html += `
${col.label}
${formatCellValue(value)}
`; container.innerHTML = html; } // ==================== 辅助函数 ==================== /** * 获取柱状图颜色 * @param {number} index - 索引 * @returns {string} 颜色值 */ function getBarColor(index) { const colors = [ '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b', '#858796', '#5a5c69', '#6610f2', '#e83e8c', '#fd7e14' ]; return colors[index % colors.length]; } /** * 格式化单元格值 * @param {any} value - 值 * @returns {string} 格式化后的字符串 */ function formatCellValue(value) { if (value === null || value === undefined) return '-'; if (Array.isArray(value)) return value.join(', '); if (typeof value === 'object') return JSON.stringify(value); return String(value); } /** * 格式化数字 * @param {number} num - 数字 * @param {number} decimals - 小数位数 * @returns {string} 格式化后的字符串 */ function formatNumber(num, decimals = 2) { if (num === null || num === undefined) return '-'; return num.toLocaleString('zh-CN', { minimumFractionDigits: decimals, maximumFractionDigits: decimals }); } /** * 格式化时间 * @param {number} seconds - 秒数 * @returns {string} 格式化后的时间字符串 */ function formatTime(seconds) { if (seconds < 1) { return `${(seconds * 1000).toFixed(0)}ms`; } else if (seconds < 60) { return `${seconds.toFixed(2)}秒`; } else { const minutes = Math.floor(seconds / 60); const secs = (seconds % 60).toFixed(0); return `${minutes}分${secs}秒`; } } /** * 延迟函数 * @param {number} ms - 毫秒数 * @returns {Promise} */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 复制文本到剪贴板 * @param {string} text - 文本 */ function copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => { showToast('已复制到剪贴板'); }).catch(err => { console.error('复制失败:', err); }); } /** * 显示提示消息 * @param {string} message - 消息内容 */ function showToast(message) { const toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('show'); }, 10); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { document.body.removeChild(toast); }, 300); }, 2000); }