@@ -89,6 +89,15 @@ export default {
type: String,
default: 'airspace' // 'airspace' or 'ranging'
},
+ scaleConfig: {
+ type: Object,
+ default: () => ({
+ scaleNumerator: 1,
+ scaleDenominator: 1000,
+ unit: 'km',
+ position: 'bottom-right'
+ })
+ }
},
watch: {
drawDomClick: {
@@ -99,6 +108,14 @@ export default {
// this.initMap()
}
}
+ },
+ scaleConfig: {
+ deep: true,
+ handler(newVal) {
+ if (newVal) {
+ this.updateScaleFromConfig(newVal)
+ }
+ }
}
},
data() {
@@ -149,6 +166,10 @@ export default {
// 比例尺(高德风格)
scaleBarText: '--',
scaleBarWidthPx: 80,
+ useCustomScale: false,
+ customScaleText: '',
+ currentScaleUnit: 'm',
+ isApplyingScale: false,
// 定位相关
locateDialogVisible: false
}
@@ -174,6 +195,102 @@ export default {
this.destroyViewer()
},
methods: {
+ updateScaleFromConfig(config) {
+ if (!config || !this.viewer) return
+
+ const { scaleNumerator, scaleDenominator, unit } = config
+ if (!scaleDenominator || scaleDenominator <= 0) return
+
+ let displayValue = ''
+ let metersPerPixel = 0
+
+ const standardDPI = 96
+ const pixelsPerCm = standardDPI * 0.393701
+
+ const scaleFactor = scaleDenominator / scaleNumerator
+
+ switch (unit) {
+ case 'm':
+ displayValue = `1:${scaleDenominator}`
+ metersPerPixel = scaleFactor / pixelsPerCm
+ break
+ case 'km':
+ displayValue = `1:${scaleDenominator}`
+ metersPerPixel = (scaleFactor * 1000) / pixelsPerCm
+ break
+ default:
+ displayValue = `1:${scaleDenominator}`
+ metersPerPixel = scaleFactor / pixelsPerCm
+ }
+
+ console.log('比例尺设置:', displayValue, '每像素米数:', metersPerPixel.toFixed(4))
+
+ this.isApplyingScale = true
+ this.useCustomScale = true
+ this.customScaleText = displayValue
+ this.currentScaleUnit = unit
+
+ this.applyScaleToCamera(metersPerPixel)
+ this.updateScaleBar()
+ this.$forceUpdate()
+
+ setTimeout(() => {
+ this.isApplyingScale = false
+ }, 1000)
+ },
+
+ applyScaleToCamera(metersPerPixel) {
+ if (!this.viewer || !this.viewer.camera) return
+
+ const canvas = this.viewer.scene.canvas
+ const width = canvas.clientWidth
+ const height = canvas.clientHeight
+
+ if (width <= 0 || height <= 0) return
+
+ const camera = this.viewer.camera
+ const scene = this.viewer.scene
+
+ if (scene.mode === Cesium.SceneMode.SCENE2D) {
+ const frustumWidth = width * metersPerPixel
+ camera.frustum.right = frustumWidth / 2
+ camera.frustum.left = -frustumWidth / 2
+ const frustumHeight = height * metersPerPixel
+ camera.frustum.top = frustumHeight / 2
+ camera.frustum.bottom = -frustumHeight / 2
+ } else {
+ const cameraPosition = camera.positionCartographic
+ const groundWidth = width * metersPerPixel
+
+ const fov = camera.frustum.fov || Cesium.Math.PI_OVER_THREE
+ const aspectRatio = width / height
+ const verticalFov = fov / aspectRatio
+
+ const targetHeight = (groundWidth / 2) / Math.tan(fov / 2)
+
+ const destination = Cesium.Cartesian3.fromRadians(
+ cameraPosition.longitude,
+ cameraPosition.latitude,
+ targetHeight
+ )
+
+ camera.flyTo({
+ destination: destination,
+ duration: 0.5,
+ orientation: {
+ heading: camera.heading,
+ pitch: camera.pitch,
+ roll: camera.roll
+ }
+ })
+ }
+ },
+ handleScaleClick() {
+ this.$emit('scale-click', {
+ useCustomScale: this.useCustomScale,
+ customScaleText: this.customScaleText
+ })
+ },
preventContextMenu(e) {
e.preventDefault();
},
@@ -2766,6 +2883,10 @@ export default {
initScaleBar() {
const that = this
const update = () => {
+ if (!that.isApplyingScale) {
+ that.useCustomScale = false
+ that.customScaleText = ''
+ }
that.updateScaleBar()
}
update()
@@ -2806,10 +2927,12 @@ export default {
if (leftCartesian && rightCartesian) {
const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian)
if (rawMeters > 0) {
- const niceMeters = this.niceScaleValue(rawMeters)
- const widthPx = Math.round((niceMeters / rawMeters) * barPx)
- const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)} 米`
- return { text, widthPx, niceMeters }
+ const metersPerPx = rawMeters / barPx
+ const scaleRatio = this.calculateScaleRatio(metersPerPx)
+ const pixelsPerCm = this.getPixelsPerCm()
+ const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
+ const text = `1:${scaleRatio}${this.currentScaleUnit}`
+ return { text, widthPx, niceMeters: rawMeters }
}
}
// 2D/WebMercator 备用:用整屏宽度对应的地理范围计算(四角 pick 得到视口矩形)
@@ -2819,11 +2942,11 @@ export default {
const widthMeters = Cesium.Cartesian3.distance(leftBottom, rightBottom)
if (widthMeters > 0) {
const metersPerPx = widthMeters / w
- const rawMeters = metersPerPx * barPx
- const niceMeters = this.niceScaleValue(rawMeters)
- const widthPx = Math.round(niceMeters / metersPerPx)
- const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)} 米`
- return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters }
+ const scaleRatio = this.calculateScaleRatio(metersPerPx)
+ const pixelsPerCm = this.getPixelsPerCm()
+ const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
+ const text = `1:${scaleRatio}${this.currentScaleUnit}`
+ return { text, widthPx, niceMeters: widthMeters }
}
}
// 最后备用:从 2D 相机视锥估算(正交宽度 -> 米)
@@ -2831,15 +2954,34 @@ export default {
const frustumWidth = camera.frustum.right - camera.frustum.left
if (frustumWidth > 0) {
const metersPerPx = frustumWidth / w
- const rawMeters = metersPerPx * barPx
- const niceMeters = this.niceScaleValue(rawMeters)
- const widthPx = Math.round(niceMeters / metersPerPx)
- const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)} 米`
- return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters }
+ const scaleRatio = this.calculateScaleRatio(metersPerPx)
+ const pixelsPerCm = this.getPixelsPerCm()
+ const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
+ const text = `1:${scaleRatio}${this.currentScaleUnit}`
+ return { text, widthPx, niceMeters: frustumWidth }
}
}
return null
},
+ /** 计算比例尺比例 */
+ calculateScaleRatio(metersPerPx) {
+ const standardDPI = 96
+ const pixelsPerCm = standardDPI * 0.393701
+ const metersPerCm = metersPerPx * pixelsPerCm
+ let scaleRatio = 0
+ if (this.currentScaleUnit === 'km') {
+ scaleRatio = Math.round(metersPerCm / 1000)
+ } else {
+ scaleRatio = Math.round(metersPerCm)
+ }
+ return scaleRatio
+ },
+ /** 获取1厘米对应的像素数 */
+ getPixelsPerCm() {
+ const standardDPI = 96
+ const pixelsPerCm = standardDPI * 0.393701
+ return pixelsPerCm
+ },
/** 将实际距离圆整为易读的刻度值(米) */
niceScaleValue(meters) {
const candidates = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000]
@@ -2850,13 +2992,19 @@ export default {
return best
},
updateScaleBar() {
- const info = this.getScaleBarInfo()
- if (info) {
- this.scaleBarText = info.text
- this.scaleBarWidthPx = Math.min(120, Math.max(40, info.widthPx))
+ if (this.useCustomScale && this.customScaleText) {
+ this.scaleBarText = `${this.customScaleText}${this.currentScaleUnit}`
+ const pixelsPerCm = this.getPixelsPerCm()
+ this.scaleBarWidthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
} else {
- this.scaleBarText = '--'
- this.scaleBarWidthPx = 80
+ const info = this.getScaleBarInfo()
+ if (info) {
+ this.scaleBarText = info.text
+ this.scaleBarWidthPx = Math.min(120, Math.max(40, info.widthPx))
+ } else {
+ this.scaleBarText = '--'
+ this.scaleBarWidthPx = 80
+ }
}
this.$nextTick()
},
diff --git a/ruoyi-ui/src/views/childRoom/TopHeader.vue b/ruoyi-ui/src/views/childRoom/TopHeader.vue
index b3e3b34..2d21f3e 100644
--- a/ruoyi-ui/src/views/childRoom/TopHeader.vue
+++ b/ruoyi-ui/src/views/childRoom/TopHeader.vue
@@ -319,6 +319,14 @@ export default {
isIconEditMode: {
type: Boolean,
default: false
+ },
+ currentScaleConfig: {
+ type: Object,
+ default: () => ({
+ scaleNumerator: 1,
+ scaleDenominator: 1000,
+ unit: 'm'
+ })
}
},
data() {
@@ -351,6 +359,16 @@ export default {
]
}
},
+ watch: {
+ currentScaleConfig: {
+ deep: true,
+ handler(newVal) {
+ if (newVal) {
+ this.currentScale = { ...newVal }
+ }
+ }
+ }
+ },
methods: {
selectTopNav(item) {
this.$emit('select-nav', item)
diff --git a/ruoyi-ui/src/views/childRoom/index.vue b/ruoyi-ui/src/views/childRoom/index.vue
index b3956c1..331e55b 100644
--- a/ruoyi-ui/src/views/childRoom/index.vue
+++ b/ruoyi-ui/src/views/childRoom/index.vue
@@ -6,8 +6,10 @@
+ @open-waypoint-dialog="handleOpenWaypointEdit"
+ @scale-click="handleScaleClick" />
二维GIS地图区域
@@ -44,6 +46,7 @@
:astro-time="astroTime"
:user-avatar="userAvatar"
:is-icon-edit-mode="isIconEditMode"
+ :current-scale-config="scaleConfig"
@select-nav="selectTopNav"
@save-plan="savePlan"
@import-plan-file="importPlanFile"
@@ -238,6 +241,7 @@
v-model="showScaleDialog"
:scale="currentScale"
@save="saveScale"
+ @unit-change="handleScaleUnitChange"
/>
@@ -335,7 +339,16 @@ export default {
showPowerZoneDialog: false,
currentPowerZone: {},
showScaleDialog: false,
- currentScale: {},
+ currentScale: {
+ scaleNumerator: 1,
+ scaleDenominator: 1000,
+ unit: 'm'
+ },
+ scaleConfig: {
+ scaleNumerator: 1,
+ scaleDenominator: 1000,
+ unit: 'm'
+ },
showExternalParamsDialog: false,
currentExternalParams: {},
showPageLayoutDialog: false,
@@ -1111,10 +1124,36 @@ export default {
saveScale(scale) {
console.log('保存比例尺:', scale)
+ this.scaleConfig = {
+ scaleNumerator: scale.scaleNumerator,
+ scaleDenominator: scale.scaleDenominator,
+ unit: scale.unit
+ }
+
+ if (this.$refs.cesiumMap) {
+ this.$refs.cesiumMap.updateScaleFromConfig(this.scaleConfig)
+ }
+
const scaleText = `${scale.scaleNumerator}:${scale.scaleDenominator}`
this.$message.success(`比例尺 "${scaleText}" 保存成功`);
},
+ handleScaleClick(scaleInfo) {
+ this.currentScale = {
+ scaleNumerator: this.scaleConfig.scaleNumerator,
+ scaleDenominator: this.scaleConfig.scaleDenominator,
+ unit: this.scaleConfig.unit
+ }
+ this.showScaleDialog = true
+ },
+
+ handleScaleUnitChange(unit) {
+ this.scaleConfig.unit = unit
+ if (this.$refs.cesiumMap) {
+ this.$refs.cesiumMap.currentScaleUnit = unit
+ }
+ },
+
// 地图下拉菜单方法
loadTerrain() {
this.$message.success('加载/切换地形');
diff --git a/ruoyi-ui/src/views/dialogs/ScaleDialog.vue b/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
index 4015c8e..c4d64fb 100644
--- a/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
@@ -30,17 +30,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -72,8 +61,7 @@ export default {
formData: {
scaleNumerator: 1,
scaleDenominator: 1000,
- unit: 'km',
- position: 'bottom-right'
+ unit: 'm'
},
rules: {
scaleNumerator: [
@@ -84,9 +72,6 @@ export default {
],
unit: [
{ required: true, message: '请选择单位', trigger: 'change' }
- ],
- position: [
- { required: true, message: '请选择显示位置', trigger: 'change' }
]
}
};
@@ -101,6 +86,11 @@ export default {
if (this.value && newVal) {
this.initFormData();
}
+ },
+ 'formData.unit'(newVal) {
+ if (this.value) {
+ this.$emit('unit-change', newVal);
+ }
}
},
methods: {
@@ -108,8 +98,7 @@ export default {
this.formData = {
scaleNumerator: this.scale.scaleNumerator || 1,
scaleDenominator: this.scale.scaleDenominator || 1000,
- unit: this.scale.unit || 'km',
- position: this.scale.position || 'bottom-right'
+ unit: this.scale.unit || 'm'
};
},
closeDialog() {
@@ -151,7 +140,7 @@ export default {
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
width: 90%;
- max-width: 500px;
+ max-width: 400px;
max-height: 90vh;
overflow-y: auto;
animation: dialog-fade-in 0.3s ease;
@@ -173,7 +162,7 @@ export default {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 16px 20px;
+ padding: 12px 16px;
border-bottom: 1px solid #e8e8e8;
}
@@ -196,7 +185,17 @@ export default {
}
.dialog-body {
- padding: 20px;
+ padding: 16px;
+}
+
+.quick-scale-buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.quick-scale-buttons .el-button {
+ margin: 0;
}
.scale-inputs {
From 4e14d47edc387c506bf0a8cc9fcb9d7e262c5c3c Mon Sep 17 00:00:00 2001
From: sd <1504629600@qq.com>
Date: Thu, 5 Feb 2026 15:29:30 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E6=AF=94=E4=BE=8B=E5=B0=BA=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ruoyi-ui/src/views/cesiumMap/index.vue | 38 ++++++++++++++++++------------
ruoyi-ui/src/views/dialogs/ScaleDialog.vue | 8 +++----
2 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/ruoyi-ui/src/views/cesiumMap/index.vue b/ruoyi-ui/src/views/cesiumMap/index.vue
index c5f5a32..fd6a625 100644
--- a/ruoyi-ui/src/views/cesiumMap/index.vue
+++ b/ruoyi-ui/src/views/cesiumMap/index.vue
@@ -2928,11 +2928,10 @@ export default {
const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian)
if (rawMeters > 0) {
const metersPerPx = rawMeters / barPx
- const scaleRatio = this.calculateScaleRatio(metersPerPx)
- const pixelsPerCm = this.getPixelsPerCm()
- const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
- const text = `1:${scaleRatio}${this.currentScaleUnit}`
- return { text, widthPx, niceMeters: rawMeters }
+ const niceMeters = this.niceScaleValue(rawMeters)
+ const widthPx = Math.round((niceMeters / rawMeters) * barPx)
+ const text = this.formatScaleText(niceMeters)
+ return { text, widthPx, niceMeters }
}
}
// 2D/WebMercator 备用:用整屏宽度对应的地理范围计算(四角 pick 得到视口矩形)
@@ -2942,11 +2941,11 @@ export default {
const widthMeters = Cesium.Cartesian3.distance(leftBottom, rightBottom)
if (widthMeters > 0) {
const metersPerPx = widthMeters / w
- const scaleRatio = this.calculateScaleRatio(metersPerPx)
- const pixelsPerCm = this.getPixelsPerCm()
- const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
- const text = `1:${scaleRatio}${this.currentScaleUnit}`
- return { text, widthPx, niceMeters: widthMeters }
+ const rawMeters = metersPerPx * barPx
+ const niceMeters = this.niceScaleValue(rawMeters)
+ const widthPx = Math.round(niceMeters / metersPerPx)
+ const text = this.formatScaleText(niceMeters)
+ return { text, widthPx, niceMeters }
}
}
// 最后备用:从 2D 相机视锥估算(正交宽度 -> 米)
@@ -2954,11 +2953,11 @@ export default {
const frustumWidth = camera.frustum.right - camera.frustum.left
if (frustumWidth > 0) {
const metersPerPx = frustumWidth / w
- const scaleRatio = this.calculateScaleRatio(metersPerPx)
- const pixelsPerCm = this.getPixelsPerCm()
- const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
- const text = `1:${scaleRatio}${this.currentScaleUnit}`
- return { text, widthPx, niceMeters: frustumWidth }
+ const rawMeters = metersPerPx * barPx
+ const niceMeters = this.niceScaleValue(rawMeters)
+ const widthPx = Math.round(niceMeters / metersPerPx)
+ const text = this.formatScaleText(niceMeters)
+ return { text, widthPx, niceMeters }
}
}
return null
@@ -2991,6 +2990,15 @@ export default {
}
return best
},
+ /** 格式化比例尺文本 */
+ formatScaleText(meters) {
+ if (meters >= 1000) {
+ const kmValue = (meters / 1000).toFixed(0)
+ return `${kmValue}km`
+ } else {
+ return `${meters}m`
+ }
+ },
updateScaleBar() {
if (this.useCustomScale && this.customScaleText) {
this.scaleBarText = `${this.customScaleText}${this.currentScaleUnit}`
diff --git a/ruoyi-ui/src/views/dialogs/ScaleDialog.vue b/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
index c4d64fb..b6650ba 100644
--- a/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
@@ -60,8 +60,8 @@ export default {
return {
formData: {
scaleNumerator: 1,
- scaleDenominator: 1000,
- unit: 'm'
+ scaleDenominator: 20,
+ unit: 'km'
},
rules: {
scaleNumerator: [
@@ -97,8 +97,8 @@ export default {
initFormData() {
this.formData = {
scaleNumerator: this.scale.scaleNumerator || 1,
- scaleDenominator: this.scale.scaleDenominator || 1000,
- unit: this.scale.unit || 'm'
+ scaleDenominator: this.scale.scaleDenominator || 20,
+ unit: this.scale.unit || 'km'
};
},
closeDialog() {