Compare commits

...

23 Commits

Author SHA1 Message Date
e14f03cf79 生产管理系统前端 - gis地图管理开发 2025-10-29 16:02:42 +08:00
9340252c25 生产管理系统 - 地块分类与标签管理,统计分析2个页面开发 2025-10-29 15:40:10 +08:00
df8e6bf515 生产管理系统前端 - 地块档案管理页面开发 2025-10-29 14:13:51 +08:00
5d34bc3643 生产管理系统前端 - 亮暗模式缓存问题修复 2025-10-28 20:39:04 +08:00
2f0196ae4a 生产管理系统 - 绿线修复 2025-10-28 20:33:22 +08:00
94f83d36ff 生产管理系统前端 - 水肥机控制页面框架搭建 2025-10-28 20:10:18 +08:00
e3829d2fcc 生产管理系统前端 - 瓦力0.73原型图提交 2025-10-28 19:51:17 +08:00
58f5ca7f22 生产管理系统前端 - AI作物模型精准决策系统 框架搭建 2025-10-28 19:46:50 +08:00
7a0096caed 生产管理系统前端 - 农业资产管理系统框架搭建 2025-10-28 17:51:07 +08:00
3fc8f883cf 生产管理系统前端 - 农事操作管理页面搭建&白天晚上切换 2025-10-28 17:20:41 +08:00
0b6ae9fc5c 生产管理系统前端 - 地块信息管理系统、智能农机管理系统页面空壳子提交 2025-10-28 16:49:02 +08:00
b907cc4299 生产管理系统前端 - 瓦力0.71原型图更新 2025-10-28 15:26:08 +08:00
26213aaa76 生产管理系统前端 提交个人中心2个页面开发 2025-10-28 15:22:54 +08:00
2c3227fb64 生产管理系统前端 - 发布脚本统一用一个环境文件 2025-10-28 14:22:26 +08:00
3286d4366a 生产管理系统前端 - 环境变量配置 2025-10-28 14:11:53 +08:00
4aae686264 生产管理系统前端 - 测试环境接入测试后台服务器 2025-10-28 10:54:52 +08:00
88c8bbb2a7 生产管理系前端 - 删除不需要的文件夹,比如apis和utils 2025-10-28 09:55:58 +08:00
59a9743992 生产管理系统 前端不需要的文件删除 2025-10-28 09:40:08 +08:00
42a4a9f566 生产管理系统前端 - openapi - fetch生成器开发 2025-10-27 21:53:18 +08:00
5055e40de6 生产管理系统前端 - 页面主区域滚动条fix 2025-10-27 11:46:35 +08:00
c0ea1fb9f3 生产管理系统前端 - 提交边框颜色修改 2025-10-27 11:16:12 +08:00
2b39c1dd1a 生产管理系统前端 - fetchapi 基础提交 2025-10-27 11:08:23 +08:00
1f1d94ed84 生产管理系统前端 - 瓦力提交代码&文档更新 2025-10-25 16:11:15 +08:00
792 changed files with 246039 additions and 15387 deletions

View File

@@ -1,3 +1,4 @@
# <!-- Powered by BMAD™ Core --> # <!-- Powered by BMAD™ Core -->
template: template:
id: competitor-analysis-template-v2 id: competitor-analysis-template-v2

1
.gitignore vendored
View File

@@ -147,3 +147,4 @@ Thumbs.db
tmp/ tmp/
temp/ temp/
nul nul
/nextjs-frontend

35
crop-x/.env.example Normal file
View File

@@ -0,0 +1,35 @@
# 环境配置示例文件
# 复制此文件为 .env.local 并根据实际情况修改配置
# 当前环境: development, test, production
NODE_ENV=development
# API 服务器地址 (用于 API 代码生成)
API_BASE_URL=http://localhost:8080
# React 应用配置 (用于前端运行时)
REACT_APP_API_URL=http://localhost:8080
# OpenAPI 文档地址
REACT_APP_OPENAPI_URL=http://localhost:8080/openapi.json
# 其他可选配置
# REACT_APP_API_KEY=your-api-key-here
# REACT_APP_DEBUG=true
# 不同环境配置示例:
#
# 开发环境:
# NODE_ENV=development
# API_BASE_URL=http://localhost:8080
# REACT_APP_API_URL=http://localhost:8080
#
# 测试环境:
# NODE_ENV=test
# API_BASE_URL=http://test-api.example.com
# REACT_APP_API_URL=http://test-api.example.com
#
# 生产环境:
# NODE_ENV=production
# API_BASE_URL=https://api.example.com
# REACT_APP_API_URL=https://api.example.com

113
crop-x/.gitignore vendored Normal file
View File

@@ -0,0 +1,113 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Production builds
.next/
out/
dist/
build/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# API 相关文件
# 忽略从服务器下载的临时 OpenAPI JSON 文件
api/v1-from-server.json
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
# Storybook build outputs
.out
.storybook-out
# Temporary folders
tmp/
temp/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Local development
.local
# TypeScript
*.tsbuildinfo

View File

@@ -1,126 +0,0 @@
/**
* API 统一入口文件
* 根据不同环境配置不同的API域名和端口
* 统一暴露所有模块的API接口
*/
import { createAPI } from './interceptor.js';
// 环境配置
const ENV_CONFIG = {
development: {
baseURL: 'http://localhost:8080',
timeout: 10000,
enableLogging: true
},
test: {
baseURL: 'http://test-api.smart-crop.com',
timeout: 15000,
enableLogging: true
},
production: {
baseURL: 'https://api.smart-crop.com',
timeout: 20000,
enableLogging: false
}
};
// 获取当前环境
const getEnvironment = () => {
const env = import.meta.env?.MODE || 'development';
// 可以根据其他条件判断环境
if (env === 'production') return 'production';
if (env === 'test') return 'test';
return 'development';
};
// 创建API实例
const currentEnv = getEnvironment();
const envConfig = ENV_CONFIG[currentEnv];
console.log(`🚀 API Environment: ${currentEnv}`);
console.log(`🌐 API Base URL: ${envConfig.baseURL}`);
// 创建API实例
export const api = createAPI({
baseURL: envConfig.baseURL,
timeout: envConfig.timeout,
enableLogging: envConfig.enableLogging
});
// 导入各模块API
import { agriculturalMachineryAPI } from './subModules/agriculturalMachinery.js';
import { landInformationAPI } from './subModules/landInformation.js';
import { farmingOperationAPI } from './subModules/farmingOperation.js';
import { agriculturalAssetAPI } from './subModules/agriculturalAsset.js';
import { aiCropModelAPI } from './subModules/aiCropModel.js';
import { waterFertilizerControlAPI } from './subModules/waterFertilizerControl.js';
import { centralConfigAPI } from './subModules/centralConfig.js';
// 统一导出所有API
export const API = {
// 农机管理模块
agriculturalMachinery: agriculturalMachineryAPI(api),
// 地块信息模块
landInformation: landInformationAPI(api),
// 农事操作模块
farmingOperation: farmingOperationAPI(api),
// 农业资产模块
agriculturalAsset: agriculturalAssetAPI(api),
// AI作物模型模块
aiCropModel: aiCropModelAPI(api),
// 水肥控制模块
waterFertilizerControl: waterFertilizerControlAPI(api),
// 中心配置模块
centralConfig: centralConfigAPI(api),
// 认证相关API (直接暴露,不需要模块化)
auth: {
login: (credentials) => api.post('/auth/login', credentials),
register: (userInfo) => api.post('/auth/register', userInfo),
logout: () => api.post('/auth/logout'),
refreshToken: (refreshToken) => api.post('/auth/refresh-token', { refreshToken }),
getCurrentUser: () => api.get('/auth/me'),
updateProfile: (profileData) => api.put('/auth/profile', profileData),
changePassword: (passwordData) => api.put('/auth/change-password', passwordData)
},
// 通用API
common: {
uploadFile: (fileData) => api.post('/common/upload', fileData, {
headers: { 'Content-Type': 'multipart/form-data' }
}),
downloadFile: (fileId) => api.get(`/common/download/${fileId}`, {
responseType: 'blob'
}),
getSystemConfig: () => api.get('/common/system-config'),
healthCheck: () => api.get('/common/health')
}
};
// 导出默认API实例和配置
export default API;
// 导出环境信息(供调试使用)
export { currentEnv, envConfig };
// API使用示例
/*
import { API } from '@/apis';
// 使用农机管理API
const machineryList = await API.agriculturalMachinery.getMachineryList();
// 使用认证API
const loginResult = await API.auth.login({ username, password });
// 使用通用API
const uploadResult = await API.common.uploadFile(formData);
*/

View File

@@ -1,285 +0,0 @@
/**
* API 请求拦截器
* 功能:
* 1. 为除登录接口外的所有请求添加身份信息
* 2. 统一错误处理
* 3. 请求/响应日志记录
* 4. 自动刷新token
*/
import axios from 'axios';
import { toast } from 'sonner';
/**
* 创建API实例并配置拦截器
*/
export const createAPI = ({ baseURL, timeout = 10000, enableLogging = false }) => {
// 创建axios实例
const api = axios.create({
baseURL,
timeout,
headers: {
'Content-Type': 'application/json',
},
});
// 获取存储的token和用户信息
const getAuthData = () => {
try {
const authData = localStorage.getItem('authData');
if (authData) {
const { token, user } = JSON.parse(authData);
return { token, user_id: user?.id };
}
} catch (error) {
console.warn('Failed to parse auth data from localStorage:', error);
}
return { token: null, user_id: null };
};
// 请求拦截器
api.interceptors.request.use(
(config) => {
// 添加请求日志
if (enableLogging) {
console.log(`🚀 API Request: ${config.method?.toUpperCase()} ${config.url}`, {
data: config.data,
params: config.params,
});
}
// 为除登录接口外的所有请求添加身份信息
const isLoginRequest = config.url?.includes('/auth/login');
const isRegisterRequest = config.url?.includes('/auth/register');
if (!isLoginRequest && !isRegisterRequest) {
const { token, user_id } = getAuthData();
if (token) {
config.headers['auth'] = token; // JWT token
}
if (user_id) {
config.headers['user_id'] = user_id; // 用户ID
}
}
// 添加请求时间戳
config.metadata = { startTime: new Date() };
return config;
},
(error) => {
console.error('❌ Request Error:', error);
return Promise.reject(error);
}
);
// 响应拦截器
api.interceptors.response.use(
(response) => {
// 添加响应日志
if (enableLogging) {
const duration = new Date() - response.config.metadata.startTime;
console.log(`✅ API Response: ${response.config.method?.toUpperCase()} ${response.config.url}`, {
status: response.status,
duration: `${duration}ms`,
data: response.data,
});
}
// 统一处理成功响应格式
if (response.data && typeof response.data === 'object') {
// 如果后端返回统一格式 { code, message, data }
if ('code' in response.data) {
const { code, message, data } = response.data;
if (code === 200 || code === 0) {
return { ...response, data }; // 返回实际数据
} else {
// 业务错误处理
toast.error(message || '请求失败');
return Promise.reject(new Error(message || '请求失败'));
}
}
}
return response;
},
async (error) => {
const originalRequest = error.config;
// 添加错误日志
if (enableLogging) {
const duration = originalRequest?.metadata?.startTime
? `${new Date() - originalRequest.metadata.startTime}ms`
: 'N/A';
console.error(`❌ API Error: ${originalRequest?.method?.toUpperCase()} ${originalRequest?.url}`, {
status: error.response?.status,
duration,
message: error.message,
data: error.response?.data,
});
}
// 处理不同类型的错误
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
// 未授权 - token过期或无效
return handleUnauthorizedError(originalRequest);
case 403:
// 禁止访问 - 权限不足
toast.error('您没有权限访问此资源');
break;
case 404:
// 资源不存在
toast.error('请求的资源不存在');
break;
case 422:
// 表单验证错误
if (data?.errors) {
Object.values(data.errors).flat().forEach(errorMessage => {
toast.error(errorMessage);
});
} else {
toast.error(data?.message || '请求参数错误');
}
break;
case 429:
// 请求过于频繁
toast.error('请求过于频繁,请稍后再试');
break;
case 500:
// 服务器内部错误
toast.error('服务器内部错误,请稍后再试');
break;
default:
// 其他错误
toast.error(data?.message || `请求失败 (${status})`);
}
} else if (error.request) {
// 网络错误
if (error.code === 'ECONNABORTED') {
toast.error('请求超时,请检查网络连接');
} else {
toast.error('网络连接失败,请检查网络');
}
} else {
// 其他错误
toast.error('请求配置错误');
}
return Promise.reject(error);
}
);
return api;
};
/**
* 处理401未授权错误
* 尝试刷新token或跳转到登录页
*/
const handleUnauthorizedError = async (originalRequest) => {
// 避免重复刷新token
if (originalRequest._retry) {
// 刷新失败,清除本地存储并跳转到登录页
localStorage.removeItem('authData');
window.location.href = '/login';
return Promise.reject(new Error('登录已过期,请重新登录'));
}
originalRequest._retry = true;
try {
// 尝试刷新token
const authData = JSON.parse(localStorage.getItem('authData') || '{}');
const refreshToken = authData.refreshToken;
if (refreshToken) {
const response = await axios.post(`${originalRequest.baseURL}/auth/refresh-token`, {
refreshToken
});
const { token, user } = response.data.data;
// 更新本地存储
localStorage.setItem('authData', JSON.stringify({
token,
refreshToken,
user
}));
// 重新发送原始请求
originalRequest.headers['auth'] = token;
originalRequest.headers['user_id'] = user.id;
return axios(originalRequest);
} else {
// 没有刷新token跳转到登录页
localStorage.removeItem('authData');
window.location.href = '/login';
return Promise.reject(new Error('登录已过期,请重新登录'));
}
} catch (refreshError) {
// 刷新token失败
localStorage.removeItem('authData');
window.location.href = '/login';
return Promise.reject(new Error('登录已过期,请重新登录'));
}
};
/**
* 设置认证信息
* @param {string} token JWT token
* @param {object} user 用户信息
* @param {string} refreshToken 刷新token
*/
export const setAuthData = (token, user, refreshToken) => {
const authData = {
token,
refreshToken: refreshToken || '',
user
};
localStorage.setItem('authData', JSON.stringify(authData));
};
/**
* 清除认证信息
*/
export const clearAuthData = () => {
localStorage.removeItem('authData');
};
/**
* 获取当前认证信息
*/
export const getAuthData = () => {
try {
const authData = localStorage.getItem('authData');
return authData ? JSON.parse(authData) : null;
} catch (error) {
console.warn('Failed to get auth data:', error);
return null;
}
};
/**
* 检查是否已登录
*/
export const isAuthenticated = () => {
const authData = getAuthData();
return !!(authData?.token && authData?.user);
};
export default createAPI;

View File

@@ -1,333 +0,0 @@
/**
* 智能农机管理系统 API 接口
* AgriculturalMachinery System API
*/
/**
* 农机档案管理 API
*/
export const createMachineryArchiveAPI = (api) => ({
// 获取农机列表
getMachineryList: (params = {}) => {
return api.get('/agricultural/machinery/archive/list', { params });
},
// 获取农机详情
getMachineryDetail: (id) => {
return api.get(`/agricultural/machinery/archive/detail/${id}`);
},
// 创建农机档案
createMachinery: (machineryData) => {
return api.post('/agricultural/machinery/archive/create', machineryData);
},
// 更新农机档案
updateMachinery: (id, machineryData) => {
return api.put(`/agricultural/machinery/archive/update/${id}`, machineryData);
},
// 删除农机档案
deleteMachinery: (id) => {
return api.delete(`/agricultural/machinery/archive/delete/${id}`);
},
// 批量删除农机
batchDeleteMachinery: (ids) => {
return api.post('/agricultural/machinery/archive/batch-delete', { ids });
},
// 农机分类管理
getMachineryCategories: () => {
return api.get('/agricultural/machinery/archive/categories');
},
createMachineryCategory: (categoryData) => {
return api.post('/agricultural/machinery/archive/categories/create', categoryData);
},
// QR码管理
generateQRCode: (machineryId) => {
return api.post(`/agricultural/machinery/archive/qrcode/generate/${machineryId}`);
},
downloadQRCode: (machineryId) => {
return api.get(`/agricultural/machinery/archive/qrcode/download/${machineryId}`, {
responseType: 'blob'
});
}
});
/**
* 驾驶员档案管理 API
*/
export const createDriverArchiveAPI = (api) => ({
// 获取驾驶员列表
getDriverList: (params = {}) => {
return api.get('/agricultural/driver/archive/list', { params });
},
// 获取驾驶员详情
getDriverDetail: (id) => {
return api.get(`/agricultural/driver/archive/detail/${id}`);
},
// 创建驾驶员档案
createDriver: (driverData) => {
return api.post('/agricultural/driver/archive/create', driverData);
},
// 更新驾驶员档案
updateDriver: (id, driverData) => {
return api.put(`/agricultural/driver/archive/update/${id}`, driverData);
},
// 删除驾驶员档案
deleteDriver: (id) => {
return api.delete(`/agricultural/driver/archive/delete/${id}`);
},
// 驾驶员任务管理
getDriverTasks: (driverId, params = {}) => {
return api.get(`/agricultural/driver/tasks/${driverId}`, { params });
},
assignTaskToDriver: (driverId, taskData) => {
return api.post(`/agricultural/driver/tasks/assign/${driverId}`, taskData);
}
});
/**
* 负载管理 API
*/
export const createLoadManagementAPI = (api) => ({
// 获取负载设备列表
getLoadDevices: (params = {}) => {
return api.get('/agricultural/load/devices/list', { params });
},
// 创建负载设备
createLoadDevice: (deviceData) => {
return api.post('/agricultural/load/devices/create', deviceData);
},
// 获取负载类型
getLoadTypes: () => {
return api.get('/agricultural/load/types');
},
// 获取负载参数
getLoadParameters: (deviceId) => {
return api.get(`/agricultural/load/parameters/${deviceId}`);
},
// 设置负载参数
setLoadParameters: (deviceId, parameters) => {
return api.put(`/agricultural/load/parameters/${deviceId}`, parameters);
}
});
/**
* 实时监控 API
*/
export const createMonitoringAPI = (api) => ({
// 获取实时位置
getRealTimeLocation: (machineryId) => {
return api.get(`/agricultural/monitoring/location/${machineryId}`);
},
// 获取多个设备位置
getBatchLocations: (machineryIds) => {
return api.post('/agricultural/monitoring/locations/batch', { machineryIds });
},
// 获取工作状态
getWorkStatus: (machineryId) => {
return api.get(`/agricultural/monitoring/status/${machineryId}`);
},
// 获取作业数据
getOperationData: (machineryId, params = {}) => {
return api.get(`/agricultural/monitoring/operation-data/${machineryId}`, { params });
},
// 历史轨迹
getHistoryTracks: (machineryId, params = {}) => {
return api.get(`/agricultural/monitoring/tracks/${machineryId}`, { params });
}
});
/**
* 故障诊断 API
*/
export const createFaultDiagnosisAPI = (api) => ({
// 获取故障列表
getFaultList: (params = {}) => {
return api.get('/agricultural/fault/list', { params });
},
// 获取故障详情
getFaultDetail: (faultId) => {
return api.get(`/agricultural/fault/detail/${faultId}`);
},
// 创建故障报告
createFaultReport: (faultData) => {
return api.post('/agricultural/fault/report', faultData);
},
// 获取健康评估
getHealthAssessment: (machineryId) => {
return api.get(`/agricultural/fault/health/${machineryId}`);
},
// 获取运行参数
getRunningParameters: (machineryId) => {
return api.get(`/agricultural/fault/parameters/${machineryId}`);
}
});
/**
* 精准作业 API
*/
export const createPrecisionOperationAPI = (api) => ({
// 获取作业记录
getOperationRecords: (params = {}) => {
return api.get('/agricultural/operation/records', { params });
},
// 创建作业记录
createOperationRecord: (recordData) => {
return api.post('/agricultural/operation/records/create', recordData);
},
// 路线规划
planRoute: (routeData) => {
return api.post('/agricultural/operation/route/plan', routeData);
},
// 获取路线
getRoute: (routeId) => {
return api.get(`/agricultural/operation/route/${routeId}`);
},
// 方案下发
dispatchPlan: (planData) => {
return api.post('/agricultural/operation/dispatch', planData);
},
// 驾驶舱数据
getCockpitData: (machineryId) => {
return api.get(`/agricultural/operation/cockpit/${machineryId}`);
}
});
/**
* 数据分析 API
*/
export const createDataAnalysisAPI = (api) => ({
// 作业数据分析
getOperationAnalysis: (params = {}) => {
return api.get('/agricultural/analysis/operation', { params });
},
// 历史数据对比
getHistoricalComparison: (params = {}) => {
return api.get('/agricultural/analysis/history', { params });
},
// 统计报表
getStatisticsReport: (params = {}) => {
return api.get('/agricultural/analysis/statistics', { params });
},
// 导出报表
exportReport: (reportType, params = {}) => {
return api.get(`/agricultural/analysis/export/${reportType}`, {
params,
responseType: 'blob'
});
}
});
/**
* 调度管理 API
*/
export const createSchedulingAPI = (api) => ({
// 任务分配
getTaskAssignments: (params = {}) => {
return api.get('/agricultural/scheduling/assignments', { params });
},
// 创建任务分配
createTaskAssignment: (assignmentData) => {
return api.post('/agricultural/scheduling/assignments/create', assignmentData);
},
// 实时调度
getRealTimeDispatch: (params = {}) => {
return api.get('/agricultural/scheduling/dispatch/realtime', { params });
},
// 轨迹回放
getTrackPlayback: (machineryId, params = {}) => {
return api.get(`/agricultural/scheduling/playback/${machineryId}`, { params });
}
});
/**
* 安全安防 API
*/
export const createSecurityAPI = (api) => ({
// 电子围栏
getGeoFences: (params = {}) => {
return api.get('/agricultural/security/geo-fence', { params });
},
// 创建电子围栏
createGeoFence: (fenceData) => {
return api.post('/agricultural/security/geo-fence/create', fenceData);
},
// 更新电子围栏
updateGeoFence: (fenceId, fenceData) => {
return api.put(`/agricultural/security/geo-fence/${fenceId}`, fenceData);
},
// 删除电子围栏
deleteGeoFence: (fenceId) => {
return api.delete(`/agricultural/security/geo-fence/${fenceId}`);
}
});
/**
* 导出农机管理系统的所有API
*/
export const agriculturalMachineryAPI = (api) => ({
// 农机档案
archive: createMachineryArchiveAPI(api),
// 驾驶员档案
driver: createDriverArchiveAPI(api),
// 负载管理
load: createLoadManagementAPI(api),
// 实时监控
monitoring: createMonitoringAPI(api),
// 故障诊断
fault: createFaultDiagnosisAPI(api),
// 精准作业
operation: createPrecisionOperationAPI(api),
// 数据分析
analysis: createDataAnalysisAPI(api),
// 调度管理
scheduling: createSchedulingAPI(api),
// 安全安防
security: createSecurityAPI(api)
});

View File

@@ -1,357 +0,0 @@
/**
* 农事操作管理系统 API 接口
* Farming Operation System API
*/
/**
* 农事计划 API
*/
export const createFarmingPlanAPI = (api) => ({
// 计划制定
getFarmingPlans: (params = {}) => {
return api.get('/farming/operation/planning/list', { params });
},
createFarmingPlan: (planData) => {
return api.post('/farming/operation/planning/create', planData);
},
updateFarmingPlan: (planId, planData) => {
return api.put(`/farming/operation/planning/update/${planId}`, planData);
},
deleteFarmingPlan: (planId) => {
return api.delete(`/farming/operation/planning/delete/${planId}`);
},
// 资源分配规划
getResourceAllocation: (planId) => {
return api.get(`/farming/operation/planning/allocation/${planId}`);
},
createResourceAllocation: (allocationData) => {
return api.post('/farming/operation/planning/allocation/create', allocationData);
},
// 计划进度跟踪
getPlanProgress: (planId) => {
return api.get(`/farming/operation/planning/progress/${planId}`);
},
updatePlanProgress: (planId, progressData) => {
return api.put(`/farming/operation/planning/progress/${planId}`, progressData);
}
});
/**
* 农事任务 API
*/
export const createFarmingTaskAPI = (api) => ({
// 任务管理
getFarmingTasks: (params = {}) => {
return api.get('/farming/operation/task/list', { params });
},
createFarmingTask: (taskData) => {
return api.post('/farming/operation/task/create', taskData);
},
updateFarmingTask: (taskId, taskData) => {
return api.put(`/farming/operation/task/update/${taskId}`, taskData);
},
deleteFarmingTask: (taskId) => {
return api.delete(`/farming/operation/task/delete/${taskId}`);
},
// 任务分配与派发
assignTask: (taskId, assignmentData) => {
return api.post(`/farming/operation/task/assign/${taskId}`, assignmentData);
},
dispatchTask: (taskId, dispatchData) => {
return api.post(`/farming/operation/task/dispatch/${taskId}`, dispatchData);
},
// 任务状态监控
getTaskStatus: (taskId) => {
return api.get(`/farming/operation/task/status/${taskId}`);
},
updateTaskStatus: (taskId, statusData) => {
return api.put(`/farming/operation/task/status/${taskId}`, statusData);
},
// 历史与统计
getTaskHistory: (params = {}) => {
return api.get('/farming/operation/task/history', { params });
},
getTaskStatistics: (params = {}) => {
return api.get('/farming/operation/task/statistics', { params });
}
});
/**
* 农事执行 API
*/
export const createFarmingExecutionAPI = (api) => ({
// 农事类型
getFarmingTypes: () => {
return api.get('/farming/operation/execution/types');
},
createFarmingType: (typeData) => {
return api.post('/farming/operation/execution/types/create', typeData);
},
// 操作录入
createOperationRecord: (recordData) => {
return api.post('/farming/operation/execution/record/create', recordData);
},
getOperationRecords: (params = {}) => {
return api.get('/farming/operation/execution/records', { params });
},
updateOperationRecord: (recordId, recordData) => {
return api.put(`/farming/operation/execution/record/${recordId}`, recordData);
},
// 日志多维查询
queryOperationLogs: (queryParams) => {
return api.post('/farming/operation/execution/logs/query', queryParams);
},
getOperationLogs: (params = {}) => {
return api.get('/farming/operation/execution/logs', { params });
}
});
/**
* 农事日历 API
*/
export const createFarmingCalendarAPI = (api) => ({
// 可视化视图
getCalendarView: (params = {}) => {
return api.get('/farming/operation/calendar/view', { params });
},
// 甘特图
getGanttChart: (params = {}) => {
return api.get('/farming/operation/calendar/gantt', { params });
},
updateGanttChart: (chartData) => {
return api.put('/farming/operation/calendar/gantt/update', chartData);
},
// 进度状态可视化
getProgressVisualization: (params = {}) => {
return api.get('/farming/operation/calendar/progress', { params });
},
// 日历事件管理
createCalendarEvent: (eventData) => {
return api.post('/farming/operation/calendar/event/create', eventData);
},
updateCalendarEvent: (eventId, eventData) => {
return api.put(`/farming/operation/calendar/event/${eventId}`, eventData);
},
deleteCalendarEvent: (eventId) => {
return api.delete(`/farming/operation/calendar/event/${eventId}`);
}
});
/**
* 农事档案 API
*/
export const createFarmingArchiveAPI = (api) => ({
// 档案归集与生成
getArchives: (params = {}) => {
return api.get('/farming/operation/archive/list', { params });
},
generateArchive: (archiveConfig) => {
return api.post('/farming/operation/archive/generate', archiveConfig);
},
// 全维度数据视图
getArchiveView: (archiveId) => {
return api.get(`/farming/operation/archive/view/${archiveId}`);
},
updateArchiveView: (archiveId, viewData) => {
return api.put(`/farming/operation/archive/view/${archiveId}`, viewData);
},
// 追踪与溯源
getTraceability: (params = {}) => {
return api.get('/farming/operation/archive/traceability', { params });
},
createTraceabilityRecord: (traceData) => {
return api.post('/farming/operation/archive/traceability/create', traceData);
}
});
/**
* 农事知识库 API
*/
export const createFarmingKnowledgeAPI = (api) => ({
// 多模态知识内容管理
getKnowledgeBase: (params = {}) => {
return api.get('/farming/operation/knowledge/list', { params });
},
createKnowledgeContent: (contentData) => {
return api.post('/farming/operation/knowledge/create', contentData);
},
updateKnowledgeContent: (knowledgeId, contentData) => {
return api.put(`/farming/operation/knowledge/update/${knowledgeId}`, contentData);
},
deleteKnowledgeContent: (knowledgeId) => {
return api.delete(`/farming/operation/knowledge/delete/${knowledgeId}`);
},
// 分类与标签
getKnowledgeCategories: () => {
return api.get('/farming/operation/knowledge/categories');
},
getKnowledgeTags: () => {
return api.get('/farming/operation/knowledge/tags');
},
// 智能检索
searchKnowledge: (searchData) => {
return api.post('/farming/operation/knowledge/search', searchData);
},
getSearchSuggestions: (query) => {
return api.get('/farming/operation/knowledge/suggestions', { params: { query } });
}
});
/**
* 绩效管理 API
*/
export const createFarmingPerformanceAPI = (api) => ({
// 人员管理
getStaffList: (params = {}) => {
return api.get('/farming/operation/performance/staff', { params });
},
createStaff: (staffData) => {
return api.post('/farming/operation/performance/staff/create', staffData);
},
updateStaff: (staffId, staffData) => {
return api.put(`/farming/operation/performance/staff/${staffId}`, staffData);
},
// 工时记录
getWorkHours: (params = {}) => {
return api.get('/farming/operation/performance/hours', { params });
},
createWorkHourRecord: (hourData) => {
return api.post('/farming/operation/performance/hours/create', hourData);
},
// 绩效统计
getPerformanceStatistics: (params = {}) => {
return api.get('/farming/operation/performance/statistics', { params });
},
generatePerformanceReport: (reportConfig) => {
return api.post('/farming/operation/performance/report/generate', reportConfig);
},
// 排班管理
getScheduleList: (params = {}) => {
return api.get('/farming/operation/performance/schedule', { params });
},
createSchedule: (scheduleData) => {
return api.post('/farming/operation/performance/schedule/create', scheduleData);
},
updateSchedule: (scheduleId, scheduleData) => {
return api.put(`/farming/operation/performance/schedule/${scheduleId}`, scheduleData);
}
});
/**
* 农事问题协同 API
*/
export const createFarmingIssueAPI = (api) => ({
// 问题一键上报
reportIssue: (issueData) => {
return api.post('/farming/operation/issue/report', issueData);
},
getIssueList: (params = {}) => {
return api.get('/farming/operation/issue/list', { params });
},
// 问题处理与分派
assignIssue: (issueId, assignmentData) => {
return api.post(`/farming/operation/issue/assign/${issueId}`, assignmentData);
},
updateIssueStatus: (issueId, statusData) => {
return api.put(`/farming/operation/issue/status/${issueId}`, statusData);
},
// 在线协同
getCollaborationData: (issueId) => {
return api.get(`/farming/operation/issue/collaboration/${issueId}`);
},
createCollaborationRecord: (collaborationData) => {
return api.post('/farming/operation/issue/collaboration/create', collaborationData);
},
// 问题解决方案
getIssueSolutions: (params = {}) => {
return api.get('/farming/operation/issue/solutions', { params });
},
createSolution: (solutionData) => {
return api.post('/farming/operation/issue/solution/create', solutionData);
}
});
/**
* 导出农事操作管理系统的所有API
*/
export const farmingOperationAPI = (api) => ({
// 农事计划
planning: createFarmingPlanAPI(api),
// 农事任务
task: createFarmingTaskAPI(api),
// 农事执行
execution: createFarmingExecutionAPI(api),
// 农事日历
calendar: createFarmingCalendarAPI(api),
// 农事档案
archive: createFarmingArchiveAPI(api),
// 农事知识库
knowledge: createFarmingKnowledgeAPI(api),
// 绩效管理
performance: createFarmingPerformanceAPI(api),
// 农事问题协同
issue: createFarmingIssueAPI(api)
});

View File

@@ -1,299 +0,0 @@
/**
* 地块信息管理系统 API 接口
* Land Information System API
*/
/**
* 地块档案管理 API
*/
export const createFieldArchiveAPI = (api) => ({
// 获取地块列表
getFieldList: (params = {}) => {
return api.get('/land/field/archive/list', { params });
},
// 获取地块详情
getFieldDetail: (fieldId) => {
return api.get(`/land/field/archive/detail/${fieldId}`);
},
// 创建地块档案
createField: (fieldData) => {
return api.post('/land/field/archive/create', fieldData);
},
// 更新地块档案
updateField: (fieldId, fieldData) => {
return api.put(`/land/field/archive/update/${fieldId}`, fieldData);
},
// 删除地块档案
deleteField: (fieldId) => {
return api.delete(`/land/field/archive/delete/${fieldId}`);
},
// 地块分类标签
getFieldCategories: () => {
return api.get('/land/field/archive/categories');
},
getFieldTags: () => {
return api.get('/land/field/archive/tags');
},
// 批量操作
batchDeleteFields: (fieldIds) => {
return api.post('/land/field/archive/batch-delete', { fieldIds });
}
});
/**
* 地块数字化地图管理 API
*/
export const createFieldMapAPI = (api) => ({
// GIS地图管理
getGISMapData: (params = {}) => {
return api.get('/land/field/map/gis', { params });
},
// 上传地块边界数据
uploadFieldBoundary: (fieldId, boundaryData) => {
return api.post(`/land/field/map/boundary/upload/${fieldId}`, boundaryData);
},
// 数字化绘制与编辑
saveFieldDrawing: (drawingData) => {
return api.post('/land/field/map/drawing/save', drawingData);
},
getFieldDrawing: (fieldId) => {
return api.get(`/land/field/map/drawing/${fieldId}`);
},
// 空间数据管理
getSpatialData: (params = {}) => {
return api.get('/land/field/map/spatial-data', { params });
},
// 空间查询
spatialQuery: (queryData) => {
return api.post('/land/field/map/spatial-query', queryData);
},
// 地块影像
getSatelliteImages: (fieldId, params = {}) => {
return api.get(`/land/field/map/satellite/${fieldId}`, { params });
},
// 地图瓦片
getMapTiles: (params = {}) => {
return api.get('/land/field/map/tiles', { params });
}
});
/**
* 空间分析与决策支持 API
*/
export const createFieldAnalysisAPI = (api) => ({
// 土壤基础数据
getSoilData: (fieldId) => {
return api.get(`/land/field/analysis/soil-data/${fieldId}`);
},
updateSoilData: (fieldId, soilData) => {
return api.put(`/land/field/analysis/soil-data/${fieldId}`, soilData);
},
// 分层采样分析
getLayerSampling: (fieldId, params = {}) => {
return api.get(`/land/field/analysis/layer-sampling/${fieldId}`, { params });
},
createLayerSampling: (samplingData) => {
return api.post('/land/field/analysis/layer-sampling/create', samplingData);
},
// 土壤质量评价
getSoilQualityAssessment: (fieldId) => {
return api.get(`/land/field/analysis/soil-quality/${fieldId}`);
},
generateSoilQualityReport: (fieldId) => {
return api.post(`/land/field/analysis/soil-quality/report/${fieldId}`);
}
});
/**
* 地块环境监测 API
*/
export const createFieldMonitoringAPI = (api) => ({
// 气象监测
getWeatherData: (params = {}) => {
return api.get('/land/field/monitoring/weather', { params });
},
getWeatherForecast: (fieldId, days = 7) => {
return api.get(`/land/field/monitoring/forecast/${fieldId}`, { params: { days } });
},
// 环境监测
getEnvironmentalData: (fieldId, params = {}) => {
return api.get(`/land/field/monitoring/environment/${fieldId}`, { params });
},
// 传感器数据
getSensorData: (fieldId, sensorType) => {
return api.get(`/land/field/monitoring/sensors/${fieldId}`, { params: { type: sensorType } });
},
// 监测历史数据
getMonitoringHistory: (fieldId, params = {}) => {
return api.get(`/land/field/monitoring/history/${fieldId}`, { params });
}
});
/**
* 地块适宜性评价 API
*/
export const createFieldSuitabilityAPI = (api) => ({
// 多因子综合评价
getComprehensiveEvaluation: (fieldId) => {
return api.get(`/land/field/suitability/comprehensive/${fieldId}`);
},
generateComprehensiveEvaluation: (fieldId, factors) => {
return api.post(`/land/field/suitability/comprehensive/generate/${fieldId}`, { factors });
},
// 自动化空间分析
getBatchAnalysis: (params = {}) => {
return api.get('/land/field/suitability/batch-analysis', { params });
},
startBatchAnalysis: (analysisConfig) => {
return api.post('/land/field/suitability/batch-analysis/start', analysisConfig);
},
// 作物适配推荐
getCropRecommendation: (fieldId) => {
return api.get(`/land/field/suitability/crop/${fieldId}`);
},
generateCropRecommendation: (fieldId, preferences) => {
return api.post(`/land/field/suitability/crop/generate/${fieldId}`, preferences);
},
// 权重配置
getWeightConfig: () => {
return api.get('/land/field/suitability/weight-config');
},
updateWeightConfig: (configData) => {
return api.put('/land/field/suitability/weight-config', configData);
}
});
/**
* 地块对比分析 API
*/
export const createFieldComparisonAPI = (api) => ({
// 多维度指标看板
getMultiIndicatorDashboard: (fieldIds, params = {}) => {
return api.post('/land/field/comparison/indicator', { fieldIds, ...params });
},
// 可视化图表分析
getChartAnalysis: (comparisonData) => {
return api.post('/land/field/comparison/chart', comparisonData);
},
// 对比报告生成
generateComparisonReport: (reportConfig) => {
return api.post('/land/field/comparison/report/generate', reportConfig);
},
getComparisonReport: (reportId) => {
return api.get(`/land/field/comparison/report/${reportId}`);
},
// 导出对比报告
exportComparisonReport: (reportId, format = 'pdf') => {
return api.get(`/land/field/comparison/report/export/${reportId}`, {
params: { format },
responseType: 'blob'
});
}
});
/**
* 地块风险预警 API
*/
export const createFieldRiskAPI = (api) => ({
// 实时风险监测
getRiskMonitoring: (fieldId) => {
return api.get(`/land/field/risk/monitoring/${fieldId}`);
},
// 获取风险等级
getRiskLevel: (fieldId, riskType) => {
return api.get(`/land/field/risk/level/${fieldId}`, { params: { type: riskType } });
},
// 预警推送管理
getWarningPushSettings: (fieldId) => {
return api.get(`/land/field/risk/push-settings/${fieldId}`);
},
updateWarningPushSettings: (fieldId, settings) => {
return api.put(`/land/field/risk/push-settings/${fieldId}`, settings);
},
// 预警记录
getWarningHistory: (params = {}) => {
return api.get('/land/field/risk/warning-history', { params });
},
// 预警处置跟踪
getDisposalTracking: (warningId) => {
return api.get(`/land/field/risk/disposal/${warningId}`);
},
updateDisposalStatus: (warningId, disposalData) => {
return api.put(`/land/field/risk/disposal/${warningId}`, disposalData);
},
// 创建预警规则
createWarningRule: (ruleData) => {
return api.post('/land/field/risk/warning-rules/create', ruleData);
},
getWarningRules: (params = {}) => {
return api.get('/land/field/risk/warning-rules', { params });
}
});
/**
* 导出地块信息管理系统的所有API
*/
export const landInformationAPI = (api) => ({
// 地块档案
archive: createFieldArchiveAPI(api),
// 地图管理
map: createFieldMapAPI(api),
// 空间分析
analysis: createFieldAnalysisAPI(api),
// 环境监测
monitoring: createFieldMonitoringAPI(api),
// 适宜性评价
suitability: createFieldSuitabilityAPI(api),
// 对比分析
comparison: createFieldComparisonAPI(api),
// 风险预警
risk: createFieldRiskAPI(api)
});

View File

@@ -0,0 +1,427 @@
# 开发项目规范
## pathland-information/archive/statisticsname统计分析页面开发经验
### 总体开发经验总结
在实现统计分析页面过程中我们遵循了以下8条核心开发规范确保代码质量、可维护性和用户体验的一致性。
### 1. shadcn 样式系统优先原则
**经验总结:**
- 优先使用shadcn的语义化颜色类`bg-card``bg-background`替代硬编码的Tailwind CSS颜色
- 避免使用 `bg-red-500` 等具体颜色值,改用 `bg-red-50 dark:bg-red-950` 等语义化类
- 统计卡片使用 `bg-green-50 dark:bg-green-950` 等主题感知的背景色
**最佳实践:**
```tsx
// ✅ 推荐写法
<Card className="p-4 bg-green-50 dark:bg-green-950">
<Card className="bg-card hover:bg-muted">
// ❌ 避免写法
<Card className="p-4 bg-green-100">
<Card className="bg-white hover:bg-gray-100">
```
### 2. 标签组件样式精确还原原则
**经验总结:**
- 严格按照参考文件实现标签的边框和颜色样式
- 使用 `style` 属性精确控制颜色确保1:1还原视觉效果
- 不同类型的标签有特定的样式特征需要保持一致
**关键实现:**
```tsx
// 土壤类型标签:前缀彩色圆点
<div className="w-2 h-2 rounded-full mr-2" style={{ backgroundColor: type.color }} />
// 种植模式标签前缀emoji + 固定绿色边框
<span className="mr-1">{mode.emoji}</span>
style={{
backgroundColor: filters.plantingModes.includes(mode.key) ? '#16a34a' : 'transparent',
borderColor: '#16a34a',
}}
// 自定义标签:纯色边框背景
style={{
backgroundColor: filters.tags.includes(tag.name) ? tag.color : 'transparent',
borderColor: tag.color,
}}
```
### 3. 最小化修改原则
**经验总结:**
- 严格遵循参考文件的功能边界,不添加多余功能
- 保持与原有系统的功能一致性
- 避免过度设计,专注核心功能实现
**实施要点:**
- 只实现参考文件中明确展示的功能
- 保持相同的用户交互流程
- 维持原有的数据结构和逻辑
### 4. 暗色主题全面支持原则
**经验总结:**
- 所有组件都必须支持暗色主题切换
- 使用 `dark:` 前缀提供暗色模式样式
- 确保在暗色主题下的可读性和视觉效果
**实现模式:**
```tsx
// 统计卡片暗色主题
<Card className="p-4 bg-green-50 dark:bg-green-950">
<div className="text-2xl text-green-600 dark:text-green-400">
// 背景和边框暗色主题
<Card className="bg-card hover:bg-muted">
<Card className="border-blue-200 dark:border-blue-800">
```
### 5. useReducer 状态管理架构原则
**经验总结:**
- 使用useReducer实现复杂状态管理避免prop drilling
- 通过dispatch实现跨组件状态同步
- 集中化状态逻辑,提高代码可维护性
**架构模式:**
```tsx
// Reducer定义
export interface LandStatisticsState {
fields: Land[];
filters: FilterCondition;
statistics: StatisticsResult | null;
chartType: 'bar' | 'pie';
}
// Action类型定义
export type LandStatisticsAction =
| { type: 'SET_FIELDS'; payload: Land[] }
| { type: 'UPDATE_FILTER'; payload: { key: keyof FilterCondition; value: any } }
| { type: 'SET_STATISTICS'; payload: StatisticsResult | null };
// 状态同步使用
const handleFilterChange = (key: keyof FilterCondition, value: any) => {
dispatch({ type: 'UPDATE_FILTER', payload: { key, value } });
};
```
### 6. 模块化组件架构原则
**经验总结:**
- 每个页面建立独立的components文件夹
- 按功能职责拆分组件,确保单一职责
- 组件间通过props和回调函数通信
**目录结构示例:**
```
src/app/(app)/land-information/archive/statistics/
├── page.tsx # 主页面
└── components/
├── landStatisticsReducer.tsx # 状态管理
├── FilterPanel.tsx # 筛选面板
├── StatisticsResults.tsx # 统计结果
└── UsageExamples.tsx # 使用示例
```
### 7. 完整依赖引用实现原则
**经验总结:**
- 仔细分析参考文件的import依赖确保完整实现
- 将所有引用的组件都实现在components目录中
- 保持与参考文件相同的组件结构和功能
**依赖检查清单:**
- UI组件Card, Button, Badge, Input, Label等
- 图标组件lucide-react图标
- 图表组件recharts相关组件
- 工具函数toast通知等
### 8. 1:1 功能还原实现原则
**经验总结:**
- 严格按照参考文件的功能逻辑实现
- 保持相同的用户交互体验
- 确保数据流和业务逻辑的一致性
**关键实现要点:**
- 筛选条件的多选逻辑
- 数据统计的计算方法
- 图表切换和显示逻辑
- 数据导出功能
## 开发工具和最佳实践
### 推荐工具链
- **状态管理**React useReducer
- **UI组件库**shadcn/ui
- **样式系统**Tailwind CSS + 语义化颜色
- **图表库**Recharts
- **图标库**Lucide React
- **通知系统**Sonner
### 代码质量保证
- TypeScript严格类型检查
- ESLint代码规范检查
- 组件props类型定义完整
- 状态管理逻辑清晰可维护
### 测试数据管理
- localStorage数据持久化
- 完整的测试数据覆盖所有业务场景
- 数据初始化和清理机制完善
通过遵循这些开发规范,我们可以确保代码的一致性、可维护性和用户体验的统一性。
---
## pathland-information/archive/statisticsname统计分析页面开发经验与问题解决
### 问题1图表横轴显示不完整
**问题描述:**
- 初始实现中,图表只显示有数据的土壤类型和种植模式
- 没有数据的项目在横轴上不显示,导致图表看起来不完整
**原始需求分析:**
- 土壤类型分布应显示所有定义的土壤类型即使数量为0
- 种植模式分布应显示所有定义的种植模式,提供完整的分类视图
**解决方案:**
- 修改数据计算逻辑,从"基于筛选结果生成数据"改为"基于所有定义的分类生成数据"
- 使用 `state.soilTypes.map()``state.plantingModes.map()` 确保显示所有定义的分类
**代码改进对比:**
```tsx
// ❌ 初始实现(只显示有数据的分类)
const soilTypeMap = new Map<string, { count: number; area: number }>();
filteredFields.forEach(f => {
const current = soilTypeMap.get(f.soilType) || { count: 0, area: 0 };
soilTypeMap.set(f.soilType, {
count: current.count + 1,
area: current.area + f.area,
});
});
const soilTypeDistribution = Array.from(soilTypeMap.entries()).map(([key, value]) => ({
name: state.soilTypes.find(s => s.key === key)?.name || key,
count: value.count,
area: value.area,
color: state.soilTypes.find(s => s.key === key)?.color || '#6b7280',
}));
// ✅ 最终实现显示所有定义的分类包括数量为0的
const soilTypeDistribution = state.soilTypes.map(soilType => {
const count = filteredFields.filter(f => f.soilType === soilType.key).length;
const area = filteredFields
.filter(f => f.soilType === soilType.key)
.reduce((sum, f) => sum + f.area, 0);
return {
name: soilType.name,
count,
area,
color: soilType.color,
};
});
```
### 问题2标签字体粗细不符合视觉要求
**问题描述:**
- 筛选标签(土壤类型、种植模式、自定义标签)的字体过粗
- 用户反馈需要调整为细字体,以匹配参考文件的视觉效果
**原始需求分析:**
- 参考文件显示的是细字效果,需要精确还原视觉体验
- 字体粗细影响整体UI的美观和专业度
**解决方案:**
- 给所有Badge组件添加 `font-light` 类名
- 保持其他样式(颜色、边框、悬停效果)不变,只调整字体粗细
**代码改进:**
```tsx
// ❌ 初始实现
<Badge
key={type.id}
variant={filters.soilTypes.includes(type.key) ? 'default' : 'outline'}
className="cursor-pointer"
style={{ backgroundColor: filters.soilTypes.includes(type.key) ? type.color : 'transparent' }}
>
{type.name}
</Badge>
// ✅ 最终实现(添加字体细体)
<Badge
key={type.id}
variant={filters.soilTypes.includes(type.key) ? 'default' : 'outline'}
className="cursor-pointer font-light"
style={{ backgroundColor: filters.soilTypes.includes(type.key) ? type.color : 'transparent' }}
>
{type.name}
</Badge>
```
### 问题3测试数据覆盖不完整影响演示效果
**问题描述:**
- localStorage中存在旧数据导致某些土壤类型和种植模式没有对应的地块数据
- 部分图表项目显示为空或缺失,影响演示效果和用户体验
**原始需求分析:**
- 所有土壤类型和种植模式都应该有对应的测试数据
- 确保图表能完整展示所有分类的统计数据即使是0也要显示
- 为用户提供完整的演示环境
**解决方案:**
- 创建完整的测试数据集覆盖所有7种土壤类型和5种种植模式
-`loadData` 函数中初始化这些测试数据,确保首次访问时有完整数据
- 通过localStorage持久化确保数据在页面刷新后仍然存在
**测试数据设计原则:**
```tsx
const testFields = [
{
id: '1',
code: 'TD001',
name: '东区沙质土试验田',
area: 85.5,
location: '东区1号地块',
soilType: 'sandy', // 沙质土
plantingMode: 'conventional', // 传统种植
tags: ['有机种植', '高产示范', '滴灌设施'],
// ...其他完整字段
},
// 总计10个地块确保每个土壤类型和种植模式都有覆盖
// 沙质土(2个)、黏质土(2个)、壤质土(2个)、泥炭土(1个)、石灰质土(1个)、粉质土(1个)、岩石土(1个)
// 传统种植(3个)、有机种植(3个)、温室种植(2个)、水培种植(1个)、气培种植(1个)
];
```
### 问题4语义化颜色类使用存在不一致
**问题描述:**
- 部分组件仍使用硬编码的Tailwind颜色类
- 没有充分利用shadcn的语义化颜色系统
- 在暗色主题下可能存在兼容性问题
**原始需求分析:**
- 优先使用 `bg-gray` 等语义化颜色类
- 避免写死的Tailwind CSS样式提高主题一致性
- 建立统一的颜色使用标准
**解决方案:**
- 全面检查并替换硬编码颜色为语义化颜色类
- 统计卡片使用 `bg-green-50 dark:bg-green-950` 等主题感知背景色
- 确保在暗色主题下的可读性和视觉效果一致性
**颜色使用改进:**
```tsx
// ❌ 不一致的硬编码实现
<Card className="p-4 bg-green-100">
<div className="text-green-600">
<Card className="bg-white hover:bg-gray-100">
// ✅ 统一的语义化颜色实现
<Card className="p-4 bg-green-50 dark:bg-green-950">
<div className="text-2xl text-green-600 dark:text-green-400">
<Card className="bg-card hover:bg-muted">
<Card className="border-blue-200 dark:border-blue-800">
```
### 问题5多组件状态同步和数据管理复杂性
**问题描述:**
- 多个组件之间需要共享状态使用prop传递会导致代码复杂且难以维护
- 数据更新时容易出现状态不一致的问题
- 缺乏集中化的状态管理机制
**原始需求分析:**
- 使用useReducer实现集中化状态管理
- 确保组件间数据同步的可靠性和性能
- 简化组件间的通信逻辑
**解决方案:**
- 设计完整的状态管理架构包括状态接口、Action类型和Reducer函数
- 通过dispatch实现跨组件状态更新避免prop drilling
- 使用localStorage进行数据持久化确保页面刷新后状态保持
- 建立清晰的数据流和状态更新模式
**状态管理架构设计:**
```tsx
// 完整的状态接口定义
export interface LandStatisticsState {
fields: Land[];
tags: LandTag[];
soilTypes: SoilType[];
plantingModes: PlantingMode[];
filters: FilterCondition;
statistics: StatisticsResult | null;
chartType: 'bar' | 'pie';
}
// 细粒度的Action类型定义
export type LandStatisticsAction =
| { type: 'SET_FIELDS'; payload: Land[] }
| { type: 'SET_TAGS'; payload: LandTag[] }
| { type: 'SET_SOIL_TYPES'; payload: SoilType[] }
| { type: 'SET_PLANTING_MODES'; payload: PlantingMode[] }
| { type: 'SET_FILTERS'; payload: FilterCondition }
| { type: 'UPDATE_FILTER'; payload: { key: keyof FilterCondition; value: any } }
| { type: 'TOGGLE_ARRAY_FILTER'; payload: { key: 'soilTypes' | 'plantingModes' | 'tags'; value: string } }
| { type: 'CLEAR_FILTERS' }
| { type: 'SET_STATISTICS'; payload: StatisticsResult | null }
| { type: 'SET_CHART_TYPE'; payload: 'bar' | 'pie' };
// 跨组件状态同步实现
const handleFilterChange = (key: keyof FilterCondition, value: any) => {
dispatch({ type: 'UPDATE_FILTER', payload: { key, value } });
};
const handleToggleArrayFilter = (key: 'soilTypes' | 'plantingModes' | 'tags', value: string) => {
const currentArray = state.filters[key];
const newArray = currentArray.includes(value)
? currentArray.filter(v => v !== value)
: [...currentArray, value];
dispatch({ type: 'TOGGLE_ARRAY_FILTER', payload: { key, value } });
};
```
## 开发经验对比总结
### 与原始要求的差异分析
| 原始要求 | 实际实现 | 差异说明 | 解决过程 |
|---------|---------|---------|---------|
| 使用shadcn语义化样式 | 完全实现 + 统一规范 | 需要建立统一的使用标准 | 全面替换硬编码颜色,建立语义化颜色使用指南 |
| 1:1还原标签样式 | 精确还原 + 字体优化 | 字体粗细需要调整以匹配视觉 | 添加font-light类名保持样式一致性 |
| 不动无关内容 | 完全遵循 | 严格保持功能边界,不添加多余功能 | 只实现参考文件中的明确功能 |
| 暗色主题支持 | 全面支持 | 需要系统化处理所有UI组件 | 使用dark:前缀系统化处理暗色主题 |
| useReducer状态管理 | 架构实现 + 最佳实践 | 需要设计状态同步机制和数据持久化 | 建立完整的状态管理架构和同步机制 |
| 模块化组件 | 完全拆分 | 需要合理的组件职责划分和通信机制 | 按功能领域拆分组件通过props和回调通信 |
| 完整依赖引用 | 1:1还原 | 需要仔细分析引用关系和依赖完整性 | 建立依赖检查清单,确保所有引用组件完整实现 |
| 1:1功能还原 | 完整实现 | 需要精确还原业务逻辑和用户体验 | 严格对照参考文件实现所有功能 |
### 关键学习点和改进
1. **数据完整性思维**:不仅要实现功能,还要考虑数据的完整性和演示效果的完整性
- 学会了从用户体验角度思考数据展示的完整性
- 理解了即使count为0也应该显示的重要性
2. **细节精确把控**:字体粗细、颜色、边框等视觉细节需要精确还原
- 培养了对UI细节的敏感度
- 掌握了通过用户反馈快速迭代优化的方法
3. **状态管理设计**useReducer不仅是技术选择更是架构设计决策
- 深入理解了状态管理的架构设计原则
- 掌握了跨组件状态同步的最佳实践
4. **渐进式优化**:在开发过程中根据反馈不断调整和改进
- 学会了灵活应对开发过程中的需求变化
- 建立了基于反馈的快速响应机制
5. **文档化习惯**:将开发过程中的经验和教训记录下来,形成知识积累
- 认识到文档化对团队协作和知识传承的重要性
- 建立了完整的开发规范文档体系

23
crop-x/env/.env.dev vendored Normal file
View File

@@ -0,0 +1,23 @@
# 开发环境配置
NODE_ENV=development
# 前端域名
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.dev.maimaiag.com
# 后端 API 地址
BACKEND_BASE_URL=http://pengcode.tech:8080
# API 版本
API_VERSION=v1
# 调试模式
DEBUG=true
# 是否开启 Mock 数据
USE_MOCK=false
# 应用名称
APP_NAME=智慧农业生产管理系统
# 环境描述
ENV_DESCRIPTION=开发环境

43
crop-x/env/.env.prod vendored Normal file
View File

@@ -0,0 +1,43 @@
# 生产环境配置
NODE_ENV=production
# 前端域名
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.prod.maimaiag.com
# 后端 API 地址
BACKEND_BASE_URL=https://cavin-smart-crop-backend-app.prod.maimaiag.com
# API 版本
API_VERSION=v1
# 调试模式
DEBUG=false
# 是否开启 Mock 数据
USE_MOCK=false
# 应用名称
APP_NAME=智慧农业生产管理系统
# 环境描述
ENV_DESCRIPTION=生产环境
# 生产环境特有配置
API_TIMEOUT=30000
ENABLE_ERROR_LOGGING=true
ENABLE_PERFORMANCE_MONITORING=true
ENABLE_USER_BEHAVIOR_TRACKING=true
ENABLE_ANALYTICS=true
# 安全配置
SENTRY_DSN=https://your-sentry-dsn.prod@sentry.io/project-id
CDN_BASE_URL=https://cdn.cavin-smart-crop.com
# 功能开关
ENABLE_MAINTENANCE_MODE=false
ENABLE_NEW_FEATURES=true
ENABLE_BETA_FEATURES=false
# 缓存配置
CACHE_TTL=3600
ENABLE_SERVICE_WORKER=true

28
crop-x/env/.env.test vendored Normal file
View File

@@ -0,0 +1,28 @@
# 测试环境配置
NODE_ENV=test
# 前端域名
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.test.maimaiag.com
# 后端 API 地址
BACKEND_BASE_URL=http://pengcode.tech:8080
# API 版本
API_VERSION=v1
# 调试模式
DEBUG=true
# 是否开启 Mock 数据
USE_MOCK=false
# 应用名称
APP_NAME=智慧农业生产管理系统
# 环境描述
ENV_DESCRIPTION=测试环境
# 其他测试环境特有配置
API_TIMEOUT=15000
ENABLE_ERROR_LOGGING=true
ENABLE_PERFORMANCE_MONITORING=true

30
crop-x/env/.env.uat vendored Normal file
View File

@@ -0,0 +1,30 @@
# UAT (用户验收测试) 环境配置
NODE_ENV=production
# 前端域名
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.uat.maimaiag.com
# 后端 API 地址
BACKEND_BASE_URL=https://cavin-smart-crop-backend-app.uat.maimaiag.com
# API 版本
API_VERSION=v1
# 调试模式
DEBUG=false
# 是否开启 Mock 数据
USE_MOCK=false
# 应用名称
APP_NAME=智慧农业生产管理系统
# 环境描述
ENV_DESCRIPTION=UAT 环境
# UAT 特有配置
API_TIMEOUT=20000
ENABLE_ERROR_LOGGING=true
ENABLE_PERFORMANCE_MONITORING=true
ENABLE_USER_BEHAVIOR_TRACKING=true
SENTRY_DSN=https://your-sentry-dsn.uat@sentry.io/project-id

View File

@@ -11,15 +11,18 @@ const nextConfig = {
// 修复CSS构建问题 // 修复CSS构建问题
experimental: { experimental: {
// forceSwcTransforms: true, // forceSwcTransforms: true,
turbo: { },
rules: { // 新的 Turbopack 配置
'*.svg': { turbopack: {
loaders: ['@svgr/webpack'], rules: {
as: '*.js', '*.svg': {
}, loaders: ['@svgr/webpack'],
as: '*.js',
}, },
}, },
}, },
// 解决工作区根目录问题
outputFileTracingRoot: process.cwd(),
}; };
export default nextConfig; export default nextConfig;

View File

@@ -0,0 +1,17 @@
import { defineConfig } from "@hey-api/openapi-ts";
// 获取环境变量配置
const baseUrl = process.env.API_BASE_URL || 'http://localhost:8080';
export default defineConfig({
client: "@hey-api/client-fetch",
input: `${baseUrl}/openapi.json`,
output: "./src/lib/api",
schemas: {
name: "types.gen.ts",
},
services: {
name: "sdk.gen.ts",
},
clientName: "client.gen.ts",
});

937
crop-x/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,13 +9,13 @@
"start": "next start", "start": "next start",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext ts,tsx --fix", "lint:fix": "eslint . --ext ts,tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"type-check": "tsc --noEmit", "type-check": "tsc --noEmit",
"scripts:setup": "node scripts/setup-dev-tools.js", "api:generate": "node scripts/generate-api.cjs",
"scripts:enable": "node scripts/setup-dev-tools.js --enable", "deploy": "node scripts/deploy.js",
"scripts:disable": "node scripts/setup-dev-tools.js --disable", "build:dev": "node scripts/build.cjs dev",
"deploy": "node scripts/deploy.js" "build:test": "node scripts/build.cjs test",
"build:uat": "node scripts/build.cjs uat",
"build:prod": "node scripts/build.cjs prod"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.2.2", "@hookform/resolvers": "^5.2.2",
@@ -56,6 +56,7 @@
"lucide-react": "^0.487.0", "lucide-react": "^0.487.0",
"next": "^15.5.6", "next": "^15.5.6",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"openapi-fetch": "^0.15.0",
"qrcode": "*", "qrcode": "*",
"react": "^19.2.0", "react": "^19.2.0",
"react-day-picker": "^9.11.1", "react-day-picker": "^9.11.1",
@@ -71,8 +72,10 @@
"zustand": "^5.0.8" "zustand": "^5.0.8"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.1.14", "@hey-api/client-fetch": "^0.13.1",
"@hey-api/openapi-ts": "^0.86.6",
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@tailwindcss/vite": "^4.1.14",
"@types/node": "^20.10.0", "@types/node": "^20.10.0",
"@types/react": "^18.3.11", "@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
@@ -87,11 +90,13 @@
"husky": "^9.1.6", "husky": "^9.1.6",
"install": "^0.13.0", "install": "^0.13.0",
"lint-staged": "^15.2.10", "lint-staged": "^15.2.10",
"node-fetch": "^3.3.2",
"npm": "^11.6.2", "npm": "^11.6.2",
"openapi-typescript": "^7.10.1",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"typescript": "^5.6.2", "typescript": "^5.9.3",
"vite": "^6.4.0" "vite": "^6.4.0"
} }
} }

387
crop-x/scripts/build.cjs Normal file
View File

@@ -0,0 +1,387 @@
/**
* 统一构建脚本
*
* 整合环境设置、API生成和Next.js构建的完整构建流程
* 支持多环境构建dev, test, uat, prod
*/
const fs = require('fs');
const path = require('path');
const { execSync, spawn } = require('child_process');
// ANSI 颜色代码
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
white: '\x1b[37m'
};
// 日志函数
function log(message, color = 'white') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function logSuccess(message) {
log(`${message}`, 'green');
}
function logError(message) {
log(`${message}`, 'red');
}
function logInfo(message) {
log(` ${message}`, 'blue');
}
function logStep(message) {
log(`🔄 ${message}`, 'cyan');
}
function logWarning(message) {
log(`${message}`, 'yellow');
}
/**
* 获取命令行参数中的环境
*/
function getEnvironmentFromArgs() {
const args = process.argv.slice(2);
// 查找 --env 参数
const envArg = args.find(arg => arg.startsWith('--env='));
if (envArg) {
return envArg.split('=')[1];
}
// 查找直接的参数
const directEnv = args[0];
if (['dev', 'test', 'uat', 'prod'].includes(directEnv)) {
return directEnv;
}
return null;
}
/**
* 验证环境名称
*/
function validateEnvironment(env) {
const validEnvs = ['dev', 'test', 'uat', 'prod'];
if (!validEnvs.includes(env)) {
logError(`无效的环境名称: ${env}`);
logInfo(`支持的环境: ${validEnvs.join(', ')}`);
return false;
}
return true;
}
/**
* 复制环境配置文件
*/
function copyEnvironmentConfig(env) {
logStep(`设置 ${env} 环境配置`);
const envDir = path.join(process.cwd(), 'env');
const sourceFile = path.join(envDir, `.env.${env}`);
const targetFile = path.join(process.cwd(), '.env.local');
// 检查源文件是否存在
if (!fs.existsSync(sourceFile)) {
logError(`环境配置文件不存在: ${sourceFile}`);
return false;
}
try {
// 复制文件
fs.copyFileSync(sourceFile, targetFile);
logSuccess(`已复制环境配置: ${env}`);
return true;
} catch (error) {
logError(`复制环境配置文件失败: ${error.message}`);
return false;
}
}
/**
* 清理缓存目录
*/
function cleanCache() {
logStep('清理缓存目录');
const dirsToClean = ['.next', 'node_modules/.cache'];
dirsToClean.forEach(dir => {
const dirPath = path.join(process.cwd(), dir);
if (fs.existsSync(dirPath)) {
try {
fs.rmSync(dirPath, { recursive: true, force: true });
logSuccess(`已清理: ${dir}`);
} catch (error) {
logWarning(`清理 ${dir} 失败: ${error.message}`);
}
}
});
}
/**
* 生成API客户端代码
*/
function generateApi(env) {
logStep('生成API客户端代码');
try {
// 设置环境变量
const apiBaseUrl = getApiBaseUrl(env);
process.env.API_BASE_URL = apiBaseUrl;
logInfo(`API服务器: ${apiBaseUrl}`);
// 执行API生成脚本
execSync('node scripts/generate-api.cjs', {
stdio: 'inherit',
cwd: process.cwd()
});
logSuccess('API客户端代码生成完成');
return true;
} catch (error) {
logError(`API生成失败: ${error.message}`);
return false;
}
}
/**
* 从环境配置文件中读取配置
*/
function getEnvConfigFromFile(env) {
try {
// 读取对应环境配置文件
const envFilePath = path.join(process.cwd(), 'env', `.env.${env}`);
if (!fs.existsSync(envFilePath)) {
logWarning(`环境配置文件不存在: ${envFilePath}`);
return {
FRONTEND_BASE_URL: 'http://localhost:3000',
BACKEND_BASE_URL: 'http://localhost:8080',
ENV_DESCRIPTION: `${env}环境`
};
}
const envContent = fs.readFileSync(envFilePath, 'utf8');
const lines = envContent.split('\n');
const config = {
FRONTEND_BASE_URL: 'http://localhost:3000',
BACKEND_BASE_URL: 'http://localhost:8080',
ENV_DESCRIPTION: `${env}环境`
};
// 解析配置文件
for (const line of lines) {
if (line.startsWith('FRONTEND_BASE_URL=')) {
config.FRONTEND_BASE_URL = line.split('=')[1].trim();
} else if (line.startsWith('BACKEND_BASE_URL=')) {
config.BACKEND_BASE_URL = line.split('=')[1].trim();
logInfo(`${envFilePath} 读取到后端地址: ${config.BACKEND_BASE_URL}`);
} else if (line.startsWith('ENV_DESCRIPTION=')) {
config.ENV_DESCRIPTION = line.split('=')[1].trim();
}
}
return config;
} catch (error) {
logError(`读取环境配置失败: ${error.message}`);
return {
FRONTEND_BASE_URL: 'http://localhost:3000',
BACKEND_BASE_URL: 'http://localhost:8080',
ENV_DESCRIPTION: `${env}环境`
};
}
}
/**
* 获取API基础URL - 从环境配置文件中读取
*/
function getApiBaseUrl(env) {
const config = getEnvConfigFromFile(env);
return config.BACKEND_BASE_URL;
}
/**
* 获取前端基础URL - 从环境配置文件中读取
*/
function getFrontendUrl(env) {
const config = getEnvConfigFromFile(env);
return config.FRONTEND_BASE_URL;
}
/**
* Next.js 构建
*/
function buildNext(env) {
logStep('执行 Next.js 构建');
try {
// 设置环境变量
process.env.NODE_ENV = 'production'; // 所有构建都使用 production 模式
process.env.NEXT_PUBLIC_ENV = env;
// 执行构建
execSync('npm run build', {
stdio: 'inherit',
cwd: process.cwd()
});
logSuccess('Next.js 构建完成');
return true;
} catch (error) {
logError(`Next.js 构建失败: ${error.message}`);
return false;
}
}
/**
* 显示构建信息
*/
function showBuildInfo(env, totalTime) {
const envConfig = getEnvConfigFromFile(env);
log('='.repeat(60), 'green');
logSuccess(`构建完成!总耗时: ${totalTime}ms`);
logSuccess(`环境: ${envConfig.ENV_DESCRIPTION} (${env})`);
logSuccess(`前端地址: ${envConfig.FRONTEND_BASE_URL}`);
logSuccess(`后端地址: ${envConfig.BACKEND_BASE_URL}`);
logSuccess('构建产物: .next 目录');
log('='.repeat(60), 'green');
logInfo('部署建议:');
logInfo(` 将 .next 目录和 package.json 部署到 ${envConfig.ENV_DESCRIPTION}`);
if (env !== 'dev') {
logInfo(` 确保环境变量 NODE_ENV=production 和 NEXT_PUBLIC_ENV=${env}`);
}
}
/**
* 显示帮助信息
*/
function showHelp() {
log('用法: node scripts/build.cjs [环境] [选项]', 'cyan');
log('');
log('环境:', 'yellow');
log(' dev 开发环境', 'white');
log(' test 测试环境', 'white');
log(' uat UAT 环境', 'white');
log(' prod 生产环境', 'white');
log('');
log('选项:', 'yellow');
log(' --env=<environment> 指定环境 (与直接指定环境等效)', 'white');
log(' --clean 构建前清理缓存', 'white');
log(' --skip-api 跳过API生成', 'white');
log(' --skip-build 跳过Next.js构建仅设置环境', 'white');
log(' --help 显示此帮助信息', 'white');
log('');
log('示例:', 'yellow');
log(' node scripts/build.cjs dev # 开发环境完整构建', 'white');
log(' node scripts/build.cjs test --clean # 测试环境清理缓存后构建', 'white');
log(' node scripts/build.cjs prod --skip-api # 生产环境跳过API生成构建', 'white');
log(' node scripts/build.cjs uat --skip-build # UAT环境仅设置环境和API生成', 'white');
log('');
}
/**
* 主函数
*/
function main() {
const startTime = Date.now();
log('='.repeat(60), 'cyan');
log('统一构建脚本', 'cyan');
log('整合环境设置、API生成和Next.js构建', 'cyan');
log('='.repeat(60), 'cyan');
// 检查帮助参数
if (process.argv.includes('--help') || process.argv.includes('-h')) {
showHelp();
process.exit(0);
}
// 获取环境参数
let env = getEnvironmentFromArgs();
if (!env) {
logError('请指定环境参数');
logInfo('使用 --help 查看帮助信息');
process.exit(1);
}
// 验证环境
if (!validateEnvironment(env)) {
process.exit(1);
}
// 解析选项
const shouldClean = process.argv.includes('--clean');
const skipApi = process.argv.includes('--skip-api');
const skipBuild = process.argv.includes('--skip-build');
logInfo(`目标环境: ${env}`);
logInfo(`工作目录: ${process.cwd()}`);
try {
// 1. 设置环境配置
const envSuccess = copyEnvironmentConfig(env);
if (!envSuccess) {
process.exit(1);
}
// 2. 清理缓存(可选)
if (shouldClean) {
cleanCache();
}
// 3. 生成API客户端可选
if (!skipApi) {
const apiSuccess = generateApi(env);
if (!apiSuccess) {
process.exit(1);
}
} else {
logInfo('跳过API生成');
}
// 4. Next.js 构建(可选)
if (!skipBuild) {
const buildSuccess = buildNext(env);
if (!buildSuccess) {
process.exit(1);
}
} else {
logInfo('跳过Next.js构建');
}
const totalTime = Date.now() - startTime;
showBuildInfo(env, totalTime);
} catch (error) {
const totalTime = Date.now() - startTime;
logError(`构建失败 (${totalTime}ms): ${error.message}`);
process.exit(1);
}
}
// 执行主函数
if (require.main === module) {
main();
}
module.exports = {
copyEnvironmentConfig,
generateApi,
buildNext,
validateEnvironment,
getEnvironmentFromArgs
};

View File

@@ -0,0 +1,293 @@
/**
* 简化的 API 生成脚本
*
* 这个脚本现在主要负责:
* 1. 使用 @hey-api/openapi-ts 命令生成客户端代码
* 2. 环境配置通过 openapi-ts.config.ts 处理
*/
const fs = require('fs');
const path = require('path');
// ANSI 颜色代码
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m'
};
// 日志函数
function log(message, color = 'white') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function logSuccess(message) {
log(`${message}`, 'green');
}
function logError(message) {
log(`${message}`, 'red');
}
function logWarning(message) {
log(`${message}`, 'yellow');
}
function logInfo(message) {
log(` ${message}`, 'blue');
}
// 显示环境配置信息
logInfo(`当前环境: ${process.env.NODE_ENV || 'development'}`);
logInfo(`API 服务器: ${process.env.API_BASE_URL || 'http://localhost:8080'}`);
/**
* 检查自定义文件是否存在
* 如果某些文件已经被自定义,我们不希望覆盖它们
*/
function checkCustomFiles() {
const customFiles = [
'client.gen.ts' // 客户端文件通常是自定义的
];
const outputDir = path.join(process.cwd(), 'src', 'lib', 'api');
const existingCustomFiles = [];
for (const file of customFiles) {
const filePath = path.join(outputDir, file);
if (fs.existsSync(filePath)) {
// 检查文件是否包含自定义内容的标识
const content = fs.readFileSync(filePath, 'utf8');
// 检查是否有自定义配置的标识
const customIndicators = [
'getBaseUrl',
'createDynamicClient',
'getCurrentClientConfig',
// 可以添加更多自定义标识
];
const hasCustomContent = customIndicators.some(indicator =>
content.includes(indicator)
);
if (hasCustomContent) {
existingCustomFiles.push(file);
logWarning(`检测到自定义文件: ${file}`);
}
}
}
return existingCustomFiles;
}
/**
* 备份自定义文件
*/
function backupCustomFiles(customFiles) {
if (customFiles.length === 0) return;
logInfo('备份自定义文件...');
const outputDir = path.join(process.cwd(), 'src', 'lib', 'api');
const backupDir = path.join(process.cwd(), 'src', 'lib', 'api', 'backup');
// 创建备份目录
if (!fs.existsSync(backupDir)) {
fs.mkdirSync(backupDir, { recursive: true });
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const timestampedBackupDir = path.join(backupDir, `backup-${timestamp}`);
fs.mkdirSync(timestampedBackupDir);
for (const file of customFiles) {
const srcPath = path.join(outputDir, file);
const destPath = path.join(timestampedBackupDir, file);
fs.copyFileSync(srcPath, destPath);
logInfo(` 备份: ${file} -> backup/${timestamp}/${file}`);
}
logSuccess(`自定义文件已备份到: backup/${timestamp}/`);
}
/**
* 恢复自定义文件
*/
function restoreCustomFiles(customFiles) {
if (customFiles.length === 0) return;
logInfo('恢复自定义文件...');
const outputDir = path.join(process.cwd(), 'src', 'lib', 'api');
const backupDir = path.join(process.cwd(), 'src', 'lib', 'api', 'backup');
// 找到最新的备份目录
const backupDirs = fs.existsSync(backupDir)
? fs.readdirSync(backupDir)
.filter(name => name.startsWith('backup-'))
.sort()
.reverse()
: [];
if (backupDirs.length === 0) {
logWarning('没有找到备份文件,跳过恢复');
return;
}
const latestBackupDir = path.join(backupDir, backupDirs[0]);
for (const file of customFiles) {
const srcPath = path.join(latestBackupDir, file);
const destPath = path.join(outputDir, file);
if (fs.existsSync(srcPath)) {
fs.copyFileSync(srcPath, destPath);
logInfo(` 恢复: ${file}`);
}
}
logSuccess('自定义文件已恢复');
}
/**
* 使用 openapi-ts 命令生成客户端代码
*/
function generateWithOpenApiTS() {
return new Promise((resolve, reject) => {
logInfo('使用 @hey-api/openapi-ts 生成客户端代码...');
const { exec } = require('child_process');
const command = 'npx @hey-api/openapi-ts';
logInfo(`执行命令: ${command}`);
const startTime = Date.now();
exec(command, { cwd: process.cwd() }, (error, stdout, stderr) => {
const executionTime = Date.now() - startTime;
if (error) {
logError(`openapi-ts 执行失败 (${executionTime}ms)`);
logError(`错误信息: ${error.message}`);
if (stderr) {
logError(`stderr: ${stderr}`);
}
reject(error);
return;
}
if (stderr) {
logWarning(`stderr: ${stderr}`);
}
logSuccess(`openapi-ts 执行成功 (${executionTime}ms)`);
if (stdout) {
logInfo(`输出: ${stdout}`);
}
resolve();
});
});
}
/**
* 验证生成的文件
*/
function validateGeneratedFiles() {
try {
logInfo('验证生成的文件...');
const outputDir = path.join(process.cwd(), 'src', 'lib', 'api');
if (!fs.existsSync(outputDir)) {
throw new Error('输出目录不存在');
}
const files = fs.readdirSync(outputDir);
logInfo(`输出目录包含 ${files.length} 个文件:`);
const requiredFiles = ['types.gen.ts', 'sdk.gen.ts', 'index.ts'];
const generatedFiles = [];
for (const file of files) {
const filePath = path.join(outputDir, file);
const stats = fs.statSync(filePath);
// 只显示文件,不显示目录
if (stats.isFile()) {
const size = (stats.size / 1024).toFixed(2);
logInfo(` 📄 ${file} (${size} KB)`);
generatedFiles.push(file);
}
}
// 检查必要文件
const missingFiles = requiredFiles.filter(file => !generatedFiles.includes(file));
if (missingFiles.length > 0) {
logWarning(`缺少期望的文件: ${missingFiles.join(', ')}`);
} else {
logSuccess('所有期望的文件都已生成');
}
return true;
} catch (error) {
logError(`验证生成的文件失败: ${error.message}`);
return false;
}
}
/**
* 主函数
*/
async function main() {
const startTime = Date.now();
log('='.repeat(60), 'cyan');
log('API 客户端代码生成脚本', 'cyan');
log('基于 @hey-api/openapi-ts', 'cyan');
log('='.repeat(60), 'cyan');
try {
// 检查是否有自定义文件
const customFiles = checkCustomFiles();
if (customFiles.length > 0) {
logInfo('检测到自定义文件,将在生成前进行备份');
backupCustomFiles(customFiles);
}
// 使用 openapi-ts 生成代码
await generateWithOpenApiTS();
// 恢复自定义文件
if (customFiles.length > 0) {
restoreCustomFiles(customFiles);
}
// 验证生成的文件
if (!validateGeneratedFiles()) {
logWarning('文件验证发现问题,但生成过程已完成');
}
const totalTime = Date.now() - startTime;
logSuccess(`API 客户端代码生成完成!总耗时: ${totalTime}ms`);
logSuccess('生成的文件位于 src/lib/api/ 目录');
if (customFiles.length > 0) {
logInfo('自定义文件已保持不变,避免覆盖手动配置');
}
} catch (error) {
const totalTime = Date.now() - startTime;
logError(`脚本执行失败 (${totalTime}ms): ${error.message}`);
process.exit(1);
}
}
// 执行主函数
if (require.main === module) {
main();
}

342
crop-x/scripts/openapi.json Normal file
View File

@@ -0,0 +1,342 @@
{
"openapi": "3.1.0",
"info": {
"title": "Crop Admin API",
"description": "一个支持OpenAPI和前端代码自动生成的后台管理系统",
"version": "1.0.0"
},
"paths": {
"/api/v1/auth/login": {
"post": {
"tags": [
"认证"
],
"summary": "用户登录",
"description": "用户登录接口\n\n- **username**: 用户名\n- **password**: 密码\n\n返回JWT访问令牌",
"operationId": "login_api_v1_auth_login_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserLogin"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIResponse"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/v1/auth/register": {
"post": {
"tags": [
"认证"
],
"summary": "用户注册",
"description": "用户注册接口\n\n- **username**: 用户名 (必须唯一)\n- **password**: 密码\n\n注意这是一个演示版本实际生产环境需要更严格的验证",
"operationId": "register_api_v1_auth_register_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserRegister"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIResponse"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/v1/auth/me": {
"get": {
"tags": [
"认证"
],
"summary": "获取当前用户信息",
"description": "获取当前登录用户的信息",
"operationId": "get_current_user_api_v1_auth_me_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIResponse"
}
}
}
}
},
"security": [
{
"HTTPBearer": []
}
]
}
},
"/api/v1/auth/logout": {
"post": {
"tags": [
"认证"
],
"summary": "用户登出",
"description": "用户登出接口\n\n注意由于JWT是无状态的实际登出需要客户端删除token\n这里只是验证token并返回成功消息",
"operationId": "logout_api_v1_auth_logout_post",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIResponse"
}
}
}
}
},
"security": [
{
"HTTPBearer": []
}
]
}
},
"/api/v1/auth/users": {
"get": {
"tags": [
"认证"
],
"summary": "获取所有用户列表",
"description": "获取系统中所有用户的列表 (仅用于演示)",
"operationId": "get_all_users_api_v1_auth_users_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIResponse"
}
}
}
}
}
}
},
"/": {
"get": {
"summary": "Root",
"operationId": "root__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
}
}
},
"/health": {
"get": {
"summary": "Health Check",
"operationId": "health_check_health_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
}
}
}
},
"components": {
"schemas": {
"APIResponse": {
"properties": {
"success": {
"type": "boolean",
"title": "Success"
},
"message": {
"type": "string",
"title": "Message"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
],
"title": "Data"
}
},
"type": "object",
"required": [
"success",
"message"
],
"title": "APIResponse",
"example": {
"data": {},
"message": "操作成功",
"success": true
}
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"UserLogin": {
"properties": {
"username": {
"type": "string",
"title": "Username"
},
"password": {
"type": "string",
"title": "Password"
}
},
"type": "object",
"required": [
"username",
"password"
],
"title": "UserLogin",
"example": {
"password": "admin123",
"username": "admin"
}
},
"UserRegister": {
"properties": {
"username": {
"type": "string",
"title": "Username"
},
"password": {
"type": "string",
"title": "Password"
}
},
"type": "object",
"required": [
"username",
"password"
],
"title": "UserRegister",
"example": {
"password": "newpassword",
"username": "newuser"
}
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
}
},
"securitySchemes": {
"HTTPBearer": {
"type": "http",
"scheme": "bearer"
}
}
},
"servers": [
{
"url": "https://gitea-admin-smart-crop-x-app.dev.maimaiag.com/",
"description": "测试环境服务器"
}
]
}

View File

@@ -1,21 +0,0 @@
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'

View File

@@ -1,163 +0,0 @@
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 })
}
}

View File

@@ -1,135 +0,0 @@
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 })
}
}

View File

@@ -1,72 +0,0 @@
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)
}
}

View File

@@ -1,262 +0,0 @@
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')
}
}

View File

@@ -1,192 +0,0 @@
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`)
}
}

View File

@@ -1,127 +0,0 @@
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)
}
}

View File

@@ -1,105 +0,0 @@
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)
}
}

View File

@@ -1,147 +0,0 @@
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}`)
}
}

View File

@@ -1,80 +0,0 @@
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()

View File

@@ -1,51 +0,0 @@
// 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'
}

View File

@@ -1,7 +0,0 @@
export default function AssetLabelingLayout({
children,
}: {
children: React.ReactNode
}) {
return <>{children}</>
}

View File

@@ -1,8 +0,0 @@
export default function HomePage() {
return (
<div className="">
</div>
)
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function CustomersPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/basic/customers
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function MaterialsPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/basic/materials
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function BasicPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/basic
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function SuppliersPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/basic/suppliers
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ToolsPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/basic/tools
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ArchivePage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment/archive
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function DepreciationPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment/depreciation
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function DispatchPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment/dispatch
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function DisposalPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment/disposal
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function MaintenancePage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment/maintenance
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function EquipmentPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/equipment
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function CheckPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/check
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function DetailPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/detail
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function InPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/in
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function LocationPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/location
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function InventoryPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function SuggestPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/suggest
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function WarningPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/inventory/warning
</p>
</div>
</Card>
</div>
);
}

View File

@@ -1,147 +1,18 @@
import Link from 'next/link' 'use client';
import { Metadata } from 'next'
export const metadata: Metadata = { import { Card } from '@/components/ui/card';
title: '农业资产管理 - Crop-X 智慧农业管理系统',
description: '农业资产管理系统主页面',
}
export default function AgriculturalAssetPage() { export default function AgriculturalAssetPage() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="bg-white rounded-lg shadow p-6"> <Card className="p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4"> <h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
</h2> <p className="text-sm">
<p className="text-gray-600 mb-6"> <strong></strong> /agricultural-asset
</p>
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Link
href="/agricultural-asset/basic-information"
className="block p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors"
>
<h3 className="font-semibold text-green-900 mb-2">
📋
</h3>
<p className="text-green-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/procurement-management"
className="block p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors"
>
<h3 className="font-semibold text-blue-900 mb-2">
🛒
</h3>
<p className="text-blue-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/inventory-management"
className="block p-4 bg-purple-50 rounded-lg hover:bg-purple-100 transition-colors"
>
<h3 className="font-semibold text-purple-900 mb-2">
📦
</h3>
<p className="text-purple-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/material-requisition"
className="block p-4 bg-orange-50 rounded-lg hover:bg-orange-100 transition-colors"
>
<h3 className="font-semibold text-orange-900 mb-2">
📤
</h3>
<p className="text-orange-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/material-return"
className="block p-4 bg-teal-50 rounded-lg hover:bg-teal-100 transition-colors"
>
<h3 className="font-semibold text-teal-900 mb-2">
📥
</h3>
<p className="text-teal-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/agricultural-supplies"
className="block p-4 bg-indigo-50 rounded-lg hover:bg-indigo-100 transition-colors"
>
<h3 className="font-semibold text-indigo-900 mb-2">
🚜
</h3>
<p className="text-indigo-700 text-sm">
</p>
</Link>
<Link
href="/agricultural-asset/visualization-reports"
className="block p-4 bg-pink-50 rounded-lg hover:bg-pink-100 transition-colors"
>
<h3 className="font-semibold text-pink-900 mb-2">
📊
</h3>
<p className="text-pink-700 text-sm">
</p>
</Link>
</div> </div>
</div> </Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
📊
</h3>
<div className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-gray-600"></span>
<span className="text-green-600 font-semibold">1,245 </span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600"></span>
<span className="text-orange-600 font-semibold">12 </span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600"></span>
<span className="text-blue-600 font-semibold">8 </span>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
🔧
</h3>
<div className="space-y-2">
<button className="w-full px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-colors">
</button>
<button className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
</button>
<button className="w-full px-4 py-2 bg-purple-600 text-white rounded hover:bg-purple-700 transition-colors">
</button>
</div>
</div>
</div>
</div> </div>
) );
} }

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function OrderPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/purchase/order
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function PurchasePage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/purchase
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function PlanPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/purchase/plan
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ConsumptionPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/report/consumption
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function InventoryReportPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/report/inventory
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function OverviewPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/report/overview
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ReportPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/report
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ApplyPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/requisition/apply
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ApprovalPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/requisition/approval
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function CheckoutPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/requisition/checkout
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function RequisitionPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/requisition
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function RecordPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/requisition/record
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function HistoryPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/return/history
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ReturnPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/return
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ProcessPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/return/process
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function RegisterPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/return/register
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function SettlementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-asset/return/settlement
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ClassificationPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/archive/classification
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { FileText } from 'lucide-react';
export default function AgriculturalMachineryEntryPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<FileText className="w-6 h-6 text-blue-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/archive/entry
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -1,94 +0,0 @@
import { Metadata } from 'next'
export const metadata: Metadata = {
title: '农机档案录入与维护 - Crop-X 智慧农业管理系统',
description: '农机设备信息管理',
}
export default function MachineryEntryPage() {
return (
<div className="space-y-6">
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6">
📋
</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-green-50 rounded-lg p-6">
<h3 className="text-lg font-semibold text-green-900 mb-4">
</h3>
<form className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="text"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
placeholder="请输入农机编号"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<select className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500">
<option></option>
<option></option>
<option></option>
<option></option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="date"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
/>
</div>
<button
type="submit"
className="w-full px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
>
</button>
</form>
</div>
<div className="bg-blue-50 rounded-lg p-6">
<h3 className="text-lg font-semibold text-blue-900 mb-4">
</h3>
<div className="space-y-3">
{[
{ id: 'NJ001', type: '拖拉机', status: '运行中', date: '2023-01-15' },
{ id: 'NJ002', type: '收割机', status: '空闲中', date: '2023-03-20' },
{ id: 'NJ003', type: '播种机', status: '维护中', date: '2023-02-10' },
].map((machine) => (
<div key={machine.id} className="bg-white rounded-lg p-4 shadow-sm">
<div className="flex justify-between items-start">
<div>
<h4 className="font-semibold text-gray-800">{machine.id}</h4>
<p className="text-sm text-gray-600">{machine.type}</p>
<p className="text-xs text-gray-500">: {machine.date}</p>
</div>
<span className={`px-2 py-1 text-xs font-medium rounded-full ${
machine.status === '运行中' ? 'bg-green-100 text-green-800' :
machine.status === '空闲中' ? 'bg-gray-100 text-gray-800' :
'bg-yellow-100 text-yellow-800'
}`}>
{machine.status}
</span>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -1,71 +1,30 @@
import { Metadata } from 'next' 'use client';
export const metadata: Metadata = { import { Card } from '@/components/ui/card';
title: '农机档案管理 - Crop-X 智慧农业管理系统', import { Package } from 'lucide-react';
description: '农机设备档案信息管理',
}
export default function MachineryArchivePage() { export default function ArchivePage() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="bg-white rounded-lg shadow p-6"> <Card className="p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6"> <div className="flex items-center gap-3 mb-4">
📋 <Package className="w-6 h-6 text-blue-600" />
</h2> <h2 className="text-xl font-semibold"></h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="space-y-3">
<div className="bg-green-50 rounded-lg p-6 hover:bg-green-100 transition-colors cursor-pointer"> <p className="text-muted-foreground">
<h3 className="font-semibold text-green-900 mb-2">
📝 </p>
</h3> <div className="p-3 bg-muted rounded-lg">
<p className="text-green-700 text-sm"> <p className="text-sm">
<strong></strong> /agricultural-machinery/archive
</p> </p>
</div> <p className="text-sm mt-1">
<strong></strong>
<div className="bg-blue-50 rounded-lg p-6 hover:bg-blue-100 transition-colors cursor-pointer">
<h3 className="font-semibold text-blue-900 mb-2">
🏷
</h3>
<p className="text-blue-700 text-sm">
</p>
</div>
<div className="bg-purple-50 rounded-lg p-6 hover:bg-purple-100 transition-colors cursor-pointer">
<h3 className="font-semibold text-purple-900 mb-2">
📱
</h3>
<p className="text-purple-700 text-sm">
</p> </p>
</div> </div>
</div> </div>
</Card>
<div className="mt-6 bg-gray-50 rounded-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
📊
</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="bg-white rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-green-600 mb-2">156</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="bg-white rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-blue-600 mb-2">12</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="bg-white rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-purple-600 mb-2">89</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="bg-white rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-orange-600 mb-2">95%</div>
<div className="text-sm text-gray-600"></div>
</div>
</div>
</div>
</div>
</div> </div>
) );
} }

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { QrCode } from 'lucide-react';
export default function AgriculturalMachineryQrCodePage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<QrCode className="w-6 h-6 text-purple-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/archive/qrcode
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function AnalysisPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/data-analysis/analysis
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ComparisonPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/data-analysis/comparison
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { PieChart } from 'lucide-react';
export default function DataAnalysisPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<PieChart className="w-6 h-6 text-cyan-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/data-analysis
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { User } from 'lucide-react';
export default function DriverInfoManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<User className="w-6 h-6 text-orange-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/driver-archive/info
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { User } from 'lucide-react';
export default function DriverArchivePage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<User className="w-6 h-6 text-green-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/driver-archive
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Calendar } from 'lucide-react';
export default function DriverTaskManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Calendar className="w-6 h-6 text-indigo-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/driver-archive/task
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function AlertRulesPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/fault-diagnosis/alert-rules
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function HealthPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/fault-diagnosis/health
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Zap } from 'lucide-react';
export default function FaultDiagnosisPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Zap className="w-6 h-6 text-red-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/fault-diagnosis
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function ParameterPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/fault-diagnosis/parameter
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function WarningPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/fault-diagnosis/warning
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Cog } from 'lucide-react';
export default function LoadDeviceManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Cog className="w-6 h-6 text-red-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/load-management/device
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Database } from 'lucide-react';
export default function LoadDeviceLibraryPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Database className="w-6 h-6 text-emerald-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/load-management/library
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Settings } from 'lucide-react';
export default function LoadManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Settings className="w-6 h-6 text-orange-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/load-management
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Settings } from 'lucide-react';
export default function LoadParameterManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Settings className="w-6 h-6 text-amber-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/load-management/parameter
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Package } from 'lucide-react';
export default function LoadTypeManagementPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Package className="w-6 h-6 text-cyan-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/load-management/type
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { MapPin } from 'lucide-react';
export default function RealTimeLocationMonitoringPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<MapPin className="w-6 h-6 text-blue-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
GPS定位和轨迹监控系统
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/monitoring/location
</p>
<p className="text-sm mt-1">
<strong></strong> GPS定位
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { BarChart3 } from 'lucide-react';
export default function OperationDataMonitoringPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<BarChart3 className="w-6 h-6 text-orange-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/monitoring/operation
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Navigation } from 'lucide-react';
export default function MonitoringPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Navigation className="w-6 h-6 text-purple-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/monitoring
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -1,93 +0,0 @@
import { Metadata } from 'next'
export const metadata: Metadata = {
title: '实时位置追踪 - Crop-X 智慧农业管理系统',
description: '农机设备定位监控',
}
export default function RealTimeLocationTrackingPage() {
return (
<div className="space-y-6">
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6">
📍
</h2>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<div className="bg-gray-100 rounded-lg h-96 flex items-center justify-center">
<div className="text-center">
<div className="text-6xl mb-4">🗺</div>
<h3 className="text-lg font-semibold text-gray-700 mb-2">
</h3>
<p className="text-gray-600">
</p>
</div>
</div>
</div>
<div className="space-y-4">
<div className="bg-green-50 rounded-lg p-4">
<h3 className="font-semibold text-green-900 mb-3">
线
</h3>
<div className="space-y-2">
{[
{ id: 'NJ001', name: '拖拉机-001', location: '东区农田', status: '工作中' },
{ id: 'NJ002', name: '收割机-002', location: '西区农田', status: '工作中' },
{ id: 'NJ003', name: '播种机-003', location: '南区农田', status: '空闲' },
].map((machine) => (
<div key={machine.id} className="bg-white rounded-lg p-3 shadow-sm">
<div className="flex items-center justify-between">
<div>
<h4 className="font-medium text-gray-800">{machine.name}</h4>
<p className="text-sm text-gray-600">{machine.location}</p>
</div>
<div className={`w-2 h-2 rounded-full ${
machine.status === '工作中' ? 'bg-green-500' : 'bg-gray-400'
}`} />
</div>
</div>
))}
</div>
</div>
<div className="bg-blue-50 rounded-lg p-4">
<h3 className="font-semibold text-blue-900 mb-3">
</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-600"></span>
<span className="font-semibold">12 </span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">线</span>
<span className="font-semibold text-green-600">10 </span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">线</span>
<span className="font-semibold text-gray-600">2 </span>
</div>
</div>
</div>
</div>
</div>
<div className="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<button className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors">
🔄
</button>
<button className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
📍
</button>
<button className="px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700 transition-colors">
📊
</button>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Activity } from 'lucide-react';
export default function WorkStatusMonitoringPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Activity className="w-6 h-6 text-green-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/monitoring/status
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function CockpitPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/precision-operation/cockpit
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function DispatchPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/precision-operation/dispatch
</p>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Card } from '@/components/ui/card';
import { Target } from 'lucide-react';
export default function PrecisionOperationPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Target className="w-6 h-6 text-indigo-600" />
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="space-y-3">
<p className="text-muted-foreground">
</p>
<div className="p-3 bg-muted rounded-lg">
<p className="text-sm">
<strong></strong> /agricultural-machinery/precision-operation
</p>
<p className="text-sm mt-1">
<strong></strong>
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { Card } from '@/components/ui/card';
export default function RecordPage() {
return (
<div className="space-y-6">
<Card className="p-6">
<h2 className="text-xl font-semibold"></h2>
<div className="p-3 bg-muted rounded-lg mt-3">
<p className="text-sm">
<strong></strong> /agricultural-machinery/precision-operation/record
</p>
</div>
</Card>
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More