Browse Source

航线颜色等样式显示、右键菜单项显示修复、控制地图拖动小手图标

mh
menghao 4 weeks ago
parent
commit
0fdb456e2c
  1. 65
      ruoyi-system/src/main/java/com/ruoyi/system/domain/RouteWaypoints.java
  2. 62
      ruoyi-system/src/main/java/com/ruoyi/system/domain/WaypointDisplayStyle.java
  3. 59
      ruoyi-system/src/main/java/com/ruoyi/system/typehandler/WaypointDisplayStyleTypeHandler.java
  4. 14
      ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml
  5. 27
      ruoyi-system/src/main/resources/mapper/system/route_waypoints_display_style_json.sql
  6. 2
      ruoyi-ui/.env.development
  7. 8
      ruoyi-ui/src/layout/components/TagsView/index.vue
  8. 19
      ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
  9. 240
      ruoyi-ui/src/views/cesiumMap/index.vue
  10. 47
      ruoyi-ui/src/views/childRoom/TopHeader.vue
  11. 17
      ruoyi-ui/src/views/childRoom/index.vue
  12. 67
      ruoyi-ui/src/views/dialogs/RouteEditDialog.vue
  13. 104
      ruoyi-ui/src/views/dialogs/WaypointEditDialog.vue
  14. 15
      ruoyi-ui/src/views/selectRoom/index.vue
  15. 2
      ruoyi-ui/vue.config.js

65
ruoyi-system/src/main/java/com/ruoyi/system/domain/RouteWaypoints.java

@ -1,6 +1,7 @@
package com.ruoyi.system.domain;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@ -63,11 +64,8 @@ public class RouteWaypoints extends BaseEntity
@Excel(name = "盘旋参数")
private String holdParams;
/** 航点标签文字大小(px),用于地图显示 */
private Integer labelFontSize;
/** 航点标签文字颜色(如 #333333),用于地图显示 */
private String labelColor;
/** 航点显示样式 JSON:字号、文字颜色、标记大小与颜色等,对应表 display_style 列 */
private WaypointDisplayStyle displayStyle;
public void setId(Long id)
{
@ -185,20 +183,66 @@ public class RouteWaypoints extends BaseEntity
return holdParams;
}
@JsonIgnore
public WaypointDisplayStyle getDisplayStyle() {
return displayStyle;
}
@JsonIgnore
public void setDisplayStyle(WaypointDisplayStyle displayStyle) {
this.displayStyle = displayStyle;
}
private WaypointDisplayStyle getOrCreateDisplayStyle() {
if (displayStyle == null) {
displayStyle = new WaypointDisplayStyle();
}
return displayStyle;
}
/** API/前端:地图上航点名称字号,默认 16 */
public void setLabelFontSize(Integer labelFontSize) {
this.labelFontSize = labelFontSize;
getOrCreateDisplayStyle().setLabelFontSize(labelFontSize);
}
public Integer getLabelFontSize() {
return labelFontSize;
return displayStyle != null && displayStyle.getLabelFontSize() != null ? displayStyle.getLabelFontSize() : 16;
}
/** API/前端:地图上航点名称颜色,默认 #000000 */
public void setLabelColor(String labelColor) {
this.labelColor = labelColor;
getOrCreateDisplayStyle().setLabelColor(labelColor);
}
public String getLabelColor() {
return labelColor;
return displayStyle != null && displayStyle.getLabelColor() != null ? displayStyle.getLabelColor() : "#000000";
}
/** API/前端:航点圆点填充色,默认 #ffffff */
public void setColor(String color) {
getOrCreateDisplayStyle().setColor(color);
}
public String getColor() {
return displayStyle != null && displayStyle.getColor() != null ? displayStyle.getColor() : "#ffffff";
}
/** API/前端:航点圆点直径(像素),默认 12 */
public void setPixelSize(Integer pixelSize) {
getOrCreateDisplayStyle().setPixelSize(pixelSize);
}
public Integer getPixelSize() {
return displayStyle != null && displayStyle.getPixelSize() != null ? displayStyle.getPixelSize() : 12;
}
/** API/前端:航点圆点边框颜色,默认 #000000 */
public void setOutlineColor(String outlineColor) {
getOrCreateDisplayStyle().setOutlineColor(outlineColor);
}
public String getOutlineColor() {
return displayStyle != null && displayStyle.getOutlineColor() != null ? displayStyle.getOutlineColor() : "#000000";
}
@Override
@ -216,8 +260,7 @@ public class RouteWaypoints extends BaseEntity
.append("turnAngle", getTurnAngle())
.append("pointType", getPointType())
.append("holdParams", getHoldParams())
.append("labelFontSize", getLabelFontSize())
.append("labelColor", getLabelColor())
.append("displayStyle", displayStyle)
.toString();
}

62
ruoyi-system/src/main/java/com/ruoyi/system/domain/WaypointDisplayStyle.java

@ -0,0 +1,62 @@
package com.ruoyi.system.domain;
import java.io.Serializable;
/**
* 航点显示样式地图上名称字号/颜色标记大小/颜色对应表字段 display_style JSON 结构
*/
public class WaypointDisplayStyle implements Serializable {
private static final long serialVersionUID = 1L;
/** 航点名称在地图上的字号,默认 16 */
private Integer labelFontSize;
/** 航点名称在地图上的颜色,默认 #000000 */
private String labelColor;
/** 航点圆点填充色,默认 #ffffff */
private String color;
/** 航点圆点直径(像素),默认 12 */
private Integer pixelSize;
/** 航点圆点边框颜色,默认 #000000 */
private String outlineColor;
public Integer getLabelFontSize() {
return labelFontSize;
}
public void setLabelFontSize(Integer labelFontSize) {
this.labelFontSize = labelFontSize;
}
public String getLabelColor() {
return labelColor;
}
public void setLabelColor(String labelColor) {
this.labelColor = labelColor;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Integer getPixelSize() {
return pixelSize;
}
public void setPixelSize(Integer pixelSize) {
this.pixelSize = pixelSize;
}
public String getOutlineColor() {
return outlineColor;
}
public void setOutlineColor(String outlineColor) {
this.outlineColor = outlineColor;
}
}

59
ruoyi-system/src/main/java/com/ruoyi/system/typehandler/WaypointDisplayStyleTypeHandler.java

@ -0,0 +1,59 @@
package com.ruoyi.system.typehandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.system.domain.WaypointDisplayStyle;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* MyBatis 类型处理器display_style TEXT/JSON WaypointDisplayStyle 互转
*/
@MappedTypes(WaypointDisplayStyle.class)
public class WaypointDisplayStyleTypeHandler extends BaseTypeHandler<WaypointDisplayStyle> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, WaypointDisplayStyle parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, MAPPER.writeValueAsString(parameter));
} catch (Exception e) {
throw new SQLException("WaypointDisplayStyle serialize error", e);
}
}
@Override
public WaypointDisplayStyle getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return parse(json);
}
@Override
public WaypointDisplayStyle getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
return parse(json);
}
@Override
public WaypointDisplayStyle getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
return parse(json);
}
private static WaypointDisplayStyle parse(String json) {
if (json == null || json.trim().isEmpty()) {
return null;
}
try {
return MAPPER.readValue(json, WaypointDisplayStyle.class);
} catch (Exception e) {
return null;
}
}
}

14
ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml

@ -17,12 +17,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="turnAngle" column="turn_angle" />
<result property="pointType" column="point_type" />
<result property="holdParams" column="hold_params" />
<result property="labelFontSize" column="label_font_size" />
<result property="labelColor" column="label_color" />
<result property="displayStyle" column="display_style" typeHandler="com.ruoyi.system.typehandler.WaypointDisplayStyleTypeHandler" />
</resultMap>
<sql id="selectRouteWaypointsVo">
select id, route_id, name, seq, lat, lng, alt, speed, start_time, turn_angle, point_type, hold_params, label_font_size, label_color from route_waypoints
select id, route_id, name, seq, lat, lng, alt, speed, start_time, turn_angle, point_type, hold_params, display_style from route_waypoints
</sql>
<select id="selectRouteWaypointsList" parameterType="RouteWaypoints" resultMap="RouteWaypointsResult">
@ -66,8 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="turnAngle != null">turn_angle,</if>
<if test="pointType != null and pointType != ''">point_type,</if>
<if test="holdParams != null">hold_params,</if>
<if test="labelFontSize != null">label_font_size,</if>
<if test="labelColor != null">label_color,</if>
<if test="displayStyle != null">display_style,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="routeId != null">#{routeId},</if>
@ -81,8 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="turnAngle != null">#{turnAngle},</if>
<if test="pointType != null and pointType != ''">#{pointType},</if>
<if test="holdParams != null">#{holdParams},</if>
<if test="labelFontSize != null">#{labelFontSize},</if>
<if test="labelColor != null">#{labelColor},</if>
<if test="displayStyle != null">#{displayStyle, typeHandler=com.ruoyi.system.typehandler.WaypointDisplayStyleTypeHandler},</if>
</trim>
</insert>
@ -100,8 +97,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="turnAngle != null">turn_angle = #{turnAngle},</if>
<if test="pointType != null">point_type = #{pointType},</if>
<if test="holdParams != null">hold_params = #{holdParams},</if>
<if test="labelFontSize != null">label_font_size = #{labelFontSize},</if>
<if test="labelColor != null">label_color = #{labelColor},</if>
<if test="displayStyle != null">display_style = #{displayStyle, typeHandler=com.ruoyi.system.typehandler.WaypointDisplayStyleTypeHandler},</if>
</trim>
where id = #{id}
</update>

27
ruoyi-system/src/main/resources/mapper/system/route_waypoints_display_style_json.sql

@ -0,0 +1,27 @@
-- 将航点显示相关字段合并为单列 JSON:display_style
-- 执行前请备份。若表带 schema(如 ry.route_waypoints)请自行替换表名。
-- 1. 新增 JSON 列
ALTER TABLE route_waypoints
ADD COLUMN display_style JSON DEFAULT NULL COMMENT '航点显示样式JSON: labelFontSize,labelColor,color,pixelSize,outlineColor';
-- 2. 从旧列回填(仅存在 label_font_size / label_color 时)
UPDATE route_waypoints
SET display_style = JSON_OBJECT(
'labelFontSize', COALESCE(label_font_size, 16),
'labelColor', COALESCE(label_color, '#000000'),
'color', '#ffffff',
'pixelSize', 12,
'outlineColor', '#000000'
)
WHERE display_style IS NULL;
-- 3. 删除旧列(若你曾加过 color/pixel_size/outline_color 三列,也一并删除)
ALTER TABLE route_waypoints
DROP COLUMN label_font_size,
DROP COLUMN label_color;
-- 若存在以下列则逐条执行(没有则跳过):
-- ALTER TABLE route_waypoints DROP COLUMN color;
-- ALTER TABLE route_waypoints DROP COLUMN pixel_size;
-- ALTER TABLE route_waypoints DROP COLUMN outline_color;

2
ruoyi-ui/.env.development

@ -8,7 +8,7 @@ ENV = 'development'
VUE_APP_BASE_API = '/dev-api'
# 访问地址(绕过 /dev-api 代理,用于解决静态资源/图片访问 401 认证问题)
VUE_APP_BACKEND_URL = 'http://192.168.1.107:8080'
VUE_APP_BACKEND_URL = 'http://127.0.0.1:8080'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true

8
ruoyi-ui/src/layout/components/TagsView/index.vue

@ -223,7 +223,13 @@ export default {
this.left = left
}
this.top = e.clientY
const padding = 12
const menuHeight = 250
const winH = window.innerHeight
let top = e.clientY
if (top + menuHeight + padding > winH) top = winH - menuHeight - padding
if (top < padding) top = padding
this.top = top
this.visible = true
this.selectedTag = tag
},

19
ruoyi-ui/src/views/cesiumMap/ContextMenu.vue

@ -445,10 +445,25 @@ export default {
}
},
computed: {
/** 根据视口边界修正菜单位置,避免菜单在屏幕底部或右侧被截断 */
adjustedPosition() {
const padding = 12
const menuMaxWidth = 220
const menuMaxHeight = 560
const winW = typeof window !== 'undefined' ? window.innerWidth : 1920
const winH = typeof window !== 'undefined' ? window.innerHeight : 1080
let x = this.position.x
let y = this.position.y
if (x + menuMaxWidth + padding > winW) x = winW - menuMaxWidth - padding
if (x < padding) x = padding
if (y + menuMaxHeight + padding > winH) y = winH - menuMaxHeight - padding
if (y < padding) y = padding
return { x, y }
},
positionStyle() {
return {
left: this.position.x + 'px',
top: this.position.y + 'px'
left: this.adjustedPosition.x + 'px',
top: this.adjustedPosition.y + 'px'
}
},
isRouteLocked() {

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

@ -381,6 +381,11 @@ export default {
roomId: {
type: [String, Number],
default: null
},
/** 是否允许地图拖动(由顶部“小手”图标切换,默认关闭) */
mapDragEnabled: {
type: Boolean,
default: false
}
},
@ -611,6 +616,13 @@ export default {
this.updateCoordinatesDisplay()
}
},
mapDragEnabled: {
handler(enabled) {
if (this.viewer && this.viewer.scene && this.viewer.scene.screenSpaceCameraController) {
this.viewer.scene.screenSpaceCameraController.enableTranslate = !!enabled
}
}
},
routeLabelVisible: {
handler(newVal) {
// 使 debounce setTimeout
@ -988,17 +1000,17 @@ export default {
},
point: {
pixelSize: 10,
color: Cesium.Color.WHITE,
outlineColor: Cesium.Color.fromCssColorString('#0078FF'),
outlineWidth: 3,
color: Cesium.Color.fromCssColorString('#f1f5f9'),
outlineColor: Cesium.Color.fromCssColorString('#64748b'),
outlineWidth: 1.5,
disableDepthTestDistance: Number.POSITIVE_INFINITY
},
label: {
text: `WP${wpIndex}`,
font: '14px Microsoft YaHei',
pixelOffset: new Cesium.Cartesian2(0, -20),
fillColor: Cesium.Color.fromCssColorString('#333333'),
outlineColor: Cesium.Color.BLACK,
fillColor: Cesium.Color.fromCssColorString('#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 1,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -1015,7 +1027,7 @@ export default {
polyline: {
positions: solidPositions,
width: 3,
material: Cesium.Color.fromCssColorString('#2E5C3E'),
material: Cesium.Color.fromCssColorString('#64748b'),
arcType: Cesium.ArcType.NONE
}
});
@ -1037,7 +1049,7 @@ export default {
}, false),
width: 3,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString('#2E5C3E'),
color: Cesium.Color.fromCssColorString('#64748b'),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
@ -1137,17 +1149,17 @@ export default {
properties: { isMissionWaypoint: true, originalIndex: 1, temp: true },
point: {
pixelSize: 10,
color: Cesium.Color.WHITE,
outlineColor: Cesium.Color.fromCssColorString('#0078FF'),
outlineWidth: 3,
color: Cesium.Color.fromCssColorString('#f1f5f9'),
outlineColor: Cesium.Color.fromCssColorString('#64748b'),
outlineWidth: 1.5,
disableDepthTestDistance: Number.POSITIVE_INFINITY
},
label: {
text: this.platformRouteDrawing.platformName,
font: '14px Microsoft YaHei',
pixelOffset: new Cesium.Cartesian2(0, -20),
fillColor: Cesium.Color.fromCssColorString('#333333'),
outlineColor: Cesium.Color.BLACK,
fillColor: Cesium.Color.fromCssColorString('#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 1,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -1179,7 +1191,7 @@ export default {
}, false),
width: 3,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString('#2E5C3E'),
color: Cesium.Color.fromCssColorString('#64748b'),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
@ -1204,17 +1216,17 @@ export default {
properties: { isMissionWaypoint: true, originalIndex: wpIndex, temp: true },
point: {
pixelSize: 10,
color: Cesium.Color.WHITE,
outlineColor: Cesium.Color.fromCssColorString('#0078FF'),
outlineWidth: 3,
color: Cesium.Color.fromCssColorString('#f1f5f9'),
outlineColor: Cesium.Color.fromCssColorString('#64748b'),
outlineWidth: 1.5,
disableDepthTestDistance: Number.POSITIVE_INFINITY
},
label: {
text: `WP${wpIndex}`,
font: '14px Microsoft YaHei',
pixelOffset: new Cesium.Cartesian2(0, -20),
fillColor: Cesium.Color.fromCssColorString('#333333'),
outlineColor: Cesium.Color.BLACK,
fillColor: Cesium.Color.fromCssColorString('#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 1,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -1239,7 +1251,7 @@ export default {
polyline: {
positions: solidPositions,
width: 3,
material: Cesium.Color.fromCssColorString('#2E5C3E'),
material: Cesium.Color.fromCssColorString('#64748b'),
arcType: Cesium.ArcType.NONE
}
});
@ -1253,7 +1265,7 @@ export default {
}, false),
width: 3,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString('#2E5C3E'),
color: Cesium.Color.fromCssColorString('#64748b'),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
@ -1295,17 +1307,17 @@ export default {
properties: { isMissionWaypoint: true, temp: true },
point: {
pixelSize: 10,
color: Cesium.Color.WHITE,
outlineColor: Cesium.Color.fromCssColorString('#0078FF'),
outlineWidth: 3,
color: Cesium.Color.fromCssColorString('#f1f5f9'),
outlineColor: Cesium.Color.fromCssColorString('#64748b'),
outlineWidth: 1.5,
disableDepthTestDistance: Number.POSITIVE_INFINITY
},
label: {
text: pr.platformName,
font: '14px Microsoft YaHei',
pixelOffset: new Cesium.Cartesian2(0, -20),
fillColor: Cesium.Color.fromCssColorString('#333333'),
outlineColor: Cesium.Color.BLACK,
fillColor: Cesium.Color.fromCssColorString('#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 1,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -1405,7 +1417,7 @@ export default {
polyline: {
positions: solidPositions,
width: 3,
material: Cesium.Color.fromCssColorString('#2E5C3E'),
material: Cesium.Color.fromCssColorString('#64748b'),
arcType: Cesium.ArcType.NONE
}
});
@ -1950,12 +1962,12 @@ export default {
renderRouteWaypoints(waypoints, routeId = 'default', platformId, platform, style) {
if (!waypoints || waypoints.length < 1) return;
this.waypointDragPreview = null;
// 线
// 线线 + id 便
const lineId = `route-line-${routeId}`;
const existingLine = this.viewer.entities.getById(lineId);
if (existingLine) {
this.viewer.entities.remove(existingLine);
}
[lineId, lineId + '-hit', lineId + '-soft'].forEach((id) => {
const existing = this.viewer.entities.getById(id);
if (existing) this.viewer.entities.remove(existing);
});
// 线
const platformBillboardId = `route-platform-${routeId}`;
const platformLabelId = `route-platform-label-${routeId}`;
@ -1992,14 +2004,14 @@ export default {
});
const wpStyle = (style && style.waypoint) ? style.waypoint : {};
const lineStyle = (style && style.line) ? style.line : {};
const pixelSize = wpStyle.pixelSize != null ? wpStyle.pixelSize : 7;
const wpColor = wpStyle.color || '#ffffff';
const wpOutline = wpStyle.outlineColor || '#0078FF';
const wpOutlineW = wpStyle.outlineWidth != null ? wpStyle.outlineWidth : 2;
// 线绿线 3线线线
const defaultPixelSize = wpStyle.pixelSize != null ? wpStyle.pixelSize : 10;
const defaultWpColor = wpStyle.color || '#f1f5f9';
const defaultWpOutline = wpStyle.outlineColor || '#64748b';
const wpOutlineW = wpStyle.outlineWidth != null ? wpStyle.outlineWidth : 1.5;
// 线线 3线线线
const lineWidth = lineStyle.width != null ? lineStyle.width : 3;
const lineColor = lineStyle.color || '#2E5C3E';
const gapColor = lineStyle.gapColor != null ? lineStyle.gapColor : '#000000';
const lineColor = lineStyle.color || '#64748b';
const gapColor = lineStyle.gapColor != null ? lineStyle.gapColor : '#cbd5e1';
const dashLen = lineStyle.dashLength != null ? lineStyle.dashLength : 20;
const useDash = (lineStyle.style || 'solid') === 'dash';
const lineMaterial = useDash
@ -2041,9 +2053,12 @@ export default {
}
return !!nextLogical;
};
// + entry/exit 线
// + entry/exit 线 pixelSize/color/outlineColor
waypoints.forEach((wp, index) => {
const pos = originalPositions[index];
const pixelSize = wp.pixelSize != null ? Number(wp.pixelSize) : defaultPixelSize;
const wpColor = wp.color || defaultWpColor;
const wpOutline = wp.outlineColor != null ? wp.outlineColor : defaultWpOutline;
if (this.isHoldWaypoint(wp)) {
this.viewer.entities.add({
id: `wp_${routeId}_${wp.id}`,
@ -2065,8 +2080,8 @@ export default {
text: wp.name || `盘旋${index + 1}`,
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`,
pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)),
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#2c2c2c'),
outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'),
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 0.5,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -2095,8 +2110,8 @@ export default {
text: wp.name || `WP${index + 1}`,
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`,
pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)),
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#2c2c2c'),
outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'),
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#334155'),
outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'),
outlineWidth: 0.5,
style: Cesium.LabelStyle.FILL_AND_OUTLINE
}
@ -2453,6 +2468,9 @@ export default {
properties: { routeId: routeId }
});
const wpName = wp.name || `WP${i + 1}`;
const arcPixelSize = wp.pixelSize != null ? Number(wp.pixelSize) : defaultPixelSize;
const arcWpColor = wp.color || defaultWpColor;
const arcWpOutline = wp.outlineColor != null ? wp.outlineColor : defaultWpOutline;
[0, 1].forEach((idx) => {
const suffix = idx === 0 ? '_entry' : '_exit';
const getPos = () => { const pts = getArcPoints(); return idx === 0 ? pts[0] : pts[pts.length - 1]; };
@ -2461,8 +2479,8 @@ export default {
name: wpName,
position: new Cesium.CallbackProperty(getPos, false),
properties: { isMissionWaypoint: true, routeId: routeId, dbId: wp.id },
point: { pixelSize: pixelSize, color: Cesium.Color.fromCssColorString(wpColor), outlineColor: Cesium.Color.fromCssColorString(wpOutline), outlineWidth: wpOutlineW, disableDepthTestDistance: Number.POSITIVE_INFINITY },
label: { text: wpName, font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`, pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)), fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#2c2c2c'), outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'), outlineWidth: 0.5, style: Cesium.LabelStyle.FILL_AND_OUTLINE }
point: { pixelSize: arcPixelSize, color: Cesium.Color.fromCssColorString(arcWpColor), outlineColor: Cesium.Color.fromCssColorString(arcWpOutline), outlineWidth: wpOutlineW, disableDepthTestDistance: Number.POSITIVE_INFINITY },
label: { text: wpName, font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`, pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, arcPixelSize + 8)), fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#334155'), outlineColor: Cesium.Color.fromCssColorString('#e2e8f0'), outlineWidth: 0.5, style: Cesium.LabelStyle.FILL_AND_OUTLINE }
});
});
const arcPoints = getArcPoints();
@ -2478,21 +2496,40 @@ export default {
}
}
}
// 线 CallbackProperty 线
// 便线线
const that = this;
const hitLineId = lineId + '-hit';
const routePositionsCallback = new Cesium.CallbackProperty(function () {
return that.getRouteLinePositionsFromWaypointEntities(routeId) || finalPathPositions;
}, false);
const hitEntity = this.viewer.entities.add({
id: hitLineId,
polyline: {
positions: routePositionsCallback,
width: 48,
material: Cesium.Color.TRANSPARENT,
arcType: Cesium.ArcType.NONE,
zIndex: 0
},
properties: { isMissionRouteLine: true, routeId: routeId }
});
if (this.allEntities) {
this.allEntities.push({ id: hitLineId, entity: hitEntity, type: 'route', routeId });
}
const displayWidth = Math.max(lineWidth, 5);
// 线
const routeEntity = this.viewer.entities.add({
id: lineId,
polyline: {
positions: new Cesium.CallbackProperty(function () {
return that.getRouteLinePositionsFromWaypointEntities(routeId) || finalPathPositions;
}, false),
width: lineWidth,
positions: routePositionsCallback,
width: displayWidth,
material: lineMaterial,
arcType: Cesium.ArcType.NONE,
zIndex: 1
},
properties: {isMissionRouteLine: true, routeId: routeId}
properties: { isMissionRouteLine: true, routeId: routeId }
});
routeEntity.isPickable = false;
if (this.allEntities) {
this.allEntities.push({ id: lineId, entity: routeEntity, type: 'route', routeId });
}
@ -2535,6 +2572,27 @@ export default {
return positions.length > 0 ? positions : null;
},
/** 在屏幕坐标附近做半径拾取,若命中航线则返回 routeId,用于左键点击“附近”也能打开航线弹窗 */
pickRouteNearScreenPosition(screenPosition, radiusPixels) {
if (!this.viewer || !this.viewer.scene) return null;
const x = screenPosition.x;
const y = screenPosition.y;
const step = Math.max(4, Math.floor(radiusPixels / 4));
for (let dy = -radiusPixels; dy <= radiusPixels; dy += step) {
for (let dx = -radiusPixels; dx <= radiusPixels; dx += step) {
if (dx * dx + dy * dy > radiusPixels * radiusPixels) continue;
const picked = this.viewer.scene.pick(new Cesium.Cartesian2(x + dx, y + dy));
if (!Cesium.defined(picked) || !picked.id) continue;
const props = picked.id.properties ? picked.id.properties.getValue(Cesium.JulianDate.now()) : null;
if (props && props.isMissionRouteLine && props.routeId != null) {
const rid = props.routeId.getValue ? props.routeId.getValue() : props.routeId;
return rid;
}
}
}
return null;
},
/** 渲染/路径用:非首尾航点默认转弯坡度 45°,首尾为 0 */
getEffectiveTurnAngle(wp, index, waypointsLength) {
if (index === 0 || index === waypointsLength - 1) return wp.turnAngle != null ? wp.turnAngle : 0;
@ -3867,23 +3925,24 @@ export default {
}
const pickedObject = this.viewer.scene.pick(click.position);
let isWaypoint = false;
let isRouteLine = false;
let isPlatformIcon = false;
let props = null;
let entity = null;
if (Cesium.defined(pickedObject) && pickedObject.id) {
const entity = pickedObject.id;
entity = pickedObject.id;
const idStr = (entity && entity.id) ? entity.id : '';
if (idStr && (idStr.endsWith('-rotate-handle') || idStr.indexOf('-scale-') !== -1)) return;
const platformIconData = this.allEntities.find(e => e.type === 'platformIcon' && e.entity === entity);
// 仿
const now = Cesium.JulianDate.now();
const props = entity.properties ? entity.properties.getValue(now) : null;
const isWaypoint = props && props.isMissionWaypoint;
const isRouteLine = props && props.isMissionRouteLine;
const isPlatformIcon = !!platformIconData;
// 线
if (!isWaypoint && !isRouteLine && !isPlatformIcon) return;
props = entity.properties ? entity.properties.getValue(now) : null;
isWaypoint = props && props.isMissionWaypoint;
isRouteLine = props && props.isMissionRouteLine;
isPlatformIcon = !!platformIconData;
}
if (isWaypoint || isRouteLine || isPlatformIcon) {
const nowMs = Date.now();
//
if (nowMs - this.lastEntityClickTime < 300) {
if (this.entityClickDebounceTimer) {
clearTimeout(this.entityClickDebounceTimer);
@ -3894,7 +3953,6 @@ export default {
}
this.lastEntityClickTime = nowMs;
this.saveCameraStateBeforeEntityClick();
if (this.entityClickDebounceTimer) clearTimeout(this.entityClickDebounceTimer);
const dbId = isWaypoint ? props.dbId : null;
const routeId = isWaypoint ? props.routeId : (isRouteLine ? props.routeId : null);
@ -3902,7 +3960,6 @@ export default {
this.entityClickDebounceTimer = setTimeout(() => {
this.entityClickDebounceTimer = null;
this.lastEntityClickTime = 0;
// 线
if (this.routeLocked[routeId]) {
this.$message && this.$message.info('该航线已上锁,请右键选择“解锁”后再编辑');
return;
@ -3910,12 +3967,31 @@ export default {
if (isWaypoint) {
console.log(`>>> [地图触发] 点击了点 ${dbId}, 属于航线 ${routeId}`);
this.$emit('open-waypoint-dialog', dbId, routeId);
} else if (isRouteLine) {
} else {
console.log(`>>> [地图触发] 点击了航线 ${routeId}`);
this.$emit('open-route-dialog', routeId);
}
}, 250);
}
return;
}
// /线/便线
const routeIdFromRadius = this.pickRouteNearScreenPosition(click.position, 32);
if (routeIdFromRadius != null) {
const nowMs = Date.now();
if (nowMs - this.lastEntityClickTime < 300) return;
this.lastEntityClickTime = nowMs;
this.saveCameraStateBeforeEntityClick();
if (this.entityClickDebounceTimer) clearTimeout(this.entityClickDebounceTimer);
this.entityClickDebounceTimer = setTimeout(() => {
this.entityClickDebounceTimer = null;
this.lastEntityClickTime = 0;
if (this.routeLocked[routeIdFromRadius]) {
this.$message && this.$message.info('该航线已上锁,请右键选择“解锁”后再编辑');
return;
}
this.$emit('open-route-dialog', routeIdFromRadius);
}, 250);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
@ -4077,8 +4153,26 @@ export default {
}
},
initRightClickHandler() {
const canvas = this.viewer.scene.canvas
//
const container = this.$el
if (!container) return
this._contextMenuPreventContainer = container
this._boundPreventContextMenu = (e) => {
e.preventDefault()
e.stopPropagation()
}
container.addEventListener('contextmenu', this._boundPreventContextMenu, true)
// window
this._boundPreventContextMenuWindow = (e) => {
if (container.contains(e.target)) {
e.preventDefault()
e.stopPropagation()
}
}
window.addEventListener('contextmenu', this._boundPreventContextMenuWindow, true)
//
this.rightClickHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
this.rightClickHandler = new Cesium.ScreenSpaceEventHandler(canvas)
//
this.rightClickHandler.setInputAction((click) => {
//
@ -4599,6 +4693,8 @@ export default {
controller.enableTilt = false
controller.enableRotate = false
controller.enableLook = false
// mapDragEnabled true
controller.enableTranslate = !!this.mapDragEnabled
scene.screenSpaceCameraController.maximumPitch = 0
scene.screenSpaceCameraController.minimumPitch = 0
//
@ -6776,7 +6872,7 @@ export default {
positions: positions,
width: 3,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString('#2E5C3E'),
color: Cesium.Color.fromCssColorString('#64748b'),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
@ -9505,6 +9601,16 @@ export default {
this.rightClickHandler.destroy()
this.rightClickHandler = null
}
if (this._boundPreventContextMenuWindow) {
window.removeEventListener('contextmenu', this._boundPreventContextMenuWindow, true)
this._boundPreventContextMenuWindow = null
}
const preventContainer = this._contextMenuPreventContainer || (this.$el || (this.viewer && this.viewer.scene && this.viewer.scene.canvas && this.viewer.scene.canvas.parentElement))
if (preventContainer && this._boundPreventContextMenu) {
preventContainer.removeEventListener('contextmenu', this._boundPreventContextMenu, true)
this._contextMenuPreventContainer = null
this._boundPreventContextMenu = null
}
if (this.mouseCoordinatesHandler) {
this.mouseCoordinatesHandler.destroy()

47
ruoyi-ui/src/views/childRoom/TopHeader.vue

@ -216,6 +216,22 @@
</div>
<div class="header-right">
<!-- 地图拖动开关点击小手图标后才允许拖动地图 -->
<div
class="map-drag-toggle"
:class="{ active: mapDragEnabled }"
:title="mapDragEnabled ? '已开启拖动,点击可关闭' : '点击开启地图拖动'"
@click="$emit('toggle-map-drag')"
>
<svg class="hand-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="20" height="20">
<path d="M18 11.5V9a2 2 0 0 0-2-2 2 2 0 0 0-2 2v1.4" />
<path d="M14 10V8a2 2 0 0 0-2-2 2 2 0 0 0-2 2v2" />
<path d="M10 9.9V9a2 2 0 0 0-2-2 2 2 0 0 0-2 2v5" />
<path d="M6 14a2 2 0 0 0-2-2 2 2 0 0 0-2 2" />
<path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-4a8 8 0 0 1-8-8 2 2 0 1 1 4 0" />
</svg>
<span class="map-drag-label">{{ mapDragEnabled ? '拖动开' : '拖动' }}</span>
</div>
<!-- 作战信息区域 -->
<div class="combat-info-group">
<div
@ -350,6 +366,11 @@ export default {
scaleDenominator: 1000,
unit: 'm'
})
},
/** 是否允许地图拖动(与小手图标联动) */
mapDragEnabled: {
type: Boolean,
default: false
}
},
data() {
@ -877,6 +898,32 @@ export default {
gap: 20px;
}
.map-drag-toggle {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
color: #909399;
transition: color 0.2s, background 0.2s;
}
.map-drag-toggle:hover {
color: #409eff;
background: rgba(64, 158, 255, 0.08);
}
.map-drag-toggle.active {
color: #409eff;
background: rgba(64, 158, 255, 0.12);
}
.map-drag-toggle .hand-icon {
flex-shrink: 0;
}
.map-drag-toggle .map-drag-label {
font-size: 13px;
white-space: nowrap;
}
.combat-info-group {
display: flex;
align-items: center;

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

@ -14,6 +14,7 @@
:scaleConfig="scaleConfig"
:coordinateFormat="coordinateFormat"
:bottomPanelVisible="bottomPanelVisible"
:map-drag-enabled="mapDragEnabled"
:route-locked="routeLocked"
:route-locked-by-other-ids="routeLockedByOtherRouteIds"
:deduction-time-minutes="deductionMinutesFromK"
@ -98,7 +99,9 @@
:user-avatar="userAvatar"
:is-icon-edit-mode="isIconEditMode"
:current-scale-config="scaleConfig"
:map-drag-enabled="mapDragEnabled"
@select-nav="selectTopNav"
@toggle-map-drag="mapDragEnabled = !mapDragEnabled"
@set-k-time="openKTimeSetDialog"
@save-plan="savePlan"
@import-plan-file="importPlanFile"
@ -470,6 +473,8 @@ export default {
return {
drawDom:false,
airspaceDrawDom:false,
/** 是否允许地图拖动(由顶部小手图标切换,默认关闭) */
mapDragEnabled: false,
// 线
showOnlineMembers: false,
//
@ -1679,11 +1684,11 @@ export default {
//
this.showPlatformDialog = false;
},
/** 新建航线时写入数据库的默认样式(与地图默认显示一致:墨绿色实线线宽3) */
/** 新建航线时写入数据库的默认样式(简约风格:灰蓝配色、细描边) */
getDefaultRouteAttributes() {
const defaultAttrs = {
waypointStyle: { pixelSize: 7, color: '#ffffff', outlineColor: '#0078FF', outlineWidth: 2 },
lineStyle: { style: 'solid', width: 3, color: '#2E5C3E', gapColor: '#000000', dashLength: 20 }
waypointStyle: { pixelSize: 10, color: '#f1f5f9', outlineColor: '#64748b', outlineWidth: 1.5 },
lineStyle: { style: 'solid', width: 3, color: '#64748b', gapColor: '#cbd5e1', dashLength: 20 }
};
return JSON.stringify(defaultAttrs);
},
@ -1767,6 +1772,9 @@ export default {
if (wp.holdParams != null) payload.holdParams = wp.holdParams;
if (wp.labelFontSize != null) payload.labelFontSize = wp.labelFontSize;
if (wp.labelColor != null) payload.labelColor = wp.labelColor;
if (wp.pixelSize != null) payload.pixelSize = wp.pixelSize;
if (wp.color != null) payload.color = wp.color;
if (wp.outlineColor != null) payload.outlineColor = wp.outlineColor;
if (payload.turnAngle > 0 && this.$refs.cesiumMap) {
payload.turnRadius = this.$refs.cesiumMap.getWaypointRadius(payload);
} else {
@ -2141,6 +2149,9 @@ export default {
if (updatedWaypoint.holdParams != null) payload.holdParams = updatedWaypoint.holdParams;
if (updatedWaypoint.labelFontSize != null) payload.labelFontSize = updatedWaypoint.labelFontSize;
if (updatedWaypoint.labelColor != null) payload.labelColor = updatedWaypoint.labelColor;
if (updatedWaypoint.pixelSize != null) payload.pixelSize = updatedWaypoint.pixelSize;
if (updatedWaypoint.color != null) payload.color = updatedWaypoint.color;
if (updatedWaypoint.outlineColor != null) payload.outlineColor = updatedWaypoint.outlineColor;
const response = await updateWaypoints(payload);
if (response.code === 200) {
const roomId = this.currentRoomId;

67
ruoyi-ui/src/views/dialogs/RouteEditDialog.vue

@ -262,7 +262,7 @@
<el-input v-else v-model.number="scope.row.labelFontSize" size="mini" placeholder="14" />
</template>
</el-table-column>
<el-table-column label="颜色" width="100">
<el-table-column label="文字颜色" width="100">
<template slot-scope="scope">
<span v-if="!waypointsEditMode" class="color-text">{{ scope.row.labelColor || '' }}</span>
<div v-else class="table-color-wrap">
@ -271,6 +271,30 @@
</div>
</template>
</el-table-column>
<el-table-column label="标记大小" width="80">
<template slot-scope="scope">
<span v-if="!waypointsEditMode">{{ formatNum(scope.row.pixelSize) }}</span>
<el-input v-else v-model.number="scope.row.pixelSize" size="mini" placeholder="12" :min="4" :max="24" />
</template>
</el-table-column>
<el-table-column label="标记颜色" width="100">
<template slot-scope="scope">
<span v-if="!waypointsEditMode" class="color-text">{{ scope.row.color || '' }}</span>
<div v-else class="table-color-wrap">
<el-color-picker v-model="scope.row.color" size="mini" :predefine="presetColors" />
<span class="color-value">{{ scope.row.color }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="边框颜色" width="100">
<template slot-scope="scope">
<span v-if="!waypointsEditMode" class="color-text">{{ scope.row.outlineColor || '' }}</span>
<div v-else class="table-color-wrap">
<el-color-picker v-model="scope.row.outlineColor" size="mini" :predefine="presetColors" />
<span class="color-value">{{ scope.row.outlineColor }}</span>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
@ -323,22 +347,23 @@ export default {
selectedPlatform: null,
waypointsTableData: [],
waypointsEditMode: false,
skipBasicStyleSyncOnce: true,
form: {
id: '',
name: ''
},
// 线线线3 attributes 退
// 线 attributes 退
defaultStyle: {
waypoint: { pixelSize: 7, color: '#ffffff', outlineColor: '#0078FF', outlineWidth: 2 },
line: { style: 'solid', width: 3, color: '#800080', gapColor: '#000000', dashLength: 20 }
waypoint: { pixelSize: 10, color: '#f1f5f9', outlineColor: '#64748b', outlineWidth: 1.5 },
line: { style: 'solid', width: 3, color: '#64748b', gapColor: '#cbd5e1', dashLength: 20 }
},
styleForm: {
waypoint: { pixelSize: 7, color: '#ffffff', outlineColor: '#0078FF', outlineWidth: 2 },
line: { style: 'solid', width: 3, color: '#800080', gapColor: '#000000', dashLength: 20 }
waypoint: { pixelSize: 10, color: '#f1f5f9', outlineColor: '#64748b', outlineWidth: 1.5 },
line: { style: 'solid', width: 3, color: '#64748b', gapColor: '#cbd5e1', dashLength: 20 }
},
presetColors: [
'#ffffff', '#000000', '#0078FF', '#409EFF', '#67C23A', '#E6A23C', '#F56C6C',
'#909399', '#303133', '#00CED1', '#FF1493', '#FFD700', '#4B0082', '#00FF00', '#FF4500'
'#f1f5f9', '#64748b', '#334155', '#94a3b8', '#e2e8f0', '#0078FF', '#409EFF', '#67C23A',
'#E6A23C', '#F56C6C', '#909399', '#00CED1', '#FF1493', '#FFD700', '#4B0082', '#FF4500'
]
}
},
@ -382,8 +407,24 @@ export default {
if (val) {
this.loadPosition()
if (this.activeTab === 'platform') this.loadPlatforms()
this.skipBasicStyleSyncOnce = true
}
},
'styleForm.waypoint': {
handler(wpStyle) {
if (!wpStyle || !this.waypointsTableData.length) return
if (this.skipBasicStyleSyncOnce) {
this.skipBasicStyleSyncOnce = false
return
}
this.waypointsTableData.forEach(row => {
row.color = wpStyle.color || '#f1f5f9'
row.pixelSize = wpStyle.pixelSize != null ? wpStyle.pixelSize : 10
row.outlineColor = wpStyle.outlineColor || '#64748b'
})
},
deep: true
},
activeTab(val) {
if (val === 'platform' && this.visible) this.loadPlatforms()
if (val === 'waypoints' && this.panelWidth < 920) this.panelWidth = 920
@ -593,7 +634,10 @@ export default {
...wp,
minutesFromK: this.startTimeToMinutes(wp.startTime),
labelFontSize: wp.labelFontSize != null ? Number(wp.labelFontSize) : 14,
labelColor: wp.labelColor || '#333333'
labelColor: wp.labelColor || '#333333',
pixelSize: wp.pixelSize != null ? Number(wp.pixelSize) : 10,
color: wp.color || '#f1f5f9',
outlineColor: wp.outlineColor || '#64748b'
}))
this.waypointsEditMode = false
},
@ -626,7 +670,10 @@ export default {
pointType: row.pointType || null,
holdParams: row.holdParams || null,
labelFontSize: row.labelFontSize != null ? row.labelFontSize : 14,
labelColor: row.labelColor || '#333333'
labelColor: row.labelColor || '#333333',
color: row.color != null && row.color !== '' ? row.color : '#f1f5f9',
pixelSize: row.pixelSize != null ? row.pixelSize : 10,
outlineColor: row.outlineColor != null && row.outlineColor !== '' ? row.outlineColor : '#64748b'
}))
},
handleSave() {

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

@ -9,7 +9,7 @@
</div>
<div class="dialog-body">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="100px" size="small">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="130px" size="small" class="waypoint-form">
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入航点名称"></el-input>
</el-form-item>
@ -21,13 +21,39 @@
:max="28"
controls-position="right"
placeholder="字号(px)"
style="width: 100%;"
class="full-width-input"
/>
<div class="form-tip">航点名称在地图上的显示字号微软雅黑字体</div>
</el-form-item>
<el-form-item label="地图文字颜色" prop="labelColor">
<el-color-picker v-model="formData.labelColor" size="small" />
<span class="color-value">{{ formData.labelColor }}</span>
<!-- 颜色前两项并排利用横向空间第三项单独一行 -->
<el-row :gutter="20" class="color-form-row">
<el-col :span="12">
<el-form-item label="地图文字颜色" prop="labelColor" class="color-form-item-inline">
<el-color-picker v-model="formData.labelColor" size="small" />
<span class="color-value">{{ formData.labelColor }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="航点标记颜色" prop="color" class="color-form-item-inline">
<el-color-picker v-model="formData.color" size="small" />
<span class="color-value">{{ formData.color }}</span>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="航点标记边框颜色" prop="outlineColor">
<el-color-picker v-model="formData.outlineColor" size="small" />
<span class="color-value">{{ formData.outlineColor }}</span>
</el-form-item>
<el-form-item label="航点标记大小" prop="pixelSize">
<el-input-number
v-model="formData.pixelSize"
:min="4"
:max="24"
controls-position="right"
placeholder="像素"
class="full-width-input"
/>
</el-form-item>
<el-form-item label="高度" prop="alt">
@ -36,7 +62,7 @@
:min="0"
controls-position="right"
placeholder="请输入高度"
style="width: 100%;"
class="full-width-input"
></el-input-number>
</el-form-item>
@ -46,7 +72,7 @@
:min="0"
controls-position="right"
placeholder="请输入速度"
style="width: 100%;"
class="full-width-input"
></el-input-number>
</el-form-item>
@ -55,10 +81,10 @@
v-model="formData.turnAngle"
controls-position="right"
placeholder="请输入转弯坡度"
style="width: 100%;"
class="full-width-input"
:disabled="formData.isBankDisabled"
></el-input-number>
<div v-if="formData.isBankDisabled" style="color: #909399; font-size: 12px; margin-top: 4px;">
<div v-if="formData.isBankDisabled" class="form-tip form-warn">
首尾航点坡度已锁定为 0不可编辑
</div>
</el-form-item>
@ -70,17 +96,17 @@
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.pointType === 'hold_circle'" label="半径(米)">
<el-input-number v-model="formData.holdRadius" :min="100" :max="50000" style="width:100%" />
<el-input-number v-model="formData.holdRadius" :min="100" :max="50000" class="full-width-input" />
</el-form-item>
<template v-if="formData.pointType === 'hold_ellipse'">
<el-form-item label="长半轴(米)">
<el-input-number v-model="formData.holdSemiMajor" :min="100" :max="50000" style="width:100%" />
<el-input-number v-model="formData.holdSemiMajor" :min="100" :max="50000" class="full-width-input" />
</el-form-item>
<el-form-item label="短半轴(米)">
<el-input-number v-model="formData.holdSemiMinor" :min="50" :max="50000" style="width:100%" />
<el-input-number v-model="formData.holdSemiMinor" :min="50" :max="50000" class="full-width-input" />
</el-form-item>
<el-form-item label="长轴方位(度)">
<el-input-number v-model="formData.holdHeadingDeg" :min="-180" :max="180" style="width:100%" />
<el-input-number v-model="formData.holdHeadingDeg" :min="-180" :max="180" class="full-width-input" />
</el-form-item>
</template>
<el-form-item label="盘旋方向">
@ -91,16 +117,15 @@
</el-form-item>
</template>
<el-form-item label="相对 K 时(分钟)" prop="minutesFromK">
<el-form-item label="相对K时(分)" prop="minutesFromK">
<el-input-number
v-model="formData.minutesFromK"
:min="-9999"
:max="9999"
controls-position="right"
placeholder="相对 K 的分钟数,正数表示 K 后,负数表示 K 前"
style="width: 100%;"
placeholder="正数 K 后,负数 K 前"
class="full-width-input"
/>
<div class="form-tip">正数=K 之后负数=K 之前40 表示 K+00:40-15 表示 K-00:15</div>
</el-form-item>
</el-form>
@ -155,7 +180,10 @@ export default {
holdHeadingDeg: 0,
holdClockwise: true,
labelFontSize: 14,
labelColor: '#333333'
labelColor: '#334155',
pixelSize: 10,
color: '#f1f5f9',
outlineColor: '#64748b'
},
rules: {
name: [
@ -214,7 +242,10 @@ export default {
}
} catch (e) {}
const labelFontSize = this.waypoint.labelFontSize != null ? Number(this.waypoint.labelFontSize) : 14;
const labelColor = this.waypoint.labelColor || '#333333';
const labelColor = this.waypoint.labelColor || '#334155';
const pixelSize = this.waypoint.pixelSize != null ? Number(this.waypoint.pixelSize) : 10;
const color = this.waypoint.color || '#f1f5f9';
const outlineColor = this.waypoint.outlineColor != null ? this.waypoint.outlineColor : '#64748b';
this.formData = {
name: this.waypoint.name || '',
alt: this.waypoint.alt !== undefined && this.waypoint.alt !== null ? Number(this.waypoint.alt) : 0,
@ -231,7 +262,10 @@ export default {
holdHeadingDeg,
holdClockwise,
labelFontSize: Math.min(28, Math.max(10, labelFontSize)),
labelColor
labelColor,
pixelSize: Math.min(24, Math.max(4, pixelSize)),
color,
outlineColor
};
this.$nextTick(() => {
@ -253,7 +287,10 @@ export default {
...rest,
startTime: startTimeStr,
labelFontSize: this.formData.labelFontSize,
labelColor: this.formData.labelColor
labelColor: this.formData.labelColor,
pixelSize: this.formData.pixelSize,
color: this.formData.color,
outlineColor: this.formData.outlineColor
};
if (this.formData.pointType && this.formData.pointType !== 'normal') {
payload.pointType = this.formData.pointType;
@ -328,7 +365,7 @@ export default {
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 500px;
max-width: 640px;
max-height: 90vh;
overflow-y: auto;
animation: dialog-fade-in 0.3s ease;
@ -384,6 +421,22 @@ export default {
gap: 10px;
}
.waypoint-form >>> .el-form-item__label {
white-space: nowrap;
}
.full-width-input {
width: 100%;
}
.color-form-row {
margin-bottom: 0;
}
.waypoint-form >>> .color-form-row .el-form-item {
margin-bottom: 18px;
}
.form-tip {
font-size: 12px;
color: #909399;
@ -391,9 +444,14 @@ export default {
line-height: 1.4;
}
.form-warn {
color: #e6a23c;
}
.color-value {
margin-left: 8px;
font-size: 12px;
color: #606266;
word-break: keep-all;
}
</style>

15
ruoyi-ui/src/views/selectRoom/index.vue

@ -214,9 +214,20 @@ export default {
}
},
showContextMenu(event, room) {
const padding = 12
const menuWidth = 160
const menuHeight = 90
const winW = window.innerWidth
const winH = window.innerHeight
let x = event.clientX
let y = event.clientY
if (x + menuWidth + padding > winW) x = winW - menuWidth - padding
if (x < padding) x = padding
if (y + menuHeight + padding > winH) y = winH - menuHeight - padding
if (y < padding) y = padding
this.contextMenu.visible = true
this.contextMenu.x = event.clientX
this.contextMenu.y = event.clientY
this.contextMenu.x = x
this.contextMenu.y = y
this.contextMenu.room = room
},
hideContextMenu() {

2
ruoyi-ui/vue.config.js

@ -15,7 +15,7 @@ const CompressionPlugin = require('compression-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
const baseUrl = 'http://192.168.1.107:8080' // 后端接口
const baseUrl = 'http://127.0.0.1:8080' // 后端接口
const port = process.env.port || process.env.npm_config_port || 80 // 端口
// 定义 Cesium 源码路径

Loading…
Cancel
Save