'use strict';
// 校区地图:中国地图 + 学校点位(启用=涟漪点亮,停用/未定位=灰点)+ 右侧列表。
// 数据:GET /api/schools(JSON 种子 + 运行时合并);底图:/vendor/china-map.json
(function () {
const $ = (id) => document.getElementById(id);
const CENTER = [104.5, 36];
const ZOOM = 1.55;
let chart = null;
let schools = [];
let inited = false;
let loading = false;
let selectedId = null;
function esc(value) {
const div = document.createElement('div');
div.textContent = String(value == null ? '' : value);
return div.innerHTML;
}
function fmtDate(ymd) {
const s = String(ymd || '');
return /^\d{8}$/.test(s) ? `${Number(s.slice(4, 6))}/${Number(s.slice(6, 8))}` : '';
}
function opsText(school) {
if (!school.ops) return '运营数据不可用';
if (!school.ops.shopCount) return '未关联运营数据';
return `${school.ops.shopCount} 店${school.ops.lastDate ? ' · 最近 ' + fmtDate(school.ops.lastDate) : ''}`;
}
function toPoint(school) {
return {
name: school.name,
value: [school.lng, school.lat],
schoolId: school.id,
};
}
function buildOption() {
const on = schools.filter((s) => s.enabled && s.lng != null && s.lat != null);
const off = schools.filter((s) => !(s.enabled && s.lng != null && s.lat != null));
return {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(255,255,255,.96)',
borderColor: '#e8e2db',
borderWidth: 1,
padding: [8, 10],
textStyle: { color: '#3f3a35', fontSize: 12 },
formatter(params) {
const school = schools.find((s) => s.id === params.data.schoolId);
if (!school) return params.name;
const status = school.enabled ? '已启用' : '已停用';
const point = school.lng == null ? '未定位' : `${school.lng.toFixed(4)}, ${school.lat.toFixed(4)}`;
return `${esc(school.name)}
${esc(school.province || '—')}${school.city ? ' · ' + esc(school.city) : ''}
${status} · ${opsText(school)}
${point}`;
},
},
geo: {
map: 'china',
roam: true,
center: CENTER,
zoom: ZOOM,
scaleLimit: { min: 1, max: 12 },
itemStyle: { areaColor: '#f7f5f2', borderColor: '#ddd6cd', borderWidth: 0.8 },
emphasis: { itemStyle: { areaColor: '#efeae4' }, label: { show: false } },
select: { disabled: true },
},
series: [
{
id: 'on',
type: 'effectScatter',
coordinateSystem: 'geo',
data: on.map(toPoint),
symbolSize: 11,
showEffectOn: 'render',
rippleEffect: { brushType: 'stroke', scale: 3.4, period: 3.6 },
itemStyle: { color: '#ff6b35', shadowBlur: 8, shadowColor: 'rgba(255,107,53,.45)' },
label: {
show: true,
position: 'right',
distance: 6,
formatter: (p) => p.name,
fontSize: 11,
color: '#5b534c',
},
emphasis: { scale: 1.4 },
zlevel: 2,
},
{
id: 'off',
type: 'scatter',
coordinateSystem: 'geo',
data: off.map(toPoint),
symbolSize: 6,
itemStyle: { color: '#b8b2ad' },
label: { show: false },
zlevel: 1,
},
],
};
}
function renderMap(force) {
const box = $('schoolsMap');
if (!box) return;
if (!chart) chart = echarts.init(box);
chart.setOption(buildOption(), !!force);
}
function renderList(data) {
const list = $('schoolsList');
const count = $('schoolsCount');
if (!list) return;
const enabled = data.enabledCount || 0;
if (count) count.textContent = `${enabled} / ${schools.length} 已启用`;
if (!schools.length) {
list.innerHTML = '
还没有学校
内置学校缺失,请检查 server/schools.seed.json地图加载失败
${esc(e.message || e)}