30 KiB
开发项目规范
通用开发规约
1. 文件头部注释规范(filekorolheader)
规范要求: 所有页面文件(page.tsx)必须在最上方添加filekorolheader注释,说明文件对应的页面功能、路径和用途。
格式标准:
/**
* filekorolheader: [页面名称] - [功能描述]
* 功能:[主要功能列表]
* 路径:[页面路由路径]
* 规范:[遵循的特殊规范说明]
*/
示例:
/**
* filekorolheader: 物联设备数据接入页面 - IoT设备数据管理中心
* 功能:设备列表管理、实时数据监控、数据对比分析、报告生成
* 路径:/ai-crop-model/data-sense-center/iot
* 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,shadcn语义化样式
*/
实施要点:
- 必须放在文件最顶部,在'use client'之前
- 页面名称要准确反映业务功能
- 功能描述要简明扼要,列出核心功能
- 路径必须是完整的路由路径
- 如有特殊规范遵循,需要在规范字段说明
path:land-information/archive/statistics,name:统计分析页面开发经验
总体开发经验总结
在实现统计分析页面过程中,我们遵循了以下8条核心开发规范,确保代码质量、可维护性和用户体验的一致性。
1. shadcn 样式系统优先原则
经验总结:
- 优先使用shadcn的语义化颜色类(如
bg-card、bg-background)替代硬编码的Tailwind CSS颜色 - 避免使用
bg-red-500等具体颜色值,改用bg-red-50 dark:bg-red-950等语义化类 - 统计卡片使用
bg-green-50 dark:bg-green-950等主题感知的背景色
最佳实践:
// ✅ 推荐写法
<Card className="p-4 bg-green-50 dark:bg-green-950">
<Card className="bg-card hover:bg-muted">
// ❌ 避免写法
<Card className="p-4 bg-green-100">
<Card className="bg-white hover:bg-gray-100">
2. 标签组件样式精确还原原则
经验总结:
- 严格按照参考文件实现标签的边框和颜色样式
- 使用
style属性精确控制颜色,确保1:1还原视觉效果 - 不同类型的标签有特定的样式特征需要保持一致
关键实现:
// 土壤类型标签:前缀彩色圆点
<div className="w-2 h-2 rounded-full mr-2" style={{ backgroundColor: type.color }} />
// 种植模式标签:前缀emoji + 固定绿色边框
<span className="mr-1">{mode.emoji}</span>
style={{
backgroundColor: filters.plantingModes.includes(mode.key) ? '#16a34a' : 'transparent',
borderColor: '#16a34a',
}}
// 自定义标签:纯色边框背景
style={{
backgroundColor: filters.tags.includes(tag.name) ? tag.color : 'transparent',
borderColor: tag.color,
}}
3. 最小化修改原则
经验总结:
- 严格遵循参考文件的功能边界,不添加多余功能
- 保持与原有系统的功能一致性
- 避免过度设计,专注核心功能实现
实施要点:
- 只实现参考文件中明确展示的功能
- 保持相同的用户交互流程
- 维持原有的数据结构和逻辑
4. 暗色主题全面支持原则
经验总结:
- 所有组件都必须支持暗色主题切换
- 使用
dark:前缀提供暗色模式样式 - 确保在暗色主题下的可读性和视觉效果
实现模式:
// 统计卡片暗色主题
<Card className="p-4 bg-green-50 dark:bg-green-950">
<div className="text-2xl text-green-600 dark:text-green-400">
// 背景和边框暗色主题
<Card className="bg-card hover:bg-muted">
<Card className="border-blue-200 dark:border-blue-800">
5. useReducer 状态管理架构原则
经验总结:
- 使用useReducer实现复杂状态管理,避免prop drilling
- 通过dispatch实现跨组件状态同步
- 集中化状态逻辑,提高代码可维护性
架构模式:
// Reducer定义
export interface LandStatisticsState {
fields: Land[];
filters: FilterCondition;
statistics: StatisticsResult | null;
chartType: 'bar' | 'pie';
}
// Action类型定义
export type LandStatisticsAction =
| { type: 'SET_FIELDS'; payload: Land[] }
| { type: 'UPDATE_FILTER'; payload: { key: keyof FilterCondition; value: any } }
| { type: 'SET_STATISTICS'; payload: StatisticsResult | null };
// 状态同步使用
const handleFilterChange = (key: keyof FilterCondition, value: any) => {
dispatch({ type: 'UPDATE_FILTER', payload: { key, value } });
};
6. 模块化组件架构原则
经验总结:
- 每个页面建立独立的components文件夹
- 按功能职责拆分组件,确保单一职责
- 组件间通过props和回调函数通信
目录结构示例:
src/app/(app)/land-information/archive/statistics/
├── page.tsx # 主页面
└── components/
├── landStatisticsReducer.tsx # 状态管理
├── FilterPanel.tsx # 筛选面板
├── StatisticsResults.tsx # 统计结果
└── UsageExamples.tsx # 使用示例
7. 完整依赖引用实现原则
经验总结:
- 仔细分析参考文件的import依赖,确保完整实现
- 将所有引用的组件都实现在components目录中
- 保持与参考文件相同的组件结构和功能
依赖检查清单:
- UI组件:Card, Button, Badge, Input, Label等
- 图标组件:lucide-react图标
- 图表组件:recharts相关组件
- 工具函数:toast通知等
8. 1:1 功能还原实现原则
经验总结:
- 严格按照参考文件的功能逻辑实现
- 保持相同的用户交互体验
- 确保数据流和业务逻辑的一致性
关键实现要点:
- 筛选条件的多选逻辑
- 数据统计的计算方法
- 图表切换和显示逻辑
- 数据导出功能
开发工具和最佳实践
推荐工具链
- 状态管理:React useReducer
- UI组件库:shadcn/ui
- 样式系统:Tailwind CSS + 语义化颜色
- 图表库:Recharts
- 图标库:Lucide React
- 通知系统:Sonner
代码质量保证
- TypeScript严格类型检查
- ESLint代码规范检查
- 组件props类型定义完整
- 状态管理逻辑清晰可维护
测试数据管理
- localStorage数据持久化
- 完整的测试数据覆盖所有业务场景
- 数据初始化和清理机制完善
通过遵循这些开发规范,我们可以确保代码的一致性、可维护性和用户体验的统一性。
9.注意乱码原则
生成的代码注意看看有没有乱码,必须遵守utf-8编码
10.接口调用原则。
接口必须调用 D:\code\repotest\smart-crop-ui\crop-x\src\lib\api\sdk.gen.ts 这个里面的接口 比如 /api/v1/departments/tree 这个路径,就是要调用export const getDepartmentTreeApiV1DepartmentsDepartmentsTreeGet = (options?: Options<GetDepartmentTreeApiV1DepartmentsDepartmentsTreeGetData, ThrowOnError>) => { return (options?.client ?? client).get<GetDepartmentTreeApiV1DepartmentsDepartmentsTreeGetResponses, unknown, ThrowOnError>({ security: [ { scheme: 'bearer', type: 'http' } ], url: '/api/v1/departments/departments/tree', ...options }); }; 实际使用的时候,要参考D:\code\repotest\smart-crop-ui\crop-x\src\app(app)\central-config\tenant\audit-history\components\auditHistoryApi.ts 里面 import { getTenantAuditLogsApiV1TenantsAuditLogsGet, } from "@/lib/api/sdk.gen"; 这个引入和用法。
path:land-information/archive/statistics,name:统计分析页面开发经验与问题解决
问题1:图表横轴显示不完整
问题描述:
- 初始实现中,图表只显示有数据的土壤类型和种植模式
- 没有数据的项目在横轴上不显示,导致图表看起来不完整
原始需求分析:
- 土壤类型分布应显示所有定义的土壤类型,即使数量为0
- 种植模式分布应显示所有定义的种植模式,提供完整的分类视图
解决方案:
- 修改数据计算逻辑,从"基于筛选结果生成数据"改为"基于所有定义的分类生成数据"
- 使用
state.soilTypes.map()和state.plantingModes.map()确保显示所有定义的分类
代码改进对比:
// ❌ 初始实现(只显示有数据的分类)
const soilTypeMap = new Map<string, { count: number; area: number }>();
filteredFields.forEach(f => {
const current = soilTypeMap.get(f.soilType) || { count: 0, area: 0 };
soilTypeMap.set(f.soilType, {
count: current.count + 1,
area: current.area + f.area,
});
});
const soilTypeDistribution = Array.from(soilTypeMap.entries()).map(([key, value]) => ({
name: state.soilTypes.find(s => s.key === key)?.name || key,
count: value.count,
area: value.area,
color: state.soilTypes.find(s => s.key === key)?.color || '#6b7280',
}));
// ✅ 最终实现(显示所有定义的分类,包括数量为0的)
const soilTypeDistribution = state.soilTypes.map(soilType => {
const count = filteredFields.filter(f => f.soilType === soilType.key).length;
const area = filteredFields
.filter(f => f.soilType === soilType.key)
.reduce((sum, f) => sum + f.area, 0);
return {
name: soilType.name,
count,
area,
color: soilType.color,
};
});
问题2:标签字体粗细不符合视觉要求
问题描述:
- 筛选标签(土壤类型、种植模式、自定义标签)的字体过粗
- 用户反馈需要调整为细字体,以匹配参考文件的视觉效果
原始需求分析:
- 参考文件显示的是细字效果,需要精确还原视觉体验
- 字体粗细影响整体UI的美观和专业度
解决方案:
- 给所有Badge组件添加
font-light类名 - 保持其他样式(颜色、边框、悬停效果)不变,只调整字体粗细
代码改进:
// ❌ 初始实现
<Badge
key={type.id}
variant={filters.soilTypes.includes(type.key) ? 'default' : 'outline'}
className="cursor-pointer"
style={{ backgroundColor: filters.soilTypes.includes(type.key) ? type.color : 'transparent' }}
>
{type.name}
</Badge>
// ✅ 最终实现(添加字体细体)
<Badge
key={type.id}
variant={filters.soilTypes.includes(type.key) ? 'default' : 'outline'}
className="cursor-pointer font-light"
style={{ backgroundColor: filters.soilTypes.includes(type.key) ? type.color : 'transparent' }}
>
{type.name}
</Badge>
问题3:测试数据覆盖不完整影响演示效果
问题描述:
- localStorage中存在旧数据,导致某些土壤类型和种植模式没有对应的地块数据
- 部分图表项目显示为空或缺失,影响演示效果和用户体验
原始需求分析:
- 所有土壤类型和种植模式都应该有对应的测试数据
- 确保图表能完整展示所有分类的统计数据,即使是0也要显示
- 为用户提供完整的演示环境
解决方案:
- 创建完整的测试数据集,覆盖所有7种土壤类型和5种种植模式
- 在
loadData函数中初始化这些测试数据,确保首次访问时有完整数据 - 通过localStorage持久化,确保数据在页面刷新后仍然存在
测试数据设计原则:
const testFields = [
{
id: '1',
code: 'TD001',
name: '东区沙质土试验田',
area: 85.5,
location: '东区1号地块',
soilType: 'sandy', // 沙质土
plantingMode: 'conventional', // 传统种植
tags: ['有机种植', '高产示范', '滴灌设施'],
// ...其他完整字段
},
// 总计10个地块,确保每个土壤类型和种植模式都有覆盖
// 沙质土(2个)、黏质土(2个)、壤质土(2个)、泥炭土(1个)、石灰质土(1个)、粉质土(1个)、岩石土(1个)
// 传统种植(3个)、有机种植(3个)、温室种植(2个)、水培种植(1个)、气培种植(1个)
];
问题4:语义化颜色类使用存在不一致
问题描述:
- 部分组件仍使用硬编码的Tailwind颜色类
- 没有充分利用shadcn的语义化颜色系统
- 在暗色主题下可能存在兼容性问题
原始需求分析:
- 优先使用
bg-gray等语义化颜色类 - 避免写死的Tailwind CSS样式,提高主题一致性
- 建立统一的颜色使用标准
解决方案:
- 全面检查并替换硬编码颜色为语义化颜色类
- 统计卡片使用
bg-green-50 dark:bg-green-950等主题感知背景色 - 确保在暗色主题下的可读性和视觉效果一致性
颜色使用改进:
// ❌ 不一致的硬编码实现
<Card className="p-4 bg-green-100">
<div className="text-green-600">
<Card className="bg-white hover:bg-gray-100">
// ✅ 统一的语义化颜色实现
<Card className="p-4 bg-green-50 dark:bg-green-950">
<div className="text-2xl text-green-600 dark:text-green-400">
<Card className="bg-card hover:bg-muted">
<Card className="border-blue-200 dark:border-blue-800">
问题5:多组件状态同步和数据管理复杂性
问题描述:
- 多个组件之间需要共享状态,使用prop传递会导致代码复杂且难以维护
- 数据更新时容易出现状态不一致的问题
- 缺乏集中化的状态管理机制
原始需求分析:
- 使用useReducer实现集中化状态管理
- 确保组件间数据同步的可靠性和性能
- 简化组件间的通信逻辑
解决方案:
- 设计完整的状态管理架构,包括状态接口、Action类型和Reducer函数
- 通过dispatch实现跨组件状态更新,避免prop drilling
- 使用localStorage进行数据持久化,确保页面刷新后状态保持
- 建立清晰的数据流和状态更新模式
状态管理架构设计:
// 完整的状态接口定义
export interface LandStatisticsState {
fields: Land[];
tags: LandTag[];
soilTypes: SoilType[];
plantingModes: PlantingMode[];
filters: FilterCondition;
statistics: StatisticsResult | null;
chartType: 'bar' | 'pie';
}
// 细粒度的Action类型定义
export type LandStatisticsAction =
| { type: 'SET_FIELDS'; payload: Land[] }
| { type: 'SET_TAGS'; payload: LandTag[] }
| { type: 'SET_SOIL_TYPES'; payload: SoilType[] }
| { type: 'SET_PLANTING_MODES'; payload: PlantingMode[] }
| { type: 'SET_FILTERS'; payload: FilterCondition }
| { type: 'UPDATE_FILTER'; payload: { key: keyof FilterCondition; value: any } }
| { type: 'TOGGLE_ARRAY_FILTER'; payload: { key: 'soilTypes' | 'plantingModes' | 'tags'; value: string } }
| { type: 'CLEAR_FILTERS' }
| { type: 'SET_STATISTICS'; payload: StatisticsResult | null }
| { type: 'SET_CHART_TYPE'; payload: 'bar' | 'pie' };
// 跨组件状态同步实现
const handleFilterChange = (key: keyof FilterCondition, value: any) => {
dispatch({ type: 'UPDATE_FILTER', payload: { key, value } });
};
const handleToggleArrayFilter = (key: 'soilTypes' | 'plantingModes' | 'tags', value: string) => {
const currentArray = state.filters[key];
const newArray = currentArray.includes(value)
? currentArray.filter(v => v !== value)
: [...currentArray, value];
dispatch({ type: 'TOGGLE_ARRAY_FILTER', payload: { key, value } });
};
开发经验对比总结
与原始要求的差异分析
| 原始要求 | 实际实现 | 差异说明 | 解决过程 |
|---|---|---|---|
| 使用shadcn语义化样式 | 完全实现 + 统一规范 | 需要建立统一的使用标准 | 全面替换硬编码颜色,建立语义化颜色使用指南 |
| 1:1还原标签样式 | 精确还原 + 字体优化 | 字体粗细需要调整以匹配视觉 | 添加font-light类名,保持样式一致性 |
| 不动无关内容 | 完全遵循 | 严格保持功能边界,不添加多余功能 | 只实现参考文件中的明确功能 |
| 暗色主题支持 | 全面支持 | 需要系统化处理所有UI组件 | 使用dark:前缀系统化处理暗色主题 |
| useReducer状态管理 | 架构实现 + 最佳实践 | 需要设计状态同步机制和数据持久化 | 建立完整的状态管理架构和同步机制 |
| 模块化组件 | 完全拆分 | 需要合理的组件职责划分和通信机制 | 按功能领域拆分组件,通过props和回调通信 |
| 完整依赖引用 | 1:1还原 | 需要仔细分析引用关系和依赖完整性 | 建立依赖检查清单,确保所有引用组件完整实现 |
| 1:1功能还原 | 完整实现 | 需要精确还原业务逻辑和用户体验 | 严格对照参考文件实现所有功能 |
关键学习点和改进
-
数据完整性思维:不仅要实现功能,还要考虑数据的完整性和演示效果的完整性
- 学会了从用户体验角度思考数据展示的完整性
- 理解了即使count为0也应该显示的重要性
-
细节精确把控:字体粗细、颜色、边框等视觉细节需要精确还原
- 培养了对UI细节的敏感度
- 掌握了通过用户反馈快速迭代优化的方法
-
状态管理设计:useReducer不仅是技术选择,更是架构设计决策
- 深入理解了状态管理的架构设计原则
- 掌握了跨组件状态同步的最佳实践
-
渐进式优化:在开发过程中根据反馈不断调整和改进
- 学会了灵活应对开发过程中的需求变化
- 建立了基于反馈的快速响应机制
-
文档化习惯:将开发过程中的经验和教训记录下来,形成知识积累
- 认识到文档化对团队协作和知识传承的重要性
- 建立了完整的开发规范文档体系
path:land-information/map/gis,name:GIS地图管理开发经验与问题解决
总体开发经验总结
GIS地图管理页面的开发过程是一个复杂的技术集成挑战,涉及到第三方地图库的集成、异步资源加载、多层级组件交互等多个技术难点。通过这次开发,我们建立了一套完整的GIS应用开发模式,特别是在处理真实地图数据源和优雅降级方面积累了宝贵经验。
问题1:地图组件初始化时缺少真实地图数据源
问题描述:
- 初始实现的BaseMap组件只是简单的模拟展示,无法加载真实的卫星图像
- 用户反馈参考文件可以看到真实的卫星图,但当前页面只显示占位符
- 缺乏对真实地图服务商的集成支持
原始需求分析:
- 需要支持真实的卫星影像显示,而不是简单的占位地图
- 必须支持多种地图图层切换(卫星、电子、地形、混合)
- 需要完整的地图交互功能,包括缩放、平移、全屏等
解决方案:
- 复制完整的GISMapEngine实现,支持多种地图提供商
- 实现leafletLoader动态加载器,支持异步加载地图库
- 建立真实地图瓦片数据源连接,包括ArcGIS卫星影像和OpenStreetMap
代码实现对比:
// ❌ 初始简化实现
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统一管理地图库的加载过程
- 实现加载状态管理和重试机制
- 建立全局加载状态缓存,避免重复加载
关键实现代码:
// 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暴露地图实例方法给父组件
- 实现地图引擎的引用管理,确保状态同步
- 建立完整的生命周期管理,包括组件卸载时的资源清理
状态管理集成代码:
// 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格式
- 实现智能的图层切换和缓存机制
地图数据源配置:
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实现地块渲染
- 建立事件处理机制,连接地图交互和状态管理
- 集成完整的地图控件套件,提供专业级用户体验
交互功能实现:
// 地块多边形渲染
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,建立事件处理机制 |
关键学习点和改进
-
第三方库集成思维:学会了如何可靠地集成和管理复杂的第三方JavaScript库
- 掌握了异步加载、错误处理、优雅降级的完整流程
- 理解了库版本管理和兼容性处理的重要性
-
地图API应用经验:深入了解了Web地图开发的技术栈和最佳实践
- 学会了瓦片地图的原理和多种数据源的使用
- 掌握了地图交互事件的处理和状态同步机制
-
React高级模式应用:在复杂组件中应用了useImperativeHandle、useRef等高级React模式
- 深入理解了React组件的暴露方法和引用传递机制
- 掌握了复杂组件生命周期管理的最佳实践
-
性能优化意识:建立了地图应用的性能优化思维
- 学会了资源懒加载和缓存策略的设计
- 理解了大型第三方库对应用性能的影响和优化方法
-
用户体验设计:在技术实现中始终考虑用户体验
- 建立了加载状态和错误处理的设计模式
- 掌握了优雅降级和渐进增强的实现方法
-
架构设计能力:设计了可扩展的地图应用架构
- 建立了插件化的地图引擎设计
path:src/app/(app)/land-information/map/draw,name:数字化绘制与编辑页面开发经验
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. 可扩展性设计
- 插件化架构:编辑工具采用插件化设计,易于扩展新功能
- 接口标准化:统一的接口设计,便于功能模块替换
- 配置化开发:支持通过配置文件调整功能和行为
- 理解了复杂应用中的组件分层和职责划分