生产管理系统前端 - 数字化绘制与编辑

This commit is contained in:
2025-10-29 16:57:06 +08:00
parent e14f03cf79
commit 3239f819d0
8 changed files with 2844 additions and 7 deletions

View File

@@ -424,4 +424,343 @@ const handleToggleArrayFilter = (key: 'soilTypes' | 'plantingModes' | 'tags', va
5. **文档化习惯**:将开发过程中的经验和教训记录下来,形成知识积累
- 认识到文档化对团队协作和知识传承的重要性
- 建立了完整的开发规范文档体系
- 建立了完整的开发规范文档体系
---
## pathland-information/map/gisnameGIS地图管理开发经验与问题解决
### 总体开发经验总结
GIS地图管理页面的开发过程是一个复杂的技术集成挑战涉及到第三方地图库的集成、异步资源加载、多层级组件交互等多个技术难点。通过这次开发我们建立了一套完整的GIS应用开发模式特别是在处理真实地图数据源和优雅降级方面积累了宝贵经验。
### 问题1地图组件初始化时缺少真实地图数据源
**问题描述:**
- 初始实现的BaseMap组件只是简单的模拟展示无法加载真实的卫星图像
- 用户反馈参考文件可以看到真实的卫星图,但当前页面只显示占位符
- 缺乏对真实地图服务商的集成支持
**原始需求分析:**
- 需要支持真实的卫星影像显示,而不是简单的占位地图
- 必须支持多种地图图层切换(卫星、电子、地形、混合)
- 需要完整的地图交互功能,包括缩放、平移、全屏等
**解决方案:**
- 复制完整的GISMapEngine实现支持多种地图提供商
- 实现leafletLoader动态加载器支持异步加载地图库
- 建立真实地图瓦片数据源连接包括ArcGIS卫星影像和OpenStreetMap
**代码实现对比:**
```tsx
// ❌ 初始简化实现
export class GISMapEngine {
constructor(map: any) {
this.map = map; // 只是一个模拟对象
}
addPolygon(polygon: MapPolygon): void {
this.polygons.set(polygon.id, polygon); // 没有真实渲染
}
}
// ✅ 最终完整实现
export class GISMapEngine {
constructor(config: MapConfig) {
this.provider = config.provider;
this.initialize(config); // 真实初始化流程
}
private async initLeaflet(config: MapConfig) {
// 动态加载Leaflet库
if (!window.L) {
await this.loadLeaflet();
}
// 创建真实地图实例
this.map = window.L.map(this.container).setView([center[1], center[0]], zoom);
// 设置真实瓦片图层
this.getLeafletLayer(layer).addTo(this.map);
}
}
```
### 问题2地图库异步加载和依赖管理复杂性
**问题描述:**
- 地图库Leaflet需要从CDN异步加载存在加载失败风险
- 地图组件需要在库加载完成后才能初始化,存在时序问题
- 多个地图组件可能重复加载同一资源,造成性能浪费
**原始需求分析:**
- 确保地图库能够可靠加载,提供良好的用户体验
- 处理加载失败的情况,提供优雅的降级方案
- 优化资源加载性能,避免重复加载
**解决方案:**
- 创建leafletLoader统一管理地图库的加载过程
- 实现加载状态管理和重试机制
- 建立全局加载状态缓存,避免重复加载
**关键实现代码:**
```tsx
// leafletLoader.ts - 统一加载管理
export const preloadLeaflet = (): Promise<boolean> => {
return new Promise((resolve) => {
if (leafletLoaded || window.L) {
resolve(true);
return;
}
if (leafletLoading) {
// 等待正在进行的加载完成
const checkInterval = setInterval(() => {
if (leafletLoaded || window.L) {
clearInterval(checkInterval);
resolve(true);
}
}, 100);
return;
}
// 执行实际加载过程
const script = document.createElement('script');
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.onload = () => { leafletLoaded = true; resolve(true); };
script.onerror = () => { resolve(false); };
document.head.appendChild(script);
});
};
```
### 问题3地图组件与状态管理的深度集成
**问题描述:**
- 地图组件需要与useReducer状态管理深度集成
- 地图的异步初始化过程与React生命周期存在冲突
- 地图事件回调与状态更新的时序同步问题
**原始需求分析:**
- 地图操作需要能够更新全局状态(如选中地块、图层切换)
- 状态变化需要能够反映到地图显示上
- 需要处理地图组件的清理和资源释放
**解决方案:**
- 使用useImperativeHandle暴露地图实例方法给父组件
- 实现地图引擎的引用管理,确保状态同步
- 建立完整的生命周期管理,包括组件卸载时的资源清理
**状态管理集成代码:**
```tsx
// BaseMap组件中的状态集成
useImperativeHandle(ref, () => ({
getMapEngine: () => mapEngineRef.current,
addMarker: (marker: Marker) => {
mapEngineRef.current?.addMarker(marker);
},
addPolygon: (polygon: Polygon) => {
mapEngineRef.current?.addPolygon(polygon);
},
setCenter: (position: MapPosition, zoom?: number) => {
mapEngineRef.current?.setCenter(position, zoom);
},
}));
// 组件卸载时的资源清理
useEffect(() => {
return () => {
if (mapEngineRef.current) {
mapEngineRef.current.destroy();
}
};
}, []);
```
### 问题4真实地图数据源的集成和配置
**问题描述:**
- 需要集成多种真实的地图瓦片数据源
- 不同地图服务商的API格式和坐标系统存在差异
- 需要处理地图瓦片的加载性能和缓存策略
**原始需求分析:**
- 提供真实的卫星影像、电子地图、地形图等多种图层
- 确保地图数据的准确性和时效性
- 优化地图加载性能,提供流畅的用户体验
**解决方案:**
- 集成多个开源地图数据源,确保服务的可靠性
- 统一不同数据源的坐标系统和API格式
- 实现智能的图层切换和缓存机制
**地图数据源配置:**
```tsx
private getLeafletLayer(layer: MapLayer) {
const baseLayers: Record<MapLayer, string> = {
// ArcGIS卫星影像 - 真实卫星图
satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
// OpenStreetMap - 开源电子地图
street: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
// OpenTopoMap - 开源地形图
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',
maxZoom: 18,
});
}
```
### 问题5地图交互功能的完整实现
**问题描述:**
- 需要实现地块多边形的渲染和交互
- 地图标记点的添加和点击事件处理
- 地图控件(缩放、图层切换、全屏等)的集成
**原始需求分析:**
- 地块需要在地图上以彩色多边形形式显示
- 点击地块需要触发选择事件和状态更新
- 提供完整的地图导航和操作功能
**解决方案:**
- 使用地图引擎的Polygon和Marker API实现地块渲染
- 建立事件处理机制,连接地图交互和状态管理
- 集成完整的地图控件套件,提供专业级用户体验
**交互功能实现:**
```tsx
// 地块多边形渲染
const polygon: Polygon = {
id: field.id,
path: field.coordinates,
fillColor: field.color,
strokeColor: field.color,
fillOpacity: 0.3,
strokeWeight: 2,
onClick: () => {
onFieldSelect(field); // 更新全局状态
toast.success(`已选择: ${field.name}`);
},
};
engine.addPolygon(polygon);
// 地块标记点渲染
const marker: Marker = {
id: `marker-${field.id}`,
position: { lat: centerLat, lng: centerLng },
title: field.name,
color: field.color,
onClick: () => {
onFieldSelect(field);
toast.success(`已选择: ${field.name}`);
},
};
engine.addMarker(marker);
```
## 开发经验对比总结
### 与原始要求的差异分析
| 原始要求 | 实际实现 | 差异说明 | 解决过程 |
|---------|---------|---------|---------|
| 1:1还原地图功能 | 完整实现 + 真实数据源 | 需要集成真实地图服务商和瓦片数据 | 建立完整的地图引擎架构,支持多种数据源 |
| 第三方库集成 | 专业级集成 | 需要处理异步加载、错误处理、性能优化 | 实现统一加载器和优雅降级机制 |
| 组件状态管理 | 深度集成 + 生命周期管理 | 地图组件与React状态系统需要深度集成 | 使用useImperativeHandle和引用管理 |
| 交互功能实现 | 完整交互套件 | 需要实现多边形、标记、控件等完整功能 | 集成地图引擎API建立事件处理机制 |
### 关键学习点和改进
1. **第三方库集成思维**学会了如何可靠地集成和管理复杂的第三方JavaScript库
- 掌握了异步加载、错误处理、优雅降级的完整流程
- 理解了库版本管理和兼容性处理的重要性
2. **地图API应用经验**深入了解了Web地图开发的技术栈和最佳实践
- 学会了瓦片地图的原理和多种数据源的使用
- 掌握了地图交互事件的处理和状态同步机制
3. **React高级模式应用**在复杂组件中应用了useImperativeHandle、useRef等高级React模式
- 深入理解了React组件的暴露方法和引用传递机制
- 掌握了复杂组件生命周期管理的最佳实践
4. **性能优化意识**:建立了地图应用的性能优化思维
- 学会了资源懒加载和缓存策略的设计
- 理解了大型第三方库对应用性能的影响和优化方法
5. **用户体验设计**:在技术实现中始终考虑用户体验
- 建立了加载状态和错误处理的设计模式
- 掌握了优雅降级和渐进增强的实现方法
6. **架构设计能力**:设计了可扩展的地图应用架构
- 建立了插件化的地图引擎设计
---
## pathsrc/app/(app)/land-information/map/drawname数字化绘制与编辑页面开发经验
### 1. **复杂状态管理设计**
- **useReducer 模式应用**:使用 useReducer 管理复杂的编辑状态,包含多个布尔状态、数组和对象
- **状态结构设计**:设计了包含高级编辑器状态、活动标签页、地块数据、保存对话框等的状态结构
- **Action 设计模式**:采用类型安全的 Action 设计,支持状态更新、字段管理、对话框控制等操作
### 2. **组件化架构设计**
- **模块化组件结构**将复杂功能拆分为6个独立组件每个组件负责单一职责
- `drawEditReducer.tsx`:状态管理核心
- `DrawingTools.tsx`:绘制工具组件
- `EditingTools.tsx`:编辑工具组件
- `FieldEntryDialog.tsx`:地块信息录入对话框
- `UsageGuide.tsx`:使用指南组件
- `AdvancedEditorPromo.tsx`:高级编辑器推广组件
- **组件通信设计**:通过 props 和回调函数实现组件间的数据传递和事件处理
### 3. **Canvas 绘图技术实现**
- **多种绘制模式**:实现点、线、多边形、矩形等多种绘制模式
- **实时交互反馈**:支持鼠标移动吸附、节点高亮、实时预览等功能
- **几何计算算法**
- Shoelace 公式计算多边形面积
- 坐标距离计算周长
- 点在多边形内判断算法
- 自相交检测算法
### 4. **高级编辑功能实现**
- **节点编辑**:支持拖拽节点、添加节点、删除节点
- **地块分割**:绘制分割线将地块分成两部分,支持垂直和水平分割
- **地块合并**:多地块选择和凸包算法合并
- **历史记录管理**:实现撤销/重做功能,支持操作历史追踪
### 5. **用户体验设计**
- **分步骤操作引导**:为复杂操作提供详细的操作步骤说明
- **实时状态反馈**Toast 通知、状态栏显示、操作确认等
- **键盘快捷键支持**Ctrl+Z 撤销、Ctrl+S 保存、Delete 清除、Esc 取消
- **视觉状态管理**:选中高亮、禁用状态、加载状态等
### 6. **数据管理与持久化**
- **表单验证设计**:完整的表单验证逻辑,支持必填项检查和格式验证
- **本地存储集成**:与 localStorage 集成,支持地块数据的持久化
- **自动数据生成**:地块编号、名称的自动生成逻辑
- **标签管理功能**:支持标签的添加、删除和展示
### 7. **技术规范遵循**
- **shadcn/ui 语义样式**:使用 `bg-card``bg-muted``text-muted-foreground` 等语义化样式
- **暗色主题支持**:完整支持暗色主题,使用 `dark:` 前缀
- **TypeScript 类型安全**:完整的类型定义,确保类型安全
- **响应式设计**:支持不同屏幕尺寸的适配
### 8. **开发效率提升**
- **组件复用设计**:通用组件可在其他页面复用
- **配置化参数**:画布尺寸、吸附距离等参数可配置
- **错误处理机制**:完善的错误处理和用户提示
- **代码组织结构**:清晰的文件结构和命名规范
### 9. **性能优化考虑**
- **事件处理优化**:使用 useCallback 避免不必要的重渲染
- **状态更新策略**:合理的状态更新时机和批量处理
- **Canvas 渲染优化**:减少不必要的重绘和计算
### 10. **可扩展性设计**
- **插件化架构**:编辑工具采用插件化设计,易于扩展新功能
- **接口标准化**:统一的接口设计,便于功能模块替换
- **配置化开发**:支持通过配置文件调整功能和行为
- 理解了复杂应用中的组件分层和职责划分