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

15 KiB
Raw Permalink Blame History

区域侦查任务前端集成文档

📋 概述

本文档详细说明前端如何集成区域侦查任务API包括接口调用、数据格式、错误处理等完整的前端集成方案。

🚀 快速开始

基础配置

// 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和创建结果

前端实现

/**
 * 创建区域侦查任务
 * @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;
  }
}

请求数据格式

// 完整的任务创建数据示例(区域闭合由后端自动处理,前端无需首尾重复)
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
  • 功能: 获取区域侦查任务列表
  • 支持分页: 是

前端实现

/**
 * 获取任务列表
 * @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}
  • 功能: 获取指定任务的详细信息

前端实现

/**
 * 获取任务详情
 * @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
  • 功能: 更新任务状态

前端实现

/**
 * 更新任务状态
 * @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}
  • 功能: 删除指定任务

前端实现

/**
 * 删除任务
 * @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组件示例

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组件示例

<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>

📊 数据格式说明

任务对象结构

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;         // 高度
}

响应数据格式

// 成功响应
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;
}

⚠️ 错误处理

常见错误码

const ERROR_CODES = {
  400: '请求参数错误',
  401: '未授权访问',
  403: '禁止访问',
  404: '资源不存在',
  500: '服务器内部错误',
  1001: '任务名称不能为空',
  1002: '区域点列表不能为空',
  1003: '任务不存在',
  1004: '任务状态不允许此操作'
};

错误处理示例

/**
 * 统一错误处理(配合 fetch 请求助手)
 * @param {Error} error - 错误对象
 * @returns {string} 用户友好的错误信息
 */
function handleApiError(error) {
  // request() 已将后端 message 或 HTTP 状态转为 Error(message)
  return error?.message || '请求失败';
}

🔧 工具函数

数据验证

/**
 * 验证任务数据
 * @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
  };
}

数据转换

/**
 * 转换区域数据为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
    }))
  };
}

📱 移动端适配

响应式设计

/* 移动端适配 */
@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;
  }
}

🧪 测试示例

单元测试

// 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. 性能优化

  • 实现数据缓存
  • 使用分页加载大量数据
  • 防抖处理用户输入

🔗 相关资源

📞 技术支持

如有问题,请联系开发团队或查看项目文档。


最后更新: 2025-01-27