Files
012-kaopeilian/tests/test_practice_records_frontend.html
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

339 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>陪练记录API测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 3px solid #409eff;
padding-bottom: 10px;
}
.section {
margin: 20px 0;
padding: 20px;
background: #f9f9f9;
border-radius: 5px;
}
.form-group {
margin: 15px 0;
}
label {
display: inline-block;
width: 100px;
font-weight: bold;
}
input {
padding: 8px;
width: 300px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 10px 20px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin: 5px;
}
button:hover {
background: #66b1ff;
}
.success { color: #67c23a; }
.error { color: #f56c6c; }
.warning { color: #e6a23c; }
.info { color: #909399; }
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
max-height: 400px;
}
.status-item {
margin: 10px 0;
padding: 10px;
background: white;
border-left: 4px solid #409eff;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th {
background: #409eff;
color: white;
}
tr:hover {
background: #f5f7fa;
}
</style>
</head>
<body>
<div class="container">
<h1>陪练记录列表 - API测试工具</h1>
<div class="section">
<h3>1. 登录</h3>
<div class="form-group">
<label>用户名:</label>
<input type="text" id="username" value="admin" />
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" id="password" value="admin123" />
</div>
<button onclick="login()">登录</button>
<button onclick="checkLoginStatus()">检查登录状态</button>
<button onclick="logout()">退出登录</button>
<div id="loginStatus" class="status-item" style="display:none;"></div>
</div>
<div class="section">
<h3>2. 获取陪练记录</h3>
<button onclick="getPracticeRecords()">获取陪练记录列表</button>
<button onclick="getPracticeStats()">获取统计数据</button>
<div id="recordsStatus" class="status-item" style="display:none;"></div>
<div id="recordsTable"></div>
</div>
<div class="section">
<h3>3. 调试信息</h3>
<button onclick="showDebugInfo()">显示调试信息</button>
<pre id="debugInfo" style="display:none;"></pre>
</div>
</div>
<script>
const API_BASE = window.location.origin;
// 显示状态信息
function showStatus(elementId, message, type = 'info') {
const el = document.getElementById(elementId);
el.style.display = 'block';
el.className = `status-item ${type}`;
el.innerHTML = message;
}
// 登录
async function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
showStatus('loginStatus', '正在登录...', 'info');
const response = await fetch(`${API_BASE}/api/v1/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
const result = await response.json();
if (result.code === 200) {
const token = result.data.token.access_token;
const refreshToken = result.data.token.refresh_token;
localStorage.setItem('access_token', token);
localStorage.setItem('refresh_token', refreshToken);
localStorage.setItem('user_info', JSON.stringify(result.data.user));
showStatus('loginStatus',
`✓ 登录成功!<br>
用户: ${result.data.user.username} (${result.data.user.full_name})<br>
Token已保存到localStorage`,
'success'
);
} else {
showStatus('loginStatus', `✗ 登录失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('loginStatus', `✗ 登录错误: ${error.message}`, 'error');
}
}
// 检查登录状态
function checkLoginStatus() {
const token = localStorage.getItem('access_token');
const userInfo = localStorage.getItem('user_info');
if (token && userInfo) {
const user = JSON.parse(userInfo);
showStatus('loginStatus',
`✓ 已登录<br>
用户: ${user.username} (${user.full_name})<br>
Token: ${token.substring(0, 30)}...`,
'success'
);
} else {
showStatus('loginStatus', '✗ 未登录', 'warning');
}
}
// 退出登录
function logout() {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_info');
showStatus('loginStatus', '✓ 已退出登录', 'info');
}
// 获取陪练记录
async function getPracticeRecords() {
const token = localStorage.getItem('access_token');
if (!token) {
showStatus('recordsStatus', '✗ 请先登录', 'error');
return;
}
try {
showStatus('recordsStatus', '正在获取陪练记录...', 'info');
const response = await fetch(`${API_BASE}/api/v1/practice/sessions/list?page=1&size=20`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (result.code === 200) {
const items = result.data.items || [];
const total = result.data.total || 0;
showStatus('recordsStatus',
`✓ 成功获取陪练记录!<br>
总记录数: ${total}<br>
当前页记录数: ${items.length}`,
'success'
);
// 显示表格
displayRecordsTable(items);
} else {
showStatus('recordsStatus', `✗ 获取失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('recordsStatus', `✗ 请求错误: ${error.message}`, 'error');
}
}
// 获取统计数据
async function getPracticeStats() {
const token = localStorage.getItem('access_token');
if (!token) {
showStatus('recordsStatus', '✗ 请先登录', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/api/v1/practice/stats`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (result.code === 200) {
const data = result.data;
showStatus('recordsStatus',
`✓ 统计数据:<br>
总次数: ${data.total_count}<br>
平均分: ${data.avg_score.toFixed(1)}<br>
总时长: ${data.total_duration_hours}小时<br>
本月进步: ${data.month_improvement}%`,
'success'
);
} else {
showStatus('recordsStatus', `✗ 获取失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('recordsStatus', `✗ 请求错误: ${error.message}`, 'error');
}
}
// 显示记录表格
function displayRecordsTable(items) {
if (items.length === 0) {
document.getElementById('recordsTable').innerHTML = '<p class="warning">⚠️ 没有陪练记录</p>';
return;
}
let html = '<table><thead><tr>';
html += '<th>会话ID</th><th>场景名称</th><th>开始时间</th><th>时长(秒)</th><th>对话轮数</th><th>评分</th><th>结果</th>';
html += '</tr></thead><tbody>';
items.forEach(item => {
html += '<tr>';
html += `<td>${item.session_id}</td>`;
html += `<td>${item.scene_name}</td>`;
html += `<td>${item.start_time}</td>`;
html += `<td>${item.duration_seconds}</td>`;
html += `<td>${item.turns}</td>`;
html += `<td>${item.total_score}</td>`;
html += `<td>${item.result}</td>`;
html += '</tr>';
});
html += '</tbody></table>';
document.getElementById('recordsTable').innerHTML = html;
}
// 显示调试信息
function showDebugInfo() {
const debugEl = document.getElementById('debugInfo');
debugEl.style.display = 'block';
const info = {
'API Base URL': API_BASE,
'localStorage.access_token': localStorage.getItem('access_token') ? localStorage.getItem('access_token').substring(0, 50) + '...' : 'null',
'localStorage.user_info': localStorage.getItem('user_info') || 'null',
'Current URL': window.location.href,
'User Agent': navigator.userAgent
};
debugEl.textContent = JSON.stringify(info, null, 2);
}
// 页面加载时检查登录状态
window.onload = function() {
checkLoginStatus();
};
</script>
</body>
</html>