Files
smart-crop-ui/crop-x-new/DEVELOPMENT.md
2025-11-10 09:19:56 +08:00

13 KiB
Raw Blame History

开发指南

本文档为智慧农业生产管理系统的开发指南,帮助开发者快速了解项目结构、开发规范和最佳实践。

📋 目录

🛠️ 环境准备

必需软件

  • Node.js: >= 18.0.0
  • npm: >= 8.0.0 或 yarn: >= 1.22.0
  • Git: 最新版本

推荐工具

  • IDE: Visual Studio Code
  • 浏览器: Chrome/Firefox (最新版本)
  • Node管理: nvm (可选)

VSCode扩展推荐

项目已配置 .vscode/extensions.json,安装以下扩展获得最佳开发体验:

{
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "bradlc.vscode-tailwindcss",
    "ms-vscode.vscode-typescript-next",
    "formulahendry.auto-rename-tag",
    "christian-kohler.path-intellisense"
  ]
}

📁 项目结构详解

核心目录

src/
├── components/          # 组件库
│   ├── ui/             # shadcn/ui基础组件
│   ├── common/         # 通用业务组件
│   └── layouts/        # 布局组件
├── pages/              # 页面组件
├── hooks/              # 自定义Hooks
├── lib/                # 工具库
├── config/             # 配置文件
├── types/              # TypeScript类型
├── utils/              # 工具函数
├── styles/             # 样式文件
└── assets/             # 静态资源

组件组织原则

  1. UI组件 (components/ui/): 纯UI组件无业务逻辑
  2. 业务组件 (components/common/): 包含业务逻辑的复用组件
  3. 页面组件 (pages/): 具体页面实现,组合业务组件
  4. 布局组件 (components/layouts/): 页面布局结构

📏 开发规范

代码规范

TypeScript规范

// ✅ 使用类型注解
interface UserData {
  id: string
  name: string
  email: string
}

const getUser = async (id: string): Promise<UserData> => {
  // 实现
}

// ✅ 使用泛型
interface ApiResponse<T> {
  data: T
  success: boolean
}

// ✅ 枚举使用
enum UserRole {
  ADMIN = 'admin',
  USER = 'user'
}

React组件规范

// ✅ 函数式组件 + Hooks
interface UserCardProps {
  user: UserData
  onEdit?: (user: UserData) => void
  className?: string
}

export const UserCard: React.FC<UserCardProps> = ({
  user,
  onEdit,
  className
}) => {
  const [isEditing, setIsEditing] = useState(false)

  const handleEdit = useCallback(() => {
    onEdit?.(user)
    setIsEditing(true)
  }, [user, onEdit])

  return (
    <div className={cn('user-card', className)}>
      {/* 组件内容 */}
    </div>
  )
}

命名规范

  • 文件名: kebab-case (user-card.tsx)
  • 组件名: PascalCase (UserCard)
  • 变量名: camelCase (userName)
  • 常量名: UPPER_SNAKE_CASE (API_BASE_URL)
  • 类型名: PascalCase (UserData)

Git提交规范

使用 Conventional Commits 规范:

<type>(<scope>): <subject>

<body>

<footer>

提交类型

  • feat: 新功能
  • fix: 修复bug
  • docs: 文档更新
  • style: 代码格式化
  • refactor: 重构代码
  • test: 测试相关
  • chore: 构建工具、依赖更新

示例

feat(machinery): add machinery status monitoring
fix(auth): resolve login validation issue
docs(readme): update installation guide

🧩 组件开发

组件结构模板

// src/components/example/ExampleComponent.tsx
import React, { useState, useCallback } from 'react'
import { cn } from '@/lib/utils'

interface ExampleComponentProps {
  title: string
  onAction?: () => void
  className?: string
}

export const ExampleComponent: React.FC<ExampleComponentProps> = ({
  title,
  onAction,
  className
}) => {
  const [state, setState] = useState(false)

  const handleClick = useCallback(() => {
    setState(prev => !prev)
    onAction?.()
  }, [onAction])

  return (
    <div className={cn('example-component', className)}>
      <h3 className="example-title">{title}</h3>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  )
}

组件样式规范

/* 优先使用Tailwind CSS类名 */
.example-component {
  @apply rounded-lg border border-gray-200 p-4 bg-white shadow-sm;
}

.example-title {
  @apply text-lg font-semibold text-gray-900 mb-2;
}

/* 必要时使用传统CSS */
.example-component:hover {
  transform: translateY(-1px);
  transition: transform 0.2s ease;
}

shadcn/ui组件使用

import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'

export const MachineryCard = ({ machinery }) => {
  return (
    <Card className="machinery-card">
      <CardHeader>
        <CardTitle>{machinery.name}</CardTitle>
        <Badge variant={machinery.status === 'running' ? 'default' : 'secondary'}>
          {machinery.status}
        </Badge>
      </CardHeader>
      <CardContent>
        {/* 内容 */}
      </CardContent>
    </Card>
  )
}

🔄 状态管理

useState使用

const [formData, setFormData] = useState({
  name: '',
  email: '',
  phone: ''
})

// 更新对象状态
const handleChange = (field: string, value: string) => {
  setFormData(prev => ({
    ...prev,
    [field]: value
  }))
}

useReducer使用

type State = {
  count: number
  loading: boolean
}

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'setLoading'; payload: boolean }

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 }
    case 'decrement':
      return { ...state, count: state.count - 1 }
    case 'setLoading':
      return { ...state, loading: action.payload }
    default:
      return state
  }
}

const [state, dispatch] = useReducer(reducer, {
  count: 0,
  loading: false
})

自定义Hooks

// src/hooks/useApi.ts
import { useState, useEffect } from 'react'

export function useApi<T>(url: string) {
  const [data, setData] = useState<T | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true)
        const response = await fetch(url)
        const result = await response.json()
        setData(result)
      } catch (err) {
        setError(err.message)
      } finally {
        setLoading(false)
      }
    }

    fetchData()
  }, [url])

  return { data, loading, error }
}

🎨 样式指南

Tailwind CSS最佳实践

// ✅ 使用cn工具函数合并类名
import { cn } from '@/lib/utils'

const Button = ({ variant = 'primary', className, ...props }) => {
  return (
    <button
      className={cn(
        'px-4 py-2 rounded-md font-medium transition-colors',
        {
          'bg-blue-600 text-white hover:bg-blue-700': variant === 'primary',
          'bg-gray-200 text-gray-900 hover:bg-gray-300': variant === 'secondary'
        },
        className
      )}
      {...props}
    />
  )
}

响应式设计

// 移动优先的响应式设计
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* 内容 */}
</div>

// 响应式间距
<div className="px-4 sm:px-6 lg:px-8">
  {/* 内容 */}
</div>

深色模式支持

import { useTheme } from '@/hooks/useTheme'

export const ThemedComponent = () => {
  const { theme, setTheme } = useTheme()

  return (
    <div className="bg-background text-foreground">
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        切换主题
      </button>
    </div>
  )
}

🌐 API集成

API配置

// src/config/api.ts
export const API_CONFIG = {
  baseUrl: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
}

export const apiClient = {
  get: <T>(url: string): Promise<T> => {
    return fetch(`${API_CONFIG.baseUrl}${url}`).then(res => res.json())
  },

  post: <T>(url: string, data: any): Promise<T> => {
    return fetch(`${API_CONFIG.baseUrl}${url}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    }).then(res => res.json())
  }
}

数据获取Hook

// src/hooks/useMachinery.ts
import { useState, useEffect } from 'react'
import { apiClient } from '@/config/api'

export function useMachinery() {
  const [machinery, setMachinery] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    apiClient.get('/machinery')
      .then(setMachinery)
      .catch(setError)
      .finally(() => setLoading(false))
  }, [])

  return { machinery, loading, error }
}

🧪 测试指南

单元测试示例

// src/components/__tests__/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from '../Button'

describe('Button', () => {
  it('renders correctly', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByRole('button')).toBeInTheDocument()
  })

  it('calls onClick when clicked', () => {
    const handleClick = jest.fn()
    render(<Button onClick={handleClick}>Click me</Button>)

    fireEvent.click(screen.getByRole('button'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })
})

Hook测试

// src/hooks/__tests__/useCounter.test.ts
import { renderHook, act } from '@testing-library/react'
import { useCounter } from '../useCounter'

describe('useCounter', () => {
  it('initializes with default value', () => {
    const { result } = renderHook(() => useCounter())
    expect(result.current.count).toBe(0)
  })

  it('increments count', () => {
    const { result } = renderHook(() => useCounter())

    act(() => {
      result.current.increment()
    })

    expect(result.current.count).toBe(1)
  })
})

🐛 调试技巧

React DevTools

安装 React Developer Tools 浏览器扩展:

# 检查组件状态
# 查看组件层次结构
# 性能分析

VSCode调试配置

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug React",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/vite",
      "args": ["--mode", "development"],
      "env": {
        "NODE_ENV": "development"
      }
    }
  ]
}

常用调试代码

// 开发环境调试
if (import.meta.env.DEV) {
  console.log('Debug info:', data)
}

// 性能监控
console.time('component-render')
// ... 组件渲染逻辑
console.timeEnd('component-render')

// 网络请求调试
const debugFetch = async (url: string) => {
  console.log(`Fetching: ${url}`)
  const start = performance.now()

  try {
    const response = await fetch(url)
    const data = await response.json()
    console.log(`Fetched in ${performance.now() - start}ms`, data)
    return data
  } catch (error) {
    console.error(`Fetch failed after ${performance.now() - start}ms`, error)
    throw error
  }
}

常见问题

Q: 如何添加新的UI组件

A:

  1. src/components/ui/ 下创建组件文件
  2. 使用 shadcn/ui 设计规范
  3. 添加 TypeScript 类型定义
  4. 编写组件文档

Q: 如何处理表单验证?

A: 推荐使用 react-hook-form + zod

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  name: z.string().min(1, '名称不能为空'),
  email: z.string().email('邮箱格式不正确')
})

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema)
})

Q: 如何优化性能?

A:

  1. 使用 React.memo 避免不必要的重渲染
  2. 使用 useMemo 和 useCallback 缓存计算结果
  3. 实现虚拟列表处理大数据
  4. 使用代码分割减少初始加载时间

Q: 如何处理国际化?

A: 项目支持多语言配置:

// src/config/i18n.ts
export const locales = {
  'zh-CN': '简体中文',
  'en-US': 'English'
}

export const translations = {
  'zh-CN': {
    'machinery.title': '农机管理',
    'machinery.status.running': '运行中'
  },
  'en-US': {
    'machinery.title': 'Machinery Management',
    'machinery.status.running': 'Running'
  }
}

Q: 如何配置开发工具?

A: 使用项目提供的脚本:

# 查看状态
npm run scripts:setup

# 启用工具
npm run scripts:enable

# 禁用工具
npm run scripts:disable

📚 更多资源


💡 提示: 如果遇到问题,请先查看常见问题部分,或联系项目维护者获取帮助。