Browse Source

平台

master
menghao 2 months ago
parent
commit
39b88f602d
  1. 26
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/PlatformLibController.java
  2. 4
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
  3. 13
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
  4. 60
      ruoyi-system/src/main/java/com/ruoyi/system/domain/PlatformLib.java
  5. 7
      ruoyi-system/src/main/resources/mapper/system/PlatformLibMapper.xml
  6. 3
      ruoyi-ui/.env.development
  7. 7
      ruoyi-ui/src/api/system/lib.js
  8. 146
      ruoyi-ui/src/views/cesiumMap/index.vue
  9. 87
      ruoyi-ui/src/views/childRoom/RightPanel.vue
  10. 146
      ruoyi-ui/src/views/childRoom/index.vue
  11. 128
      ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue
  12. 44
      ruoyi-ui/src/views/dialogs/WaypointEditDialog.vue

26
ruoyi-admin/src/main/java/com/ruoyi/web/controller/PlatformLibController.java

@ -1,17 +1,11 @@
package com.ruoyi.web.controller; package com.ruoyi.web.controller;
import java.io.IOException;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
@ -20,6 +14,9 @@ import com.ruoyi.system.domain.PlatformLib;
import com.ruoyi.system.service.IPlatformLibService; import com.ruoyi.system.service.IPlatformLibService;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.config.RuoYiConfig;
import org.springframework.web.multipart.MultipartFile;
/** /**
* 平台模版库Controller * 平台模版库Controller
@ -74,9 +71,18 @@ public class PlatformLibController extends BaseController
*/ */
@PreAuthorize("@ss.hasPermi('system:lib:add')") @PreAuthorize("@ss.hasPermi('system:lib:add')")
@Log(title = "平台模版库", businessType = BusinessType.INSERT) @Log(title = "平台模版库", businessType = BusinessType.INSERT)
@PostMapping @PostMapping("/add")
public AjaxResult add(@RequestBody PlatformLib platformLib) public AjaxResult add(PlatformLib platformLib, @RequestParam("file") MultipartFile file) throws IOException
{ {
// 判断前端是否有文件传过来
if (file != null && !file.isEmpty())
{
String fileName = FileUploadUtils.upload(RuoYiConfig.getProfile(), file);
// 把这个路径存入实体类的 iconUrl 属性,对应数据库 icon_url 字段
platformLib.setIconUrl(fileName);
}
// 执行原有的插入逻辑
return toAjax(platformLibService.insertPlatformLib(platformLib)); return toAjax(platformLibService.insertPlatformLib(platformLib));
} }

4
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java

@ -172,7 +172,9 @@ public class FileUploadUtils
{ {
int dirLastIndex = RuoYiConfig.getProfile().length() + 1; int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex); String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; String path = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
return path.replaceAll("/+", "/");
} }
/** /**

13
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@ -28,8 +28,7 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
*/ */
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration @Configuration
public class SecurityConfig public class SecurityConfig {
{
/** /**
* 自定义用户认证逻辑 * 自定义用户认证逻辑
*/ */
@ -70,8 +69,7 @@ public class SecurityConfig
* 身份验证实现 * 身份验证实现
*/ */
@Bean @Bean
public AuthenticationManager authenticationManager() public AuthenticationManager authenticationManager() {
{
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService); daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
@ -94,8 +92,7 @@ public class SecurityConfig
* authenticated | 用户登录后可访问 * authenticated | 用户登录后可访问
*/ */
@Bean @Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
{
return httpSecurity return httpSecurity
// CSRF禁用,因为不使用session // CSRF禁用,因为不使用session
.csrf(csrf -> csrf.disable()) .csrf(csrf -> csrf.disable())
@ -115,6 +112,7 @@ public class SecurityConfig
// 静态资源,可匿名访问 // 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/profile/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证 // 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated(); .anyRequest().authenticated();
}) })
@ -132,8 +130,7 @@ public class SecurityConfig
* 强散列哈希加密实现 * 强散列哈希加密实现
*/ */
@Bean @Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() public BCryptPasswordEncoder bCryptPasswordEncoder() {
{
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
} }

60
ruoyi-system/src/main/java/com/ruoyi/system/domain/PlatformLib.java

@ -11,72 +11,84 @@ import com.ruoyi.common.core.domain.BaseEntity;
* @author ruoyi * @author ruoyi
* @date 2026-01-14 * @date 2026-01-14
*/ */
public class PlatformLib extends BaseEntity public class PlatformLib extends BaseEntity {
{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 平台库ID */ /**
* 平台库ID
*/
private Long id; private Long id;
/** 平台名称 (如: F-22 猛禽) */ /**
* 平台名称 (: F-22 猛禽)
*/
@Excel(name = "平台名称 (如: F-22 猛禽)") @Excel(name = "平台名称 (如: F-22 猛禽)")
private String name; private String name;
/** 类型: Aircraft, Vehicle, Radar, Ship */ /**
@Excel(name = "类型: Aircraft, Vehicle, Radar, Ship") * 类型: Aircraft, Vehicle, Radar, Ship
*/
@Excel(name = "类型: Air, Sea, Ground")
private String type; private String type;
/** 核心参数JSON: {icon_url, max_speed, max_fuel, radar_range...} */ /**
* 核心参数JSON: {icon_url, max_speed, max_fuel, radar_range...}
*/
@Excel(name = "核心参数JSON: {icon_url, max_speed, max_fuel, radar_range...}") @Excel(name = "核心参数JSON: {icon_url, max_speed, max_fuel, radar_range...}")
private String specsJson; private String specsJson;
public void setId(Long id) @Excel(name = "平台图标路径")
{ private String iconUrl;
public void setId(Long id) {
this.id = id; this.id = id;
} }
public Long getId() public Long getId() {
{
return id; return id;
} }
public void setName(String name) public void setName(String name) {
{
this.name = name; this.name = name;
} }
public String getName() public String getName() {
{
return name; return name;
} }
public void setType(String type) public void setType(String type) {
{
this.type = type; this.type = type;
} }
public String getType() public String getType() {
{
return type; return type;
} }
public void setSpecsJson(String specsJson) public void setSpecsJson(String specsJson) {
{
this.specsJson = specsJson; this.specsJson = specsJson;
} }
public String getSpecsJson() public String getSpecsJson() {
{
return specsJson; return specsJson;
} }
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public String getIconUrl() {
return iconUrl;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId()) .append("id", getId())
.append("name", getName()) .append("name", getName())
.append("type", getType()) .append("type", getType())
.append("specsJson", getSpecsJson()) .append("specsJson", getSpecsJson())
.append("iconUrl", getIconUrl())
.toString(); .toString();
} }
} }

7
ruoyi-system/src/main/resources/mapper/system/PlatformLibMapper.xml

@ -9,10 +9,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="name" column="name" /> <result property="name" column="name" />
<result property="type" column="type" /> <result property="type" column="type" />
<result property="specsJson" column="specs_json" /> <result property="specsJson" column="specs_json" />
<result property="iconUrl" column="icon_url" />
</resultMap> </resultMap>
<sql id="selectPlatformLibVo"> <sql id="selectPlatformLibVo">
select id, name, type, specs_json from platform_lib select id, name, type, specs_json,icon_url from platform_lib
</sql> </sql>
<select id="selectPlatformLibList" parameterType="PlatformLib" resultMap="PlatformLibResult"> <select id="selectPlatformLibList" parameterType="PlatformLib" resultMap="PlatformLibResult">
@ -21,6 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if> <if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>
<if test="type != null and type != ''"> and type = #{type}</if> <if test="type != null and type != ''"> and type = #{type}</if>
<if test="specsJson != null and specsJson != ''"> and specs_json = #{specsJson}</if> <if test="specsJson != null and specsJson != ''"> and specs_json = #{specsJson}</if>
<if test="iconUrl != null and iconUrl != ''"> and icon_url = #{iconUrl}</if>
</where> </where>
</select> </select>
@ -35,11 +37,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="name != null and name != ''">name,</if> <if test="name != null and name != ''">name,</if>
<if test="type != null and type != ''">type,</if> <if test="type != null and type != ''">type,</if>
<if test="specsJson != null and specsJson != ''">specs_json,</if> <if test="specsJson != null and specsJson != ''">specs_json,</if>
<if test="iconUrl != null and iconUrl != ''">icon_url,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="name != null and name != ''">#{name},</if> <if test="name != null and name != ''">#{name},</if>
<if test="type != null and type != ''">#{type},</if> <if test="type != null and type != ''">#{type},</if>
<if test="specsJson != null and specsJson != ''">#{specsJson},</if> <if test="specsJson != null and specsJson != ''">#{specsJson},</if>
<if test="iconUrl != null and iconUrl != ''">#{iconUrl},</if>
</trim> </trim>
</insert> </insert>
@ -49,6 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="name != null and name != ''">name = #{name},</if> <if test="name != null and name != ''">name = #{name},</if>
<if test="type != null and type != ''">type = #{type},</if> <if test="type != null and type != ''">type = #{type},</if>
<if test="specsJson != null and specsJson != ''">specs_json = #{specsJson},</if> <if test="specsJson != null and specsJson != ''">specs_json = #{specsJson},</if>
<if test="iconUrl != null and iconUrl != ''">icon_url = #{iconUrl},</if>
</trim> </trim>
where id = #{id} where id = #{id}
</update> </update>

3
ruoyi-ui/.env.development

@ -7,5 +7,8 @@ ENV = 'development'
# 若依管理系统/开发环境 # 若依管理系统/开发环境
VUE_APP_BASE_API = '/dev-api' VUE_APP_BASE_API = '/dev-api'
# 访问地址(绕过 /dev-api 代理,用于解决静态资源/图片访问 401 认证问题)
VUE_APP_BACKEND_URL = 'http://localhost:8080'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true

7
ruoyi-ui/src/api/system/lib.js

@ -20,9 +20,12 @@ export function getLib(id) {
// 新增平台模版库 // 新增平台模版库
export function addLib(data) { export function addLib(data) {
return request({ return request({
url: '/system/lib', url: '/system/lib/add',
method: 'post', method: 'post',
data: data data: data,
headers: {
'Content-Type': 'multipart/form-data'
}
}) })
} }

146
ruoyi-ui/src/views/cesiumMap/index.vue

@ -181,6 +181,7 @@ export default {
this.drawingPoints = []; this.drawingPoints = [];
let activeCursorPosition = null; let activeCursorPosition = null;
this.isDrawing = true; this.isDrawing = true;
this.viewer.canvas.style.cursor = 'crosshair';
this.drawingHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); this.drawingHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
window.addEventListener('contextmenu', this.preventContextMenu, true); window.addEventListener('contextmenu', this.preventContextMenu, true);
// //
@ -256,8 +257,8 @@ export default {
name: `WP${index + 1}`, name: `WP${index + 1}`,
lat: coords.lat, lat: coords.lat,
lng: coords.lng, lng: coords.lng,
alt: 500, // alt: 5000,
speed: 600 // speed: 800
}; };
}); });
this.$emit('draw-complete', latLngPoints); this.$emit('draw-complete', latLngPoints);
@ -271,15 +272,37 @@ export default {
}, 200); }, 200);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}, },
//线
renderRouteWaypoints(waypoints, routeId = 'default') { renderRouteWaypoints(waypoints, routeId = 'default') {
if (!waypoints || waypoints.length < 1) return; if (!waypoints || waypoints.length < 1) return;
const positions = []; // 线
// 1. const lineId = `route-line-${routeId}`;
const existingLine = this.viewer.entities.getById(lineId);
if (existingLine) {
this.viewer.entities.remove(existingLine);
}
// 线
waypoints.forEach((wp,index) => {
const waypointEntityId = `wp_${routeId}_${wp.id}`;
const existingWaypoint = this.viewer.entities.getById(waypointEntityId);
if (existingWaypoint) {
this.viewer.entities.remove(existingWaypoint);
}
const arcId = `arc-line-${routeId}-${index}`;
const existingArc = this.viewer.entities.getById(arcId);
if (existingArc) {
this.viewer.entities.remove(existingArc);
}
});
//
const originalPositions = [];
waypoints.forEach((wp, index) => { waypoints.forEach((wp, index) => {
const lon = parseFloat(wp.lng); const lon = parseFloat(wp.lng);
const lat = parseFloat(wp.lat); const lat = parseFloat(wp.lat);
const pos = Cesium.Cartesian3.fromDegrees(lon, lat, parseFloat(wp.altitude || wp.alt || 500)); // 使 Number Double
positions.push(pos); const altValue = Number(wp.alt || 5000);
const pos = Cesium.Cartesian3.fromDegrees(lon, lat, altValue);
originalPositions.push(pos);
this.viewer.entities.add({ this.viewer.entities.add({
id: `wp_${routeId}_${wp.id}`, id: `wp_${routeId}_${wp.id}`,
name: wp.name || `WP${index + 1}`, name: wp.name || `WP${index + 1}`,
@ -307,25 +330,99 @@ export default {
} }
}); });
}); });
// 2. 线 > 1 // 线
if (positions.length > 1) { if (waypoints.length > 1) {
let finalPathPositions = [];
for (let i = 0; i < waypoints.length; i++) {
const currPos = originalPositions[i];
const radius = this.getWaypointRadius(waypoints[i]);
// 线
if (i > 0 && i < waypoints.length - 1 && radius > 0) {
const prevPos = originalPositions[i - 1];
const nextPos = originalPositions[i + 1];
const arcPoints = this.computeArcPositions(prevPos, currPos, nextPos, radius);
// 线
this.viewer.entities.add({
id: `arc-line-${routeId}-${i}`,
polyline: {
positions: arcPoints,
width: 8,
material: Cesium.Color.RED,
clampToGround: true,
zIndex: 20
}
});
console.log(`>>> 航点 ${waypoints[i].name} 已渲染红色转弯弧,半径: ${radius}`);
}
if (i === 0 || i === waypoints.length - 1 || radius <= 0) {
finalPathPositions.push(currPos);
} else {
const prevPos = originalPositions[i - 1];
const nextPos = originalPositions[i + 1];
// 线
const arcPoints = this.computeArcPositions(prevPos, currPos, nextPos, radius);
finalPathPositions.push(...arcPoints);
}
}
// 线 Polyline
const routeEntity = this.viewer.entities.add({ const routeEntity = this.viewer.entities.add({
id: `route-line-${routeId}`, id: lineId,
polyline: { polyline: {
positions: positions, positions: finalPathPositions,
width: 4, width: 4,
material: new Cesium.PolylineDashMaterialProperty({ material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.WHITE, color: Cesium.Color.WHITE,
gapColor: Cesium.Color.BLACK, gapColor: Cesium.Color.BLACK,
dashLength: 20.0 dashLength: 20.0
}), }),
clampToGround: true clampToGround: true,
zIndex: 1
}, },
properties: {isMissionRouteLine: true, routeId: routeId} properties: {isMissionRouteLine: true, routeId: routeId}
}); });
this.allEntities.push({id: `route-line-${routeId}`, entity: routeEntity, type: 'line'}); if (this.allEntities) {
this.allEntities.push({id: lineId, entity: routeEntity, type: 'line'});
}
} }
}, },
//
getWaypointRadius(wp) {
const speed = wp.speed || 800;
const bankAngle = wp.turnAngle || 0;
if (bankAngle <= 0) return 0;
const v_mps = speed / 3.6;
const radians = bankAngle * (Math.PI / 180);
const g = 9.8;
return (v_mps * v_mps) / (g * Math.tan(radians));
},
//
computeArcPositions(p1, p2, p3, radius) {
const v1 = Cesium.Cartesian3.subtract(p1, p2, new Cesium.Cartesian3());
const v2 = Cesium.Cartesian3.subtract(p3, p2, new Cesium.Cartesian3());
const dir1 = Cesium.Cartesian3.normalize(v1, new Cesium.Cartesian3());
const dir2 = Cesium.Cartesian3.normalize(v2, new Cesium.Cartesian3());
const angle = Cesium.Cartesian3.angleBetween(dir1, dir2);
const dist = radius / Math.tan(angle / 2);
const t1 = Cesium.Cartesian3.add(p2, Cesium.Cartesian3.multiplyByScalar(dir1, dist, new Cesium.Cartesian3()), new Cesium.Cartesian3());
const t2 = Cesium.Cartesian3.add(p2, Cesium.Cartesian3.multiplyByScalar(dir2, dist, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let arc = [];
// 15线
for (let t = 0; t <= 1; t += 0.07) {
const c1 = Math.pow(1 - t, 2);
const c2 = 2 * (1 - t) * t;
const c3 = Math.pow(t, 2);
arc.push(new Cesium.Cartesian3(
c1 * t1.x + c2 * p2.x + c3 * t2.x,
c1 * t1.y + c2 * p2.y + c3 * t2.y,
c1 * t1.z + c2 * p2.z + c3 * t2.z
));
}
return arc;
},
removeRouteById(routeId) { removeRouteById(routeId) {
// routeId // routeId
const entityList = this.viewer.entities.values; const entityList = this.viewer.entities.values;
@ -659,37 +756,52 @@ export default {
console.log(`开始绘制 ${this.getTypeName(mode)}`) console.log(`开始绘制 ${this.getTypeName(mode)}`)
}, },
stopDrawing() { stopDrawing() {
//
if (this.drawingHandler) { if (this.drawingHandler) {
this.drawingHandler.destroy(); this.drawingHandler.destroy();
this.drawingHandler = null; this.drawingHandler = null;
} }
//
this.viewer.scene.canvas.style.cursor = 'default';
//
const allEntities = this.viewer.entities.values;
for (let i = allEntities.length - 1; i >= 0; i--) {
const entity = allEntities[i];
if (entity.id && (
entity.id.toString().startsWith('temp_wp_') ||
entity.id.toString().includes('temp-preview')
)) {
this.viewer.entities.remove(entity);
}
}
//
if (this.tempEntity) { if (this.tempEntity) {
this.viewer.entities.remove(this.tempEntity); this.viewer.entities.remove(this.tempEntity);
this.tempEntity = null; this.tempEntity = null;
} }
//
if (this.tempPreviewEntity) { if (this.tempPreviewEntity) {
this.viewer.entities.remove(this.tempPreviewEntity); this.viewer.entities.remove(this.tempPreviewEntity);
this.tempPreviewEntity = null; this.tempPreviewEntity = null;
} }
// //
if (this.drawingPointEntities) { if (this.drawingPointEntities && this.drawingPointEntities.length > 0) {
this.drawingPointEntities.forEach(pointEntity => { this.drawingPointEntities.forEach(pointEntity => {
this.viewer.entities.remove(pointEntity); this.viewer.entities.remove(pointEntity);
}); });
this.drawingPointEntities = []; this.drawingPointEntities = [];
} }
//
this.drawingPoints = []; this.drawingPoints = [];
this.drawingStartPoint = null; this.drawingStartPoint = null;
this.isDrawing = false; this.isDrawing = false;
this.activeCursorPosition = null; this.activeCursorPosition = null;
// //
if (!this.rightClickHandler) { if (!this.rightClickHandler) {
this.initRightClickHandler(); this.initRightClickHandler();
} }
// //
this.measurementResult = null; this.measurementResult = null;
this.viewer.scene.canvas.style.cursor = 'default'; console.log(">>> 绘制状态已彻底重置,临时实体已清理");
}, },
cancelDrawing() { cancelDrawing() {
// //

87
ruoyi-ui/src/views/childRoom/RightPanel.vue

@ -77,7 +77,7 @@
<!-- 航点列表 --> <!-- 航点列表 -->
<div v-if="expandedRoutes.includes(route.id)" class="tree-children waypoint-children"> <div v-if="expandedRoutes.includes(route.id)" class="tree-children waypoint-children">
<div <div
v-for="point in (expandedRoutes.includes(route.id) && route.waypoints ? route.waypoints : [])" v-for="(point,index) in (expandedRoutes.includes(route.id) && route.waypoints ? route.waypoints : [])"
:key="point.name" :key="point.name"
class="tree-item waypoint-item" class="tree-item waypoint-item"
> >
@ -85,10 +85,10 @@
<i class="el-icon-location tree-icon"></i> <i class="el-icon-location tree-icon"></i>
<div class="tree-item-info"> <div class="tree-item-info">
<div class="tree-item-name">{{ point.name }}</div> <div class="tree-item-name">{{ point.name }}</div>
<div class="tree-item-meta">高度: {{ point.altitude }}m | 速度: {{ point.speed }}</div> <div class="tree-item-meta">高度: {{ point.alt }}m | 速度: {{ point.speed }}</div>
</div> </div>
<div class="tree-item-actions"> <div class="tree-item-actions">
<i class="el-icon-edit" title="编辑" @click.stop="handleOpenWaypointDialog(point)"></i> <i class="el-icon-edit" title="编辑" @click.stop="handleOpenWaypointDialog(point, index, route.waypoints.length)"></i>
</div> </div>
</div> </div>
</div> </div>
@ -144,6 +144,19 @@
</div> </div>
</div> </div>
<div v-if="activeTab === 'platform'" class="tab-content platform-content"> <div v-if="activeTab === 'platform'" class="tab-content platform-content">
<div class="section-header" style="padding: 10px 0; display: flex; justify-content: space-between; align-items: center;">
<div class="section-title">平台列表</div>
<el-button
type="primary"
size="mini"
icon="el-icon-upload2"
@click="handleImportPlatform"
class="create-route-btn-new"
style="width: 90px;height: 25px;line-height: 25px;padding: 0;font-size: 14px;"
>
导入平台
</el-button>
</div>
<div class="platform-categories"> <div class="platform-categories">
<el-tabs v-model="activePlatformTab" type="card" size="mini" class="blue-tabs"> <el-tabs v-model="activePlatformTab" type="card" size="mini" class="blue-tabs">
<el-tab-pane label="空中" name="air"> <el-tab-pane label="空中" name="air">
@ -155,7 +168,11 @@
@click="handleOpenPlatformDialog(platform)" @click="handleOpenPlatformDialog(platform)"
> >
<div class="platform-icon" :style="{ color: platform.color }"> <div class="platform-icon" :style="{ color: platform.color }">
<i :class="platform.icon"></i> <img v-if="isImg(platform.imageUrl)"
:src="formatImg(platform.imageUrl)"
class="platform-img"
/>
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i>
</div> </div>
<div class="platform-info"> <div class="platform-info">
<div class="platform-name">{{ platform.name }}</div> <div class="platform-name">{{ platform.name }}</div>
@ -176,7 +193,11 @@
@click="handleOpenPlatformDialog(platform)" @click="handleOpenPlatformDialog(platform)"
> >
<div class="platform-icon" :style="{ color: platform.color }"> <div class="platform-icon" :style="{ color: platform.color }">
<i :class="platform.icon"></i> <img v-if="isImg(platform.imageUrl)"
:src="formatImg(platform.imageUrl)"
class="platform-img"
/>
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i>
</div> </div>
<div class="platform-info"> <div class="platform-info">
<div class="platform-name">{{ platform.name }}</div> <div class="platform-name">{{ platform.name }}</div>
@ -197,7 +218,11 @@
@click="handleOpenPlatformDialog(platform)" @click="handleOpenPlatformDialog(platform)"
> >
<div class="platform-icon" :style="{ color: platform.color }"> <div class="platform-icon" :style="{ color: platform.color }">
<i :class="platform.icon"></i> <img v-if="isImg(platform.imageUrl)"
:src="formatImg(platform.imageUrl)"
class="platform-img"
/>
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i>
</div> </div>
<div class="platform-info"> <div class="platform-info">
<div class="platform-name">{{ platform.name }}</div> <div class="platform-name">{{ platform.name }}</div>
@ -275,7 +300,7 @@ export default {
groundPlatforms: { groundPlatforms: {
type: Array, type: Array,
default: () => [] default: () => []
} },
}, },
data() { data() {
return { return {
@ -372,6 +397,26 @@ export default {
} }
}, },
//
isImg(path) {
if (!path) return false;
return path.includes('/') || path.includes('data:image');
},
//
formatImg(url) {
if (!url) return '';
//
const cleanPath = url.replace(/\/+/g, '/');
const backendUrl = process.env.VUE_APP_BACKEND_URL;
const finalUrl = backendUrl + cleanPath;
// 👈 F12
console.log('>>> [图片渲染调试] 最终拼接地址:', finalUrl);
return finalUrl;
},
handleHide() { handleHide() {
this.$emit('hide') this.$emit('hide')
}, },
@ -404,6 +449,10 @@ export default {
this.$emit('open-plan-dialog', plan) this.$emit('open-plan-dialog', plan)
}, },
handleImportPlatform() {
this.$emit('open-import-dialog');
},
handleOpenRouteDialog(route) { handleOpenRouteDialog(route) {
this.$emit('open-route-dialog', route) this.$emit('open-route-dialog', route)
}, },
@ -422,8 +471,12 @@ export default {
active: this.activeRouteIds.includes(routeId) active: this.activeRouteIds.includes(routeId)
} }
}, },
handleOpenWaypointDialog(point) { handleOpenWaypointDialog(point,index,total) {
this.$emit('open-waypoint-dialog', point) this.$emit('open-waypoint-dialog', {
...point,
currentIndex: index,
totalPoints: total
});
}, },
handleViewConflict(conflict) { handleViewConflict(conflict) {
@ -883,4 +936,20 @@ export default {
.blue-warning { .blue-warning {
color: #e6a23c; color: #e6a23c;
} }
.platform-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden; /* 裁剪超出部分 */
}
.platform-img {
width: 100%;
height: 100%;
object-fit: cover; /* 关键:图片自动填充不拉伸 */
border-radius: 4px;
}
</style> </style>

146
ruoyi-ui/src/views/childRoom/index.vue

@ -135,6 +135,7 @@
@resolve-conflict="resolveConflict" @resolve-conflict="resolveConflict"
@run-conflict-check="runConflictCheck" @run-conflict-check="runConflictCheck"
@open-platform-dialog="openPlatformDialog" @open-platform-dialog="openPlatformDialog"
@open-import-dialog="showImportDialog = true"
/> />
<!-- 左下角工具面板 --> <!-- 左下角工具面板 -->
<bottom-left-panel /> <bottom-left-panel />
@ -249,6 +250,12 @@
@save="savePageLayout" @save="savePageLayout"
/> />
<!-- 导入平台弹窗 -->
<PlatformImportDialog
:visible.sync="showImportDialog"
@confirm="handleImportConfirm"
/>
<el-dialog <el-dialog
title="新建方案" title="新建方案"
:visible.sync="showPlanNameDialog" :visible.sync="showPlanNameDialog"
@ -285,9 +292,12 @@ import TopHeader from './TopHeader'
import { listScenario,addScenario,delScenario} from "@/api/system/scenario"; import { listScenario,addScenario,delScenario} from "@/api/system/scenario";
import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes"; import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes";
import { updateWaypoints } from "@/api/system/waypoints"; import { updateWaypoints } from "@/api/system/waypoints";
import { listLib,addLib } from "@/api/system/lib";
import PlatformImportDialog from "@/views/dialogs/PlatformImportDialog.vue";
export default { export default {
name: 'MissionPlanningView', name: 'MissionPlanningView',
components: { components: {
PlatformImportDialog,
cesiumMap, cesiumMap,
OnlineMembersDialog, OnlineMembersDialog,
PlatformEditDialog, PlatformEditDialog,
@ -326,6 +336,8 @@ export default {
showNameDialog: false, showNameDialog: false,
newRouteName: '', newRouteName: '',
tempMapPoints: [], tempMapPoints: [],
//
showImportDialog: false,
// //
roomCode: 'JTF-7-ALPHA', roomCode: 'JTF-7-ALPHA',
@ -436,22 +448,9 @@ export default {
// //
activePlatformTab: 'air', activePlatformTab: 'air',
airPlatforms: [ airPlatforms: [],
{ id: 1, name: 'J-20 歼击机', type: '战斗机', icon: 'el-icon-fighter', color: '#52c41a', status: 'ready' }, seaPlatforms: [],
{ id: 2, name: 'KJ-2000 预警机', type: '预警机', icon: 'el-icon-s-promotion', color: '#1890ff', status: 'flying' }, groundPlatforms: [],
{ id: 3, name: 'UAV-01 无人机', type: '侦察无人机', icon: 'el-icon-drone', color: '#fa8c16', status: 'scouting' },
{ id: 4, name: 'H-6K 轰炸机', type: '轰炸机', icon: 'el-icon-bomb', color: '#722ed1', status: 'ready' },
],
seaPlatforms: [
{ id: 5, name: '辽宁舰', type: '航空母舰', icon: 'el-icon-s-cooperation', color: '#1890ff', status: 'sailing' },
{ id: 6, name: '055型驱逐舰', type: '驱逐舰', icon: 'el-icon-ship', color: '#52c41a', status: 'patrol' },
{ id: 7, name: '093型潜艇', type: '核潜艇', icon: 'el-icon-s-help', color: '#333', status: 'hidden' },
],
groundPlatforms: [
{ id: 8, name: 'HQ-9防空系统', type: '防空导弹', icon: 'el-icon-mic', color: '#f5222d', status: 'alert' },
{ id: 9, name: 'PLZ-05自行火炮', type: '自行火炮', icon: 'el-icon-aim', color: '#fa8c16', status: 'ready' },
{ id: 10, name: '指挥控制车', type: '指挥车', icon: 'el-icon-monitor', color: '#1890ff', status: 'operating' },
],
// //
timeProgress: 45, timeProgress: 45,
@ -488,6 +487,7 @@ export default {
this.currentRoomId = this.$route.query.roomId; this.currentRoomId = this.$route.query.roomId;
console.log("从路由接收到的真实房间 ID:", this.currentRoomId); console.log("从路由接收到的真实房间 ID:", this.currentRoomId);
this.getList(); this.getList();
this.getPlatformList();
}, },
methods: { methods: {
// //
@ -523,6 +523,24 @@ export default {
this.$message.info('未找到该航点的业务数据'); this.$message.info('未找到该航点的业务数据');
console.error("查找失败!账本内IDs:", this.selectedRouteDetails.waypoints.map(w => w.id)); console.error("查找失败!账本内IDs:", this.selectedRouteDetails.waypoints.map(w => w.id));
} }
//
const waypointsList = this.selectedRouteDetails.waypoints;
//
const index = waypointsList.findIndex(item => item.id == wpId);
const total = waypointsList.length;
if (index !== -1) {
const wpData = waypointsList[index];
this.selectedWaypoint = {
...JSON.parse(JSON.stringify(wpData)),
currentIndex: index,
totalPoints: total
};
console.log(`>>> [地图触发锁定] 序号: ${index}, 总数: ${total}, 数据已装载`);
this.showWaypointDialog = true;
} else {
this.$message.info('未找到该航点的业务数据');
console.error("查找失败!账本内IDs:", waypointsList.map(w => w.id));
}
}, },
// 线 // 线
showOnlineMembersDialog() { showOnlineMembersDialog() {
@ -533,6 +551,61 @@ export default {
this.selectedPlatform = platform; this.selectedPlatform = platform;
this.showPlatformDialog = true; this.showPlatformDialog = true;
}, },
/** 从数据库查询并分拣平台库数据 */
getPlatformList() {
listLib().then(res => {
const allData = res.rows || [];
this.airPlatforms = [];
this.seaPlatforms = [];
this.groundPlatforms = [];
allData.forEach(item => {
const platform = {
id: item.id,
name: item.name,
type: item.type,
imageUrl: item.iconUrl || '',
icon: item.iconUrl ? '' : 'el-icon-picture-outline',
status: 'ready'
};
if (item.type === 'Air') {
this.airPlatforms.push(platform);
} else if (item.type === 'Sea') {
this.seaPlatforms.push(platform);
} else if (item.type === 'Ground') {
this.groundPlatforms.push(platform);
}
});
});
},
/** 导入确认:将弹窗填写的模版数据存入数据库 */
handleImportConfirm(formData) {
if (!formData.name || !formData.type) {
this.$modal.msgError("请填写完整的平台信息");
return;
}
// FormData
let data = new FormData();
//
if (formData.icon_file) {
data.append("file", formData.icon_file);
}
//
data.append("name", formData.name);
data.append("type", formData.type);
data.append("specsJson", JSON.stringify({
scenarioId: this.selectedPlanId,
createTime: new Date().getTime()
}));
addLib(data).then(response => {
this.$modal.msgSuccess("导入成功");
this.showImportDialog = false;
this.getPlatformList();
}).catch(err => {
//
console.error("上传出错:", err);
});
},
updatePlatform(updatedPlatform) { updatePlatform(updatedPlatform) {
// //
this.$message.success('平台更新成功'); this.$message.success('平台更新成功');
@ -542,7 +615,7 @@ export default {
this.selectedRoute = route; this.selectedRoute = route;
this.showRouteDialog = true; this.showRouteDialog = true;
}, },
// 线 // 线
updateRoute(updatedRoute) { updateRoute(updatedRoute) {
const index = this.routes.findIndex(r => r.id === updatedRoute.id); const index = this.routes.findIndex(r => r.id === updatedRoute.id);
if (index !== -1) { if (index !== -1) {
@ -561,13 +634,11 @@ export default {
// 线 // 线
createRoute(plan) { createRoute(plan) {
if (!plan || !plan.id) return; if (!plan || !plan.id) return;
console.log(`>>> [新建航线触发] 目标方案: ${plan.name}`);
// //
if (this.selectedPlanId !== plan.id) { if (this.selectedPlanId !== plan.id) {
console.log(">>> [执行跨方案切换] 正在清理旧方案数据...");
this.selectPlan(plan); this.selectPlan(plan);
} else { } else {
console.log(">>> [同方案内操作] 保持当前航线显示,直接进入规划模式"); console.log(">>> 保持当前航线显示,直接进入规划模式");
} }
// Cesium // Cesium
if (this.$refs.cesiumMap) { if (this.$refs.cesiumMap) {
@ -753,34 +824,47 @@ export default {
} }
}, },
// //
openWaypointDialog(waypoint) { openWaypointDialog(data) {
this.selectedWaypoint = waypoint; console.log(">>> [父组件接收] 编辑航点详情:", data);
//
this.selectedWaypoint = data;
this.showWaypointDialog = true; this.showWaypointDialog = true;
}, },
/** 航点编辑保存:更新数据库并同步地图显示 */ /** 航点编辑保存:更新数据库并同步地图显示 */
async updateWaypoint(updatedWaypoint) { async updateWaypoint(updatedWaypoint) {
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return; if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return;
try { try {
const response = await updateWaypoints(updatedWaypoint); // if (this.$refs.cesiumMap && updatedWaypoint.turnAngle > 0) {
updatedWaypoint.turnRadius = this.$refs.cesiumMap.getWaypointRadius(updatedWaypoint);
} else {
updatedWaypoint.turnRadius = 0;
}
const response = await updateWaypoints(updatedWaypoint);
if (response.code === 200) { if (response.code === 200) {
const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id); const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id);
if (index !== -1) { if (index !== -1) {
// 1. Vue UI //
this.selectedRouteDetails.waypoints.splice(index, 1, {...updatedWaypoint}); this.selectedRouteDetails.waypoints.splice(index, 1, { ...updatedWaypoint });
//
// 2. ID
if (this.$refs.cesiumMap) { if (this.$refs.cesiumMap) {
// ID //
this.$refs.cesiumMap.updateWaypointGraphicById(updatedWaypoint.id, updatedWaypoint.name); this.$refs.cesiumMap.updateWaypointGraphicById(updatedWaypoint.id, updatedWaypoint.name);
// 线
this.$refs.cesiumMap.renderRouteWaypoints(
this.selectedRouteDetails.waypoints,
this.selectedRouteDetails.id
);
} }
this.showWaypointDialog = false; this.showWaypointDialog = false;
this.$message.success('航点信息已持久化至数据库'); this.$message.success('航点信息已持久化至数据库');
} }
} else {
// code 200catch
throw new Error(response.msg || '后端业务逻辑校验失败');
} }
} catch (error) { } catch (error) {
console.error("更新航点失败:", error); console.error("更新航点失败:", error);
this.$message.error('数据库更新失败,请重试'); this.$message.error(error.message || '数据库更新失败,请重试');
} }
}, },
updateTime() { updateTime() {
@ -1442,7 +1526,7 @@ export default {
const count = this.selectedRouteDetails.waypoints.length + 1; const count = this.selectedRouteDetails.waypoints.length + 1;
this.selectedRouteDetails.waypoints.push({ this.selectedRouteDetails.waypoints.push({
name: `WP${count}`, name: `WP${count}`,
altitude: 5000, alt: 5000,
speed: '800km/h', speed: '800km/h',
eta: `K+01:${(count * 15).toString().padStart(2, '0')}:00` eta: `K+01:${(count * 15).toString().padStart(2, '0')}:00`
}); });

128
ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue

@ -0,0 +1,128 @@
<template>
<el-dialog
title="新增平台"
:visible="visible"
width="500px"
@close="handleClose"
custom-class="blue-dialog"
>
<el-form :model="formData" :rules="rules" ref="importForm" label-width="100px" size="small">
<el-form-item label="平台名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入平台名称"></el-input>
</el-form-item>
<el-form-item label="平台分类" prop="type">
<el-select v-model="formData.type" placeholder="请选择分类" style="width: 100%">
<el-option label="空中" value="Air"></el-option>
<el-option label="海上" value="Sea"></el-option>
<el-option label="地面" value="Ground"></el-option>
</el-select>
</el-form-item>
<el-form-item label="平台图标">
<el-upload
class="icon-uploader"
action="#"
:auto-upload="false"
:show-file-list="false"
:on-change="handleIconChange"
>
<div v-if="imageUrl" class="preview-container">
<img :src="imageUrl" class="platform-icon-preview">
<div class="re-upload-mask">修改图标</div>
</div>
<i v-else class="el-icon-plus uploader-icon"></i>
</el-upload>
<div class="upload-tip">建议尺寸: 64x64, 支持 png/jpg</div>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认新增</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'PlatformImportDialog',
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
return {
imageUrl: '',
formData: {
name: '',
type: '',
icon_file: null
},
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
type: [{ required: true, message: '请选择分类', trigger: 'change' }]
}
}
},
methods: {
handleIconChange(file) {
//
this.imageUrl = URL.createObjectURL(file.raw);
this.formData.icon_file = file.raw;
},
handleClose() {
this.imageUrl = '';
//
this.formData = {
name: '',
type: '',
icon_file: null
};
this.$emit('update:visible', false);
},
handleConfirm() {
this.$refs.importForm.validate((valid) => {
if (valid) {
// File formData
this.$emit('confirm', { ...this.formData });
this.handleClose();
}
});
}
}
}
</script>
<style scoped>
/* 简单的上传样式 */
.icon-uploader {
border: 1px dashed #409EFF;
border-radius: 6px;
width: 80px;
height: 80px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.platform-icon-preview {
width: 60px;
height: 60px;
object-fit: contain;
}
.uploader-icon {
font-size: 24px;
color: #8c939d;
}
.upload-tip {
font-size: 12px;
color: #999;
line-height: 20px;
}
</style>

44
ruoyi-ui/src/views/dialogs/WaypointEditDialog.vue

@ -14,9 +14,9 @@
<el-input v-model="formData.name" placeholder="请输入航点名称"></el-input> <el-input v-model="formData.name" placeholder="请输入航点名称"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="高度" prop="altitude"> <el-form-item label="高度" prop="alt">
<el-input-number <el-input-number
v-model="formData.altitude" v-model="formData.alt"
:min="0" :min="0"
controls-position="right" controls-position="right"
placeholder="请输入高度" placeholder="请输入高度"
@ -34,13 +34,17 @@
></el-input-number> ></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="转弯坡度" prop="turnBank"> <el-form-item label="转弯坡度" prop="turnAngle">
<el-input-number <el-input-number
v-model="formData.turnBank" v-model="formData.turnAngle"
controls-position="right" controls-position="right"
placeholder="请输入转弯坡度" placeholder="请输入转弯坡度"
style="width: 100%;" style="width: 100%;"
:disabled="formData.isBankDisabled"
></el-input-number> ></el-input-number>
<div v-if="formData.isBankDisabled" style="color: #909399; font-size: 12px; margin-top: 4px;">
首尾航点坡度已锁定为 0不可编辑
</div>
</el-form-item> </el-form-item>
<el-form-item label="起始时间" prop="startTime"> <el-form-item label="起始时间" prop="startTime">
@ -78,7 +82,6 @@ export default {
} }
}, },
data() { data() {
// 0
const validateNumber = (rule, value, callback) => { const validateNumber = (rule, value, callback) => {
// value undefined, null // value undefined, null
// !value !0 true // !value !0 true
@ -92,16 +95,19 @@ export default {
return { return {
formData: { formData: {
name: '', name: '',
altitude: 0, alt: 5000,
speed: 0, speed: 800,
turnBank: 0, turnAngle: 0,
startTime: '' startTime: '',
currentIndex: -1,
totalPoints: 0,
isBankDisabled: false
}, },
rules: { rules: {
name: [ name: [
{ required: true, message: '请输入航点名称', trigger: 'blur' } { required: true, message: '请输入航点名称', trigger: 'blur' }
], ],
altitude: [ alt: [
// 使 validator type: 'number' 0 // 使 validator type: 'number' 0
{ required: true, validator: validateNumber, message: '请输入有效高度', trigger: ['blur', 'change'] } { required: true, validator: validateNumber, message: '请输入有效高度', trigger: ['blur', 'change'] }
], ],
@ -109,7 +115,7 @@ export default {
// 使 validator 0 // 使 validator 0
{ required: true, validator: validateNumber, message: '请输入有效速度', trigger: ['blur', 'change'] } { required: true, validator: validateNumber, message: '请输入有效速度', trigger: ['blur', 'change'] }
], ],
turnBank: [ turnAngle: [
// //
{ required: true, validator: validateNumber, message: '请输入有效转弯坡度', trigger: ['blur', 'change'] } { required: true, validator: validateNumber, message: '请输入有效转弯坡度', trigger: ['blur', 'change'] }
], ],
@ -133,17 +139,21 @@ export default {
}, },
methods: { methods: {
initFormData() { initFormData() {
// Number const index = this.waypoint.currentIndex !== undefined ? this.waypoint.currentIndex : -1;
const total = this.waypoint.totalPoints || 0;
const locked = (index === 0) || (total > 0 && index === total - 1);
this.formData = { this.formData = {
name: this.waypoint.name || '', name: this.waypoint.name || '',
// alt: this.waypoint.alt !== undefined && this.waypoint.alt !== null ? Number(this.waypoint.alt) : 0,
altitude: this.waypoint.altitude !== undefined && this.waypoint.altitude !== null ? Number(this.waypoint.altitude) : 0,
speed: this.waypoint.speed !== undefined && this.waypoint.speed !== null ? Number(this.waypoint.speed) : 0, speed: this.waypoint.speed !== undefined && this.waypoint.speed !== null ? Number(this.waypoint.speed) : 0,
turnBank: this.waypoint.turnBank !== undefined && this.waypoint.turnBank !== null ? Number(this.waypoint.turnBank) : 0, startTime: this.waypoint.startTime || '',
startTime: this.waypoint.startTime || '' currentIndex: index,
totalPoints: total,
isBankDisabled: locked,
turnAngle: locked ? 0 : (Number(this.waypoint.turnAngle) || 0)
}; };
//
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.formRef) { if (this.$refs.formRef) {
this.$refs.formRef.clearValidate(); this.$refs.formRef.clearValidate();

Loading…
Cancel
Save