714 lines
26 KiB
JavaScript
714 lines
26 KiB
JavaScript
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; |