修改文件结构,统一文档格式

This commit is contained in:
ylweng
2025-09-01 02:42:03 +08:00
parent 2bd1d8c032
commit abc1184f81
151 changed files with 870 additions and 589 deletions

121
README.md Normal file
View File

@@ -0,0 +1,121 @@
# 宁夏智慧养殖监管平台
## 项目概述
宁夏智慧养殖监管平台是一个现代化的农场管理系统,提供实时监控、数据分析和多终端访问功能。
## 目录结构
```
nxxmdata/
├── backend/ # 后端服务 (Node.js + Express + Sequelize)
│ ├── config/ # 配置文件
│ ├── controllers/ # 业务逻辑控制器
│ ├── models/ # 数据模型
│ ├── routes/ # API路由
│ ├── middleware/ # 中间件
│ ├── utils/ # 工具类
│ ├── migrations/ # 数据库迁移
│ ├── seeds/ # 种子数据
│ └── server.js # 服务器入口
├── admin-system/ # 管理后台 (Vue.js 3.x)
│ └── frontend/ # 前端项目
├── website/ # 官网 (Vue.js 3.x)
│ └── data-screen/ # 数据大屏展示
├── mini_program/ # 微信小程序矩阵
│ └── farm-monitor-dashboard/ # 监控仪表盘小程序
├── scripts/ # 执行脚本目录
│ ├── init-db.js # 数据库初始化
│ ├── migration-manager.js # 迁移管理
│ ├── seed-manager.js # 种子数据管理
│ ├── test-connection.js # 连接测试
│ └── test-map-api.js # 地图API测试
├── docs/ # 文档目录
│ ├── baidu-map-license.md # 百度地图许可
│ └── performance-monitoring.md # 性能监控文档
├── test/ # 测试文件目录
│ └── performance-monitor-test.js # 性能监控测试
├── examples/ # 示例代码
└── 配置文件
├── arch.md # 架构文档
├── design.md # 详细设计
├── dev-plan.md # 开发计划
├── task.md # 任务列表
├── schema.sql # 数据库架构
└── create_tables.sql # 建表脚本
```
## 技术栈
### 后端技术栈
- **运行环境**: Node.js 18.0+
- **Web框架**: Express.js 4.18+
- **ORM框架**: Sequelize 6.30+
- **数据库**: MySQL (mysql2 3.0+)
- **认证**: JWT (jsonwebtoken 9.0+)
- **密码加密**: bcrypt 5.1+
- **API文档**: Swagger (swagger-jsdoc + swagger-ui-express)
- **日志管理**: Winston 3.17+
- **开发工具**: nodemon 3.0+
- **跨域处理**: CORS 2.8+
- **数据验证**: express-validator 7.2+
- **环境变量**: dotenv 16.0+
- **HTTP客户端**: Axios 1.4+
- **服务器端口**: 5350
### 前端技术栈
- **管理后台**: Vue.js 3.4+ + Ant Design Vue 4.0+
- **官网**: Vue.js 3.4+ + ECharts 5.4+
- **微信小程序**: React + TypeScript + Tailwind CSS
- **构建工具**: Vite 5.0+
- **状态管理**: Pinia 2.0+
- **路由管理**: Vue Router 4.0+
## 核心功能
- ✅ 实时设备监控
- ✅ 动物健康管理
- ✅ 地图可视化展示
- ✅ 数据分析报表
- ✅ 多角色权限系统
- ✅ 订单管理功能
- ✅ 预警管理系统
- ✅ 性能监控分析
## 快速开始
### 后端启动
```bash
cd backend
npm install
npm run dev
```
### 管理后台启动
```bash
cd admin-system/frontend
npm install
npm run dev
```
### 官网启动
```bash
cd website/data-screen
npm install
npm run dev
```
## 开发规范
1. 遵循模块化开发原则
2. 代码注释规范清晰
3. API接口文档完整
4. 数据库迁移脚本可重复执行
5. 测试用例覆盖核心功能
## 部署说明
生产环境建议使用Docker容器化部署具体部署流程参考各模块的部署文档。
---
*最后更新: 2025年8月*

130
arch.md
View File

@@ -57,12 +57,6 @@
- `middleware/`: 中间件
- `auth.js`: 认证中间件
- `performance-middleware.js`: 性能监控中间件
- `scripts/`: 数据库管理脚本
- `init-db.js`: 数据库初始化
- `migration-manager.js`: 迁移管理
- `seed-manager.js`: 种子数据管理
- `test-connection.js`: 连接测试
- `test-map-api.js`: 地图API测试
- `utils/`: 工具类
- `logger.js`: 日志工具
- `performance-monitor.js`: 性能监控
@@ -72,59 +66,77 @@
- `seeds/`: 种子数据文件
- `logs/`: 日志文件目录
### 2.2 前端 (`frontend/`)
- `src/components/`: 可复用的UI组件
- `BaiduMap.vue`: 百度地图组件
- `EChart.vue`: 图组件
- `Menu.vue`: 导航菜单
- `Dashboard.vue`: 仪表盘组件
- `AlertStats.vue`: 告警统计组件
- `AnimalStats.vue`: 动物统计组件
- `DeviceStats.vue`: 设备统计组件
- `FarmDetail.vue`: 农场详情组件
- `MonitorChart.vue`: 监控图表组件
- `ChartPerformanceMonitor.vue`: 性能监控图表
- `VirtualScrollChart.vue`: 虚拟滚动图表
- `src/views/`: 页面级组件
- `Home.vue`: 首页
- `Dashboard.vue`: 系统概览
- `MapView.vue`: 地图监控
- `Analytics.vue`: 数据分析
- `Monitor.vue`: 实时监控
- `Login.vue`: 登录页面
- `Users.vue`: 用户管理
- `Products.vue`: 产品管理
- `Orders.vue`: 订单管理
- `Devices.vue`: 设备管理
- `Animals.vue`: 动物管理
- `Alerts.vue`: 预警管理
- `TestAnalytics.vue`: 测试分析页面
- `MapZoomDemo.vue`: 地图缩放演示
- `NotFound.vue`: 404页面
- `src/stores/`: 状态管理Pinia
- `user.js`: 用户状态管理
- `data.js`: 数据状态管理
- `settings.js`: 设置状态管理
- `index.js`: 状态管理入口
- `src/router/`: 路由配置
- `index.js`: 路由实例和守卫
- `routes.js`: 路由定义
- `src/utils/`: 工具类API调用、图表服务等
- `api.js`: API请求封装
- `src/config/`: 配置文件
- `public/`: 静态资源和测试页面
- `debug-devices.html`: 设备调试页面
- `debug-users.html`: 用户调试页面
- `map-test.html`: 地图测试页面
- `test-auto-login.html`: 自动登录测试
- `test-users-display.html`: 用户显示测试
### 2.2 管理后台 (`admin-system/`)
- `frontend/`: 管理后台前端项目
- `src/components/`: 可复用的UI组件
- `BaiduMap.vue`: 百度地图组件
- `EChart.vue`: 图表组件
- `Menu.vue`: 导航菜单
- `Dashboard.vue`: 仪表盘组件
- `AlertStats.vue`: 告警统计组件
- `AnimalStats.vue`: 动物统计组件
- `DeviceStats.vue`: 设备统计组件
- `FarmDetail.vue`: 农场详情组件
- `MonitorChart.vue`: 监控图表组件
- `ChartPerformanceMonitor.vue`: 性能监控图表
- `VirtualScrollChart.vue`: 虚拟滚动图表
- `src/views/`: 页面级组件
- `Home.vue`: 首页
- `Dashboard.vue`: 系统概览
- `MapView.vue`: 地图监控
- `Analytics.vue`: 数据分析
- `Monitor.vue`: 实时监控
- `Login.vue`: 登录页面
- `Users.vue`: 用户管理
- `Products.vue`: 产品管理
- `Orders.vue`: 订单管理
- `Devices.vue`: 设备管理
- `Animals.vue`: 动物管理
- `Alerts.vue`: 预警管理
- `TestAnalytics.vue`: 测试分析页面
- `MapZoomDemo.vue`: 地图缩放演示
- `NotFound.vue`: 404页面
- `src/stores/`: 状态管理Pinia
- `user.js`: 用户状态管理
- `data.js`: 数据状态管理
- `settings.js`: 设置状态管理
- `index.js`: 状态管理入口
- `src/router/`: 路由配置
- `index.js`: 路由实例和守卫
- `routes.js`: 路由定义
- `src/utils/`: 工具类API调用、图表服务等
- `api.js`: API请求封装
- `src/config/`: 配置文件
- `public/`: 静态资源和测试页面
- `debug-devices.html`: 设备调试页面
- `debug-users.html`: 用户调试页面
- `map-test.html`: 地图测试页面
- `test-auto-login.html`: 自动登录测试
- `test-users-display.html`: 用户显示测试
### 2.3 其他
- `docs/`: 项目文档
- `baidu-map-license.md`: 百度地图许可文档
- `performance-monitoring.md`: 性能监控文档
- `tests/`: 测试脚本
- `performance-monitor-test.js`: 性能监控测试
### 2.3 官网 (`website/`)
- `data-screen/`: 官网前端项目
- 包含官网展示页面和数据大屏功能
### 2.4 微信小程序 (`mini_program/`)
- `farm-monitor-dashboard/`: 微信小程序项目
- 包含移动端监控仪表盘功能
### 2.5 脚本目录 (`scripts/`)
- `init-db.js`: 数据库初始化
- `migration-manager.js`: 迁移管理
- `seed-manager.js`: 种子数据管理
- `test-connection.js`: 连接测试
- `test-map-api.js`: 地图API测试
### 2.6 文档目录 (`docs/`)
- `baidu-map-license.md`: 百度地图许可文档
- `performance-monitoring.md`: 性能监控文档
### 2.7 测试目录 (`test/`)
- `performance-monitor-test.js`: 性能监控测试
### 2.8 其他文件
- `examples/`: 示例代码
- `performance-monitor-integration.js`: 性能监控集成示例
- `create_tables.sql`: 数据库表创建脚本
@@ -162,7 +174,7 @@
## 4. 核心架构
### 4.1 技术栈
**后端技术栈:**
- **运行环境**: Node.js
- **运行环境**: Node.js 18.0+
- **Web框架**: Express.js 4.18+
- **ORM框架**: Sequelize 6.30+
- **数据库**: MySQL (mysql2 3.0+)

View File

@@ -306,6 +306,55 @@ const options = {
description: '更新时间'
}
}
},
FarmInput: {
type: 'object',
required: ['name', 'type', 'location'],
properties: {
name: {
type: 'string',
description: '养殖场名称'
},
type: {
type: 'string',
description: '养殖场类型'
},
location: {
type: 'object',
required: ['latitude', 'longitude'],
properties: {
latitude: {
type: 'number',
format: 'float',
description: '纬度'
},
longitude: {
type: 'number',
format: 'float',
description: '经度'
}
},
description: '地理位置'
},
address: {
type: 'string',
description: '详细地址'
},
contact: {
type: 'string',
description: '联系人'
},
phone: {
type: 'string',
description: '联系电话'
},
status: {
type: 'string',
enum: ['active', 'inactive', 'maintenance'],
description: '养殖场状态',
default: 'active'
}
}
}
}
},

View File

@@ -20,6 +20,7 @@ const userController = require('../controllers/userController');
* - id
* - username
* - email
* - password
* properties:
* id:
* type: integer
@@ -30,10 +31,36 @@ const userController = require('../controllers/userController');
* email:
* type: string
* description: 邮箱地址
* password:
* type: string
* description: 密码(加密存储)
* phone:
* type: string
* description: 手机号码
* avatar:
* type: string
* description: 头像URL
* status:
* type: string
* enum: [active, inactive, suspended]
* description: 用户状态
* createdAt:
* type: string
* format: date-time
* description: 创建时间
* updatedAt:
* type: string
* format: date-time
* description: 更新时间
* example:
* id: 1
* username: "john_doe"
* email: "john@example.com"
* phone: "13800138000"
* avatar: "/uploads/avatars/default.png"
* status: "active"
* createdAt: "2024-01-01T00:00:00.000Z"
* updatedAt: "2024-01-01T00:00:00.000Z"
*/
/**

136
design.md
View File

@@ -26,7 +26,14 @@
## 2. 技术架构设计
### 2.1 整体架构
本项目采用前后端分离的架构设计模式,前端使用Vue.js框架后端采用Python技术栈数据库使用MySQL进行数据存储。
本项目采用前后端分离的架构设计模式,包含以下主要模块:
- **后端服务** (`backend/`): Node.js + Express + Sequelize ORM
- **管理后台** (`admin-system/`): Vue.js 3.x + Ant Design Vue
- **官网** (`website/`): Vue.js 3.x + 数据大屏展示
- **微信小程序** (`mini_program/`): React + TypeScript + Tailwind CSS
- **脚本工具** (`scripts/`): 数据库管理和维护脚本
- **文档** (`docs/`): 项目技术文档
- **测试** (`test/`): 测试脚本和用例
### 2.2 前端技术栈
- **核心框架**: Vue.js 3.x
@@ -38,23 +45,23 @@
- **图表库**: ECharts
### 2.3 后端技术栈
- **运行环境**: Python 3.8+
- **Web框架**: FastAPI 或 Flask可选
- **运行环境**: Node.js
- **Web框架**: Express.js 4.18+
- **API风格**: RESTful API
- **认证授权**: JWT
- **数据库访问**: SQLAlchemy 或 Peewee ORM
- **依赖管理**: uv 工具
- **认证授权**: JWT (jsonwebtoken 9.0+)
- **数据库访问**: Sequelize ORM 6.30+
- **数据库**: MySQL (mysql2 3.0+)
- **密码加密**: bcrypt 5.1+
- **API文档**: Swagger (swagger-jsdoc + swagger-ui-express)
- **日志管理**: Winston 3.17+
- **开发工具**: nodemon 3.0+
- **跨域处理**: CORS 2.8+
- **数据验证**: express-validator 7.2+
- **环境变量**: dotenv 16.0+
- **HTTP客户端**: Axios 1.4+
- **服务器端口**: 5350
#### 2.3.1 Python环境配置与依赖管理
1. `uv` 是一个快速的 Python 包和虚拟环境管理工具
2. MCP Server 依赖 `uv` 工具运行,必须确保系统环境中已安装 uv
3. 安装 `uv` 的推荐方法:
- 使用官方安装脚本:`powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
- 或通过 pip 安装:`pip install uv`
4. 设置国内镜像源加速下载:
```bash
$env:UV_INDEX_URL="https://mirrors.aliyun.com/pypi/simple/"
```
### 2.4 部署架构
- **开发环境**: 本地运行
@@ -111,45 +118,52 @@
| username | VARCHAR(50) | UNIQUE, NOT NULL | 用户名 |
| email | VARCHAR(100) | UNIQUE, NOT NULL | 邮箱地址 |
| password | VARCHAR(255) | NOT NULL | 密码(加密后) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
| phone | VARCHAR(20) | NULL | 手机号 |
| avatar | VARCHAR(255) | NULL | 头像URL |
| status | ENUM('active','inactive','banned') | DEFAULT 'active' | 用户状态 |
| last_login | DATETIME | NULL | 最后登录时间 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.2 角色表 (roles)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 角色ID |
| name | VARCHAR(50) | UNIQUE, NOT NULL | 角色名称 |
| description | TEXT | | 角色描述 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| description | TEXT | NULL | 角色描述 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
#### 3.3.3 用户角色关联表 (user_roles)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| user_id | INT | FOREIGN KEY (users.id), NOT NULL | 用户ID |
| role_id | INT | FOREIGN KEY (roles.id), NOT NULL | 角色ID |
| assigned_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 分配时间 |
| assigned_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 分配时间 |
#### 3.3.4 产品表 (products)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 产品ID |
| name | VARCHAR(100) | NOT NULL | 产品名称 |
| description | TEXT | | 产品描述 |
| price | DECIMAL(10,2) | NOT NULL | 产品价格 |
| description | TEXT | NULL | 产品描述 |
| price | INT | NOT NULL | 产品价格(单位:分) |
| stock | INT | DEFAULT 0 | 库存数量 |
| status | ENUM('active', 'inactive') | DEFAULT 'active' | 产品状态 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
| image_url | VARCHAR(255) | NULL | 产品图片URL |
| is_active | TINYINT(1) | DEFAULT 1 | 是否激活 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.5 订单表 (orders)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 订单ID |
| user_id | INT | FOREIGN KEY (users.id), NOT NULL | 用户ID |
| total_amount | DECIMAL(10,2) | NOT NULL | 订单总金额 |
| status | ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') | DEFAULT 'pending' | 订单状态 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
| total_amount | INT | DEFAULT 0 | 订单总金额(单位:分) |
| status | ENUM('pending','processing','shipped','delivered','cancelled') | DEFAULT 'pending' | 订单状态 |
| payment_status | ENUM('unpaid','paid','refunded') | DEFAULT 'unpaid' | 支付状态 |
| shipping_address | TEXT | NULL | 收货地址 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.6 订单项表 (order_items)
| 字段名 | 类型 | 约束 | 描述 |
@@ -157,8 +171,66 @@
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 订单项ID |
| order_id | INT | FOREIGN KEY (orders.id), NOT NULL | 订单ID |
| product_id | INT | FOREIGN KEY (products.id), NOT NULL | 产品ID |
| quantity | INT | NOT NULL | 数量 |
| price | DECIMAL(10,2) | NOT NULL | 单价 |
| quantity | INT | DEFAULT 1 | 数量 |
| price | INT | NOT NULL | 单价(单位:分) |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
#### 3.3.7 养殖场表 (farms)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 养殖场ID |
| name | VARCHAR(100) | NOT NULL | 养殖场名称 |
| type | VARCHAR(50) | NOT NULL | 养殖场类型 |
| location | JSON | NOT NULL | 地理位置信息 |
| address | VARCHAR(255) | NULL | 详细地址 |
| contact | VARCHAR(50) | NULL | 联系人 |
| phone | VARCHAR(20) | NULL | 联系电话 |
| status | ENUM('active','inactive','maintenance') | DEFAULT 'active' | 养殖场状态 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.8 动物表 (animals)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 动物ID |
| type | VARCHAR(50) | NOT NULL | 动物类型 |
| count | INT | DEFAULT 0 | 数量 |
| farm_id | INT | FOREIGN KEY (farms.id), NOT NULL | 所属养殖场ID |
| health_status | ENUM('healthy','sick','quarantine') | DEFAULT 'healthy' | 健康状态 |
| last_inspection | DATETIME | NULL | 最后检查时间 |
| notes | TEXT | NULL | 备注信息 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.9 设备表 (devices)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 设备ID |
| name | VARCHAR(100) | NOT NULL | 设备名称 |
| type | VARCHAR(50) | NOT NULL | 设备类型 |
| status | ENUM('online','offline','maintenance') | DEFAULT 'offline' | 设备状态 |
| farm_id | INT | FOREIGN KEY (farms.id), NOT NULL | 所属养殖场ID |
| last_maintenance | DATETIME | NULL | 最后维护时间 |
| installation_date | DATETIME | NULL | 安装日期 |
| metrics | JSON | NULL | 设备指标数据 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
#### 3.3.10 预警表 (alerts)
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 预警ID |
| type | VARCHAR(50) | NOT NULL | 预警类型 |
| level | ENUM('low','medium','high','critical') | DEFAULT 'medium' | 预警级别 |
| message | TEXT | NOT NULL | 预警消息 |
| status | ENUM('active','acknowledged','resolved') | DEFAULT 'active' | 预警状态 |
| farm_id | INT | FOREIGN KEY (farms.id), NOT NULL | 所属养殖场ID |
| device_id | INT | FOREIGN KEY (devices.id), NULL | 关联设备ID |
| resolved_at | DATETIME | NULL | 解决时间 |
| resolved_by | INT | FOREIGN KEY (users.id), NULL | 解决人ID |
| resolution_notes | TEXT | NULL | 解决说明 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
### 3.4 初始数据

View File

@@ -1,286 +1,286 @@
'use client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Progress } from "@/components/ui/progress"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { FarmMap } from "@/components/farm-map"
import {
TrendingUp,
Users,
MapPin,
AlertTriangle,
Thermometer,
Droplets,
Sun,
Wifi,
Server
} from "lucide-react"
// Mock data for the dashboard
const mockData = {
farms: {
total: 12,
online: 9,
offline: 3
},
animals: {
total: 2456,
cattle: 890,
pigs: 756,
chickens: 810
},
environment: {
temperature: 25.6,
humidity: 65,
light: 1200
},
alerts: [
{ id: 1, type: 'critical', message: '温度异常: 农场3号温度超过阈值', time: '10:30' },
{ id: 2, type: 'warning', message: '湿度偏低: 农场5号湿度低于正常值', time: '09:45' },
{ id: 3, type: 'info', message: '设备维护: 农场2号传感器需要校准', time: '08:15' }
],
devices: {
total: 156,
online: 142,
offline: 14,
types: {
sensor: 89,
camera: 32,
controller: 35
}
}
}
export default function Dashboard() {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-blue-950 to-slate-900 text-white">
{/* Header */}
<header className="border-b border-slate-700/50 bg-slate-900/50 backdrop-blur-sm">
<div className="container mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="w-3 h-3 bg-green-400 rounded-full animate-pulse"></div>
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
</h1>
</div>
<div className="flex items-center space-x-4">
<div className="text-sm text-slate-400">
{new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}
</div>
<Badge variant="secondary" className="bg-green-500/20 text-green-400">
<Wifi className="w-3 h-3 mr-1" />
线
</Badge>
</div>
</div>
</div>
</header>
{/* Main Content */}
<main className="container mx-auto px-6 py-8">
{/* Stats Overview */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<MapPin className="h-4 w-4 text-blue-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.farms.total}</div>
<div className="flex items-center text-xs text-slate-400 mt-2">
<TrendingUp className="h-3 w-3 mr-1 text-green-400" />
<span>线: {mockData.farms.online}</span>
<span className="mx-2">|</span>
<span className="text-red-400">线: {mockData.farms.offline}</span>
</div>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Users className="h-4 w-4 text-green-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.animals.total.toLocaleString()}</div>
<div className="text-xs text-slate-400 mt-2">
: {mockData.animals.cattle} | : {mockData.animals.pigs} | : {mockData.animals.chickens}
</div>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Server className="h-4 w-4 text-cyan-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.devices.online}/{mockData.devices.total}</div>
<div className="text-xs text-slate-400 mt-2">
线: {((mockData.devices.online / mockData.devices.total) * 100).toFixed(1)}%
</div>
<Progress
value={(mockData.devices.online / mockData.devices.total) * 100}
className="h-2 mt-2 bg-slate-700"
/>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Thermometer className="h-4 w-4 text-orange-400" />
</CardHeader>
<CardContent>
<div className="flex items-center justify-between text-sm">
<div className="flex items-center">
<Thermometer className="h-3 w-3 mr-1 text-orange-400" />
<span>{mockData.environment.temperature}°C</span>
</div>
<div className="flex items-center">
<Droplets className="h-3 w-3 mr-1 text-blue-400" />
<span>{mockData.environment.humidity}%</span>
</div>
<div className="flex items-center">
<Sun className="h-3 w-3 mr-1 text-yellow-400" />
<span>{mockData.environment.light}lux</span>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Main Dashboard Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left Column - Alerts */}
<div className="lg:col-span-1">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm h-full">
<CardHeader>
<CardTitle className="flex items-center text-lg">
<AlertTriangle className="h-5 w-5 mr-2 text-red-400" />
</CardTitle>
<CardDescription className="text-slate-400">
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{mockData.alerts.map((alert) => (
<div
key={alert.id}
className="p-3 rounded-lg border border-slate-700 bg-slate-800/50 backdrop-blur-sm"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center mb-1">
<Badge
variant={
alert.type === 'critical'
? 'destructive'
: alert.type === 'warning'
? 'secondary'
: 'default'
}
className="mr-2"
>
{alert.type === 'critical' ? '严重' : alert.type === 'warning' ? '警告' : '信息'}
</Badge>
<span className="text-xs text-slate-400">{alert.time}</span>
</div>
<p className="text-sm text-slate-200">{alert.message}</p>
</div>
<Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
</Button>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* Middle Column - Charts and Data */}
<div className="lg:col-span-2">
<Tabs defaultValue="overview" className="w-full">
<TabsList className="bg-slate-800/50 border border-slate-700 backdrop-blur-sm">
<TabsTrigger value="overview"></TabsTrigger>
<TabsTrigger value="environment"></TabsTrigger>
<TabsTrigger value="devices"></TabsTrigger>
</TabsList>
<TabsContent value="overview" className="mt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<FarmMap />
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="h-64 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<Server className="h-12 w-12 mx-auto mb-2" />
<p></p>
<p className="text-sm">: {mockData.devices.types.sensor} | : {mockData.devices.types.camera} | : {mockData.devices.types.controller}</p>
</div>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="environment" className="mt-6">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription>湿</CardDescription>
</CardHeader>
<CardContent>
<div className="h-80 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<TrendingUp className="h-12 w-12 mx-auto mb-2" />
<p>线</p>
<p className="text-sm">24湿</p>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="devices" className="mt-6">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="h-80 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<Wifi className="h-12 w-12 mx-auto mb-2" />
<p></p>
<p className="text-sm">线: {mockData.devices.online} | 线: {mockData.devices.offline}</p>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
</div>
</main>
</div>
)
'use client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Progress } from "@/components/ui/progress"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { FarmMap } from "@/components/farm-map"
import {
TrendingUp,
Users,
MapPin,
AlertTriangle,
Thermometer,
Droplets,
Sun,
Wifi,
Server
} from "lucide-react"
// Mock data for the dashboard
const mockData = {
farms: {
total: 12,
online: 9,
offline: 3
},
animals: {
total: 2456,
cattle: 890,
pigs: 756,
chickens: 810
},
environment: {
temperature: 25.6,
humidity: 65,
light: 1200
},
alerts: [
{ id: 1, type: 'critical', message: '温度异常: 农场3号温度超过阈值', time: '10:30' },
{ id: 2, type: 'warning', message: '湿度偏低: 农场5号湿度低于正常值', time: '09:45' },
{ id: 3, type: 'info', message: '设备维护: 农场2号传感器需要校准', time: '08:15' }
],
devices: {
total: 156,
online: 142,
offline: 14,
types: {
sensor: 89,
camera: 32,
controller: 35
}
}
}
export default function Dashboard() {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-blue-950 to-slate-900 text-white">
{/* Header */}
<header className="border-b border-slate-700/50 bg-slate-900/50 backdrop-blur-sm">
<div className="container mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="w-3 h-3 bg-green-400 rounded-full animate-pulse"></div>
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
</h1>
</div>
<div className="flex items-center space-x-4">
<div className="text-sm text-slate-400">
{new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}
</div>
<Badge variant="secondary" className="bg-green-500/20 text-green-400">
<Wifi className="w-3 h-3 mr-1" />
线
</Badge>
</div>
</div>
</div>
</header>
{/* Main Content */}
<main className="container mx-auto px-6 py-8">
{/* Stats Overview */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<MapPin className="h-4 w-4 text-blue-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.farms.total}</div>
<div className="flex items-center text-xs text-slate-400 mt-2">
<TrendingUp className="h-3 w-3 mr-1 text-green-400" />
<span>线: {mockData.farms.online}</span>
<span className="mx-2">|</span>
<span className="text-red-400">线: {mockData.farms.offline}</span>
</div>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Users className="h-4 w-4 text-green-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.animals.total.toLocaleString()}</div>
<div className="text-xs text-slate-400 mt-2">
: {mockData.animals.cattle} | : {mockData.animals.pigs} | : {mockData.animals.chickens}
</div>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Server className="h-4 w-4 text-cyan-400" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{mockData.devices.online}/{mockData.devices.total}</div>
<div className="text-xs text-slate-400 mt-2">
线: {((mockData.devices.online / mockData.devices.total) * 100).toFixed(1)}%
</div>
<Progress
value={(mockData.devices.online / mockData.devices.total) * 100}
className="h-2 mt-2 bg-slate-700"
/>
</CardContent>
</Card>
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300"></CardTitle>
<Thermometer className="h-4 w-4 text-orange-400" />
</CardHeader>
<CardContent>
<div className="flex items-center justify-between text-sm">
<div className="flex items-center">
<Thermometer className="h-3 w-3 mr-1 text-orange-400" />
<span>{mockData.environment.temperature}°C</span>
</div>
<div className="flex items-center">
<Droplets className="h-3 w-3 mr-1 text-blue-400" />
<span>{mockData.environment.humidity}%</span>
</div>
<div className="flex items-center">
<Sun className="h-3 w-3 mr-1 text-yellow-400" />
<span>{mockData.environment.light}lux</span>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Main Dashboard Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left Column - Alerts */}
<div className="lg:col-span-1">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm h-full">
<CardHeader>
<CardTitle className="flex items-center text-lg">
<AlertTriangle className="h-5 w-5 mr-2 text-red-400" />
</CardTitle>
<CardDescription className="text-slate-400">
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{mockData.alerts.map((alert) => (
<div
key={alert.id}
className="p-3 rounded-lg border border-slate-700 bg-slate-800/50 backdrop-blur-sm"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center mb-1">
<Badge
variant={
alert.type === 'critical'
? 'destructive'
: alert.type === 'warning'
? 'secondary'
: 'default'
}
className="mr-2"
>
{alert.type === 'critical' ? '严重' : alert.type === 'warning' ? '警告' : '信息'}
</Badge>
<span className="text-xs text-slate-400">{alert.time}</span>
</div>
<p className="text-sm text-slate-200">{alert.message}</p>
</div>
<Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
</Button>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* Middle Column - Charts and Data */}
<div className="lg:col-span-2">
<Tabs defaultValue="overview" className="w-full">
<TabsList className="bg-slate-800/50 border border-slate-700 backdrop-blur-sm">
<TabsTrigger value="overview"></TabsTrigger>
<TabsTrigger value="environment"></TabsTrigger>
<TabsTrigger value="devices"></TabsTrigger>
</TabsList>
<TabsContent value="overview" className="mt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<FarmMap />
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="h-64 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<Server className="h-12 w-12 mx-auto mb-2" />
<p></p>
<p className="text-sm">: {mockData.devices.types.sensor} | : {mockData.devices.types.camera} | : {mockData.devices.types.controller}</p>
</div>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="environment" className="mt-6">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription>湿</CardDescription>
</CardHeader>
<CardContent>
<div className="h-80 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<TrendingUp className="h-12 w-12 mx-auto mb-2" />
<p>线</p>
<p className="text-sm">24湿</p>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="devices" className="mt-6">
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="h-80 flex items-center justify-center bg-slate-900/50 rounded-lg">
<div className="text-center text-slate-400">
<Wifi className="h-12 w-12 mx-auto mb-2" />
<p></p>
<p className="text-sm">线: {mockData.devices.online} | 线: {mockData.devices.offline}</p>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
</div>
</main>
</div>
)
}

View File

@@ -1,21 +1,21 @@
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export default function Home() {
const router = useRouter()
useEffect(() => {
router.push('/dashboard')
}, [router])
return (
<div className="min-h-screen flex items-center justify-center bg-slate-950">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-400 mx-auto mb-4"></div>
<p className="text-slate-400">...</p>
</div>
</div>
)
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export default function Home() {
const router = useRouter()
useEffect(() => {
router.push('/dashboard')
}, [router])
return (
<div className="min-h-screen flex items-center justify-center bg-slate-950">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-400 mx-auto mb-4"></div>
<p className="text-slate-400">...</p>
</div>
</div>
)
}

View File

@@ -1,190 +1,190 @@
'use client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { MapPin } from "lucide-react"
interface FarmLocation {
id: number
name: string
type: string
address: string
contact: string
phone: string
position: { left: string; top: string }
status: 'online' | 'offline' | 'warning'
}
// const farmLocations: FarmLocation[] = [
// {
// id: 1,
// name: "宁夏农场1",
// type: "综合农场",
// address: "宁夏回族自治区银川市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "45%", top: "25%" },
// status: "online"
// },
// {
// id: 2,
// name: "宁夏农场2",
// type: "综合农场",
// address: "宁夏回族自治区石嘴山市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "40%", top: "15%" },
// status: "online"
// },
// {
// id: 3,
// name: "宁夏农场3",
// type: "综合农场",
// address: "宁夏回族自治区吴忠市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "50%", top: "45%" },
// status: "warning"
// },
// {
// id: 4,
// name: "阳光农场",
// type: "养猪场",
// address: "北京市朝阳区农场路1号",
// contact: "张三",
// phone: "13800138001",
// position: { left: "75%", top: "20%" },
// status: "online"
// },
// {
// id: 5,
// name: "绿野牧场",
// type: "养牛场",
// address: "上海市浦东新区牧场路2号",
// contact: "李四",
// phone: "13800138002",
// position: { left: "30%", top: "40%" },
// status: "online"
// },
// {
// id: 6,
// name: "山谷羊场",
// type: "养羊场",
// address: "广州市天河区山谷路3号",
// contact: "王五",
// phone: "13800138003",
// position: { left: "45%", top: "60%" },
// status: "offline"
// }
// ]
export function FarmMap() {
const getStatusColor = (status: FarmLocation['status']) => {
switch (status) {
case 'online': return 'bg-green-400'
case 'offline': return 'bg-red-400'
case 'warning': return 'bg-yellow-400'
default: return 'bg-gray-400'
}
}
const getStatusText = (status: FarmLocation['status']) => {
switch (status) {
case 'online': return '在线'
case 'offline': return '离线'
case 'warning': return '警告'
default: return '未知'
}
}
return (
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle className="flex items-center text-lg">
<MapPin className="h-5 w-5 mr-2 text-blue-400" />
</CardTitle>
<CardDescription className="text-slate-400">
</CardDescription>
</CardHeader>
<CardContent>
<div className="relative h-96 bg-gradient-to-br from-blue-900/50 to-green-900/50 rounded-lg border border-slate-600/50 overflow-hidden">
{/* Map Background */}
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMEE0QjZDIi8+PHBhdGggZD0iTTAgMCBMNjAwIDAgTDYwMCA0MDAgTDAgNDAwIFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFFNUI4QyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48L3N2Zz4=')] bg-cover bg-center opacity-30"></div>
{/* Grid Lines */}
<div className="absolute inset-0 bg-grid-pattern opacity-20"></div>
{/* Farm Markers */}
{/* 需要从props或状态管理中获取farmLocations数据 */}
{([] as FarmLocation[]).map((farm) => (
<div
key={farm.id}
className="absolute transform -translate-x-1/2 -translate-y-1/2 group cursor-pointer"
style={{ left: farm.position.left, top: farm.position.top }}
>
<div className="relative">
<div className={`w-4 h-4 rounded-full border-2 border-white ${getStatusColor(farm.status)} animate-pulse`}></div>
<div className="absolute -top-6 left-1/2 transform -translate-x-1/2 bg-slate-800/90 backdrop-blur-sm text-white text-xs px-2 py-1 rounded border border-slate-600 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
{farm.name}
</div>
{/* Tooltip on hover */}
<div className="absolute -top-32 left-1/2 transform -translate-x-1/2 bg-slate-800/95 backdrop-blur-sm text-white p-3 rounded-lg border border-slate-600 shadow-lg opacity-0 group-hover:opacity-100 transition-opacity w-48 z-50">
<div className="text-sm font-semibold mb-2">{farm.name}</div>
<div className="text-xs space-y-1 text-slate-300">
<div>: {farm.type}</div>
<div>: {farm.address}</div>
<div>: {farm.contact}</div>
<div>: {farm.phone}</div>
<div className={`inline-flex items-center px-2 py-1 rounded text-xs ${
farm.status === 'online' ? 'bg-green-500/20 text-green-400' :
farm.status === 'offline' ? 'bg-red-500/20 text-red-400' :
'bg-yellow-500/20 text-yellow-400'
}`}>
<div className={`w-2 h-2 rounded-full mr-1 ${
farm.status === 'online' ? 'bg-green-400' :
farm.status === 'offline' ? 'bg-red-400' :
'bg-yellow-400'
}`}></div>
{getStatusText(farm.status)}
</div>
</div>
</div>
</div>
</div>
))}
{/* Map Controls */}
<div className="absolute top-4 right-4 flex flex-col space-y-2">
<button className="w-8 h-8 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded text-white font-bold hover:bg-slate-700 transition-colors">
+
</button>
<button className="w-8 h-8 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded text-white font-bold hover:bg-slate-700 transition-colors">
-
</button>
</div>
{/* Legend */}
<div className="absolute bottom-4 left-4 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded-lg p-3 text-xs text-white">
<div className="font-semibold mb-2"></div>
<div className="space-y-1">
<div className="flex items-center">
<div className="w-3 h-3 bg-green-400 rounded-full mr-2"></div>
<span>线</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-red-400 rounded-full mr-2"></div>
<span>线</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-yellow-400 rounded-full mr-2"></div>
<span></span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)
'use client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { MapPin } from "lucide-react"
interface FarmLocation {
id: number
name: string
type: string
address: string
contact: string
phone: string
position: { left: string; top: string }
status: 'online' | 'offline' | 'warning'
}
// const farmLocations: FarmLocation[] = [
// {
// id: 1,
// name: "宁夏农场1",
// type: "综合农场",
// address: "宁夏回族自治区银川市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "45%", top: "25%" },
// status: "online"
// },
// {
// id: 2,
// name: "宁夏农场2",
// type: "综合农场",
// address: "宁夏回族自治区石嘴山市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "40%", top: "15%" },
// status: "online"
// },
// {
// id: 3,
// name: "宁夏农场3",
// type: "综合农场",
// address: "宁夏回族自治区吴忠市",
// contact: "管理员",
// phone: "400-000-0000",
// position: { left: "50%", top: "45%" },
// status: "warning"
// },
// {
// id: 4,
// name: "阳光农场",
// type: "养猪场",
// address: "北京市朝阳区农场路1号",
// contact: "张三",
// phone: "13800138001",
// position: { left: "75%", top: "20%" },
// status: "online"
// },
// {
// id: 5,
// name: "绿野牧场",
// type: "养牛场",
// address: "上海市浦东新区牧场路2号",
// contact: "李四",
// phone: "13800138002",
// position: { left: "30%", top: "40%" },
// status: "online"
// },
// {
// id: 6,
// name: "山谷羊场",
// type: "养羊场",
// address: "广州市天河区山谷路3号",
// contact: "王五",
// phone: "13800138003",
// position: { left: "45%", top: "60%" },
// status: "offline"
// }
// ]
export function FarmMap() {
const getStatusColor = (status: FarmLocation['status']) => {
switch (status) {
case 'online': return 'bg-green-400'
case 'offline': return 'bg-red-400'
case 'warning': return 'bg-yellow-400'
default: return 'bg-gray-400'
}
}
const getStatusText = (status: FarmLocation['status']) => {
switch (status) {
case 'online': return '在线'
case 'offline': return '离线'
case 'warning': return '警告'
default: return '未知'
}
}
return (
<Card className="bg-slate-800/50 border-slate-700 backdrop-blur-sm">
<CardHeader>
<CardTitle className="flex items-center text-lg">
<MapPin className="h-5 w-5 mr-2 text-blue-400" />
</CardTitle>
<CardDescription className="text-slate-400">
</CardDescription>
</CardHeader>
<CardContent>
<div className="relative h-96 bg-gradient-to-br from-blue-900/50 to-green-900/50 rounded-lg border border-slate-600/50 overflow-hidden">
{/* Map Background */}
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMEE0QjZDIi8+PHBhdGggZD0iTTAgMCBMNjAwIDAgTDYwMCA0MDAgTDAgNDAwIFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFFNUI4QyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48L3N2Zz4=')] bg-cover bg-center opacity-30"></div>
{/* Grid Lines */}
<div className="absolute inset-0 bg-grid-pattern opacity-20"></div>
{/* Farm Markers */}
{/* 需要从props或状态管理中获取farmLocations数据 */}
{([] as FarmLocation[]).map((farm) => (
<div
key={farm.id}
className="absolute transform -translate-x-1/2 -translate-y-1/2 group cursor-pointer"
style={{ left: farm.position.left, top: farm.position.top }}
>
<div className="relative">
<div className={`w-4 h-4 rounded-full border-2 border-white ${getStatusColor(farm.status)} animate-pulse`}></div>
<div className="absolute -top-6 left-1/2 transform -translate-x-1/2 bg-slate-800/90 backdrop-blur-sm text-white text-xs px-2 py-1 rounded border border-slate-600 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
{farm.name}
</div>
{/* Tooltip on hover */}
<div className="absolute -top-32 left-1/2 transform -translate-x-1/2 bg-slate-800/95 backdrop-blur-sm text-white p-3 rounded-lg border border-slate-600 shadow-lg opacity-0 group-hover:opacity-100 transition-opacity w-48 z-50">
<div className="text-sm font-semibold mb-2">{farm.name}</div>
<div className="text-xs space-y-1 text-slate-300">
<div>: {farm.type}</div>
<div>: {farm.address}</div>
<div>: {farm.contact}</div>
<div>: {farm.phone}</div>
<div className={`inline-flex items-center px-2 py-1 rounded text-xs ${
farm.status === 'online' ? 'bg-green-500/20 text-green-400' :
farm.status === 'offline' ? 'bg-red-500/20 text-red-400' :
'bg-yellow-500/20 text-yellow-400'
}`}>
<div className={`w-2 h-2 rounded-full mr-1 ${
farm.status === 'online' ? 'bg-green-400' :
farm.status === 'offline' ? 'bg-red-400' :
'bg-yellow-400'
}`}></div>
{getStatusText(farm.status)}
</div>
</div>
</div>
</div>
</div>
))}
{/* Map Controls */}
<div className="absolute top-4 right-4 flex flex-col space-y-2">
<button className="w-8 h-8 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded text-white font-bold hover:bg-slate-700 transition-colors">
+
</button>
<button className="w-8 h-8 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded text-white font-bold hover:bg-slate-700 transition-colors">
-
</button>
</div>
{/* Legend */}
<div className="absolute bottom-4 left-4 bg-slate-800/80 backdrop-blur-sm border border-slate-600 rounded-lg p-3 text-xs text-white">
<div className="font-semibold mb-2"></div>
<div className="space-y-1">
<div className="flex items-center">
<div className="w-3 h-3 bg-green-400 rounded-full mr-2"></div>
<span>线</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-red-400 rounded-full mr-2"></div>
<span>线</span>
</div>
<div className="flex items-center">
<div className="w-3 h-3 bg-yellow-400 rounded-full mr-2"></div>
<span></span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)
}

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