Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
203 lines
11 KiB
HTML
203 lines
11 KiB
HTML
{# 各维度详细图表:得分柱状图、子维度雷达图、对比柱状图、水平分布、散点图 #}
|
||
<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, '区均值', '同类学校'], 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: '均值基线(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, '区均值', '同类学校'], bottom: 5, textStyle: { fontSize: 11 } },
|
||
radar: {
|
||
indicator: d.sub_dims.map(sd => ({ name: 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: '区均值',
|
||
lineStyle: { width: 1, type: 'dashed', color: COLORS.grayDark }, itemStyle: { color: COLORS.grayDark } },
|
||
{ value: d.same_type_avg, name: '同类学校',
|
||
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, '区均值', '同类学校'], bottom: 5, textStyle: { fontSize: 11 } },
|
||
grid: { left: '3%', right: '4%', bottom: '18%', top: '5%', containLabel: true },
|
||
xAxis: { type: 'category', data: d.sub_dims, 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: '区均值', type: 'bar', data: d.district_avg, itemStyle: { color: COLORS.gray }, barWidth: '22%' },
|
||
{ name: '同类学校', 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 }};
|
||
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 }}', '区均值'], bottom: 5 },
|
||
grid: { left: '3%', right: '4%', bottom: '18%', containLabel: true },
|
||
xAxis: { type: 'category', data: d.categories, axisLabel: { rotate: 15, fontSize: 11 } },
|
||
yAxis: { type: 'value', min: 20, max: 80 },
|
||
series: [
|
||
{ name: '{{ school }}', type: 'bar', data: d.school_values,
|
||
itemStyle: { color: COLORS.primary }, barWidth: '28%',
|
||
label: { show: true, position: 'top', fontSize: 10 } },
|
||
{ name: '区均值', type: 'bar', data: d.avg_values, itemStyle: { color: COLORS.gray }, barWidth: '28%' },
|
||
{ name: '均值基线(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 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 = d.categories.indexOf(params[0].axisValue);
|
||
if (idx >= 0) s += '<strong>{{ school }}:水平' + d.school_levels[idx] + '</strong>';
|
||
return s;
|
||
}
|
||
},
|
||
legend: { data: ['水平一', '水平二', '水平三', '水平四'], bottom: 5, textStyle: { fontSize: 11 } },
|
||
grid: { left: '3%', right: '4%', bottom: '18%', top: '5%', containLabel: true },
|
||
xAxis: { type: 'category', data: d.categories, axisLabel: { fontSize: 11, rotate: 15 } },
|
||
yAxis: { type: 'value', max: 100, axisLabel: { formatter: '{value}%' } },
|
||
series: [
|
||
{ name: '水平一', type: 'bar', stack: 'total', data: d.level1, itemStyle: { color: COLORS.level1 }, barWidth: '50%' },
|
||
{ name: '水平二', type: 'bar', stack: 'total', data: d.level2, itemStyle: { color: COLORS.level2 } },
|
||
{ name: '水平三', type: 'bar', stack: 'total', data: d.level3, itemStyle: { color: COLORS.level3 } },
|
||
{ name: '水平四', 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);
|
||
|
||
if (d.type === '3d') {
|
||
const axes = d.axes;
|
||
const option = {
|
||
tooltip: {
|
||
formatter: function(params) {
|
||
const v = params.value || (params.data && params.data.value);
|
||
if (!v) return '';
|
||
return params.seriesName + '<br/>'
|
||
+ axes[0] + ': ' + (typeof v[0] === 'number' ? v[0].toFixed(2) : v[0]) + '<br/>'
|
||
+ axes[1] + ': ' + (typeof v[1] === 'number' ? v[1].toFixed(2) : v[1]) + '<br/>'
|
||
+ axes[2] + ': ' + (typeof v[2] === 'number' ? v[2].toFixed(2) : v[2]);
|
||
}
|
||
},
|
||
legend: { data: ['较好类', '待提升类', d.school_name], bottom: 5, textStyle: { fontSize: 11 } },
|
||
xAxis3D: { name: d.axes[0], type: 'value', nameTextStyle: { fontSize: 10 } },
|
||
yAxis3D: { name: d.axes[1], type: 'value', nameTextStyle: { fontSize: 10 } },
|
||
zAxis3D: { name: d.axes[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: '较好类', type: 'scatter3D', data: d.good_data.map(p => ({ value: p })),
|
||
symbolSize: 8, itemStyle: { color: COLORS.success, opacity: 0.7 } },
|
||
{ name: '待提升类', 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 : d.axes[0] + ': ' + p.value[0] + '<br/>' + d.axes[1] + ': ' + p.value[1] },
|
||
legend: { data: ['较好类', '待提升类', d.school_name], bottom: 5, textStyle: { fontSize: 11 } },
|
||
grid: { left: '10%', right: '8%', bottom: '18%', top: '5%' },
|
||
xAxis: { name: d.axes[0], nameLocation: 'center', nameGap: 30, nameTextStyle: { fontSize: 11 }, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
|
||
yAxis: { name: d.axes[1], nameLocation: 'center', nameGap: 40, nameTextStyle: { fontSize: 11 }, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
|
||
series: [
|
||
{ name: '较好类', type: 'scatter', data: d.good_data, symbolSize: 10, itemStyle: { color: COLORS.success, opacity: 0.6 } },
|
||
{ name: '待提升类', 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>
|