Ver Fonte

更新地图组件,优化标记点数据处理逻辑,增加全局配置获取方式,提升代码可维护性和用户体验。同时,新增地图标记数据接口定义,确保数据结构一致性。

zyh há 8 meses atrás
pai
commit
e6ef6d656e
4 ficheiros alterados com 81 adições e 46 exclusões
  1. 7 4
      client/admin/components_amap.tsx
  2. 17 13
      client/admin/pages_map.tsx
  3. 51 29
      client/share/types.ts
  4. 6 0
      deno.lock

+ 7 - 4
client/admin/components_amap.tsx

@@ -1,5 +1,7 @@
 import React, { useEffect, useRef } from 'react';
 import { useQuery, useQueryClient } from '@tanstack/react-query';
+import { getGlobalConfig } from './utils.ts';
+import type { GlobalConfig } from '../share/types.ts';
 import { Spin } from 'antd';
 import './style_amap.css';
 import { MapMode, MarkerData } from '../share/types.ts';
@@ -7,9 +9,9 @@ import { MapMode, MarkerData } from '../share/types.ts';
 // 在线地图配置
 export const AMAP_ONLINE_CONFIG = {
   // 高德地图 Web API 密钥
-  API_KEY: window.CONFIG?.MAP_CONFIG?.KEY,
+  API_KEY: getGlobalConfig('MAP_CONFIG')?.KEY || '',
   // 主JS文件路径
-  MAIN_JS: 'https://webapi.amap.com/maps?v=2.0&key=' + window.CONFIG?.MAP_CONFIG?.KEY,
+  MAIN_JS: 'https://webapi.amap.com/maps?v=2.0&key=' + (getGlobalConfig('MAP_CONFIG')?.KEY || ''),
   // 插件列表
   PLUGINS: ['AMap.MouseTool', 'AMap.RangingTool', 'AMap.Scale', 'AMap.ToolBar', 'AMap.MarkerCluster'],
 };
@@ -82,6 +84,7 @@ export interface AMapInstance {
 declare global {
   interface Window {
     AMap: any;
+    CONFIG?: GlobalConfig;
   }
 }
 
@@ -338,7 +341,7 @@ const AMapComponent: React.FC<AMapProps> = ({
   height = '400px',
   center = TILE_CONFIG.DEFAULT_CENTER as [number, number],
   zoom = TILE_CONFIG.DEFAULT_ZOOM,
-  mode = window.CONFIG?.MAP_CONFIG?.MAP_MODE || MapMode.ONLINE,
+  mode = (getGlobalConfig('MAP_CONFIG')?.MAP_MODE as MapMode) || MapMode.ONLINE,
   onMarkerClick,
   onClick,
   markers = [],
@@ -438,4 +441,4 @@ const AMapComponent: React.FC<AMapProps> = ({
   );
 };
 
-export default AMapComponent;
+export default AMapComponent;

+ 17 - 13
client/admin/pages_map.tsx

@@ -108,16 +108,18 @@ export const LoginMapPage = () => {
 
   // 渲染地图标记点
   const renderMarkers = (locations: LoginLocation[]): MarkerData[] => {
-    return locations.map(location => ({
-      id: location.id,
-      longitude: location.longitude,
-      latitude: location.latitude,
-      title: location.user.nickname || location.user.username,
-      description: `登录时间: ${dayjs(location.loginTime).format('YYYY-MM-DD HH:mm:ss')}\nIP地址: ${location.ipAddress}`,
-      status: 'online',
-      type: 'login',
-      extraData: location
-    }));
+    return locations
+      .filter(location => location.longitude !== null && location.latitude !== null)
+      .map(location => ({
+        id: location.id?.toString() || '',
+        longitude: location.longitude as number,
+        latitude: location.latitude as number,
+        title: location.user?.nickname || location.user?.username || '未知用户',
+        description: `登录时间: ${dayjs(location.login_time).format('YYYY-MM-DD HH:mm:ss')}\nIP地址: ${location.ip_address}`,
+        status: 'online',
+        type: 'login',
+        extraData: location
+      }));
   };
 
   return (
@@ -156,7 +158,9 @@ export const LoginMapPage = () => {
           <div style={{ height: '100%', minHeight: '500px' }}>
             <AMap
               markers={renderMarkers(locations)}
-              center={locations[0] ? [locations[0].longitude, locations[0].latitude] : undefined}
+              center={locations[0] && locations[0].longitude !== null && locations[0].latitude !== null 
+                ? [locations[0].longitude, locations[0].latitude] as [number, number] 
+                : undefined}
               onMarkerClick={handleMarkerClick}
               height={'100%'}
             />
@@ -179,7 +183,7 @@ export const LoginMapPage = () => {
         ) : markerDetail ? (
           <Descriptions column={1}>
             <Descriptions.Item label={<><UserOutlined /> 用户</>}>
-              {markerDetail.user.nickname || markerDetail.user.username}
+              {markerDetail.user?.nickname || markerDetail.user?.username || '未知用户'}
             </Descriptions.Item>
             <Descriptions.Item label={<><ClockCircleOutlined /> 登录时间</>}>
               {dayjs(markerDetail.login_time).format('YYYY-MM-DD HH:mm:ss')}
@@ -208,4 +212,4 @@ export const LoginMapPage = () => {
       </Drawer>
     </div>
   );
-};
+};

+ 51 - 29
client/share/types.ts

@@ -161,6 +161,37 @@ export enum MapMode {
   OFFLINE = 'offline'
 }
 
+
+// 地图标记数据接口 - 基础定义
+export interface MarkerData {
+  /** 标记点经度 */
+  longitude: number;
+  
+  /** 标记点纬度 */
+  latitude: number;
+  
+  /** 标记点ID */
+  id?: string | number;
+  
+  /** 标记点标题 */
+  title?: string;
+  
+  /** 标记点描述 */
+  description?: string;
+  
+  /** 标记点图标URL */
+  iconUrl?: string;
+  
+  /** 标记点状态 */
+  status?: string;
+  
+  /** 标记点类型 */
+  type?: string;
+  
+  /** 标记点额外数据 */
+  extraData?: Record<string, any>;
+}
+
 // 审核状态枚举
 export enum AuditStatus {
   PENDING = 0,   // 待审核
@@ -424,46 +455,37 @@ export interface KnowInfo {
   updated_at: string;
 }
 
-// 登录位置详细信息
+// 登录位置相关类型定义
+export interface LoginLocation {
+  id: number;
+  loginTime: string;
+  ipAddress: string;
+  longitude: number;
+  latitude: number;
+  location_name?: string;
+  user: {
+    id: number;
+    username: string;
+    nickname: string;
+  };
+}
+
 export interface LoginLocationDetail {
-  /** 记录ID */
   id: number;
-  /** 用户ID */
   user_id: number;
-  /** 登录时间 */
   login_time: string;
-  /** IP地址 */
   ip_address: string;
-  /** 用户代理 */
+  longitude: number;
+  latitude: number;
+  location_name: string;
   user_agent: string;
-  /** 纬度 */
-  latitude: number | null;
-  /** 经度 */
-  longitude: number | null;
-  /** 位置名称 */
-  location_name?: string;
-  /** 关联用户信息 */
-  user?: {
+  user: {
     id: number;
     username: string;
-    nickname?: string;
+    nickname: string;
   };
 }
 
-// 登录位置信息
-export interface LoginLocation {
-  /** 纬度 */
-  latitude: number | null;
-  /** 经度 */
-  longitude: number | null;
-  /** IP地址 */
-  ip_address?: string;
-  /** 用户代理 */
-  user_agent?: string;
-  /** 登录时间 */
-  login_time?: string;
-}
-
 // 消息类型枚举
 export enum MessageType {
   SYSTEM = 'system',   // 系统通知

+ 6 - 0
deno.lock

@@ -643,6 +643,10 @@
     "https://esm.d8d.fun/@deno/shim-deno-test@0.5.0?target=denonext": "503b73de1a14bd33782220e11fa2b33e9c87d574ac793e7addf1466c5436e66a",
     "https://esm.d8d.fun/@deno/shim-deno@0.18.2/denonext/shim-deno.mjs": "819d8ac34fdaf60658cf03d137f14adaff3f13a279ffd79cd8797d84a6ac46ab",
     "https://esm.d8d.fun/@deno/shim-deno@0.18.2?target=denonext": "ffa3ca347bb6b6530720158f307a2e31b16728fbb52e6432254a07d52fcbc404",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0": "5e99f4d40ce60c55b5cf421c3cf3f13df1707cf53152e447b2332570412cd77a",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0,react-dom@19.0.0": "e3940182b574da537337b1e90a1b7f380e17050457423e13d5ac8c7bc88a3cc0",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/solid.mjs": "dcbd0c377d92857b6eb23c7dbb2ee6e650b12aa6ae1ef7fcc10dc1964df8ba47",
     "https://esm.d8d.fun/@socket.io/component-emitter@3.1.2/denonext/component-emitter.mjs": "3c6c5f2d64d4933b577a7117df1d8855c51ff01ab3dea8f42af1adcb1a5989e7",
     "https://esm.d8d.fun/@socket.io/component-emitter@3.1.2?target=denonext": "f6ff0f94ae3c9850a2c3a925cc2b236ec03a80fc2298d0ca48c2a90b10487db3",
     "https://esm.d8d.fun/@tanstack/query-core@5.67.1/denonext/query-core.mjs": "3001acc66d3efeab4900278cf630cb56ba23ac70cd77f7e0c413abb8a1f223f3",
@@ -886,6 +890,8 @@
     "https://esm.d8d.fun/react-dom@19.0.0/client": "c972c16184c695fc5828dfa61d7f341edbc463d20d8108765c93a98027c24227",
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
+    "https://esm.d8d.fun/react-hook-form@7.55.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-hook-form.mjs": "788ec1a54e10051f539ba435aa513802c823bad03e11e2534b1b17df99189a87",
+    "https://esm.d8d.fun/react-hook-form@7.55.0?deps=react@19.0.0,react-dom@19.0.0": "8ed376b3af6e11be43538b15e654692d5995232523a6dc16ce7f81263b1a3614",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/dist/development/chunk-K6CSEXPM.mjs": "441898046ad7c4fd9a6b53e13a398c9c74c4412c519e942f82b8a77f7af9f9d6",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-router.mjs": "b0b05fcfc3a03c5f679cd0bc69ca19aa10abaa977395df00e86b3fb114e5e346",
     "https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0": "ad747718e32a45020d67eb4ff98f9734cb06a10ceb393baac0a965043e96cdf0",