提交1 bmad搭建与项目启动 - ok
This commit is contained in:
244
crop-x/src/App.tsx
Normal file
244
crop-x/src/App.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
import React from 'react'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
|
||||
function App() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
{/* 头部导航 */}
|
||||
<header className="nav-agriculture">
|
||||
<div className="container-agriculture mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<h1 className="text-2xl font-bold text-white">
|
||||
🌾 智慧农业生产管理系统
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* 主题切换按钮 */}
|
||||
<button
|
||||
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
||||
className="px-3 py-2 rounded-md bg-white/20 hover:bg-white/30 text-white transition-colors"
|
||||
>
|
||||
{theme === 'light' ? '🌙' : '☀️'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 主要内容区域 */}
|
||||
<main className="container-agriculture mx-auto px-4 py-8">
|
||||
{/* 欢迎页面 */}
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-4xl font-bold mb-4 text-agriculture-green">
|
||||
欢迎使用智慧农业生产管理系统
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground mb-8">
|
||||
基于 React 18 + Vite 6 + TypeScript + shadcn/ui 构建的现代化农业管理平台
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 系统状态卡片 */}
|
||||
<div className="grid-agriculture mb-12">
|
||||
<div className="card-agriculture p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center mr-4">
|
||||
🚜
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">农机管理</h3>
|
||||
<p className="text-sm text-muted-foreground">9个模块,20个子功能</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>档案管理</span>
|
||||
<span className="text-green-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>运行监控</span>
|
||||
<span className="text-green-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>调度管理</span>
|
||||
<span className="text-green-600">✅ 已配置</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-agriculture p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mr-4">
|
||||
🌾
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">地块管理</h3>
|
||||
<p className="text-sm text-muted-foreground">土壤、作物、种植计划</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>地块档案</span>
|
||||
<span className="text-blue-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>作物管理</span>
|
||||
<span className="text-blue-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>种植计划</span>
|
||||
<span className="text-blue-600">✅ 已配置</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-agriculture p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-amber-100 rounded-lg flex items-center justify-center mr-4">
|
||||
📊
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">数据统计</h3>
|
||||
<p className="text-sm text-muted-foreground">实时数据与分析</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>农机总数</span>
|
||||
<span className="text-amber-600">0 台</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>地块面积</span>
|
||||
<span className="text-amber-600">0 亩</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>今日作业</span>
|
||||
<span className="text-amber-600">0 项</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-agriculture p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center mr-4">
|
||||
⚙️
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">系统设置</h3>
|
||||
<p className="text-sm text-muted-foreground">配置与权限管理</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>用户管理</span>
|
||||
<span className="text-purple-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>权限设置</span>
|
||||
<span className="text-purple-600">✅ 已配置</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>系统配置</span>
|
||||
<span className="text-purple-600">✅ 已配置</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 技术栈展示 */}
|
||||
<div className="card-agriculture p-8 mb-12">
|
||||
<h3 className="text-2xl font-bold mb-6 text-center">🛠️ 技术栈</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">⚛️</div>
|
||||
<h4 className="font-semibold">React 18</h4>
|
||||
<p className="text-sm text-muted-foreground">现代化UI框架</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">🚀</div>
|
||||
<h4 className="font-semibold">Vite 6</h4>
|
||||
<p className="text-sm text-muted-foreground">极速构建工具</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">📘</div>
|
||||
<h4 className="font-semibold">TypeScript</h4>
|
||||
<p className="text-sm text-muted-foreground">类型安全</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">🎨</div>
|
||||
<h4 className="font-semibold">Tailwind CSS</h4>
|
||||
<p className="text-sm text-muted-foreground">原子化CSS</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">🧩</div>
|
||||
<h4 className="font-semibold">shadcn/ui</h4>
|
||||
<p className="text-sm text-muted-foreground">组件库</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">📊</div>
|
||||
<h4 className="font-semibold">Recharts</h4>
|
||||
<p className="text-sm text-muted-foreground">数据可视化</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">🔧</div>
|
||||
<h4 className="font-semibold">ESLint</h4>
|
||||
<p className="text-sm text-muted-foreground">代码检查</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl mb-2">💅</div>
|
||||
<h4 className="font-semibold">Prettier</h4>
|
||||
<p className="text-sm text-muted-foreground">代码格式化</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 快速操作按钮 */}
|
||||
<div className="text-center">
|
||||
<div className="space-x-4">
|
||||
<button
|
||||
onClick={() => window.location.href = '/machinery'}
|
||||
className="btn-agriculture px-6 py-3 rounded-lg font-semibold mr-4"
|
||||
>
|
||||
🚜 进入农机管理
|
||||
</button>
|
||||
<button
|
||||
onClick={() => window.location.href = '/field'}
|
||||
className="btn-agriculture-secondary px-6 py-3 rounded-lg font-semibold"
|
||||
>
|
||||
🌾 进入地块管理
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 开发工具状态 */}
|
||||
<div className="mt-12 p-4 bg-muted rounded-lg">
|
||||
<h4 className="font-semibold mb-2">🔧 开发工具状态</h4>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p>✅ 项目依赖已安装</p>
|
||||
<p>✅ 开发服务器可正常启动</p>
|
||||
<p>✅ 热重载功能已配置</p>
|
||||
<p>✅ TypeScript类型检查已配置</p>
|
||||
<p>⚙️ ESLint/Prettier可通过开关控制</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* 页脚 */}
|
||||
<footer className="bg-muted py-8 mt-16">
|
||||
<div className="container-agriculture mx-auto px-4 text-center">
|
||||
<p className="text-muted-foreground">
|
||||
智慧农业生产管理系统 v1.0.0 | 基于 React 18 + Vite 6 + TypeScript 构建
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mt-2">
|
||||
🌾 专为现代化农业而生 | 科技赋能农业,智慧创造未来
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
21
crop-x/src/apis/index.ts
Normal file
21
crop-x/src/apis/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { authApi } from './modules/auth'
|
||||
import { machineryApi } from './modules/machinery'
|
||||
import { landApi } from './modules/land'
|
||||
import { operationApi } from './modules/operation'
|
||||
import { assetApi } from './modules/asset'
|
||||
import { aiModelApi } from './modules/ai-model'
|
||||
import { irrigationApi } from './modules/irrigation'
|
||||
import { configApi } from './modules/config'
|
||||
|
||||
export const api = {
|
||||
auth: authApi,
|
||||
machinery: machineryApi,
|
||||
land: landApi,
|
||||
operation: operationApi,
|
||||
asset: assetApi,
|
||||
aiModel: aiModelApi,
|
||||
irrigation: irrigationApi,
|
||||
config: configApi
|
||||
}
|
||||
|
||||
export * from './types'
|
||||
163
crop-x/src/apis/modules/ai-model.ts
Normal file
163
crop-x/src/apis/modules/ai-model.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// AI模型相关类型
|
||||
export interface AIModel {
|
||||
id: string
|
||||
name: string
|
||||
version: string
|
||||
type: 'prediction' | 'classification' | 'recommendation' | 'anomaly_detection'
|
||||
domain: 'crop_yield' | 'disease_detection' | 'pest_prediction' | 'irrigation_optimization' | 'fertilizer_recommendation'
|
||||
description: string
|
||||
algorithm: string
|
||||
accuracy: number
|
||||
trainingDataset: string
|
||||
trainingDate: string
|
||||
status: 'training' | 'trained' | 'deployed' | 'deprecated'
|
||||
parameters: Record<string, any>
|
||||
features: string[]
|
||||
targetVariable: string
|
||||
modelFile?: string
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface ModelTraining {
|
||||
id: string
|
||||
modelId: string
|
||||
name: string
|
||||
datasetId: string
|
||||
parameters: Record<string, any>
|
||||
status: 'pending' | 'running' | 'completed' | 'failed'
|
||||
progress: number
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
metrics?: {
|
||||
accuracy: number
|
||||
precision: number
|
||||
recall: number
|
||||
f1Score: number
|
||||
loss?: number
|
||||
}
|
||||
logs?: string[]
|
||||
errorMessage?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface PredictionRequest {
|
||||
modelId: string
|
||||
inputData: Record<string, any>
|
||||
options?: {
|
||||
probability?: boolean
|
||||
explanations?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface PredictionResult {
|
||||
id: string
|
||||
modelId: string
|
||||
prediction: any
|
||||
confidence?: number
|
||||
probability?: Record<string, number>
|
||||
explanations?: Array<{
|
||||
feature: string
|
||||
importance: number
|
||||
contribution: string
|
||||
}>
|
||||
inputData: Record<string, any>
|
||||
timestamp: string
|
||||
processingTime: number
|
||||
}
|
||||
|
||||
export interface Recommendation {
|
||||
id: string
|
||||
type: 'fertilizer' | 'irrigation' | 'planting' | 'harvesting' | 'pest_control'
|
||||
priority: 'low' | 'medium' | 'high'
|
||||
title: string
|
||||
description: string
|
||||
recommendation: string
|
||||
confidence: number
|
||||
validFrom: string
|
||||
validTo: string
|
||||
applicableArea?: string
|
||||
estimatedCost?: number
|
||||
estimatedBenefit?: number
|
||||
landParcelId?: string
|
||||
cropType?: string
|
||||
metadata: Record<string, any>
|
||||
status: 'active' | 'applied' | 'expired' | 'dismissed'
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export const aiModelApi = {
|
||||
// 模型管理
|
||||
getModelList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<AIModel>>> => {
|
||||
return request.get('/ai-model/models', params)
|
||||
},
|
||||
|
||||
getModelDetail: (id: string): Promise<ApiResponse<AIModel>> => {
|
||||
return request.get(`/ai-model/models/${id}`)
|
||||
},
|
||||
|
||||
createModel: (data: Omit<AIModel, 'id' | 'createdAt' | 'updatedAt' | 'accuracy' | 'status' | 'trainingDate'>): Promise<ApiResponse<AIModel>> => {
|
||||
return request.post('/ai-model/models', data)
|
||||
},
|
||||
|
||||
updateModel: (id: string, data: Partial<AIModel>): Promise<ApiResponse<AIModel>> => {
|
||||
return request.put(`/ai-model/models/${id}`, data)
|
||||
},
|
||||
|
||||
deleteModel: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/ai-model/models/${id}`)
|
||||
},
|
||||
|
||||
deployModel: (id: string): Promise<ApiResponse<AIModel>> => {
|
||||
return request.post(`/ai-model/models/${id}/deploy`)
|
||||
},
|
||||
|
||||
// 模型训练
|
||||
startTraining: (data: Omit<ModelTraining, 'id' | 'status' | 'progress' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<ModelTraining>> => {
|
||||
return request.post('/ai-model/training', data)
|
||||
},
|
||||
|
||||
getTrainingDetail: (id: string): Promise<ApiResponse<ModelTraining>> => {
|
||||
return request.get(`/ai-model/training/${id}`)
|
||||
},
|
||||
|
||||
getTrainingList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<ModelTraining>>> => {
|
||||
return request.get('/ai-model/training', params)
|
||||
},
|
||||
|
||||
stopTraining: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.post(`/ai-model/training/${id}/stop`)
|
||||
},
|
||||
|
||||
// 预测分析
|
||||
makePrediction: (data: PredictionRequest): Promise<ApiResponse<PredictionResult>> => {
|
||||
return request.post('/ai-model/predict', data)
|
||||
},
|
||||
|
||||
getPredictionHistory: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<PredictionResult>>> => {
|
||||
return request.get('/ai-model/predictions', params)
|
||||
},
|
||||
|
||||
getPredictionDetail: (id: string): Promise<ApiResponse<PredictionResult>> => {
|
||||
return request.get(`/ai-model/predictions/${id}`)
|
||||
},
|
||||
|
||||
// 推荐系统
|
||||
getRecommendations: (params: QueryRequest & { type?: string; landParcelId?: string }): Promise<ApiResponse<PaginatedResponse<Recommendation>>> => {
|
||||
return request.get('/ai-model/recommendations', params)
|
||||
},
|
||||
|
||||
applyRecommendation: (id: string): Promise<ApiResponse<Recommendation>> => {
|
||||
return request.put(`/ai-model/recommendations/${id}/apply`)
|
||||
},
|
||||
|
||||
dismissRecommendation: (id: string, reason: string): Promise<ApiResponse<Recommendation>> => {
|
||||
return request.put(`/ai-model/recommendations/${id}/dismiss`, { reason })
|
||||
}
|
||||
}
|
||||
135
crop-x/src/apis/modules/asset.ts
Normal file
135
crop-x/src/apis/modules/asset.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 资产管理相关类型
|
||||
export interface AgriculturalAsset {
|
||||
id: string
|
||||
name: string
|
||||
category: 'machinery' | 'equipment' | 'building' | 'land_improvement' | 'livestock' | 'other'
|
||||
subcategory: string
|
||||
serialNumber?: string
|
||||
model?: string
|
||||
brand?: string
|
||||
purchaseDate: string
|
||||
purchasePrice: number
|
||||
currentValue: number
|
||||
depreciationMethod: 'straight_line' | 'declining_balance' | 'units_of_production'
|
||||
usefulLifeYears: number
|
||||
salvageValue: number
|
||||
location: string
|
||||
status: 'active' | 'inactive' | 'disposed' | 'maintenance'
|
||||
assignedTo?: string
|
||||
description?: string
|
||||
images?: string[]
|
||||
documents?: string[]
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface MaintenanceRecord {
|
||||
id: string
|
||||
assetId: string
|
||||
type: 'preventive' | 'corrective' | 'emergency'
|
||||
description: string
|
||||
performedDate: string
|
||||
performedBy: string
|
||||
cost: number
|
||||
partsUsed?: Array<{
|
||||
name: string
|
||||
quantity: number
|
||||
unitCost: number
|
||||
}>
|
||||
nextMaintenanceDate?: string
|
||||
notes?: string
|
||||
documents?: string[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface AssetInventory {
|
||||
id: string
|
||||
assetId: string
|
||||
quantity: number
|
||||
unit: string
|
||||
location: string
|
||||
lastCountDate: string
|
||||
expectedQuantity: number
|
||||
variance: number
|
||||
varianceReason?: string
|
||||
countedBy: string
|
||||
verifiedBy?: string
|
||||
status: 'verified' | 'pending_verification' | 'discrepancy'
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export const assetApi = {
|
||||
// 资产管理
|
||||
getAssetList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<AgriculturalAsset>>> => {
|
||||
return request.get('/asset/assets', params)
|
||||
},
|
||||
|
||||
getAssetDetail: (id: string): Promise<ApiResponse<AgriculturalAsset>> => {
|
||||
return request.get(`/asset/assets/${id}`)
|
||||
},
|
||||
|
||||
createAsset: (data: Omit<AgriculturalAsset, 'id' | 'createdAt' | 'updatedAt' | 'currentValue'>): Promise<ApiResponse<AgriculturalAsset>> => {
|
||||
return request.post('/asset/assets', data)
|
||||
},
|
||||
|
||||
updateAsset: (id: string, data: Partial<AgriculturalAsset>): Promise<ApiResponse<AgriculturalAsset>> => {
|
||||
return request.put(`/asset/assets/${id}`, data)
|
||||
},
|
||||
|
||||
deleteAsset: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/asset/assets/${id}`)
|
||||
},
|
||||
|
||||
calculateDepreciation: (id: string, date: string): Promise<ApiResponse<{ currentValue: number; accumulatedDepreciation: number }>> => {
|
||||
return request.post(`/asset/assets/${id}/depreciation`, { date })
|
||||
},
|
||||
|
||||
// 维护记录
|
||||
getMaintenanceRecords: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<MaintenanceRecord>>> => {
|
||||
return request.get('/asset/maintenance', params)
|
||||
},
|
||||
|
||||
getMaintenanceDetail: (id: string): Promise<ApiResponse<MaintenanceRecord>> => {
|
||||
return request.get(`/asset/maintenance/${id}`)
|
||||
},
|
||||
|
||||
createMaintenanceRecord: (data: Omit<MaintenanceRecord, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<MaintenanceRecord>> => {
|
||||
return request.post('/asset/maintenance', data)
|
||||
},
|
||||
|
||||
updateMaintenanceRecord: (id: string, data: Partial<MaintenanceRecord>): Promise<ApiResponse<MaintenanceRecord>> => {
|
||||
return request.put(`/asset/maintenance/${id}`, data)
|
||||
},
|
||||
|
||||
deleteMaintenanceRecord: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/asset/maintenance/${id}`)
|
||||
},
|
||||
|
||||
// 库存管理
|
||||
getInventoryList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<AssetInventory>>> => {
|
||||
return request.get('/asset/inventory', params)
|
||||
},
|
||||
|
||||
getInventoryDetail: (id: string): Promise<ApiResponse<AssetInventory>> => {
|
||||
return request.get(`/asset/inventory/${id}`)
|
||||
},
|
||||
|
||||
createInventoryRecord: (data: Omit<AssetInventory, 'id' | 'createdAt' | 'updatedAt' | 'variance'>): Promise<ApiResponse<AssetInventory>> => {
|
||||
return request.post('/asset/inventory', data)
|
||||
},
|
||||
|
||||
updateInventoryRecord: (id: string, data: Partial<AssetInventory>): Promise<ApiResponse<AssetInventory>> => {
|
||||
return request.put(`/asset/inventory/${id}`, data)
|
||||
},
|
||||
|
||||
verifyInventory: (id: string, verifiedBy: string): Promise<ApiResponse<AssetInventory>> => {
|
||||
return request.put(`/asset/inventory/${id}/verify`, { verifiedBy })
|
||||
}
|
||||
}
|
||||
72
crop-x/src/apis/modules/auth.ts
Normal file
72
crop-x/src/apis/modules/auth.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse } from '../types'
|
||||
|
||||
// 认证相关类型
|
||||
export interface LoginRequest {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface PhoneLoginRequest {
|
||||
phone: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
refreshToken: string
|
||||
user: {
|
||||
id: string
|
||||
username: string
|
||||
phone: string
|
||||
role: string
|
||||
tenantId: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
username: string
|
||||
phone: string
|
||||
role: string
|
||||
tenantId: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
export const authApi = {
|
||||
// 用户名密码登录
|
||||
login: (data: LoginRequest): Promise<ApiResponse<LoginResponse>> => {
|
||||
return request.post('/auth/login', data)
|
||||
},
|
||||
|
||||
// 手机验证码登录
|
||||
loginByPhone: (data: PhoneLoginRequest): Promise<ApiResponse<LoginResponse>> => {
|
||||
return request.post('/auth/login/phone', data)
|
||||
},
|
||||
|
||||
// 获取验证码
|
||||
sendSmsCode: (phone: string): Promise<ApiResponse<void>> => {
|
||||
return request.post('/auth/sms/send', { phone })
|
||||
},
|
||||
|
||||
// 刷新token
|
||||
refreshToken: (refreshToken: string): Promise<ApiResponse<{ token: string }>> => {
|
||||
return request.post('/auth/refresh', { refreshToken })
|
||||
},
|
||||
|
||||
// 登出
|
||||
logout: (): Promise<ApiResponse<void>> => {
|
||||
return request.post('/auth/logout')
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getUserInfo: (): Promise<ApiResponse<User>> => {
|
||||
return request.get('/auth/user')
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
updateUserInfo: (data: Partial<User>): Promise<ApiResponse<User>> => {
|
||||
return request.put('/auth/user', data)
|
||||
}
|
||||
}
|
||||
262
crop-x/src/apis/modules/config.ts
Normal file
262
crop-x/src/apis/modules/config.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 中心配置相关类型
|
||||
export interface Tenant {
|
||||
id: string
|
||||
name: string
|
||||
code: string
|
||||
type: 'individual' | 'enterprise' | 'cooperative'
|
||||
status: 'active' | 'inactive' | 'suspended'
|
||||
contactPerson: string
|
||||
contactPhone: string
|
||||
contactEmail: string
|
||||
address: string
|
||||
maxUsers: number
|
||||
currentUsers: number
|
||||
subscriptionPlan: 'basic' | 'standard' | 'premium'
|
||||
subscriptionExpiry?: string
|
||||
settings: {
|
||||
timezone: string
|
||||
language: string
|
||||
currency: string
|
||||
dateFormat: string
|
||||
numberFormat: string
|
||||
}
|
||||
features: string[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
username: string
|
||||
email: string
|
||||
phone: string
|
||||
realName: string
|
||||
avatar?: string
|
||||
role: 'admin' | 'manager' | 'operator' | 'viewer'
|
||||
tenantId: string
|
||||
status: 'active' | 'inactive' | 'locked'
|
||||
lastLoginAt?: string
|
||||
permissions: string[]
|
||||
preferences: {
|
||||
language: string
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
notifications: {
|
||||
email: boolean
|
||||
sms: boolean
|
||||
push: boolean
|
||||
}
|
||||
}
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface SystemParameter {
|
||||
id: string
|
||||
category: 'system' | 'business' | 'notification' | 'security' | 'integration'
|
||||
key: string
|
||||
value: string
|
||||
type: 'string' | 'number' | 'boolean' | 'json'
|
||||
description: string
|
||||
isEditable: boolean
|
||||
requiresRestart: boolean
|
||||
validationRules?: {
|
||||
required?: boolean
|
||||
min?: number
|
||||
max?: number
|
||||
pattern?: string
|
||||
options?: string[]
|
||||
}
|
||||
tenantId?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface SystemLog {
|
||||
id: string
|
||||
level: 'debug' | 'info' | 'warn' | 'error' | 'fatal'
|
||||
category: 'system' | 'business' | 'security' | 'performance'
|
||||
message: string
|
||||
details?: any
|
||||
userId?: string
|
||||
tenantId?: string
|
||||
ip?: string
|
||||
userAgent?: string
|
||||
timestamp: string
|
||||
duration?: number
|
||||
stackTrace?: string
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
type: 'system' | 'business' | 'notification' | 'alert'
|
||||
category: 'info' | 'warning' | 'error' | 'success'
|
||||
title: string
|
||||
content: string
|
||||
senderId?: string
|
||||
recipientId?: string
|
||||
recipientRole?: string
|
||||
tenantId?: string
|
||||
status: 'unread' | 'read' | 'archived'
|
||||
priority: 'low' | 'medium' | 'high' | 'urgent'
|
||||
expiresAt?: string
|
||||
metadata?: Record<string, any>
|
||||
createdAt: string
|
||||
readAt?: string
|
||||
}
|
||||
|
||||
export const configApi = {
|
||||
// 租户管理
|
||||
getTenantList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<Tenant>>> => {
|
||||
return request.get('/config/tenants', params)
|
||||
},
|
||||
|
||||
getTenantDetail: (id: string): Promise<ApiResponse<Tenant>> => {
|
||||
return request.get(`/config/tenants/${id}`)
|
||||
},
|
||||
|
||||
createTenant: (data: Omit<Tenant, 'id' | 'createdAt' | 'updatedAt' | 'currentUsers'>): Promise<ApiResponse<Tenant>> => {
|
||||
return request.post('/config/tenants', data)
|
||||
},
|
||||
|
||||
updateTenant: (id: string, data: Partial<Tenant>): Promise<ApiResponse<Tenant>> => {
|
||||
return request.put(`/config/tenants/${id}`, data)
|
||||
},
|
||||
|
||||
deleteTenant: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/config/tenants/${id}`)
|
||||
},
|
||||
|
||||
suspendTenant: (id: string): Promise<ApiResponse<Tenant>> => {
|
||||
return request.post(`/config/tenants/${id}/suspend`)
|
||||
},
|
||||
|
||||
activateTenant: (id: string): Promise<ApiResponse<Tenant>> => {
|
||||
return request.post(`/config/tenants/${id}/activate`)
|
||||
},
|
||||
|
||||
// 用户管理
|
||||
getUserList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<User>>> => {
|
||||
return request.get('/config/users', params)
|
||||
},
|
||||
|
||||
getUserDetail: (id: string): Promise<ApiResponse<User>> => {
|
||||
return request.get(`/config/users/${id}`)
|
||||
},
|
||||
|
||||
createUser: (data: Omit<User, 'id' | 'createdAt' | 'updatedAt' | 'lastLoginAt'>): Promise<ApiResponse<User>> => {
|
||||
return request.post('/config/users', data)
|
||||
},
|
||||
|
||||
updateUser: (id: string, data: Partial<User>): Promise<ApiResponse<User>> => {
|
||||
return request.put(`/config/users/${id}`, data)
|
||||
},
|
||||
|
||||
deleteUser: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/config/users/${id}`)
|
||||
},
|
||||
|
||||
resetPassword: (id: string, newPassword: string): Promise<ApiResponse<void>> => {
|
||||
return request.post(`/config/users/${id}/reset-password`, { newPassword })
|
||||
},
|
||||
|
||||
lockUser: (id: string): Promise<ApiResponse<User>> => {
|
||||
return request.post(`/config/users/${id}/lock`)
|
||||
},
|
||||
|
||||
unlockUser: (id: string): Promise<ApiResponse<User>> => {
|
||||
return request.post(`/config/users/${id}/unlock`)
|
||||
},
|
||||
|
||||
// 系统参数
|
||||
getParameterList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<SystemParameter>>> => {
|
||||
return request.get('/config/parameters', params)
|
||||
},
|
||||
|
||||
getParameterDetail: (id: string): Promise<ApiResponse<SystemParameter>> => {
|
||||
return request.get(`/config/parameters/${id}`)
|
||||
},
|
||||
|
||||
createParameter: (data: Omit<SystemParameter, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<SystemParameter>> => {
|
||||
return request.post('/config/parameters', data)
|
||||
},
|
||||
|
||||
updateParameter: (id: string, data: Partial<SystemParameter>): Promise<ApiResponse<SystemParameter>> => {
|
||||
return request.put(`/config/parameters/${id}`, data)
|
||||
},
|
||||
|
||||
deleteParameter: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/config/parameters/${id}`)
|
||||
},
|
||||
|
||||
getParametersByCategory: (category: string): Promise<ApiResponse<SystemParameter[]>> => {
|
||||
return request.get(`/config/parameters/category/${category}`)
|
||||
},
|
||||
|
||||
// 系统监控
|
||||
getSystemLogs: (params: QueryRequest & { level?: string; category?: string }): Promise<ApiResponse<PaginatedResponse<SystemLog>>> => {
|
||||
return request.get('/config/logs', params)
|
||||
},
|
||||
|
||||
getSystemMetrics: (): Promise<ApiResponse<{
|
||||
totalUsers: number
|
||||
activeUsers: number
|
||||
totalTenants: number
|
||||
activeTenants: number
|
||||
systemUptime: number
|
||||
cpuUsage: number
|
||||
memoryUsage: number
|
||||
diskUsage: number
|
||||
databaseConnections: number
|
||||
}>> => {
|
||||
return request.get('/config/metrics')
|
||||
},
|
||||
|
||||
getSystemHealth: (): Promise<ApiResponse<{
|
||||
status: 'healthy' | 'warning' | 'critical'
|
||||
services: Array<{
|
||||
name: string
|
||||
status: 'up' | 'down'
|
||||
responseTime: number
|
||||
lastCheck: string
|
||||
}>
|
||||
issues: Array<{
|
||||
type: 'error' | 'warning'
|
||||
message: string
|
||||
timestamp: string
|
||||
}>
|
||||
}>> => {
|
||||
return request.get('/config/health')
|
||||
},
|
||||
|
||||
// 消息中心
|
||||
getMessageList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<Message>>> => {
|
||||
return request.get('/config/messages', params)
|
||||
},
|
||||
|
||||
getMessageDetail: (id: string): Promise<ApiResponse<Message>> => {
|
||||
return request.get(`/config/messages/${id}`)
|
||||
},
|
||||
|
||||
createMessage: (data: Omit<Message, 'id' | 'createdAt' | 'readAt' | 'status'>): Promise<ApiResponse<Message>> => {
|
||||
return request.post('/config/messages', data)
|
||||
},
|
||||
|
||||
markMessageAsRead: (id: string): Promise<ApiResponse<Message>> => {
|
||||
return request.put(`/config/messages/${id}/read`)
|
||||
},
|
||||
|
||||
archiveMessage: (id: string): Promise<ApiResponse<Message>> => {
|
||||
return request.put(`/config/messages/${id}/archive`)
|
||||
},
|
||||
|
||||
deleteMessage: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/config/messages/${id}`)
|
||||
},
|
||||
|
||||
getUnreadCount: (): Promise<ApiResponse<{ count: number }>> => {
|
||||
return request.get('/config/messages/unread/count')
|
||||
}
|
||||
}
|
||||
192
crop-x/src/apis/modules/irrigation.ts
Normal file
192
crop-x/src/apis/modules/irrigation.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 灌溉控制相关类型
|
||||
export interface IrrigationSystem {
|
||||
id: string
|
||||
name: string
|
||||
type: 'drip' | 'sprinkler' | 'flood' | 'micro_sprinkler'
|
||||
location: string
|
||||
landParcelId: string
|
||||
area: number
|
||||
waterSource: string
|
||||
pumpCapacity: number
|
||||
flowRate: number
|
||||
pressure: number
|
||||
status: 'active' | 'inactive' | 'maintenance'
|
||||
installationDate: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
description?: string
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface IrrigationZone {
|
||||
id: string
|
||||
systemId: string
|
||||
name: string
|
||||
area: number
|
||||
cropType: string
|
||||
soilType: string
|
||||
waterRequirement: number
|
||||
currentMoisture: number
|
||||
targetMoisture: number
|
||||
status: 'active' | 'inactive' | 'irrigating'
|
||||
sensors: Array<{
|
||||
id: string
|
||||
type: 'moisture' | 'temperature' | 'humidity' | 'ph'
|
||||
location: string
|
||||
currentValue: number
|
||||
unit: string
|
||||
lastReading: string
|
||||
}>
|
||||
valves: Array<{
|
||||
id: string
|
||||
name: string
|
||||
status: 'open' | 'closed'
|
||||
flowRate: number
|
||||
}>
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface IrrigationSchedule {
|
||||
id: string
|
||||
zoneId: string
|
||||
name: string
|
||||
type: 'fixed_time' | 'sensor_based' | 'weather_based'
|
||||
startTime: string
|
||||
duration: number
|
||||
waterAmount: number
|
||||
frequency: 'daily' | 'weekly' | 'custom'
|
||||
daysOfWeek?: number[]
|
||||
startDate: string
|
||||
endDate?: string
|
||||
priority: 'low' | 'medium' | 'high'
|
||||
status: 'active' | 'paused' | 'completed' | 'cancelled'
|
||||
conditions?: {
|
||||
moistureThreshold?: number
|
||||
weatherCondition?: string
|
||||
temperatureRange?: {
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
}
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface MonitoringData {
|
||||
id: string
|
||||
zoneId: string
|
||||
sensorType: 'moisture' | 'temperature' | 'humidity' | 'ph' | 'flow_rate' | 'pressure'
|
||||
value: number
|
||||
unit: string
|
||||
timestamp: string
|
||||
location?: string
|
||||
}
|
||||
|
||||
export interface ControlCommand {
|
||||
id: string
|
||||
systemId: string
|
||||
zoneId?: string
|
||||
command: 'start_irrigation' | 'stop_irrigation' | 'open_valve' | 'close_valve' | 'adjust_flow_rate'
|
||||
parameters: Record<string, any>
|
||||
status: 'pending' | 'executing' | 'completed' | 'failed'
|
||||
executedAt?: string
|
||||
errorMessage?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export const irrigationApi = {
|
||||
// 系统控制
|
||||
getSystemList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<IrrigationSystem>>> => {
|
||||
return request.get('/irrigation/systems', params)
|
||||
},
|
||||
|
||||
getSystemDetail: (id: string): Promise<ApiResponse<IrrigationSystem>> => {
|
||||
return request.get(`/irrigation/systems/${id}`)
|
||||
},
|
||||
|
||||
createSystem: (data: Omit<IrrigationSystem, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<IrrigationSystem>> => {
|
||||
return request.post('/irrigation/systems', data)
|
||||
},
|
||||
|
||||
updateSystem: (id: string, data: Partial<IrrigationSystem>): Promise<ApiResponse<IrrigationSystem>> => {
|
||||
return request.put(`/irrigation/systems/${id}`, data)
|
||||
},
|
||||
|
||||
deleteSystem: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/irrigation/systems/${id}`)
|
||||
},
|
||||
|
||||
startIrrigation: (systemId: string, zoneId?: string, duration?: number): Promise<ApiResponse<ControlCommand>> => {
|
||||
return request.post(`/irrigation/systems/${systemId}/start`, { zoneId, duration })
|
||||
},
|
||||
|
||||
stopIrrigation: (systemId: string, zoneId?: string): Promise<ApiResponse<ControlCommand>> => {
|
||||
return request.post(`/irrigation/systems/${systemId}/stop`, { zoneId })
|
||||
},
|
||||
|
||||
// 分区管理
|
||||
getZoneList: (systemId: string, params: QueryRequest): Promise<ApiResponse<PaginatedResponse<IrrigationZone>>> => {
|
||||
return request.get(`/irrigation/systems/${systemId}/zones`, params)
|
||||
},
|
||||
|
||||
getZoneDetail: (id: string): Promise<ApiResponse<IrrigationZone>> => {
|
||||
return request.get(`/irrigation/zones/${id}`)
|
||||
},
|
||||
|
||||
createZone: (data: Omit<IrrigationZone, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<IrrigationZone>> => {
|
||||
return request.post('/irrigation/zones', data)
|
||||
},
|
||||
|
||||
updateZone: (id: string, data: Partial<IrrigationZone>): Promise<ApiResponse<IrrigationZone>> => {
|
||||
return request.put(`/irrigation/zones/${id}`, data)
|
||||
},
|
||||
|
||||
deleteZone: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/irrigation/zones/${id}`)
|
||||
},
|
||||
|
||||
// 监控系统
|
||||
getMonitoringData: (zoneId: string, params: QueryRequest & { sensorType?: string }): Promise<ApiResponse<PaginatedResponse<MonitoringData>>> => {
|
||||
return request.get(`/irrigation/zones/${zoneId}/monitoring`, params)
|
||||
},
|
||||
|
||||
getRealTimeData: (zoneId: string): Promise<ApiResponse<MonitoringData[]>> => {
|
||||
return request.get(`/irrigation/zones/${zoneId}/realtime`)
|
||||
},
|
||||
|
||||
// 调度系统
|
||||
getScheduleList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<IrrigationSchedule>>> => {
|
||||
return request.get('/irrigation/schedules', params)
|
||||
},
|
||||
|
||||
getScheduleDetail: (id: string): Promise<ApiResponse<IrrigationSchedule>> => {
|
||||
return request.get(`/irrigation/schedules/${id}`)
|
||||
},
|
||||
|
||||
createSchedule: (data: Omit<IrrigationSchedule, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<IrrigationSchedule>> => {
|
||||
return request.post('/irrigation/schedules', data)
|
||||
},
|
||||
|
||||
updateSchedule: (id: string, data: Partial<IrrigationSchedule>): Promise<ApiResponse<IrrigationSchedule>> => {
|
||||
return request.put(`/irrigation/schedules/${id}`, data)
|
||||
},
|
||||
|
||||
deleteSchedule: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/irrigation/schedules/${id}`)
|
||||
},
|
||||
|
||||
pauseSchedule: (id: string): Promise<ApiResponse<IrrigationSchedule>> => {
|
||||
return request.post(`/irrigation/schedules/${id}/pause`)
|
||||
},
|
||||
|
||||
resumeSchedule: (id: string): Promise<ApiResponse<IrrigationSchedule>> => {
|
||||
return request.post(`/irrigation/schedules/${id}/resume`)
|
||||
}
|
||||
}
|
||||
127
crop-x/src/apis/modules/land.ts
Normal file
127
crop-x/src/apis/modules/land.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 地块相关类型
|
||||
export interface LandParcel {
|
||||
id: string
|
||||
name: string
|
||||
code: string
|
||||
area: number
|
||||
areaUnit: 'mu' | 'hectare' | 'acre'
|
||||
location: {
|
||||
latitude: number
|
||||
longitude: number
|
||||
address: string
|
||||
}
|
||||
boundaries: Array<{
|
||||
latitude: number
|
||||
longitude: number
|
||||
}>
|
||||
soilType: string
|
||||
landUse: string
|
||||
ownershipType: 'owned' | 'rented' | 'shared'
|
||||
tenantId: string
|
||||
description?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface LandClassification {
|
||||
id: string
|
||||
landParcelId: string
|
||||
category: string
|
||||
subcategory: string
|
||||
tags: string[]
|
||||
attributes: Record<string, any>
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface MapLayer {
|
||||
id: string
|
||||
name: string
|
||||
type: 'base' | 'overlay' | 'analysis'
|
||||
url: string
|
||||
opacity: number
|
||||
visible: boolean
|
||||
attribution?: string
|
||||
}
|
||||
|
||||
export interface SpatialAnalysis {
|
||||
id: string
|
||||
name: string
|
||||
type: 'buffer' | 'overlay' | 'proximity' | 'suitability'
|
||||
parameters: Record<string, any>
|
||||
result: {
|
||||
area: number
|
||||
geometry: any
|
||||
statistics: Record<string, number>
|
||||
}
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export const landApi = {
|
||||
// 地块档案管理
|
||||
getLandParcelList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<LandParcel>>> => {
|
||||
return request.get('/land/parcels', params)
|
||||
},
|
||||
|
||||
getLandParcelDetail: (id: string): Promise<ApiResponse<LandParcel>> => {
|
||||
return request.get(`/land/parcels/${id}`)
|
||||
},
|
||||
|
||||
createLandParcel: (data: Omit<LandParcel, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<LandParcel>> => {
|
||||
return request.post('/land/parcels', data)
|
||||
},
|
||||
|
||||
updateLandParcel: (id: string, data: Partial<LandParcel>): Promise<ApiResponse<LandParcel>> => {
|
||||
return request.put(`/land/parcels/${id}`, data)
|
||||
},
|
||||
|
||||
deleteLandParcel: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/land/parcels/${id}`)
|
||||
},
|
||||
|
||||
// 地块分类管理
|
||||
getClassificationList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<LandClassification>>> => {
|
||||
return request.get('/land/classifications', params)
|
||||
},
|
||||
|
||||
createClassification: (data: Omit<LandClassification, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<LandClassification>> => {
|
||||
return request.post('/land/classifications', data)
|
||||
},
|
||||
|
||||
updateClassification: (id: string, data: Partial<LandClassification>): Promise<ApiResponse<LandClassification>> => {
|
||||
return request.put(`/land/classifications/${id}`, data)
|
||||
},
|
||||
|
||||
deleteClassification: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/land/classifications/${id}`)
|
||||
},
|
||||
|
||||
// 地图管理
|
||||
getMapLayers: (): Promise<ApiResponse<MapLayer[]>> => {
|
||||
return request.get('/land/maps/layers')
|
||||
},
|
||||
|
||||
createMapLayer: (data: Omit<MapLayer, 'id'>): Promise<ApiResponse<MapLayer>> => {
|
||||
return request.post('/land/maps/layers', data)
|
||||
},
|
||||
|
||||
updateMapLayer: (id: string, data: Partial<MapLayer>): Promise<ApiResponse<MapLayer>> => {
|
||||
return request.put(`/land/maps/layers/${id}`, data)
|
||||
},
|
||||
|
||||
deleteMapLayer: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/land/maps/layers/${id}`)
|
||||
},
|
||||
|
||||
// 空间分析
|
||||
performSpatialAnalysis: (data: Omit<SpatialAnalysis, 'id' | 'createdAt' | 'result'>): Promise<ApiResponse<SpatialAnalysis>> => {
|
||||
return request.post('/land/analysis/spatial', data)
|
||||
},
|
||||
|
||||
getAnalysisHistory: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<SpatialAnalysis>>> => {
|
||||
return request.get('/land/analysis/history', params)
|
||||
}
|
||||
}
|
||||
105
crop-x/src/apis/modules/machinery.ts
Normal file
105
crop-x/src/apis/modules/machinery.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 农机相关类型
|
||||
export interface Machinery {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
model: string
|
||||
status: 'active' | 'inactive' | 'maintenance' | 'repair'
|
||||
location: {
|
||||
latitude: number
|
||||
longitude: number
|
||||
address: string
|
||||
}
|
||||
driverId?: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
purchaseDate: string
|
||||
price: number
|
||||
description?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface Driver {
|
||||
id: string
|
||||
name: string
|
||||
phone: string
|
||||
licenseNumber: string
|
||||
licenseType: string
|
||||
experience: number
|
||||
status: 'active' | 'inactive'
|
||||
machineryId?: string
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface MonitoringData {
|
||||
id: string
|
||||
machineryId: string
|
||||
location: {
|
||||
latitude: number
|
||||
longitude: number
|
||||
}
|
||||
speed: number
|
||||
fuelLevel: number
|
||||
engineHours: number
|
||||
workingStatus: 'working' | 'idle' | 'stopped'
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export const machineryApi = {
|
||||
// 农机管理
|
||||
getMachineryList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<Machinery>>> => {
|
||||
return request.get('/machinery', params)
|
||||
},
|
||||
|
||||
getMachineryDetail: (id: string): Promise<ApiResponse<Machinery>> => {
|
||||
return request.get(`/machinery/${id}`)
|
||||
},
|
||||
|
||||
createMachinery: (data: Omit<Machinery, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Machinery>> => {
|
||||
return request.post('/machinery', data)
|
||||
},
|
||||
|
||||
updateMachinery: (id: string, data: Partial<Machinery>): Promise<ApiResponse<Machinery>> => {
|
||||
return request.put(`/machinery/${id}`, data)
|
||||
},
|
||||
|
||||
deleteMachinery: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/machinery/${id}`)
|
||||
},
|
||||
|
||||
// 驾驶员管理
|
||||
getDriverList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<Driver>>> => {
|
||||
return request.get('/machinery/drivers', params)
|
||||
},
|
||||
|
||||
getDriverDetail: (id: string): Promise<ApiResponse<Driver>> => {
|
||||
return request.get(`/machinery/drivers/${id}`)
|
||||
},
|
||||
|
||||
createDriver: (data: Omit<Driver, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Driver>> => {
|
||||
return request.post('/machinery/drivers', data)
|
||||
},
|
||||
|
||||
updateDriver: (id: string, data: Partial<Driver>): Promise<ApiResponse<Driver>> => {
|
||||
return request.put(`/machinery/drivers/${id}`, data)
|
||||
},
|
||||
|
||||
deleteDriver: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/machinery/drivers/${id}`)
|
||||
},
|
||||
|
||||
// 实时监控
|
||||
getRealTimeData: (machineryId: string): Promise<ApiResponse<MonitoringData>> => {
|
||||
return request.get(`/machinery/monitoring/${machineryId}`)
|
||||
},
|
||||
|
||||
getMonitoringHistory: (machineryId: string, params: QueryRequest): Promise<ApiResponse<PaginatedResponse<MonitoringData>>> => {
|
||||
return request.get(`/machinery/monitoring/${machineryId}/history`, params)
|
||||
}
|
||||
}
|
||||
147
crop-x/src/apis/modules/operation.ts
Normal file
147
crop-x/src/apis/modules/operation.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { request } from '../request'
|
||||
import { ApiResponse, PaginatedResponse, QueryRequest } from '../types'
|
||||
|
||||
// 农事操作相关类型
|
||||
export interface FarmingTask {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
type: 'planting' | 'harvesting' | 'fertilizing' | 'irrigating' | 'pest_control' | 'other'
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'cancelled'
|
||||
priority: 'low' | 'medium' | 'high' | 'urgent'
|
||||
assignedTo?: string
|
||||
landParcelId: string
|
||||
machineryId?: string
|
||||
scheduledStartDate: string
|
||||
scheduledEndDate: string
|
||||
actualStartDate?: string
|
||||
actualEndDate?: string
|
||||
estimatedCost?: number
|
||||
actualCost?: number
|
||||
notes?: string
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface TaskTemplate {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
taskType: string
|
||||
defaultDuration: number
|
||||
requiredResources: string[]
|
||||
checklistItems: string[]
|
||||
costEstimate: number
|
||||
tenantId: string
|
||||
}
|
||||
|
||||
export interface ResourceAllocation {
|
||||
id: string
|
||||
taskId: string
|
||||
resourceType: 'machinery' | 'labor' | 'material' | 'tool'
|
||||
resourceId: string
|
||||
quantity: number
|
||||
allocatedDate: string
|
||||
returnDate?: string
|
||||
status: 'allocated' | 'in_use' | 'returned'
|
||||
}
|
||||
|
||||
export interface Workflow {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
stages: Array<{
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
duration: number
|
||||
dependencies: string[]
|
||||
resources: string[]
|
||||
}>
|
||||
landParcelId: string
|
||||
status: 'draft' | 'active' | 'completed' | 'paused'
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
tenantId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export const operationApi = {
|
||||
// 任务管理
|
||||
getTaskList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<FarmingTask>>> => {
|
||||
return request.get('/operation/tasks', params)
|
||||
},
|
||||
|
||||
getTaskDetail: (id: string): Promise<ApiResponse<FarmingTask>> => {
|
||||
return request.get(`/operation/tasks/${id}`)
|
||||
},
|
||||
|
||||
createTask: (data: Omit<FarmingTask, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<FarmingTask>> => {
|
||||
return request.post('/operation/tasks', data)
|
||||
},
|
||||
|
||||
updateTask: (id: string, data: Partial<FarmingTask>): Promise<ApiResponse<FarmingTask>> => {
|
||||
return request.put(`/operation/tasks/${id}`, data)
|
||||
},
|
||||
|
||||
deleteTask: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/operation/tasks/${id}`)
|
||||
},
|
||||
|
||||
updateTaskStatus: (id: string, status: FarmingTask['status']): Promise<ApiResponse<FarmingTask>> => {
|
||||
return request.put(`/operation/tasks/${id}/status`, { status })
|
||||
},
|
||||
|
||||
// 任务模板
|
||||
getTaskTemplateList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<TaskTemplate>>> => {
|
||||
return request.get('/operation/templates', params)
|
||||
},
|
||||
|
||||
createTaskTemplate: (data: Omit<TaskTemplate, 'id'>): Promise<ApiResponse<TaskTemplate>> => {
|
||||
return request.post('/operation/templates', data)
|
||||
},
|
||||
|
||||
updateTaskTemplate: (id: string, data: Partial<TaskTemplate>): Promise<ApiResponse<TaskTemplate>> => {
|
||||
return request.put(`/operation/templates/${id}`, data)
|
||||
},
|
||||
|
||||
deleteTaskTemplate: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/operation/templates/${id}`)
|
||||
},
|
||||
|
||||
// 资源分配
|
||||
getResourceAllocations: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<ResourceAllocation>>> => {
|
||||
return request.get('/operation/resources', params)
|
||||
},
|
||||
|
||||
allocateResource: (data: Omit<ResourceAllocation, 'id' | 'allocatedDate'>): Promise<ApiResponse<ResourceAllocation>> => {
|
||||
return request.post('/operation/resources/allocate', data)
|
||||
},
|
||||
|
||||
returnResource: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.put(`/operation/resources/${id}/return`)
|
||||
},
|
||||
|
||||
// 工作流管理
|
||||
getWorkflowList: (params: QueryRequest): Promise<ApiResponse<PaginatedResponse<Workflow>>> => {
|
||||
return request.get('/operation/workflows', params)
|
||||
},
|
||||
|
||||
getWorkflowDetail: (id: string): Promise<ApiResponse<Workflow>> => {
|
||||
return request.get(`/operation/workflows/${id}`)
|
||||
},
|
||||
|
||||
createWorkflow: (data: Omit<Workflow, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Workflow>> => {
|
||||
return request.post('/operation/workflows', data)
|
||||
},
|
||||
|
||||
updateWorkflow: (id: string, data: Partial<Workflow>): Promise<ApiResponse<Workflow>> => {
|
||||
return request.put(`/operation/workflows/${id}`, data)
|
||||
},
|
||||
|
||||
deleteWorkflow: (id: string): Promise<ApiResponse<void>> => {
|
||||
return request.delete(`/operation/workflows/${id}`)
|
||||
}
|
||||
}
|
||||
80
crop-x/src/apis/request.ts
Normal file
80
crop-x/src/apis/request.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import axios, { AxiosInstance, AxiosResponse } from 'axios'
|
||||
import { config } from '@/config/features'
|
||||
import { ApiResponse, ApiError } from './types'
|
||||
|
||||
/**
|
||||
* 请求管理器类
|
||||
* 封装了基于Axios的HTTP请求操作,包括请求和响应拦截器
|
||||
*/
|
||||
/**
|
||||
private instance: AxiosInstance // Axios实例,用于发送HTTP请求
|
||||
* 使用Axios库进行HTTP请求,并添加了请求和响应拦截器
|
||||
*/
|
||||
class RequestManager {
|
||||
private instance: AxiosInstance
|
||||
|
||||
constructor() {
|
||||
this.instance = axios.create({
|
||||
baseURL: config.apiUrl,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
this.setupInterceptors()
|
||||
}
|
||||
|
||||
private setupInterceptors() {
|
||||
// 请求拦截器
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
this.instance.interceptors.response.use(
|
||||
(response: AxiosResponse<ApiResponse>) => {
|
||||
const { data } = response
|
||||
if (data.success) {
|
||||
return response
|
||||
} else {
|
||||
return Promise.reject(new Error(data.message))
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public get<T = any>(url: string, params?: any): Promise<ApiResponse<T>> {
|
||||
return this.instance.get(url, { params }).then(res => res.data)
|
||||
}
|
||||
|
||||
public post<T = any>(url: string, data?: any): Promise<ApiResponse<T>> {
|
||||
return this.instance.post(url, data).then(res => res.data)
|
||||
}
|
||||
|
||||
public put<T = any>(url: string, data?: any): Promise<ApiResponse<T>> {
|
||||
return this.instance.put(url, data).then(res => res.data)
|
||||
}
|
||||
|
||||
public delete<T = any>(url: string): Promise<ApiResponse<T>> {
|
||||
return this.instance.delete(url).then(res => res.data)
|
||||
}
|
||||
}
|
||||
|
||||
export const request = new RequestManager()
|
||||
51
crop-x/src/apis/types.ts
Normal file
51
crop-x/src/apis/types.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// API 响应类型定义
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
success: boolean
|
||||
}
|
||||
|
||||
// 分页请求参数
|
||||
export interface PaginationParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
total?: number
|
||||
}
|
||||
|
||||
// 分页响应数据
|
||||
export interface PaginatedResponse<T> {
|
||||
items: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
// 错误响应
|
||||
export interface ApiError {
|
||||
code: number
|
||||
message: string
|
||||
details?: any
|
||||
}
|
||||
|
||||
// 通用CRUD操作类型
|
||||
export interface CreateRequest<T> {
|
||||
data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>
|
||||
}
|
||||
|
||||
export interface UpdateRequest<T> {
|
||||
id: string | number
|
||||
data: Partial<T>
|
||||
}
|
||||
|
||||
export interface DeleteRequest {
|
||||
id: string | number
|
||||
}
|
||||
|
||||
export interface QueryRequest extends PaginationParams {
|
||||
keyword?: string
|
||||
filters?: Record<string, any>
|
||||
sortBy?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
57
crop-x/src/config/constants.ts
Normal file
57
crop-x/src/config/constants.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
// 应用常量配置
|
||||
export const APP_CONFIG = {
|
||||
name: '智慧农业生产管理系统',
|
||||
version: '1.0.0',
|
||||
description: 'Smart Agriculture Production Management System'
|
||||
}
|
||||
|
||||
// 路由常量
|
||||
export const ROUTE_CONSTANTS = {
|
||||
LOGIN: '/login',
|
||||
DASHBOARD: '/dashboard',
|
||||
|
||||
// 模块路由前缀
|
||||
MACHINERY: '/machinery',
|
||||
LAND: '/land',
|
||||
OPERATION: '/operation',
|
||||
ASSET: '/asset',
|
||||
AI_MODEL: '/ai-model',
|
||||
IRRIGATION: '/irrigation',
|
||||
CONFIG: '/config'
|
||||
}
|
||||
|
||||
// 状态常量
|
||||
export const STATUS = {
|
||||
ACTIVE: 'active',
|
||||
INACTIVE: 'inactive',
|
||||
PENDING: 'pending',
|
||||
COMPLETED: 'completed',
|
||||
CANCELLED: 'cancelled',
|
||||
ERROR: 'error'
|
||||
}
|
||||
|
||||
// 用户角色
|
||||
export const USER_ROLES = {
|
||||
ADMIN: 'admin',
|
||||
MANAGER: 'manager',
|
||||
OPERATOR: 'operator',
|
||||
VIEWER: 'viewer'
|
||||
}
|
||||
|
||||
// 分页默认配置
|
||||
export const PAGINATION = {
|
||||
DEFAULT_PAGE_SIZE: 10,
|
||||
DEFAULT_PAGE: 1,
|
||||
PAGE_SIZE_OPTIONS: [10, 20, 50, 100]
|
||||
}
|
||||
|
||||
// 主题配置
|
||||
export const THEME = {
|
||||
colors: {
|
||||
primary: '#0ea5e9',
|
||||
secondary: '#64748b',
|
||||
success: '#22c55e',
|
||||
warning: '#f59e0b',
|
||||
error: '#ef4444'
|
||||
}
|
||||
}
|
||||
85
crop-x/src/config/features.ts
Normal file
85
crop-x/src/config/features.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
// 功能配置系统 - 特性开关
|
||||
export const featureConfig = {
|
||||
// 认证系统
|
||||
auth: {
|
||||
enabled: true,
|
||||
mockData: true,
|
||||
phoneLogin: true,
|
||||
autoLogin: true
|
||||
},
|
||||
|
||||
// 地块管理
|
||||
landManagement: {
|
||||
enabled: true,
|
||||
mapIntegration: true,
|
||||
spatialAnalysis: true,
|
||||
riskWarning: true
|
||||
},
|
||||
|
||||
// 农机管理
|
||||
machineryManagement: {
|
||||
enabled: true,
|
||||
realTimeMonitoring: true,
|
||||
faultDiagnosis: true,
|
||||
precisionFarming: true,
|
||||
scheduling: true
|
||||
},
|
||||
|
||||
// 农事管理
|
||||
farmingOperation: {
|
||||
enabled: true,
|
||||
planning: true,
|
||||
tracking: true,
|
||||
analysis: true
|
||||
},
|
||||
|
||||
// 资产管理
|
||||
assetManagement: {
|
||||
enabled: true,
|
||||
inventory: true,
|
||||
depreciation: true
|
||||
},
|
||||
|
||||
// AI模型
|
||||
aiModel: {
|
||||
enabled: true,
|
||||
prediction: true,
|
||||
recommendation: true,
|
||||
analysis: true
|
||||
},
|
||||
|
||||
// 水肥控制
|
||||
irrigationControl: {
|
||||
enabled: true,
|
||||
automation: true,
|
||||
monitoring: true,
|
||||
scheduling: true
|
||||
},
|
||||
|
||||
// 系统配置
|
||||
systemConfig: {
|
||||
enabled: true,
|
||||
tenantManagement: true,
|
||||
userManagement: true,
|
||||
systemMonitoring: true,
|
||||
messageCenter: true
|
||||
}
|
||||
}
|
||||
|
||||
// 环境配置
|
||||
export const envConfig = {
|
||||
development: {
|
||||
apiUrl: 'http://localhost:3001/api',
|
||||
mockApi: true,
|
||||
debugMode: true
|
||||
},
|
||||
production: {
|
||||
apiUrl: '/api',
|
||||
mockApi: false,
|
||||
debugMode: false
|
||||
}
|
||||
}
|
||||
|
||||
// 当前环境
|
||||
export const currentEnv = import.meta.env.MODE || 'development'
|
||||
export const config = envConfig[currentEnv]
|
||||
17
crop-x/src/hooks/useDebounce.ts
Normal file
17
crop-x/src/hooks/useDebounce.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useDebounce<T>(value: T, delay: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value)
|
||||
}, delay)
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler)
|
||||
}
|
||||
}, [value, delay])
|
||||
|
||||
return debouncedValue
|
||||
}
|
||||
56
crop-x/src/hooks/useLocalStorage.ts
Normal file
56
crop-x/src/hooks/useLocalStorage.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
): [T, (value: T | ((prev: T) => T)) => void] {
|
||||
// 获取初始值
|
||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return initialValue
|
||||
}
|
||||
|
||||
try {
|
||||
const item = window.localStorage.getItem(key)
|
||||
return item ? JSON.parse(item) : initialValue
|
||||
} catch (error) {
|
||||
console.warn(`Error reading localStorage key "${key}":`, error)
|
||||
return initialValue
|
||||
}
|
||||
})
|
||||
|
||||
// 设置值的函数
|
||||
const setValue = (value: T | ((prev: T) => T)) => {
|
||||
try {
|
||||
// 允许value是一个函数,类似于useState
|
||||
const valueToStore =
|
||||
value instanceof Function ? value(storedValue) : value
|
||||
|
||||
setStoredValue(valueToStore)
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore))
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Error setting localStorage key "${key}":`, error)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听localStorage变化
|
||||
useEffect(() => {
|
||||
const handleStorageChange = (event: StorageEvent) => {
|
||||
if (event.key === key && event.newValue !== null) {
|
||||
try {
|
||||
setStoredValue(JSON.parse(event.newValue))
|
||||
} catch (error) {
|
||||
console.warn(`Error parsing localStorage change for key "${key}":`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('storage', handleStorageChange)
|
||||
return () => window.removeEventListener('storage', handleStorageChange)
|
||||
}, [key])
|
||||
|
||||
return [storedValue, setValue]
|
||||
}
|
||||
58
crop-x/src/hooks/useTheme.ts
Normal file
58
crop-x/src/hooks/useTheme.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import type { Theme } from '@/types'
|
||||
|
||||
export function useTheme() {
|
||||
const [theme, setTheme] = useState<Theme>(() => {
|
||||
// 从localStorage获取主题设置
|
||||
const saved = localStorage.getItem('agriculture-theme')
|
||||
if (saved && ['light', 'dark', 'system'].includes(saved)) {
|
||||
return saved as Theme
|
||||
}
|
||||
return 'system'
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement
|
||||
|
||||
// 移除之前的主题类
|
||||
root.classList.remove('light', 'dark')
|
||||
|
||||
if (theme === 'system') {
|
||||
// 使用系统主题
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
.matches
|
||||
? 'dark'
|
||||
: 'light'
|
||||
root.classList.add(systemTheme)
|
||||
} else {
|
||||
// 使用指定主题
|
||||
root.classList.add(theme)
|
||||
}
|
||||
|
||||
// 保存到localStorage
|
||||
localStorage.setItem('agriculture-theme', theme)
|
||||
}, [theme])
|
||||
|
||||
// 监听系统主题变化
|
||||
useEffect(() => {
|
||||
if (theme === 'system') {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
const handleChange = () => {
|
||||
const root = window.document.documentElement
|
||||
root.classList.remove('light', 'dark')
|
||||
root.classList.add(mediaQuery.matches ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
mediaQuery.addEventListener('change', handleChange)
|
||||
return () => mediaQuery.removeEventListener('change', handleChange)
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
return {
|
||||
theme,
|
||||
setTheme,
|
||||
isDark: theme === 'dark' || (theme === 'system' &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
}
|
||||
}
|
||||
218
crop-x/src/lib/utils.ts
Normal file
218
crop-x/src/lib/utils.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
// 合并 Tailwind CSS 类名的工具函数
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
export function formatDate(date: Date | string | number): string {
|
||||
const d = new Date(date)
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
export function formatTime(date: Date | string | number): string {
|
||||
const d = new Date(date)
|
||||
return d.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
export function formatDateTime(date: Date | string | number): string {
|
||||
return `${formatDate(date)} ${formatTime(date)}`
|
||||
}
|
||||
|
||||
// 相对时间格式化
|
||||
export function formatRelativeTime(date: Date | string | number): string {
|
||||
const now = new Date()
|
||||
const target = new Date(date)
|
||||
const diffMs = now.getTime() - target.getTime()
|
||||
const diffSecs = Math.floor(diffMs / 1000)
|
||||
const diffMins = Math.floor(diffSecs / 60)
|
||||
const diffHours = Math.floor(diffMins / 60)
|
||||
const diffDays = Math.floor(diffHours / 24)
|
||||
|
||||
if (diffSecs < 60) {
|
||||
return '刚刚'
|
||||
} else if (diffMins < 60) {
|
||||
return `${diffMins}分钟前`
|
||||
} else if (diffHours < 24) {
|
||||
return `${diffHours}小时前`
|
||||
} else if (diffDays < 7) {
|
||||
return `${diffDays}天前`
|
||||
} else {
|
||||
return formatDate(date)
|
||||
}
|
||||
}
|
||||
|
||||
// 数字格式化
|
||||
export function formatNumber(num: number, precision = 2): string {
|
||||
return num.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: precision,
|
||||
maximumFractionDigits: precision,
|
||||
})
|
||||
}
|
||||
|
||||
// 货币格式化
|
||||
export function formatCurrency(amount: number): string {
|
||||
return new Intl.NumberFormat('zh-CN', {
|
||||
style: 'currency',
|
||||
currency: 'CNY',
|
||||
}).format(amount)
|
||||
}
|
||||
|
||||
// 百分比格式化
|
||||
export function formatPercentage(value: number, precision = 1): string {
|
||||
return `${(value * 100).toFixed(precision)}%`
|
||||
}
|
||||
|
||||
// 文件大小格式化
|
||||
export function formatFileSize(bytes: number): string {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let size = bytes
|
||||
let unitIndex = 0
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
|
||||
return `${formatNumber(size, unitIndex === 0 ? 0 : 2)} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
// 农机状态映射
|
||||
export const machineryStatusMap = {
|
||||
running: { label: '运行中', color: 'status-running' },
|
||||
idle: { label: '空闲中', color: 'status-idle' },
|
||||
maintenance: { label: '维护中', color: 'status-maintenance' },
|
||||
error: { label: '故障中', color: 'status-error' },
|
||||
offline: { label: '离线', color: 'status-offline' },
|
||||
} as const
|
||||
|
||||
// 获取农机状态信息
|
||||
export function getMachineryStatus(status: keyof typeof machineryStatusMap) {
|
||||
return machineryStatusMap[status] || { label: '未知', color: 'status-idle' }
|
||||
}
|
||||
|
||||
// 农作物类型映射
|
||||
export const cropTypeMap = {
|
||||
rice: { label: '水稻', icon: '🌾' },
|
||||
wheat: { label: '小麦', icon: '🌾' },
|
||||
corn: { label: '玉米', icon: '🌽' },
|
||||
soybean: { label: '大豆', icon: '🫘' },
|
||||
vegetable: { label: '蔬菜', icon: '🥬' },
|
||||
fruit: { label: '水果', icon: '🍎' },
|
||||
} as const
|
||||
|
||||
// 获取农作物信息
|
||||
export function getCropInfo(type: keyof typeof cropTypeMap) {
|
||||
return cropTypeMap[type] || { label: '未知', icon: '🌱' }
|
||||
}
|
||||
|
||||
// 防抖函数
|
||||
export function debounce<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: NodeJS.Timeout | null = null
|
||||
|
||||
return (...args: Parameters<T>) => {
|
||||
if (timeout !== null) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
timeout = setTimeout(() => func(...args), wait)
|
||||
}
|
||||
}
|
||||
|
||||
// 节流函数
|
||||
export function throttle<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
limit: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let inThrottle: boolean = false
|
||||
|
||||
return (...args: Parameters<T>) => {
|
||||
if (!inThrottle) {
|
||||
func(...args)
|
||||
inThrottle = true
|
||||
setTimeout(() => (inThrottle = false), limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 深拷贝
|
||||
export function deepClone<T>(obj: T): T {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as T
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item)) as T
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const clonedObj = {} as T
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
clonedObj[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
return clonedObj
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// 生成随机ID
|
||||
export function generateId(length = 8): string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
let result = ''
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 验证邮箱
|
||||
export function isValidEmail(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return emailRegex.test(email)
|
||||
}
|
||||
|
||||
// 验证手机号
|
||||
export function isValidPhone(phone: string): boolean {
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
return phoneRegex.test(phone)
|
||||
}
|
||||
|
||||
// 计算两个日期之间的天数差
|
||||
export function daysBetween(date1: Date | string, date2: Date | string): number {
|
||||
const d1 = new Date(date1)
|
||||
const d2 = new Date(date2)
|
||||
const diffTime = Math.abs(d2.getTime() - d1.getTime())
|
||||
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// 获取季节
|
||||
export function getSeason(date: Date | string = new Date()): string {
|
||||
const d = new Date(date)
|
||||
const month = d.getMonth() + 1
|
||||
|
||||
if (month >= 3 && month <= 5) return '春季'
|
||||
if (month >= 6 && month <= 8) return '夏季'
|
||||
if (month >= 9 && month <= 11) return '秋季'
|
||||
return '冬季'
|
||||
}
|
||||
10
crop-x/src/main.tsx
Normal file
10
crop-x/src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './styles/globals.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
1
crop-x/src/pages/NotFound.css
Normal file
1
crop-x/src/pages/NotFound.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 404页面样式 */
|
||||
14
crop-x/src/pages/NotFound.tsx
Normal file
14
crop-x/src/pages/NotFound.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const NotFound = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现404页面逻辑
|
||||
console.log('404页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>页面未找到</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/ServerError.css
Normal file
1
crop-x/src/pages/ServerError.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 服务器错误页面样式 */
|
||||
14
crop-x/src/pages/ServerError.tsx
Normal file
14
crop-x/src/pages/ServerError.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const ServerError = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现服务器错误页面逻辑
|
||||
console.log('服务器错误页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>服务器错误</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/ai-model/AIModelEntry.css
Normal file
1
crop-x/src/pages/ai-model/AIModelEntry.css
Normal file
@@ -0,0 +1 @@
|
||||
/* AI模型入口样式 */
|
||||
14
crop-x/src/pages/ai-model/AIModelEntry.tsx
Normal file
14
crop-x/src/pages/ai-model/AIModelEntry.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const AIModelEntry = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现AI模型入口逻辑
|
||||
console.log('AI模型入口页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>AI模型入口</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/ai-model/ModelTraining.css
Normal file
1
crop-x/src/pages/ai-model/ModelTraining.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 模型训练样式 */
|
||||
14
crop-x/src/pages/ai-model/ModelTraining.tsx
Normal file
14
crop-x/src/pages/ai-model/ModelTraining.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const ModelTraining = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现模型训练逻辑
|
||||
console.log('模型训练页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>模型训练</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/ai-model/PredictionAnalysis.css
Normal file
1
crop-x/src/pages/ai-model/PredictionAnalysis.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 预测分析样式 */
|
||||
14
crop-x/src/pages/ai-model/PredictionAnalysis.tsx
Normal file
14
crop-x/src/pages/ai-model/PredictionAnalysis.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const PredictionAnalysis = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现预测分析逻辑
|
||||
console.log('预测分析页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>预测分析</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/ai-model/RecommendationSystem.css
Normal file
1
crop-x/src/pages/ai-model/RecommendationSystem.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 推荐系统样式 */
|
||||
14
crop-x/src/pages/ai-model/RecommendationSystem.tsx
Normal file
14
crop-x/src/pages/ai-model/RecommendationSystem.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const RecommendationSystem = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现推荐系统逻辑
|
||||
console.log('推荐系统页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>推荐系统</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/asset/AssetDepreciation.css
Normal file
1
crop-x/src/pages/asset/AssetDepreciation.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 资产折旧样式 */
|
||||
14
crop-x/src/pages/asset/AssetDepreciation.tsx
Normal file
14
crop-x/src/pages/asset/AssetDepreciation.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const AssetDepreciation = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现资产折旧逻辑
|
||||
console.log('资产折旧页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>资产折旧</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/asset/AssetEntry.css
Normal file
1
crop-x/src/pages/asset/AssetEntry.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 资产管理入口样式 */
|
||||
14
crop-x/src/pages/asset/AssetEntry.tsx
Normal file
14
crop-x/src/pages/asset/AssetEntry.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const AssetEntry = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现资产管理入口逻辑
|
||||
console.log('资产管理入口页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>资产管理入口</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/asset/InventoryManagement.css
Normal file
1
crop-x/src/pages/asset/InventoryManagement.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 库存管理样式 */
|
||||
14
crop-x/src/pages/asset/InventoryManagement.tsx
Normal file
14
crop-x/src/pages/asset/InventoryManagement.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const InventoryManagement = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现库存管理逻辑
|
||||
console.log('库存管理页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>库存管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/asset/MaintenanceRecords.css
Normal file
1
crop-x/src/pages/asset/MaintenanceRecords.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 维护记录样式 */
|
||||
14
crop-x/src/pages/asset/MaintenanceRecords.tsx
Normal file
14
crop-x/src/pages/asset/MaintenanceRecords.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const MaintenanceRecords = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现维护记录逻辑
|
||||
console.log('维护记录页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>维护记录</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/auth/Login.css
Normal file
1
crop-x/src/pages/auth/Login.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 登录页面样式 */
|
||||
14
crop-x/src/pages/auth/Login.tsx
Normal file
14
crop-x/src/pages/auth/Login.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const Login = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现登录逻辑
|
||||
console.log('登录页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<h1>用户登录</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/CentralConfigEntry.css
Normal file
1
crop-x/src/pages/config/CentralConfigEntry.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 中心配置入口样式 */
|
||||
14
crop-x/src/pages/config/CentralConfigEntry.tsx
Normal file
14
crop-x/src/pages/config/CentralConfigEntry.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const CentralConfigEntry = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现中心配置入口逻辑
|
||||
console.log('中心配置入口页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>中心配置入口</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/MessageCenter.css
Normal file
1
crop-x/src/pages/config/MessageCenter.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 消息中心样式 */
|
||||
14
crop-x/src/pages/config/MessageCenter.tsx
Normal file
14
crop-x/src/pages/config/MessageCenter.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const MessageCenter = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现消息中心逻辑
|
||||
console.log('消息中心页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>消息中心</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/SystemMonitoring.css
Normal file
1
crop-x/src/pages/config/SystemMonitoring.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 系统监控样式 */
|
||||
14
crop-x/src/pages/config/SystemMonitoring.tsx
Normal file
14
crop-x/src/pages/config/SystemMonitoring.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SystemMonitoring = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现系统监控逻辑
|
||||
console.log('系统监控页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>系统监控</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/SystemParameters.css
Normal file
1
crop-x/src/pages/config/SystemParameters.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 系统参数样式 */
|
||||
14
crop-x/src/pages/config/SystemParameters.tsx
Normal file
14
crop-x/src/pages/config/SystemParameters.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SystemParameters = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现系统参数逻辑
|
||||
console.log('系统参数页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>系统参数</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/TenantManagement.css
Normal file
1
crop-x/src/pages/config/TenantManagement.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 租户管理样式 */
|
||||
14
crop-x/src/pages/config/TenantManagement.tsx
Normal file
14
crop-x/src/pages/config/TenantManagement.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const TenantManagement = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现租户管理逻辑
|
||||
console.log('租户管理页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>租户管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/config/UserManagement.css
Normal file
1
crop-x/src/pages/config/UserManagement.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 用户管理样式 */
|
||||
14
crop-x/src/pages/config/UserManagement.tsx
Normal file
14
crop-x/src/pages/config/UserManagement.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const UserManagement = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现用户管理逻辑
|
||||
console.log('用户管理页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>用户管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/dashboard/Dashboard.css
Normal file
1
crop-x/src/pages/dashboard/Dashboard.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 仪表板页面样式 */
|
||||
14
crop-x/src/pages/dashboard/Dashboard.tsx
Normal file
14
crop-x/src/pages/dashboard/Dashboard.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const Dashboard = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现仪表板逻辑
|
||||
console.log('仪表板页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>系统仪表板</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/irrigation/IrrigationEntry.css
Normal file
1
crop-x/src/pages/irrigation/IrrigationEntry.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 灌溉控制入口样式 */
|
||||
14
crop-x/src/pages/irrigation/IrrigationEntry.tsx
Normal file
14
crop-x/src/pages/irrigation/IrrigationEntry.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const IrrigationEntry = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现灌溉控制入口逻辑
|
||||
console.log('灌溉控制入口页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>灌溉控制入口</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/irrigation/MonitoringSystem.css
Normal file
1
crop-x/src/pages/irrigation/MonitoringSystem.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 监控系统样式 */
|
||||
14
crop-x/src/pages/irrigation/MonitoringSystem.tsx
Normal file
14
crop-x/src/pages/irrigation/MonitoringSystem.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const MonitoringSystem = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现监控系统逻辑
|
||||
console.log('监控系统页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>监控系统</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/irrigation/SchedulingSystem.css
Normal file
1
crop-x/src/pages/irrigation/SchedulingSystem.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 调度系统样式 */
|
||||
14
crop-x/src/pages/irrigation/SchedulingSystem.tsx
Normal file
14
crop-x/src/pages/irrigation/SchedulingSystem.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SchedulingSystem = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现调度系统逻辑
|
||||
console.log('调度系统页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>调度系统</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/irrigation/SystemControl.css
Normal file
1
crop-x/src/pages/irrigation/SystemControl.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 系统控制样式 */
|
||||
14
crop-x/src/pages/irrigation/SystemControl.tsx
Normal file
14
crop-x/src/pages/irrigation/SystemControl.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SystemControl = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现系统控制逻辑
|
||||
console.log('系统控制页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>系统控制</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/ComparativeAnalysis.css
Normal file
1
crop-x/src/pages/land/ComparativeAnalysis.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 对比分析样式 */
|
||||
14
crop-x/src/pages/land/ComparativeAnalysis.tsx
Normal file
14
crop-x/src/pages/land/ComparativeAnalysis.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const ComparativeAnalysis = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现对比分析逻辑
|
||||
console.log('对比分析页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>对比分析</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/LandArchiveManagement.css
Normal file
1
crop-x/src/pages/land/LandArchiveManagement.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 地块档案管理样式 */
|
||||
14
crop-x/src/pages/land/LandArchiveManagement.tsx
Normal file
14
crop-x/src/pages/land/LandArchiveManagement.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const LandArchiveManagement = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现地块档案管理逻辑
|
||||
console.log('地块档案管理页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>地块档案管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/LandClassification.css
Normal file
1
crop-x/src/pages/land/LandClassification.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 地块分类样式 */
|
||||
14
crop-x/src/pages/land/LandClassification.tsx
Normal file
14
crop-x/src/pages/land/LandClassification.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const LandClassification = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现地块分类逻辑
|
||||
console.log('地块分类页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>地块分类</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
crop-x/src/pages/land/LandEntry.tsx
Normal file
14
crop-x/src/pages/land/LandEntry.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const LandEntry = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现地块管理入口逻辑
|
||||
console.log('地块管理入口页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>地块管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/MapManagement.css
Normal file
1
crop-x/src/pages/land/MapManagement.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 地图管理样式 */
|
||||
14
crop-x/src/pages/land/MapManagement.tsx
Normal file
14
crop-x/src/pages/land/MapManagement.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const MapManagement = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现地图管理逻辑
|
||||
console.log('地图管理页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>地图管理</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/RiskWarning.css
Normal file
1
crop-x/src/pages/land/RiskWarning.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 风险预警样式 */
|
||||
14
crop-x/src/pages/land/RiskWarning.tsx
Normal file
14
crop-x/src/pages/land/RiskWarning.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const RiskWarning = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现风险预警逻辑
|
||||
console.log('风险预警页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>风险预警</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/SpatialAnalysis.css
Normal file
1
crop-x/src/pages/land/SpatialAnalysis.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 空间分析样式 */
|
||||
14
crop-x/src/pages/land/SpatialAnalysis.tsx
Normal file
14
crop-x/src/pages/land/SpatialAnalysis.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SpatialAnalysis = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现空间分析逻辑
|
||||
console.log('空间分析页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>空间分析</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
crop-x/src/pages/land/SuitabilityEvaluation.css
Normal file
1
crop-x/src/pages/land/SuitabilityEvaluation.css
Normal file
@@ -0,0 +1 @@
|
||||
/* 适宜性评价样式 */
|
||||
14
crop-x/src/pages/land/SuitabilityEvaluation.tsx
Normal file
14
crop-x/src/pages/land/SuitabilityEvaluation.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const SuitabilityEvaluation = () => {
|
||||
useEffect(() => {
|
||||
// TODO: 实现适宜性评价逻辑
|
||||
console.log('适宜性评价页面')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1>适宜性评价</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* CategoryForm 样式文件 */
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
|
||||
export function CategoryForm() {
|
||||
console.log('CategoryForm component rendered');
|
||||
return <div className="CategoryForm"><h4>CategoryForm</h4></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface CategoryFormProps {
|
||||
// Props will be defined later
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* CategoryTree 样式文件 */
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
|
||||
export function CategoryTree() {
|
||||
console.log('CategoryTree component rendered');
|
||||
return <div className="CategoryTree"><h4>CategoryTree</h4></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface CategoryTreeProps {
|
||||
// Props will be defined later
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* TagForm 样式文件 */
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
|
||||
export function TagForm() {
|
||||
console.log('TagForm component rendered');
|
||||
return <div className="TagForm"><h4>TagForm</h4></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface TagFormProps {
|
||||
// Props will be defined later
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* TagManager 样式文件 */
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
|
||||
export function TagManager() {
|
||||
console.log('TagManager component rendered');
|
||||
return <div className="TagManager"><h4>TagManager</h4></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface TagManagerProps {
|
||||
// Props will be defined later
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* TagSelector 样式文件 */
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
|
||||
export function TagSelector() {
|
||||
console.log('TagSelector component rendered');
|
||||
return <div className="TagSelector"><h4>TagSelector</h4></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface TagSelectorProps {
|
||||
// Props will be defined later
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const CATEGORY_COLORS = [
|
||||
"#1890ff",
|
||||
"#52c41a",
|
||||
"#fa8c16",
|
||||
"#722ed1"
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { Category, Tag } from "../index.types";
|
||||
|
||||
export function useClassification() {
|
||||
const [selectedCategory, setSelectedCategory] = useState<Category | null>(null);
|
||||
const [selectedTags, setSelectedTags] = useState<Tag[]>([]);
|
||||
|
||||
const handleCategorySelect = useCallback((category: Category) => {
|
||||
setSelectedCategory(category);
|
||||
}, []);
|
||||
|
||||
const handleTagToggle = useCallback((tag: Tag) => {
|
||||
setSelectedTags(prev =>
|
||||
prev.find(t => t.id === tag.id)
|
||||
? prev.filter(t => t.id \!== tag.id)
|
||||
: [...prev, tag]
|
||||
);
|
||||
}, []);
|
||||
|
||||
return { selectedCategory, selectedTags, handleCategorySelect, handleTagToggle };
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useCallback } from "react";
|
||||
import { Category, Tag } from "../index.types";
|
||||
|
||||
export function usePageActions(refreshData: () => void) {
|
||||
const handleCreateCategory = useCallback((category: Partial<Category>) => {
|
||||
console.log("Create category:", category);
|
||||
}, []);
|
||||
|
||||
const handleCreateTag = useCallback((tag: Partial<Tag>) => {
|
||||
console.log("Create tag:", tag);
|
||||
}, []);
|
||||
|
||||
return { handleCreateCategory, handleCreateTag };
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { Category, Tag } from "../index.types";
|
||||
|
||||
export function usePageData() {
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API调用
|
||||
setCategories([]);
|
||||
setTags([]);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => { fetchData(); }, [fetchData]);
|
||||
|
||||
return { categories, tags, loading, error, refreshData: fetchData };
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* MachineryClassification 样式文件 */
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export function MachineryClassification() {
|
||||
console.log("MachineryClassification component rendered");
|
||||
return <div className="machinery-classification"><h1>农机分类与标签管理</h1></div>;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
category?: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user