生产管理系统 nextjs标准化改造
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"tools": {
|
||||
"eslint": {
|
||||
"enabled": false,
|
||||
"description": "ESLint代码检查工具",
|
||||
"configFile": ".eslintrc.cjs",
|
||||
"ignoreFile": ".eslintignore"
|
||||
},
|
||||
"prettier": {
|
||||
"enabled": false,
|
||||
"description": "Prettier代码格式化工具",
|
||||
"configFile": ".prettierrc",
|
||||
"ignoreFile": ".prettierignore"
|
||||
},
|
||||
"husky": {
|
||||
"enabled": false,
|
||||
"description": "Git hooks工具",
|
||||
"hooksDir": ".husky"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"setup": "node scripts/setup-dev-tools.js",
|
||||
"enable": "node scripts/setup-dev-tools.js --enable",
|
||||
"disable": "node scripts/setup-dev-tools.js --disable"
|
||||
},
|
||||
"note": "将enabled设置为true来启用对应的开发工具,或运行 npm run scripts:enable 来启用所有工具"
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
build
|
||||
.output
|
||||
.nuxt
|
||||
.next
|
||||
.vite
|
||||
.cache
|
||||
|
||||
# Development files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# IDE files
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
LICENSE.md
|
||||
docs
|
||||
|
||||
# Config files that shouldn't be in container
|
||||
.eslintrc*
|
||||
.prettierrc*
|
||||
prettier.config.js
|
||||
.editorconfig
|
||||
|
||||
# Testing
|
||||
jest.config.js
|
||||
cypress
|
||||
test
|
||||
tests
|
||||
|
||||
# Misc
|
||||
.turbo
|
||||
.vercel
|
||||
.netlify
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Package manager lock files (keep package-lock.json but ignore others)
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
.dockerignore
|
||||
35
.env.example
35
.env.example
@@ -1,35 +0,0 @@
|
||||
# 环境配置示例文件
|
||||
# 复制此文件为 .env.local 并根据实际情况修改配置
|
||||
|
||||
# 当前环境: development, test, production
|
||||
NODE_ENV=development
|
||||
|
||||
# API 服务器地址 (用于 API 代码生成)
|
||||
API_BASE_URL=http://localhost:8080
|
||||
|
||||
# React 应用配置 (用于前端运行时)
|
||||
REACT_APP_API_URL=http://localhost:8080
|
||||
|
||||
# OpenAPI 文档地址
|
||||
REACT_APP_OPENAPI_URL=http://localhost:8080/openapi.json
|
||||
|
||||
# 其他可选配置
|
||||
# REACT_APP_API_KEY=your-api-key-here
|
||||
# REACT_APP_DEBUG=true
|
||||
|
||||
# 不同环境配置示例:
|
||||
#
|
||||
# 开发环境:
|
||||
# NODE_ENV=development
|
||||
# API_BASE_URL=http://localhost:8080
|
||||
# REACT_APP_API_URL=http://localhost:8080
|
||||
#
|
||||
# 测试环境:
|
||||
# NODE_ENV=test
|
||||
# API_BASE_URL=http://test-api.example.com
|
||||
# REACT_APP_API_URL=http://test-api.example.com
|
||||
#
|
||||
# 生产环境:
|
||||
# NODE_ENV=production
|
||||
# API_BASE_URL=https://api.example.com
|
||||
# REACT_APP_API_URL=https://api.example.com
|
||||
@@ -1,47 +0,0 @@
|
||||
// ESLint配置文件 - 需要通过 .dev-tools-config.json 启用
|
||||
// 运行 `npm run scripts:enable` 来启用ESLint
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 检查开发工具配置
|
||||
const configPath = path.join(__dirname, '.dev-tools-config.json');
|
||||
let eslintEnabled = false;
|
||||
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
eslintEnabled = config.tools?.eslint?.enabled === true;
|
||||
} catch (error) {
|
||||
console.warn('⚠️ 无法读取开发工具配置,ESLint将被禁用');
|
||||
}
|
||||
|
||||
// 如果ESLint被禁用,返回空配置
|
||||
if (!eslintEnabled) {
|
||||
console.log('ℹ️ ESLint已在配置中被禁用,如需启用请运行: npm run scripts:enable');
|
||||
module.exports = {};
|
||||
return;
|
||||
}
|
||||
|
||||
// ESLint正常配置
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs', 'node_modules'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
},
|
||||
};
|
||||
142
.gitignore
vendored
142
.gitignore
vendored
@@ -1,113 +1,41 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Production builds
|
||||
.next/
|
||||
out/
|
||||
dist/
|
||||
build/
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# API 相关文件
|
||||
# 忽略从服务器下载的临时 OpenAPI JSON 文件
|
||||
api/v1-from-server.json
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Local development
|
||||
.local
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
*.log
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
coverage
|
||||
.nyc_output
|
||||
.cache
|
||||
.temp
|
||||
.vscode
|
||||
.idea
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
12
.prettierrc
12
.prettierrc
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
277
CHANGELOG.md
277
CHANGELOG.md
@@ -1,277 +0,0 @@
|
||||
# 更新日志
|
||||
|
||||
所有重要的项目变更都会记录在此文件中。
|
||||
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [未发布]
|
||||
|
||||
### 计划中
|
||||
- 完整的UI组件库集成
|
||||
- 农业管理专用组件开发
|
||||
- 视觉一致性验证系统
|
||||
- 完整的测试覆盖
|
||||
- 国际化支持
|
||||
|
||||
## [1.0.0] - 2024-01-20
|
||||
|
||||
### 🎉 首次发布
|
||||
|
||||
#### ✨ 新增功能
|
||||
- **完整的项目基础架构**
|
||||
- React 18 + Vite 6 + TypeScript 技术栈
|
||||
- shadcn/ui + Tailwind CSS UI框架
|
||||
- 完整的开发工具链(可选启用)
|
||||
- 标准化的项目目录结构
|
||||
|
||||
- **React应用完整实现**
|
||||
- main.tsx 应用入口文件
|
||||
- App.tsx 主应用组件(包含完整管理系统界面)
|
||||
- 欢迎页面和系统状态展示
|
||||
- 技术栈展示和快速操作
|
||||
- 主题切换功能
|
||||
- 响应式布局设计
|
||||
|
||||
- **开发工具配置**
|
||||
- ESLint + Prettier 代码质量工具
|
||||
- 可选的开发工具开关控制
|
||||
- VSCode 工作区配置
|
||||
- 自动化代码格式化和检查
|
||||
|
||||
- **构建优化**
|
||||
- Vite 6 构建配置优化
|
||||
- 代码分割和懒加载
|
||||
- 热重载优化(<2秒响应时间)
|
||||
- 生产环境构建优化
|
||||
|
||||
- **样式系统**
|
||||
- 农业主题色彩系统
|
||||
- 响应式设计支持
|
||||
- 深色/浅色主题切换
|
||||
- 完整的设计令牌
|
||||
|
||||
- **类型系统**
|
||||
- 完整的 TypeScript 类型定义
|
||||
- 农业管理领域模型
|
||||
- API 响应类型定义
|
||||
- 组件 Props 类型规范
|
||||
|
||||
- **工具函数库**
|
||||
- 日期时间处理工具
|
||||
- 数据格式化函数
|
||||
- 农机状态映射
|
||||
- 通用工具函数
|
||||
|
||||
- **自定义 Hooks**
|
||||
- useTheme 主题管理
|
||||
- useLocalStorage 本地存储
|
||||
- useDebounce 防抖处理
|
||||
- 扩展中...
|
||||
|
||||
- **项目文档**
|
||||
- 详细的 README.md
|
||||
- 完整的开发指南
|
||||
- 贡献指南和行为准则
|
||||
- API 文档(规划中)
|
||||
|
||||
#### 🏗️ 架构改进
|
||||
- 模块化的项目结构
|
||||
- 组件驱动的开发模式
|
||||
- 类型安全的开发体验
|
||||
- 可扩展的架构设计
|
||||
|
||||
#### 📦 依赖管理
|
||||
- **核心依赖**:
|
||||
- React 18.3.1
|
||||
- Vite 6.3.5
|
||||
- TypeScript 5.6.2
|
||||
- Tailwind CSS 3.4.13
|
||||
|
||||
- **UI组件库**:
|
||||
- 完整的 Radix UI 组件集合
|
||||
- shadcn/ui 组件库基础
|
||||
- Lucide React 图标库
|
||||
|
||||
- **开发工具**:
|
||||
- ESLint 9.11.1
|
||||
- Prettier 3.3.3
|
||||
- Husky 9.1.6(可选)
|
||||
|
||||
#### 🎨 设计系统
|
||||
- 农业绿色主题 (#16a34a)
|
||||
- 一致的视觉语言
|
||||
- 响应式断点系统
|
||||
- 无障碍设计支持
|
||||
|
||||
#### 📚 文档完善
|
||||
- **README.md**: 项目介绍和快速开始
|
||||
- **DEVELOPMENT.md**: 详细的开发指南
|
||||
- **CONTRIBUTING.md**: 贡献流程和规范
|
||||
- **CHANGELOG.md**: 变更记录
|
||||
|
||||
#### 🔧 开发体验
|
||||
- 热重载开发服务器
|
||||
- TypeScript 严格模式
|
||||
- 自动代码格式化
|
||||
- 智能代码补全
|
||||
|
||||
### 🎯 功能模块规划
|
||||
|
||||
#### 🚜 农机管理 (Machinery)
|
||||
- [x] 模块结构搭建
|
||||
- [x] 基础组件框架
|
||||
- [ ] 农机档案管理
|
||||
- [ ] 驾驶员管理
|
||||
- [ ] 负载管理
|
||||
- [ ] 运行监控
|
||||
- [ ] 故障管理
|
||||
- [ ] 作业管理
|
||||
- [ ] 数据分析
|
||||
- [ ] 调度管理
|
||||
- [ ] 安全管理
|
||||
|
||||
#### 🌾 地块管理 (Field)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 地块档案
|
||||
- [ ] 土壤信息管理
|
||||
- [ ] 作物管理
|
||||
- [ ] 种植计划
|
||||
|
||||
#### 📋 农事管理 (Operation)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 作业计划
|
||||
- [ ] 进度跟踪
|
||||
- [ ] 成本核算
|
||||
- [ ] 产量预测
|
||||
|
||||
#### 💰 资产管理 (Asset)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 设备资产管理
|
||||
- [ ] 库存管理
|
||||
- [ ] 采购管理
|
||||
- [ ] 维护记录
|
||||
|
||||
#### 🤖 AI模型 (AI Model)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 智能预测
|
||||
- [ ] 图像识别
|
||||
- [ ] 数据分析
|
||||
- [ ] 决策支持
|
||||
|
||||
#### 💧 灌溉控制 (Irrigation)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 智能灌溉
|
||||
- [ ] 水资源管理
|
||||
- [ ] 设备控制
|
||||
- [ ] 用水统计
|
||||
|
||||
#### ⚙️ 配置管理 (Config)
|
||||
- [x] 模块结构搭建
|
||||
- [ ] 系统配置
|
||||
- [ ] 用户管理
|
||||
- [ ] 权限设置
|
||||
- [ ] 数据字典
|
||||
|
||||
### 🚀 性能指标
|
||||
|
||||
- **构建时间**: < 30秒
|
||||
- **热重载**: < 2秒
|
||||
- **首屏加载**: < 3秒(目标)
|
||||
- **代码分割**: 按模块自动分割
|
||||
- **包大小**: 优化中...
|
||||
|
||||
### 🔒 安全性
|
||||
- TypeScript 类型安全
|
||||
- 输入验证框架
|
||||
- XSS 防护
|
||||
- CSRF 保护(规划中)
|
||||
|
||||
### 🌍 国际化
|
||||
- 中文界面支持
|
||||
- 英文界面(规划中)
|
||||
- 多语言切换(规划中)
|
||||
|
||||
### 📱 兼容性
|
||||
- **现代浏览器**: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
|
||||
- **移动端**: iOS Safari 14+, Chrome Mobile 90+
|
||||
- **响应式**: 完整的移动端适配
|
||||
|
||||
### 🧪 测试覆盖
|
||||
- **单元测试**: 规划中
|
||||
- **集成测试**: 规划中
|
||||
- **E2E测试**: 规划中
|
||||
- **视觉回归测试**: 规划中
|
||||
|
||||
## 📈 版本规划
|
||||
|
||||
### [1.1.0] - 计划中
|
||||
- 完整的UI组件库实现
|
||||
- 农机管理核心功能
|
||||
- 基础数据可视化
|
||||
- 用户认证系统
|
||||
|
||||
### [1.2.0] - 计划中
|
||||
- 地块管理功能
|
||||
- 农事管理功能
|
||||
- 移动端适配优化
|
||||
- API 集成
|
||||
|
||||
### [1.3.0] - 计划中
|
||||
- 资产管理功能
|
||||
- AI 模型集成
|
||||
- 高级数据分析
|
||||
- 报表系统
|
||||
|
||||
### [2.0.0] - 计划中
|
||||
- 完整的功能覆盖
|
||||
- 微服务架构
|
||||
- 实时数据同步
|
||||
- 第三方系统集成
|
||||
|
||||
## 🏷️ 标签说明
|
||||
|
||||
- `✨ 新增功能`: 新的功能特性
|
||||
- `🔧 改进`: 现有功能的改进
|
||||
- `🐛 修复`: Bug修复
|
||||
- `📚 文档`: 文档相关变更
|
||||
- `🎨 样式`: UI/UX 相关变更
|
||||
- `⚡ 性能`: 性能优化
|
||||
- `🔒 安全`: 安全相关修复
|
||||
- `💥 破坏性变更`: 不兼容的API变更
|
||||
- `🗑️ 废弃`: 功能的废弃
|
||||
|
||||
## 🤝 贡献者
|
||||
|
||||
感谢所有为项目做出贡献的开发者!
|
||||
|
||||
- **主要贡献者**: [@your-username](https://github.com/your-username)
|
||||
- **项目维护**: [@maintainer](https://github.com/maintainer)
|
||||
|
||||
### 贡献统计
|
||||
|
||||
- 代码提交: XX 次
|
||||
- 功能添加: XX 个
|
||||
- Bug修复: XX 个
|
||||
- 文档更新: XX 次
|
||||
|
||||
## 📞 反馈和支持
|
||||
|
||||
如果您有任何问题或建议,请通过以下方式联系我们:
|
||||
|
||||
- **GitHub Issues**: [项目Issues页面](https://github.com/your-username/agriculture-management/issues)
|
||||
- **GitHub Discussions**: [讨论区](https://github.com/your-username/agriculture-management/discussions)
|
||||
- **邮箱**: support@example.com
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
- [项目主页](https://github.com/your-username/agriculture-management)
|
||||
- [在线演示](https://demo.example.com)
|
||||
- [API 文档](https://docs.example.com/api)
|
||||
- [设计规范](https://design.example.com)
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/) 规范。
|
||||
|
||||
📅 **最后更新**: 2024-01-20
|
||||
382
CONTRIBUTING.md
382
CONTRIBUTING.md
@@ -1,382 +0,0 @@
|
||||
# 贡献指南
|
||||
|
||||
感谢您对智慧农业生产管理系统项目的关注!我们欢迎所有形式的贡献,包括但不限于代码贡献、问题反馈、文档改进和功能建议。
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [行为准则](#行为准则)
|
||||
- [如何贡献](#如何贡献)
|
||||
- [开发流程](#开发流程)
|
||||
- [代码规范](#代码规范)
|
||||
- [提交规范](#提交规范)
|
||||
- [问题报告](#问题报告)
|
||||
- [功能请求](#功能请求)
|
||||
- [代码审查](#代码审查)
|
||||
- [发布流程](#发布流程)
|
||||
|
||||
## 🤝 行为准则
|
||||
|
||||
### 我们的承诺
|
||||
|
||||
为了营造一个开放和友好的环境,我们作为贡献者和维护者承诺让每个人都能参与我们的项目和社区。
|
||||
|
||||
### 我们的标准
|
||||
|
||||
积极行为包括:
|
||||
|
||||
- 使用友好和包容的语言
|
||||
- 尊重不同的观点和经验
|
||||
- 优雅地接受建设性批评
|
||||
- 关注对社区最有利的事情
|
||||
- 对其他社区成员表示同理心
|
||||
|
||||
不可接受的行为包括:
|
||||
|
||||
- 使用性化的语言或图像
|
||||
- 人身攻击或政治攻击
|
||||
- 公开或私下骚扰
|
||||
- 未经明确许可发布他人的私人信息
|
||||
- 其他在专业环境中可能被认为不当的行为
|
||||
|
||||
## 🚀 如何贡献
|
||||
|
||||
### 1. 准备工作
|
||||
|
||||
```bash
|
||||
# Fork 项目到您的GitHub账户
|
||||
# 克隆您的fork
|
||||
git clone https://github.com/your-username/agriculture-management.git
|
||||
|
||||
# 添加上游仓库
|
||||
git remote add upstream https://github.com/original-owner/agriculture-management.git
|
||||
|
||||
# 安装依赖
|
||||
cd agriculture-management
|
||||
npm install
|
||||
|
||||
# 启动开发环境
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 2. 选择贡献方式
|
||||
|
||||
- 🐛 **报告Bug**: 发现问题并创建详细的问题报告
|
||||
- 💡 **功能建议**: 提出新功能或改进建议
|
||||
- 📝 **文档改进**: 完善项目文档
|
||||
- 💻 **代码贡献**: 修复bug或实现新功能
|
||||
|
||||
## 🔄 开发流程
|
||||
|
||||
### 1. 创建分支
|
||||
|
||||
```bash
|
||||
# 确保master分支是最新的
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
|
||||
# 创建功能分支
|
||||
git checkout -b feature/your-feature-name
|
||||
# 或
|
||||
git checkout -b fix/your-bug-fix
|
||||
```
|
||||
|
||||
### 2. 开发过程
|
||||
|
||||
```bash
|
||||
# 启用开发工具(可选)
|
||||
npm run scripts:enable
|
||||
|
||||
# 进行开发
|
||||
# ...
|
||||
|
||||
# 运行测试
|
||||
npm run test
|
||||
|
||||
# 代码检查
|
||||
npm run lint
|
||||
|
||||
# 类型检查
|
||||
npm run type-check
|
||||
|
||||
# 格式化代码
|
||||
npm run format
|
||||
```
|
||||
|
||||
### 3. 提交更改
|
||||
|
||||
```bash
|
||||
# 添加更改
|
||||
git add .
|
||||
|
||||
# 提交(遵循提交规范)
|
||||
git commit -m "feat(component): add new machinery status component"
|
||||
|
||||
# 推送到您的fork
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
|
||||
### 4. 创建Pull Request
|
||||
|
||||
1. 访问GitHub上的fork页面
|
||||
2. 点击"New Pull Request"
|
||||
3. 选择正确的分支
|
||||
4. 填写PR模板
|
||||
5. 提交Pull Request
|
||||
|
||||
## 📏 代码规范
|
||||
|
||||
### TypeScript规范
|
||||
|
||||
```typescript
|
||||
// ✅ 好的示例
|
||||
interface MachineryData {
|
||||
id: string
|
||||
name: string
|
||||
status: MachineryStatus
|
||||
lastMaintenance?: Date
|
||||
}
|
||||
|
||||
const getMachineryStatus = async (id: string): Promise<MachineryData> => {
|
||||
const response = await fetch(`/api/machinery/${id}`)
|
||||
return response.json()
|
||||
}
|
||||
|
||||
// ❌ 避免的写法
|
||||
const getData = (id) => {
|
||||
return fetch('/api/machinery/' + id).then(r => r.json())
|
||||
}
|
||||
```
|
||||
|
||||
### React组件规范
|
||||
|
||||
```typescript
|
||||
// ✅ 函数式组件 + TypeScript
|
||||
interface MachineryCardProps {
|
||||
machinery: MachineryData
|
||||
onEdit?: (machinery: MachineryData) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const MachineryCard: React.FC<MachineryCardProps> = ({
|
||||
machinery,
|
||||
onEdit,
|
||||
className
|
||||
}) => {
|
||||
const handleEdit = useCallback(() => {
|
||||
onEdit?.(machinery)
|
||||
}, [machinery, onEdit])
|
||||
|
||||
return (
|
||||
<div className={cn('machinery-card', className)}>
|
||||
{/* 组件内容 */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 样式规范
|
||||
|
||||
```typescript
|
||||
// ✅ 使用Tailwind CSS + cn工具
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Button = ({ variant = 'primary', className, ...props }) => (
|
||||
<button
|
||||
className={cn(
|
||||
'px-4 py-2 rounded-md font-medium transition-colors',
|
||||
{
|
||||
'bg-blue-600 text-white': variant === 'primary',
|
||||
'bg-gray-200 text-gray-900': variant === 'secondary'
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
## 📝 提交规范
|
||||
|
||||
### 提交格式
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### 提交类型
|
||||
|
||||
- `feat`: 新功能
|
||||
- `fix`: Bug修复
|
||||
- `docs`: 文档更新
|
||||
- `style`: 代码格式化(不影响功能)
|
||||
- `refactor`: 代码重构
|
||||
- `perf`: 性能优化
|
||||
- `test`: 添加或修改测试
|
||||
- `chore`: 构建过程或辅助工具的变动
|
||||
|
||||
### 示例
|
||||
|
||||
```bash
|
||||
# 新功能
|
||||
git commit -m "feat(machinery): add real-time monitoring dashboard"
|
||||
|
||||
# Bug修复
|
||||
git commit -m "fix(auth): resolve login validation error for special characters"
|
||||
|
||||
# 文档更新
|
||||
git commit -m "docs(readme): update installation instructions for Windows"
|
||||
|
||||
# 性能优化
|
||||
git commit -m "perf(machinery): optimize data loading with virtual scrolling"
|
||||
```
|
||||
|
||||
## 🐛 问题报告
|
||||
|
||||
### 报告Bug
|
||||
|
||||
使用以下模板创建Bug报告:
|
||||
|
||||
```markdown
|
||||
## Bug描述
|
||||
简洁明了地描述Bug
|
||||
|
||||
## 复现步骤
|
||||
1. 进入 '...'
|
||||
2. 点击 '....'
|
||||
3. 滚动到 '....'
|
||||
4. 看到错误
|
||||
|
||||
## 期望行为
|
||||
描述您期望发生的行为
|
||||
|
||||
## 实际行为
|
||||
描述实际发生的行为
|
||||
|
||||
## 截图
|
||||
如果适用,添加截图来帮助解释问题
|
||||
|
||||
## 环境信息
|
||||
- 操作系统: [例如 iOS]
|
||||
- 浏览器: [例如 chrome, safari]
|
||||
- 版本: [例如 22]
|
||||
|
||||
## 附加信息
|
||||
添加任何其他关于问题的信息
|
||||
```
|
||||
|
||||
### 安全漏洞
|
||||
|
||||
如果您发现安全漏洞,请不要公开报告。请发送邮件至:security@example.com
|
||||
|
||||
## 💡 功能请求
|
||||
|
||||
### 请求新功能
|
||||
|
||||
```markdown
|
||||
## 功能描述
|
||||
简洁明了地描述您想要的功能
|
||||
|
||||
## 问题背景
|
||||
描述这个功能要解决的问题
|
||||
|
||||
## 解决方案
|
||||
描述您希望的解决方案
|
||||
|
||||
## 替代方案
|
||||
描述您考虑过的其他解决方案
|
||||
|
||||
## 附加信息
|
||||
添加任何其他关于功能请求的信息
|
||||
```
|
||||
|
||||
## 👀 代码审查
|
||||
|
||||
### 审查者指南
|
||||
|
||||
当审查代码时,请关注:
|
||||
|
||||
1. **功能正确性**: 代码是否按预期工作
|
||||
2. **代码质量**: 是否遵循项目规范
|
||||
3. **性能影响**: 是否有性能问题
|
||||
4. **安全性**: 是否存在安全隐患
|
||||
5. **测试覆盖**: 是否有足够的测试
|
||||
6. **文档**: 是否需要更新文档
|
||||
|
||||
### 审查评论规范
|
||||
|
||||
```markdown
|
||||
# ✅ 好的评论
|
||||
"建议将这个函数提取为自定义Hook,以提高复用性"
|
||||
"这个变量名不够清晰,建议改为更具描述性的名称"
|
||||
"考虑添加错误处理逻辑"
|
||||
|
||||
# ❌ 避免的评论
|
||||
"这段代码不好"(过于模糊)
|
||||
"重写这个"(没有具体建议)
|
||||
```
|
||||
|
||||
### 被审查者指南
|
||||
|
||||
- 对建设性反馈持开放态度
|
||||
- 解释您的技术决策
|
||||
- 感谢审查者的时间
|
||||
- 及时响应评论
|
||||
|
||||
## 🏷️ 发布流程
|
||||
|
||||
### 版本号规范
|
||||
|
||||
遵循 [语义化版本](https://semver.org/lang/zh-CN/):
|
||||
|
||||
- `MAJOR.MINOR.PATCH`
|
||||
- `MAJOR`: 不兼容的API修改
|
||||
- `MINOR`: 向下兼容的功能性新增
|
||||
- `PATCH`: 向下兼容的问题修正
|
||||
|
||||
### 发布检查清单
|
||||
|
||||
发布前确认:
|
||||
|
||||
- [ ] 所有测试通过
|
||||
- [ ] 代码审查完成
|
||||
- [ ] 文档已更新
|
||||
- [ ] CHANGELOG已更新
|
||||
- [ ] 版本号已更新
|
||||
- [ ] 性能测试通过
|
||||
|
||||
## 🏆 贡献者认可
|
||||
|
||||
### 贡献者类型
|
||||
|
||||
- 💻 **代码贡献**: 提交代码
|
||||
- 🐛 **Bug报告**: 发现并报告问题
|
||||
- 💡 **功能建议**: 提出新功能想法
|
||||
- 📝 **文档改进**: 完善项目文档
|
||||
- 🎨 **设计贡献**: UI/UX设计改进
|
||||
- 🌐 **翻译贡献**: 多语言支持
|
||||
|
||||
### 认可方式
|
||||
|
||||
- 在README中添加贡献者列表
|
||||
- 在发布说明中感谢贡献者
|
||||
- 颁发贡献者徽章
|
||||
- 邀请加入核心团队
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
- **项目维护者**: maintainer@example.com
|
||||
- **技术讨论**: GitHub Discussions
|
||||
- **Bug报告**: GitHub Issues
|
||||
- **安全问题**: security@example.com
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
通过贡献代码,您同意您的贡献将在与项目相同的 [MIT License](LICENSE) 下获得许可。
|
||||
|
||||
---
|
||||
|
||||
感谢您的贡献!🎉
|
||||
644
DEVELOPMENT.md
644
DEVELOPMENT.md
@@ -1,644 +0,0 @@
|
||||
# 开发指南
|
||||
|
||||
本文档为智慧农业生产管理系统的开发指南,帮助开发者快速了解项目结构、开发规范和最佳实践。
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [环境准备](#环境准备)
|
||||
- [项目结构](#项目结构)
|
||||
- [开发规范](#开发规范)
|
||||
- [组件开发](#组件开发)
|
||||
- [状态管理](#状态管理)
|
||||
- [样式指南](#样式指南)
|
||||
- [API集成](#api集成)
|
||||
- [测试指南](#测试指南)
|
||||
- [调试技巧](#调试技巧)
|
||||
- [常见问题](#常见问题)
|
||||
|
||||
## 🛠️ 环境准备
|
||||
|
||||
### 必需软件
|
||||
|
||||
- **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`,安装以下扩展获得最佳开发体验:
|
||||
|
||||
```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规范
|
||||
|
||||
```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组件规范
|
||||
|
||||
```typescript
|
||||
// ✅ 函数式组件 + 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](https://conventionalcommits.org/) 规范:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
#### 提交类型
|
||||
|
||||
- `feat`: 新功能
|
||||
- `fix`: 修复bug
|
||||
- `docs`: 文档更新
|
||||
- `style`: 代码格式化
|
||||
- `refactor`: 重构代码
|
||||
- `test`: 测试相关
|
||||
- `chore`: 构建工具、依赖更新
|
||||
|
||||
#### 示例
|
||||
|
||||
```bash
|
||||
feat(machinery): add machinery status monitoring
|
||||
fix(auth): resolve login validation issue
|
||||
docs(readme): update installation guide
|
||||
```
|
||||
|
||||
## 🧩 组件开发
|
||||
|
||||
### 组件结构模板
|
||||
|
||||
```typescript
|
||||
// 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>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 组件样式规范
|
||||
|
||||
```css
|
||||
/* 优先使用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组件使用
|
||||
|
||||
```typescript
|
||||
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使用
|
||||
|
||||
```typescript
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: ''
|
||||
})
|
||||
|
||||
// 更新对象状态
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
### useReducer使用
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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最佳实践
|
||||
|
||||
```typescript
|
||||
// ✅ 使用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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 响应式设计
|
||||
|
||||
```typescript
|
||||
// 移动优先的响应式设计
|
||||
<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>
|
||||
```
|
||||
|
||||
### 深色模式支持
|
||||
|
||||
```typescript
|
||||
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配置
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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 }
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 测试指南
|
||||
|
||||
### 单元测试示例
|
||||
|
||||
```typescript
|
||||
// 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测试
|
||||
|
||||
```typescript
|
||||
// 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 浏览器扩展:
|
||||
|
||||
```bash
|
||||
# 检查组件状态
|
||||
# 查看组件层次结构
|
||||
# 性能分析
|
||||
```
|
||||
|
||||
### VSCode调试配置
|
||||
|
||||
```json
|
||||
// .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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 常用调试代码
|
||||
|
||||
```typescript
|
||||
// 开发环境调试
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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: 项目支持多语言配置:
|
||||
|
||||
```typescript
|
||||
// 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: 使用项目提供的脚本:
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
npm run scripts:setup
|
||||
|
||||
# 启用工具
|
||||
npm run scripts:enable
|
||||
|
||||
# 禁用工具
|
||||
npm run scripts:disable
|
||||
```
|
||||
|
||||
## 📚 更多资源
|
||||
|
||||
- [React 官方文档](https://react.dev/)
|
||||
- [TypeScript 手册](https://www.typescriptlang.org/docs/)
|
||||
- [Tailwind CSS 文档](https://tailwindcss.com/docs)
|
||||
- [shadcn/ui 组件库](https://ui.shadcn.com/)
|
||||
- [Vite 构建工具](https://vitejs.dev/)
|
||||
|
||||
---
|
||||
|
||||
💡 **提示**: 如果遇到问题,请先查看常见问题部分,或联系项目维护者获取帮助。
|
||||
326
README.md
326
README.md
@@ -1,322 +1,36 @@
|
||||
# 智慧农业生产管理系统
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
基于 React 18 + Vite 6 + TypeScript + shadcn/ui 构建的现代化农业管理平台。
|
||||
## Getting Started
|
||||
|
||||
## 🌟 项目特色
|
||||
|
||||
- 🚀 **现代化技术栈**: React 18 + Vite 6 + TypeScript
|
||||
- 🎨 **优美UI设计**: 基于 shadcn/ui + Tailwind CSS
|
||||
- 📱 **响应式布局**: 支持桌面端和移动端
|
||||
- 🌾 **农业专业化**: 针对农业生产场景定制
|
||||
- 🔧 **开发工具**: ESLint + Prettier + Husky(可选启用)
|
||||
- 📊 **数据可视化**: 集成 Recharts 图表库
|
||||
|
||||
## 🏗️ 技术架构
|
||||
|
||||
### 核心技术栈
|
||||
|
||||
- **前端框架**: React 18.3.1
|
||||
- **构建工具**: Vite 6.3.5
|
||||
- **类型系统**: TypeScript 5.6.2
|
||||
- **UI组件库**: shadcn/ui + Radix UI
|
||||
- **样式方案**: Tailwind CSS 3.4.13
|
||||
- **状态管理**: React Context + Hooks
|
||||
- **路由系统**: 自定义基于路径的路由
|
||||
- **图表库**: Recharts 2.15.2
|
||||
- **图标库**: Lucide React 0.487.0
|
||||
- **日期处理**: date-fns 4.1.0
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
crop-x/
|
||||
├── 📄 public/ # 静态资源
|
||||
├── 📄 src/
|
||||
│ ├── 📄 components/ # 组件目录
|
||||
│ │ ├── ui/ # shadcn/ui基础组件
|
||||
│ │ ├── common/ # 通用业务组件
|
||||
│ │ └── layouts/ # 布局组件
|
||||
│ ├── 📄 pages/ # 页面组件
|
||||
│ │ ├── machinery/ # 农机管理
|
||||
│ │ ├── field/ # 地块管理
|
||||
│ │ ├── operation/ # 农事管理
|
||||
│ │ ├── asset/ # 资产管理
|
||||
│ │ ├── ai-model/ # AI模型
|
||||
│ │ ├── irrigation/ # 灌溉控制
|
||||
│ │ └── config/ # 配置管理
|
||||
│ ├── 📄 hooks/ # 自定义Hooks
|
||||
│ ├── 📄 lib/ # 工具库
|
||||
│ ├── 📄 config/ # 配置文件
|
||||
│ ├── 📄 types/ # 类型定义
|
||||
│ ├── 📄 utils/ # 工具函数
|
||||
│ ├── 📄 assets/ # 资源文件
|
||||
│ ├── 📄 styles/ # 样式文件
|
||||
│ └── 📄 App.tsx # 主应用组件
|
||||
├── 📄 docs/ # 文档目录
|
||||
├── 📄 scripts/ # 构建脚本
|
||||
└── 📄 .vscode/ # VSCode配置
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js >= 18.0.0
|
||||
- npm >= 8.0.0
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 开发环境
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
项目将在 http://localhost:3000 启动(如果端口被占用会自动切换)
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
**✅ React应用验证**:
|
||||
- 启动时间: < 2秒(实测2012ms)
|
||||
- 热重载: 正常工作
|
||||
- 主题切换: 支持深色/浅色模式
|
||||
- 响应式设计: 支持桌面端和移动端
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
### 构建生产版本
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
## Learn More
|
||||
|
||||
### 预览生产版本
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
## 🛠️ 开发工具
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
项目配置了可选的开发工具链,默认禁用,可通过以下命令启用:
|
||||
## Deploy on Vercel
|
||||
|
||||
### 查看开发工具状态
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
```bash
|
||||
npm run scripts:setup
|
||||
```
|
||||
|
||||
### 启用所有开发工具
|
||||
|
||||
```bash
|
||||
npm run scripts:enable
|
||||
```
|
||||
|
||||
### 禁用所有开发工具
|
||||
|
||||
```bash
|
||||
npm run scripts:disable
|
||||
```
|
||||
|
||||
### 代码检查
|
||||
|
||||
```bash
|
||||
# ESLint检查
|
||||
npm run lint
|
||||
|
||||
# ESLint自动修复
|
||||
npm run lint:fix
|
||||
```
|
||||
|
||||
### 代码格式化
|
||||
|
||||
```bash
|
||||
# Prettier格式化
|
||||
npm run format
|
||||
|
||||
# 检查格式
|
||||
npm run format:check
|
||||
```
|
||||
|
||||
### 类型检查
|
||||
|
||||
```bash
|
||||
npm run type-check
|
||||
```
|
||||
|
||||
## 📁 功能模块
|
||||
|
||||
### 🚜 农机管理 (Machinery)
|
||||
- 农机档案管理
|
||||
- 驾驶员管理
|
||||
- 负载管理
|
||||
- 运行监控
|
||||
- 故障管理
|
||||
- 作业管理
|
||||
- 数据分析
|
||||
- 调度管理
|
||||
- 安全管理
|
||||
|
||||
### 🌾 地块管理 (Field)
|
||||
- 地块档案
|
||||
- 土壤信息
|
||||
- 作物管理
|
||||
- 种植计划
|
||||
|
||||
### 📋 农事管理 (Operation)
|
||||
- 作业计划
|
||||
- 进度跟踪
|
||||
- 成本核算
|
||||
- 产量预测
|
||||
|
||||
### 💰 资产管理 (Asset)
|
||||
- 设备资产
|
||||
- 库存管理
|
||||
- 采购管理
|
||||
- 维护记录
|
||||
|
||||
### 🤖 AI模型 (AI Model)
|
||||
- 智能预测
|
||||
- 图像识别
|
||||
- 数据分析
|
||||
- 决策支持
|
||||
|
||||
### 💧 灌溉控制 (Irrigation)
|
||||
- 智能灌溉
|
||||
- 水资源管理
|
||||
- 设备控制
|
||||
- 用水统计
|
||||
|
||||
### ⚙️ 配置管理 (Config)
|
||||
- 系统配置
|
||||
- 用户管理
|
||||
- 权限设置
|
||||
- 数据字典
|
||||
|
||||
## 🎨 设计系统
|
||||
|
||||
### 颜色系统
|
||||
|
||||
- **主色调**: 农业绿色 (#16a34a)
|
||||
- **辅助色**: 技术蓝色 (#3b82f6)
|
||||
- **状态色**: 运行(绿)、空闲(灰)、维护(黄)、故障(红)、离线(深灰)
|
||||
|
||||
### 组件规范
|
||||
|
||||
- 基于 shadcn/ui 组件库
|
||||
- 遵循 Material Design 设计规范
|
||||
- 支持深色/浅色主题切换
|
||||
- 完整的无障碍支持
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### 环境变量
|
||||
|
||||
```bash
|
||||
# API地址
|
||||
VITE_API_BASE_URL=http://localhost:8080/api
|
||||
|
||||
# 应用标题
|
||||
VITE_APP_TITLE=智慧农业生产管理系统
|
||||
|
||||
# 开发模式
|
||||
VITE_DEV_MODE=true
|
||||
```
|
||||
|
||||
### 开发工具配置
|
||||
|
||||
通过 `.dev-tools-config.json` 文件控制开发工具的启用状态:
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"eslint": { "enabled": false },
|
||||
"prettier": { "enabled": false },
|
||||
"husky": { "enabled": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📖 开发指南
|
||||
|
||||
### 添加新页面
|
||||
|
||||
1. 在对应模块下创建页面组件
|
||||
2. 在路由配置中添加路由规则
|
||||
3. 在菜单配置中添加菜单项
|
||||
|
||||
### 添加新组件
|
||||
|
||||
1. 在 `src/components/ui/` 下创建UI组件
|
||||
2. 遵循 shadcn/ui 设计规范
|
||||
3. 添加 TypeScript 类型定义
|
||||
4. 编写组件文档和使用示例
|
||||
|
||||
### 样式规范
|
||||
|
||||
- 使用 Tailwind CSS 类名
|
||||
- 遵循 BEM 命名规范
|
||||
- 响应式设计优先
|
||||
- 支持深色模式
|
||||
|
||||
## 🧪 测试
|
||||
|
||||
```bash
|
||||
# 运行测试
|
||||
npm run test
|
||||
|
||||
# 运行测试覆盖率
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
## 📤 部署
|
||||
|
||||
### Docker部署
|
||||
|
||||
```bash
|
||||
# 构建镜像
|
||||
docker build -t agriculture-management .
|
||||
|
||||
# 运行容器
|
||||
docker run -p 3000:3000 agriculture-management
|
||||
```
|
||||
|
||||
### 传统部署
|
||||
|
||||
```bash
|
||||
# 构建项目
|
||||
npm run build
|
||||
|
||||
# 部署build目录到Web服务器
|
||||
```
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### v1.0.0 (2024-01-20)
|
||||
- ✨ 初始版本发布
|
||||
- 🚀 完成基础架构搭建
|
||||
- 🎨 集成shadcn/ui组件库
|
||||
- 📱 实现响应式设计
|
||||
- 🌾 添加农业专业化功能
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
1. Fork 项目
|
||||
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 创建 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
||||
|
||||
## 📞 联系我们
|
||||
|
||||
- 项目地址: [GitHub Repository](https://github.com/your-username/agriculture-management)
|
||||
- 问题反馈: [Issues](https://github.com/your-username/agriculture-management/issues)
|
||||
- 邮箱: your-email@example.com
|
||||
|
||||
---
|
||||
|
||||
⭐ 如果这个项目对你有帮助,请给我们一个星标!
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
||||
26
env/.env.dev
vendored
26
env/.env.dev
vendored
@@ -1,26 +0,0 @@
|
||||
# 开发环境配置
|
||||
NODE_ENV=development
|
||||
|
||||
# 前端域名
|
||||
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.dev.maimaiag.com
|
||||
|
||||
# 后端 API 地址
|
||||
BACKEND_BASE_URL=https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com/
|
||||
|
||||
# OpenAPI 生成配置
|
||||
API_BASE_URL=https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com
|
||||
|
||||
# API 版本
|
||||
API_VERSION=v1
|
||||
|
||||
# 调试模式
|
||||
DEBUG=true
|
||||
|
||||
# 是否开启 Mock 数据
|
||||
USE_MOCK=false
|
||||
|
||||
# 应用名称
|
||||
APP_NAME=智慧农业生产管理系统
|
||||
|
||||
# 环境描述
|
||||
ENV_DESCRIPTION=开发环境
|
||||
43
env/.env.prod
vendored
43
env/.env.prod
vendored
@@ -1,43 +0,0 @@
|
||||
# 生产环境配置
|
||||
NODE_ENV=production
|
||||
|
||||
# 前端域名
|
||||
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.prod.maimaiag.com
|
||||
|
||||
# 后端 API 地址
|
||||
BACKEND_BASE_URL=https://cavin-smart-crop-backend-app.prod.maimaiag.com
|
||||
|
||||
# API 版本
|
||||
API_VERSION=v1
|
||||
|
||||
# 调试模式
|
||||
DEBUG=false
|
||||
|
||||
# 是否开启 Mock 数据
|
||||
USE_MOCK=false
|
||||
|
||||
# 应用名称
|
||||
APP_NAME=智慧农业生产管理系统
|
||||
|
||||
# 环境描述
|
||||
ENV_DESCRIPTION=生产环境
|
||||
|
||||
# 生产环境特有配置
|
||||
API_TIMEOUT=30000
|
||||
ENABLE_ERROR_LOGGING=true
|
||||
ENABLE_PERFORMANCE_MONITORING=true
|
||||
ENABLE_USER_BEHAVIOR_TRACKING=true
|
||||
ENABLE_ANALYTICS=true
|
||||
|
||||
# 安全配置
|
||||
SENTRY_DSN=https://your-sentry-dsn.prod@sentry.io/project-id
|
||||
CDN_BASE_URL=https://cdn.cavin-smart-crop.com
|
||||
|
||||
# 功能开关
|
||||
ENABLE_MAINTENANCE_MODE=false
|
||||
ENABLE_NEW_FEATURES=true
|
||||
ENABLE_BETA_FEATURES=false
|
||||
|
||||
# 缓存配置
|
||||
CACHE_TTL=3600
|
||||
ENABLE_SERVICE_WORKER=true
|
||||
28
env/.env.test
vendored
28
env/.env.test
vendored
@@ -1,28 +0,0 @@
|
||||
# 测试环境配置
|
||||
NODE_ENV=test
|
||||
|
||||
# 前端域名
|
||||
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.test.maimaiag.com
|
||||
|
||||
# 后端 API 地址
|
||||
BACKEND_BASE_URL=http://pengcode.tech:8080
|
||||
|
||||
# API 版本
|
||||
API_VERSION=v1
|
||||
|
||||
# 调试模式
|
||||
DEBUG=true
|
||||
|
||||
# 是否开启 Mock 数据
|
||||
USE_MOCK=false
|
||||
|
||||
# 应用名称
|
||||
APP_NAME=智慧农业生产管理系统
|
||||
|
||||
# 环境描述
|
||||
ENV_DESCRIPTION=测试环境
|
||||
|
||||
# 其他测试环境特有配置
|
||||
API_TIMEOUT=15000
|
||||
ENABLE_ERROR_LOGGING=true
|
||||
ENABLE_PERFORMANCE_MONITORING=true
|
||||
30
env/.env.uat
vendored
30
env/.env.uat
vendored
@@ -1,30 +0,0 @@
|
||||
# UAT (用户验收测试) 环境配置
|
||||
NODE_ENV=production
|
||||
|
||||
# 前端域名
|
||||
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.uat.maimaiag.com
|
||||
|
||||
# 后端 API 地址
|
||||
BACKEND_BASE_URL=https://cavin-smart-crop-backend-app.uat.maimaiag.com
|
||||
|
||||
# API 版本
|
||||
API_VERSION=v1
|
||||
|
||||
# 调试模式
|
||||
DEBUG=false
|
||||
|
||||
# 是否开启 Mock 数据
|
||||
USE_MOCK=false
|
||||
|
||||
# 应用名称
|
||||
APP_NAME=智慧农业生产管理系统
|
||||
|
||||
# 环境描述
|
||||
ENV_DESCRIPTION=UAT 环境
|
||||
|
||||
# UAT 特有配置
|
||||
API_TIMEOUT=20000
|
||||
ENABLE_ERROR_LOGGING=true
|
||||
ENABLE_PERFORMANCE_MONITORING=true
|
||||
ENABLE_USER_BEHAVIOR_TRACKING=true
|
||||
SENTRY_DSN=https://your-sentry-dsn.uat@sentry.io/project-id
|
||||
@@ -1,25 +1,18 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
ignores: [
|
||||
"node_modules/**",
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
],
|
||||
},
|
||||
];
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
13
index.html
13
index.html
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>智慧农业生产管理系统</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
6
lib/utils.ts
Normal file
6
lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: true, // TODO: 暂时完全禁用TypeScript类型检查
|
||||
},
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true, // TODO: 暂时禁用eslint校验错误
|
||||
},
|
||||
transpilePackages: ['lucide-react'],
|
||||
output: 'standalone',
|
||||
// 修复CSS构建问题
|
||||
experimental: {
|
||||
// forceSwcTransforms: true,
|
||||
},
|
||||
// 新的 Turbopack 配置
|
||||
turbopack: {
|
||||
rules: {
|
||||
'*.svg': {
|
||||
loaders: ['@svgr/webpack'],
|
||||
as: '*.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
// 解决工作区根目录问题
|
||||
outputFileTracingRoot: process.cwd(),
|
||||
// 添加代理配置解决CORS问题
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com/api/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
36
next.config.ts
Normal file
36
next.config.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// next.config.ts
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// 构建时报 TS 校验(可按需开启/关闭)
|
||||
typescript: {
|
||||
ignoreBuildErrors: false,
|
||||
},
|
||||
|
||||
// 将 ESM 包转译(如有 Tree-shaking/TS 产物需要)
|
||||
transpilePackages: ['lucide-react'],
|
||||
|
||||
// 便于部署到容器/Serverless
|
||||
output: 'standalone',
|
||||
|
||||
// 若有其它实验性开关可放这里(保持空对象即可)
|
||||
experimental: {
|
||||
// 例如:typedRoutes: true
|
||||
},
|
||||
|
||||
// 解决工作区根目录问题(通常保持默认即可;你这里明确指定也可)
|
||||
outputFileTracingRoot: process.cwd(),
|
||||
|
||||
// 反向代理(避免本地 CORS)
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination:
|
||||
'https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com/api/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
28131
package-lock.json
generated
28131
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
167
package.json
167
package.json
@@ -1,102 +1,69 @@
|
||||
{
|
||||
"name": "智慧农业生产管理系统",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint:fix": "eslint . --ext ts,tsx --fix",
|
||||
"type-check": "tsc --noEmit",
|
||||
"api:generate": "node scripts/generate-api.cjs",
|
||||
"deploy": "node scripts/deploy.js",
|
||||
"build:dev": "node scripts/build.cjs dev",
|
||||
"build:test": "node scripts/build.cjs test",
|
||||
"build:uat": "node scripts/build.cjs uat",
|
||||
"build:prod": "node scripts/build.cjs prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-menubar": "^1.1.16",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-progress": "^1.1.7",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toggle": "^1.1.10",
|
||||
"@radix-ui/react-toggle-group": "^1.1.11",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"axios": "^1.12.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^16.0.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"openapi-fetch": "^0.15.0",
|
||||
"qrcode": "*",
|
||||
"react": "^19.2.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-hook-form": "^7.65.0",
|
||||
"react-resizable-panels": "^2.1.9",
|
||||
"recharts": "^2.15.4",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^4.1.12",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hey-api/client-fetch": "^0.13.1",
|
||||
"@hey-api/openapi-ts": "^0.86.6",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.3.11",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
||||
"@typescript-eslint/parser": "^8.7.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-config-next": "15.5.6",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.12",
|
||||
"husky": "^9.1.6",
|
||||
"install": "^0.13.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"node-fetch": "^3.3.2",
|
||||
"npm": "^11.6.2",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.3",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.4.0"
|
||||
}
|
||||
"name": "crop-x-next",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint",
|
||||
"deploy": "node scripts/deploy.js",
|
||||
"api:generate": "node scripts/generate-api.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "^0.87.1",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.8",
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-menubar": "^1.1.16",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-toggle": "^1.1.10",
|
||||
"@radix-ui/react-toggle-group": "^1.1.11",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"axios": "^1.13.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"lucide-react": "^0.553.0",
|
||||
"next": "16.0.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"npx": "^10.2.2",
|
||||
"openapi-fetch": "^0.15.0",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.1",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
7
postcss.config.mjs
Normal file
7
postcss.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
1
public/file.svg
Normal file
1
public/file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
public/globe.svg
Normal file
1
public/globe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
public/next.svg
Normal file
1
public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/vercel.svg
Normal file
1
public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
1
public/window.svg
Normal file
1
public/window.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
@@ -6,46 +6,15 @@
|
||||
* 2. 环境配置通过 openapi-ts.config.ts 处理
|
||||
*/
|
||||
|
||||
// 加载环境变量
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 从环境配置文件中读取 API_BASE_URL
|
||||
function getApiBaseUrl() {
|
||||
try {
|
||||
// 首先尝试从 env/.env.dev 文件读取
|
||||
const envDevPath = path.join(process.cwd(), 'env', '.env.dev');
|
||||
if (fs.existsSync(envDevPath)) {
|
||||
const envContent = fs.readFileSync(envDevPath, 'utf8');
|
||||
const apiBaseUrlMatch = envContent.match(/API_BASE_URL=([^\r\n]+)/);
|
||||
|
||||
if (apiBaseUrlMatch && apiBaseUrlMatch[1]) {
|
||||
return apiBaseUrlMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果上面的文件不存在,尝试从 TypeScript 环境配置文件读取
|
||||
const envConfigPath = path.join(process.cwd(), 'src', 'env', 'index.ts');
|
||||
if (fs.existsSync(envConfigPath)) {
|
||||
const envContent = fs.readFileSync(envConfigPath, 'utf8');
|
||||
const devConfigMatch = envContent.match(/dev:\s*\{[\s\S]*?BACKEND_BASE_URL:\s*['"`]([^'"`]+)['"`]/);
|
||||
|
||||
if (devConfigMatch && devConfigMatch[1]) {
|
||||
return devConfigMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('无法找到 API_BASE_URL 或 BACKEND_BASE_URL 配置');
|
||||
} catch (error) {
|
||||
console.log(`读取环境配置失败: ${error.message}`);
|
||||
return 'http://localhost:8080'; // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 API 基础 URL
|
||||
const API_BASE_URL = getApiBaseUrl();
|
||||
|
||||
// 设置环境变量供后续使用
|
||||
process.env.API_BASE_URL = API_BASE_URL;
|
||||
|
||||
// ANSI 颜色代码
|
||||
const colors = {
|
||||
@@ -79,11 +48,10 @@ function logWarning(message) {
|
||||
function logInfo(message) {
|
||||
log(`ℹ ${message}`, 'blue');
|
||||
}
|
||||
|
||||
const API_BASE_URL = process.env.API_BASE_URL
|
||||
// 显示环境配置信息
|
||||
logInfo(`当前环境: ${process.env.NODE_ENV || 'development'}`);
|
||||
logInfo(`API 服务器: ${API_BASE_URL}`);
|
||||
logInfo(`已从 env/.env.dev 读取 API_BASE_URL 配置`);
|
||||
|
||||
/**
|
||||
* 检查自定义文件是否存在
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -234,7 +234,7 @@ export class NetworkLogService {
|
||||
]
|
||||
|
||||
// 应用筛选器
|
||||
let filteredLogs = mockLogs.filter(log => {
|
||||
const filteredLogs = mockLogs.filter(log => {
|
||||
if (params.filters?.searchKeyword) {
|
||||
const keyword = params.filters.searchKeyword.toLowerCase()
|
||||
if (!log.url.toLowerCase().includes(keyword) &&
|
||||
|
||||
@@ -266,7 +266,7 @@ export default function MenuManagementPage() {
|
||||
|
||||
const expandAll = () => {
|
||||
const getAllMenuIds = (menus: Menu[]): string[] => {
|
||||
let ids: string[] = [];
|
||||
const ids: string[] = [];
|
||||
menus.forEach(menu => {
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
ids.push(menu.id);
|
||||
|
||||
@@ -96,7 +96,7 @@ export function PaginationComponent({
|
||||
// 否则生成智能的页码显示范围
|
||||
const half = Math.floor(maxVisiblePages / 2);
|
||||
let start = Math.max(1, page - half);
|
||||
let end = Math.min(totalPages, start + maxVisiblePages - 1);
|
||||
const end = Math.min(totalPages, start + maxVisiblePages - 1);
|
||||
|
||||
// 调整开始位置,确保显示足够数量的页码
|
||||
if (end - start < maxVisiblePages - 1) {
|
||||
|
||||
289
src/env/index.ts
vendored
289
src/env/index.ts
vendored
@@ -1,289 +0,0 @@
|
||||
/**
|
||||
* 环境变量管理系统
|
||||
*
|
||||
* 这个模块提供统一的环境配置管理,支持多环境切换
|
||||
* 根据构建时的 NODE_ENV 自动加载对应的环境配置文件
|
||||
*/
|
||||
|
||||
// 环境类型定义
|
||||
export type Environment = 'dev' | 'test' | 'uat' | 'prod';
|
||||
|
||||
// 环境配置接口
|
||||
export interface EnvironmentConfig {
|
||||
// 基础配置
|
||||
NODE_ENV: string;
|
||||
FRONTEND_BASE_URL: string;
|
||||
BACKEND_BASE_URL: string;
|
||||
API_VERSION: string;
|
||||
|
||||
// 功能配置
|
||||
DEBUG: boolean;
|
||||
USE_MOCK: boolean;
|
||||
|
||||
// 应用信息
|
||||
APP_NAME: string;
|
||||
ENV_DESCRIPTION: string;
|
||||
|
||||
// 高级配置
|
||||
API_TIMEOUT?: number;
|
||||
ENABLE_ERROR_LOGGING?: boolean;
|
||||
ENABLE_PERFORMANCE_MONITORING?: boolean;
|
||||
ENABLE_USER_BEHAVIOR_TRACKING?: boolean;
|
||||
SENTRY_DSN?: string;
|
||||
CDN_BASE_URL?: string;
|
||||
|
||||
// 生产环境特有
|
||||
ENABLE_MAINTENANCE_MODE?: boolean;
|
||||
ENABLE_NEW_FEATURES?: boolean;
|
||||
ENABLE_BETA_FEATURES?: boolean;
|
||||
ENABLE_ANALYTICS?: boolean;
|
||||
CACHE_TTL?: number;
|
||||
ENABLE_SERVICE_WORKER?: boolean;
|
||||
}
|
||||
|
||||
// 环境配置映射
|
||||
const ENV_CONFIGS: Record<Environment, EnvironmentConfig> = {
|
||||
dev: {
|
||||
NODE_ENV: 'development',
|
||||
FRONTEND_BASE_URL: 'https://cavin-smart-crop-ui-app.dev.maimaiag.com',
|
||||
BACKEND_BASE_URL: 'https://cavin-smart-crop-backend-app.dev.maimaiag.com',
|
||||
API_VERSION: 'v1',
|
||||
DEBUG: true,
|
||||
USE_MOCK: false,
|
||||
APP_NAME: '智慧农业生产管理系统',
|
||||
ENV_DESCRIPTION: '开发环境',
|
||||
API_TIMEOUT: 10000,
|
||||
ENABLE_ERROR_LOGGING: true,
|
||||
ENABLE_PERFORMANCE_MONITORING: true,
|
||||
},
|
||||
|
||||
test: {
|
||||
NODE_ENV: 'test',
|
||||
FRONTEND_BASE_URL: 'https://cavin-smart-crop-ui-app.test.maimaiag.com',
|
||||
BACKEND_BASE_URL: 'https://cavin-smart-crop-backend-app.dev.maimaiag.com', // 临时使用开发环境后端
|
||||
API_VERSION: 'v1',
|
||||
DEBUG: true,
|
||||
USE_MOCK: false,
|
||||
APP_NAME: '智慧农业生产管理系统',
|
||||
ENV_DESCRIPTION: '测试环境',
|
||||
API_TIMEOUT: 15000,
|
||||
ENABLE_ERROR_LOGGING: true,
|
||||
ENABLE_PERFORMANCE_MONITORING: true,
|
||||
ENABLE_USER_BEHAVIOR_TRACKING: true,
|
||||
},
|
||||
|
||||
uat: {
|
||||
NODE_ENV: 'production',
|
||||
FRONTEND_BASE_URL: 'https://cavin-smart-crop-ui-app.uat.maimaiag.com',
|
||||
BACKEND_BASE_URL: 'https://cavin-smart-crop-backend-app.uat.maimaiag.com',
|
||||
API_VERSION: 'v1',
|
||||
DEBUG: false,
|
||||
USE_MOCK: false,
|
||||
APP_NAME: '智慧农业生产管理系统',
|
||||
ENV_DESCRIPTION: 'UAT 环境',
|
||||
API_TIMEOUT: 20000,
|
||||
ENABLE_ERROR_LOGGING: true,
|
||||
ENABLE_PERFORMANCE_MONITORING: true,
|
||||
ENABLE_USER_BEHAVIOR_TRACKING: true,
|
||||
SENTRY_DSN: 'https://your-sentry-dsn.uat@sentry.io/project-id',
|
||||
},
|
||||
|
||||
prod: {
|
||||
NODE_ENV: 'production',
|
||||
FRONTEND_BASE_URL: 'https://cavin-smart-crop-ui-app.prod.maimaiag.com',
|
||||
BACKEND_BASE_URL: 'https://cavin-smart-crop-backend-app.prod.maimaiag.com',
|
||||
API_VERSION: 'v1',
|
||||
DEBUG: false,
|
||||
USE_MOCK: false,
|
||||
APP_NAME: '智慧农业生产管理系统',
|
||||
ENV_DESCRIPTION: '生产环境',
|
||||
API_TIMEOUT: 30000,
|
||||
ENABLE_ERROR_LOGGING: true,
|
||||
ENABLE_PERFORMANCE_MONITORING: true,
|
||||
ENABLE_USER_BEHAVIOR_TRACKING: true,
|
||||
ENABLE_ANALYTICS: true,
|
||||
SENTRY_DSN: 'https://your-sentry-dsn.prod@sentry.io/project-id',
|
||||
CDN_BASE_URL: 'https://cdn.cavin-smart-crop.com',
|
||||
ENABLE_MAINTENANCE_MODE: false,
|
||||
ENABLE_NEW_FEATURES: true,
|
||||
ENABLE_BETA_FEATURES: false,
|
||||
CACHE_TTL: 3600,
|
||||
ENABLE_SERVICE_WORKER: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前环境
|
||||
*
|
||||
* 优先级:
|
||||
* 1. 环境变量 NEXT_PUBLIC_ENV
|
||||
* 2. 环境变量 NODE_ENV
|
||||
* 3. 默认 'dev' 环境
|
||||
*/
|
||||
export const getEnv = (): Environment => {
|
||||
// 浏览器环境
|
||||
if (typeof window !== 'undefined') {
|
||||
const env = process.env.NEXT_PUBLIC_ENV as Environment;
|
||||
if (env && Object.keys(ENV_CONFIGS).includes(env)) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
||||
// 服务端环境
|
||||
const nodeEnv = process.env.NODE_ENV;
|
||||
if (nodeEnv === 'development') return 'dev';
|
||||
if (nodeEnv === 'test') return 'test';
|
||||
if (nodeEnv === 'production') {
|
||||
// 生产环境下需要进一步区分 UAT 和 PROD
|
||||
const env = process.env.NEXT_PUBLIC_ENV as Environment;
|
||||
return (env === 'uat' || env === 'prod') ? env : 'prod';
|
||||
}
|
||||
|
||||
return 'dev'; // 默认开发环境
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前环境配置
|
||||
*/
|
||||
export const getEnvConfig = (): EnvironmentConfig => {
|
||||
const currentEnv = getEnv();
|
||||
return ENV_CONFIGS[currentEnv];
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前环境名称
|
||||
*/
|
||||
export const getEnvironmentName = (): string => {
|
||||
const config = getEnvConfig();
|
||||
return config.ENV_DESCRIPTION;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前服务器域名(前端地址)
|
||||
*/
|
||||
export const getLocalhost = (): string => {
|
||||
const config = getEnvConfig();
|
||||
return config.FRONTEND_BASE_URL;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取后端 API 地址
|
||||
*/
|
||||
export const getBackendUrl = (): string => {
|
||||
const config = getEnvConfig();
|
||||
return config.BACKEND_BASE_URL;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取完整的 API 地址
|
||||
*/
|
||||
export const getApiUrl = (path?: string): string => {
|
||||
const backendUrl = getBackendUrl();
|
||||
const apiVersion = getEnvConfig().API_VERSION;
|
||||
const apiPath = `/api/${apiVersion}${path || ''}`;
|
||||
return `${backendUrl}${apiPath}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为开发环境
|
||||
*/
|
||||
export const isDevelopment = (): boolean => {
|
||||
return getEnv() === 'dev';
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为测试环境
|
||||
*/
|
||||
export const isTest = (): boolean => {
|
||||
return getEnv() === 'test';
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为 UAT 环境
|
||||
*/
|
||||
export const isUAT = (): boolean => {
|
||||
return getEnv() === 'uat';
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为生产环境
|
||||
*/
|
||||
export const isProduction = (): boolean => {
|
||||
return getEnv() === 'prod';
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为非开发环境
|
||||
*/
|
||||
export const isNonDevelopment = (): boolean => {
|
||||
return !isDevelopment();
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否开启调试模式
|
||||
*/
|
||||
export const isDebugMode = (): boolean => {
|
||||
return getEnvConfig().DEBUG;
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否使用 Mock 数据
|
||||
*/
|
||||
export const shouldUseMock = (): boolean => {
|
||||
return getEnvConfig().USE_MOCK;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 API 超时时间(毫秒)
|
||||
*/
|
||||
export const getApiTimeout = (): number => {
|
||||
return getEnvConfig().API_TIMEOUT || 10000;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取应用名称
|
||||
*/
|
||||
export const getAppName = (): string => {
|
||||
return getEnvConfig().APP_NAME;
|
||||
};
|
||||
|
||||
/**
|
||||
* 环境信息调试输出
|
||||
*/
|
||||
export const debugEnvInfo = (): void => {
|
||||
if (isDebugMode()) {
|
||||
const config = getEnvConfig();
|
||||
const env = getEnv();
|
||||
|
||||
console.group('🌍 环境信息');
|
||||
console.log('当前环境:', env);
|
||||
console.log('环境描述:', config.ENV_DESCRIPTION);
|
||||
console.log('前端地址:', config.FRONTEND_BASE_URL);
|
||||
console.log('后端地址:', config.BACKEND_BASE_URL);
|
||||
console.log('调试模式:', config.DEBUG);
|
||||
console.log('使用 Mock:', config.USE_MOCK);
|
||||
console.log('NODE_ENV:', process.env.NODE_ENV);
|
||||
console.log('NEXT_PUBLIC_ENV:', process.env.NEXT_PUBLIC_ENV);
|
||||
console.groupEnd();
|
||||
}
|
||||
};
|
||||
|
||||
// 默认导出主要函数
|
||||
export default {
|
||||
getEnv,
|
||||
getEnvConfig,
|
||||
getEnvironmentName,
|
||||
getLocalhost,
|
||||
getBackendUrl,
|
||||
getApiUrl,
|
||||
isDevelopment,
|
||||
isTest,
|
||||
isUAT,
|
||||
isProduction,
|
||||
isNonDevelopment,
|
||||
isDebugMode,
|
||||
shouldUseMock,
|
||||
getApiTimeout,
|
||||
getAppName,
|
||||
debugEnvInfo,
|
||||
};
|
||||
@@ -16,7 +16,6 @@ export type {
|
||||
Config,
|
||||
CreateClientConfig,
|
||||
Options,
|
||||
OptionsLegacyParser,
|
||||
RequestOptions,
|
||||
RequestResult,
|
||||
ResolvedRequestOptions,
|
||||
|
||||
@@ -194,7 +194,7 @@ type BuildUrlFn = <
|
||||
url: string;
|
||||
},
|
||||
>(
|
||||
options: Pick<TData, 'url'> & Options<TData>,
|
||||
options: TData & Options<TData>,
|
||||
) => string;
|
||||
|
||||
export type Client = CoreClient<
|
||||
@@ -238,31 +238,4 @@ export type Options<
|
||||
RequestOptions<TResponse, TResponseStyle, ThrowOnError>,
|
||||
'body' | 'path' | 'query' | 'url'
|
||||
> &
|
||||
Omit<TData, 'url'>;
|
||||
|
||||
export type OptionsLegacyParser<
|
||||
TData = unknown,
|
||||
ThrowOnError extends boolean = boolean,
|
||||
TResponseStyle extends ResponseStyle = 'fields',
|
||||
> = TData extends { body?: any }
|
||||
? TData extends { headers?: any }
|
||||
? OmitKeys<
|
||||
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
||||
'body' | 'headers' | 'url'
|
||||
> &
|
||||
TData
|
||||
: OmitKeys<
|
||||
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
||||
'body' | 'url'
|
||||
> &
|
||||
TData &
|
||||
Pick<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'headers'>
|
||||
: TData extends { headers?: any }
|
||||
? OmitKeys<
|
||||
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
||||
'headers' | 'url'
|
||||
> &
|
||||
TData &
|
||||
Pick<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'body'>
|
||||
: OmitKeys<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'url'> &
|
||||
TData;
|
||||
([TData] extends [never] ? unknown : Omit<TData, 'url'>);
|
||||
|
||||
@@ -22,6 +22,17 @@ export type Field =
|
||||
*/
|
||||
key?: string;
|
||||
map?: string;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Field name. This is the name we want the user to see and use.
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* Field mapped name. This is the name we want to use in the request.
|
||||
* If `in` is omitted, `map` aliases `key` to the transport layer.
|
||||
*/
|
||||
map: Slot;
|
||||
};
|
||||
|
||||
export interface Fields {
|
||||
@@ -41,10 +52,14 @@ const extraPrefixes = Object.entries(extraPrefixesMap);
|
||||
|
||||
type KeyMap = Map<
|
||||
string,
|
||||
{
|
||||
in: Slot;
|
||||
map?: string;
|
||||
}
|
||||
| {
|
||||
in: Slot;
|
||||
map?: string;
|
||||
}
|
||||
| {
|
||||
in?: never;
|
||||
map: Slot;
|
||||
}
|
||||
>;
|
||||
|
||||
const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
|
||||
@@ -60,6 +75,10 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
|
||||
map: config.map,
|
||||
});
|
||||
}
|
||||
} else if ('key' in config) {
|
||||
map.set(config.key, {
|
||||
map: config.map,
|
||||
});
|
||||
} else if (config.args) {
|
||||
buildKeyMap(config.args, map);
|
||||
}
|
||||
@@ -111,7 +130,9 @@ export const buildClientParams = (
|
||||
if (config.key) {
|
||||
const field = map.get(config.key)!;
|
||||
const name = field.map || config.key;
|
||||
(params[field.in] as Record<string, unknown>)[name] = arg;
|
||||
if (field.in) {
|
||||
(params[field.in] as Record<string, unknown>)[name] = arg;
|
||||
}
|
||||
} else {
|
||||
params.body = arg;
|
||||
}
|
||||
@@ -120,8 +141,12 @@ export const buildClientParams = (
|
||||
const field = map.get(key);
|
||||
|
||||
if (field) {
|
||||
const name = field.map || key;
|
||||
(params[field.in] as Record<string, unknown>)[name] = value;
|
||||
if (field.in) {
|
||||
const name = field.map || key;
|
||||
(params[field.in] as Record<string, unknown>)[name] = value;
|
||||
} else {
|
||||
params[field.map] = value;
|
||||
}
|
||||
} else {
|
||||
const extra = extraPrefixes.find(([prefix]) =>
|
||||
key.startsWith(prefix),
|
||||
@@ -132,10 +157,8 @@ export const buildClientParams = (
|
||||
(params[slot] as Record<string, unknown>)[
|
||||
key.slice(prefix.length)
|
||||
] = value;
|
||||
} else {
|
||||
for (const [slot, allowed] of Object.entries(
|
||||
config.allowExtra ?? {},
|
||||
)) {
|
||||
} else if ('allowExtra' in config && config.allowExtra) {
|
||||
for (const [slot, allowed] of Object.entries(config.allowExtra)) {
|
||||
if (allowed) {
|
||||
(params[slot as Slot] as Record<string, unknown>)[key] = value;
|
||||
break;
|
||||
|
||||
415
temp_file.tsx
415
temp_file.tsx
@@ -1,415 +0,0 @@
|
||||
/**
|
||||
* 多因子综合评价组件
|
||||
* 提供地块适宜性评价、权重配置、批量分析和作物推荐功能
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Slider } from '@/components/ui/slider';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
Leaf,
|
||||
TrendingUp,
|
||||
Award,
|
||||
AlertTriangle,
|
||||
CheckCircle2,
|
||||
Play,
|
||||
Settings,
|
||||
Download,
|
||||
Eye,
|
||||
Calculator,
|
||||
Database,
|
||||
RefreshCw,
|
||||
Zap,
|
||||
Target,
|
||||
Droplet,
|
||||
Cloud,
|
||||
Sun,
|
||||
ThermometerSun,
|
||||
BookOpen,
|
||||
Beaker,
|
||||
Info,
|
||||
BarChart3,
|
||||
Filter
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import {
|
||||
EvaluationFactor,
|
||||
SuitabilityResult,
|
||||
FactorWeight,
|
||||
BatchProgress,
|
||||
getGradeColor,
|
||||
getScoreColor,
|
||||
getSuitabilityLevelColor,
|
||||
formatDate,
|
||||
MOCK_FIELDS
|
||||
} from './multiFactorTypes';
|
||||
import {
|
||||
MultiFactorService
|
||||
} from './multiFactorService';
|
||||
import {
|
||||
matchCropsForField,
|
||||
cropKnowledgeBase
|
||||
} from './cropKnowledgeBase';
|
||||
|
||||
export function MultiFactorEvaluation() {
|
||||
const [selectedField, setSelectedField] = useState('field-1');
|
||||
const [showWeightConfig, setShowWeightConfig] = useState(false);
|
||||
const [showKnowledgeBase, setShowKnowledgeBase] = useState(false);
|
||||
const [batchProgress, setBatchProgress] = useState<BatchProgress>({
|
||||
total: 0,
|
||||
processed: 0,
|
||||
highSuitability: 0,
|
||||
mediumSuitability: 0,
|
||||
lowSuitability: 0,
|
||||
currentField: '',
|
||||
});
|
||||
const [isBatchRunning, setIsBatchRunning] = useState(false);
|
||||
const [batchAnalysisResults, setBatchAnalysisResults] = useState<SuitabilityResult[]>([]);
|
||||
|
||||
// 评价因子权重配置
|
||||
const [factorWeights, setFactorWeights] = useState<FactorWeight[]>([
|
||||
{ id: 'ph', name: 'pH值', weight: 20, unit: '' },
|
||||
{ id: 'organic', name: '有机质含量', weight: 25, unit: 'g/kg' },
|
||||
{ id: 'depth', name: '土层厚度', weight: 20, unit: 'cm' },
|
||||
{ id: 'nitrogen', name: '全氮', weight: 10, unit: 'g/kg' },
|
||||
{ id: 'phosphorus', name: '全磷', weight: 10, unit: 'g/kg' },
|
||||
{ id: 'potassium', name: '全钾', weight: 10, unit: 'g/kg' },
|
||||
{ id: 'drainage', name: '排水性', weight: 5, unit: '' },
|
||||
]);
|
||||
|
||||
// 模拟适宜性评价结果
|
||||
const [evaluationResults, setEvaluationResults] = useState<SuitabilityResult[]>(
|
||||
MultiFactorService.generateMockEvaluationData()
|
||||
);
|
||||
|
||||
// 获取当前选中的地块结果
|
||||
const currentResult =
|
||||
evaluationResults.find(r => r.fieldId === selectedField) ||
|
||||
batchAnalysisResults.find(r => r.fieldId === selectedField) ||
|
||||
evaluationResults[0];
|
||||
|
||||
// 批量分析处理函数
|
||||
const handleRunBatchAnalysis = async () => {
|
||||
const validation = MultiFactorService.validateWeights(factorWeights);
|
||||
if (!validation.isValid) {
|
||||
toast.error(`权重总和必须为100%才能进行批量分析(当前:${validation.totalWeight}%)`);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsBatchRunning(true);
|
||||
setBatchProgress({
|
||||
total: 68,
|
||||
processed: 0,
|
||||
highSuitability: 0,
|
||||
mediumSuitability: 0,
|
||||
lowSuitability: 0,
|
||||
currentField: '',
|
||||
});
|
||||
setBatchAnalysisResults([]);
|
||||
|
||||
toast.success('开始批量分析,正在读取地块数据...');
|
||||
|
||||
try {
|
||||
const results = await MultiFactorService.runBatchAnalysis(
|
||||
factorWeights,
|
||||
(progress) => {
|
||||
setBatchProgress(progress);
|
||||
}
|
||||
);
|
||||
|
||||
setBatchAnalysisResults(results);
|
||||
setIsBatchRunning(false);
|
||||
toast.success(`批量分析完成!已为${results.length}个地块生成适宜性评价结果`);
|
||||
} catch (error) {
|
||||
setIsBatchRunning(false);
|
||||
toast.error('批量分析失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateWeight = (id: string, newWeight: number) => {
|
||||
setFactorWeights(prev =>
|
||||
MultiFactorService.updateWeight(prev, id, newWeight)
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetWeights = () => {
|
||||
setFactorWeights(MultiFactorService.resetWeights());
|
||||
toast.success('权重已恢复默认值');
|
||||
};
|
||||
|
||||
const handleApplyPreset = (preset: 'grain' | 'economic' | 'default') => {
|
||||
setFactorWeights(MultiFactorService.getWeightPreset(preset));
|
||||
const presetName = preset === 'grain' ? '粮食作物' : preset === 'economic' ? '经济作物' : '默认';
|
||||
toast.success(`已应用${presetName}权重方案`);
|
||||
};
|
||||
|
||||
const totalWeight = factorWeights.reduce((sum, f) => sum + f.weight, 0);
|
||||
|
||||
// 执行地块适宜性评价
|
||||
const handleEvaluateField = () => {
|
||||
const validation = MultiFactorService.validateWeights(factorWeights);
|
||||
if (!validation.isValid) {
|
||||
toast.error(`权重总和必须为100%才能进行评价(当前:${validation.totalWeight}%)`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = MultiFactorService.generateEvaluationResult(selectedField, factorWeights);
|
||||
setEvaluationResults(prev =>
|
||||
prev.map(r => r.fieldId === selectedField ? result : r)
|
||||
);
|
||||
toast.success('评价完成!已应用当前权重配置计算综合得分');
|
||||
} catch (error) {
|
||||
toast.error('评价失败');
|
||||
}
|
||||
};
|
||||
|
||||
const exportResults = () => {
|
||||
const resultsToExport = batchAnalysisResults.length > 0 ? batchAnalysisResults : evaluationResults;
|
||||
toast.success('正在导出评价结果...');
|
||||
// 模拟导出功能
|
||||
setTimeout(() => {
|
||||
toast.success(`已导出${resultsToExport.length}个地块的评价结果`);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-green-800 dark:text-green-200">多因子综合评价</h2>
|
||||
<p className="text-muted-foreground">
|
||||
地块适宜性评价、自动化空间分析与作物适配推荐
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => setShowKnowledgeBase(true)}>
|
||||
<BookOpen className="w-4 h-4 mr-2" />
|
||||
作物知识库
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => setShowWeightConfig(true)}>
|
||||
<Settings className="w-4 h-4 mr-2" />
|
||||
权重配置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 多因子综合评价 */}
|
||||
<div className="space-y-4">
|
||||
<Card className="p-4 bg-card">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<label className="text-xs text-muted-foreground mb-2 block">选择地块</label>
|
||||
<Select value={selectedField} onValueChange={setSelectedField}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{evaluationResults.map((result) => (
|
||||
<SelectItem key={result.fieldId} value={result.fieldId}>
|
||||
{result.fieldName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-end">
|
||||
<Button
|
||||
className="bg-green-600 hover:bg-green-700"
|
||||
onClick={handleEvaluateField}
|
||||
>
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
开始评价
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 评价结果总览 */}
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<Card className="p-6 bg-gradient-to-br from-green-50 to-green-100 dark:from-green-950 dark:to-green-900">
|
||||
<div className="text-center">
|
||||
<Award className="w-12 h-12 text-green-600 dark:text-green-400 mx-auto mb-3" />
|
||||
<p className="text-xs text-muted-foreground mb-2">综合评分</p>
|
||||
<p
|
||||
className="text-4xl mb-2"
|
||||
style={{ color: getGradeColor(currentResult.grade) }}
|
||||
>
|
||||
{currentResult.totalScore}
|
||||
</p>
|
||||
<Badge
|
||||
className="text-white font-light"
|
||||
style={{ backgroundColor: getGradeColor(currentResult.grade) }}
|
||||
>
|
||||
{currentResult.grade}
|
||||
</Badge>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-6 bg-card">
|
||||
<div className="text-center">
|
||||
<CheckCircle2 className="w-12 h-12 text-blue-600 dark:text-blue-400 mx-auto mb-3" />
|
||||
<p className="text-xs text-muted-foreground mb-2">优秀因子</p>
|
||||
<p className="text-4xl text-blue-600 dark:text-blue-400 mb-2">
|
||||
{currentResult.factors.filter(f => f.score >= 80).length}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
/ {currentResult.factors.length} 项
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-6 bg-card">
|
||||
<div className="text-center">
|
||||
<AlertTriangle className="w-12 h-12 text-yellow-600 dark:text-yellow-400 mx-auto mb-3" />
|
||||
<p className="text-xs text-muted-foreground mb-2">待改善因子</p>
|
||||
<p className="text-4xl text-yellow-600 dark:text-yellow-400 mb-2">
|
||||
{currentResult.factors.filter(f => f.score < 70).length}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">需要优化</p>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-6 bg-card">
|
||||
<div className="text-center">
|
||||
<TrendingUp className="w-12 h-12 text-purple-600 dark:text-purple-400 mx-auto mb-3" />
|
||||
<p className="text-xs text-muted-foreground mb-2">评价时间</p>
|
||||
<p className="text-sm text-purple-600 dark:text-purple-400 mb-2">
|
||||
{formatDate(currentResult.timestamp)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">最近更新</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 因子详细评分 */}
|
||||
<Card className="p-6 bg-card">
|
||||
<h3 className="mb-4">评价因子详细分析</h3>
|
||||
<div className="space-y-4">
|
||||
{currentResult.factors.map((factor) => (
|
||||
<div key={factor.id} className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm font-medium">{factor.name}</span>
|
||||
<Badge variant="outline" className="text-xs font-light">
|
||||
权重: {factor.weight}%
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
实际值: {factor.value.toFixed(1)}{factor.unit}
|
||||
</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
最佳范围: {factor.optimalRange[0]}-{factor.optimalRange[1]}{factor.unit}
|
||||
</span>
|
||||
<span
|
||||
className="text-sm font-medium"
|
||||
style={{ color: getScoreColor(factor.score) }}
|
||||
>
|
||||
{factor.score}分
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Progress value={factor.score} className="h-2" />
|
||||
<div className="absolute top-0 left-0 h-2 w-full flex items-center">
|
||||
<div
|
||||
className="absolute h-3 w-1 bg-blue-500"
|
||||
style={{
|
||||
left: `${((factor.optimalRange[0] - 0) / (factor.optimalRange[1] * 1.5)) * 100}%`
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="absolute h-3 w-1 bg-blue-500"
|
||||
style={{
|
||||
left: `${((factor.optimalRange[1] - 0) / (factor.optimalRange[1] * 1.5)) * 100}%`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 加权计算说明 */}
|
||||
<Card className="p-6 bg-card">
|
||||
<h3 className="mb-4">层次分析法(AHP)加权计算</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-950 rounded-lg">
|
||||
<p className="text-sm text-blue-900 dark:text-blue-100 mb-2">计算公式:</p>
|
||||
<code className="text-xs text-blue-800 dark:text-blue-200 block mb-2">
|
||||
总分 = Σ(因子得分 × 因子权重)
|
||||
</code>
|
||||
<p className="text-xs text-blue-800 dark:text-blue-200">
|
||||
= ({currentResult.factors.map((f, i) =>
|
||||
`${f.score} × ${f.weight}%${i < currentResult.factors.length - 1 ? ' + ' : ''}`
|
||||
).join('')})
|
||||
</p>
|
||||
<p className="text-xs text-blue-800 dark:text-blue-200 mt-2">
|
||||
= {currentResult.totalScore}分
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="p-4 bg-green-50 dark:bg-green-950 rounded-lg">
|
||||
<h4 className="text-green-900 dark:text-green-100 mb-2">高度适宜 (≥80分)</h4>
|
||||
<ul className="text-sm text-green-800 dark:text-green-200 space-y-1">
|
||||
<li>• 土壤条件优秀</li>
|
||||
<li>• 适合多种作物</li>
|
||||
<li>• 预期产量高</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-950 rounded-lg">
|
||||
<h4 className="text-yellow-900 dark:text-yellow-100 mb-2">一般适宜 (60-79分)</h4>
|
||||
<ul className="text-sm text-yellow-800 dark:text-yellow-200 space-y-1">
|
||||
<li>• 部分因子需改善</li>
|
||||
<li>• 可种植耐性作物</li>
|
||||
<li>• 需适当改良</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="p-4 bg-red-50 dark:bg-red-950 rounded-lg">
|
||||
<h4 className="text-red-900 dark:text-red-100 mb-2">不适宜 (<60分)</h4>
|
||||
<ul className="text-sm text-red-800 dark:text-red-200 space-y-1">
|
||||
<li>• 土壤条件较差</li>
|
||||
<li>• 需大幅改良</li>
|
||||
<li>• 风险较高</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 多因子综合评价功能说明 */}
|
||||
<Card className="p-4 bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800">
|
||||
<div className="flex items-start gap-2">
|
||||
<Zap className="w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm text-blue-800 dark:text-blue-200">
|
||||
<p className="mb-2">多因子综合评价功能说明:</p>
|
||||
<ul className="space-y-1 text-xs">
|
||||
<li>• <strong>关键指标整合</strong>: 整合pH值、有机质含量、土层厚度、全氮、全磷、全钾、排水性等7项关键土壤指标</li>
|
||||
<li>• <strong>层次分析法(AHP)</strong>: 采用层次分析法构建加权评分体系,公式:总分 = Σ(因子得分 × 因子权重)</li>
|
||||
<li>• <strong>单因子评分</strong>: 根据实际值与最佳范围的接近程度计算各因子得分(0-100分),范围内得分85-100分</li>
|
||||
<li>• <strong>自定义权重</strong>: 支持手动调整各指标权重,提供粮食作物和经济作物两种预设方案</li>
|
||||
<li>• <strong>综合得分</strong>: 输出0-100分的适宜性评分,自动计算加权总分</li>
|
||||
<li>• <strong>三级分级</strong>: 高度适宜(≥80分)、一般适宜(60-79分)、不适宜(<60分)</li>
|
||||
<li>• <strong>可视化展示</strong>: 提供进度条、颜色标识等多维度可视化,直观展示各因子评分与最佳范围对比</li>
|
||||
<li>• <strong>改善建议</strong>: 自动识别待改善因子,为土壤改良提供决策依据</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-4 bg-card">
|
||||
<div className="flex items-center gap-4">
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import path from 'path'
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
open: true,
|
||||
hmr: {
|
||||
overlay: true
|
||||
}
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
outDir: 'build',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: ['react', 'react-dom'],
|
||||
radix: ['@radix-ui'],
|
||||
charts: ['recharts'],
|
||||
utils: ['date-fns', 'clsx', 'tailwind-merge'],
|
||||
hooks: ['react-hook-form'],
|
||||
icons: ['lucide-react']
|
||||
}
|
||||
}
|
||||
},
|
||||
chunkSizeWarningLimit: 1000
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'@radix-ui/react-slot',
|
||||
'@radix-ui/react-dialog',
|
||||
'@radix-ui/react-dropdown-menu',
|
||||
'lucide-react',
|
||||
'date-fns',
|
||||
'clsx',
|
||||
'tailwind-merge'
|
||||
]
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user