# 开发项目规范 ## 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` 等主题感知的背景色 **最佳实践:** ```tsx // ✅ 推荐写法 // ❌ 避免写法 ``` ### 2. 标签组件样式精确还原原则 **经验总结:** - 严格按照参考文件实现标签的边框和颜色样式 - 使用 `style` 属性精确控制颜色,确保1:1还原视觉效果 - 不同类型的标签有特定的样式特征需要保持一致 **关键实现:** ```tsx // 土壤类型标签:前缀彩色圆点
// 种植模式标签:前缀emoji + 固定绿色边框 {mode.emoji} 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:` 前缀提供暗色模式样式 - 确保在暗色主题下的可读性和视觉效果 **实现模式:** ```tsx // 统计卡片暗色主题
// 背景和边框暗色主题 ``` ### 5. useReducer 状态管理架构原则 **经验总结:** - 使用useReducer实现复杂状态管理,避免prop drilling - 通过dispatch实现跨组件状态同步 - 集中化状态逻辑,提高代码可维护性 **架构模式:** ```tsx // 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数据持久化 - 完整的测试数据覆盖所有业务场景 - 数据初始化和清理机制完善 通过遵循这些开发规范,我们可以确保代码的一致性、可维护性和用户体验的统一性。 --- ## path:land-information/archive/statistics,name:统计分析页面开发经验与问题解决 ### 问题1:图表横轴显示不完整 **问题描述:** - 初始实现中,图表只显示有数据的土壤类型和种植模式 - 没有数据的项目在横轴上不显示,导致图表看起来不完整 **原始需求分析:** - 土壤类型分布应显示所有定义的土壤类型,即使数量为0 - 种植模式分布应显示所有定义的种植模式,提供完整的分类视图 **解决方案:** - 修改数据计算逻辑,从"基于筛选结果生成数据"改为"基于所有定义的分类生成数据" - 使用 `state.soilTypes.map()` 和 `state.plantingModes.map()` 确保显示所有定义的分类 **代码改进对比:** ```tsx // ❌ 初始实现(只显示有数据的分类) const soilTypeMap = new Map(); 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` 类名 - 保持其他样式(颜色、边框、悬停效果)不变,只调整字体粗细 **代码改进:** ```tsx // ❌ 初始实现 {type.name} // ✅ 最终实现(添加字体细体) {type.name} ``` ### 问题3:测试数据覆盖不完整影响演示效果 **问题描述:** - localStorage中存在旧数据,导致某些土壤类型和种植模式没有对应的地块数据 - 部分图表项目显示为空或缺失,影响演示效果和用户体验 **原始需求分析:** - 所有土壤类型和种植模式都应该有对应的测试数据 - 确保图表能完整展示所有分类的统计数据,即使是0也要显示 - 为用户提供完整的演示环境 **解决方案:** - 创建完整的测试数据集,覆盖所有7种土壤类型和5种种植模式 - 在 `loadData` 函数中初始化这些测试数据,确保首次访问时有完整数据 - 通过localStorage持久化,确保数据在页面刷新后仍然存在 **测试数据设计原则:** ```tsx 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` 等主题感知背景色 - 确保在暗色主题下的可读性和视觉效果一致性 **颜色使用改进:** ```tsx // ❌ 不一致的硬编码实现
// ✅ 统一的语义化颜色实现
``` ### 问题5:多组件状态同步和数据管理复杂性 **问题描述:** - 多个组件之间需要共享状态,使用prop传递会导致代码复杂且难以维护 - 数据更新时容易出现状态不一致的问题 - 缺乏集中化的状态管理机制 **原始需求分析:** - 使用useReducer实现集中化状态管理 - 确保组件间数据同步的可靠性和性能 - 简化组件间的通信逻辑 **解决方案:** - 设计完整的状态管理架构,包括状态接口、Action类型和Reducer函数 - 通过dispatch实现跨组件状态更新,避免prop drilling - 使用localStorage进行数据持久化,确保页面刷新后状态保持 - 建立清晰的数据流和状态更新模式 **状态管理架构设计:** ```tsx // 完整的状态接口定义 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功能还原 | 完整实现 | 需要精确还原业务逻辑和用户体验 | 严格对照参考文件实现所有功能 | ### 关键学习点和改进 1. **数据完整性思维**:不仅要实现功能,还要考虑数据的完整性和演示效果的完整性 - 学会了从用户体验角度思考数据展示的完整性 - 理解了即使count为0也应该显示的重要性 2. **细节精确把控**:字体粗细、颜色、边框等视觉细节需要精确还原 - 培养了对UI细节的敏感度 - 掌握了通过用户反馈快速迭代优化的方法 3. **状态管理设计**:useReducer不仅是技术选择,更是架构设计决策 - 深入理解了状态管理的架构设计原则 - 掌握了跨组件状态同步的最佳实践 4. **渐进式优化**:在开发过程中根据反馈不断调整和改进 - 学会了灵活应对开发过程中的需求变化 - 建立了基于反馈的快速响应机制 5. **文档化习惯**:将开发过程中的经验和教训记录下来,形成知识积累 - 认识到文档化对团队协作和知识传承的重要性 - 建立了完整的开发规范文档体系