Files
lofyerandfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> 71db82393a Initial commit
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-07-13 15:38:41 +08:00

205 lines
12 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{# 各维度详细图表:得分柱状图、子维度雷达图、对比柱状图、水平分布、散点图 #}
<script>
// ===== 8. 各维度独立得分柱状图 =====
const dimScoreBars = {{ dim_score_bar_data | tojson }};
Object.keys(dimScoreBars).forEach(partId => {
const el = document.getElementById('dim-score-' + partId);
if (!el) return;
const chart = echarts.init(el);
const d = dimScoreBars[partId];
const option = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '8%', right: '8%', bottom: '12%', top: '8%', containLabel: true },
xAxis: { type: 'category', data: [d.school_name, I18N.ec_district_avg, I18N.ec_same_type], axisLabel: { fontSize: 12 } },
yAxis: { type: 'value', min: 30, max: 80 },
series: [{
type: 'bar', barWidth: '40%',
data: [
{ value: d.school_score, itemStyle: { color: COLORS.primary } },
{ value: d.district_avg, itemStyle: { color: COLORS.gray } },
{ value: d.same_type_avg, itemStyle: { color: COLORS.warning } },
],
label: { show: true, position: 'top', fontSize: 13, fontWeight: 'bold', formatter: p => p.value.toFixed(2) },
markLine: { silent: true, data: [{ yAxis: 50, label: { formatter: I18N.ec_baseline_50, fontSize: 10 }, lineStyle: { color: '#ef4444', type: 'dashed' } }] },
}],
};
chart.setOption(option);
window.addEventListener('resize', () => chart.resize());
});
// ===== 9. 各维度子维度雷达图 =====
const dimSubRadar = {{ dim_sub_radar_data | tojson }};
Object.keys(dimSubRadar).forEach(partId => {
const el = document.getElementById('sub-radar-' + partId);
if (!el) return;
const chart = echarts.init(el);
const d = dimSubRadar[partId];
if (d.type === 'radar' && d.sub_dims && d.sub_dims.length >= 3) {
const option = {
tooltip: {},
legend: { data: [d.school_name, I18N.ec_district_avg, I18N.ec_same_type], bottom: 5, textStyle: { fontSize: 11 } },
radar: {
indicator: d.sub_dims.map(sd => ({ name: tSub(sd), max: 80 })),
shape: 'polygon', splitNumber: 4, axisName: { color: '#333', fontSize: 10 }, radius: '60%',
},
series: [{
type: 'radar',
data: [
{ value: d.school_values, name: d.school_name,
areaStyle: { opacity: 0.3, color: COLORS.primary },
lineStyle: { width: 3, color: COLORS.primary }, itemStyle: { color: COLORS.primary } },
{ value: d.district_avg, name: I18N.ec_district_avg,
lineStyle: { width: 1, type: 'dashed', color: COLORS.grayDark }, itemStyle: { color: COLORS.grayDark } },
{ value: d.same_type_avg, name: I18N.ec_same_type,
lineStyle: { width: 1, type: 'dotted', color: COLORS.warning }, itemStyle: { color: COLORS.warning } },
],
}],
};
chart.setOption(option);
} else {
const option = {
tooltip: { trigger: 'axis' },
legend: { data: [d.school_name, I18N.ec_district_avg, I18N.ec_same_type], bottom: 5, textStyle: { fontSize: 11 } },
grid: { left: '3%', right: '4%', bottom: '18%', top: '5%', containLabel: true },
xAxis: { type: 'category', data: d.sub_dims.map(tSub), axisLabel: { fontSize: 11 } },
yAxis: { type: 'value', min: 20, max: 80 },
series: [
{ name: d.school_name, type: 'bar', data: d.school_values,
itemStyle: { color: COLORS.primary }, barWidth: '22%',
label: { show: true, position: 'top', fontSize: 10 } },
{ name: I18N.ec_district_avg, type: 'bar', data: d.district_avg, itemStyle: { color: COLORS.gray }, barWidth: '22%' },
{ name: I18N.ec_same_type, type: 'bar', data: d.same_type_avg, itemStyle: { color: COLORS.warning }, barWidth: '22%' },
],
};
chart.setOption(option);
}
window.addEventListener('resize', () => chart.resize());
});
// ===== 10. 各维度子维度柱状图 =====
const subDimCharts = {{ sub_dim_chart_data | tojson }};
const SCHOOL_DISPLAY = {{ school_display | tojson }};
Object.keys(subDimCharts).forEach(chartId => {
const el = document.getElementById('chart-' + chartId);
if (!el) return;
const chart = echarts.init(el);
const d = subDimCharts[chartId];
const option = {
tooltip: { trigger: 'axis' },
legend: { data: [SCHOOL_DISPLAY, I18N.ec_district_avg], bottom: 5 },
grid: { left: '3%', right: '4%', bottom: '18%', containLabel: true },
xAxis: { type: 'category', data: d.categories.map(tSub), axisLabel: { rotate: 15, fontSize: 11 } },
yAxis: { type: 'value', min: 20, max: 80 },
series: [
{ name: SCHOOL_DISPLAY, type: 'bar', data: d.school_values,
itemStyle: { color: COLORS.primary }, barWidth: '28%',
label: { show: true, position: 'top', fontSize: 10 } },
{ name: I18N.ec_district_avg, type: 'bar', data: d.avg_values, itemStyle: { color: COLORS.gray }, barWidth: '28%' },
{ name: I18N.ec_baseline_50, type: 'line', data: d.categories.map(() => 50),
lineStyle: { color: '#ef4444', type: 'dashed', width: 1 }, symbol: 'none' },
],
};
chart.setOption(option);
window.addEventListener('resize', () => chart.resize());
});
// ===== 11. 水平分布堆叠条形图 =====
const levelDistData = {{ level_dist_data | tojson }};
Object.keys(levelDistData).forEach(chartId => {
const el = document.getElementById('level-' + chartId);
if (!el) return;
const chart = echarts.init(el);
const d = levelDistData[chartId];
if (!d.categories || d.categories.length === 0) return;
const localizedCats = d.categories.map(tSub);
const option = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' },
formatter: function(params) {
let s = params[0].axisValue + '<br/>';
params.forEach(p => { s += p.marker + ' ' + p.seriesName + ': ' + p.value + '%<br/>'; });
const idx = localizedCats.indexOf(params[0].axisValue);
if (idx >= 0) s += '<strong>' + I18N.ec_at_level.replace('{name}', SCHOOL_DISPLAY).replace('{lv}', d.school_levels[idx]) + '</strong>';
return s;
}
},
legend: { data: [I18N.ec_lvl_one, I18N.ec_lvl_two, I18N.ec_lvl_three, I18N.ec_lvl_four], bottom: 5, textStyle: { fontSize: 11 } },
grid: { left: '3%', right: '4%', bottom: '18%', top: '5%', containLabel: true },
xAxis: { type: 'category', data: localizedCats, axisLabel: { fontSize: 11, rotate: 15 } },
yAxis: { type: 'value', max: 100, axisLabel: { formatter: '{value}%' } },
series: [
{ name: I18N.ec_lvl_one, type: 'bar', stack: 'total', data: d.level1, itemStyle: { color: COLORS.level1 }, barWidth: '50%' },
{ name: I18N.ec_lvl_two, type: 'bar', stack: 'total', data: d.level2, itemStyle: { color: COLORS.level2 } },
{ name: I18N.ec_lvl_three, type: 'bar', stack: 'total', data: d.level3, itemStyle: { color: COLORS.level3 } },
{ name: I18N.ec_lvl_four, type: 'bar', stack: 'total', data: d.level4, itemStyle: { color: COLORS.level4 } },
],
};
chart.setOption(option);
window.addEventListener('resize', () => chart.resize());
});
// ===== 12. 各维度聚类散点图(2D / 3D =====
const dimScatterData = {{ dim_scatter_data | tojson }};
Object.keys(dimScatterData).forEach(partId => {
const el = document.getElementById('scatter-' + partId);
if (!el) return;
const d = dimScatterData[partId];
if (!d || !d.axes) return;
const chart = echarts.init(el);
const axisLabels = d.axes.map(tSub);
if (d.type === '3d') {
const option = {
tooltip: {
formatter: function(params) {
const v = params.value || (params.data && params.data.value);
if (!v) return '';
return params.seriesName + '<br/>'
+ axisLabels[0] + ': ' + (typeof v[0] === 'number' ? v[0].toFixed(2) : v[0]) + '<br/>'
+ axisLabels[1] + ': ' + (typeof v[1] === 'number' ? v[1].toFixed(2) : v[1]) + '<br/>'
+ axisLabels[2] + ': ' + (typeof v[2] === 'number' ? v[2].toFixed(2) : v[2]);
}
},
legend: { data: [I18N.ec_good_type_short, I18N.ec_weak_type_short, d.school_name], bottom: 5, textStyle: { fontSize: 11 } },
xAxis3D: { name: axisLabels[0], type: 'value', nameTextStyle: { fontSize: 10 } },
yAxis3D: { name: axisLabels[1], type: 'value', nameTextStyle: { fontSize: 10 } },
zAxis3D: { name: axisLabels[2], type: 'value', nameTextStyle: { fontSize: 10 } },
grid3D: {
viewControl: { autoRotate: true, autoRotateSpeed: 5, distance: 200, alpha: 20, beta: 30 },
boxWidth: 100, boxHeight: 100, boxDepth: 100,
light: { main: { intensity: 1.2, shadow: true }, ambient: { intensity: 0.3 } },
},
series: [
{ name: I18N.ec_good_type_short, type: 'scatter3D', data: d.good_data.map(p => ({ value: p })),
symbolSize: 8, itemStyle: { color: COLORS.success, opacity: 0.7 } },
{ name: I18N.ec_weak_type_short, type: 'scatter3D', data: d.weak_data.map(p => ({ value: p })),
symbolSize: 8, itemStyle: { color: COLORS.danger, opacity: 0.7 } },
{ name: d.school_name, type: 'scatter3D',
data: d.school_point ? [{ value: d.school_point }] : [],
symbolSize: 18, itemStyle: { color: COLORS.primary, borderColor: '#fff', borderWidth: 2 },
label: { show: true, formatter: d.school_name, textStyle: { fontSize: 12, fontWeight: 'bold', color: COLORS.primary } } },
],
};
chart.setOption(option);
} else {
const option = {
tooltip: { formatter: p => (p.data && p.data.name) ? p.data.name : axisLabels[0] + ': ' + p.value[0] + '<br/>' + axisLabels[1] + ': ' + p.value[1] },
legend: { data: [I18N.ec_good_type_short, I18N.ec_weak_type_short, d.school_name], bottom: 5, textStyle: { fontSize: 11 } },
grid: { left: '10%', right: '8%', bottom: '18%', top: '5%' },
xAxis: { name: axisLabels[0], nameLocation: 'center', nameGap: 30, nameTextStyle: { fontSize: 11 }, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
yAxis: { name: axisLabels[1], nameLocation: 'center', nameGap: 40, nameTextStyle: { fontSize: 11 }, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
series: [
{ name: I18N.ec_good_type_short, type: 'scatter', data: d.good_data, symbolSize: 10, itemStyle: { color: COLORS.success, opacity: 0.6 } },
{ name: I18N.ec_weak_type_short, type: 'scatter', data: d.weak_data, symbolSize: 10, itemStyle: { color: COLORS.danger, opacity: 0.6 } },
{ name: d.school_name, type: 'scatter',
data: d.school_point ? [{ value: d.school_point, name: d.school_name, symbolSize: 22 }] : [],
symbolSize: 22, itemStyle: { color: COLORS.primary, borderColor: '#fff', borderWidth: 3 },
label: { show: true, formatter: d.school_name, position: 'top', textStyle: { fontSize: 13, fontWeight: 'bold', color: COLORS.primary } } },
],
};
chart.setOption(option);
}
window.addEventListener('resize', () => chart.resize());
});
</script>