Files
xiaomubiao-master/前端集成文档.md
2026-05-22 16:13:20 +08:00

649 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 区域侦查任务前端集成文档
## 📋 概述
本文档详细说明前端如何集成区域侦查任务API包括接口调用、数据格式、错误处理等完整的前端集成方案。
## 🚀 快速开始
### 基础配置
```javascript
// API基础配置
const API_BASE_URL = 'http://localhost:8080/api/v1/region-reconnaissance';
// 动态获取鉴权头(如有需要)
function getAuthHeaders() {
const token = window.localStorage.getItem('token');
return token ? { Authorization: `Bearer ${token}` } : {};
}
// 统一请求方法(基于 fetch
async function request(path, options = {}) {
const url = path.startsWith('http') ? path : `${API_BASE_URL}${path}`;
const headers = {
'Content-Type': 'application/json',
...getAuthHeaders(),
...(options.headers || {})
};
const response = await fetch(url, { ...options, headers });
// 尝试解析响应体(可能为 204 无内容)
let payload = null;
if (response.status !== 204) {
try {
payload = await response.json();
} catch {
// 非 JSON 响应体容忍
}
}
if (!response.ok) {
const message = payload?.message || `HTTP ${response.status}`;
throw new Error(message);
}
// 兼容 204 的删除等请求
return payload ?? { code: 200, message: 'OK', data: null };
}
```
## 📡 API接口集成
### 1. 创建区域侦查任务
#### 接口信息
- **URL**: `POST /api/v1/region-reconnaissance/tasks`
- **功能**: 创建新的区域侦查任务
- **返回**: 任务ID和创建结果
#### 前端实现
```javascript
/**
* 创建区域侦查任务
* @param {Object} taskData - 任务数据
* @returns {Promise<Object>} 创建结果
*/
async function createRegionReconnaissanceTask(taskData) {
try {
const result = await request('/tasks', {
method: 'POST',
body: JSON.stringify(taskData)
});
console.log('任务创建成功:', result);
return result;
} catch (error) {
console.error('创建任务失败:', error);
throw error;
}
}
```
#### 请求数据格式
```javascript
// 完整的任务创建数据示例(区域闭合由后端自动处理,前端无需首尾重复)
const taskData = {
taskName: "测试区域侦查任务",
taskType: 1, // 1-区域侦查, 2-航线侦查
regionZone: {
pointList: [
{ latitude: 39.9042, longitude: 116.4074, altitude: 100.0 },
{ latitude: 39.9142, longitude: 116.4174, altitude: 100.0 },
{ latitude: 39.9242, longitude: 116.4274, altitude: 100.0 }
]
},
description: "这是一个测试任务",
executeTime: "2025-01-27T10:00:00Z" // 使用 ISO 8601UTC
};
```
> 说明
> - 区域闭合规则:后端会基于点列表自动闭合多边形,前端不需要重复首尾点。
> - 时间与时区:所有时间字段使用 ISO 8601 UTC例如 `2025-01-27T10:00:00Z`)。服务端按 UTC 存储与计算,前端展示时可按本地时区格式化。
### 2. 获取任务列表
#### 接口信息
- **URL**: `GET /api/v1/region-reconnaissance/tasks`
- **功能**: 获取区域侦查任务列表
- **支持分页**: 是
#### 前端实现
```javascript
/**
* 获取任务列表
* @param {Object} params - 查询参数
* @returns {Promise<Object>} 任务列表
*/
async function getRegionReconnaissanceTasks(params = {}) {
try {
const qp = Object.fromEntries(
Object.entries({ page: params.page ?? 1, size: params.size ?? 10, ...params })
.filter(([, v]) => v !== undefined && v !== null && v !== '')
);
const queryParams = new URLSearchParams(qp).toString();
const result = await request(`/tasks?${queryParams}`, { method: 'GET' });
return result;
} catch (error) {
console.error('获取任务列表失败:', error);
throw error;
}
}
```
### 3. 获取任务详情
#### 接口信息
- **URL**: `GET /api/v1/region-reconnaissance/tasks/{taskId}`
- **功能**: 获取指定任务的详细信息
#### 前端实现
```javascript
/**
* 获取任务详情
* @param {string} taskId - 任务ID
* @returns {Promise<Object>} 任务详情
*/
async function getRegionReconnaissanceTaskDetail(taskId) {
try {
const result = await request(`/tasks/${taskId}`, { method: 'GET' });
return result;
} catch (error) {
console.error('获取任务详情失败:', error);
throw error;
}
}
```
### 4. 更新任务状态
#### 接口信息
- **URL**: `PUT /api/v1/region-reconnaissance/tasks/{taskId}/status`
- **功能**: 更新任务状态
#### 前端实现
```javascript
/**
* 更新任务状态
* @param {string} taskId - 任务ID
* @param {number} status - 新状态
* @returns {Promise<Object>} 更新结果
*/
async function updateTaskStatus(taskId, status) {
try {
const result = await request(`/tasks/${taskId}/status`, {
method: 'PUT',
body: JSON.stringify({ status })
});
return result;
} catch (error) {
console.error('更新任务状态失败:', error);
throw error;
}
}
```
### 5. 删除任务
#### 接口信息
- **URL**: `DELETE /api/v1/region-reconnaissance/tasks/{taskId}`
- **功能**: 删除指定任务
#### 前端实现
```javascript
/**
* 删除任务
* @param {string} taskId - 任务ID
* @returns {Promise<Object>} 删除结果
*/
async function deleteRegionReconnaissanceTask(taskId) {
try {
const result = await request(`/tasks/${taskId}`, { method: 'DELETE' });
return result; // 兼容 204无内容时返回 { code: 200, message: 'OK', data: null }
} catch (error) {
console.error('删除任务失败:', error);
throw error;
}
}
```
## 🎨 前端组件示例
### React组件示例
```jsx
import React, { useState, useEffect } from 'react';
const RegionReconnaissanceTaskManager = () => {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// 获取任务列表
const fetchTasks = async () => {
setLoading(true);
try {
const result = await getRegionReconnaissanceTasks();
setTasks(result.data?.records ?? []);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
// 创建任务
const handleCreateTask = async (taskData) => {
try {
await createRegionReconnaissanceTask(taskData);
await fetchTasks(); // 刷新列表
alert('任务创建成功!');
} catch (err) {
alert(`创建失败: ${err.message}`);
}
};
// 删除任务
const handleDeleteTask = async (taskId) => {
if (window.confirm('确定要删除这个任务吗?')) {
try {
await deleteRegionReconnaissanceTask(taskId);
await fetchTasks(); // 刷新列表
alert('任务删除成功!');
} catch (err) {
alert(`删除失败: ${err.message}`);
}
}
};
useEffect(() => {
fetchTasks();
}, []);
return (
<div className="task-manager">
<h2>区域侦查任务管理</h2>
{loading && <div>加载中...</div>}
{error && <div className="error">错误: {error}</div>}
<div className="task-list">
{tasks.map(task => (
<div key={task.taskId} className="task-item">
<h3>{task.taskName}</h3>
<p>类型: {task.taskType === 1 ? '区域侦查' : '航线侦查'}</p>
<p>状态: {getStatusText(task.status)}</p>
<p>创建时间: {new Date(task.createTime).toLocaleString()}</p>
<button onClick={() => handleDeleteTask(task.taskId)}>
删除
</button>
</div>
))}
</div>
</div>
);
};
// 状态文本转换
const getStatusText = (status) => {
const statusMap = {
0: '待执行',
1: '执行中',
2: '已完成',
3: '已取消',
4: '执行失败'
};
return statusMap[status] || '未知状态';
};
export default RegionReconnaissanceTaskManager;
```
### Vue组件示例
```vue
<template>
<div class="task-manager">
<h2>区域侦查任务管理</h2>
<div v-if="loading" class="loading">加载中...</div>
<div v-if="error" class="error">错误: {{ error }}</div>
<div class="task-list">
<div
v-for="task in tasks"
:key="task.taskId"
class="task-item"
>
<h3>{{ task.taskName }}</h3>
<p>类型: {{ task.taskType === 1 ? '区域侦查' : '航线侦查' }}</p>
<p>状态: {{ getStatusText(task.status) }}</p>
<p>创建时间: {{ formatDate(task.createTime) }}</p>
<button @click="handleDeleteTask(task.taskId)">
删除
</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'RegionReconnaissanceTaskManager',
data() {
return {
tasks: [],
loading: false,
error: null
};
},
async mounted() {
await this.fetchTasks();
},
methods: {
async fetchTasks() {
this.loading = true;
try {
const result = await getRegionReconnaissanceTasks();
this.tasks = result.data?.records ?? [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async handleCreateTask(taskData) {
try {
await createRegionReconnaissanceTask(taskData);
await this.fetchTasks();
alert('任务创建成功!');
} catch (err) {
alert(`创建失败: ${err.message}`);
}
},
async handleDeleteTask(taskId) {
if (confirm('确定要删除这个任务吗?')) {
try {
await deleteRegionReconnaissanceTask(taskId);
await this.fetchTasks();
alert('任务删除成功!');
} catch (err) {
alert(`删除失败: ${err.message}`);
}
}
},
getStatusText(status) {
const statusMap = {
0: '待执行',
1: '执行中',
2: '已完成',
3: '已取消',
4: '执行失败'
};
return statusMap[status] || '未知状态';
},
formatDate(dateString) {
return new Date(dateString).toLocaleString();
}
}
};
</script>
```
## 📊 数据格式说明
### 任务对象结构
```typescript
interface RegionReconnaissanceTask {
taskId: string; // 任务ID
taskName: string; // 任务名称
taskType: number; // 任务类型 (1-区域侦查, 2-航线侦查)
status: number; // 任务状态 (0-待执行, 1-执行中, 2-已完成, 3-已取消, 4-执行失败)
regionZone: RegionZone; // 区域信息
description?: string; // 任务描述
executeTime?: string; // 执行时间
createTime: string; // 创建时间
updateTime: string; // 更新时间
createUserId: string; // 创建用户ID
mqttSendStatus: number; // MQTT发送状态
}
interface RegionZone {
pointList: Point[]; // 区域点列表
}
interface Point {
latitude: number; // 纬度
longitude: number; // 经度
altitude: number; // 高度
}
```
### 响应数据格式
```typescript
// 成功响应
interface ApiResponse<T> {
code: number; // 响应码 (200-成功)
message: string; // 响应消息
data: T; // 响应数据
timestamp: string; // 时间戳
}
// 分页响应
interface PageResponse<T> {
code: number;
message: string;
data: {
records: T[]; // 数据列表
total: number; // 总记录数
size: number; // 每页大小
current: number; // 当前页码
pages: number; // 总页数
};
timestamp: string;
}
```
## ⚠️ 错误处理
### 常见错误码
```javascript
const ERROR_CODES = {
400: '请求参数错误',
401: '未授权访问',
403: '禁止访问',
404: '资源不存在',
500: '服务器内部错误',
1001: '任务名称不能为空',
1002: '区域点列表不能为空',
1003: '任务不存在',
1004: '任务状态不允许此操作'
};
```
### 错误处理示例
```javascript
/**
* 统一错误处理(配合 fetch 请求助手)
* @param {Error} error - 错误对象
* @returns {string} 用户友好的错误信息
*/
function handleApiError(error) {
// request() 已将后端 message 或 HTTP 状态转为 Error(message)
return error?.message || '请求失败';
}
```
## 🔧 工具函数
### 数据验证
```javascript
/**
* 验证任务数据
* @param {Object} taskData - 任务数据
* @returns {Object} 验证结果
*/
function validateTaskData(taskData) {
const errors = [];
if (!taskData.taskName || taskData.taskName.trim() === '') {
errors.push('任务名称不能为空');
}
if (![1, 2].includes(taskData.taskType)) {
errors.push('任务类型必须是1(区域侦查)或2(航线侦查)');
}
if (!taskData.regionZone || !Array.isArray(taskData.regionZone.pointList)) {
errors.push('区域信息不能为空');
} else if (taskData.regionZone.pointList.length < 3) {
errors.push('区域至少需要3个点');
}
return {
isValid: errors.length === 0,
errors
};
}
```
### 数据转换
```javascript
/**
* 转换区域数据为API格式
* @param {Array} points - 前端点数据
* @returns {Object} API格式的区域数据
*/
function convertRegionData(points) {
return {
pointList: points.map(point => ({
latitude: parseFloat(point.latitude),
longitude: parseFloat(point.longitude),
altitude: parseFloat(point.altitude) || 0
}))
};
}
```
## 📱 移动端适配
### 响应式设计
```css
/* 移动端适配 */
@media (max-width: 768px) {
.task-manager {
padding: 10px;
}
.task-item {
margin-bottom: 15px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
.task-item h3 {
font-size: 16px;
margin-bottom: 8px;
}
.task-item p {
font-size: 14px;
margin: 4px 0;
}
.task-item button {
width: 100%;
padding: 8px;
margin-top: 10px;
}
}
```
## 🧪 测试示例
### 单元测试
```javascript
// Jest测试示例
describe('RegionReconnaissanceTask API', () => {
test('创建任务', async () => {
const taskData = {
taskName: '测试任务',
taskType: 1,
regionZone: {
pointList: [
{ latitude: 39.9042, longitude: 116.4074, altitude: 100 },
{ latitude: 39.9142, longitude: 116.4174, altitude: 100 },
{ latitude: 39.9242, longitude: 116.4274, altitude: 100 }
]
}
};
const result = await createRegionReconnaissanceTask(taskData);
expect(result.code).toBe(200);
expect(result.data.taskId).toBeDefined();
});
test('获取任务列表', async () => {
const result = await getRegionReconnaissanceTasks();
expect(result.code).toBe(200);
expect(Array.isArray(result.data.records)).toBe(true);
});
});
```
## 📚 最佳实践
### 1. 错误处理
- 始终使用try-catch包装API调用
- 提供用户友好的错误信息
- 记录详细的错误日志用于调试
### 2. 数据验证
- 在发送请求前验证数据格式
- 使用TypeScript或PropTypes进行类型检查
- 提供实时验证反馈
### 3. 用户体验
- 显示加载状态
- 提供操作确认
- 使用乐观更新提升响应速度
### 4. 性能优化
- 实现数据缓存
- 使用分页加载大量数据
- 防抖处理用户输入
## 🔗 相关资源
- [后端API接口说明](./后端API接口说明.md)
- [前端对接指南](./前端对接指南.md)
- [前端集成说明](./前端集成说明.md)
## 📞 技术支持
如有问题,请联系开发团队或查看项目文档。
---
*最后更新: 2025-01-27*