增加map组件

修改useTable类型定义
table组件的align:center降低优先级
master
LiuJiaNan 2025-10-23 16:52:53 +08:00
parent 8e5842d345
commit 9e781329b7
7 changed files with 302 additions and 3 deletions

18
components/Map/MapSelector.d.ts vendored Normal file
View File

@ -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<MapSelectorProps>;
export default MapSelector;

View File

@ -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 (
<Modal
open={visible}
title="坐标"
onCancel={handleClose}
onOk={handleConfirm}
width={800}
destroyOnHidden={false}
afterClose={handleAfterClose}
>
<Form labelAlign="right" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="关键字搜索">
<Input
value={localSearch}
onChange={e => setLocalSearch(e.target.value)}
allowClear
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label=" " colon={false}>
<Button type="primary" onClick={handleLocalSearch}>
搜索
</Button>
</Form.Item>
</Col>
</Row>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="经度">
<Input disabled value={currentLongitude} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="纬度">
<Input disabled value={currentLatitude} />
</Form.Item>
</Col>
</Row>
</Form>
<div
ref={mapContainerRef}
style={{ width: "100%", height: "500px", position: "relative" }}
>
<Spin size="large" tip="地图正在加载中..." spinning={loading}>
<div style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 1000,
}}
/>
</Spin>
</div>
</Modal>
);
};
export default MapSelector;

16
components/Map/index.d.ts vendored Normal file
View File

@ -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<MapProps>;
export default Map;

61
components/Map/index.js Normal file
View File

@ -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 (
<>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="经度" name={longitudeProps} style={{ flex: 1 }} rules={[{ required, message: "请选择经度" }]}>
<Input disabled />
</Form.Item>
</Col>
<Col span={12}>
<div style={{ display: "flex" }}>
<Form.Item label="纬度" name={latitudeProps} style={{ flex: 1 }} rules={[{ required, message: "请选择纬度" }]}>
<Input disabled />
</Form.Item>
<Form.Item label=" " colon={false}>
<Button type="primary" onClick={() => setMapVisible(true)}>
点击定位
</Button>
</Form.Item>
</div>
</Col>
</Row>
<MapSelector
visible={mapVisible}
onClose={() => setMapVisible(false)}
longitude={currentLongitude}
latitude={currentLatitude}
onConfirm={handleMapConfirm}
/>
</>
);
};
export default Map;

View File

@ -2,7 +2,7 @@ import type { FC } from "react";
export interface PreviewPdfProps {
/** 文件列表,和 name、url 冲突 */
files?: { [p: string]: any }[];
files?: Record<string, any>[];
/** 文件名字段名,传入 files 时会优先查找是否存在 name、fileName */
nameKey?: string;
/** 文件路径字段名,传入 files 时会优先查找是否存在 filePath */

View File

@ -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 <Table storeIndex={storeIndex} columns={calcColumns()} {...restProps} />;
}

View File

@ -13,7 +13,7 @@ export interface UseTableOptions<TData extends Data, TParams extends Params> ext
/** 是否使用存储查询条件,默认是 */
useStorageQueryCriteria?: boolean;
/** 额外参数 */
params?: FormValues | (() => FormValues);
params?: Record<string, any> | (() => Record<string, any>);
/** 表单数据转换函数,在每次请求之前调用,接收当前搜索的表单项,要求返回一个对象 */
transform?: (formData: FormValues) => FormValues;
/** 回调函数 */