@@ -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();
},
@@ -2920,6 +3037,10 @@ export default {
initScaleBar() {
const that = this
const update = () => {
+ if (!that.isApplyingScale) {
+ that.useCustomScale = false
+ that.customScaleText = ''
+ }
that.updateScaleBar()
}
update()
@@ -2960,9 +3081,10 @@ export default {
if (leftCartesian && rightCartesian) {
const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian)
if (rawMeters > 0) {
+ const metersPerPx = rawMeters / barPx
const niceMeters = this.niceScaleValue(rawMeters)
const widthPx = Math.round((niceMeters / rawMeters) * barPx)
- const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)} 米`
+ const text = this.formatScaleText(niceMeters)
return { text, widthPx, niceMeters }
}
}
@@ -2976,8 +3098,8 @@ export default {
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 text = this.formatScaleText(niceMeters)
+ return { text, widthPx, niceMeters }
}
}
// 最后备用:从 2D 相机视锥估算(正交宽度 -> 米)
@@ -2988,12 +3110,31 @@ export default {
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 text = this.formatScaleText(niceMeters)
+ return { text, widthPx, niceMeters }
}
}
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]
@@ -3003,14 +3144,29 @@ export default {
}
return best
},
+ /** 格式化比例尺文本 */
+ formatScaleText(meters) {
+ if (meters >= 1000) {
+ const kmValue = (meters / 1000).toFixed(0)
+ return `${kmValue}km`
+ } else {
+ return `${meters}m`
+ }
+ },
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/BottomTimeline.vue b/ruoyi-ui/src/views/childRoom/BottomTimeline.vue
index d4ecdba..8b8b636 100644
--- a/ruoyi-ui/src/views/childRoom/BottomTimeline.vue
+++ b/ruoyi-ui/src/views/childRoom/BottomTimeline.vue
@@ -471,18 +471,19 @@ export default {
})
this.scaleLabels = []
- const labelCount = 5
- for (let i = 0; i <= labelCount; i++) {
- const percentage = (i / labelCount) * 100
- const currentSeconds = startSeconds + (totalSeconds * percentage / 100)
- const hours = Math.floor(currentSeconds / 3600)
- const minutes = Math.floor((currentSeconds % 3600) / 60)
+ const sortedSegments = [...this.timelineSegments].sort((a, b) => {
+ return this.timeToSeconds(a.time) - this.timeToSeconds(b.time)
+ })
+ sortedSegments.forEach(segment => {
+ const segmentSeconds = this.timeToSeconds(segment.time)
+ const hours = Math.floor(segmentSeconds / 3600)
+ const minutes = Math.floor((segmentSeconds % 3600) / 60)
const timeStr = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`
this.scaleLabels.push({
- position: percentage,
+ position: segment.position,
time: timeStr
})
- }
+ })
},
editSegment(segment) {
diff --git a/ruoyi-ui/src/views/childRoom/TopHeader.vue b/ruoyi-ui/src/views/childRoom/TopHeader.vue
index b04d5d2..2b027a7 100644
--- a/ruoyi-ui/src/views/childRoom/TopHeader.vue
+++ b/ruoyi-ui/src/views/childRoom/TopHeader.vue
@@ -334,6 +334,14 @@ export default {
isIconEditMode: {
type: Boolean,
default: false
+ },
+ currentScaleConfig: {
+ type: Object,
+ default: () => ({
+ scaleNumerator: 1,
+ scaleDenominator: 1000,
+ unit: 'm'
+ })
}
},
data() {
@@ -366,6 +374,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 42fa47e..0d42696 100644
--- a/ruoyi-ui/src/views/childRoom/index.vue
+++ b/ruoyi-ui/src/views/childRoom/index.vue
@@ -6,8 +6,10 @@
@open-route-dialog="handleOpenRouteEdit" />
@@ -67,6 +69,7 @@
:can-set-k-time="canSetKTime"
:user-avatar="userAvatar"
:is-icon-edit-mode="isIconEditMode"
+ :current-scale-config="scaleConfig"
@select-nav="selectTopNav"
@set-k-time="openKTimeSetDialog"
@save-plan="savePlan"
@@ -269,6 +272,7 @@
v-model="showScaleDialog"
:scale="currentScale"
@save="saveScale"
+ @unit-change="handleScaleUnitChange"
/>
@@ -368,7 +372,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,
@@ -1395,10 +1408,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..b6650ba 100644
--- a/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/ScaleDialog.vue
@@ -30,17 +30,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -71,9 +60,8 @@ export default {
return {
formData: {
scaleNumerator: 1,
- scaleDenominator: 1000,
- unit: 'km',
- position: 'bottom-right'
+ scaleDenominator: 20,
+ unit: 'km'
},
rules: {
scaleNumerator: [
@@ -84,9 +72,6 @@ export default {
],
unit: [
{ required: true, message: '请选择单位', trigger: 'change' }
- ],
- position: [
- { required: true, message: '请选择显示位置', trigger: 'change' }
]
}
};
@@ -101,15 +86,19 @@ export default {
if (this.value && newVal) {
this.initFormData();
}
+ },
+ 'formData.unit'(newVal) {
+ if (this.value) {
+ this.$emit('unit-change', newVal);
+ }
}
},
methods: {
initFormData() {
this.formData = {
scaleNumerator: this.scale.scaleNumerator || 1,
- scaleDenominator: this.scale.scaleDenominator || 1000,
- unit: this.scale.unit || 'km',
- position: this.scale.position || 'bottom-right'
+ scaleDenominator: this.scale.scaleDenominator || 20,
+ unit: this.scale.unit || 'km'
};
},
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 {