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 -->
|
# <!-- Powered by BMAD™ Core -->
|
||||||
template:
|
template:
|
||||||
id: competitor-analysis-template-v2
|
id: competitor-analysis-template-v2
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -147,3 +147,4 @@ Thumbs.db
|
|||||||
tmp/
|
tmp/
|
||||||
temp/
|
temp/
|
||||||
nul
|
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" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <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
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ const nextConfig = {
|
|||||||
// 修复CSS构建问题
|
// 修复CSS构建问题
|
||||||
experimental: {
|
experimental: {
|
||||||
// forceSwcTransforms: true,
|
// forceSwcTransforms: true,
|
||||||
turbo: {
|
},
|
||||||
|
// 新的 Turbopack 配置
|
||||||
|
turbopack: {
|
||||||
rules: {
|
rules: {
|
||||||
'*.svg': {
|
'*.svg': {
|
||||||
loaders: ['@svgr/webpack'],
|
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",
|
"start": "next start",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"lint:fix": "eslint . --ext ts,tsx --fix",
|
"lint:fix": "eslint . --ext ts,tsx --fix",
|
||||||
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
||||||
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
"scripts:setup": "node scripts/setup-dev-tools.js",
|
"api:generate": "node scripts/generate-api.cjs",
|
||||||
"scripts:enable": "node scripts/setup-dev-tools.js --enable",
|
"deploy": "node scripts/deploy.js",
|
||||||
"scripts:disable": "node scripts/setup-dev-tools.js --disable",
|
"build:dev": "node scripts/build.cjs dev",
|
||||||
"deploy": "node scripts/deploy.js"
|
"build:test": "node scripts/build.cjs test",
|
||||||
|
"build:uat": "node scripts/build.cjs uat",
|
||||||
|
"build:prod": "node scripts/build.cjs prod"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
@@ -54,8 +54,9 @@
|
|||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lucide-react": "^0.487.0",
|
"lucide-react": "^0.487.0",
|
||||||
"next": "^15.5.6",
|
"next": "^16.0.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"openapi-fetch": "^0.15.0",
|
||||||
"qrcode": "*",
|
"qrcode": "*",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-day-picker": "^9.11.1",
|
"react-day-picker": "^9.11.1",
|
||||||
@@ -71,8 +72,10 @@
|
|||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.14",
|
"@hey-api/client-fetch": "^0.13.1",
|
||||||
|
"@hey-api/openapi-ts": "^0.86.6",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
@@ -87,11 +90,13 @@
|
|||||||
"husky": "^9.1.6",
|
"husky": "^9.1.6",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
|
"node-fetch": "^3.3.2",
|
||||||
"npm": "^11.6.2",
|
"npm": "^11.6.2",
|
||||||
|
"openapi-typescript": "^7.10.1",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^6.4.0"
|
"vite": "^6.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
387
crop-x/scripts/build.cjs
Normal file
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'
|
'use client';
|
||||||
import { Metadata } from 'next'
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
import { Card } from '@/components/ui/card';
|
||||||
title: '农业资产管理 - Crop-X 智慧农业管理系统',
|
|
||||||
description: '农业资产管理系统主页面',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AgriculturalAssetPage() {
|
export default function AgriculturalAssetPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="bg-white rounded-lg shadow p-6">
|
<Card className="p-6">
|
||||||
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
<h2 className="text-xl font-semibold">农业资产管理</h2>
|
||||||
农业资产管理系统
|
<div className="p-3 bg-muted rounded-lg mt-3">
|
||||||
</h2>
|
<p className="text-sm">
|
||||||
<p className="text-gray-600 mb-6">
|
<strong>页面路径:</strong> /agricultural-asset
|
||||||
管理农业资产信息、采购库存、物资领用和可视化报表
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/basic-information"
|
|
||||||
className="block p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-green-900 mb-2">
|
|
||||||
📋 基础信息管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-green-700 text-sm">
|
|
||||||
资产信息录入和分类管理
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/procurement-management"
|
|
||||||
className="block p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-blue-900 mb-2">
|
|
||||||
🛒 采购管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-blue-700 text-sm">
|
|
||||||
采购计划和供应商管理
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/inventory-management"
|
|
||||||
className="block p-4 bg-purple-50 rounded-lg hover:bg-purple-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-purple-900 mb-2">
|
|
||||||
📦 库存管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-purple-700 text-sm">
|
|
||||||
库存监控和调整管理
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/material-requisition"
|
|
||||||
className="block p-4 bg-orange-50 rounded-lg hover:bg-orange-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-orange-900 mb-2">
|
|
||||||
📤 物资领用管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-orange-700 text-sm">
|
|
||||||
领用申请和审批流程
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/material-return"
|
|
||||||
className="block p-4 bg-teal-50 rounded-lg hover:bg-teal-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-teal-900 mb-2">
|
|
||||||
📥 物资归还管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-teal-700 text-sm">
|
|
||||||
归还申请和跟踪管理
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/agricultural-supplies"
|
|
||||||
className="block p-4 bg-indigo-50 rounded-lg hover:bg-indigo-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-indigo-900 mb-2">
|
|
||||||
🚜 农资农具管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-indigo-700 text-sm">
|
|
||||||
农具农资和维护管理
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/agricultural-asset/visualization-reports"
|
|
||||||
className="block p-4 bg-pink-50 rounded-lg hover:bg-pink-100 transition-colors"
|
|
||||||
>
|
|
||||||
<h3 className="font-semibold text-pink-900 mb-2">
|
|
||||||
📊 可视化报表
|
|
||||||
</h3>
|
|
||||||
<p className="text-pink-700 text-sm">
|
|
||||||
资产库存成本分析报表
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Card>
|
||||||
</div>
|
</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 = {
|
import { Card } from '@/components/ui/card';
|
||||||
title: '农机档案管理 - Crop-X 智慧农业管理系统',
|
import { Package } from 'lucide-react';
|
||||||
description: '农机设备档案信息管理',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function MachineryArchivePage() {
|
export default function ArchivePage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="bg-white rounded-lg shadow p-6">
|
<Card className="p-6">
|
||||||
<h2 className="text-2xl font-bold text-gray-800 mb-6">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
📋 农机档案管理
|
<Package className="w-6 h-6 text-blue-600" />
|
||||||
</h2>
|
<h2 className="text-xl font-semibold">农机档案</h2>
|
||||||
|
|
||||||
<div 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>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-3">
|
||||||
<div className="bg-blue-50 rounded-lg p-6 hover:bg-blue-100 transition-colors cursor-pointer">
|
<p className="text-muted-foreground">
|
||||||
<h3 className="font-semibold text-blue-900 mb-2">
|
农机档案管理模块用于管理农业机械的基础信息和档案资料。
|
||||||
🏷️ 农机分类与标签管理
|
|
||||||
</h3>
|
|
||||||
<p className="text-blue-700 text-sm">
|
|
||||||
农机设备分类和标签体系管理
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<div className="p-3 bg-muted rounded-lg">
|
||||||
|
<p className="text-sm">
|
||||||
<div className="bg-purple-50 rounded-lg p-6 hover:bg-purple-100 transition-colors cursor-pointer">
|
<strong>页面路径:</strong> /agricultural-machinery/archive
|
||||||
<h3 className="font-semibold text-purple-900 mb-2">
|
</p>
|
||||||
📱 农机二维码管理
|
<p className="text-sm mt-1">
|
||||||
</h3>
|
<strong>主要功能:</strong>农机信息录入、分类管理、二维码生成等
|
||||||
<p className="text-purple-700 text-sm">
|
|
||||||
农机设备二维码生成和管理
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Card>
|
||||||
<div className="mt-6 bg-gray-50 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">
|
|
||||||
📊 档案统计概览
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
||||||
<div className="bg-white rounded-lg p-4 text-center">
|
|
||||||
<div className="text-2xl font-bold text-green-600 mb-2">156</div>
|
|
||||||
<div className="text-sm text-gray-600">农机总数</div>
|
|
||||||
</div>
|
</div>
|
||||||
<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