子仓库提交

This commit is contained in:
2025-11-10 09:19:56 +08:00
parent 62f92213f7
commit 5feb24e4e2
733 changed files with 141413 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
'use client';
import { useState, useRef, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card } from '@/components/ui/card';
import { Alert, AlertDescription } from '@/components/ui/alert';
import {
Map,
Upload,
Pen,
Square,
Layers,
CheckCircle2,
FileType,
MapPin
} from 'lucide-react';
import { GeoCoordinate } from '@/app/(app)/land-information/archive/manage/page';
import { toast } from 'sonner';
interface LandMapContainerProps {
coordinates: GeoCoordinate[];
area: number;
perimeter: number;
onCoordinatesChange: (coordinates: GeoCoordinate[], area: number, perimeter: number) => void;
}
export function LandMapContainer({
coordinates,
area,
perimeter,
onCoordinatesChange
}: LandMapContainerProps) {
const [isMapLoaded, setIsMapLoaded] = useState(false);
const [mapLayerType, setMapLayerType] = useState<'satellite' | 'street'>('satellite');
const [drawMode, setDrawMode] = useState<'none' | 'polygon' | 'rectangle'>('none');
const mapContainerRef = useRef<HTMLDivElement>(null);
// 初始化地图
useEffect(() => {
// 模拟地图加载
setTimeout(() => {
setIsMapLoaded(true);
toast.info('演示地图模式:可通过文件导入或手动输入坐标数据', { duration: 3000 });
}, 1000);
}, []);
// 切换地图图层
const toggleMapLayer = () => {
const newLayerType = mapLayerType === 'satellite' ? 'street' : 'satellite';
setMapLayerType(newLayerType);
toast.success(`已切换到${newLayerType === 'satellite' ? '卫星图' : '电子地图'}`);
};
// 开始绘制
const startDrawing = (mode: 'polygon' | 'rectangle') => {
setDrawMode(mode);
toast.info(`开始绘制${mode === 'polygon' ? '多边形' : '矩形'},点击地图描点,双击结束`);
// 模拟绘制完成
setTimeout(() => {
const mockCoordinates = [
{ lat: 39.9042 + 0.01, lng: 116.4074 - 0.01 },
{ lat: 39.9042 + 0.01, lng: 116.4074 + 0.01 },
{ lat: 39.9042 - 0.01, lng: 116.4074 + 0.01 },
{ lat: 39.9042 - 0.01, lng: 116.4074 - 0.01 },
];
const mockArea = 120.5;
const mockPerimeter = 1380;
onCoordinatesChange(mockCoordinates, mockArea, mockPerimeter);
setDrawMode('none');
toast.success(`绘制完成!面积:${mockArea.toFixed(2)}亩,周长:${mockPerimeter.toFixed(0)}`);
}, 2000);
};
// 文件导入
const handleFileImport = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
const fileExtension = file.name.split('.').pop()?.toLowerCase();
if (!['kml', 'geojson', 'json', 'shp'].includes(fileExtension || '')) {
toast.error('不支持的文件格式。请上传 KML、GeoJSON 或 SHP 文件');
return;
}
try {
// 模拟文件解析
const mockCoordinates = [
{ lat: 39.9042 + 0.008, lng: 116.4074 - 0.008 },
{ lat: 39.9042 + 0.008, lng: 116.4074 + 0.008 },
{ lat: 39.9042 - 0.008, lng: 116.4074 + 0.008 },
{ lat: 39.9042 - 0.008, lng: 116.4074 - 0.008 },
];
const mockArea = 85.3;
const mockPerimeter = 1120;
onCoordinatesChange(mockCoordinates, mockArea, mockPerimeter);
toast.success(`成功导入${fileExtension.toUpperCase()}文件!面积:${mockArea.toFixed(2)}亩,${mockCoordinates.length}个点`);
} catch (error) {
console.error('文件导入错误:', error);
toast.error('文件导入失败,请检查文件格式');
}
// 清空input
event.target.value = '';
};
return (
<Card className="p-6">
<div className="space-y-4">
{/* 地图工具栏 */}
<div className="flex items-center justify-between">
<h3 className="flex items-center gap-2">
<Map className="w-5 h-5 text-green-600" />
</h3>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={toggleMapLayer}
>
<Layers className="w-4 h-4 mr-2" />
{mapLayerType === 'satellite' ? '卫星图' : '电子地图'}
</Button>
<Button
variant={drawMode === 'polygon' ? 'default' : 'outline'}
size="sm"
onClick={() => startDrawing('polygon')}
>
<Pen className="w-4 h-4 mr-2" />
</Button>
<Button
variant={drawMode === 'rectangle' ? 'default' : 'outline'}
size="sm"
onClick={() => startDrawing('rectangle')}
>
<Square className="w-4 h-4 mr-2" />
</Button>
<div className="relative">
<Input
type="file"
accept=".kml,.geojson,.json,.shp"
onChange={handleFileImport}
className="absolute inset-0 opacity-0 cursor-pointer"
/>
<Button
variant="outline"
size="sm"
className="bg-green-50 hover:bg-green-100 border-green-300"
>
<Upload className="w-4 h-4 mr-2 text-green-600" />
<span className="text-green-700"></span>
</Button>
</div>
</div>
</div>
{/* 地图容器 */}
<div
ref={mapContainerRef}
className="w-full h-[500px] rounded-lg border-2 border-dashed relative bg-gradient-to-br from-green-50 to-blue-50"
>
{isMapLoaded && (
<div className="absolute inset-0 flex items-center justify-center p-8">
<div className="text-center max-w-md">
<Map className="w-16 h-16 mx-auto mb-4 text-green-600 opacity-50" />
<h3 className="text-green-800 mb-2"></h3>
<p className="text-sm text-muted-foreground mb-4">
使
</p>
<ul className="text-sm text-left text-muted-foreground space-y-2">
<li className="flex items-start gap-2">
<CheckCircle2 className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
<span> KML/GeoJSON/SHP </span>
</li>
<li className="flex items-start gap-2">
<CheckCircle2 className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
<span></span>
</li>
<li className="flex items-start gap-2">
<CheckCircle2 className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
<span></span>
</li>
<li className="flex items-start gap-2">
<CheckCircle2 className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
<span></span>
</li>
</ul>
<div className="mt-4 p-3 bg-blue-50 rounded-md">
<p className="text-xs text-blue-800">
💡 SDK
</p>
</div>
</div>
</div>
)}
{!isMapLoaded && (
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-center">
<MapPin className="w-12 h-12 mx-auto mb-3 text-green-600 animate-pulse" />
<p className="text-sm text-muted-foreground">...</p>
</div>
</div>
)}
</div>
{/* 地图信息 */}
{coordinates && coordinates.length > 0 && (
<Alert>
<CheckCircle2 className="w-4 h-4 text-green-600" />
<AlertDescription>
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<span className="text-muted-foreground"></span>
<span className="ml-2 font-medium">{coordinates.length} </span>
</div>
<div>
<span className="text-muted-foreground"></span>
<span className="ml-2 font-medium text-green-600">{area.toFixed(2)} </span>
</div>
<div>
<span className="text-muted-foreground"></span>
<span className="ml-2 font-medium">{perimeter.toFixed(0)} </span>
</div>
</div>
</AlertDescription>
</Alert>
)}
{/* 文件格式说明 */}
<Alert className="bg-blue-50 border-blue-200">
<FileType className="w-4 h-4 text-blue-600" />
<AlertDescription className="text-sm text-blue-800">
<strong></strong>
<ul className="mt-2 space-y-1 ml-4 list-disc">
<li>KML - Google Earth </li>
<li>GeoJSON - Web GIS </li>
<li>SHP - ArcGIS Shapefile </li>
</ul>
</AlertDescription>
</Alert>
</div>
</Card>
);
}