From 9e781329b7661549498157dc11c4542a9dfd3385 Mon Sep 17 00:00:00 2001 From: LiuJiaNan <15703339975@163.com> Date: Thu, 23 Oct 2025 16:52:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0map=E7=BB=84=E4=BB=B6=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9useTable=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=20table=E7=BB=84=E4=BB=B6=E7=9A=84align:center=E9=99=8D?= =?UTF-8?q?=E4=BD=8E=E4=BC=98=E5=85=88=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Map/MapSelector.d.ts | 18 +++ components/Map/MapSelector.js | 204 +++++++++++++++++++++++++++++++ components/Map/index.d.ts | 16 +++ components/Map/index.js | 61 +++++++++ components/PreviewPdf/index.d.ts | 2 +- components/Table/index.js | 2 +- hooks/useTable/index.d.ts | 2 +- 7 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 components/Map/MapSelector.d.ts create mode 100644 components/Map/MapSelector.js create mode 100644 components/Map/index.d.ts create mode 100644 components/Map/index.js diff --git a/components/Map/MapSelector.d.ts b/components/Map/MapSelector.d.ts new file mode 100644 index 0000000..d79b9b7 --- /dev/null +++ b/components/Map/MapSelector.d.ts @@ -0,0 +1,18 @@ +import type { FC } from "react"; + +export interface MapSelectorProps { + /** 是否显示弹窗 */ + visible: boolean; + /** 关闭弹窗回调 */ + onClose: () => void; + /** 经度值 */ + longitude?: number | string; + /** 纬度值 */ + latitude?: number | string; + /** 确认选择回调 */ + onConfirm?: (longitude: number | string, latitude: number | string) => void; +} + +declare const MapSelector: FC; + +export default MapSelector; diff --git a/components/Map/MapSelector.js b/components/Map/MapSelector.js new file mode 100644 index 0000000..c948373 --- /dev/null +++ b/components/Map/MapSelector.js @@ -0,0 +1,204 @@ +import { Button, Col, Form, Input, Modal, Row, Spin } from "antd"; +import { useEffect, useRef, useState } from "react"; + +const MapSelector = ({ + visible, + onClose, + longitude, + latitude, + onConfirm, // 新增确认回调 + }) => { + const mapContainerRef = useRef(null); + const mapInstanceRef = useRef(null); + const [loading, setLoading] = useState(false); + const [currentLongitude, setCurrentLongitude] = useState(longitude || ""); + const [currentLatitude, setCurrentLatitude] = useState(latitude || ""); + const [localSearch, setLocalSearch] = useState(""); + + // 当外部经纬度变化时,更新内部状态 + useEffect(() => { + setCurrentLongitude(longitude || ""); + setCurrentLatitude(latitude || ""); + }, [longitude, latitude]); + + // 初始化地图 + const initMap = async () => { + if (!window.BMapGL) { + console.error("BMapGL is not loaded"); + return; + } + + setLoading(true); + + // 确保DOM已经渲染 + await new Promise(resolve => setTimeout(resolve, 100)); + + if (mapContainerRef.current) { + // 只有在没有地图实例时才创建新地图 + if (!mapInstanceRef.current) { + const map = new window.BMapGL.Map(mapContainerRef.current); + mapInstanceRef.current = map; + + map.centerAndZoom( + new window.BMapGL.Point( + longitude || "119.69457721306945", + latitude || "39.940504336846665", + ), + 16, + ); + + map.enableScrollWheelZoom(true); + + // 如果有初始坐标,添加标记 + if (longitude && latitude) { + const point = new window.BMapGL.Point(longitude, latitude); + const marker = new window.BMapGL.Marker(point); + map.addOverlay(marker); + } + + // 添加点击事件 + map.addEventListener("click", (event) => { + map.clearOverlays(); + const point = new window.BMapGL.Point(event.latlng.lng, event.latlng.lat); + const marker = new window.BMapGL.Marker(point); + map.addOverlay(marker); + setCurrentLatitude(event.latlng.lat); + setCurrentLongitude(event.latlng.lng); + }); + } + + setLoading(false); + } + }; + + // 搜索功能 + const handleLocalSearch = () => { + if (localSearch && mapInstanceRef.current) { + const local = new window.BMapGL.LocalSearch(mapInstanceRef.current, { + renderOptions: { map: mapInstanceRef.current }, + }); + local.search(localSearch); + } + }; + + // 关闭弹窗 + const handleClose = () => { + setLocalSearch(""); + if (onClose) + onClose(); + }; + + // 确认选择 + const handleConfirm = () => { + if (onConfirm) { + onConfirm(currentLongitude, currentLatitude); + } + handleClose(); + }; + + // 监听visible变化 + useEffect(() => { + let initTimer; + + if (visible) { + // 延迟初始化地图,确保DOM完全渲染 + initTimer = setTimeout(() => { + initMap(); + }, 100); + } + + return () => { + if (initTimer) { + clearTimeout(initTimer); + } + }; + }, [visible]); + + const handleAfterClose = () => { + if (mapInstanceRef.current) { + try { + mapInstanceRef.current.clearOverlays(); + mapInstanceRef.current.destroy(); + mapInstanceRef.current = null; + } + catch (e) { + console.warn("Error destroying map on unmount:", e); + } + mapInstanceRef.current = null; + } + }; + + // 组件卸载时清理地图 + useEffect(() => { + return () => { + handleAfterClose(); + }; + }, []); + + return ( + +
+ + + + setLocalSearch(e.target.value)} + allowClear + /> + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + ); +}; + +export default MapSelector; diff --git a/components/Map/index.d.ts b/components/Map/index.d.ts new file mode 100644 index 0000000..4202142 --- /dev/null +++ b/components/Map/index.d.ts @@ -0,0 +1,16 @@ +import type { FC } from "react"; + +export interface MapProps { + /** 经度属性名,默认 longitude */ + longitudeProps?: string; + /** 纬度属性名,默认 latitude */ + latitudeProps?: string; + /** 经纬度变化回调 */ + onConfirm?: (longitude: number | string, latitude: number | string) => void; + /** 经纬度是否必填,默认 true */ + required?: boolean; +} + +declare const Map: FC; + +export default Map; diff --git a/components/Map/index.js b/components/Map/index.js new file mode 100644 index 0000000..45bdd2f --- /dev/null +++ b/components/Map/index.js @@ -0,0 +1,61 @@ +import { Button, Col, Form, Input, Row } from "antd"; +import { useState } from "react"; +import MapSelector from "./MapSelector"; + +const Map = (props) => { + const { + longitudeProps = "longitude", + latitudeProps = "latitude", + onConfirm, + required = true, + } = props; + + const form = Form.useFormInstance(); + const [mapVisible, setMapVisible] = useState(false); + const [currentLongitude, setCurrentLongitude] = useState(form.getFieldValue(longitudeProps) || ""); + const [currentLatitude, setCurrentLatitude] = useState(form.getFieldValue(latitudeProps) || ""); + + const handleMapConfirm = (longitudeValue, latitudeValue) => { + setCurrentLongitude(longitudeValue); + setCurrentLatitude(latitudeValue); + form.setFieldsValue({ + [longitudeProps]: longitudeValue, + [latitudeProps]: latitudeValue, + }); + onConfirm?.(longitudeValue, latitudeValue); + setMapVisible(false); + }; + + return ( + <> + + + + + + + +
+ + + + + + +
+ +
+ setMapVisible(false)} + longitude={currentLongitude} + latitude={currentLatitude} + onConfirm={handleMapConfirm} + /> + + ); +}; + +export default Map; diff --git a/components/PreviewPdf/index.d.ts b/components/PreviewPdf/index.d.ts index 75ac15e..3b454d9 100644 --- a/components/PreviewPdf/index.d.ts +++ b/components/PreviewPdf/index.d.ts @@ -2,7 +2,7 @@ import type { FC } from "react"; export interface PreviewPdfProps { /** 文件列表,和 name、url 冲突 */ - files?: { [p: string]: any }[]; + files?: Record[]; /** 文件名字段名,传入 files 时会优先查找是否存在 name、fileName */ nameKey?: string; /** 文件路径字段名,传入 files 时会优先查找是否存在 filePath */ diff --git a/components/Table/index.js b/components/Table/index.js index dca26e0..45fe129 100644 --- a/components/Table/index.js +++ b/components/Table/index.js @@ -11,7 +11,7 @@ function TablePro(props) { const storeIndex = props.storeIndex || `${window.process.env.app.antd["ant-prefix"]}_${Math.random().toString(36).substring(2)}`; function calcColumns() { showIndex && columns.unshift(getIndexColumn(props.pagination)); - return columns.map(item => ({ ...item, align: useAlignCenter ? "center" : "left" })); + return columns.map(item => ({ align: useAlignCenter ? "center" : "left", ...item })); } return ; } diff --git a/hooks/useTable/index.d.ts b/hooks/useTable/index.d.ts index 1e74f65..dd5c97e 100644 --- a/hooks/useTable/index.d.ts +++ b/hooks/useTable/index.d.ts @@ -13,7 +13,7 @@ export interface UseTableOptions ext /** 是否使用存储查询条件,默认是 */ useStorageQueryCriteria?: boolean; /** 额外参数 */ - params?: FormValues | (() => FormValues); + params?: Record | (() => Record); /** 表单数据转换函数,在每次请求之前调用,接收当前搜索的表单项,要求返回一个对象 */ transform?: (formData: FormValues) => FormValues; /** 回调函数 */