Compare commits
44 Commits
fetch-test
...
9898a5ea38
| Author | SHA1 | Date | |
|---|---|---|---|
| 9898a5ea38 | |||
| 73c41b76ab | |||
| 9791e76d17 | |||
| fdeb455e47 | |||
| c942a2ce07 | |||
| 624fc38b21 | |||
| 3459cae699 | |||
| cb46f91846 | |||
| e99567e6b3 | |||
| a1b3335664 | |||
| 0df19c9cfb | |||
| ad600ce059 | |||
| 46ff61eaed | |||
| 2fa64e66c9 | |||
| 8b7e86b8bf | |||
| 5d5a24ac89 | |||
| 77bf48f88a | |||
| 2aa93f941e | |||
| 304edcbb38 | |||
| 71bc00cc4e | |||
| 3239f819d0 | |||
| e14f03cf79 | |||
| 9340252c25 | |||
| df8e6bf515 | |||
| 5d34bc3643 | |||
| 2f0196ae4a | |||
| 94f83d36ff | |||
| e3829d2fcc | |||
| 58f5ca7f22 | |||
| 7a0096caed | |||
| 3fc8f883cf | |||
| 0b6ae9fc5c | |||
| b907cc4299 | |||
| 26213aaa76 | |||
| 2c3227fb64 | |||
| 3286d4366a | |||
| 4aae686264 | |||
| 88c8bbb2a7 | |||
| 59a9743992 | |||
| 42a4a9f566 | |||
| 5055e40de6 | |||
| c0ea1fb9f3 | |||
| 2b39c1dd1a | |||
| 1f1d94ed84 |
@@ -1,3 +1,4 @@
|
||||
|
||||
# <!-- Powered by BMAD™ Core -->
|
||||
template:
|
||||
id: competitor-analysis-template-v2
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -147,3 +147,4 @@ Thumbs.db
|
||||
tmp/
|
||||
temp/
|
||||
nul
|
||||
/nextjs-frontend
|
||||
35
crop-x/.env.example
Normal file
35
crop-x/.env.example
Normal 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
113
crop-x/.gitignore
vendored
Normal 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
|
||||
@@ -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);
|
||||
*/
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
});
|
||||
@@ -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)
|
||||
});
|
||||
@@ -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)
|
||||
});
|
||||
802
crop-x/docs/开发项目规范.md
Normal file
802
crop-x/docs/开发项目规范.md
Normal file
@@ -0,0 +1,802 @@
|
||||
# 开发项目规范
|
||||
|
||||
## 通用开发规约
|
||||
|
||||
### 1. 文件头部注释规范(filekorolheader)
|
||||
|
||||
**规范要求:**
|
||||
所有页面文件(page.tsx)必须在最上方添加filekorolheader注释,说明文件对应的页面功能、路径和用途。
|
||||
|
||||
**格式标准:**
|
||||
```tsx
|
||||
/**
|
||||
* filekorolheader: [页面名称] - [功能描述]
|
||||
* 功能:[主要功能列表]
|
||||
* 路径:[页面路由路径]
|
||||
* 规范:[遵循的特殊规范说明]
|
||||
*/
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```tsx
|
||||
/**
|
||||
* filekorolheader: 物联设备数据接入页面 - IoT设备数据管理中心
|
||||
* 功能:设备列表管理、实时数据监控、数据对比分析、报告生成
|
||||
* 路径:/ai-crop-model/data-sense-center/iot
|
||||
* 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,shadcn语义化样式
|
||||
*/
|
||||
```
|
||||
|
||||
**实施要点:**
|
||||
- 必须放在文件最顶部,在'use client'之前
|
||||
- 页面名称要准确反映业务功能
|
||||
- 功能描述要简明扼要,列出核心功能
|
||||
- 路径必须是完整的路由路径
|
||||
- 如有特殊规范遵循,需要在规范字段说明
|
||||
|
||||
---
|
||||
|
||||
## path:land-information/archive/statistics,name:统计分析页面开发经验
|
||||
|
||||
### 总体开发经验总结
|
||||
|
||||
在实现统计分析页面过程中,我们遵循了以下8条核心开发规范,确保代码质量、可维护性和用户体验的一致性。
|
||||
|
||||
### 1. shadcn 样式系统优先原则
|
||||
|
||||
**经验总结:**
|
||||
- 优先使用shadcn的语义化颜色类(如 `bg-card`、`bg-background`)替代硬编码的Tailwind CSS颜色
|
||||
- 避免使用 `bg-red-500` 等具体颜色值,改用 `bg-red-50 dark:bg-red-950` 等语义化类
|
||||
- 统计卡片使用 `bg-green-50 dark:bg-green-950` 等主题感知的背景色
|
||||
|
||||
**最佳实践:**
|
||||
```tsx
|
||||
// ✅ 推荐写法
|
||||
<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数据持久化
|
||||
- 完整的测试数据覆盖所有业务场景
|
||||
- 数据初始化和清理机制完善
|
||||
|
||||
通过遵循这些开发规范,我们可以确保代码的一致性、可维护性和用户体验的统一性。
|
||||
|
||||
---
|
||||
|
||||
## path:land-information/archive/statistics,name:统计分析页面开发经验与问题解决
|
||||
|
||||
### 问题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. **文档化习惯**:将开发过程中的经验和教训记录下来,形成知识积累
|
||||
- 认识到文档化对团队协作和知识传承的重要性
|
||||
- 建立了完整的开发规范文档体系
|
||||
|
||||
---
|
||||
|
||||
## path:land-information/map/gis,name:GIS地图管理开发经验与问题解决
|
||||
|
||||
### 总体开发经验总结
|
||||
|
||||
GIS地图管理页面的开发过程是一个复杂的技术集成挑战,涉及到第三方地图库的集成、异步资源加载、多层级组件交互等多个技术难点。通过这次开发,我们建立了一套完整的GIS应用开发模式,特别是在处理真实地图数据源和优雅降级方面积累了宝贵经验。
|
||||
|
||||
### 问题1:地图组件初始化时缺少真实地图数据源
|
||||
|
||||
**问题描述:**
|
||||
- 初始实现的BaseMap组件只是简单的模拟展示,无法加载真实的卫星图像
|
||||
- 用户反馈参考文件可以看到真实的卫星图,但当前页面只显示占位符
|
||||
- 缺乏对真实地图服务商的集成支持
|
||||
|
||||
**原始需求分析:**
|
||||
- 需要支持真实的卫星影像显示,而不是简单的占位地图
|
||||
- 必须支持多种地图图层切换(卫星、电子、地形、混合)
|
||||
- 需要完整的地图交互功能,包括缩放、平移、全屏等
|
||||
|
||||
**解决方案:**
|
||||
- 复制完整的GISMapEngine实现,支持多种地图提供商
|
||||
- 实现leafletLoader动态加载器,支持异步加载地图库
|
||||
- 建立真实地图瓦片数据源连接,包括ArcGIS卫星影像和OpenStreetMap
|
||||
|
||||
**代码实现对比:**
|
||||
```tsx
|
||||
// ❌ 初始简化实现
|
||||
export class GISMapEngine {
|
||||
constructor(map: any) {
|
||||
this.map = map; // 只是一个模拟对象
|
||||
}
|
||||
addPolygon(polygon: MapPolygon): void {
|
||||
this.polygons.set(polygon.id, polygon); // 没有真实渲染
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 最终完整实现
|
||||
export class GISMapEngine {
|
||||
constructor(config: MapConfig) {
|
||||
this.provider = config.provider;
|
||||
this.initialize(config); // 真实初始化流程
|
||||
}
|
||||
|
||||
private async initLeaflet(config: MapConfig) {
|
||||
// 动态加载Leaflet库
|
||||
if (!window.L) {
|
||||
await this.loadLeaflet();
|
||||
}
|
||||
// 创建真实地图实例
|
||||
this.map = window.L.map(this.container).setView([center[1], center[0]], zoom);
|
||||
// 设置真实瓦片图层
|
||||
this.getLeafletLayer(layer).addTo(this.map);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题2:地图库异步加载和依赖管理复杂性
|
||||
|
||||
**问题描述:**
|
||||
- 地图库(Leaflet)需要从CDN异步加载,存在加载失败风险
|
||||
- 地图组件需要在库加载完成后才能初始化,存在时序问题
|
||||
- 多个地图组件可能重复加载同一资源,造成性能浪费
|
||||
|
||||
**原始需求分析:**
|
||||
- 确保地图库能够可靠加载,提供良好的用户体验
|
||||
- 处理加载失败的情况,提供优雅的降级方案
|
||||
- 优化资源加载性能,避免重复加载
|
||||
|
||||
**解决方案:**
|
||||
- 创建leafletLoader统一管理地图库的加载过程
|
||||
- 实现加载状态管理和重试机制
|
||||
- 建立全局加载状态缓存,避免重复加载
|
||||
|
||||
**关键实现代码:**
|
||||
```tsx
|
||||
// leafletLoader.ts - 统一加载管理
|
||||
export const preloadLeaflet = (): Promise<boolean> => {
|
||||
return new Promise((resolve) => {
|
||||
if (leafletLoaded || window.L) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (leafletLoading) {
|
||||
// 等待正在进行的加载完成
|
||||
const checkInterval = setInterval(() => {
|
||||
if (leafletLoaded || window.L) {
|
||||
clearInterval(checkInterval);
|
||||
resolve(true);
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行实际加载过程
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
|
||||
script.onload = () => { leafletLoaded = true; resolve(true); };
|
||||
script.onerror = () => { resolve(false); };
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 问题3:地图组件与状态管理的深度集成
|
||||
|
||||
**问题描述:**
|
||||
- 地图组件需要与useReducer状态管理深度集成
|
||||
- 地图的异步初始化过程与React生命周期存在冲突
|
||||
- 地图事件回调与状态更新的时序同步问题
|
||||
|
||||
**原始需求分析:**
|
||||
- 地图操作需要能够更新全局状态(如选中地块、图层切换)
|
||||
- 状态变化需要能够反映到地图显示上
|
||||
- 需要处理地图组件的清理和资源释放
|
||||
|
||||
**解决方案:**
|
||||
- 使用useImperativeHandle暴露地图实例方法给父组件
|
||||
- 实现地图引擎的引用管理,确保状态同步
|
||||
- 建立完整的生命周期管理,包括组件卸载时的资源清理
|
||||
|
||||
**状态管理集成代码:**
|
||||
```tsx
|
||||
// BaseMap组件中的状态集成
|
||||
useImperativeHandle(ref, () => ({
|
||||
getMapEngine: () => mapEngineRef.current,
|
||||
addMarker: (marker: Marker) => {
|
||||
mapEngineRef.current?.addMarker(marker);
|
||||
},
|
||||
addPolygon: (polygon: Polygon) => {
|
||||
mapEngineRef.current?.addPolygon(polygon);
|
||||
},
|
||||
setCenter: (position: MapPosition, zoom?: number) => {
|
||||
mapEngineRef.current?.setCenter(position, zoom);
|
||||
},
|
||||
}));
|
||||
|
||||
// 组件卸载时的资源清理
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (mapEngineRef.current) {
|
||||
mapEngineRef.current.destroy();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
```
|
||||
|
||||
### 问题4:真实地图数据源的集成和配置
|
||||
|
||||
**问题描述:**
|
||||
- 需要集成多种真实的地图瓦片数据源
|
||||
- 不同地图服务商的API格式和坐标系统存在差异
|
||||
- 需要处理地图瓦片的加载性能和缓存策略
|
||||
|
||||
**原始需求分析:**
|
||||
- 提供真实的卫星影像、电子地图、地形图等多种图层
|
||||
- 确保地图数据的准确性和时效性
|
||||
- 优化地图加载性能,提供流畅的用户体验
|
||||
|
||||
**解决方案:**
|
||||
- 集成多个开源地图数据源,确保服务的可靠性
|
||||
- 统一不同数据源的坐标系统和API格式
|
||||
- 实现智能的图层切换和缓存机制
|
||||
|
||||
**地图数据源配置:**
|
||||
```tsx
|
||||
private getLeafletLayer(layer: MapLayer) {
|
||||
const baseLayers: Record<MapLayer, string> = {
|
||||
// ArcGIS卫星影像 - 真实卫星图
|
||||
satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||
// OpenStreetMap - 开源电子地图
|
||||
street: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
// OpenTopoMap - 开源地形图
|
||||
terrain: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
|
||||
// 混合图层使用卫星影像作为基础
|
||||
hybrid: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||
};
|
||||
|
||||
return window.L.tileLayer(baseLayers[layer], {
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
maxZoom: 18,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 问题5:地图交互功能的完整实现
|
||||
|
||||
**问题描述:**
|
||||
- 需要实现地块多边形的渲染和交互
|
||||
- 地图标记点的添加和点击事件处理
|
||||
- 地图控件(缩放、图层切换、全屏等)的集成
|
||||
|
||||
**原始需求分析:**
|
||||
- 地块需要在地图上以彩色多边形形式显示
|
||||
- 点击地块需要触发选择事件和状态更新
|
||||
- 提供完整的地图导航和操作功能
|
||||
|
||||
**解决方案:**
|
||||
- 使用地图引擎的Polygon和Marker API实现地块渲染
|
||||
- 建立事件处理机制,连接地图交互和状态管理
|
||||
- 集成完整的地图控件套件,提供专业级用户体验
|
||||
|
||||
**交互功能实现:**
|
||||
```tsx
|
||||
// 地块多边形渲染
|
||||
const polygon: Polygon = {
|
||||
id: field.id,
|
||||
path: field.coordinates,
|
||||
fillColor: field.color,
|
||||
strokeColor: field.color,
|
||||
fillOpacity: 0.3,
|
||||
strokeWeight: 2,
|
||||
onClick: () => {
|
||||
onFieldSelect(field); // 更新全局状态
|
||||
toast.success(`已选择: ${field.name}`);
|
||||
},
|
||||
};
|
||||
engine.addPolygon(polygon);
|
||||
|
||||
// 地块标记点渲染
|
||||
const marker: Marker = {
|
||||
id: `marker-${field.id}`,
|
||||
position: { lat: centerLat, lng: centerLng },
|
||||
title: field.name,
|
||||
color: field.color,
|
||||
onClick: () => {
|
||||
onFieldSelect(field);
|
||||
toast.success(`已选择: ${field.name}`);
|
||||
},
|
||||
};
|
||||
engine.addMarker(marker);
|
||||
```
|
||||
|
||||
## 开发经验对比总结
|
||||
|
||||
### 与原始要求的差异分析
|
||||
|
||||
| 原始要求 | 实际实现 | 差异说明 | 解决过程 |
|
||||
|---------|---------|---------|---------|
|
||||
| 1:1还原地图功能 | 完整实现 + 真实数据源 | 需要集成真实地图服务商和瓦片数据 | 建立完整的地图引擎架构,支持多种数据源 |
|
||||
| 第三方库集成 | 专业级集成 | 需要处理异步加载、错误处理、性能优化 | 实现统一加载器和优雅降级机制 |
|
||||
| 组件状态管理 | 深度集成 + 生命周期管理 | 地图组件与React状态系统需要深度集成 | 使用useImperativeHandle和引用管理 |
|
||||
| 交互功能实现 | 完整交互套件 | 需要实现多边形、标记、控件等完整功能 | 集成地图引擎API,建立事件处理机制 |
|
||||
|
||||
### 关键学习点和改进
|
||||
|
||||
1. **第三方库集成思维**:学会了如何可靠地集成和管理复杂的第三方JavaScript库
|
||||
- 掌握了异步加载、错误处理、优雅降级的完整流程
|
||||
- 理解了库版本管理和兼容性处理的重要性
|
||||
|
||||
2. **地图API应用经验**:深入了解了Web地图开发的技术栈和最佳实践
|
||||
- 学会了瓦片地图的原理和多种数据源的使用
|
||||
- 掌握了地图交互事件的处理和状态同步机制
|
||||
|
||||
3. **React高级模式应用**:在复杂组件中应用了useImperativeHandle、useRef等高级React模式
|
||||
- 深入理解了React组件的暴露方法和引用传递机制
|
||||
- 掌握了复杂组件生命周期管理的最佳实践
|
||||
|
||||
4. **性能优化意识**:建立了地图应用的性能优化思维
|
||||
- 学会了资源懒加载和缓存策略的设计
|
||||
- 理解了大型第三方库对应用性能的影响和优化方法
|
||||
|
||||
5. **用户体验设计**:在技术实现中始终考虑用户体验
|
||||
- 建立了加载状态和错误处理的设计模式
|
||||
- 掌握了优雅降级和渐进增强的实现方法
|
||||
|
||||
6. **架构设计能力**:设计了可扩展的地图应用架构
|
||||
- 建立了插件化的地图引擎设计
|
||||
|
||||
---
|
||||
|
||||
## path:src/app/(app)/land-information/map/draw,name:数字化绘制与编辑页面开发经验
|
||||
|
||||
### 1. **复杂状态管理设计**
|
||||
- **useReducer 模式应用**:使用 useReducer 管理复杂的编辑状态,包含多个布尔状态、数组和对象
|
||||
- **状态结构设计**:设计了包含高级编辑器状态、活动标签页、地块数据、保存对话框等的状态结构
|
||||
- **Action 设计模式**:采用类型安全的 Action 设计,支持状态更新、字段管理、对话框控制等操作
|
||||
|
||||
### 2. **组件化架构设计**
|
||||
- **模块化组件结构**:将复杂功能拆分为6个独立组件,每个组件负责单一职责
|
||||
- `drawEditReducer.tsx`:状态管理核心
|
||||
- `DrawingTools.tsx`:绘制工具组件
|
||||
- `EditingTools.tsx`:编辑工具组件
|
||||
- `FieldEntryDialog.tsx`:地块信息录入对话框
|
||||
- `UsageGuide.tsx`:使用指南组件
|
||||
- `AdvancedEditorPromo.tsx`:高级编辑器推广组件
|
||||
- **组件通信设计**:通过 props 和回调函数实现组件间的数据传递和事件处理
|
||||
|
||||
### 3. **Canvas 绘图技术实现**
|
||||
- **多种绘制模式**:实现点、线、多边形、矩形等多种绘制模式
|
||||
- **实时交互反馈**:支持鼠标移动吸附、节点高亮、实时预览等功能
|
||||
- **几何计算算法**:
|
||||
- Shoelace 公式计算多边形面积
|
||||
- 坐标距离计算周长
|
||||
- 点在多边形内判断算法
|
||||
- 自相交检测算法
|
||||
|
||||
### 4. **高级编辑功能实现**
|
||||
- **节点编辑**:支持拖拽节点、添加节点、删除节点
|
||||
- **地块分割**:绘制分割线将地块分成两部分,支持垂直和水平分割
|
||||
- **地块合并**:多地块选择和凸包算法合并
|
||||
- **历史记录管理**:实现撤销/重做功能,支持操作历史追踪
|
||||
|
||||
### 5. **用户体验设计**
|
||||
- **分步骤操作引导**:为复杂操作提供详细的操作步骤说明
|
||||
- **实时状态反馈**:Toast 通知、状态栏显示、操作确认等
|
||||
- **键盘快捷键支持**:Ctrl+Z 撤销、Ctrl+S 保存、Delete 清除、Esc 取消
|
||||
- **视觉状态管理**:选中高亮、禁用状态、加载状态等
|
||||
|
||||
### 6. **数据管理与持久化**
|
||||
- **表单验证设计**:完整的表单验证逻辑,支持必填项检查和格式验证
|
||||
- **本地存储集成**:与 localStorage 集成,支持地块数据的持久化
|
||||
- **自动数据生成**:地块编号、名称的自动生成逻辑
|
||||
- **标签管理功能**:支持标签的添加、删除和展示
|
||||
|
||||
### 7. **技术规范遵循**
|
||||
- **shadcn/ui 语义样式**:使用 `bg-card`、`bg-muted`、`text-muted-foreground` 等语义化样式
|
||||
- **暗色主题支持**:完整支持暗色主题,使用 `dark:` 前缀
|
||||
- **TypeScript 类型安全**:完整的类型定义,确保类型安全
|
||||
- **响应式设计**:支持不同屏幕尺寸的适配
|
||||
|
||||
### 8. **开发效率提升**
|
||||
- **组件复用设计**:通用组件可在其他页面复用
|
||||
- **配置化参数**:画布尺寸、吸附距离等参数可配置
|
||||
- **错误处理机制**:完善的错误处理和用户提示
|
||||
- **代码组织结构**:清晰的文件结构和命名规范
|
||||
|
||||
### 9. **性能优化考虑**
|
||||
- **事件处理优化**:使用 useCallback 避免不必要的重渲染
|
||||
- **状态更新策略**:合理的状态更新时机和批量处理
|
||||
- **Canvas 渲染优化**:减少不必要的重绘和计算
|
||||
|
||||
### 10. **可扩展性设计**
|
||||
- **插件化架构**:编辑工具采用插件化设计,易于扩展新功能
|
||||
- **接口标准化**:统一的接口设计,便于功能模块替换
|
||||
- **配置化开发**:支持通过配置文件调整功能和行为
|
||||
- 理解了复杂应用中的组件分层和职责划分
|
||||
26
crop-x/env/.env.dev
vendored
Normal file
26
crop-x/env/.env.dev
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# 开发环境配置
|
||||
NODE_ENV=development
|
||||
|
||||
# 前端域名
|
||||
FRONTEND_BASE_URL=https://cavin-smart-crop-ui-app.dev.maimaiag.com
|
||||
|
||||
# 后端 API 地址
|
||||
BACKEND_BASE_URL=https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com/
|
||||
|
||||
# OpenAPI 生成配置
|
||||
API_BASE_URL=https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com
|
||||
|
||||
# API 版本
|
||||
API_VERSION=v1
|
||||
|
||||
# 调试模式
|
||||
DEBUG=true
|
||||
|
||||
# 是否开启 Mock 数据
|
||||
USE_MOCK=false
|
||||
|
||||
# 应用名称
|
||||
APP_NAME=智慧农业生产管理系统
|
||||
|
||||
# 环境描述
|
||||
ENV_DESCRIPTION=开发环境
|
||||
43
crop-x/env/.env.prod
vendored
Normal file
43
crop-x/env/.env.prod
vendored
Normal 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
28
crop-x/env/.env.test
vendored
Normal 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
30
crop-x/env/.env.uat
vendored
Normal 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
|
||||
2
crop-x/next-env.d.ts
vendored
2
crop-x/next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -11,7 +11,9 @@ const nextConfig = {
|
||||
// 修复CSS构建问题
|
||||
experimental: {
|
||||
// forceSwcTransforms: true,
|
||||
turbo: {
|
||||
},
|
||||
// 新的 Turbopack 配置
|
||||
turbopack: {
|
||||
rules: {
|
||||
'*.svg': {
|
||||
loaders: ['@svgr/webpack'],
|
||||
@@ -19,6 +21,16 @@ const nextConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
// 解决工作区根目录问题
|
||||
outputFileTracingRoot: process.cwd(),
|
||||
// 添加代理配置解决CORS问题
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com/api/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
17
crop-x/openapi-ts.config.ts
Normal file
17
crop-x/openapi-ts.config.ts
Normal 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",
|
||||
});
|
||||
1032
crop-x/package-lock.json
generated
1032
crop-x/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,13 @@
|
||||
"start": "next start",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"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",
|
||||
"scripts:setup": "node scripts/setup-dev-tools.js",
|
||||
"scripts:enable": "node scripts/setup-dev-tools.js --enable",
|
||||
"scripts:disable": "node scripts/setup-dev-tools.js --disable",
|
||||
"deploy": "node scripts/deploy.js"
|
||||
"api:generate": "node scripts/generate-api.cjs",
|
||||
"deploy": "node scripts/deploy.js",
|
||||
"build:dev": "node scripts/build.cjs dev",
|
||||
"build:test": "node scripts/build.cjs test",
|
||||
"build:uat": "node scripts/build.cjs uat",
|
||||
"build:prod": "node scripts/build.cjs prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
@@ -54,8 +54,9 @@
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.5.6",
|
||||
"next": "^16.0.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"openapi-fetch": "^0.15.0",
|
||||
"qrcode": "*",
|
||||
"react": "^19.2.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
@@ -71,8 +72,10 @@
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@hey-api/client-fetch": "^0.13.1",
|
||||
"@hey-api/openapi-ts": "^0.86.6",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.3.11",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
@@ -87,11 +90,13 @@
|
||||
"husky": "^9.1.6",
|
||||
"install": "^0.13.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"node-fetch": "^3.3.2",
|
||||
"npm": "^11.6.2",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.3",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
387
crop-x/scripts/build.cjs
Normal file
387
crop-x/scripts/build.cjs
Normal 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
|
||||
};
|
||||
388
crop-x/scripts/generate-api.cjs
Normal file
388
crop-x/scripts/generate-api.cjs
Normal file
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* 简化的 API 生成脚本
|
||||
*
|
||||
* 这个脚本现在主要负责:
|
||||
* 1. 使用 @hey-api/openapi-ts 命令生成客户端代码
|
||||
* 2. 环境配置通过 openapi-ts.config.ts 处理
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 从环境配置文件中读取 API_BASE_URL
|
||||
function getApiBaseUrl() {
|
||||
try {
|
||||
// 首先尝试从 env/.env.dev 文件读取
|
||||
const envDevPath = path.join(process.cwd(), 'env', '.env.dev');
|
||||
if (fs.existsSync(envDevPath)) {
|
||||
const envContent = fs.readFileSync(envDevPath, 'utf8');
|
||||
const apiBaseUrlMatch = envContent.match(/API_BASE_URL=([^\r\n]+)/);
|
||||
|
||||
if (apiBaseUrlMatch && apiBaseUrlMatch[1]) {
|
||||
return apiBaseUrlMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果上面的文件不存在,尝试从 TypeScript 环境配置文件读取
|
||||
const envConfigPath = path.join(process.cwd(), 'src', 'env', 'index.ts');
|
||||
if (fs.existsSync(envConfigPath)) {
|
||||
const envContent = fs.readFileSync(envConfigPath, 'utf8');
|
||||
const devConfigMatch = envContent.match(/dev:\s*\{[\s\S]*?BACKEND_BASE_URL:\s*['"`]([^'"`]+)['"`]/);
|
||||
|
||||
if (devConfigMatch && devConfigMatch[1]) {
|
||||
return devConfigMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('无法找到 API_BASE_URL 或 BACKEND_BASE_URL 配置');
|
||||
} catch (error) {
|
||||
console.log(`读取环境配置失败: ${error.message}`);
|
||||
return 'http://localhost:8080'; // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 API 基础 URL
|
||||
const API_BASE_URL = getApiBaseUrl();
|
||||
|
||||
// 设置环境变量供后续使用
|
||||
process.env.API_BASE_URL = API_BASE_URL;
|
||||
|
||||
// ANSI 颜色代码
|
||||
const colors = {
|
||||
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 服务器: ${API_BASE_URL}`);
|
||||
logInfo(`已从 env/.env.dev 读取 API_BASE_URL 配置`);
|
||||
|
||||
/**
|
||||
* 检查自定义文件是否存在
|
||||
* 如果某些文件已经被自定义,我们不希望覆盖它们
|
||||
*/
|
||||
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.json 文件到本地
|
||||
*/
|
||||
function downloadOpenApiJson() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const fileUrl = `${API_BASE_URL}/openapi.json`;
|
||||
const outputPath = path.join(process.cwd(), 'scripts', 'openapi.json');
|
||||
|
||||
logInfo(`下载 OpenAPI 规范文件: ${fileUrl}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// 创建 HTTPS 代理(如果需要,可以忽略 SSL 证书验证)
|
||||
const agent = new https.Agent({
|
||||
rejectUnauthorized: false // 跳过 SSL 证书验证
|
||||
});
|
||||
|
||||
https.get(fileUrl, { agent }, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
||||
return;
|
||||
}
|
||||
|
||||
let data = '';
|
||||
|
||||
response.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
response.on('end', () => {
|
||||
try {
|
||||
// 验证 JSON 格式
|
||||
JSON.parse(data);
|
||||
|
||||
// 写入文件
|
||||
fs.writeFileSync(outputPath, data, 'utf8');
|
||||
const executionTime = Date.now() - startTime;
|
||||
logSuccess(`OpenAPI 规范已保存到: scripts/openapi.json (${executionTime}ms)`);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(new Error(`JSON 格式错误: ${error.message}`));
|
||||
}
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 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.json 文件到本地
|
||||
await downloadOpenApiJson();
|
||||
|
||||
// 使用 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();
|
||||
}
|
||||
1
crop-x/scripts/openapi.json
Normal file
1
crop-x/scripts/openapi.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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'
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
@@ -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`)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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}`)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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'
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function AssetLabelingLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <>{children}</>
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="">
|
||||
资产标签
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/basic/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/basic/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/basic/tools/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/basic/tools/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/equipment/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/equipment/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/inventory/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/inventory/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -1,147 +1,18 @@
|
||||
import Link from 'next/link'
|
||||
import { Metadata } from 'next'
|
||||
'use client';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '农业资产管理 - Crop-X 智慧农业管理系统',
|
||||
description: '农业资产管理系统主页面',
|
||||
}
|
||||
import { Card } from '@/components/ui/card';
|
||||
|
||||
export default function AgriculturalAssetPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
||||
农业资产管理系统
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-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
|
||||
</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>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/purchase/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/purchase/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/report/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/report/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/requisition/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/requisition/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
18
crop-x/src/app/(app)/agricultural-asset/return/page.tsx
Normal file
18
crop-x/src/app/(app)/agricultural-asset/return/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1,71 +1,30 @@
|
||||
import { Metadata } from 'next'
|
||||
'use client';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '农机档案管理 - Crop-X 智慧农业管理系统',
|
||||
description: '农机设备档案信息管理',
|
||||
}
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Package } from 'lucide-react';
|
||||
|
||||
export default function MachineryArchivePage() {
|
||||
export default function ArchivePage() {
|
||||
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 md:grid-cols-3 gap-6">
|
||||
<div className="bg-green-50 rounded-lg p-6 hover:bg-green-100 transition-colors cursor-pointer">
|
||||
<h3 className="font-semibold text-green-900 mb-2">
|
||||
📝 农机档案录入与维护
|
||||
</h3>
|
||||
<p className="text-green-700 text-sm">
|
||||
管理农机设备的基本信息档案
|
||||
</p>
|
||||
<Card className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Package className="w-6 h-6 text-blue-600" />
|
||||
<h2 className="text-xl font-semibold">农机档案</h2>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
农机设备分类和标签体系管理
|
||||
<div className="space-y-3">
|
||||
<p className="text-muted-foreground">
|
||||
农机档案管理模块用于管理农业机械的基础信息和档案资料。
|
||||
</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">
|
||||
农机设备二维码生成和管理
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<p className="text-sm">
|
||||
<strong>页面路径:</strong> /agricultural-machinery/archive
|
||||
</p>
|
||||
<p className="text-sm mt-1">
|
||||
<strong>主要功能:</strong>农机信息录入、分类管理、二维码生成等
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</Card>
|
||||
</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>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user