增加前端分支

This commit is contained in:
2026-09-22 20:18:44 +08:00
parent 0a1a7eec16
commit 1abb8ab737
19 changed files with 1330 additions and 13 deletions
+49
View File
@@ -0,0 +1,49 @@
const BASE_URL = 'http://127.0.0.1:12345';
async function request(path, params = {}) {
const url = new URL(BASE_URL + path);
Object.keys(params).forEach(key => {
if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
url.searchParams.append(key, params[key]);
}
});
try {
const res = await fetch(url.toString());
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
return data;
} catch (e) {
console.error('请求失败:', url.toString(), e);
throw e;
}
}
const API = {
getStudentsByAgeRange: (n, m, minAge, maxAge) =>
request('/basic-information', { n, m, min_age: minAge, max_age: maxAge }),
getClassInfo: (classId) =>
request('/basic-information/' + classId),
getAllClassStats: () =>
request('/class-stats'),
getStudentsByScore: (n, m, score) =>
request('/scores', { n, m, score }),
getNoPassStudents: (n, m, failCount) =>
request('/scores_no_pass', { n, m, fail_count: failCount }),
getClassExamAvg: (n, m) =>
request('/scores_avg', { n, m }),
getSalaryTop: (m = 5) =>
request('/salary_top', { m }),
getTimeSize: (n, m) =>
request('/time_size', { n, m }),
getClassAvgTimeSize: (n, m) =>
request('/class_avg_time_size', { n, m })
};
+714
View File
@@ -0,0 +1,714 @@
const state = {
age: { page: 1, size: 10, totalPages: 0, total: 0 },
score: { page: 1, size: 10, totalPages: 0, total: 0 },
nopass: { page: 1, size: 10, totalPages: 0, total: 0 },
examAvg: { page: 1, size: 10, totalPages: 0, total: 0 },
timeSize: { page: 1, size: 10, totalPages: 0, total: 0 },
classAvgTime: { page: 1, size: 10, totalPages: 0, total: 0 }
};
const pages = [
{ id: 'age', name: '按年龄区间查询', icon: '👤' },
{ id: 'class-stats', name: '指定班级人数统计', icon: '🏫' },
{ id: 'all-class-stats', name: '所有班级人数统计', icon: '📊' },
{ id: 'score', name: '按分数段查询', icon: '📝' },
{ id: 'no-pass', name: '不合格学生查询', icon: '⚠️' },
{ id: 'exam-avg', name: '班级考试平均分', icon: '📈' },
{ id: 'salary-top', name: '薪资TOP排行', icon: '💰' },
{ id: 'time-size', name: '就业时长查询', icon: '⏱️' },
{ id: 'class-avg-time', name: '班级平均就业时长', icon: '📅' }
];
function renderSidebar() {
const nav = document.getElementById('nav');
nav.innerHTML = pages.map((p, i) =>
`<div class="nav-item ${i === 0 ? 'active' : ''}" data-page="${p.id}">
<span class="icon">${p.icon}</span>${p.name}
</div>`
).join('');
nav.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', () => {
nav.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
item.classList.add('active');
showPage(item.dataset.page);
});
});
}
function showPage(id) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
const target = document.getElementById('page-' + id);
if (target) target.classList.add('active');
document.getElementById('top-title').textContent =
pages.find(p => p.id === id)?.name || '';
if (id === 'all-class-stats') loadAllClassStats();
if (id === 'salary-top') loadSalaryTop();
if (id === 'exam-avg') loadExamAvg();
if (id === 'time-size') loadTimeSize();
if (id === 'class-avg-time') loadClassAvgTime();
}
function toast(msg, type = 'success') {
const div = document.createElement('div');
div.className = 'toast ' + type;
div.textContent = msg;
document.body.appendChild(div);
setTimeout(() => div.remove(), 2000);
}
function renderPagination(container, s, onChange) {
if (s.totalPages <= 1) {
container.innerHTML = '';
return;
}
let html = `<span class="page-info">共 ${s.total} 条 / 第 ${s.page}/${s.totalPages} 页</span>`;
html += `<button ${s.page <= 1 ? 'disabled' : ''} data-act="prev">上一页</button>`;
const range = [];
const start = Math.max(1, s.page - 2);
const end = Math.min(s.totalPages, start + 4);
for (let i = start; i <= end; i++) range.push(i);
range.forEach(p => {
html += `<button class="${p === s.page ? 'active' : ''}" data-p="${p}">${p}</button>`;
});
html += `<button ${s.page >= s.totalPages ? 'disabled' : ''} data-act="next">下一页</button>`;
html += `<span style="margin-left:12px">跳至</span>`;
html += `<input type="number" min="1" max="${s.totalPages}" value="${s.page}" data-act="jumpto" style="width:60px;padding:4px 8px;border:1px solid #d9d9d9;border-radius:4px;text-align:center">`;
html += `<span>页</span>`;
container.innerHTML = html;
container.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', () => {
let p = btn.dataset.p ? parseInt(btn.dataset.p) : null;
if (btn.dataset.act === 'prev') p = s.page - 1;
if (btn.dataset.act === 'next') p = s.page + 1;
if (p && p !== s.page) onChange(p);
});
});
const jumpInput = container.querySelector('input[data-act="jumpto"]');
if (jumpInput) {
jumpInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
let p = parseInt(jumpInput.value);
if (!isNaN(p)) {
p = Math.max(1, Math.min(s.totalPages, p));
if (p !== s.page) onChange(p);
}
}
});
}
}
function formatDate(d) {
if (!d) return '-';
return d.toString().slice(0, 10);
}
function createPages() {
const container = document.getElementById('pages-container');
container.innerHTML = `
<!-- 年龄区间查询 -->
<div class="page active" id="page-age">
<div class="card">
<div class="card-title">查询条件</div>
<div class="form-row">
<div class="form-group">
<label>最小年龄:</label>
<input type="number" id="age-min" value="18" min="0" max="100">
</div>
<div class="form-group">
<label>最大年龄:</label>
<input type="number" id="age-max" value="30" min="0" max="100">
</div>
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="age-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="searchAge()">查询</button>
</div>
</div>
<div class="card">
<div class="card-title">查询结果</div>
<div class="table-wrapper">
<table id="age-table">
<thead>
<tr>
<th>学号</th><th>姓名</th><th>性别</th><th>身份证号</th>
<th>出生日期</th><th>民族</th><th>专业</th><th>班级</th>
<th>入学日期</th><th>学籍状态</th><th>学历</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="age-pagination"></div>
</div>
</div>
<!-- 指定班级人数 -->
<div class="page" id="page-class-stats">
<div class="card">
<div class="card-title">查询条件</div>
<div class="form-row">
<div class="form-group">
<label>班级编号:</label>
<input type="text" id="class-id" placeholder="如 CLS001">
</div>
<button class="btn btn-primary" onclick="searchClassInfo()">查询</button>
</div>
</div>
<div id="class-stats-result"></div>
</div>
<!-- 所有班级人数 -->
<div class="page" id="page-all-class-stats">
<div class="card">
<div class="card-title">所有班级人数统计</div>
<div id="all-class-chart" class="chart-container"></div>
</div>
<div class="card">
<div class="table-wrapper">
<table id="all-class-table">
<thead>
<tr>
<th>班级编号</th><th>班级名称</th><th>总人数</th><th>男生</th><th>女生</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<!-- 分数段查询 -->
<div class="page" id="page-score">
<div class="card">
<div class="card-title">查询条件</div>
<div class="form-row">
<div class="form-group">
<label>及格分数线:</label>
<input type="number" id="score-val" value="60" min="0" max="100" step="0.5">
</div>
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="score-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="searchScore()">查询</button>
</div>
</div>
<div class="card">
<div class="card-title">分数 ≥ 指定值的学生</div>
<div class="table-wrapper">
<table id="score-table">
<thead><tr><th>学号</th><th>姓名</th><th>分数</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="score-pagination"></div>
</div>
</div>
<!-- 不合格学生 -->
<div class="page" id="page-no-pass">
<div class="card">
<div class="card-title">查询条件</div>
<div class="form-row">
<div class="form-group">
<label>不及格门数 ≥</label>
<input type="number" id="fail-count" value="1" min="1">
</div>
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="nopass-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="searchNoPass()">查询</button>
</div>
</div>
<div class="card">
<div class="card-title">不合格学生列表</div>
<div class="table-wrapper">
<table id="nopass-table">
<thead><tr><th>学号</th><th>姓名</th><th>不及格门数</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="nopass-pagination"></div>
</div>
</div>
<!-- 班级考试平均分 -->
<div class="page" id="page-exam-avg">
<div class="card">
<div class="card-title">每次考试每个班级平均分(从高到低)</div>
<div class="form-row" style="margin-bottom:16px">
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="exam-avg-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="reloadExamAvg()">刷新</button>
</div>
<div id="exam-avg-chart" class="chart-container"></div>
</div>
<div class="card">
<div class="table-wrapper">
<table id="exam-avg-table">
<thead>
<tr><th>课程编号</th><th>班级编号</th><th>班级名称</th><th>平均分</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="exam-avg-pagination"></div>
</div>
</div>
<!-- 薪资TOP -->
<div class="page" id="page-salary-top">
<div class="card">
<div class="card-title">薪资排行 TOP</div>
<div class="form-row" style="margin-bottom:16px">
<div class="form-group">
<label>TOP N:</label>
<input type="number" id="salary-n" value="5" min="1">
</div>
<button class="btn btn-primary" onclick="loadSalaryTop()">查询</button>
</div>
<div id="salary-chart" class="chart-container"></div>
</div>
<div class="card">
<div class="table-wrapper">
<table id="salary-table">
<thead>
<tr><th>姓名</th><th>班级</th><th>公司</th><th>入职时间</th><th>薪资(元/月)</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<!-- 就业时长 -->
<div class="page" id="page-time-size">
<div class="card">
<div class="card-title">每个学生的就业时长</div>
<div class="form-row" style="margin-bottom:16px">
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="time-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="reloadTimeSize()">刷新</button>
</div>
<div class="table-wrapper">
<table id="time-table">
<thead>
<tr><th>学号</th><th>姓名</th><th>开放简历时间</th><th>Offer时间</th><th>就业时长(天)</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="time-pagination"></div>
</div>
</div>
<!-- 班级平均就业时长 -->
<div class="page" id="page-class-avg-time">
<div class="card">
<div class="card-title">每个班级的平均就业时长</div>
<div class="form-row" style="margin-bottom:16px">
<div class="form-group">
<label>每页条数(m):</label>
<input type="number" id="class-avg-time-size" value="10" min="1" max="100" style="width:80px">
</div>
<button class="btn btn-primary" onclick="reloadClassAvgTime()">刷新</button>
</div>
<div id="class-avg-time-chart" class="chart-container"></div>
</div>
<div class="card">
<div class="table-wrapper">
<table id="class-avg-time-table">
<thead>
<tr><th>班级编号</th><th>班级名称</th><th>平均就业时长(天)</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="pagination" id="class-avg-time-pagination"></div>
</div>
</div>
`;
}
// ============ 各页面加载函数 ============
async function searchAge() {
const min = parseInt(document.getElementById('age-min').value);
const max = parseInt(document.getElementById('age-max').value);
state.age.page = 1;
await loadAge(min, max);
}
async function loadAge(min, max) {
const s = state.age;
s.size = parseInt(document.getElementById('age-size').value) || 10;
document.querySelector('#age-table tbody').innerHTML = '<tr><td colspan="11" class="loading">加载中...</td></tr>';
try {
const res = await API.getStudentsByAgeRange(s.page, s.size, min, max);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total;
s.totalPages = res.total_pages;
const tbody = document.querySelector('#age-table tbody');
if (!res.data || res.data.length === 0) {
tbody.innerHTML = '<tr><td colspan="11" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = res.data.map(r => `
<tr>
<td>${r.student_id}</td><td>${r.student_name}</td><td>${r.gender}</td>
<td>${r.id_card}</td><td>${formatDate(r.birthday)}</td><td>${r.ethnicity}</td>
<td>${r.major}</td><td>${r.class_id}</td><td>${formatDate(r.enrollment_date)}</td>
<td>${r.student_status}</td><td>${r.education_level}</td>
</tr>`).join('');
}
renderPagination(document.getElementById('age-pagination'), s, (p) => {
s.page = p; loadAge(min, max);
});
} catch (e) {
document.querySelector('#age-table tbody').innerHTML = '<tr><td colspan="11" class="empty">请求失败,请检查后端服务是否启动</td></tr>';
}
}
async function searchClassInfo() {
const classId = document.getElementById('class-id').value.trim();
if (!classId) { toast('请输入班级编号', 'error'); return; }
const box = document.getElementById('class-stats-result');
box.innerHTML = '<div class="loading">加载中...</div>';
try {
const res = await API.getClassInfo(classId);
if (res.code !== 200) { toast(res.message, 'error'); box.innerHTML = ''; return; }
const d = res.data;
box.innerHTML = `
<div class="card">
<div class="card-title">班级:${d.class_id} - ${d.class_name}</div>
<div class="stats-grid">
<div class="stat-card total">
<div class="stat-label">总人数</div>
<div class="stat-value">${d.total_count}</div>
</div>
<div class="stat-card male">
<div class="stat-label">男生</div>
<div class="stat-value">${d.male_count}</div>
</div>
<div class="stat-card female">
<div class="stat-label">女生</div>
<div class="stat-value">${d.female_count}</div>
</div>
</div>
<div id="class-info-chart" class="chart-container" style="margin-top:20px"></div>
</div>`;
const chart = echarts.init(document.getElementById('class-info-chart'));
chart.setOption({
tooltip: { trigger: 'item' },
legend: { bottom: 10 },
series: [{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 },
label: { show: true, formatter: '{b}: {c} ({d}%)' },
data: [
{ value: d.male_count, name: '男生', itemStyle: { color: '#1890ff' } },
{ value: d.female_count, name: '女生', itemStyle: { color: '#eb2f96' } }
]
}]
});
} catch (e) {
box.innerHTML = '<div class="empty">请求失败,请检查后端服务是否启动</div>';
}
}
async function loadAllClassStats() {
try {
const res = await API.getAllClassStats();
if (res.code !== 200) { toast(res.message, 'error'); return; }
const data = res.data || [];
const tbody = document.querySelector('#all-class-table tbody');
if (!data.length) {
tbody.innerHTML = '<tr><td colspan="5" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = data.map(r => `
<tr>
<td>${r.class_id}</td><td>${r.class_name}</td>
<td>${r.total_count}</td><td>${r.male_count}</td><td>${r.female_count}</td>
</tr>`).join('');
}
const chart = echarts.init(document.getElementById('all-class-chart'));
chart.setOption({
tooltip: { trigger: 'axis' },
legend: { data: ['男生', '女生', '总人数'] },
grid: { left: 50, right: 30, top: 50, bottom: 80 },
xAxis: {
type: 'category',
data: data.map(d => d.class_name),
axisLabel: { rotate: 30 }
},
yAxis: { type: 'value' },
series: [
{ name: '男生', type: 'bar', data: data.map(d => d.male_count), itemStyle: { color: '#1890ff' } },
{ name: '女生', type: 'bar', data: data.map(d => d.female_count), itemStyle: { color: '#eb2f96' } },
{ name: '总人数', type: 'bar', data: data.map(d => d.total_count), itemStyle: { color: '#52c41a' } }
]
});
} catch (e) {
console.error(e);
}
}
async function searchScore() {
state.score.page = 1;
await loadScore();
}
async function loadScore() {
const s = state.score;
s.size = parseInt(document.getElementById('score-size').value) || 10;
const score = parseFloat(document.getElementById('score-val').value);
try {
const res = await API.getStudentsByScore(s.page, s.size, score);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total; s.totalPages = res.total_pages;
const tbody = document.querySelector('#score-table tbody');
if (!res.data.length) {
tbody.innerHTML = '<tr><td colspan="3" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = res.data.map(r =>
`<tr><td>${r.student_id}</td><td>${r.student_name}</td><td>${r.score}</td></tr>`
).join('');
}
renderPagination(document.getElementById('score-pagination'), s, (p) => {
s.page = p; loadScore();
});
} catch (e) { toast('请求失败', 'error'); }
}
async function searchNoPass() {
state.nopass.page = 1;
await loadNoPass();
}
async function loadNoPass() {
const s = state.nopass;
s.size = parseInt(document.getElementById('nopass-size').value) || 10;
const fc = parseInt(document.getElementById('fail-count').value);
try {
const res = await API.getNoPassStudents(s.page, s.size, fc);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total; s.totalPages = res.total_pages;
const tbody = document.querySelector('#nopass-table tbody');
if (!res.data.length) {
tbody.innerHTML = '<tr><td colspan="3" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = res.data.map(r =>
`<tr><td>${r.student_id}</td><td>${r.student_name}</td><td>${r.fail_count}</td></tr>`
).join('');
}
renderPagination(document.getElementById('nopass-pagination'), s, (p) => {
s.page = p; loadNoPass();
});
} catch (e) { toast('请求失败', 'error'); }
}
async function loadExamAvg() {
const s = state.examAvg;
s.size = parseInt(document.getElementById('exam-avg-size').value) || 10;
try {
const res = await API.getClassExamAvg(s.page, s.size);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total; s.totalPages = res.total_pages;
const data = res.data || [];
const tbody = document.querySelector('#exam-avg-table tbody');
if (!data.length) {
tbody.innerHTML = '<tr><td colspan="4" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = data.map(r =>
`<tr><td>${r.course_id}</td><td>${r.class_id}</td><td>${r.class_name}</td><td>${r.avg_score.toFixed(2)}</td></tr>`
).join('');
}
const chart = echarts.init(document.getElementById('exam-avg-chart'));
chart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 30, top: 30, bottom: 80 },
xAxis: {
type: 'category',
data: data.map(d => d.class_name + '-' + d.course_id),
axisLabel: { rotate: 40 }
},
yAxis: { type: 'value', min: 0, max: 100 },
series: [{
type: 'bar',
data: data.map(d => d.avg_score.toFixed(2)),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2a5298' },
{ offset: 1, color: '#1e3c72' }
])
},
label: { show: true, position: 'top', formatter: '{c}' }
}]
});
renderPagination(document.getElementById('exam-avg-pagination'), s, (p) => {
s.page = p; loadExamAvg();
});
} catch (e) { toast('请求失败', 'error'); }
}
async function loadSalaryTop() {
const m = parseInt(document.getElementById('salary-n').value) || 5;
try {
const res = await API.getSalaryTop(m);
if (res.code !== 200) { toast(res.message, 'error'); return; }
const data = res.data || [];
const tbody = document.querySelector('#salary-table tbody');
if (!data.length) {
tbody.innerHTML = '<tr><td colspan="5" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = data.map(r =>
`<tr><td>${r.student_name}</td><td>${r.class_name}</td><td>${r.company_name}</td>
<td>${formatDate(r.part_time)}</td><td>${r.salary}</td></tr>`
).join('');
}
const chart = echarts.init(document.getElementById('salary-chart'));
chart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 80, right: 30, top: 30, bottom: 30 },
xAxis: { type: 'value' },
yAxis: { type: 'category', data: data.map(d => d.student_name).reverse() },
series: [{
type: 'bar',
data: data.map(d => d.salary).reverse(),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#fa709a' },
{ offset: 1, color: '#fee140' }
])
},
label: { show: true, position: 'right', formatter: '{c}' }
}]
});
} catch (e) { toast('请求失败', 'error'); }
}
async function loadTimeSize() {
const s = state.timeSize;
s.size = parseInt(document.getElementById('time-size').value) || 10;
try {
const res = await API.getTimeSize(s.page, s.size);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total; s.totalPages = res.total_pages;
const tbody = document.querySelector('#time-table tbody');
if (!res.data.length) {
tbody.innerHTML = '<tr><td colspan="5" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = res.data.map(r =>
`<tr><td>${r.student_id}</td><td>${r.student_name}</td>
<td>${formatDate(r.resume_open_date)}</td>
<td>${formatDate(r.offer_date)}</td><td>${r.time_size}</td></tr>`
).join('');
}
renderPagination(document.getElementById('time-pagination'), s, (p) => {
s.page = p; loadTimeSize();
});
} catch (e) { toast('请求失败', 'error'); }
}
async function loadClassAvgTime() {
const s = state.classAvgTime;
s.size = parseInt(document.getElementById('class-avg-time-size').value) || 10;
try {
const res = await API.getClassAvgTimeSize(s.page, s.size);
if (res.code !== 200) { toast(res.message, 'error'); return; }
s.total = res.total; s.totalPages = res.total_pages;
const data = res.data || [];
const tbody = document.querySelector('#class-avg-time-table tbody');
if (!data.length) {
tbody.innerHTML = '<tr><td colspan="3" class="empty">暂无数据</td></tr>';
} else {
tbody.innerHTML = data.map(r =>
`<tr><td>${r.class_id}</td><td>${r.class_name}</td><td>${r.avg_time_size.toFixed(1)}</td></tr>`
).join('');
}
const chart = echarts.init(document.getElementById('class-avg-time-chart'));
chart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 30, top: 30, bottom: 80 },
xAxis: {
type: 'category',
data: data.map(d => d.class_name),
axisLabel: { rotate: 30 }
},
yAxis: { type: 'value', name: '天数' },
series: [{
type: 'bar',
data: data.map(d => d.avg_time_size.toFixed(1)),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#43e97b' },
{ offset: 1, color: '#38f9d7' }
])
},
label: { show: true, position: 'top', formatter: '{c}天' }
}]
});
renderPagination(document.getElementById('class-avg-time-pagination'), s, (p) => {
s.page = p; loadClassAvgTime();
});
} catch (e) { toast('请求失败', 'error'); }
}
function reloadExamAvg() { state.examAvg.page = 1; loadExamAvg(); }
function reloadTimeSize() { state.timeSize.page = 1; loadTimeSize(); }
function reloadClassAvgTime() { state.classAvgTime.page = 1; loadClassAvgTime(); }
// ============ 启动 ============
window.addEventListener('DOMContentLoaded', () => {
createPages();
renderSidebar();
window.searchAge = searchAge;
window.searchClassInfo = searchClassInfo;
window.searchScore = searchScore;
window.searchNoPass = searchNoPass;
window.loadSalaryTop = loadSalaryTop;
window.reloadExamAvg = reloadExamAvg;
window.reloadTimeSize = reloadTimeSize;
window.reloadClassAvgTime = reloadClassAvgTime;
window.addEventListener('resize', () => {
document.querySelectorAll('.chart-container').forEach(el => {
const inst = echarts.getInstanceByDom(el);
if (inst) inst.resize();
});
});
});
// 页面切换时也触发对应数据加载
const origShowPage = showPage;