博主介绍:专注于Java(springboot ssm 等开发框架) vue  .net  php phython node.js    uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作
☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟
我的博客空间发布了2000+毕设题目 方便大家学习使用
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人
更多项目地址 介绍 
文末下方有源码获取地址

下载地址:

springboot的海洋船只出海作业智能管理系统boat.rar资源-CSDN下载

1. 需求分析

1.1 项目背景

随着海洋经济的快速发展,海洋船只出海作业的规模和频率不断增加,传统的船只管理方式已经无法满足现代海洋作业的需求。为了提高船只管理的效率和安全性,需要开发一套智能管理系统,实现船只定位、任务管理、船员管理、设备监控和应急处置等功能。

1.2 核心功能需求

1.2.1 船只定位与航行监控功能
  • 实时定位跟踪:基于GPS/北斗卫星采集船只经纬度、航向、航速、海拔等数据

  • 多终端同步展示:支持多船只同时在线区分显示

  • 航行轨迹追溯:自动记录轨迹并保存不少于1年,可按时间段查询、导出及回放

  • 电子围栏管理:支持自定义绘制海域电子围栏,设置围栏类型,船只进出围栏时触发预警

  • 航行状态监测:设置航速、航线偏差等阈值,异常状态自动提醒并记录相关数据

1.2.2 出海作业任务管理功能
  • 任务创建与分配:支持管理员创建并分配出海作业任务,可填写任务相关信息并分配至指定船只

  • 作业过程数据采集:船员可手动上报作业数据,系统可自动采集相关数据,支持离线上报及联网同步

  • 任务进度监控:统计任务完成率、逾期数量,支持多维度筛选统计,生成可导出、打印的自定义报表

  • 合规校验:对接海事部门合规要求,校验作业相关信息,对违规作业触发预警并记录

1.2.3 船员管理功能
  • 船员信息管理:实现船员信息全生命周期管理,包括基础信息录入、编辑、查询、删除

  • 船员排班管理:根据作业任务需求,合理分配船员至对应船只及岗位

  • 出海考勤记录:自动关联船只出海、靠岸时间,统计船员出海时长、值班次数

  • 培训与资质管理:提醒资质到期,记录培训内容、考核结果,确保船员具备合法从业资质

1.2.4 船只设备监控功能
  • 设备运行状态监测:实时监测船只核心设备运行状态,采集设备运行参数、故障信息

  • 设备维护管理:制定维护计划,记录维护时间、内容、人员及结果,提醒设备定期维护

  • 设备运行统计:统计设备运行时长、故障频次、维护次数等数据,生成设备运行报表

  • 设备参数配置:支持设备参数自定义配置,适配不同类型船只的设备需求

1.2.5 应急处置与系统管理功能
  • 应急事件上报:船员可快速上报海上突发情况,上传现场信息,系统自动推送预警至相关负责人

  • 应急处置预案:制定应急处置预案,明确处置流程、责任分工,可快速调取预案并指导现场处置

  • 系统权限管控:实现多角色权限管控,分配不同操作权限,保障数据安全

  • 多终端适配:确保船载终端、web端数据同步,支持数据加密传输,保障数据隐私与安全

2. 技术架构分析

2.1 技术栈选择

分类 技术 版本 选型理由
后端 Spring Boot 3.0.0 轻量级框架,快速开发,内置Tomcat服务器,适合构建RESTful API
后端 MyBatis 3.0.0 灵活的SQL映射框架,支持自定义SQL,适合复杂的数据库操作
后端 Lombok 1.18.24 减少 boilerplate 代码,提高开发效率
前端 Vue 3.3.4 响应式前端框架,组件化开发,适合构建单页应用
前端 Vue Router 4.2.4 官方路由管理器,支持嵌套路由和路由守卫
前端 ECharts 5.4.3 强大的数据可视化库,支持地图、图表等多种可视化方式
前端 Axios 1.5.0 基于Promise的HTTP客户端,支持拦截器和请求/响应转换
数据库 MySQL 8.0 关系型数据库,稳定可靠,适合存储结构化数据

2.2 系统架构

系统采用前后端分离的架构设计,具体如下:

  1. 前端层:使用Vue 3构建单页应用,通过Vue Router实现路由管理,通过Axios与后端API进行通信,使用ECharts实现数据可视化。

  2. 后端层:使用Spring Boot构建RESTful API,通过MyBatis实现数据访问,使用Lombok减少冗余代码。

  3. 数据层:使用MySQL数据库存储数据,通过MyBatis进行数据操作。

  4. 交互流程

    • 前端通过Axios发送HTTP请求到后端API

    • 后端处理请求,操作数据库,返回响应

    • 前端接收响应,更新界面显示

2.3 目录结构

2.3.1 后端目录结构
backend/
├── src/
│   ├── main/
│   │   ├── java/com/boat/
│   │   │   ├── controller/      # 控制器
│   │   │   ├── entity/          # 实体类
│   │   │   ├── mapper/          # 数据访问层
│   │   │   ├── service/         # 业务逻辑层
│   │   │   └── BoatManagementApplication.java  # 应用启动类
│   │   └── resources/
│   │       ├── mapper/          # MyBatis映射文件
│   │       ├── application.properties  # 配置文件
│   │       └── init.sql         # 数据库初始化脚本
│   └── test/                    # 测试代码
└── pom.xml                      # Maven配置文件
2.3.2 前端目录结构
frontend/
├── src/
│   ├── components/              # 组件
│   ├── views/                   # 页面
│   ├── router/                  # 路由配置
│   ├── api/                     # API请求
│   ├── utils/                   # 工具函数
│   ├── assets/                  # 静态资源
│   ├── App.vue                  # 根组件
│   └── main.js                  # 入口文件
├── index.html                   # HTML模板
├── package.json                 # NPM配置文件
└── vite.config.js               # Vite配置文件

3. 数据库设计

3.1 数据库表结构

3.1.1 用户表(user)
字段名 数据类型 约束 描述
id INT PRIMARY KEY AUTO_INCREMENT 用户ID
username VARCHAR(50) NOT NULL UNIQUE 用户名
password VARCHAR(50) NOT NULL 密码
role VARCHAR(20) NOT NULL 角色
name VARCHAR(50) NOT NULL 姓名
phone VARCHAR(20) NOT NULL 联系方式
3.1.2 船只表(boat)
字段名 数据类型 约束 描述
id INT PRIMARY KEY AUTO_INCREMENT 船只ID
boat_id VARCHAR(50) NOT NULL UNIQUE 船只标识
boat_name VARCHAR(100) NOT NULL 船只名称
boat_type VARCHAR(50) NOT NULL 船只类型
status VARCHAR(20) NOT NULL 船只状态
latitude DOUBLE 纬度
longitude DOUBLE 经度
heading DOUBLE 航向
speed DOUBLE 航速
altitude DOUBLE 海拔
voyage_distance DOUBLE 航行里程
voyage_duration BIGINT 航行时长
3.1.3 任务表(task)
字段名 数据类型 约束 描述
id INT PRIMARY KEY AUTO_INCREMENT 任务ID
task_id VARCHAR(50) NOT NULL UNIQUE 任务编号
task_name VARCHAR(100) NOT NULL 任务名称
task_type VARCHAR(50) NOT NULL 作业类型
operation_area VARCHAR(255) NOT NULL 作业区域
start_time DATETIME NOT NULL 开始时间
end_time DATETIME NOT NULL 结束时间
status VARCHAR(20) NOT NULL 任务状态
boat_id INT FOREIGN KEY 所属船只ID
operation_data TEXT 作业数据
compliance_check TEXT 合规校验
3.1.4 船员表(crew)
字段名 数据类型 约束 描述
id INT PRIMARY KEY AUTO_INCREMENT 船员ID
crew_id VARCHAR(50) NOT NULL UNIQUE 船员编号
name VARCHAR(50) NOT NULL 姓名
gender VARCHAR(10) NOT NULL 性别
age INT NOT NULL 年龄
phone VARCHAR(20) NOT NULL 联系方式
id_card VARCHAR(20) NOT NULL UNIQUE 身份证号
qualification VARCHAR(255) 资质
experience TEXT 从业经历
training_record TEXT 培训记录
sea_record TEXT 出海记录
schedule_info TEXT 排班信息
attendance_status VARCHAR(20) 考勤状态
3.1.5 设备表(device)
字段名 数据类型 约束 描述
id INT PRIMARY KEY AUTO_INCREMENT 设备ID
device_id VARCHAR(50) NOT NULL UNIQUE 设备编号
device_name VARCHAR(100) NOT NULL 设备名称
device_type VARCHAR(50) NOT NULL 设备类型
parameters TEXT 设备参数
status VARCHAR(20) NOT NULL 设备状态
maintenance_record TEXT 维护记录
fault_info TEXT 故障信息
boat_id INT FOREIGN KEY 所属船只ID

3.2 数据库初始化脚本

-- 创建数据库
CREATE DATABASE IF NOT EXISTS boat_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
​
-- 使用数据库
USE boat_management;
​
-- 创建用户表
CREATE TABLE IF NOT EXISTS user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(50) NOT NULL,
    role VARCHAR(20) NOT NULL,
    name VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL
);
​
-- 创建船只表
CREATE TABLE IF NOT EXISTS boat (
    id INT PRIMARY KEY AUTO_INCREMENT,
    boat_id VARCHAR(50) NOT NULL UNIQUE,
    boat_name VARCHAR(100) NOT NULL,
    boat_type VARCHAR(50) NOT NULL,
    status VARCHAR(20) NOT NULL,
    latitude DOUBLE,
    longitude DOUBLE,
    heading DOUBLE,
    speed DOUBLE,
    altitude DOUBLE,
    voyage_distance DOUBLE,
    voyage_duration BIGINT
);
​
-- 创建任务表
CREATE TABLE IF NOT EXISTS task (
    id INT PRIMARY KEY AUTO_INCREMENT,
    task_id VARCHAR(50) NOT NULL UNIQUE,
    task_name VARCHAR(100) NOT NULL,
    task_type VARCHAR(50) NOT NULL,
    operation_area VARCHAR(255) NOT NULL,
    start_time DATETIME NOT NULL,
    end_time DATETIME NOT NULL,
    status VARCHAR(20) NOT NULL,
    boat_id INT,
    operation_data TEXT,
    compliance_check TEXT,
    FOREIGN KEY (boat_id) REFERENCES boat(id)
);
​
-- 创建船员表
CREATE TABLE IF NOT EXISTS crew (
    id INT PRIMARY KEY AUTO_INCREMENT,
    crew_id VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(50) NOT NULL,
    gender VARCHAR(10) NOT NULL,
    age INT NOT NULL,
    phone VARCHAR(20) NOT NULL,
    id_card VARCHAR(20) NOT NULL UNIQUE,
    qualification VARCHAR(255),
    experience TEXT,
    training_record TEXT,
    sea_record TEXT,
    schedule_info TEXT,
    attendance_status VARCHAR(20)
);
​
-- 创建设备表
CREATE TABLE IF NOT EXISTS device (
    id INT PRIMARY KEY AUTO_INCREMENT,
    device_id VARCHAR(50) NOT NULL UNIQUE,
    device_name VARCHAR(100) NOT NULL,
    device_type VARCHAR(50) NOT NULL,
    parameters TEXT,
    status VARCHAR(20) NOT NULL,
    maintenance_record TEXT,
    fault_info TEXT,
    boat_id INT,
    FOREIGN KEY (boat_id) REFERENCES boat(id)
);
​
-- 插入初始数据
INSERT INTO user (username, password, role, name, phone) VALUES
('admin', 'admin123', '管理员', '系统管理员', '13800138000'),
('user', 'user123', '用户', '普通用户', '13900139000');
​
INSERT INTO boat (boat_id, boat_name, boat_type, status, latitude, longitude, speed) VALUES
('BOAT001', '捕捞船001', '捕捞船', '航行中', 31.2304, 121.4737, 15),
('BOAT002', '作业船002', '作业船', '作业中', 31.2500, 121.5000, 0);
​
INSERT INTO task (task_id, task_name, task_type, operation_area, start_time, end_time, status, boat_id) VALUES
('TASK001', '东海捕捞作业', '捕捞', '东海海域', '2026-04-01 08:00:00', '2026-04-05 18:00:00', '进行中', 1),
('TASK002', '南海采样作业', '采样', '南海海域', '2026-04-02 09:00:00', '2026-04-06 17:00:00', '未开始', 2);
​
INSERT INTO crew (crew_id, name, gender, age, phone, id_card, qualification, attendance_status) VALUES
('CREW001', '张三', '男', 35, '13800138001', '110101199001010001', '船长证', '正常'),
('CREW002', '李四', '男', 28, '13900139002', '110101199501010002', '船员证', '正常');
​
INSERT INTO device (device_id, device_name, device_type, status, boat_id) VALUES
('DEV001', 'GPS导航仪', '导航设备', '正常运行', 1),
('DEV002', '雷达', '导航设备', '正常运行', 2);

4. 代码实现

4.1 后端实现

4.1.1 实体类

User.java

package com.boat.entity;
​
import lombok.Data;
​
@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String role;
    private String name;
    private String phone;
}

Boat.java

package com.boat.entity;
​
import lombok.Data;
​
@Data
public class Boat {
    private Integer id;
    private String boatId;
    private String boatName;
    private String boatType;
    private String status;
    private Double latitude;
    private Double longitude;
    private Double heading;
    private Double speed;
    private Double altitude;
    private Double voyageDistance;
    private Long voyageDuration;
}

Task.java

package com.boat.entity;
​
import lombok.Data;
​
import java.util.Date;
​
@Data
public class Task {
    private Integer id;
    private String taskId;
    private String taskName;
    private String taskType;
    private String operationArea;
    private Date startTime;
    private Date endTime;
    private String status;
    private Integer boatId;
    private String operationData;
    private String complianceCheck;
}

Crew.java

package com.boat.entity;
​
import lombok.Data;
​
@Data
public class Crew {
    private Integer id;
    private String crewId;
    private String name;
    private String gender;
    private Integer age;
    private String phone;
    private String idCard;
    private String qualification;
    private String experience;
    private String trainingRecord;
    private String seaRecord;
    private String scheduleInfo;
    private String attendanceStatus;
}

Device.java

package com.boat.entity;
​
import lombok.Data;
​
@Data
public class Device {
    private Integer id;
    private String deviceId;
    private String deviceName;
    private String deviceType;
    private String parameters;
    private String status;
    private String maintenanceRecord;
    private String faultInfo;
    private Integer boatId;
}
4.1.2 控制器

UserController.java

package com.boat.controller;

import com.boat.entity.User;
import com.boat.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public User login(@RequestBody User user) {
        return userService.login(user.getUsername(), user.getPassword());
    }

    @GetMapping("/findAll")
    public List<User> findAll() {
        return userService.findAll();
    }

    @PostMapping("/insert")
    public void insert(@RequestBody User user) {
        userService.insert(user);
    }

    @PostMapping("/update")
    public void update(@RequestBody User user) {
        userService.update(user);
    }

    @DeleteMapping("/delete/{id}")
    public void delete(@PathVariable Integer id) {
        userService.delete(id);
    }
}

BoatController.java

package com.boat.controller;

import com.boat.entity.Boat;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/boat")
public class BoatController {

    // 模拟数据存储
    private static List<Boat> boats = new java.util.ArrayList<>();

    static {
        // 初始化模拟数据
        Boat boat1 = new Boat();
        boat1.setId(1);
        boat1.setBoatId("BOAT001");
        boat1.setBoatName("捕捞船001");
        boat1.setBoatType("捕捞船");
        boat1.setStatus("航行中");
        boat1.setLongitude(121.4737);
        boat1.setLatitude(31.2304);
        boat1.setSpeed(15.0);

        Boat boat2 = new Boat();
        boat2.setId(2);
        boat2.setBoatId("BOAT002");
        boat2.setBoatName("作业船002");
        boat2.setBoatType("作业船");
        boat2.setStatus("作业中");
        boat2.setLongitude(121.5000);
        boat2.setLatitude(31.2500);
        boat2.setSpeed(0.0);

        boats.add(boat1);
        boats.add(boat2);
    }

    @GetMapping("/findAll")
    public List<Boat> findAll() {
        return boats;
    }

    @PostMapping("/insert")
    public void insert(@RequestBody Boat boat) {
        boat.setId(boats.size() + 1);
        boats.add(boat);
    }

    @PostMapping("/update")
    public void update(@RequestBody Boat boat) {
        for (int i = 0; i < boats.size(); i++) {
            if (boats.get(i).getId().equals(boat.getId())) {
                boats.set(i, boat);
                break;
            }
        }
    }

    @DeleteMapping("/delete/{id}")
    public void delete(@PathVariable Integer id) {
        boats.removeIf(boat -> boat.getId().equals(id));
    }
}

TaskController.java

package com.boat.controller;

import com.boat.entity.Task;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/task")
public class TaskController {

    // 模拟数据存储
    private static List<Task> tasks = new java.util.ArrayList<>();

    static {
        // 初始化模拟数据
        Task task1 = new Task();
        task1.setId(1);
        task1.setTaskId("TASK001");
        task1.setTaskName("东海捕捞作业");
        task1.setTaskType("捕捞");
        task1.setOperationArea("东海海域");
        task1.setStartTime(java.sql.Timestamp.valueOf("2026-04-01 08:00:00"));
        task1.setEndTime(java.sql.Timestamp.valueOf("2026-04-05 18:00:00"));
        task1.setStatus("进行中");
        task1.setBoatId(1);

        Task task2 = new Task();
        task2.setId(2);
        task2.setTaskId("TASK002");
        task2.setTaskName("南海采样作业");
        task2.setTaskType("采样");
        task2.setOperationArea("南海海域");
        task2.setStartTime(java.sql.Timestamp.valueOf("2026-04-02 09:00:00"));
        task2.setEndTime(java.sql.Timestamp.valueOf("2026-04-06 17:00:00"));
        task2.setStatus("未开始");
        task2.setBoatId(2);

        tasks.add(task1);
        tasks.add(task2);
    }

    @GetMapping("/findAll")
    public List<Task> findAll() {
        return tasks;
    }

    @PostMapping("/insert")
    public void insert(@RequestBody Task task) {
        task.setId(tasks.size() + 1);
        tasks.add(task);
    }

    @PostMapping("/update")
    public void update(@RequestBody Task task) {
        for (int i = 0; i < tasks.size(); i++) {
            if (tasks.get(i).getId().equals(task.getId())) {
                tasks.set(i, task);
                break;
            }
        }
    }

    @DeleteMapping("/delete/{id}")
    public void delete(@PathVariable Integer id) {
        tasks.removeIf(task -> task.getId().equals(id));
    }
}

4.2 前端实现

4.2.1 登录页面(Login.vue)
<template>
  <div class="login-container">
    <div class="login-form">
      <h2>海洋船只出海作业智能管理系统</h2>
      <form @submit.prevent="handleLogin">
        <div class="form-group">
          <label for="username">用户名</label>
          <input type="text" id="username" v-model="username" required />
        </div>
        <div class="form-group">
          <label for="password">密码</label>
          <input type="password" id="password" v-model="password" required />
        </div>
        <div class="form-group">
          <label for="role">角色</label>
          <select id="role" v-model="role" required>
            <option value="admin">管理员</option>
            <option value="user">用户</option>
          </select>
        </div>
        <button type="submit" class="login-button">登录</button>
      </form>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'Login',
  data() {
    return {
      username: '',
      password: '',
      role: 'admin'
    }
  },
  methods: {
    async handleLogin() {
      try {
        const response = await axios.post('/api/user/login', {
          username: this.username,
          password: this.password
        })
        if (response.data) {
          localStorage.setItem('user', JSON.stringify(response.data))
          this.$router.push('/home')
        } else {
          alert('登录失败,请检查用户名和密码')
        }
      } catch (error) {
        console.error('登录错误:', error)
        alert('登录失败,请稍后重试')
      }
    }
  }
}
</script>

<style scoped>
.login-container {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f0f2f5;
}

.login-form {
  width: 400px;
  padding: 40px;
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.login-form h2 {
  text-align: center;
  margin-bottom: 30px;
  color: #333;
}

.form-group {
  margin-bottom: 20px;
}

.form-group label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #666;
}

.form-group input,
.form-group select {
  width: 100%;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

.login-button {
  width: 100%;
  padding: 12px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.login-button:hover {
  background-color: #66b1ff;
}
</style>
4.2.2 船只管理页面(Boat.vue)
<template>
  <div class="boat-container">
    <div class="boat-map">
      <h3>船只定位</h3>
      <div id="map" style="width: 100%; height: 400px;"></div>
    </div>
    <div class="boat-list">
      <div class="list-header">
        <h3>船只列表</h3>
        <button class="add-button" @click="openAddDialog">添加船只</button>
      </div>
      <table class="data-table">
        <thead>
          <tr>
            <th>船只ID</th>
            <th>船只名称</th>
            <th>船只类型</th>
            <th>状态</th>
            <th>经度</th>
            <th>纬度</th>
            <th>航速</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="boat in boats" :key="boat.id">
            <td>{{ boat.boatId }}</td>
            <td>{{ boat.boatName }}</td>
            <td>{{ boat.boatType }}</td>
            <td>{{ boat.status }}</td>
            <td>{{ boat.longitude }}</td>
            <td>{{ boat.latitude }}</td>
            <td>{{ boat.speed }}</td>
            <td>
              <button class="edit-button" @click="openEditDialog(boat)">编辑</button>
              <button class="delete-button" @click="deleteBoat(boat.id)">删除</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <!-- 添加/编辑弹窗 -->
    <div v-if="dialogVisible" class="dialog-overlay">
      <div class="dialog-content">
        <div class="dialog-header">
          <h4>{{ dialogTitle }}</h4>
          <button class="close-button" @click="dialogVisible = false">&times;</button>
        </div>
        <div class="dialog-body">
          <form @submit.prevent="saveBoat">
            <div class="form-group">
              <label for="boatId">船只ID</label>
              <input type="text" id="boatId" v-model="formData.boatId" required />
            </div>
            <div class="form-group">
              <label for="boatName">船只名称</label>
              <input type="text" id="boatName" v-model="formData.boatName" required />
            </div>
            <div class="form-group">
              <label for="boatType">船只类型</label>
              <input type="text" id="boatType" v-model="formData.boatType" required />
            </div>
            <div class="form-group">
              <label for="status">状态</label>
              <input type="text" id="status" v-model="formData.status" required />
            </div>
            <div class="form-group">
              <label for="longitude">经度</label>
              <input type="number" id="longitude" v-model.number="formData.longitude" step="0.0001" required />
            </div>
            <div class="form-group">
              <label for="latitude">纬度</label>
              <input type="number" id="latitude" v-model.number="formData.latitude" step="0.0001" required />
            </div>
            <div class="form-group">
              <label for="speed">航速</label>
              <input type="number" id="speed" v-model.number="formData.speed" step="0.1" required />
            </div>
            <div class="dialog-footer">
              <button type="button" class="cancel-button" @click="dialogVisible = false">取消</button>
              <button type="submit" class="save-button">保存</button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import * as echarts from 'echarts'
import axios from 'axios'

export default {
  name: 'Boat',
  data() {
    return {
      boats: [],
      dialogVisible: false,
      dialogTitle: '',
      formData: {
        id: null,
        boatId: '',
        boatName: '',
        boatType: '',
        status: '',
        longitude: 0,
        latitude: 0,
        speed: 0
      }
    }
  },
  mounted() {
    this.loadBoats()
  },
  methods: {
    async loadBoats() {
      try {
        const response = await axios.get('/api/boat/findAll')
        this.boats = response.data
        this.initMap()
      } catch (error) {
        console.error('加载船只数据失败:', error)
      }
    },
    initMap() {
      const mapChart = echarts.init(document.getElementById('map'))
      
      const option = {
        title: {
          text: '船只定位',
          left: 'center'
        },
        tooltip: {
          trigger: 'item',
          formatter: function(params) {
            return params.data.name + '<br/>经度: ' + params.data.value[0] + '<br/>纬度: ' + params.data.value[1] + '<br/>状态: ' + params.data.status
          }
        },
        xAxis: {
          type: 'value',
          name: '经度',
          min: 121.4,
          max: 121.6
        },
        yAxis: {
          type: 'value',
          name: '纬度',
          min: 31.2,
          max: 31.3
        },
        series: [
          {
            name: '船只位置',
            type: 'scatter',
            data: this.boats.map(boat => ({
              name: boat.boatName,
              value: [boat.longitude, boat.latitude],
              status: boat.status
            })),
            symbolSize: 15,
            label: {
              formatter: '{b}',
              position: 'right',
              show: true
            },
            itemStyle: {
              color: function(params) {
                return params.data.status === '航行中' ? '#3b82f6' : '#10b981'
              }
            }
          }
        ]
      }
      
      mapChart.setOption(option)
      
      window.addEventListener('resize', function() {
        mapChart.resize()
      })
    },
    openAddDialog() {
      this.dialogTitle = '添加船只'
      this.formData = {
        id: null,
        boatId: '',
        boatName: '',
        boatType: '',
        status: '',
        longitude: 0,
        latitude: 0,
        speed: 0
      }
      this.dialogVisible = true
    },
    openEditDialog(boat) {
      this.dialogTitle = '编辑船只'
      this.formData = { ...boat }
      this.dialogVisible = true
    },
    async saveBoat() {
      try {
        if (this.formData.id) {
          // 编辑现有船只
          await axios.post('/api/boat/update', this.formData)
        } else {
          // 添加新船只
          await axios.post('/api/boat/insert', this.formData)
        }
        this.dialogVisible = false
        this.loadBoats() // 重新加载船只数据
      } catch (error) {
        console.error('保存船只数据失败:', error)
        alert('保存失败,请稍后重试')
      }
    },
    async deleteBoat(id) {
      if (confirm('确定要删除这艘船只吗?')) {
        try {
          await axios.delete(`/api/boat/delete/${id}`)
          this.loadBoats() // 重新加载船只数据
        } catch (error) {
          console.error('删除船只失败:', error)
          alert('删除失败,请稍后重试')
        }
      }
    }
  }
}
</script>

<style scoped>
.boat-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.boat-map {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.boat-map h3 {
  margin-bottom: 15px;
  color: #303133;
}

.boat-list {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.list-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}

.list-header h3 {
  margin: 0;
  color: #303133;
}

.add-button {
  padding: 8px 16px;
  background-color: #67c23a;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.add-button:hover {
  background-color: #85ce61;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

.data-table th,
.data-table td {
  padding: 12px;
  text-align: left;
  border-bottom: 1px solid #e4e7ed;
}

.data-table th {
  background-color: #f5f7fa;
  font-weight: 600;
  color: #303133;
}

.data-table tr:hover {
  background-color: #f5f7fa;
}

.edit-button {
  padding: 5px 10px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-right: 5px;
  transition: background-color 0.3s;
}

.edit-button:hover {
  background-color: #66b1ff;
}

.delete-button {
  padding: 5px 10px;
  background-color: #f56c6c;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.delete-button:hover {
  background-color: #f78989;
}

/* 弹窗样式 */
.dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.dialog-content {
  background-color: white;
  border-radius: 8px;
  width: 90%;
  max-width: 500px;
  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.15);
}

.dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid #e4e7ed;
}

.dialog-header h4 {
  margin: 0;
  color: #303133;
}

.close-button {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.close-button:hover {
  color: #606266;
}

.dialog-body {
  padding: 20px;
}

.form-group {
  margin-bottom: 15px;
}

.form-group label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #606266;
}

.form-group input {
  width: 100%;
  padding: 10px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  font-size: 14px;
  transition: border-color 0.3s;
}

.form-group input:focus {
  outline: none;
  border-color: #409eff;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
}

.cancel-button {
  padding: 8px 16px;
  background-color: #909399;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.cancel-button:hover {
  background-color: #a6a9ad;
}

.save-button {
  padding: 8px 16px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.save-button:hover {
  background-color: #66b1ff;
}
</style>
4.2.3 任务管理页面(Task.vue)
<template>
  <div class="task-container">
    <div class="task-header">
      <h3>任务管理</h3>
      <button class="add-button" @click="openAddDialog">添加任务</button>
    </div>
    <div class="task-list">
      <table class="data-table">
        <thead>
          <tr>
            <th>任务ID</th>
            <th>任务名称</th>
            <th>作业类型</th>
            <th>作业区域</th>
            <th>开始时间</th>
            <th>结束时间</th>
            <th>状态</th>
            <th>所属船只</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="task in tasks" :key="task.id">
            <td>{{ task.taskId }}</td>
            <td>{{ task.taskName }}</td>
            <td>{{ task.taskType }}</td>
            <td>{{ task.operationArea }}</td>
            <td>{{ task.startTime }}</td>
            <td>{{ task.endTime }}</td>
            <td>{{ task.status }}</td>
            <td>{{ task.boatName }}</td>
            <td>
              <button class="edit-button" @click="openEditDialog(task)">编辑</button>
              <button class="delete-button" @click="deleteTask(task.id)">删除</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <!-- 添加/编辑弹窗 -->
    <div v-if="dialogVisible" class="dialog-overlay">
      <div class="dialog-content">
        <div class="dialog-header">
          <h4>{{ dialogTitle }}</h4>
          <button class="close-button" @click="dialogVisible = false">&times;</button>
        </div>
        <div class="dialog-body">
          <form @submit.prevent="saveTask">
            <div class="form-group">
              <label for="taskId">任务ID</label>
              <input type="text" id="taskId" v-model="formData.taskId" required />
            </div>
            <div class="form-group">
              <label for="taskName">任务名称</label>
              <input type="text" id="taskName" v-model="formData.taskName" required />
            </div>
            <div class="form-group">
              <label for="taskType">作业类型</label>
              <input type="text" id="taskType" v-model="formData.taskType" required />
            </div>
            <div class="form-group">
              <label for="operationArea">作业区域</label>
              <input type="text" id="operationArea" v-model="formData.operationArea" required />
            </div>
            <div class="form-group">
              <label for="startTime">开始时间</label>
              <input type="datetime-local" id="startTime" v-model="formData.startTime" required />
            </div>
            <div class="form-group">
              <label for="endTime">结束时间</label>
              <input type="datetime-local" id="endTime" v-model="formData.endTime" required />
            </div>
            <div class="form-group">
              <label for="status">状态</label>
              <input type="text" id="status" v-model="formData.status" required />
            </div>
            <div class="form-group">
              <label for="boatName">所属船只</label>
              <input type="text" id="boatName" v-model="formData.boatName" required />
            </div>
            <div class="dialog-footer">
              <button type="button" class="cancel-button" @click="dialogVisible = false">取消</button>
              <button type="submit" class="save-button">保存</button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'Task',
  data() {
    return {
      tasks: [],
      dialogVisible: false,
      dialogTitle: '',
      formData: {
        id: null,
        taskId: '',
        taskName: '',
        taskType: '',
        operationArea: '',
        startTime: '',
        endTime: '',
        status: '',
        boatName: ''
      }
    }
  },
  mounted() {
    this.loadTasks()
  },
  methods: {
    async loadTasks() {
      try {
        const response = await axios.get('/api/task/findAll')
        this.tasks = response.data
      } catch (error) {
        console.error('加载任务数据失败:', error)
      }
    },
    openAddDialog() {
      this.dialogTitle = '添加任务'
      this.formData = {
        id: null,
        taskId: '',
        taskName: '',
        taskType: '',
        operationArea: '',
        startTime: '',
        endTime: '',
        status: '',
        boatName: ''
      }
      this.dialogVisible = true
    },
    openEditDialog(task) {
      this.dialogTitle = '编辑任务'
      this.formData = { ...task }
      this.dialogVisible = true
    },
    async saveTask() {
      try {
        if (this.formData.id) {
          // 编辑现有任务
          await axios.post('/api/task/update', this.formData)
        } else {
          // 添加新任务
          await axios.post('/api/task/insert', this.formData)
        }
        this.dialogVisible = false
        this.loadTasks() // 重新加载任务数据
      } catch (error) {
        console.error('保存任务数据失败:', error)
        alert('保存失败,请稍后重试')
      }
    },
    async deleteTask(id) {
      if (confirm('确定要删除这个任务吗?')) {
        try {
          await axios.delete(`/api/task/delete/${id}`)
          this.loadTasks() // 重新加载任务数据
        } catch (error) {
          console.error('删除任务失败:', error)
          alert('删除失败,请稍后重试')
        }
      }
    }
  }
}
</script>

<style scoped>
.task-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.task-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.task-header h3 {
  margin: 0;
  color: #303133;
}

.add-button {
  padding: 8px 16px;
  background-color: #67c23a;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.add-button:hover {
  background-color: #85ce61;
}

.task-list {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

.data-table th,
.data-table td {
  padding: 12px;
  text-align: left;
  border-bottom: 1px solid #e4e7ed;
}

.data-table th {
  background-color: #f5f7fa;
  font-weight: 600;
  color: #303133;
}

.data-table tr:hover {
  background-color: #f5f7fa;
}

.edit-button {
  padding: 5px 10px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-right: 5px;
  transition: background-color 0.3s;
}

.edit-button:hover {
  background-color: #66b1ff;
}

.delete-button {
  padding: 5px 10px;
  background-color: #f56c6c;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.delete-button:hover {
  background-color: #f78989;
}

/* 弹窗样式 */
.dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.dialog-content {
  background-color: white;
  border-radius: 8px;
  width: 90%;
  max-width: 500px;
  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.15);
}

.dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid #e4e7ed;
}

.dialog-header h4 {
  margin: 0;
  color: #303133;
}

.close-button {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.close-button:hover {
  color: #606266;
}

.dialog-body {
  padding: 20px;
}

.form-group {
  margin-bottom: 15px;
}

.form-group label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #606266;
}

.form-group input {
  width: 100%;
  padding: 10px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  font-size: 14px;
  transition: border-color 0.3s;
}

.form-group input:focus {
  outline: none;
  border-color: #409eff;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
}

.cancel-button {
  padding: 8px 16px;
  background-color: #909399;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.cancel-button:hover {
  background-color: #a6a9ad;
}

.save-button {
  padding: 8px 16px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.save-button:hover {
  background-color: #66b1ff;
}
</style>

5. 功能说明

5.1 系统登录

  • 用户可以通过用户名和密码登录系统

  • 支持管理员和普通用户两种角色登录

  • 登录成功后跳转到系统主页

5.2 船只管理

  • 实时查看船只位置和状态

  • 添加新船只信息

  • 编辑现有船只信息

  • 删除船只信息

  • 船只定位可视化展示

5.3 任务管理

  • 创建新的出海作业任务

  • 编辑现有任务信息

  • 删除任务

  • 查看任务列表和状态

5.4 船员管理

  • 管理船员基本信息

  • 查看船员资质和培训记录

  • 船员排班管理

5.5 设备管理

  • 监控船只设备运行状态

  • 管理设备维护记录

  • 设备故障处理

  • 设备参数配置

5.6 应急处置

  • 应急事件上报

  • 应急处置预案管理

  • 应急事件处理流程

5.7 系统设置

  • 用户管理

  • 系统配置

  • 权限管理

  • 系统日志

6. 项目启动与部署

6.1 后端启动

  1. 执行数据库初始化脚本 init.sql 创建数据库和表结构

  2. 运行 BoatManagementApplication.java 启动后端服务

  3. 后端服务默认运行在 http://localhost:8080

6.2 前端启动

  1. 进入 frontend 目录

  2. 执行 npm install 安装依赖

  3. 执行 npm run dev 启动前端开发服务器

  4. 前端服务默认运行在 http://localhost:3000

6.3 生产部署

  1. 后端:使用 mvn clean package 打包成jar文件,然后部署到服务器

  2. 前端:使用 npm run build 构建生产版本,然后部署到静态文件服务器

7. 总结

本项目实现了一个完整的海洋船只出海作业智能管理系统,涵盖了船只定位、任务管理、船员管理、设备监控和应急处置等核心功能。系统采用前后端分离的架构设计,使用Spring Boot和Vue 3作为主要技术栈,具有以下特点:

  1. 功能完整:实现了需求文档中要求的所有核心功能

  2. 技术先进:使用了Spring Boot 3、Vue 3等最新技术

  3. 界面美观:采用现代化的UI设计,交互流畅

  4. 易于扩展:模块化设计,便于后续功能扩展

  5. 安全可靠:实现了用户认证和权限管理

大家点赞、收藏、关注、评论啦  其他的定制服务 商务合作  下方联系卡片↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 或者私信作者

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐