Browse Source

feat: 校区地图按省份点亮学校数着色

- 省份底色按「启用且已定位」的学校数分档:1 / 2-3 / 4-9 / 10+,无色为 0
- 省份归属按简称归一(浙江省/浙江、内蒙古自治区/内蒙古),避免匹配不上
- 图例增加「每省点亮学校」色阶;悬停省份显示点亮学校数
liangzhao 2 tuần trước cách đây
mục cha
commit
c9e60103f0
1 tập tin đã thay đổi với 44 bổ sung và 3 xóa
  1. 44 3
      public/schools.js

+ 44 - 3
public/schools.js

@@ -10,6 +10,7 @@
   let chart = null;
   let schools = [];
   let cityPoints = [];
+  let geoProvinceNames = [];
   let cityLabelsVisible = false;
   let inited = false;
   let loading = false;
@@ -51,6 +52,30 @@
     return '#d8d1c9';
   }
 
+  /** 每省已点亮(启用且已定位)的学校数 */
+  function provinceCounts() {
+    const byShort = new Map();
+    for (const full of geoProvinceNames) byShort.set(shortProvince(full), full);
+    const counts = new Map();
+    for (const school of schools) {
+      if (!(school.enabled && school.lng != null)) continue;
+      const full = byShort.get(shortProvince(school.province)) || school.province;
+      if (!full) continue;
+      counts.set(full, (counts.get(full) || 0) + 1);
+    }
+    return counts;
+  }
+
+  /** 省份按点亮学校数分档着色(比学校点更浅,避免压过点位) */
+  function provinceColor(count) {
+    const n = Number(count) || 0;
+    if (n >= 10) return '#ef8455';
+    if (n >= 4) return '#f4a684';
+    if (n >= 2) return '#f8c9b2';
+    if (n >= 1) return '#f9dccc';
+    return '#f7f5f2';
+  }
+
   function buildCityPoints(data) {
     const points = [];
     for (const info of Object.values(data || {})) {
@@ -83,8 +108,11 @@
         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 school = params.data && schools.find((s) => s.id === params.data.schoolId);
+          if (!school) {
+            const count = provinceCounts().get(params.name) || 0;
+            return `<b>${esc(params.name)}</b><br>点亮学校:${count} 所`;
+          }
           const status = school.enabled ? '已启用' : '已停用';
           const point = school.lng == null ? '未定位' : `${school.lng.toFixed(4)}, ${school.lat.toFixed(4)}`;
           return `<b>${esc(school.name)}</b><br>${esc(school.province || '—')}${school.city ? ' · ' + esc(school.city) : ''}<br>${status} · ${opsText(school)}<br><span style="color:#9a938c">${point}</span>`;
@@ -97,6 +125,10 @@
         zoom: ZOOM,
         scaleLimit: { min: 1, max: 12 },
         itemStyle: { areaColor: '#f7f5f2', borderColor: '#ddd6cd', borderWidth: 0.8 },
+        regions: [...provinceCounts().entries()].map(([name, count]) => ({
+          name,
+          itemStyle: { areaColor: provinceColor(count) },
+        })),
         label: { show: true, color: '#8f8478', fontSize: 10, formatter: (p) => shortProvince(p.name) },
         emphasis: { itemStyle: { areaColor: '#efeae4' }, label: { show: true, color: '#5b534c', fontSize: 10 } },
         select: { disabled: true },
@@ -163,7 +195,15 @@
       box.innerHTML = '';
       return;
     }
-    box.innerHTML = '<span class="schools-legend-title">门店数</span>'
+    const litProvinces = provinceCounts();
+    const maxLit = Math.max(0, ...litProvinces.values());
+    box.innerHTML = '<span class="schools-legend-title">每省点亮学校</span>'
+      + '<span><i style="background:#f9dccc"></i>1</span>'
+      + '<span><i style="background:#f8c9b2"></i>2-3</span>'
+      + '<span><i style="background:#f4a684"></i>4-9</span>'
+      + '<span><i style="background:#ef8455"></i>10+</span>'
+      + '<span class="schools-legend-sep"></span>'
+      + '<span class="schools-legend-title">门店数</span>'
       + '<span><i style="background:#d8d1c9"></i>0</span>'
       + '<span><i style="background:#ffb894"></i>1-3</span>'
       + '<span><i style="background:#ff8a5c"></i>4-9</span>'
@@ -266,6 +306,7 @@
       const geo = await geoRes.json();
       const data = await listRes.json();
       echarts.registerMap('china', geo);
+      geoProvinceNames = (geo.features || []).map((f) => (f.properties && f.properties.name) || '').filter(Boolean);
       if (cityRes && cityRes.ok) {
         try { cityPoints = buildCityPoints(await cityRes.json()); } catch { cityPoints = []; }
       }