add
This commit is contained in:
parent
f60eaefa37
commit
ab89bed57d
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -22,6 +22,8 @@ declare module '@vue/runtime-core' {
|
||||
Modal: typeof import('./src/components/Modal/index.vue')['default']
|
||||
Nav: typeof import('./src/components/Nav/index.vue')['default']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
||||
|
7
package-lock.json
generated
7
package-lock.json
generated
@ -28,6 +28,7 @@
|
||||
"satellite.js": "^5.0.0",
|
||||
"seemly": "^0.3.9",
|
||||
"v-viewer": "^3.0.21",
|
||||
"vanilla-js-wheel-zoom": "^9.0.4",
|
||||
"viewerjs": "^1.11.7",
|
||||
"vue": "^3.2.45",
|
||||
"vue-draggable-plus": "^0.5.6",
|
||||
@ -15120,6 +15121,12 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vanilla-js-wheel-zoom": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/vanilla-js-wheel-zoom/-/vanilla-js-wheel-zoom-9.0.4.tgz",
|
||||
"integrity": "sha512-OjmS9ihEKBCRw2OQ7IiIdQGXdC5gTEEmtcAWZcPeNAJaYiS61KCd02Z72YMtIoXLGN5TZP+wliBMylLAsr6wow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vdirs": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/vdirs/-/vdirs-0.1.8.tgz",
|
||||
|
@ -31,6 +31,7 @@
|
||||
"satellite.js": "^5.0.0",
|
||||
"seemly": "^0.3.9",
|
||||
"v-viewer": "^3.0.21",
|
||||
"vanilla-js-wheel-zoom": "^9.0.4",
|
||||
"viewerjs": "^1.11.7",
|
||||
"vue": "^3.2.45",
|
||||
"vue-draggable-plus": "^0.5.6",
|
||||
|
@ -1,8 +1,16 @@
|
||||
import { defAxios as request } from '@/utils/http'
|
||||
const baseUrl = window.settings.apis
|
||||
// export function getHangjing(data = {}) {
|
||||
// return request({
|
||||
// url: `${baseUrl}/hangjing/list`,
|
||||
// method: 'post',
|
||||
// data,
|
||||
// })
|
||||
// }
|
||||
|
||||
export function getHangjing(data = {}) {
|
||||
return request({
|
||||
url: `${baseUrl}/hangjing/list`,
|
||||
url: `${baseUrl}/hangjing/shiJianTree`,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
|
@ -56,6 +56,9 @@
|
||||
);
|
||||
|
||||
--color-bg: #1a222966;
|
||||
|
||||
--gradient-bg-title: linear-gradient(to right, #4fd2dd55, #4b877400);
|
||||
--tw-from-opacity: 33%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
8
src/assets/detail.scss
Normal file
8
src/assets/detail.scss
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
.detail-container {
|
||||
@apply flex flex-col gap-4;
|
||||
.detail-item-title{
|
||||
@apply font-bold text-xl w-[120px] h-[30px] leading-[30px];
|
||||
background-image: linear-gradient(to right, #4fd2dd55 0%, transparent 100%);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
@import './base.css';
|
||||
@import './naiveui.css';
|
||||
@import './detail.scss';
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
|
@ -1,3 +1,7 @@
|
||||
.n-dialog .n-dialog__content {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.n-base-select-option__content {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ $radius: 2rem;
|
||||
.content-title {
|
||||
@apply h-9 px-5 leading-9 tracking-[5px];
|
||||
border-radius: $radius $radius 0 0;
|
||||
background: linear-gradient(to right, #4fd2dd55, #4b877400);
|
||||
background: var(--gradient-bg-title);
|
||||
.title-text {
|
||||
@apply font-bold italic;
|
||||
color: transparent;
|
||||
|
@ -62,12 +62,12 @@ async function getHisTraj({
|
||||
|
||||
function getMBEntityOpt({
|
||||
id,
|
||||
name,
|
||||
targetType,
|
||||
name,
|
||||
}: {
|
||||
id: string
|
||||
name?: string
|
||||
targetType: string
|
||||
name?: string
|
||||
}) {
|
||||
const mubiaoDict = window.settings.mbDict[targetType]
|
||||
|
||||
@ -102,6 +102,25 @@ function getMBEntityOpt({
|
||||
scale: 1000,
|
||||
minimumPixelSize: 50,
|
||||
},
|
||||
ellipsoid: {
|
||||
show: true,
|
||||
radii: new Cesium.Cartesian3(100000, 100000, 100000),
|
||||
innerRadii: new Cesium.Cartesian3(1.0, 1.0, 1.0),
|
||||
maximumCone: Cesium.Math.toRadians(90),
|
||||
minimumCone: Cesium.Math.toRadians(40),
|
||||
minimumClock: Cesium.Math.toRadians(20),
|
||||
maximumClock: Cesium.Math.toRadians(90),
|
||||
material: Cesium.Color.fromCssColorString('#00dcff44'),
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.fromCssColorString('#00dcff'),
|
||||
outlineWidth: 1,
|
||||
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
|
||||
0.0,
|
||||
10.5e8
|
||||
),
|
||||
slicePartitions: 24,
|
||||
stackPartitions: 36,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
142
src/js/Radar.js
Normal file
142
src/js/Radar.js
Normal file
@ -0,0 +1,142 @@
|
||||
export default class Radar {
|
||||
// 创建雷达罩
|
||||
constructor(options) {
|
||||
this.radius = options.radius
|
||||
// this.viewer = options.viewer
|
||||
// this.id = options.id
|
||||
// this.entity = null
|
||||
// this.radius = options.radius
|
||||
// ;(this.longitude = options.position[0]),
|
||||
// (this.latitude = options.position[1]),
|
||||
// (this.position = Cesium.Cartesian3.fromDegrees(
|
||||
// options.position[0],
|
||||
// options.position[1]
|
||||
// ))
|
||||
// this.heading = 0
|
||||
// this.positionArr = this.calcPoints(
|
||||
// options.position[0],
|
||||
// options.position[1],
|
||||
// options.radius,
|
||||
// 0
|
||||
// ) //储存脏数据
|
||||
// this.addEntities()
|
||||
}
|
||||
cartesian32LonLat(cartesian3) {
|
||||
const cartographic =
|
||||
this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
|
||||
cartesian3._value
|
||||
)
|
||||
const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
return [lon, lat]
|
||||
}
|
||||
getRadar() {
|
||||
// this.entity = this.viewer.entities.add({
|
||||
// id: this.id,
|
||||
// position: this.position,
|
||||
// wall: {
|
||||
// positions: new Cesium.CallbackProperty(() => {
|
||||
// return Cesium.Cartesian3.fromDegreesArrayHeights(this.positionArr)
|
||||
// }, false),
|
||||
// material: Cesium.Color.fromCssColorString('#00dcff82'),
|
||||
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
|
||||
// 0.0,
|
||||
// 10.5e6
|
||||
// ),
|
||||
// },
|
||||
// ellipsoid: {
|
||||
// radii: new Cesium.Cartesian3(
|
||||
// this.radius,
|
||||
// this.radius,
|
||||
// this.radius
|
||||
// ),
|
||||
// maximumCone: Cesium.Math.toRadians(90),
|
||||
// material: Cesium.Color.fromCssColorString('#00dcff82'),
|
||||
// outline: true,
|
||||
// outlineColor: Cesium.Color.fromCssColorString('#00dcff'),
|
||||
// outlineWidth: 1,
|
||||
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
|
||||
// 0.0,
|
||||
// 10.5e8
|
||||
// ),
|
||||
// },
|
||||
// })
|
||||
return {
|
||||
// wall: {
|
||||
// positions: new Cesium.CallbackProperty(() => {
|
||||
// return Cesium.Cartesian3.fromDegreesArrayHeights(this.positionArr)
|
||||
// }, false),
|
||||
// material: Cesium.Color.fromCssColorString('#00dcff82'),
|
||||
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
|
||||
// 0.0,
|
||||
// 10.5e6
|
||||
// ),
|
||||
// },
|
||||
ellipsoid: {
|
||||
radii: new Cesium.Cartesian3(this.radius, this.radius, this.radius),
|
||||
maximumCone: Cesium.Math.toRadians(90),
|
||||
material: Cesium.Color.fromCssColorString('#00dcff82'),
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.fromCssColorString('#00dcff'),
|
||||
outlineWidth: 1,
|
||||
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
|
||||
0.0,
|
||||
10.5e8
|
||||
),
|
||||
},
|
||||
}
|
||||
// this.addPostRender()
|
||||
}
|
||||
addPostRender() {
|
||||
this.viewer.clock.onTick.addEventListener(() => {
|
||||
this.heading += 1.0 //可调节转动速度
|
||||
this.positionArr = this.calcPoints(
|
||||
this.longitude,
|
||||
this.latitude,
|
||||
this.radius,
|
||||
this.heading
|
||||
)
|
||||
})
|
||||
}
|
||||
calcPoints(x1, y1, radius, heading) {
|
||||
let m = Cesium.Transforms.eastNorthUpToFixedFrame(
|
||||
Cesium.Cartesian3.fromDegrees(x1, y1)
|
||||
)
|
||||
let rx = radius * Math.cos((heading * Math.PI) / 180.0)
|
||||
let ry = radius * Math.sin((heading * Math.PI) / 180.0)
|
||||
let translation = Cesium.Cartesian3.fromElements(rx, ry, 0)
|
||||
let d = Cesium.Matrix4.multiplyByPoint(
|
||||
m,
|
||||
translation,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
let c = Cesium.Cartographic.fromCartesian(d)
|
||||
let x2 = Cesium.Math.toDegrees(c.longitude)
|
||||
let y2 = Cesium.Math.toDegrees(c.latitude)
|
||||
return this.computeCirclularFlight(x1, y1, x2, y2, 0, 90)
|
||||
}
|
||||
computeCirclularFlight(x1, y1, x2, y2, fx, angle) {
|
||||
let positionArr = []
|
||||
positionArr.push(x1)
|
||||
positionArr.push(y1)
|
||||
positionArr.push(0)
|
||||
let radius = Cesium.Cartesian3.distance(
|
||||
Cesium.Cartesian3.fromDegrees(x1, y1),
|
||||
Cesium.Cartesian3.fromDegrees(x2, y2)
|
||||
)
|
||||
for (let i = fx; i <= fx + angle; i++) {
|
||||
let h = radius * Math.sin((i * Math.PI) / 180.0)
|
||||
let r = Math.cos((i * Math.PI) / 180.0)
|
||||
let x = (x2 - x1) * r + x1
|
||||
let y = (y2 - y1) * r + y1
|
||||
positionArr.push(x)
|
||||
positionArr.push(y)
|
||||
positionArr.push(h)
|
||||
}
|
||||
return positionArr
|
||||
}
|
||||
removeEntity() {
|
||||
this.entity && this.viewer.entities.remove(this.entity)
|
||||
this.entity = null
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ class SatelliteEntity {
|
||||
this.entity = null
|
||||
this._underPoint = false
|
||||
this.underPointEntity = null
|
||||
this._sensorType = 'rectangle'
|
||||
this._sensorType = 'conic'
|
||||
this._listener = null
|
||||
}
|
||||
get sensorType() {
|
||||
@ -443,9 +443,8 @@ class SatelliteEntity {
|
||||
},
|
||||
polyline: {
|
||||
positions: points,
|
||||
|
||||
width: 1,
|
||||
material: Cesium.Color.RED,
|
||||
material: color,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
70
src/views/BaseMB/components/HisImages/components/LImage.jsx
Normal file
70
src/views/BaseMB/components/HisImages/components/LImage.jsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { NImage } from 'naive-ui'
|
||||
import WZoom from 'vanilla-js-wheel-zoom/dist/wheel-zoom.min.js'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
imageList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// const { images, activeIndex } = toRefs(props)
|
||||
|
||||
watch(
|
||||
() => props.imageList,
|
||||
() => {
|
||||
if (props.imageList.length > 1) {
|
||||
nextTick(() => {
|
||||
props.imageList.map(image => {
|
||||
const imageElement = document
|
||||
.getElementById(`image-${image.id}`)
|
||||
.querySelector('img')
|
||||
|
||||
const wz = WZoom.create(`#image-${image.id}`, {
|
||||
type: 'html',
|
||||
maxScale: 3,
|
||||
minScale: 0.2,
|
||||
// zoomOnDoubleClick: true,
|
||||
width: imageElement.naturalWidth,
|
||||
height: imageElement.naturalHeight,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
return () => (
|
||||
<div class="l-image-container flex h-full w-full gap-4">
|
||||
{props.imageList.map(image => (
|
||||
<div class="flex h-full flex-1 flex-col gap-2">
|
||||
<div>{image.createTime}</div>
|
||||
<div class="h-0 flex-1 overflow-hidden">
|
||||
{props.imageList.length > 1 ? (
|
||||
<div
|
||||
id={`image-${image.id}`}
|
||||
class="flex h-full w-full cursor-grab items-center justify-center"
|
||||
>
|
||||
<img
|
||||
class="w-full"
|
||||
src={image.imgPath}
|
||||
draggable="false"
|
||||
// alt="image"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<NImage
|
||||
class="flex justify-center object-contain w-h-full"
|
||||
object-fit="contain"
|
||||
src={image.imgPath}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="line-clamp-3 pb-4">{image.detailContent}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
@ -4,6 +4,7 @@
|
||||
import { ImageOutline } from '@vicons/ionicons5'
|
||||
import { useHisImage } from './hooks/hisImage'
|
||||
import Panel from '@/components/Panel/index.vue'
|
||||
import LImage from './components/LImage'
|
||||
|
||||
const { sheshiData, showOrHideHisImage, getHisImages } = useHisImage()
|
||||
|
||||
@ -61,12 +62,47 @@ const getImage = async () => {
|
||||
// })
|
||||
imageList.value = new Array(10).fill(1).map((item, index) => {
|
||||
return {
|
||||
id: index,
|
||||
imgPath: `https://picsum.photos/300/200?random=${index}`,
|
||||
imgId: index,
|
||||
}
|
||||
})
|
||||
// console.log(imageList.value, 'imageList')
|
||||
}
|
||||
|
||||
const checkedImage = ref<null | string[]>(null)
|
||||
|
||||
const checkValue = e => {
|
||||
e.stopPropagation()
|
||||
if (checkedImage.value && checkedImage.value.length > 2) {
|
||||
checkedImage.value.pop()
|
||||
}
|
||||
}
|
||||
|
||||
// watch(checkedImage, newCheck => {
|
||||
// // console.log(newCheck, 'newCheck')
|
||||
// })
|
||||
const isCompare = ref(false)
|
||||
const compareImages = () => {
|
||||
if (!isCompare.value) {
|
||||
if (checkedImage.value && checkedImage.value.length === 2) {
|
||||
isCompare.value = true
|
||||
}
|
||||
} else {
|
||||
isCompare.value = false
|
||||
checkedImage.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const largeImageList = computed(() => {
|
||||
if (isCompare.value) {
|
||||
return imageList.value.filter(img => checkedImage.value.includes(img.id))
|
||||
} else {
|
||||
return imageList.value[previewIndex.value - 1]
|
||||
? [imageList.value[previewIndex.value - 1]]
|
||||
: []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -79,23 +115,8 @@ const getImage = async () => {
|
||||
>
|
||||
<div class="flex flex-col w-h-full">
|
||||
<div class="large-container">
|
||||
<div>
|
||||
{{ previewIndex > 0 ? imageList[previewIndex - 1].createTime : '' }}
|
||||
</div>
|
||||
<!-- <vue-viewer :images="imageList" :options="{ inline: true }"> -->
|
||||
<div class="h-0 flex-1">
|
||||
<n-image
|
||||
class="flex justify-center object-contain w-h-full"
|
||||
object-fit="contain"
|
||||
:src="previewIndex > 0 ? imageList[previewIndex - 1].imgPath : ''"
|
||||
/>
|
||||
</div>
|
||||
<l-image :imageList="largeImageList" />
|
||||
|
||||
<div class="line-clamp-3 pb-4">
|
||||
{{
|
||||
previewIndex > 0 ? imageList[previewIndex - 1].detailContent : ''
|
||||
}}
|
||||
</div>
|
||||
<!-- </vue-viewer> -->
|
||||
</div>
|
||||
<div ref="scrollRef" class="flex h-[178px] flex-col">
|
||||
@ -109,12 +130,19 @@ const getImage = async () => {
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
/>
|
||||
<n-button type="primary" @click="getImage">搜索</n-button>
|
||||
<n-button type="primary" @click="compareImages">{{
|
||||
isCompare ? '取消对比' : '对比'
|
||||
}}</n-button>
|
||||
</div>
|
||||
<div v-if="imageList.length === 0" class="m-auto flex">
|
||||
<n-empty description="暂无数据"> </n-empty>
|
||||
</div>
|
||||
<n-scrollbar v-else x-scrollable>
|
||||
<div class="flex h-full flex-nowrap">
|
||||
<!-- <div> -->
|
||||
<n-checkbox-group
|
||||
class="flex h-full flex-nowrap"
|
||||
v-model:value="checkedImage"
|
||||
>
|
||||
<div
|
||||
class="flex h-full w-[140px] cursor-pointer p-2"
|
||||
v-for="(i, index) in imageList"
|
||||
@ -128,6 +156,12 @@ const getImage = async () => {
|
||||
index + 1 === previewIndex,
|
||||
}"
|
||||
>
|
||||
<n-checkbox
|
||||
class="absolute"
|
||||
:value="i.id"
|
||||
label=""
|
||||
@click.stop="checkValue"
|
||||
/>
|
||||
<n-image
|
||||
class="m-auto"
|
||||
object-fit="contain"
|
||||
@ -143,7 +177,8 @@ const getImage = async () => {
|
||||
</n-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-checkbox-group>
|
||||
<!-- </div> -->
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
@ -154,6 +189,7 @@ const getImage = async () => {
|
||||
<style lang="scss" scoped>
|
||||
.pre-container {
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
.large-container {
|
||||
@apply flex h-0 flex-1 flex-col gap-2 overflow-hidden text-center;
|
||||
//
|
||||
|
@ -43,6 +43,10 @@ const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import moment from 'moment-timezone'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { time2FormatWithTimezone } from '@/utils/date'
|
||||
import WidgetNav from '../WidgetNav'
|
||||
|
||||
@ -38,6 +39,8 @@ onMounted(() => {
|
||||
getTime()
|
||||
getLocalTime()
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -64,7 +67,7 @@ onMounted(() => {
|
||||
{{ worldTime }}
|
||||
</div>
|
||||
</div>
|
||||
<widget-nav />
|
||||
<widget-nav v-if="route.path === '\/'" />
|
||||
<div class="time-container bgc-animation">
|
||||
<div class="bgc-animation">{{ localeTime }}</div>
|
||||
<div class="time-title">作战时</div>
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { NIcon, NPopover } from 'naive-ui'
|
||||
import { useEntity } from '@/hooks/entity'
|
||||
import { useSatellite } from '@/views/Satellite/hooks/satellite'
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { satelliteMap } = useEntity()
|
||||
const { showPoint, showPointUnderSat } = useSatellite()
|
||||
|
||||
const show = ref(false)
|
||||
// const show = ref(false)
|
||||
const showPointUnderSatellite = () => {
|
||||
show.value = !show.value
|
||||
;[...satelliteMap.values()].forEach(satellite => {
|
||||
satellite.underPoint = show.value
|
||||
})
|
||||
showPoint.value = !showPoint.value
|
||||
showPointUnderSat()
|
||||
}
|
||||
return () => (
|
||||
<>
|
||||
@ -18,7 +16,10 @@ export default defineComponent({
|
||||
placement="bottom"
|
||||
v-slots={{
|
||||
trigger: () => (
|
||||
<div class="btn-class" onClick={showPointUnderSatellite}>
|
||||
<div
|
||||
class={`btn-class ${showPoint.value ? 'checked' : ''}`}
|
||||
onClick={showPointUnderSatellite}
|
||||
>
|
||||
<NIcon size="15">
|
||||
<svg
|
||||
t="1736493475776"
|
||||
|
@ -54,7 +54,7 @@ const types = [
|
||||
{ name: 'XW', value: 'wzbXw' },
|
||||
]
|
||||
|
||||
const showPanelName = ref('wx')
|
||||
const showPanelName = ref('hj-2')
|
||||
|
||||
const panelList = [
|
||||
// {
|
||||
@ -122,17 +122,9 @@ const showOrHideTextReport = () => {
|
||||
|
||||
<div class="grid flex-1 grid-cols-[1.5fr_3fr_1.5fr] grid-rows-1 gap-1">
|
||||
<div class="left-panel pl-8">
|
||||
<div
|
||||
class="radio-group absolute -left-4 top-[15%] z-30 flex w-12 translate-y-[-50%] transform flex-col"
|
||||
>
|
||||
<div class="radio-group absolute -left-4 top-[15%] z-30 flex w-12 translate-y-[-50%] transform flex-col">
|
||||
<template v-for="panel in panelList" :key="panel.id">
|
||||
<input
|
||||
type="radio"
|
||||
:id="panel.id"
|
||||
name="selector"
|
||||
v-model="showPanelName"
|
||||
:value="panel.value"
|
||||
/>
|
||||
<input type="radio" :id="panel.id" name="selector" v-model="showPanelName" :value="panel.value" />
|
||||
<label for="wx" @click="hidePanel($event, panel.value)">{{
|
||||
panel.name
|
||||
}}</label>
|
||||
@ -187,20 +179,10 @@ const showOrHideTextReport = () => {
|
||||
</div>
|
||||
<!-- <div class="z-20 grid grid-cols-1 grid-rows-3 gap-1"> -->
|
||||
<div>
|
||||
<div
|
||||
class="btn-transform z-20 w-h-full"
|
||||
:class="showTextReport ? '' : 'btn-transform-pos'"
|
||||
>
|
||||
<n-button
|
||||
class="absolute -left-[16px] top-5 z-30 border border-[#29baf1] bg-[var(--color-bg)]"
|
||||
size="tiny"
|
||||
@click="showOrHideTextReport"
|
||||
>
|
||||
<n-icon
|
||||
class="btn-transform"
|
||||
:class="showTextReport ? '' : 'icon-transform'"
|
||||
><arrow-right
|
||||
/></n-icon>
|
||||
<div class="btn-transform z-20 w-h-full" :class="showTextReport ? '' : 'btn-transform-pos'">
|
||||
<n-button class="absolute -left-[16px] top-5 z-30 border border-[#29baf1] bg-[var(--color-bg)]" size="tiny"
|
||||
@click="showOrHideTextReport">
|
||||
<n-icon class="btn-transform" :class="showTextReport ? '' : 'icon-transform'"><arrow-right /></n-icon>
|
||||
</n-button>
|
||||
<!-- <transition name="slide2">.slice(0, 3) -->
|
||||
<panel title="文字报"><text-report :tabs="types" /></panel>
|
||||
@ -220,14 +202,8 @@ const showOrHideTextReport = () => {
|
||||
</div>
|
||||
<div class="absolute bottom-0 flex h-full w-full flex-col justify-end">
|
||||
<text-message class="absolute z-30 h-[200px]"></text-message>
|
||||
<mubiao-his-trajectory
|
||||
v-if="showHisTrajCom"
|
||||
class="z-30 h-[260px]"
|
||||
></mubiao-his-trajectory>
|
||||
<multi-his-trajectory
|
||||
v-if="showMultiHisTrajCom"
|
||||
class="z-30 h-[260px]"
|
||||
></multi-his-trajectory>
|
||||
<mubiao-his-trajectory v-if="showHisTrajCom" class="z-30 h-[260px]"></mubiao-his-trajectory>
|
||||
<multi-his-trajectory v-if="showMultiHisTrajCom" class="z-30 h-[260px]"></multi-his-trajectory>
|
||||
<his-images v-if="showHisImageCom" class="z-30 h-[260px]"></his-images>
|
||||
</div>
|
||||
<details-modal></details-modal>
|
||||
@ -239,9 +215,11 @@ const showOrHideTextReport = () => {
|
||||
|
||||
.time-bg {
|
||||
background-color: rgba(26, 34, 41, 0.4);
|
||||
|
||||
.time-container {
|
||||
@apply flex items-center justify-center gap-2;
|
||||
font-family: Digital;
|
||||
|
||||
.time-title {
|
||||
@apply h-6 border border-[#29baf1] px-2 text-sm leading-5 text-[#29baf1];
|
||||
}
|
||||
@ -256,31 +234,39 @@ const showOrHideTextReport = () => {
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
@apply absolute left-[50%] top-3 z-20 h-20 text-5xl font-bold tracking-[18px];
|
||||
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@keyframes gradientAnimation {
|
||||
0% {
|
||||
background-position: 200% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.header-bg {
|
||||
background: url('./header.svg') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
@apply relative z-20 w-h-full;
|
||||
|
||||
&-wrapper {
|
||||
@apply absolute h-full;
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
.left-panel-content {
|
||||
@apply absolute right-[-37px] top-[78px];
|
||||
}
|
||||
@ -312,6 +298,7 @@ const showOrHideTextReport = () => {
|
||||
.btn-transform {
|
||||
transition: transform 0.8s ease;
|
||||
}
|
||||
|
||||
.btn-transform-pos {
|
||||
transform: translateX(99%);
|
||||
}
|
||||
@ -386,5 +373,4 @@ const showOrHideTextReport = () => {
|
||||
// // background: var(--gradient-bg);
|
||||
// // background-clip: text;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
// }</style>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { difference } from 'lodash'
|
||||
import { useWebSocket } from '@vueuse/core'
|
||||
import { useTree } from '@/utils/tree'
|
||||
import { useHjPolygon } from './hangjingPolygon'
|
||||
interface IPolygonData {
|
||||
id: string
|
||||
@ -6,6 +8,8 @@ interface IPolygonData {
|
||||
color?: string
|
||||
}
|
||||
|
||||
const { filterTreeNodeByField } = useTree()
|
||||
|
||||
const hangjingMap: Map<string, any> = new Map()
|
||||
|
||||
const { addHangjingPolygon, removeHangjingPolygon } = useHjPolygon(hangjingMap)
|
||||
@ -13,8 +17,8 @@ export const useHangjing = () => {
|
||||
onMounted(() => {
|
||||
initWebSocket()
|
||||
})
|
||||
const addHangjing = (data: Record<string, any>[]) => {
|
||||
addHangjingPolygon(data)
|
||||
const addHangjing = (ids, hangjingData) => {
|
||||
addHangjingPolygon(ids, hangjingData)
|
||||
}
|
||||
|
||||
const removeHangjing = (id: string) => {
|
||||
|
34
src/views/Hangjing/hooks/hangjingDetail.jsx
Normal file
34
src/views/Hangjing/hooks/hangjingDetail.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
import { useHangjingStyle } from './hangjingStyle'
|
||||
|
||||
const { renderStyleContent } = useHangjingStyle()
|
||||
export const useHangjingDetail = () => {
|
||||
return { showDetailsModal }
|
||||
}
|
||||
|
||||
function renderDetailsContent(data) {
|
||||
return () => (
|
||||
<div class="detail-container">
|
||||
<div class="detail-item-title">基本信息</div>
|
||||
<div>
|
||||
{Object.keys(data)
|
||||
.filter(key => key !== 'geom')
|
||||
.map(key => (
|
||||
<div>
|
||||
{key}:{data[key]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div class="detail-item-title">样式配置</div>
|
||||
{renderStyleContent(data)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function showDetailsModal(title, data) {
|
||||
openDetailsModal({
|
||||
titleString: title,
|
||||
contentSlot: renderDetailsContent(data),
|
||||
})
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import { h } from 'vue'
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
|
||||
export const useHangjingDetail = () => {
|
||||
return { showDetailsModal }
|
||||
}
|
||||
|
||||
function renderDetailsContent(data: Record<string, any>) {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
Object.keys(data).map(key => h('div', {}, `${key}:${data[key]}`))
|
||||
)
|
||||
}
|
||||
|
||||
function showDetailsModal(title: string, data: Record<string, any>) {
|
||||
openDetailsModal({
|
||||
titleString: title,
|
||||
contentSlot: renderDetailsContent(data),
|
||||
})
|
||||
}
|
@ -5,37 +5,44 @@ import { difference } from 'lodash'
|
||||
import { polygonGradient } from '@/js/polygonGradient'
|
||||
import { polygonMaterial } from '@/js/polygon'
|
||||
import { centerOfMass } from '@turf/turf'
|
||||
import { useTree } from '@/utils/tree'
|
||||
|
||||
import { useHangjingPopup } from './hangjingPopup'
|
||||
|
||||
const { createPopup } = useHangjingPopup()
|
||||
const { filterTreeNodeByField } = useTree()
|
||||
|
||||
export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
||||
let subscriber: Subscriber | null = null
|
||||
|
||||
const colors = new Map()
|
||||
|
||||
function addHangjingPolygon(data: Record<string, any>[]) {
|
||||
function addHangjingPolygon(ids, data) {
|
||||
subscriber = new Subscriber(viewer, {
|
||||
pickResult: {
|
||||
enable: true,
|
||||
},
|
||||
})
|
||||
|
||||
const ids = data.map(item => item.id)
|
||||
// const ids = data.map(item => item.id)
|
||||
const addIds = difference(ids, [...polygonMap.keys()])
|
||||
const removeIds = difference([...polygonMap.keys()], ids)
|
||||
// 添加
|
||||
if (addIds.length > 0) {
|
||||
addIds.forEach(id => {
|
||||
const item = data.find(item => item.id === id)
|
||||
if (item) {
|
||||
if (item.zoneList.length > 0) {
|
||||
item.zoneList.forEach(zone => {
|
||||
const nodes = filterTreeNodeByField({
|
||||
treeData: data.value,
|
||||
params: addIds,
|
||||
paramName: 'dataId',
|
||||
})
|
||||
nodes.forEach(({ data: hjData, dataId: id }) => {
|
||||
// const item = data.find(item => item.id === id)
|
||||
if (hjData) {
|
||||
if (hjData.zoneList.length > 0) {
|
||||
hjData.zoneList.forEach(zone => {
|
||||
addPolygon(zone, id)
|
||||
})
|
||||
} else {
|
||||
addPolygon(item)
|
||||
addPolygon(hjData)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -51,12 +58,14 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
||||
}
|
||||
|
||||
function addPolygon(item, parentId: number | null = null) {
|
||||
const { id, geom } = item
|
||||
const { id, geom, title } = item
|
||||
const feature = parseWKT(geom)
|
||||
const position = feature.coordinates[0].map(pos => {
|
||||
return Cesium.Cartesian3.fromDegrees(...pos)
|
||||
})
|
||||
|
||||
const labels = addTextAlongCurve('Cesium中文垂直排列测试', position)
|
||||
|
||||
// console.log(item, id, position, 'id, position, color')
|
||||
// const randomColor =
|
||||
// '#' + Math.random().toString(16).substring(2, 8).padEnd(6, '0')
|
||||
@ -68,6 +77,7 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
||||
// if (zoneId) {
|
||||
// if()
|
||||
// }
|
||||
|
||||
const curId = parentId || id
|
||||
|
||||
if (!colors.has(curId)) {
|
||||
@ -214,3 +224,121 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
||||
removeHangjingPolygon,
|
||||
}
|
||||
}
|
||||
|
||||
function addTextAlongCurve(text, polygonPoints) {
|
||||
// 创建 Billboard 集合
|
||||
const billboardCollection = viewer.scene.primitives.add(
|
||||
new Cesium.BillboardCollection()
|
||||
)
|
||||
|
||||
// 创建文字绘制的辅助函数
|
||||
function createTextTexture(text, angle) {
|
||||
const canvas = document.createElement('canvas')
|
||||
const context = canvas.getContext('2d')
|
||||
|
||||
// 设置 Canvas 大小
|
||||
canvas.width = 20
|
||||
canvas.height = 20
|
||||
|
||||
// 设置文字样式
|
||||
context.font = '20px sans-serif' // 支持中文
|
||||
context.fillStyle = 'blue'
|
||||
context.textAlign = 'center'
|
||||
context.textBaseline = 'middle'
|
||||
|
||||
// 将文字绘制到 Canvas,并进行旋转
|
||||
context.clearRect(0, 0, canvas.width, canvas.height)
|
||||
context.save()
|
||||
|
||||
// 旋转文字
|
||||
context.translate(canvas.width / 2, canvas.height / 2)
|
||||
context.rotate(angle)
|
||||
context.fillText(text, 0, 0)
|
||||
|
||||
context.restore()
|
||||
|
||||
return canvas
|
||||
}
|
||||
|
||||
// 动态生成字符标注
|
||||
function generateLabels(cameraHeight) {
|
||||
billboardCollection.removeAll() // 清除之前的标注
|
||||
|
||||
let charIndex = 0 // 当前字符索引
|
||||
for (let i = 0; i < polygonPoints.length - 1; i++) {
|
||||
const start = polygonPoints[i] // 当前边的起点
|
||||
const end = polygonPoints[i + 1] // 当前边的终点
|
||||
|
||||
// 计算线段的方向向量
|
||||
const direction = Cesium.Cartesian3.subtract(
|
||||
end,
|
||||
start,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
Cesium.Cartesian3.normalize(direction, direction)
|
||||
|
||||
// 计算垂直于线段的方向向量
|
||||
const perpendicular = Cesium.Cartesian3.cross(
|
||||
direction,
|
||||
Cesium.Cartesian3.UNIT_Z, // 使用 Z 轴(垂直地球表面)计算垂直方向
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
Cesium.Cartesian3.normalize(perpendicular, perpendicular)
|
||||
|
||||
// 计算线段的长度
|
||||
const length = Cesium.Cartesian3.distance(start, end)
|
||||
|
||||
// 动态调整字符间隔,基于相机高度
|
||||
const baseSpacing = 50 // 基础字符间隔
|
||||
const charSpacing = Math.max((baseSpacing * cameraHeight) / 5000000, 30)
|
||||
|
||||
// 按字符间隔放置文字
|
||||
let distance = 0
|
||||
while (distance < length && charIndex < text.length) {
|
||||
// 计算字符的位置
|
||||
const fraction = distance / length // 当前字符在边上的位置比例
|
||||
const position = Cesium.Cartesian3.lerp(
|
||||
start,
|
||||
end,
|
||||
fraction,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
|
||||
// 计算旋转角度,使文字垂直于线段
|
||||
const angle = Math.atan2(perpendicular.y, perpendicular.x)
|
||||
|
||||
// 创建带旋转的文字纹理
|
||||
const canvas = createTextTexture(text[charIndex], angle)
|
||||
|
||||
// 添加 Billboard 显示文字
|
||||
billboardCollection.add({
|
||||
position: position,
|
||||
image: canvas, // 使用生成的文字纹理
|
||||
// pixelOffset: new Cesium.Cartesian2(10, 0),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
})
|
||||
|
||||
// 更新字符索引和距离
|
||||
charIndex++
|
||||
console.log(charSpacing)
|
||||
distance += charSpacing * 2000
|
||||
// distance *= 2
|
||||
console.log(distance)
|
||||
}
|
||||
|
||||
// 如果字符已用完,跳出循环
|
||||
if (charIndex >= text.length) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化标注(根据初始相机高度)
|
||||
generateLabels(viewer.camera.positionCartographic.height)
|
||||
|
||||
// 监听相机变化事件,动态更新字符间隔
|
||||
viewer.camera.changed.addEventListener(() => {
|
||||
const cameraHeight = viewer.camera.positionCartographic.height
|
||||
generateLabels(cameraHeight)
|
||||
})
|
||||
}
|
||||
|
115
src/views/Hangjing/hooks/hangjingStyle.jsx
Normal file
115
src/views/Hangjing/hooks/hangjingStyle.jsx
Normal file
@ -0,0 +1,115 @@
|
||||
import {
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NColorPicker,
|
||||
NSelect,
|
||||
NInputNumber,
|
||||
NButton,
|
||||
} from 'naive-ui'
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
|
||||
const styleForm = ref({
|
||||
fontFamily: '微软雅黑',
|
||||
fontSize: 14,
|
||||
textColor: 'rgba(255,255,0,1)',
|
||||
polygonColor: 'rgba(255,0,0,0.3)',
|
||||
lineColor: 'rgba(255,0,0,1)',
|
||||
lineWidth: 1,
|
||||
lineType: 'solid',
|
||||
})
|
||||
export const useHangjingStyle = () => {
|
||||
return { renderStyleContent, showStyleModal }
|
||||
}
|
||||
|
||||
function renderStyleContent(data) {
|
||||
return (
|
||||
<div class="flex flex-col gap-2 pt-4">
|
||||
<NForm
|
||||
model={styleForm.value}
|
||||
labelPlacement="left"
|
||||
//labelWidth: 100,
|
||||
rules={{
|
||||
fontFamily: [{ required: true, message: '请选择文字字体' }],
|
||||
fontSize: [{ required: true, message: '请选择文字大小' }],
|
||||
textColor: [{ required: true, message: '请选择文字颜色' }],
|
||||
polygonColor: [{ required: true, message: '请选择区域颜色' }],
|
||||
lineColor: [{ required: true, message: '请选择边框颜色' }],
|
||||
lineType: [{ required: true, message: '请选择边框类型' }],
|
||||
lineWidth: [{ required: true, message: '请选择边框宽度' }],
|
||||
}}
|
||||
class="grid grid-cols-2 gap-4"
|
||||
>
|
||||
<NFormItem label="文字字体" path="fontFamily">
|
||||
<NSelect
|
||||
v-model:value={styleForm.value.fontFamily}
|
||||
options={[
|
||||
{ label: '微软雅黑', value: '微软雅黑' },
|
||||
{ label: '楷体', value: '楷体' },
|
||||
{ label: '宋体', value: '宋体' },
|
||||
]}
|
||||
></NSelect>
|
||||
</NFormItem>
|
||||
<NFormItem label="边框颜色" path="lineColor">
|
||||
<NColorPicker
|
||||
v-model:value={styleForm.value.lineColor}
|
||||
></NColorPicker>
|
||||
</NFormItem>
|
||||
<NFormItem label="文字大小" path="fontSize">
|
||||
<NInputNumber
|
||||
v-model:value={styleForm.value.fontSize}
|
||||
min={1}
|
||||
max={100}
|
||||
></NInputNumber>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="边框宽度" path="lineWidth">
|
||||
<NInputNumber
|
||||
v-model:value={styleForm.value.lineWidth}
|
||||
min={1}
|
||||
max={100}
|
||||
></NInputNumber>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="文字颜色" path="textColor">
|
||||
<NColorPicker
|
||||
v-model:value={styleForm.value.textColor}
|
||||
></NColorPicker>
|
||||
</NFormItem>
|
||||
<NFormItem label="边框类型" path="lineType">
|
||||
<NSelect
|
||||
v-model:value={styleForm.value.lineType}
|
||||
options={[
|
||||
{ label: '实线', value: 'solid' },
|
||||
{ label: '虚线', value: 'dashed' },
|
||||
]}
|
||||
></NSelect>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="区域颜色" path="polygonColor">
|
||||
<NColorPicker
|
||||
v-model:value={styleForm.value.polygonColor}
|
||||
></NColorPicker>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<div class="flex justify-end gap-2">
|
||||
<NButton type="primary" onClick={updateStyle}>
|
||||
确定
|
||||
</NButton>
|
||||
<NButton onClick={updateStyle}>取消</NButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function showStyleModal(title, data) {
|
||||
openDetailsModal({
|
||||
titleString: title,
|
||||
contentSlot: renderStyleContent(data),
|
||||
})
|
||||
}
|
||||
|
||||
function updateStyle() {
|
||||
// TODO: 更新样式
|
||||
}
|
@ -9,9 +9,10 @@ import { getHangjing } from '@/api/Hangjing'
|
||||
import { useHangjing } from './hooks/hangjing'
|
||||
import { convertToWKT } from '@/utils/parseWKT'
|
||||
import { useHangjingDetail } from './hooks/hangjingDetail'
|
||||
import { useHangjingStyle } from './hooks/hangjingStyle'
|
||||
// import { useHangjingStyle } from './hooks/hangjingStyle'
|
||||
import Tree from '@/components/Tree/index.vue'
|
||||
|
||||
|
||||
const { addHangjing } = useHangjing()
|
||||
|
||||
const timeRange = ref(null)
|
||||
|
||||
@ -101,70 +102,45 @@ const drawArea = () => {
|
||||
}
|
||||
|
||||
const { showDetailsModal } = useHangjingDetail()
|
||||
const { showStyleModal } = useHangjingStyle()
|
||||
const columns = [
|
||||
{
|
||||
type: 'selection',
|
||||
// disabled(row) {
|
||||
// return row.name === 'Edward King 3'
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
key: 'hjType',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'actions',
|
||||
render(row) {
|
||||
return h('div', { class: 'flex gap-2' }, [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
size: 'small',
|
||||
onClick: () => showDetailsModal(`${row.title}详情`, row),
|
||||
},
|
||||
{ default: () => '详情' }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
size: 'small',
|
||||
onClick: () => showStyleModal(`${row.title}样式配置`, row),
|
||||
},
|
||||
{ default: () => '样式配置' }
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
]
|
||||
// const { showStyleModal } = useHangjingStyle()
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const paginationReactive = reactive({
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
showSizePicker: true,
|
||||
pageSizes: [20, 50, 100],
|
||||
onChange: page => {
|
||||
paginationReactive.page = page
|
||||
},
|
||||
onUpdatePageSize: pageSize => {
|
||||
paginationReactive.pageSize = pageSize
|
||||
paginationReactive.page = 1
|
||||
},
|
||||
const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
return option.data ? h('div', { class: 'flex items-center gap-2 pr-2' }, [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
size: 'tiny',
|
||||
type: 'info',
|
||||
onClick: () => showDetailsModal(`${option.data.title}详情`, option.data),
|
||||
},
|
||||
{ default: () => '详情' }
|
||||
),
|
||||
// h(
|
||||
// NButton,
|
||||
// {
|
||||
// text: true,
|
||||
// size: 'tiny',
|
||||
// type: 'info',
|
||||
// onClick: () => showStyleModal(`${option.data.title}样式配置`, option.data),
|
||||
// },
|
||||
// { default: () => '样式配置' }
|
||||
// ),
|
||||
]) : null
|
||||
}
|
||||
|
||||
const checkedKeys = ref<Array<string | number>>([])
|
||||
|
||||
const { addHangjing } = useHangjing()
|
||||
|
||||
watch(checkedKeys, val => {
|
||||
addHangjing(val, data)
|
||||
})
|
||||
|
||||
const handleCheck = (rowKeys: DataTableRowKey[]) => {
|
||||
const selectedList = data.value.filter(item => rowKeys.includes(item.id))
|
||||
|
||||
addHangjing(selectedList)
|
||||
}
|
||||
|
||||
@ -174,7 +150,7 @@ const getHangjingData = async (params = {}) => {
|
||||
const { code, data: resData } = await getHangjing(params)
|
||||
|
||||
if (code === '200') {
|
||||
data.value = resData || []
|
||||
data.value = [resData]
|
||||
}
|
||||
isLoading.value = false
|
||||
}
|
||||
@ -204,14 +180,8 @@ const clearSelected = () => {
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 w-h-full" v-loading="isLoading">
|
||||
<n-date-picker
|
||||
v-model:formatted-value="timeRange"
|
||||
clearable
|
||||
type="datetimerange"
|
||||
:shortcuts="rangeShortcuts"
|
||||
:update-value-on-close="true"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
/>
|
||||
<n-date-picker v-model:formatted-value="timeRange" clearable type="datetimerange" :shortcuts="rangeShortcuts"
|
||||
:update-value-on-close="true" format="yyyy-MM-dd HH:mm:ss" />
|
||||
<div class="flex gap-2">
|
||||
<n-input class="w-auto" v-model:value="searchTitle" />
|
||||
<n-select class="w-40" v-model:value="type" :options="typeOptions" />
|
||||
@ -239,15 +209,12 @@ const clearSelected = () => {
|
||||
<n-button type="primary" @click="searchHangjing">检索</n-button>
|
||||
</n-input-group>
|
||||
</div>
|
||||
<n-data-table
|
||||
class="flex-1"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:row-key="(rowData: Record<string, any>) => rowData.id"
|
||||
flex-height
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/>
|
||||
<!-- <n-data-table class="flex-1" :columns="columns" :data="data" :row-key="(rowData: Record<string, any>) => rowData.id"
|
||||
flex-height @update:checked-row-keys="handleCheck" /> -->
|
||||
<!-- :pagination="paginationReactive" -->
|
||||
|
||||
<tree :data="data" :key-field="'dataId'" :label-field="'nodeName'" v-model:checked="checkedKeys" showSearch
|
||||
:renderSuffix="renderSuffix" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -60,7 +60,7 @@ export const useMubiao = () => {
|
||||
// 获取目标坐标
|
||||
const mbPos = await getMubiaoCurPos(targetIdList)
|
||||
|
||||
console.log('mbPos', mbPos)
|
||||
// console.log('mbPos', mbPos)
|
||||
|
||||
nodes.forEach(({ data, dataId: id }: IMubiao) => {
|
||||
const { target_time, target_geom } =
|
||||
@ -105,6 +105,7 @@ export const useMubiao = () => {
|
||||
id,
|
||||
targetType,
|
||||
})
|
||||
|
||||
const mubiaoEntity = viewer.entities.add({
|
||||
name: id,
|
||||
position: Cesium.Cartesian3.fromDegrees(...(position as number[])),
|
||||
|
127
src/views/Mubiao/hooks/mubiaoDetail.jsx
Normal file
127
src/views/Mubiao/hooks/mubiaoDetail.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
// import { h } from 'vue'
|
||||
import { NDataTable, NInputNumber, NSwitch } from 'naive-ui'
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
export const useMubiaoDetail = () => {
|
||||
return { showDetailsMubiao }
|
||||
}
|
||||
|
||||
const detectingLoadColumns = [
|
||||
{
|
||||
title: '垂直起始角',
|
||||
key: 'minimumClock',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return (
|
||||
<NInputNumber
|
||||
v-model:value={row.minimumClock}
|
||||
// disabled={['光学', '雷达'].includes(row.type)}
|
||||
></NInputNumber>
|
||||
)
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: '垂直终止角',
|
||||
// key: 'maximumClock',
|
||||
// width: 180,
|
||||
// render(row) {
|
||||
// return (
|
||||
// <NInputNumber
|
||||
// v-model:value={row.maximumClock}
|
||||
// // disabled={['光学', '雷达'].includes(row.type)}
|
||||
// ></NInputNumber>
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: '水平起始角',
|
||||
key: 'minimumCone',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return (
|
||||
<NInputNumber
|
||||
v-model:value={row.minimumCone}
|
||||
// disabled={['光学', '雷达'].includes(row.type)}
|
||||
></NInputNumber>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '水平终止角',
|
||||
key: 'maximumCone',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return (
|
||||
<NInputNumber
|
||||
v-model:value={row.maximumCone}
|
||||
// disabled={['光学', '雷达'].includes(row.type)}
|
||||
></NInputNumber>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '半径',
|
||||
key: 'radius',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return (
|
||||
<NInputNumber
|
||||
v-model:value={row.radius}
|
||||
// disabled={['光学', '雷达'].includes(row.type)}
|
||||
></NInputNumber>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '是否开启',
|
||||
key: 'status',
|
||||
render(row) {
|
||||
return <NSwitch v-model:value={row.status}></NSwitch>
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const data = ref([
|
||||
{
|
||||
id: 3,
|
||||
radius: 5000,
|
||||
minimumClock: 20.0,
|
||||
maximumClock: 110.0,
|
||||
minimumCone: 20.0,
|
||||
maximumCone: 90.0,
|
||||
height: 100,
|
||||
status: true,
|
||||
},
|
||||
])
|
||||
|
||||
function renderMubiaoDetailsContent(mbData) {
|
||||
// return h(
|
||||
// 'div',
|
||||
// {},
|
||||
// Object.keys(mbData).map(key => h('div', {}, `${key}:${mbData[key]}`))
|
||||
// )
|
||||
return () => (
|
||||
<div class="detail-container">
|
||||
<div class="detail-item-title">基本信息</div>
|
||||
<div>
|
||||
{Object.keys(mbData).map(key => (
|
||||
<div>
|
||||
{key}:{mbData[key]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div class="detail-item-title">探测载荷</div>
|
||||
<NDataTable
|
||||
key={row => row.id}
|
||||
columns={detectingLoadColumns}
|
||||
data={data.value}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
function showDetailsMubiao(mbData) {
|
||||
openDetailsModal({
|
||||
titleString: 'zb详情',
|
||||
contentSlot: renderMubiaoDetailsContent(mbData),
|
||||
})
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { h } from 'vue'
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
export const useMubiaoDetail = () => {
|
||||
return { showDetailsMubiao }
|
||||
}
|
||||
function renderMubiaoDetailsContent(mbData: Record<string, any>) {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
Object.keys(mbData).map(key => h('div', {}, `${key}:${mbData[key]}`))
|
||||
)
|
||||
}
|
||||
function showDetailsMubiao(mbData: Record<string, any>) {
|
||||
openDetailsModal({
|
||||
titleString: 'zb详情',
|
||||
contentSlot: renderMubiaoDetailsContent(mbData),
|
||||
})
|
||||
}
|
18
src/views/Mubiao/hooks/mubiaoLoad.jsx
Normal file
18
src/views/Mubiao/hooks/mubiaoLoad.jsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
const { openDetailsModal } = useModal()
|
||||
export const useMubiaoDetail = () => {
|
||||
return { showDetailsMubiao }
|
||||
}
|
||||
function renderMubiaoDetailsContent(mbData) {
|
||||
return (
|
||||
<>
|
||||
<div></div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
function showDetailsMubiao(mbData) {
|
||||
openDetailsModal({
|
||||
titleString: '载荷',
|
||||
contentSlot: renderMubiaoDetailsContent(mbData),
|
||||
})
|
||||
}
|
@ -51,6 +51,16 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
},
|
||||
{ default: () => '详情' }
|
||||
),
|
||||
// h(
|
||||
// NButton,
|
||||
// {
|
||||
// text: true,
|
||||
// size: 'tiny',
|
||||
// type: 'info',
|
||||
// onClick: () => showDetailsMubiao(option.data),
|
||||
// },
|
||||
// { default: () => '载荷' }
|
||||
// ),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
@ -89,14 +99,8 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/> -->
|
||||
<div class="h-0 flex-1">
|
||||
<tree
|
||||
:data="data"
|
||||
:key-field="'dataId'"
|
||||
:label-field="'nodeName'"
|
||||
v-model:checked="checkedKeys"
|
||||
showSearch
|
||||
:renderSuffix="renderSuffix"
|
||||
/>
|
||||
<tree :data="data" :key-field="'dataId'" :label-field="'nodeName'" v-model:checked="checkedKeys" showSearch
|
||||
:renderSuffix="renderSuffix" />
|
||||
</div>
|
||||
|
||||
<!-- :nodeProps="nodeProps" -->
|
||||
|
127
src/views/Satellite/components/SatDetail.jsx
Normal file
127
src/views/Satellite/components/SatDetail.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
import { useModal } from '@/views/Content/hooks/modal'
|
||||
import { NDataTable, NSelect, NInputNumber, NSwitch } from 'naive-ui'
|
||||
|
||||
const { openDetailsModal } = useModal()
|
||||
|
||||
const detectingLoadColumns = [
|
||||
// {
|
||||
// title: '载荷类型',
|
||||
// key: 'type',
|
||||
// render(row) {
|
||||
// return (
|
||||
// <NSelect
|
||||
// v-model:value={row.type}
|
||||
// options={[
|
||||
// { label: '电子', value: '电子' },
|
||||
// { label: '雷达', value: '雷达' },
|
||||
// { label: '光学', value: '光学' },
|
||||
// ]}
|
||||
// ></NSelect>
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: '开合角',
|
||||
key: 'angle',
|
||||
width: 120,
|
||||
render(row) {
|
||||
return (
|
||||
<NInputNumber
|
||||
v-model:value={row.angle}
|
||||
max={120}
|
||||
min={0}
|
||||
// disabled={['光学', '雷达'].includes(row.type)}
|
||||
></NInputNumber>
|
||||
)
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: '水平半角',
|
||||
// key: 'xHalfAngle',
|
||||
// width: 120,
|
||||
// render(row) {
|
||||
// return (
|
||||
// <NInputNumber
|
||||
// v-model:value={row.angle}
|
||||
// max={120}
|
||||
// min={0}
|
||||
// disabled={['电子'].includes(row.type)}
|
||||
// ></NInputNumber>
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: '垂直半角',
|
||||
// key: 'yHalfAngle',
|
||||
// width: 120,
|
||||
// render(row) {
|
||||
// return (
|
||||
// <NInputNumber
|
||||
// v-model:value={row.angle}
|
||||
// max={120}
|
||||
// min={0}
|
||||
// disabled={['电子'].includes(row.type)}
|
||||
// ></NInputNumber>
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: '是否开启',
|
||||
key: 'status',
|
||||
render(row) {
|
||||
return <NSwitch v-model:value={row.status}></NSwitch>
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const data = ref([
|
||||
{
|
||||
id: 3,
|
||||
angle: 30,
|
||||
// xHalfAngle: 20,
|
||||
// yHalfAngle: 25,
|
||||
// type: '电子',
|
||||
status: true,
|
||||
},
|
||||
])
|
||||
|
||||
export function showDetailsSatellite(option) {
|
||||
openDetailsModal({
|
||||
titleString: '' + option.name + ' 详情',
|
||||
contentSlot: () => (
|
||||
<div class="detail-container">
|
||||
<div class="detail-item-title">基本信息</div>
|
||||
<div>
|
||||
<div>卫星编号:{option.name}</div>
|
||||
<div class="flex">
|
||||
<div>两行根数:</div>
|
||||
<div>
|
||||
<p>{option.tle.split('\n')[0]}</p>
|
||||
<p>{option.tle.split('\n')[1]}</p>
|
||||
<p>{option.tle.split('\n')[2]}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item-title">探测载荷</div>
|
||||
<NDataTable
|
||||
key={row => row.id}
|
||||
columns={detectingLoadColumns}
|
||||
data={data.value}
|
||||
/>
|
||||
<div class="detail-item-title">通信载荷</div>
|
||||
{/* <NDataTable
|
||||
key={row => row.id}
|
||||
columns={detectingLoadColumns}
|
||||
data={data.value}
|
||||
/> */}
|
||||
</div>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
// export default defineComponent({
|
||||
// name: 'SatDetail',
|
||||
// setup(props) {
|
||||
// return () => <></>
|
||||
// },
|
||||
// })
|
@ -2,11 +2,10 @@ import SatelliteEntity from '@/js/SatelliteEntity'
|
||||
import { difference } from 'lodash'
|
||||
import { useEntity } from '@/hooks/entity'
|
||||
// import CreateFrustum from '@/js/Sensor'
|
||||
import * as CesiumSensorVolumes from 'cesium-sensors-es6'
|
||||
|
||||
interface ISatellite {
|
||||
name: string
|
||||
id: number | string
|
||||
id: string
|
||||
tle: string
|
||||
}
|
||||
|
||||
@ -18,6 +17,17 @@ interface IBaseFilterParam {
|
||||
const satelliteList = ref<ISatellite[]>([])
|
||||
|
||||
const { satelliteMap } = useEntity()
|
||||
|
||||
const showPoint = ref(true)
|
||||
function showPointUnderSat(id?: string) {
|
||||
if (id) {
|
||||
satelliteMap.get(id).underPoint = true
|
||||
} else {
|
||||
;[...satelliteMap.values()].forEach(satellite => {
|
||||
satellite.underPoint = showPoint.value
|
||||
})
|
||||
}
|
||||
}
|
||||
export function useSatellite() {
|
||||
function addSatellites(ids: Array<string | number>) {
|
||||
const addIds = difference(ids, [...satelliteMap.keys()])
|
||||
@ -33,6 +43,9 @@ export function useSatellite() {
|
||||
nodes.forEach(node => {
|
||||
const entity = addSatellite(node)
|
||||
satelliteMap.set(node.id, entity)
|
||||
if (showPoint.value) {
|
||||
showPointUnderSat(node.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -47,9 +60,10 @@ export function useSatellite() {
|
||||
// const result = viewer.entities.add(cesiumSateEntity)
|
||||
|
||||
setTimeout(() => {
|
||||
satellite.sensorType = Math.random() > 0.5 ? 'conic' : 'rectangle'
|
||||
// satellite.sensorType = Math.random() > 0.5 ? 'conic' : 'rectangle'
|
||||
satellite.sensor = true
|
||||
}, 1000)
|
||||
|
||||
// viewer.clock.multiplier = 100
|
||||
|
||||
return satellite
|
||||
@ -85,7 +99,7 @@ export function useSatellite() {
|
||||
}
|
||||
}
|
||||
|
||||
return { satelliteList, addSatellites }
|
||||
return { satelliteList, addSatellites, showPoint, showPointUnderSat }
|
||||
}
|
||||
|
||||
function filterTreeNodeByField({
|
||||
|
@ -4,6 +4,7 @@ import { NButton } from 'naive-ui'
|
||||
import Tree from '@/components/Tree/index.vue'
|
||||
import { getSatellite } from '@/api/Satellite'
|
||||
import { useSatellite } from './hooks/satellite'
|
||||
import { showDetailsSatellite } from './components/SatDetail'
|
||||
|
||||
const { satelliteList, addSatellites } = useSatellite()
|
||||
|
||||
@ -35,7 +36,7 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
text: true,
|
||||
size: 'tiny',
|
||||
type: 'info',
|
||||
// onClick: () => showDetailsSatellite(option),
|
||||
onClick: () => showDetailsSatellite(option),
|
||||
},
|
||||
{ default: () => '详情' }
|
||||
),
|
||||
@ -67,5 +68,6 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
|
||||
showSearch
|
||||
:renderSuffix="renderSuffix"
|
||||
/>
|
||||
<!-- <sat-detail /> -->
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user