每日总结


body {
font-family: Arial, sans-serif;
background: #f5f5f5;
min-height: 100vh;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px 0;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.nav {
background: white;
padding: 15px 0;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}
.nav-links a {
margin-left: 20px;
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.nav-links a:hover {
color: #764ba2;
}
.container {
max-width: 1200px;
margin: 40px auto;
padding: 0 20px;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 30px;
flex-wrap: wrap;
}
.tab-btn {
background: #f5f5f5;
border: none;
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
font-size: 1em;
}
.tab-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.record-card {
background: white;
border-radius: 15px;
padding: 25px;
margin-bottom: 25px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
}
.record-title {
font-size: 1.3em;
color: #333;
font-weight: bold;
}
.record-date {
color: #666;
font-size: 0.9em;
}
.record-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.detail-item {
background: #f5f5f5;
padding: 10px 15px;
border-radius: 10px;
}
.detail-label {
color: #999;
font-size: 0.8em;
margin-bottom: 5px;
}
.detail-value {
color: #333;
font-weight: 500;
}
.progress-bar {
width: 100%;
height: 10px;
background: #e0e0e0;
border-radius: 5px;
overflow: hidden;
margin-bottom: 15px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
border-radius: 5px;
}
.status-badge {
padding: 5px 10px;
border-radius: 15px;
font-size: 0.8em;
font-weight: 500;
}
.status-completed {
background: #e8f5e8;
color: #2e7d32;
}
.status-in-progress {
background: #fff3e0;
color: #ef6c00;
}
.status-passed {
background: #e8f5e8;
color: #2e7d32;
}
.status-failed {
background: #ffebee;
color: #c62828;
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
flex-wrap: wrap;
}
.action-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 8px 15px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
font-size: 0.9em;
transition: opacity 0.3s ease;
text-decoration: none;
display: inline-block;
}
.action-btn:hover {
opacity: 0.9;
}
.view-btn {
background: #4caf50;
}
.no-records {
text-align: center;
padding: 60px 20px;
color: #666;
background: white;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.loading {
text-align: center;
padding: 60px 20px;
color: #666;
}
@media (max-width: 768px) {
.record-details {
grid-template-columns: 1fr;
}
.tabs {
justify-content: center;
}
.action-buttons {
justify-content: center;
}
}
</style>
</head>
<body>
<!-- 引入上边栏 -->
<div th:replace="~{_top-nav :: top-nav}"></div>
<div class="main-content-with-topnav">
<div class="container">
<div class="tabs">
<button class="tab-btn active" onclick="openTab('learning')">学习记录</button>
<button class="tab-btn" onclick="openTab('test')">测试记录</button>
<button class="tab-btn" onclick="openTab('assessment')">评估记录</button>
</div>
<!-- 学习记录 -->
<div id="learning" class="tab-content active">
<div id="learningLoading" class="loading">
<h3>加载学习记录中...</h3>
</div>
<div id="learningRecords" style="display: none;">
<!-- 学习记录将通过 JavaScript 动态生成 -->
</div>
<div id="noLearningRecords" class="no-records" style="display: none;">
<h3>暂无学习记录</h3>
<p>开始学习推荐的学习包,记录将显示在这里</p>
<a href="recommended-learning.html" class="action-btn">查看推荐学习</a>
</div>
</div>
<!-- 测试记录 -->
<div id="test" class="tab-content">
<div id="testLoading" class="loading">
<h3>加载测试记录中...</h3>
</div>
<div id="testRecords" style="display: none;">
<!-- 测试记录将通过 JavaScript 动态生成 -->
</div>
<div id="noTestRecords" class="no-records" style="display: none;">
<h3>暂无测试记录</h3>
<p>完成学习包后参加测试,记录将显示在这里</p>
<a href="recommended-learning.html" class="action-btn">查看推荐学习</a>
</div>
</div>
<!-- 评估记录 -->
<div id="assessment" class="tab-content">
<div id="assessmentLoading" class="loading">
<h3>加载评估记录中...</h3>
</div>
<div id="assessmentRecords" style="display: none;">
<!-- 评估记录将通过 JavaScript 动态生成 -->
</div>
<div id="noAssessmentRecords" class="no-records" style="display: none;">
<h3>暂无评估记录</h3>
<p>完成测试后生成评估报告,记录将显示在这里</p>
<a href="recommended-learning.html" class="action-btn">查看推荐学习</a>
</div>
</div>
</div>
</div>
<script>
// 切换标签页
function openTab(tabName) {
// 隐藏所有标签内容
const tabContents = document.querySelectorAll('.tab-content');
tabContents.forEach(content => {
content.classList.remove('active');
});
// 移除所有按钮的激活状态
const tabBtns = document.querySelectorAll('.tab-btn');
tabBtns.forEach(btn => {
btn.classList.remove('active');
});
// 显示当前标签内容
document.getElementById(tabName).classList.add('active');
event.currentTarget.classList.add('active');
// 加载对应记录
if (tabName === 'learning' && !window.learningRecordsLoaded) {
loadLearningRecords();
} else if (tabName === 'test' && !window.testRecordsLoaded) {
loadTestRecords();
} else if (tabName === 'assessment' && !window.assessmentRecordsLoaded) {
loadAssessmentRecords();
}
}
// 初始化
function init() {
// 加载学习记录
loadLearningRecords();
}
// 加载学习记录
async function loadLearningRecords() {
window.learningRecordsLoaded = true;
try {
const token = localStorage.getItem('token');
const user = JSON.parse(localStorage.getItem('user'));
const userId = user.id;
const response = await fetch(`/api/learning/user/${userId}/records`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('加载学习记录失败');
}
const records = await response.json();
displayLearningRecords(records);
} catch (error) {
console.error('加载学习记录失败:', error);
alert('加载学习记录失败,请重试');
}
}
// 显示学习记录
function displayLearningRecords(records) {
const container = document.getElementById('learningRecords');
const loading = document.getElementById('learningLoading');
const noRecords = document.getElementById('noLearningRecords');
if (records.length === 0) {
loading.style.display = 'none';
noRecords.style.display = 'block';
return;
}
container.innerHTML = '';
// 按日期分组记录
const recordsByDate = groupByDate(records, r => new Date(r.updatedDate));
// 按日期倒序排列
const sortedDates = Object.keys(recordsByDate).sort((a, b) => new Date(b) - new Date(a));
sortedDates.forEach(date => {
const dateRecords = recordsByDate[date];
dateRecords.forEach(record => {
const card = document.createElement('div');
card.className = 'record-card';
let status = '学习中';
let statusClass = 'status-in-progress';
if (record.isCompleted) {
status = '已完成';
statusClass = 'status-completed';
}
card.innerHTML = `
<div class="record-header">
<div class="record-title">${record.videoTitle}</div>
<div class="record-date">${formatDate(record.updatedDate)}</div>
</div>
<div class="record-details">
<div class="detail-item">
<div class="detail-label">学习包</div>
<div class="detail-value">${record.learningPackageName}</div>
</div>
<div class="detail-item">
<div class="detail-label">观看进度</div>
<div class="detail-value">${record.watchProgress}%</div>
</div>
<div class="detail-item">
<div class="detail-label">状态</div>
<div class="detail-value status-badge ${statusClass}">${status}</div>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${record.watchProgress}%;"></div>
</div>
<div class="action-buttons">
<a href="/learning-package-detail.html?id=${record.learningPackageId}" class="action-btn">继续学习</a>
</div>
`;
container.appendChild(card);
});
});
loading.style.display = 'none';
container.style.display = 'block';
}
// 加载测试记录
async function loadTestRecords() {
window.testRecordsLoaded = true;
try {
const token = localStorage.getItem('token');
const response = await fetch(`/api/tests/user-records`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('加载测试记录失败');
}
const records = await response.json();
displayTestRecords(records);
} catch (error) {
console.error('加载测试记录失败:', error);
alert('加载测试记录失败,请重试');
}
}
// 显示测试记录
function displayTestRecords(records) {
const container = document.getElementById('testRecords');
const loading = document.getElementById('testLoading');
const noRecords = document.getElementById('noTestRecords');
if (records.length === 0) {
loading.style.display = 'none';
noRecords.style.display = 'block';
return;
}
container.innerHTML = '';
// 按日期倒序排列
records.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
records.forEach(record => {
const card = document.createElement('div');
card.className = 'record-card';
let status = '未通过';
let statusClass = 'status-failed';
if (record.isPassed) {
status = '已通过';
statusClass = 'status-passed';
}
card.innerHTML = `
<div class="record-header">
<div class="record-title">${record.learningPackageName} - 测试</div>
<div class="record-date">${formatDate(record.createTime)}</div>
</div>
<div class="record-details">
<div class="detail-item">
<div class="detail-label">得分</div>
<div class="detail-value">${record.score} / ${record.totalQuestions}</div>
</div>
<div class="detail-item">
<div class="detail-label">正确率</div>
<div class="detail-value">${Math.round((record.score / record.totalQuestions) * 100)}%</div>
</div>
<div class="detail-item">
<div class="detail-label">状态</div>
<div class="detail-value status-badge ${statusClass}">${status}</div>
</div>
</div>
<div class="action-buttons">
<a href="/assessment-result.html?testRecordId=${record.id}" class="action-btn view-btn">查看评估报告</a>
<a href="/test-questions.html?packageId=${record.learningPackageId}" class="action-btn">重新测试</a>
</div>
`;
container.appendChild(card);
});
loading.style.display = 'none';
container.style.display = 'block';
}
// 加载评估记录
async function loadAssessmentRecords() {
window.assessmentRecordsLoaded = true;
try {
const token = localStorage.getItem('token');
const response = await fetch(`/api/assessments/user-records`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('加载评估记录失败');
}
const records = await response.json();
displayAssessmentRecords(records);
} catch (error) {
console.error('加载评估记录失败:', error);
alert('加载评估记录失败,请重试');
}
}
// 显示评估记录
function displayAssessmentRecords(records) {
const container = document.getElementById('assessmentRecords');
const loading = document.getElementById('assessmentLoading');
const noRecords = document.getElementById('noAssessmentRecords');
if (records.length === 0) {
loading.style.display = 'none';
noRecords.style.display = 'block';
return;
}
container.innerHTML = '';
// 按日期倒序排列
records.sort((a, b) => new Date(b.createdDate) - new Date(a.createdDate));
records.forEach(record => {
const card = document.createElement('div');
card.className = 'record-card';
let effectivenessLevel;
if (record.learningEffectiveness >= 80) {
effectivenessLevel = '优秀';
} else if (record.learningEffectiveness >= 60) {
effectivenessLevel = '良好';
} else {
effectivenessLevel = '有待提高';
}
card.innerHTML = `
<div class="record-header">
<div class="record-title">${record.learningPackageName} - 心理状态评估</div>
<div class="record-date">${formatDate(record.createdDate)}</div>
</div>
<div class="record-details">
<div class="detail-item">
<div class="detail-label">学习包</div>
<div class="detail-value">${record.learningPackageName}</div>
</div>
<div class="detail-item">
<div class="detail-label">学习效果</div>
<div class="detail-value">${effectivenessLevel} (${record.learningEffectiveness}%)</div>
</div>
<div class="detail-item">
<div class="detail-label">测试得分</div>
<div class="detail-value">${record.testScore}分</div>
</div>
</div>
<div class="action-buttons">
<a href="/assessment-result.html?testRecordId=${record.testRecordId}" class="action-btn view-btn">查看评估报告</a>
</div>
`;
container.appendChild(card);
});
loading.style.display = 'none';
container.style.display = 'block';
}
// 按日期分组
function groupByDate(items, dateGetter) {
return items.reduce((acc, item) => {
const date = formatDateOnly(dateGetter(item));
if (!acc[date]) {
acc[date] = [];
}
acc[date].push(item);
return acc;
}, {});
}
// 格式化日期时间
function formatDate(dateString) {
const date = new Date(dateString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
// 格式化日期(仅年月日)
function formatDateOnly(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 退出登录
function logout() {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = 'login.html';
}
// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', init);
</script>
</body>
微服务架构
微服务将单体应用拆分为独立部署的小服务,每个服务负责特定业务功能。优势:技术异构、独立扩展、容错性。挑战:服务间通信(如REST/gRPC)、数据一致性(分布式事务)、运维复杂度(监控、部署)。适合大型团队和复杂业务。

浙公网安备 33010602011771号