feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
338
tests/test_practice_records_frontend.html
Normal file
338
tests/test_practice_records_frontend.html
Normal file
@@ -0,0 +1,338 @@
|
||||
<!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>
|
||||
|
||||
Reference in New Issue
Block a user