Просмотр исходного кода

✨ feat(map): 实现贵州省银龄数据地图可视化

- 添加GuizhouMap组件,实现贵州省地图展示与交互功能
- 创建贵州区县数据文件,包含详细坐标和模拟数据生成函数
- 开发贵州数据专用hooks,支持地图数据、汇总数据和城市分组数据获取
- 更新首页仪表盘,替换全国地图为贵州地图,调整统计数据和图表展示

✨ feat(data): 添加贵州区县数据及处理功能

- 定义贵州区县数据接口和详细坐标数据
- 实现数据生成函数,创建模拟的银龄岗和银龄库数据
- 添加按城市分组功能,便于数据聚合展示

✨ refactor(dashboard): 更新首页仪表盘为贵州数据展示

- 替换全国地图为贵州省地图组件
- 更新KPI指标卡片数据来源为贵州统计数据
- 调整图表展示内容为贵州省各城市数据对比
- 修改排行榜组件显示贵州各城市数据排名
yourname 8 месяцев назад
Родитель
Сommit
a7643eb38a

+ 267 - 0
src/client/big-shadcn/components/GuizhouMap.tsx

@@ -0,0 +1,267 @@
+import { useEffect, useRef, useState } from 'react';
+import { motion } from 'framer-motion';
+import { AMapLoader } from './AMapLoader';
+
+// 高德地图组件
+declare global {
+  interface Window {
+    AMap: any;
+    AMapUI: any;
+  }
+}
+
+interface GuizhouDistrictData {
+  code: string;
+  name: string;
+  lat: number;
+  lng: number;
+  city: string;
+  dailyNewJobs?: number;
+  totalJobs?: number;
+  dailyNewTalents?: number;
+  totalTalents?: number;
+  [key: string]: any;
+}
+
+interface GuizhouMapProps {
+  data: GuizhouDistrictData[];
+  height?: string;
+  className?: string;
+}
+
+const GuizhouMap = ({ data, height = "100%", className = "" }: GuizhouMapProps) => {
+  const mapRef = useRef<HTMLDivElement>(null);
+  const [map, setMap] = useState<any>(null);
+  const [selectedDistrict, setSelectedDistrict] = useState<GuizhouDistrictData | null>(null);
+
+  useEffect(() => {
+    if (!window.AMap || !mapRef.current) return;
+
+    // 创建地图实例 - 聚焦贵州
+    const newMap = new window.AMap.Map(mapRef.current, {
+      zoom: 8,
+      center: [106.6302, 26.6470], // 贵阳市中心坐标
+      viewMode: '3D',
+      pitch: 45,
+      mapStyle: 'amap://styles/light', // 使用亮色主题调亮地图
+      resizeEnable: true,
+      showBuildingBlock: true,
+      showIndoorMap: false,
+      zooms: [7, 12] // 限制缩放级别,只显示贵州范围
+    });
+
+    // 添加控件
+    new window.AMap.plugin(['AMap.Scale', 'AMap.ToolBar'], () => {
+      newMap.addControl(new window.AMap.Scale());
+      newMap.addControl(new window.AMap.ToolBar({
+        position: 'RT',
+        offset: new window.AMap.Pixel(10, 10)
+      }));
+    });
+
+    // 设置贵州边界
+    setGuizhouBoundary(newMap);
+
+    setMap(newMap);
+
+    // 添加标记点
+    if (data && data.length > 0) {
+      addMarkers(newMap, data);
+    }
+
+    // 清理函数
+    return () => {
+      newMap.destroy();
+    };
+  }, []);
+
+  useEffect(() => {
+    if (map && data && data.length > 0) {
+      addMarkers(map, data);
+    }
+  }, [map, data]);
+
+  const setGuizhouBoundary = (mapInstance: any) => {
+    // 创建贵州边界 - 简化的贵州边界坐标
+    const guizhouBoundary = [
+      [109.5, 29.0], [109.5, 24.5], [103.5, 24.5], [103.5, 29.0]
+    ];
+    
+    // 添加边界多边形
+    const polygon = new window.AMap.Polygon({
+      path: guizhouBoundary,
+      strokeColor: '#3366FF',
+      strokeWeight: 3,
+      fillColor: 'transparent',
+      fillOpacity: 0
+    });
+    
+    mapInstance.add(polygon);
+    
+    // 设置视野到贵州范围
+    mapInstance.setBounds(polygon.getBounds());
+  };
+
+  const addMarkers = (mapInstance: any, markers: GuizhouDistrictData[]) => {
+    // 清除现有标记
+    mapInstance.clearMap();
+
+    markers.forEach((item) => {
+      // 计算圆圈大小
+      const total = (item.totalJobs || 0) + (item.totalTalents || 0);
+      const max = Math.max(...markers.map(d => (d.totalJobs || 0) + (d.totalTalents || 0)));
+      const radius = Math.max(8, (total / max) * 25);
+
+      // 计算颜色 - 使用更亮的配色
+      const ratio = total / max;
+      let color = '#00CC66'; // 亮绿色
+      if (ratio > 0.8) color = '#FF3366'; // 亮红色
+      else if (ratio > 0.6) color = '#FF9900'; // 亮橙色
+      else if (ratio > 0.4) color = '#33CCFF'; // 亮蓝色
+      else color = '#9966FF'; // 亮紫色
+
+      // 创建圆点标记
+      const circleMarker = new window.AMap.CircleMarker({
+        center: [item.lng, item.lat],
+        radius: radius,
+        strokeColor: '#FFFFFF',
+        strokeWeight: 2,
+        fillColor: color,
+        fillOpacity: 0.8,
+        zIndex: 100,
+        extData: item
+      });
+
+      // 添加点击事件
+      circleMarker.on('click', () => {
+        setSelectedDistrict(item);
+        showInfoWindow(mapInstance, item, [item.lng, item.lat]);
+      });
+
+      // 添加鼠标悬停事件
+      circleMarker.on('mouseover', () => {
+        circleMarker.setOptions({
+          radius: radius * 1.3,
+          fillOpacity: 1,
+          strokeWeight: 3
+        });
+      });
+
+      circleMarker.on('mouseout', () => {
+        circleMarker.setOptions({
+          radius: radius,
+          fillOpacity: 0.8,
+          strokeWeight: 2
+        });
+      });
+
+      mapInstance.add(circleMarker);
+    });
+  };
+
+  const showInfoWindow = (mapInstance: any, district: GuizhouDistrictData, position: [number, number]) => {
+    const content = `
+      <div class="bg-white/95 backdrop-blur-md text-gray-800 p-4 rounded-lg border border-gray-300 shadow-lg min-w-56">
+        <h4 class="font-bold text-lg mb-2 text-blue-600">${district.name}</h4>
+        <div class="text-sm space-y-1">
+          <div class="flex justify-between">
+            <span class="text-gray-600">所属城市:</span>
+            <span class="font-semibold">${district.city}</span>
+          </div>
+          <hr class="my-2">
+          <div class="flex justify-between">
+            <span class="text-green-600">每日新增银龄岗:</span>
+            <span class="font-bold text-green-600">${district.dailyNewJobs || 0}</span>
+          </div>
+          <div class="flex justify-between">
+            <span class="text-blue-600">累计银龄岗:</span>
+            <span class="font-bold text-blue-600">${district.totalJobs || 0}</span>
+          </div>
+          <div class="flex justify-between">
+            <span class="text-purple-600">每日新增银龄库:</span>
+            <span class="font-bold text-purple-600">${district.dailyNewTalents || 0}</span>
+          </div>
+          <div class="flex justify-between">
+            <span class="text-orange-600">累计银龄库:</span>
+            <span class="font-bold text-orange-600">${district.totalTalents || 0}</span>
+          </div>
+        </div>
+      </div>
+    `;
+
+    const infoWindow = new window.AMap.InfoWindow({
+      content: content,
+      offset: new window.AMap.Pixel(0, -30),
+      closeWhenClickMap: true
+    });
+
+    infoWindow.open(mapInstance, position);
+  };
+
+  return (
+    <div className={`relative ${className}`} style={{ height }}>
+      <div ref={mapRef} className="w-full h-full rounded-xl overflow-hidden" />
+      
+      {/* 地图标题 */}
+      <motion.div 
+        className="absolute top-4 left-4 bg-white/80 backdrop-blur-sm px-4 py-2 rounded-lg border border-gray-200 shadow-md"
+        initial={{ opacity: 0, y: -20 }}
+        animate={{ opacity: 1, y: 0 }}
+        transition={{ duration: 0.5 }}
+      >
+        <h3 className="text-xl font-bold text-gray-800">贵州省银龄数据分布</h3>
+        <p className="text-sm text-gray-600">点击区县查看详细数据</p>
+      </motion.div>
+
+      {/* 图例 */}
+      <motion.div 
+        className="absolute bottom-4 left-4 bg-white/80 backdrop-blur-sm px-3 py-2 rounded-lg border border-gray-200 shadow-md"
+        initial={{ opacity: 0, y: 20 }}
+        animate={{ opacity: 1, y: 0 }}
+        transition={{ duration: 0.5, delay: 0.2 }}
+      >
+        <div className="text-sm text-gray-700 space-y-1">
+          <div className="flex items-center space-x-2">
+            <div className="w-3 h-3 rounded-full bg-red-500"></div>
+            <span>数据量高</span>
+          </div>
+          <div className="flex items-center space-x-2">
+            <div className="w-3 h-3 rounded-full bg-orange-500"></div>
+            <span>数据量中高</span>
+          </div>
+          <div className="flex items-center space-x-2">
+            <div className="w-3 h-3 rounded-full bg-blue-500"></div>
+            <span>数据量中</span>
+          </div>
+          <div className="flex items-center space-x-2">
+            <div className="w-3 h-3 rounded-full bg-purple-500"></div>
+            <span>数据量低</span>
+          </div>
+        </div>
+      </motion.div>
+
+      {/* 统计信息 */}
+      <motion.div 
+        className="absolute top-4 right-4 bg-white/80 backdrop-blur-sm px-4 py-2 rounded-lg border border-gray-200 shadow-md"
+        initial={{ opacity: 0, x: 20 }}
+        animate={{ opacity: 1, x: 0 }}
+        transition={{ duration: 0.5, delay: 0.3 }}
+      >
+        <div className="text-sm text-gray-700">
+          <div>区县数量: {data.length}</div>
+          <div>总银龄岗: {data.reduce((sum, d) => sum + (d.totalJobs || 0), 0)}</div>
+          <div>总银龄库: {data.reduce((sum, d) => sum + (d.totalTalents || 0), 0)}</div>
+        </div>
+      </motion.div>
+
+      {/* 加载提示 */}
+      {!map && (
+        <div className="absolute inset-0 flex items-center justify-center bg-white/20">
+          <div className="text-gray-700 text-lg font-semibold">正在加载贵州地图...</div>
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default GuizhouMap;

+ 144 - 0
src/client/big-shadcn/data/guizhouDistricts.ts

@@ -0,0 +1,144 @@
+// 贵州省各区县数据
+export interface GuizhouDistrictData {
+  code: string;
+  name: string;
+  lat: number;
+  lng: number;
+  city: string;
+  dailyNewJobs?: number;
+  totalJobs?: number;
+  dailyNewTalents?: number;
+  totalTalents?: number;
+}
+
+// 贵州省各区县详细坐标数据
+export const guizhouDistricts: GuizhouDistrictData[] = [
+  // 贵阳市
+  { code: '520102', name: '南明区', lat: 26.5767, lng: 106.7135, city: '贵阳市' },
+  { code: '520103', name: '云岩区', lat: 26.6062, lng: 106.7170, city: '贵阳市' },
+  { code: '520111', name: '花溪区', lat: 26.4046, lng: 106.6688, city: '贵阳市' },
+  { code: '520112', name: '乌当区', lat: 26.6307, lng: 106.7517, city: '贵阳市' },
+  { code: '520113', name: '白云区', lat: 26.6794, lng: 106.6542, city: '贵阳市' },
+  { code: '520115', name: '观山湖区', lat: 26.6264, lng: 106.6290, city: '贵阳市' },
+  { code: '520121', name: '开阳县', lat: 27.0596, lng: 106.9578, city: '贵阳市' },
+  { code: '520122', name: '息烽县', lat: 27.0938, lng: 106.7383, city: '贵阳市' },
+  { code: '520123', name: '修文县', lat: 26.8416, lng: 106.5951, city: '贵阳市' },
+  { code: '520181', name: '清镇市', lat: 26.5528, lng: 106.4640, city: '贵阳市' },
+
+  // 六盘水市
+  { code: '520201', name: '钟山区', lat: 26.5917, lng: 104.8297, city: '六盘水市' },
+  { code: '520203', name: '六枝特区', lat: 26.2089, lng: 105.4779, city: '六盘水市' },
+  { code: '520221', name: '水城区', lat: 26.5447, lng: 104.9526, city: '六盘水市' },
+  { code: '520281', name: '盘州市', lat: 25.7079, lng: 104.4736, city: '六盘水市' },
+
+  // 遵义市
+  { code: '520302', name: '红花岗区', lat: 27.6889, lng: 106.9317, city: '遵义市' },
+  { code: '520303', name: '汇川区', lat: 27.7744, lng: 106.9339, city: '遵义市' },
+  { code: '520304', name: '播州区', lat: 27.5355, lng: 106.8296, city: '遵义市' },
+  { code: '520322', name: '桐梓县', lat: 28.1322, lng: 106.8254, city: '遵义市' },
+  { code: '520323', name: '绥阳县', lat: 27.9457, lng: 107.1918, city: '遵义市' },
+  { code: '520324', name: '正安县', lat: 28.5525, lng: 107.4427, city: '遵义市' },
+  { code: '520325', name: '道真县', lat: 28.8906, lng: 107.6093, city: '遵义市' },
+  { code: '520326', name: '务川县', lat: 28.5608, lng: 107.8919, city: '遵义市' },
+  { code: '520327', name: '凤冈县', lat: 27.9561, lng: 107.7156, city: '遵义市' },
+  { code: '520328', name: '湄潭县', lat: 27.7498, lng: 107.4663, city: '遵义市' },
+  { code: '520329', name: '余庆县', lat: 27.2213, lng: 107.8936, city: '遵义市' },
+  { code: '520330', name: '习水县', lat: 28.3318, lng: 106.2015, city: '遵义市' },
+  { code: '520381', name: '赤水市', lat: 28.5895, lng: 105.6986, city: '遵义市' },
+  { code: '520382', name: '仁怀市', lat: 27.8043, lng: 106.4082, city: '遵义市' },
+
+  // 安顺市
+  { code: '520402', name: '西秀区', lat: 26.2287, lng: 105.9417, city: '安顺市' },
+  { code: '520403', name: '平坝区', lat: 26.4046, lng: 106.2548, city: '安顺市' },
+  { code: '520422', name: '普定县', lat: 26.3017, lng: 105.7431, city: '安顺市' },
+  { code: '520423', name: '镇宁县', lat: 25.7650, lng: 105.7708, city: '安顺市' },
+  { code: '520424', name: '关岭县', lat: 25.9398, lng: 105.6153, city: '安顺市' },
+  { code: '520425', name: '紫云县', lat: 25.7506, lng: 106.0800, city: '安顺市' },
+
+  // 毕节市
+  { code: '520502', name: '七星关区', lat: 27.3020, lng: 105.2834, city: '毕节市' },
+  { code: '520521', name: '大方县', lat: 27.1435, lng: 105.6089, city: '毕节市' },
+  { code: '520522', name: '黔西县', lat: 27.0115, lng: 106.0335, city: '毕节市' },
+  { code: '520523', name: '金沙县', lat: 27.4590, lng: 106.2207, city: '毕节市' },
+  { code: '520524', name: '织金县', lat: 26.6647, lng: 105.7690, city: '毕节市' },
+  { code: '520525', name: '纳雍县', lat: 26.7653, lng: 105.3762, city: '毕节市' },
+  { code: '520526', name: '威宁县', lat: 26.8718, lng: 104.2868, city: '毕节市' },
+  { code: '520527', name: '赫章县', lat: 27.1230, lng: 104.7256, city: '毕节市' },
+
+  // 铜仁市
+  { code: '520602', name: '碧江区', lat: 27.7315, lng: 109.1896, city: '铜仁市' },
+  { code: '520603', name: '万山区', lat: 27.5142, lng: 109.2135, city: '铜仁市' },
+  { code: '520621', name: '江口县', lat: 27.6973, lng: 108.8450, city: '铜仁市' },
+  { code: '520622', name: '玉屏县', lat: 27.2395, lng: 108.9139, city: '铜仁市' },
+  { code: '520623', name: '石阡县', lat: 27.5175, lng: 108.2238, city: '铜仁市' },
+  { code: '520624', name: '思南县', lat: 27.9407, lng: 108.2489, city: '铜仁市' },
+  { code: '520625', name: '印江县', lat: 28.0043, lng: 108.4089, city: '铜仁市' },
+  { code: '520626', name: '德江县', lat: 28.2610, lng: 108.1179, city: '铜仁市' },
+  { code: '520627', name: '沿河县', lat: 28.5619, lng: 108.5019, city: '铜仁市' },
+  { code: '520628', name: '松桃县', lat: 28.1688, lng: 109.1989, city: '铜仁市' },
+
+  // 黔西南州
+  { code: '522301', name: '兴义市', lat: 25.0914, lng: 104.8959, city: '黔西南州' },
+  { code: '522322', name: '兴仁市', lat: 25.4316, lng: 105.1918, city: '黔西南州' },
+  { code: '522323', name: '普安县', lat: 25.7844, lng: 104.9549, city: '黔西南州' },
+  { code: '522324', name: '晴隆县', lat: 25.8349, lng: 105.2187, city: '黔西南州' },
+  { code: '522325', name: '贞丰县', lat: 25.3867, lng: 105.6505, city: '黔西南州' },
+  { code: '522326', name: '望谟县', lat: 25.1745, lng: 106.0912, city: '黔西南州' },
+  { code: '522327', name: '册亨县', lat: 24.9834, lng: 105.8130, city: '黔西南州' },
+  { code: '522328', name: '安龙县', lat: 25.1405, lng: 105.4718, city: '黔西南州' },
+
+  // 黔东南州
+  { code: '522601', name: '凯里市', lat: 26.5830, lng: 107.9774, city: '黔东南州' },
+  { code: '522622', name: '黄平县', lat: 26.8996, lng: 107.8956, city: '黔东南州' },
+  { code: '522623', name: '施秉县', lat: 27.0308, lng: 108.1215, city: '黔东南州' },
+  { code: '522624', name: '三穗县', lat: 26.9546, lng: 108.6750, city: '黔东南州' },
+  { code: '522625', name: '镇远县', lat: 27.0502, lng: 108.4333, city: '黔东南州' },
+  { code: '522626', name: '岑巩县', lat: 27.2141, lng: 108.8098, city: '黔东南州' },
+  { code: '522627', name: '天柱县', lat: 26.9088, lng: 109.2077, city: '黔东南州' },
+  { code: '522628', name: '锦屏县', lat: 26.6787, lng: 109.2045, city: '黔东南州' },
+  { code: '522629', name: '剑河县', lat: 26.7282, lng: 108.4503, city: '黔东南州' },
+  { code: '522630', name: '台江县', lat: 26.6688, lng: 108.3208, city: '黔东南州' },
+  { code: '522631', name: '黎平县', lat: 26.2306, lng: 109.1362, city: '黔东南州' },
+  { code: '522632', name: '榕江县', lat: 25.9322, lng: 108.5248, city: '黔东南州' },
+  { code: '522633', name: '从江县', lat: 25.7493, lng: 108.9061, city: '黔东南州' },
+  { code: '522634', name: '雷山县', lat: 26.3783, lng: 108.0796, city: '黔东南州' },
+  { code: '522635', name: '麻江县', lat: 26.4928, lng: 107.5883, city: '黔东南州' },
+  { code: '522636', name: '丹寨县', lat: 26.2000, lng: 107.7900, city: '黔东南州' },
+
+  // 黔南州
+  { code: '522701', name: '都匀市', lat: 26.2604, lng: 107.5178, city: '黔南州' },
+  { code: '522702', name: '福泉市', lat: 26.7000, lng: 107.5100, city: '黔南州' },
+  { code: '522722', name: '荔波县', lat: 25.4120, lng: 107.8800, city: '黔南州' },
+  { code: '522723', name: '贵定县', lat: 26.5800, lng: 107.2300, city: '黔南州' },
+  { code: '522725', name: '瓮安县', lat: 27.0800, lng: 107.4700, city: '黔南州' },
+  { code: '522726', name: '独山县', lat: 25.8300, lng: 107.5400, city: '黔南州' },
+  { code: '522727', name: '平塘县', lat: 25.8300, lng: 107.3300, city: '黔南州' },
+  { code: '522728', name: '罗甸县', lat: 25.4300, lng: 106.6700, city: '黔南州' },
+  { code: '522729', name: '长顺县', lat: 26.0300, lng: 106.4500, city: '黔南州' },
+  { code: '522730', name: '龙里县', lat: 26.4500, lng: 106.9800, city: '黔南州' },
+  { code: '522731', name: '惠水县', lat: 26.1300, lng: 106.6600, city: '黔南州' },
+  { code: '522732', name: '三都县', lat: 25.9800, lng: 107.8700, city: '黔南州' }
+];
+
+// 生成贵州区县模拟数据
+export const generateGuizhouData = () => {
+  return guizhouDistricts.map(district => ({
+    ...district,
+    dailyNewJobs: Math.floor(Math.random() * 100) + 10,
+    totalJobs: Math.floor(Math.random() * 800) + 100,
+    dailyNewTalents: Math.floor(Math.random() * 80) + 8,
+    totalTalents: Math.floor(Math.random() * 600) + 60
+  }));
+};
+
+// 按城市分组
+export const getDistrictsByCity = () => {
+  const cityMap: { [key: string]: GuizhouDistrictData[] } = {};
+  guizhouDistricts.forEach(district => {
+    if (!cityMap[district.city]) {
+      cityMap[district.city] = [];
+    }
+    cityMap[district.city].push(district);
+  });
+  return cityMap;
+};

+ 85 - 0
src/client/big-shadcn/hooks/useGuizhouMapData.ts

@@ -0,0 +1,85 @@
+import { useQuery } from '@tanstack/react-query';
+import { generateGuizhouData } from '../data/guizhouDistricts';
+import type { GuizhouDistrictData } from '../data/guizhouDistricts';
+
+// 模拟API调用
+const fetchGuizhouDistricts = async (): Promise<GuizhouDistrictData[]> => {
+  // 模拟网络延迟
+  await new Promise(resolve => setTimeout(resolve, 1000));
+  
+  // 生成模拟数据
+  const data = generateGuizhouData();
+  
+  return data;
+};
+
+// 获取贵州区县数据
+export const useGuizhouMapData = () => {
+  return useQuery({
+    queryKey: ['guizhou-districts-data'],
+    queryFn: fetchGuizhouDistricts,
+    refetchInterval: 30000, // 每30秒刷新一次
+    staleTime: 5 * 60 * 1000, // 5分钟
+  });
+};
+
+// 获取贵州统计数据
+export const useGuizhouSummary = () => {
+  return useQuery({
+    queryKey: ['guizhou-summary'],
+    queryFn: async () => {
+      const districts = await fetchGuizhouDistricts();
+      
+      const totalJobs = districts.reduce((sum, district) => sum + (district.totalJobs || 0), 0);
+      const totalTalents = districts.reduce((sum, district) => sum + (district.totalTalents || 0), 0);
+      const totalDailyNewJobs = districts.reduce((sum, district) => sum + (district.dailyNewJobs || 0), 0);
+      const totalDailyNewTalents = districts.reduce((sum, district) => sum + (district.dailyNewTalents || 0), 0);
+      
+      return {
+        totalJobs,
+        totalTalents,
+        totalDailyNewJobs,
+        totalDailyNewTalents,
+        districtCount: districts.length,
+        cityCount: new Set(districts.map(d => d.city)).size
+      };
+    },
+    refetchInterval: 30000,
+    staleTime: 5 * 60 * 1000,
+  });
+};
+
+// 按城市分组的数据
+export const useGuizhouDataByCity = () => {
+  return useQuery({
+    queryKey: ['guizhou-data-by-city'],
+    queryFn: async () => {
+      const districts = await fetchGuizhouDistricts();
+      
+      const cityMap = districts.reduce((acc, district) => {
+        const city = district.city;
+        if (!acc[city]) {
+          acc[city] = {
+            name: city,
+            districts: [],
+            totalJobs: 0,
+            totalTalents: 0,
+            dailyNewJobs: 0,
+            dailyNewTalents: 0
+          };
+        }
+        acc[city].districts.push(district);
+        acc[city].totalJobs += district.totalJobs || 0;
+        acc[city].totalTalents += district.totalTalents || 0;
+        acc[city].dailyNewJobs += district.dailyNewJobs || 0;
+        acc[city].dailyNewTalents += district.dailyNewTalents || 0;
+        
+        return acc;
+      }, {} as Record<string, any>);
+      
+      return Object.values(cityMap).sort((a: any, b: any) => b.totalJobs + b.totalTalents - (a.totalJobs + a.totalTalents));
+    },
+    refetchInterval: 30000,
+    staleTime: 5 * 60 * 1000,
+  });
+};

+ 42 - 51
src/client/big-shadcn/pages/HomePage.tsx

@@ -6,12 +6,10 @@ import { useQuery } from '@tanstack/react-query';
 import CountUp from 'react-countup';
 import { useInView } from 'react-intersection-observer';
 import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
-import { Maximize2, Minimize2, TrendingUp, Users, Briefcase, BookOpen } from 'lucide-react';
-import { useChinaMapData, useChinaSummary } from '../hooks/useChinaMapData';
-import ChinaAMap from '../components/ChinaAMap';
+import { Maximize2, Minimize2, TrendingUp, Users, Briefcase, MapPin } from 'lucide-react';
+import { useGuizhouMapData, useGuizhouSummary, useGuizhouDataByCity } from '../hooks/useGuizhouMapData';
+import GuizhouMap from '../components/GuizhouMap';
 import { AMapLoader } from '../components/AMapLoader';
-import { mapConfig } from '../config/mapConfig';
-import { generateSimulatedData, chinaCities } from '../data/chinaCities';
 
 // 玻璃拟态卡片组件
 const GlassCard = ({ children, className = '' }) => (
@@ -97,19 +95,19 @@ const KpiCard = ({ title, value, unit, trend, color = 'cyan', icon: Icon }) => {
   );
 };
 
-// 排行榜组件 - 使用中国数据
+// 排行榜组件 - 使用贵州数据
 const CityRankingList = ({ data }) => {
   const sortedCities = [...data].sort((a, b) =>
     (b.totalJobs + b.totalTalents) - (a.totalJobs + a.totalTalents)
-  ).slice(0, 10); // 只显示前10名
+  );
 
   return (
     <div className="h-full">
-      <h3 className="text-lg font-semibold text-cyan-400 mb-4">全国城市数据排行</h3>
+      <h3 className="text-lg font-semibold text-cyan-400 mb-4">贵州城市数据排行</h3>
       <div className="space-y-2">
         {sortedCities.map((city, index) => (
           <motion.div
-            key={city.code}
+            key={city.name}
             className="flex items-center space-x-3 p-3 rounded-lg bg-white/5 hover:bg-white/10 transition-colors"
             initial={{ opacity: 0, x: -20 }}
             animate={{ opacity: 1, x: 0 }}
@@ -127,7 +125,7 @@ const CityRankingList = ({ data }) => {
             <div className="flex-1 min-w-0">
               <p className="text-white font-medium truncate">{city.name}</p>
               <p className="text-gray-400 text-xs">
-                {city.province} | 岗: {city.totalJobs} 库: {city.totalTalents}
+                区县: {city.districts?.length || 0} | 岗: {city.totalJobs} 库: {city.totalTalents}
               </p>
             </div>
             <div className="text-cyan-400 font-bold text-sm">
@@ -140,8 +138,8 @@ const CityRankingList = ({ data }) => {
   );
 };
 
-// 实时图表组件 - 使用中国数据
-const ChinaChart = ({ type = 'line', title, data }) => {
+// 实时图表组件 - 使用贵州数据
+const GuizhouChart = ({ type = 'line', title, data }) => {
   const CustomTooltip = ({ active, payload, label }) => {
     if (active && payload && payload.length) {
       return (
@@ -158,18 +156,12 @@ const ChinaChart = ({ type = 'line', title, data }) => {
     return null;
   };
 
-  // 准备图表数据 - 按省份分组
-  const provinceData = data.reduce((acc, city) => {
-    const province = city.province;
-    if (!acc[province]) {
-      acc[province] = { name: province, 银龄岗: 0, 银龄库: 0 };
-    }
-    acc[province].银龄岗 += city.totalJobs || 0;
-    acc[province].银龄库 += city.totalTalents || 0;
-    return acc;
-  }, {});
-
-  const chartData = Object.values(provinceData).slice(0, 10); // 取前10个省份
+  // 准备图表数据 - 按城市分组
+  const chartData = data.map(city => ({
+    name: city.name,
+    银龄岗: city.totalJobs || 0,
+    银龄库: city.totalTalents || 0
+  }));
 
   const COLORS = ['#00ff88', '#00ffff', '#ff00ff', '#faad14', '#ff4d4f', '#52c41a', '#1890ff', '#722ed1', '#fa8c16', '#13c2c2'];
 
@@ -216,36 +208,36 @@ const ChinaChart = ({ type = 'line', title, data }) => {
             </linearGradient>
           </defs>
           <CartesianGrid strokeDasharray="3 3" stroke="#ffffff20" />
-          <XAxis 
-            dataKey="name" 
+          <XAxis
+            dataKey="name"
             stroke="#ffffff60"
-            tick={{ fill: '#ffffff80', fontSize: 10 }}
+            tick={{ fill: '#ffffff80', fontSize: 12 }}
             angle={-45}
             textAnchor="end"
-            height={50}
+            height={60}
           />
-          <YAxis 
+          <YAxis
             stroke="#ffffff60"
             tick={{ fill: '#ffffff80' }}
           />
           <Tooltip content={<CustomTooltip />} />
-          <Area 
-            type="monotone" 
-            dataKey="银龄岗" 
+          <Area
+            type="monotone"
+            dataKey="银龄岗"
             stackId="1"
-            stroke="#00ff88" 
+            stroke="#00ff88"
             strokeWidth={2}
-            fillOpacity={1} 
-            fill="url(#colorJobs)" 
+            fillOpacity={1}
+            fill="url(#colorJobs)"
           />
-          <Area 
-            type="monotone" 
-            dataKey="银龄库" 
+          <Area
+            type="monotone"
+            dataKey="银龄库"
             stackId="1"
-            stroke="#00ffff" 
+            stroke="#00ffff"
             strokeWidth={2}
-            fillOpacity={1} 
-            fill="url(#colorTalents)" 
+            fillOpacity={1}
+            fill="url(#colorTalents)"
           />
         </AreaChart>
       </ResponsiveContainer>
@@ -359,8 +351,9 @@ const FullscreenToggle = () => {
 
 // 主仪表盘组件
 const DashboardGrid = () => {
-  const { data: cities = [] } = useChinaMapData();
-  const { data: summary } = useChinaSummary();
+  const { data: districts = [] } = useGuizhouMapData();
+  const { data: summary } = useGuizhouSummary();
+  const { data: cities = [] } = useGuizhouDataByCity();
   
   return (
     <div className="flex-1 p-4 md:p-8 grid grid-cols-12 gap-4">
@@ -373,7 +366,7 @@ const DashboardGrid = () => {
       >
         <div className="h-full glass-card rounded-xl flex items-center justify-center px-6">
           <h1 className="text-2xl md:text-4xl font-bold bg-gradient-to-r from-cyan-400 to-purple-400 bg-clip-text text-transparent">
-            全国银龄智慧数据大屏
+            贵州省银龄智慧数据大屏
           </h1>
           <div className="absolute right-6 flex items-center space-x-4">
             <DigitalClock />
@@ -423,7 +416,7 @@ const DashboardGrid = () => {
         />
       </motion.div>
 
-      {/* 中间主地图区 - 高德地图 */}
+      {/* 中间主地图区 - 贵州地图 */}
       <motion.div
         className="col-span-12 md:col-span-6"
         initial={{ opacity: 0, scale: 0.9 }}
@@ -431,9 +424,7 @@ const DashboardGrid = () => {
         transition={{ duration: 0.8, delay: 0.4 }}
       >
         <div className="h-full glass-card rounded-xl p-4">
-          <AMapLoader apiKey={mapConfig.gaodeApiKey}>
-            <ChinaAMap data={cities} />
-          </AMapLoader>
+          <GuizhouMap data={districts} />
         </div>
       </motion.div>
 
@@ -449,7 +440,7 @@ const DashboardGrid = () => {
         </div>
       </motion.div>
 
-      {/* 底部图表区 - 全国数据 */}
+      {/* 底部图表区 - 贵州数据 */}
       <motion.div
         className="col-span-12 md:col-span-6"
         initial={{ opacity: 0, y: 50 }}
@@ -457,7 +448,7 @@ const DashboardGrid = () => {
         transition={{ duration: 0.8, delay: 0.8 }}
       >
         <div className="h-64 glass-card rounded-xl p-4">
-          <ChinaChart type="line" title="各省份银龄数据对比" data={cities} />
+          <GuizhouChart type="line" title="贵州各市银龄数据对比" data={cities} />
         </div>
       </motion.div>
 
@@ -468,7 +459,7 @@ const DashboardGrid = () => {
         transition={{ duration: 0.8, delay: 1.0 }}
       >
         <div className="h-64 glass-card rounded-xl p-4">
-          <ChinaChart type="pie" title="银龄岗省份分布占比" data={cities} />
+          <GuizhouChart type="pie" title="银龄岗贵州各市分布占比" data={cities} />
         </div>
       </motion.div>
     </div>