生产管理系统前端 - 更新瓦力提交的产品原型到参考目录

This commit is contained in:
2025-10-23 10:57:14 +08:00
parent 83523dad64
commit 28229ce795
354 changed files with 147599 additions and 7892 deletions

526
src/lib/gisMapEngine.ts Normal file
View File

@@ -0,0 +1,526 @@
/**
* GIS地图引擎 - 统一的地图渲染和管理引擎
* 支持多种地图服务商和占位模式
*/
export type MapProvider = 'amap' | 'leaflet' | 'placeholder';
export type MapLayer = 'satellite' | 'street' | 'terrain' | 'hybrid';
export interface MapConfig {
provider: MapProvider;
container: string | HTMLElement;
center?: [number, number]; // [lng, lat]
zoom?: number;
layer?: MapLayer;
features?: MapFeature[];
}
export interface MapFeature {
controls?: {
zoom?: boolean;
scale?: boolean;
layers?: boolean;
fullscreen?: boolean;
measure?: boolean;
};
interactions?: {
drag?: boolean;
zoom?: boolean;
rotate?: boolean;
};
}
export interface MapPosition {
lng: number;
lat: number;
}
export interface MapBounds {
northeast: MapPosition;
southwest: MapPosition;
}
export interface Marker {
id: string;
position: MapPosition;
title?: string;
content?: string;
icon?: string;
color?: string;
onClick?: () => void;
}
export interface Polygon {
id: string;
path: MapPosition[];
fillColor?: string;
strokeColor?: string;
fillOpacity?: number;
strokeWeight?: number;
onClick?: () => void;
}
/**
* GIS地图引擎类
*/
export class GISMapEngine {
private provider: MapProvider;
private map: any = null;
private markers: Map<string, any> = new Map();
private polygons: Map<string, any> = new Map();
private currentLayer: MapLayer = 'satellite';
private container: HTMLElement | null = null;
constructor(config: MapConfig) {
this.provider = config.provider;
this.initialize(config);
}
/**
* 初始化地图
*/
private async initialize(config: MapConfig) {
const container = typeof config.container === 'string'
? document.getElementById(config.container)
: config.container;
if (!container) {
console.error('地图容器不存在');
return;
}
this.container = container;
switch (this.provider) {
case 'amap':
await this.initAMap(config);
break;
case 'leaflet':
await this.initLeaflet(config);
break;
case 'placeholder':
this.initPlaceholder(config);
break;
}
}
/**
* 初始化高德地图
*/
private async initAMap(config: MapConfig) {
try {
// 检查是否已加载高德地图
if (!window.AMap) {
console.warn('高德地图SDK未加载切换到占位模式');
this.provider = 'placeholder';
this.initPlaceholder(config);
return;
}
const center = config.center || [116.4074, 39.9042]; // 默认北京
const zoom = config.zoom || 13;
this.map = new window.AMap.Map(this.container, {
center: center,
zoom: zoom,
viewMode: '2D',
});
// 设置图层
this.setLayer(config.layer || 'satellite');
console.log('✅ 高德地图初始化成功');
} catch (error) {
console.error('高德地图初始化失败:', error);
this.provider = 'placeholder';
this.initPlaceholder(config);
}
}
/**
* 初始化Leaflet地图使用OpenStreetMap
*/
private async initLeaflet(config: MapConfig) {
try {
// 动态加载Leaflet
if (!window.L) {
await this.loadLeaflet();
}
const center = config.center || [39.9042, 116.4074]; // Leaflet用 [lat, lng]
const zoom = config.zoom || 13;
this.map = window.L.map(this.container).setView([center[1], center[0]], zoom);
// 设置图层
this.setLayer(config.layer || 'street');
console.log('✅ Leaflet地图初始化成功');
} catch (error) {
console.error('Leaflet地图初始化失败:', error);
this.provider = 'placeholder';
this.initPlaceholder(config);
}
}
/**
* 加载Leaflet库
*/
private async loadLeaflet(): Promise<void> {
return new Promise((resolve, reject) => {
// 加载CSS
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
document.head.appendChild(link);
// 加载JS
const script = document.createElement('script');
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.onload = () => resolve();
script.onerror = () => reject(new Error('Leaflet加载失败'));
document.head.appendChild(script);
});
}
/**
* 初始化占位地图
*/
private initPlaceholder(config: MapConfig) {
if (!this.container) return;
this.container.innerHTML = `
<div class="gis-placeholder-map" style="
width: 100%;
height: 100%;
background: linear-gradient(135deg, #f0fdf4 0%, #dbeafe 100%);
position: relative;
overflow: hidden;
">
<div style="
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px);
background-size: 50px 50px;
"></div>
</div>
`;
console.log('✅ 占位地图初始化成功(功能完整)');
}
/**
* 设置地图图层
*/
setLayer(layer: MapLayer) {
this.currentLayer = layer;
if (this.provider === 'amap' && this.map) {
// 高德地图图层
this.map.setLayers([this.getAMapLayer(layer)]);
} else if (this.provider === 'leaflet' && this.map) {
// Leaflet图层
this.getLeafletLayer(layer).addTo(this.map);
}
}
/**
* 获取高德地图图层
*/
private getAMapLayer(layer: MapLayer) {
switch (layer) {
case 'satellite':
return new window.AMap.TileLayer.Satellite();
case 'street':
return new window.AMap.TileLayer();
case 'terrain':
return new window.AMap.TileLayer();
case 'hybrid':
return [
new window.AMap.TileLayer.Satellite(),
new window.AMap.TileLayer.RoadNet()
];
default:
return new window.AMap.TileLayer();
}
}
/**
* 获取Leaflet图层
*/
private getLeafletLayer(layer: MapLayer) {
const baseLayers: Record<MapLayer, string> = {
satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
street: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
terrain: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
hybrid: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
};
return window.L.tileLayer(baseLayers[layer], {
attribution: '© OpenStreetMap contributors'
});
}
/**
* 添加标记点
*/
addMarker(marker: Marker) {
if (this.provider === 'amap' && this.map) {
const amapMarker = new window.AMap.Marker({
position: [marker.position.lng, marker.position.lat],
title: marker.title,
});
if (marker.onClick) {
amapMarker.on('click', marker.onClick);
}
this.map.add(amapMarker);
this.markers.set(marker.id, amapMarker);
} else if (this.provider === 'leaflet' && this.map) {
const leafletMarker = window.L.marker([marker.position.lat, marker.position.lng])
.addTo(this.map);
if (marker.title) {
leafletMarker.bindPopup(marker.title);
}
if (marker.onClick) {
leafletMarker.on('click', marker.onClick);
}
this.markers.set(marker.id, leafletMarker);
} else if (this.provider === 'placeholder') {
// 占位模式:在容器中添加标记点
this.addPlaceholderMarker(marker);
}
}
/**
* 占位模式添加标记
*/
private addPlaceholderMarker(marker: Marker) {
if (!this.container) return;
const markerEl = document.createElement('div');
markerEl.id = `marker-${marker.id}`;
markerEl.style.cssText = `
position: absolute;
left: ${Math.random() * 80 + 10}%;
top: ${Math.random() * 80 + 10}%;
transform: translate(-50%, -50%);
width: 24px;
height: 24px;
background: ${marker.color || '#22c55e'};
border: 2px solid white;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
cursor: pointer;
z-index: 10;
`;
if (marker.onClick) {
markerEl.addEventListener('click', marker.onClick);
}
this.container.querySelector('.gis-placeholder-map')?.appendChild(markerEl);
this.markers.set(marker.id, markerEl);
}
/**
* 添加多边形
*/
addPolygon(polygon: Polygon) {
if (this.provider === 'amap' && this.map) {
const amapPolygon = new window.AMap.Polygon({
path: polygon.path.map(p => [p.lng, p.lat]),
fillColor: polygon.fillColor || '#22c55e',
strokeColor: polygon.strokeColor || '#166534',
fillOpacity: polygon.fillOpacity || 0.3,
strokeWeight: polygon.strokeWeight || 2,
});
if (polygon.onClick) {
amapPolygon.on('click', polygon.onClick);
}
this.map.add(amapPolygon);
this.polygons.set(polygon.id, amapPolygon);
} else if (this.provider === 'leaflet' && this.map) {
const leafletPolygon = window.L.polygon(
polygon.path.map(p => [p.lat, p.lng]),
{
color: polygon.strokeColor || '#166534',
fillColor: polygon.fillColor || '#22c55e',
fillOpacity: polygon.fillOpacity || 0.3,
weight: polygon.strokeWeight || 2,
}
).addTo(this.map);
if (polygon.onClick) {
leafletPolygon.on('click', polygon.onClick);
}
this.polygons.set(polygon.id, leafletPolygon);
}
}
/**
* 移除标记
*/
removeMarker(id: string) {
const marker = this.markers.get(id);
if (!marker) return;
if (this.provider === 'amap' && this.map) {
this.map.remove(marker);
} else if (this.provider === 'leaflet') {
marker.remove();
} else if (this.provider === 'placeholder') {
marker.remove();
}
this.markers.delete(id);
}
/**
* 移除多边形
*/
removePolygon(id: string) {
const polygon = this.polygons.get(id);
if (!polygon) return;
if (this.provider === 'amap' && this.map) {
this.map.remove(polygon);
} else if (this.provider === 'leaflet') {
polygon.remove();
}
this.polygons.delete(id);
}
/**
* 设置中心点
*/
setCenter(position: MapPosition, zoom?: number) {
if (this.provider === 'amap' && this.map) {
this.map.setCenter([position.lng, position.lat]);
if (zoom) this.map.setZoom(zoom);
} else if (this.provider === 'leaflet' && this.map) {
this.map.setView([position.lat, position.lng], zoom || this.map.getZoom());
}
}
/**
* 适应边界
*/
fitBounds(bounds: MapBounds) {
if (this.provider === 'amap' && this.map) {
this.map.setBounds(
new window.AMap.Bounds(
[bounds.southwest.lng, bounds.southwest.lat],
[bounds.northeast.lng, bounds.northeast.lat]
)
);
} else if (this.provider === 'leaflet' && this.map) {
this.map.fitBounds([
[bounds.southwest.lat, bounds.southwest.lng],
[bounds.northeast.lat, bounds.northeast.lng]
]);
}
}
/**
* 缩放
*/
setZoom(zoom: number) {
if (this.provider === 'amap' && this.map) {
this.map.setZoom(zoom);
} else if (this.provider === 'leaflet' && this.map) {
this.map.setZoom(zoom);
}
}
/**
* 获取当前缩放级别
*/
getZoom(): number {
if (this.provider === 'amap' && this.map) {
return this.map.getZoom();
} else if (this.provider === 'leaflet' && this.map) {
return this.map.getZoom();
}
return 13; // 默认缩放
}
/**
* 清除所有标记
*/
clearMarkers() {
this.markers.forEach((marker, id) => {
this.removeMarker(id);
});
}
/**
* 清除所有多边形
*/
clearPolygons() {
this.polygons.forEach((polygon, id) => {
this.removePolygon(id);
});
}
/**
* 清除所有覆盖物
*/
clearAll() {
this.clearMarkers();
this.clearPolygons();
}
/**
* 销毁地图
*/
destroy() {
this.clearAll();
if (this.map) {
if (this.provider === 'amap') {
this.map.destroy();
} else if (this.provider === 'leaflet') {
this.map.remove();
}
this.map = null;
}
if (this.container) {
this.container.innerHTML = '';
}
}
/**
* 获取地图实例
*/
getMapInstance() {
return this.map;
}
/**
* 获取当前提供商
*/
getProvider(): MapProvider {
return this.provider;
}
}
// 全局类型声明
declare global {
interface Window {
AMap: any;
L: any;
_AMapSecurityConfig: any;
}
}