finyx_data_ai/tests/base_test_framework.js
2026-01-11 07:48:19 +08:00

417 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 测试页面基础框架 - 通用函数
* 提供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<object>} 响应数据
*/
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 = `
<div class="loading-container">
<div class="spinner"></div>
<p>加载中...</p>
</div>
`;
}
}
/**
* 隐藏加载状态
* @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 = `
<div class="error-container">
<div class="error-icon">⚠️</div>
<div class="error-message">${message}</div>
</div>
`;
}
}
/**
* 显示成功消息
* @param {string} elementId - 元素ID
* @param {string} message - 成功消息
*/
function showSuccess(elementId, message) {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = `
<div class="success-container">
<div class="success-icon">✅</div>
<div class="success-message">${message}</div>
</div>
`;
}
}
// ==================== 图表渲染函数 ====================
/**
* 渲染柱状图使用纯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 = `
<div class="chart-container">
${title ? `<h3 class="chart-title">${title}</h3>` : ''}
<div class="bar-chart">
`;
data.forEach((item, index) => {
const percentage = (item.value / maxValue) * 100;
const color = item.color || getBarColor(index);
html += `
<div class="bar-item">
<div class="bar-label">${item.label}</div>
<div class="bar-track">
<div class="bar-fill" style="width: ${percentage}%; background-color: ${color};"></div>
</div>
<div class="bar-value">${item.value}</div>
</div>
`;
});
html += `
</div>
</div>
`;
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 = `
<div class="chart-container">
${title ? `<h3 class="chart-title">${title}</h3>` : ''}
<div class="pie-chart-wrapper">
<div class="pie-chart">
`;
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 += `
<div class="pie-segment" style="
--angle: ${currentAngle}deg;
--size: ${angle}deg;
background: ${color};
"></div>
`;
currentAngle += angle;
});
html += `
</div>
<div class="pie-legend">
`;
data.forEach((item, index) => {
const percentage = ((item.value / total) * 100).toFixed(1);
const color = item.color || getBarColor(index);
html += `
<div class="legend-item">
<div class="legend-color" style="background-color: ${color};"></div>
<div class="legend-label">${item.label}: ${item.value} (${percentage}%)</div>
</div>
`;
});
html += `
</div>
</div>
</div>
`;
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 = `
<div class="card-list-container">
${title ? `<h3 class="section-title">${title}</h3>` : ''}
<div class="card-list">
`;
items.forEach((item, index) => {
html += `
<div class="card-item">
<div class="card-header">
<div class="card-title">${item.title || item.name || item.id || `项目 ${index + 1}`}</div>
${item.badge ? `<div class="card-badge ${item.badgeClass || 'badge-info'}">${item.badge}</div>` : ''}
</div>
<div class="card-content">${item.content || item.description || ''}</div>
${item.details ? `
<div class="card-details">
${renderKeyValueList(item.details)}
</div>
` : ''}
</div>
`;
});
html += `
</div>
</div>
`;
container.innerHTML = html;
}
/**
* 渲染键值列表
* @param {object} data - 数据对象
* @returns {string} HTML字符串
*/
function renderKeyValueList(data) {
if (!data || typeof data !== 'object') return '';
let html = '<div class="kv-list">';
for (const [key, value] of Object.entries(data)) {
html += `
<div class="kv-item">
<span class="kv-key">${key}:</span>
<span class="kv-value">${value}</span>
</div>
`;
}
html += '</div>';
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 = `
<div class="table-container">
${title ? `<h3 class="section-title">${title}</h3>` : ''}
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
`;
columns.forEach(col => {
html += `<th style="width: ${col.width || 'auto'}">${col.label}</th>`;
});
html += `
</tr>
</thead>
<tbody>
`;
data.forEach(row => {
html += '<tr>';
columns.forEach(col => {
const value = row[col.key];
html += `<td>${formatCellValue(value)}</td>`;
});
html += '</tr>';
});
html += `
</tbody>
</table>
</div>
</div>
`;
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);
}