媒体库-历史路径展示-第一版

main
滕嵩 2 months ago
parent ca57e58fab
commit 5a7ac9539c

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

@ -49,20 +49,20 @@
},
],
},
{
id: 'achievementModel',
name: '成果模型',
children: [
{
id: '2d',
name: '二维正摄',
},
{
id: '3d',
name: '三维模型',
},
],
},
// {
// id: 'achievementModel',
// name: '',
// children: [
// {
// id: '2d',
// name: '',
// },
// {
// id: '3d',
// name: '',
// },
// ],
// },
// {
// id: 'gas',
// name: '',

@ -1,2 +1,4 @@
// export { default as PreviewImage } from './previewImage.vue';
// export { default as PreviewVideo } from './previewVideo.vue';
export { default as RecordList } from './recordList.vue';
export { default as RecordNewBuild } from './recordNewBuild.vue';
export { default as ModalChooseObject } from './modalChooseObject.vue';
export { default as ImageContrast } from './imageContrast.vue';

@ -1,269 +0,0 @@
<template>
<div class="comparisonModal">
<div class="closeButton">
<LeftOutlined style="color: white; font-size: 20px; margin-right: 10px" />
<div
class="changeButton"
:style="{
background: typeOpen ? '#2A7DC9' : '#5D5F61',
}"
@click="typeOpen = !typeOpen"
>
变化检测
<CaretUpOutlined v-if="typeOpen" />
<CaretDownOutlined v-if="!typeOpen" />
</div>
</div>
<div class="topDiv"> 照片 AI 变化检测 Beta</div>
<div class="bodyDiv">
<div v-if="typeOpen" class="jiancejiluDiv">
<div class="jiancejiluDiv_title">
<span style="margin-left: 15px; font-size: 16px; font-weight: bold">检测记录</span>
</div>
<div class="jiancejiluDiv_filter">
<div class="jiancejiluDiv_filter_row1">
<a-range-picker v-model:value="value1" />
<SearchOutlined
class="SearchOutlined"
:style="{
color: showSearch ? '#2D8CF0' : '#ffffff',
}"
@click="showSearch = !showSearch"
/>
</div>
<div class="jiancejiluDiv_filter_row2" v-if="showSearch">
<a-input
v-model:value="value2"
placeholder="按检测名称搜索"
style="width: 90%"
></a-input>
</div>
</div>
<div class="jiancejiluDiv_dropdown">
<a-dropdown>
<a class="jiancejiluDiv_dropdown_showtitle" @click.prevent>
{{ dropdownSelect }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item>
<a @click="dropdownSelect = '所有航点航线'">所有航点航线</a>
</a-menu-item>
<a-menu-item>
<a @click="dropdownSelect = '巡检航点航线'">巡检航点航线</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
<div
class="imageDiv"
:style="{
width: typeOpen ? 'calc(100% - 360px)' : '100%',
}"
>
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗1</div>
<div class="imageDiv_Comparison_1">1</div>
</div>
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗2</div>
<div class="imageDiv_Comparison_2">2</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import {
LeftOutlined,
CaretDownOutlined,
CaretUpOutlined,
SearchOutlined,
} from '@ant-design/icons-vue';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeComparisonModal']);
const typeOpen = ref(true);
const showSearch = ref(false);
const dropdownSelect = ref('所有航点航线');
</script>
<style lang="less" scoped>
.comparisonModal {
position: relative;
width: 100%;
height: 920px;
background: #000000;
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
//
.closeButton {
position: absolute;
top: 12px;
left: 0px;
height: 40px;
width: 200px;
display: flex;
align-items: center;
justify-content: center;
.changeButton {
height: 30px;
width: 120px;
border-radius: 5px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
}
//
.topDiv {
background: #101010;
color: #ffffff;
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
//
.bodyDiv {
background: #000000;
width: 100%;
display: flex;
height: calc(100% - 60px);
//
.jiancejiluDiv {
position: relative;
background: #232323;
color: #ffffff;
width: 360px;
height: 100%;
border-radius: 10px;
//
.jiancejiluDiv_title {
position: relative;
top: 0px;
left: 0px;
color: #ffffff;
height: 50px;
width: 100%;
border-bottom: 1px solid #ffffff55;
display: flex;
align-items: center;
justify-content: flex-start;
}
//
.jiancejiluDiv_filter {
position: relative;
top: 0px;
left: 0px;
width: 100%;
height: auto;
margin-top: 10px;
margin-bottom: 10px;
display: block;
.jiancejiluDiv_filter_row1 {
display: flex;
align-items: center;
justify-content: center;
.SearchOutlined {
color: #ffffff;
font-size: 22px;
margin-left: 12px;
}
}
.jiancejiluDiv_filter_row2 {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
}
//
.jiancejiluDiv_dropdown {
position: relative;
top: 0px;
left: 0px;
.jiancejiluDiv_dropdown_showtitle {
color: #ffffff;
margin-left: 18px;
}
}
}
.imageDiv {
position: relative;
display: flex;
// width: 100%;
width: calc(100% - 360px);
gap: 5px;
.imageDiv_Comparison {
position: relative;
width: 50%;
height: 100%;
.imageDiv_Comparison_title {
position: relative;
color: #ffffff;
font-size: 20px;
margin-left: 35px;
margin-top: 8px;
}
.imageDiv_Comparison_1 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0px;
top: 30px;
background: #2773c3;
width: 30px;
height: 25px;
border-radius: 10px 0px 0px 10px;
}
.imageDiv_Comparison_2 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0px;
top: 30px;
background: #cb8824;
width: 30px;
height: 25px;
border-radius: 0px 10px 10px 0px;
}
.imageDiv_Comparison_2 {
}
}
}
}
}
</style>

@ -0,0 +1,348 @@
<template>
<div class="imageDiv_">
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗1</div>
<div class="imageDiv_Comparison_1">1</div>
<div
ref="mouseCanvasRef"
@mousedown="onMouseDown"
:style="{
position: 'relative',
transform: `scale(${scale}) rotate(${rotationAngle}deg)`,
transition: 'transform 0.2s',
width: `100%`,
height: `100%`,
background: `url(${nowImageGroup?.show1?.url}) no-repeat`,
}"
/>
</div>
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗2</div>
<div class="imageDiv_Comparison_2">2</div>
<div
ref="mouseCanvasRef"
@mousedown="onMouseDown"
:style="{
position: 'relative',
transform: `scale(${scale}) rotate(${rotationAngle}deg)`,
transition: 'transform 0.2s',
width: `100%`,
height: `100%`,
background: `url(${nowImageGroup?.show2?.url}) no-repeat`,
}"
/>
</div>
<div class="imageDiv_BottomButton">
<div class="bottomButtonList">
<!-- 上一个 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>上一个</span>
</template>
<LeftOutlined @click="clickLeftOrRightButton('left')" />
</a-tooltip>
</div>
<div class="button">
{{ nowImageGroupIndex }}
/
{{ nowRecord.imageGroup.length }}
</div>
<!-- 下一个 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>下一个</span>
</template>
<RightOutlined @click="clickLeftOrRightButton('right')" />
</a-tooltip>
</div>
<span style="color: gray; margin-left: 5px; margin-right: 5px">|</span>
<!-- 放大 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>放大</span>
</template>
<ZoomInOutlined @click="zoomIn" />
</a-tooltip>
</div>
<!-- 缩小 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>缩小</span>
</template>
<ZoomOutOutlined @click="zoomOut" />
</a-tooltip>
</div>
<!-- 顺时针旋转 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>顺时针旋转</span>
</template>
<RotateRightOutlined @click="rotateClockwise" />
</a-tooltip>
</div>
<!-- 逆时针旋转 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>逆时针旋转</span>
</template>
<RotateLeftOutlined @click="rotateCounterClockwise" />
</a-tooltip>
</div>
<!-- 刷新 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>刷新</span>
</template>
<RedoOutlined @click="refresh" />
</a-tooltip>
</div>
<!-- 锁定 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>锁定</span>
</template>
<LockOutlined @click="lockOut" />
</a-tooltip>
</div>
<!-- 解锁 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>解锁</span>
</template>
<UnlockOutlined @click="unlockOut" />
</a-tooltip>
</div>
<span style="color: gray; margin-left: 5px; margin-right: 5px">|</span>
<!-- 涂鸦标记 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>涂鸦标记</span>
</template>
<EditOutlined @click="unlockOut" />
</a-tooltip>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {
RightOutlined,
LeftOutlined,
ZoomOutOutlined,
ZoomInOutlined,
RotateLeftOutlined,
RotateRightOutlined,
EditOutlined,
FontColorsOutlined,
RedoOutlined,
LockOutlined,
UnlockOutlined,
} from '@ant-design/icons-vue';
import { ref, watch, onMounted, computed } from 'vue';
import { cloneDeep } from 'lodash-es';
const props = defineProps(['recordList', 'nowRecord']);
const emit = defineEmits(['']);
// -----------------------------------
const nowImageGroup: any = ref();
const nowImageGroupIndex = ref(1);
//
function clickLeftOrRightButton(direction) {
const imageGroupList = props.nowRecord.imageGroup;
const nowImageListId = cloneDeep(nowImageGroup.value.id);
for (let index = 0; index < imageGroupList.length; index++) {
if (imageGroupList[index].id == nowImageListId) {
if (direction == 'left') {
if (index == 0) {
nowImageGroup.value = imageGroupList[imageGroupList.length - 1];
} else {
nowImageGroup.value = imageGroupList[index - 1];
}
}
if (direction == 'right') {
if (index == imageGroupList.length - 1) {
nowImageGroup.value = imageGroupList[0];
} else {
nowImageGroup.value = imageGroupList[index + 1];
}
}
getNowImageGroup();
}
}
}
//
function getNowImageGroup() {
const nowRecordImageGroupList = props.nowRecord.imageGroup.map((item) => item.id);
nowImageGroupIndex.value = nowRecordImageGroupList.indexOf(nowImageGroup.value.id) + 1;
}
// ----------------------------------
onMounted(() => {
nowImageGroup.value = props.nowRecord.imageGroup[0];
console.log(nowImageGroup.value);
getNowImageGroup();
});
watch(
() => props.nowRecord,
() => {
nowImageGroup.value = props.nowRecord.imageGroup[0];
},
{
deep: true,
},
);
//
const getImageWidthAndHeight = computed(() => {
let width = 1300;
let height = 800;
if (props.nowRecord.imageGroup.width > 1300 || props.nowRecord.height > 800) {
if (props.nowRecord.width / 1300 > props.nowRecord.height / 800) {
width = 1300;
height = (props.nowRecord.height / props.nowRecord.width) * 1300;
} else {
height = 800;
width = (props.nowRecord.width / props.nowRecord.height) * 800;
}
} else {
width = props.nowRecord.width;
height = props.nowRecord.height;
}
return [width, height];
});
// -----------------------------------
const scale = ref(1);
const rotationAngle = ref(0);
//
function zoomIn() {
if (scale.value < 3) {
// 3
scale.value += 0.1;
}
}
//
function zoomOut() {
if (scale.value > 0.5) {
// 0.5
scale.value -= 0.1;
}
}
//
function rotateClockwise() {
rotationAngle.value += 90; // 90
}
//
function rotateCounterClockwise() {
rotationAngle.value -= 90; // -90
}
//
function refresh() {
scale.value = 1;
rotationAngle.value = 0;
}
</script>
<style lang="less" scoped>
.imageDiv_ {
position: relative;
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
// gap: 4px;
}
.imageDiv_Comparison {
position: relative;
width: calc(50% - 5px);
height: 92%;
outline: 1px solid #ffffff55;
margin-left: 2px;
margin-right: 2px;
display: flex;
align-items: center;
justify-content: center;
.imageDiv_Comparison_title {
position: absolute;
top: 8px;
left: 35px;
color: #ffffff;
font-size: 20px;
z-index: 200;
pointer-events: none;
}
.imageDiv_Comparison_1 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0px;
top: 30px;
background: #2773c3;
width: 30px;
height: 25px;
border-radius: 10px 0px 0px 10px;
z-index: 200;
}
.imageDiv_Comparison_2 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0px;
top: 30px;
background: #cb8824;
width: 30px;
height: 25px;
border-radius: 0px 10px 10px 0px;
z-index: 200;
}
}
.imageDiv_BottomButton {
display: flex;
align-items: center;
justify-content: center;
background: #101010;
// background: red;
height: calc(8% - 5px);
width: 100%;
.bottomButtonList {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 40px;
.button {
position: relative;
padding-left: 10px;
padding-right: 10px;
margin-left: 6px;
margin-right: 6px;
color: #ffffff;
font-size: 22px;
}
}
}
</style>

@ -0,0 +1,312 @@
<template>
<div class="comparisonModal">
<div class="closeButton">
<LeftOutlined
style="color: white; font-size: 20px; margin-right: 10px"
@click="closeComparisonModal"
/>
<div
class="changeButton"
:style="{
background: typeOpen ? '#2A7DC9' : '#5D5F61',
}"
@click="changeTypeOpen"
>
变化检测
<CaretUpOutlined v-if="typeOpen" />
<CaretDownOutlined v-if="!typeOpen" />
</div>
</div>
<div class="topDiv"> 照片 AI 变化检测 Beta</div>
<div class="bodyDiv">
<!-- 检测记录 -->
<div v-if="typeOpen" class="jiancejiluDiv">
<RecordList
v-if="!isNewBuildStatus"
:recordList="recordList"
:nowRecord="nowRecord"
@chooseNowRecord="chooseNowRecord"
@openIsNewBuildStatus="openIsNewBuildStatus"
/>
<RecordNewBuild v-if="isNewBuildStatus" @closeIsNewBuildStatus="closeIsNewBuildStatus" />
</div>
<!-- 对比部分 -->
<div
class="imageDiv"
:style="{
width: typeOpen ? 'calc(100% - 360px)' : '100%',
}"
>
<ImageContrast :recordList="recordList" :nowRecord="nowRecord" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { LeftOutlined, CaretUpOutlined } from '@ant-design/icons-vue';
import { RecordList, RecordNewBuild, ImageContrast } from './comparison';
const props = defineProps(['']);
const emit = defineEmits(['closeComparisonModal']);
//
const typeOpen = ref(true);
function changeTypeOpen() {
typeOpen.value = !typeOpen.value;
}
const recordList = ref([
{
id: '0',
name: '变化检测_2024-05-15 17:13:27',
time: '2024-05-15 17:13:27',
createtime1: '2023-08-15 17:13:27',
createtime2: '2024-05-15 17:13:27',
changeRegion: '47',
imageGroup: [
{
id: '01',
show1: {
title: 'DJL_20230825170915_0003_W_广角1',
time: '2023-08-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
show2: {
title: 'DJL_20240925170915_0003_W_广角1',
time: '2024-09-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
},
{
id: '02',
show1: {
title: 'DJL_20230825170915_0003_W_广角2',
time: '2023-08-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
show2: {
title: 'DJL_20240925170915_0003_W_广角2',
time: '2024-09-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
},
{
id: '03',
show1: {
title: 'DJL_20230825170915_0003_W_广角3',
time: '2023-08-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
show2: {
title: 'DJL_20240925170915_0003_W_广角3',
time: '2024-09-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
},
],
},
{
id: '1',
name: '变化检测_2024-05-25 17:13:27',
time: '2024-05-25 17:13:27',
createtime1: '2024-05-25 17:13:27',
createtime2: '2024-05-25 17:13:27',
changeRegion: '47',
imageGroup: [
{
id: '11',
show1: {
title: 'DJL_20230825170915_0003_W_广角1',
time: '2023-08-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
show2: {
title: 'DJL_20240925170915_0003_W_广角1',
time: '2024-09-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
},
],
},
{
id: '2',
name: '变化检测_2024-06-05 17:13:27',
time: '2024-06-05 17:13:27',
createtime1: '2024-06-05 17:13:27',
createtime2: '2024-06-05 17:13:27',
changeRegion: '47',
imageGroup: [
{
id: '21',
show1: {
title: 'DJL_20230825170915_0003_W_广角1',
time: '2023-08-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
show2: {
title: 'DJL_20240925170915_0003_W_广角1',
time: '2024-09-25 17:22:56',
size: '8M',
width: 1024,
height: 576,
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
imageJson: [],
},
},
],
},
{
id: '3',
name: '变化检测_2024-06-25 17:13:27',
time: '2024-06-25 17:13:27',
createtime1: '2024-06-25 17:13:27',
createtime2: '2024-06-25 17:13:27',
changeRegion: '47',
},
{
id: '4',
name: '变化检测_2024-06-25 17:13:27',
time: '2024-06-25 17:13:27',
createtime1: '2024-06-25 17:13:27',
createtime2: '2024-06-25 17:13:27',
changeRegion: '47',
},
{
id: '5',
name: '变化检测_2024-06-25 17:13:27',
time: '2024-06-25 17:13:27',
createtime1: '2024-06-25 17:13:27',
createtime2: '2024-06-25 17:13:27',
changeRegion: '47',
},
]);
const nowRecord = ref(recordList.value[0]);
//
function chooseNowRecord(value) {
nowRecord.value = value;
}
//
function closeComparisonModal() {
emit('closeComparisonModal');
}
//
const isNewBuildStatus = ref(false);
//
function openIsNewBuildStatus() {
isNewBuildStatus.value = true;
}
//
function closeIsNewBuildStatus() {
isNewBuildStatus.value = false;
}
</script>
<style lang="less" scoped>
.comparisonModal {
position: relative;
width: 100%;
height: 920px;
background: #000000;
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
//
.closeButton {
position: absolute;
top: 12px;
left: 0px;
height: 40px;
width: 200px;
display: flex;
align-items: center;
justify-content: center;
.changeButton {
height: 30px;
width: 120px;
border-radius: 5px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
}
//
.topDiv {
background: #101010;
color: #ffffff;
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
//
.bodyDiv {
background: #000000;
width: 100%;
display: flex;
height: calc(100% - 60px);
//
.jiancejiluDiv {
position: relative;
background: #232323;
color: #ffffff;
width: 360px;
height: 100%;
border-radius: 10px;
}
.imageDiv {
position: relative;
width: calc(100% - 360px);
}
}
}
</style>

@ -0,0 +1,271 @@
<template>
<div class="modalChooseObjectDiv">
<div class="modalChooseObject_title">选择检测对象</div>
<div class="modalChooseObject_select">
<div class="modalChooseObject_select_title"> 1 选择航线</div>
<div class="modalChooseObject_select_">
<a-select
v-model:value="value"
style="width: 200px"
:options="hangxianOptions"
placeholder="选择航线"
></a-select>
</div>
</div>
<div class="modalChooseObject_choose">
<div class="modalChooseObject_choose_title"> 2 选择文件</div>
<div class="modalChooseObject_choose_show">
<div class="modalChooseObject_choose_showDiv">
<div class="modalChooseObject_choose_showname">
<span style="font-weight: bold; font-size: 16px">视窗1</span>
<span style="color: #dcdee1; margin-left: 10px; margin-right: 10px">|</span>
<a-input
v-model:value="inputSearchValue"
allow-clear
placeholder="搜索文件夹名称"
style="width: 300px"
>
<template #suffix>
<SearchOutlined class="SearchOutlined" @click="searchFilter" />
</template>
</a-input>
</div>
<div class="modalChooseObject_choose_table">
<a-table
:columns="columns"
:data-source="data1"
:pagination="false"
:scroll="{ x: 600, y: 325 }"
/>
<div class="table_null_1" v-if="data1.length == 0">
<div style="display: flex; align-items: center; justify-content: center">
<LaptopOutlined style="font-size: 60px; color: gray" />
</div>
<div style="display: flex; align-items: center; justify-content: center; color: gray">
暂无数据
</div>
</div>
</div>
</div>
<div class="modalChooseObject_choose_showDiv">
<div class="modalChooseObject_choose_showname"> 视窗2 </div>
<div class="modalChooseObject_choose_table">
<a-table
:columns="columns"
:data-source="data2"
:pagination="false"
:scroll="{ x: 600, y: 325 }"
/>
<div class="table_null_2" v-if="data2.length == 0">
<div style="display: flex; align-items: center; justify-content: center">
暂无可匹配内容
</div>
<div style="display: flex; align-items: center; justify-content: center">
将根据视窗 1 所选内容展示匹配项
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modalChooseObject_buttons">
<a-button @click="closeModalChooseObject"></a-button>
<a-button type="primary" :disabled="true">确定</a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { SearchOutlined, LaptopOutlined } from '@ant-design/icons-vue';
import dayjs from 'dayjs';
const props = defineProps(['']);
const emit = defineEmits(['closeModalChooseObject']);
function closeModalChooseObject() {
emit('closeModalChooseObject');
}
const hangxianOptions = [
{
value: '航点航线测试',
label: '航点航线测试',
},
{
value: '新建航点航线',
label: '新建航点航线',
},
];
const columns = [
{
title: '文件夹名称',
dataIndex: 'flodername',
width: 200,
},
{
title: '创建时间',
dataIndex: 'createtime',
width: 100,
sorter: {
compare: (a, b) => {
const timeA = dayjs(a.createtime).isValid() ? dayjs(a.createtime).valueOf() : 0;
const timeB = dayjs(b.createtime).isValid() ? dayjs(b.createtime).valueOf() : 0;
return timeA - timeB;
},
multiple: 3,
},
},
];
const data1 = [
// {
// flodername: '1',
// createtime: '2024-05-09 12:12:12',
// },
// {
// flodername: '12',
// createtime: '2025-05-09 12:12:12',
// },
];
const data2 = [];
</script>
<style lang="less" scoped>
.modalChooseObjectDiv {
height: 720px;
}
.modalChooseObject_title {
height: 40px;
width: 100%;
background: #f7f9fa;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.modalChooseObject_select {
margin-top: 30px;
padding-left: 40px;
padding-right: 40px;
display: block;
align-items: center;
justify-content: flex-start;
width: 100%;
height: auto;
.modalChooseObject_select_title {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 40px;
font-weight: bold;
font-size: 16px;
}
.modalChooseObject_select_ {
padding-bottom: 15px;
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
// height: 40px;
border-bottom: 1px solid #dcdee1;
}
}
.modalChooseObject_choose {
// margin-top: 10px;
padding-left: 40px;
padding-right: 40px;
display: block;
align-items: center;
justify-content: flex-start;
width: 100%;
height: auto;
.modalChooseObject_choose_title {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 40px;
font-weight: bold;
font-size: 16px;
}
.modalChooseObject_choose_show {
width: 100%;
display: inline-flex;
align-items: center;
justify-content: space-between;
.modalChooseObject_choose_showDiv {
width: 48%;
height: auto;
.modalChooseObject_choose_showname {
font-weight: bold;
font-size: 16px;
height: 40px;
display: flex;
align-items: center;
justify-content: flex-start;
}
.modalChooseObject_choose_table {
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
position: relative;
margin-top: 10px;
outline: 1px solid #dcdee1;
width: 100%;
height: 380px;
.table_null_1 {
display: block;
position: absolute;
top: 45%;
left: 46%;
z-index: 1000;
}
.table_null_2 {
display: block;
position: absolute;
top: 50%;
left: 35%;
z-index: 1000;
}
}
}
}
}
.modalChooseObject_buttons {
padding-right: 40px;
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
height: 94px;
gap: 10px;
}
.SearchOutlined {
font-size: 20px;
margin-left: 12px;
}
::v-deep .ant-empty {
display: none !important;
}
::v-deep .ant-table-cell {
border-bottom: 1px solid #00000000 !important;
}
</style>

@ -0,0 +1,249 @@
<template>
<div>
<div class="recordList_title">
<span style="margin-left: 15px; font-size: 16px; font-weight: bold">检测记录</span>
<a-button type="primary" style="margin-right: 15px" @click="openIsNewBuildStatus">
<PlusOutlined />新建检测
</a-button>
</div>
<div class="recordList_filter">
<div class="recordList_filter_row1">
<a-range-picker v-model:value="rangePickerValue" />
<SearchOutlined
class="SearchOutlined"
:style="{
color: showSearch ? '#2D8CF0' : '#ffffff',
}"
@click="inputSearchValue ? '' : (showSearch = !showSearch)"
/>
</div>
<div class="recordList_filter_row2" v-if="showSearch">
<a-input
v-model:value="inputSearchValue"
allow-clear
placeholder="按检测名称搜索"
style="width: 90%"
>
<template #suffix>
<SearchOutlined class="SearchOutlined" @click="searchFilter" />
</template>
</a-input>
</div>
</div>
<div class="recordList_dropdown">
<a-dropdown>
<a class="recordList_dropdown_showtitle" @click.prevent>
{{ dropdownSelect }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item>
<a @click="dropdownSelect = '所有航点航线'">所有航点航线</a>
</a-menu-item>
<a-menu-item>
<a @click="dropdownSelect = '巡检航点航线'">巡检航点航线</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
<div class="recordList_list" :style="{ height: showSearch ? '688px' : '720px' }">
<div
v-for="record in props.recordList"
:key="record.id"
class="record"
:style="{
outline: record.id == props.nowRecord.id ? '2px solid #2773c3' : '',
}"
>
<div class="record_title">
<div>{{ record.name }}</div>
<div>
<a-tooltip placement="top">
<template #title>
<span>查看</span>
</template>
<RightSquareOutlined
style="font-size: 16px; margin-right: 10px"
@click="chooseNowRecord(record)"
/>
</a-tooltip>
<a-tooltip placement="top">
<template #title>
<span>编辑</span>
</template>
<DashOutlined style="font-size: 16px" />
</a-tooltip>
</div>
</div>
<div class="record_tag">
<a-tag color="gray">变化区域{{ record.changeRegion }}</a-tag>
</div>
<div class="record_duibi">
<div class="point" style="background: #2773c3" />{{ record.createtime1 }}
</div>
<div class="record_duibi">
<div class="point" style="background: #cb8824" />{{ record.createtime2 }}
</div>
<div class="record_time">
<ClockCircleOutlined style="margin-right: 5px" />{{ record.time }}
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import {
SearchOutlined,
PlusOutlined,
CaretDownOutlined,
RightSquareOutlined,
DashOutlined,
ClockCircleOutlined,
} from '@ant-design/icons-vue';
const props = defineProps(['recordList', 'nowRecord']);
const emit = defineEmits(['chooseNowRecord', 'openIsNewBuildStatus']);
const showSearch = ref(false);
const dropdownSelect = ref('所有航点航线');
//
const rangePickerValue = ref([]);
//
const inputSearchValue = ref('');
function searchFilter() {
console.log(123);
}
//
function chooseNowRecord(value) {
emit('chooseNowRecord', value);
}
//
function openIsNewBuildStatus() {
emit('openIsNewBuildStatus');
}
</script>
<style lang="less" scoped>
//
.recordList_title {
position: relative;
top: 0px;
left: 0px;
color: #ffffff;
height: 50px;
width: 100%;
border-bottom: 1px solid #ffffff55;
display: flex;
align-items: center;
// justify-content: flex-start;
justify-content: space-between;
}
//
.recordList_filter {
position: relative;
top: 0px;
left: 0px;
width: 100%;
height: auto;
margin-top: 10px;
margin-bottom: 10px;
display: block;
.recordList_filter_row1 {
display: flex;
align-items: center;
justify-content: center;
.SearchOutlined {
color: #ffffff;
font-size: 22px;
margin-left: 12px;
}
}
.recordList_filter_row2 {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
}
//
.recordList_dropdown {
position: relative;
top: 0px;
left: 0px;
.recordList_dropdown_showtitle {
color: #ffffff;
margin-left: 18px;
}
}
//
.recordList_list {
overflow-y: auto;
position: relative;
top: 0px;
left: 0px;
// height: 705px;
width: 100%;
display: block;
gap: 5px;
.record {
position: relative;
background: #3c3c3c;
width: 90%;
height: auto;
margin: 10px 20px 10px 20px;
display: block;
border-radius: 5px;
padding: 5px 10px 5px 10px;
.record_title {
display: flex;
position: relative;
align-items: center;
justify-content: space-between;
}
.record_tag {
margin-top: 5px;
margin-bottom: 5px;
}
.record_duibi {
display: inline-flex;
align-items: center;
justify-content: flex-start;
color: gray;
// height: 15px;
width: 90%;
.point {
width: 12px;
height: 12px;
border-radius: 12px;
margin-right: 5px;
}
}
.record_time {
color: gray;
}
}
}
//
.recordList_list::-webkit-scrollbar {
display: none;
}
.recordList_list {
scrollbar-width: none;
-ms-overflow-style: none;
}
</style>

@ -0,0 +1,340 @@
<template>
<div class="recordNewBuild_div">
<div class="recordNewBuild_title">
<span style="margin-left: 15px; font-size: 16px; font-weight: bold">新建检测</span>
</div>
<div class="recordNewBuild_choose">
<a-row>
<a-col :span="24" style="margin-top: 10px">
<div style="font-size: 14px">检测名称</div>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<a-input v-model:value="newBuildRecord.name" style="width: 100%"></a-input>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<div style="font-size: 14px">检测对象</div>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<a-button type="primary" style="width: 100%" @click="openModalChooseObject">
<PlusOutlined />选择对象
</a-button>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<div style="font-size: 14px">
动态物过滤
<a-tooltip placement="bottom">
<template #title>
<span>选中项不被认定为变化区域</span>
</template>
<InfoCircleOutlined style="font-size: 14px; margin-left: 5px" />
</a-tooltip>
</div>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<div class="recordNewBuild_filter">
<div
class="car"
:style="{
background: newBuildRecord.filterCar ? '#2d8cf0' : '#3c3c3c',
}"
@click="newBuildRecord.filterCar = !newBuildRecord.filterCar"
>
</div>
</div>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<div style="font-size: 14px">
最小变化区域过滤
<a-tooltip placement="bottom">
<template #title>
<span>变化区域像素占照片像素比例小于下列设置值不将其认定为变化区域</span>
</template>
<InfoCircleOutlined style="font-size: 14px; margin-left: 5px" />
</a-tooltip>
0.01-50%</div
>
</a-col>
<a-col :span="24" style="margin-top: 10px">
<div class="recordNewBuild_minChange">
<div
class="button"
:class="{ disabled: newBuildRecord.num == 0.01 }"
@click="removeRecordNum"
>
<MinusOutlined style="font-size: 20px; color: #ffffff" />
</div>
<div class="numDiv">
<span v-if="!numInputFlag" class="numSpan" @click="focusInput">
{{ newBuildRecord.num }}
</span>
<a-input
ref="focusInputRef"
v-if="numInputFlag"
v-model:value="newBuildRecord.num"
style="width: 26%"
@blur="blurInput"
@keypress.enter="blurInput"
/>
<span style="margin-left: 10px; font-size: 18px; color: #ffffff">%</span>
</div>
<div
class="button"
:class="{ disabled: newBuildRecord.num == 50 }"
@click="addRecordNum"
>
<PlusOutlined style="font-size: 20px; color: #ffffff" />
</div>
</div>
</a-col>
</a-row>
</div>
<div class="recordNewBuild_buttons">
<div class="cancelDiv" @click="closeIsNewBuildStatus"></div>
<div class="startDiv" :class="{ disabled: !newBuildRecord.data }">开始检测</div>
</div>
<!-- width="100%"
wrap-class-name="full-modal"
-->
<a-modal
v-model:open="modalChooseObjectOpen"
width="80%"
:centered="true"
:closable="false"
:footer="null"
:destroyOnClose="true"
:keyboard="false"
:mask="false"
:maskClosable="false"
>
<ModalChooseObject @closeModalChooseObject="closeModalChooseObject" />
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, nextTick, watch } from 'vue';
import { ModalChooseObject } from './comparison';
import { PlusOutlined, MinusOutlined, InfoCircleOutlined } from '@ant-design/icons-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
const props = defineProps(['']);
const emit = defineEmits(['closeIsNewBuildStatus']);
//
const modalChooseObjectOpen = ref(false);
// -
function openModalChooseObject() {
modalChooseObjectOpen.value = true;
}
// -
function closeModalChooseObject() {
console.log(123);
modalChooseObjectOpen.value = false;
}
// ----------------------------------------------------------------------
const numInputFlag = ref(false);
const focusInputRef = ref();
// input
function focusInput() {
numInputFlag.value = true;
nextTick(() => {
if (focusInputRef.value && focusInputRef.value.focus) {
focusInputRef.value.focus();
}
});
}
// inputorEnter
function blurInput() {
numInputFlag.value = false;
//
if (/^-?\d+(\.\d+)?$/.test(newBuildRecord.value.num.toString())) {
if (newBuildRecord.value.num > 50) {
// 50
newBuildRecord.value.num = 50;
} else if (newBuildRecord.value.num < 0.01) {
// 0.01
newBuildRecord.value.num = 0.01;
} else {
//
let newnum = cloneDeep(newBuildRecord.value.num);
newBuildRecord.value.num = parseFloat(parseFloat(newnum).toFixed(2));
}
} else {
//
newBuildRecord.value.num = 0.01;
}
}
// -
function removeRecordNum() {
let newnum = cloneDeep(newBuildRecord.value.num);
if (newnum == 0.01) {
return;
}
newnum -= 0.01;
newBuildRecord.value.num = parseFloat(newnum.toFixed(2));
}
// -
function addRecordNum() {
let newnum = cloneDeep(newBuildRecord.value.num);
if (newnum == 50) {
return;
}
newnum += 0.01;
newBuildRecord.value.num = parseFloat(newnum.toFixed(2));
}
// ----------------------------------------------------------------------
const newBuildRecordClone = {
name: '变化检测_',
filterCar: false,
num: 0.05,
};
const newBuildRecord = ref(cloneDeep(newBuildRecordClone));
onMounted(() => {
//
newBuildRecord.value.name = newBuildRecord.value.name + dayjs().format('YYYY-MM-DD HH:mm:ss');
});
//
function closeIsNewBuildStatus() {
emit('closeIsNewBuildStatus');
}
</script>
<style lang="less" scoped>
.recordNewBuild_div {
position: relative;
width: 100%;
height: 100%;
}
//
.recordNewBuild_title {
position: relative;
top: 0px;
left: 0px;
color: #ffffff;
height: 50px;
width: 100%;
border-bottom: 1px solid #ffffff55;
display: flex;
align-items: center;
justify-content: flex-start;
// justify-content: space-between;
}
//
.recordNewBuild_choose {
margin-left: 15px;
margin-right: 15px;
display: block;
align-items: center;
justify-content: flex-start;
}
.car {
width: 100px;
height: 30px;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
}
//
.recordNewBuild_minChange {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 41px;
.button {
display: flex;
align-items: center;
justify-content: center;
width: 35px;
height: 35px;
background: #3c3c3c;
border-radius: 2px;
&:hover {
background: #5d5f61;
}
}
.button.disabled {
cursor: not-allowed;
opacity: 0.5;
background-color: #ccc;
}
.numDiv {
width: calc(100% - 70px);
display: flex;
align-items: center;
justify-content: center;
.numSpan {
color: #2d8cf0;
font-size: 26px;
font-weight: bold;
&:hover {
text-decoration: underline;
text-decoration-color: #2d8cf0;
}
}
}
}
//
.recordNewBuild_buttons {
position: absolute;
bottom: 0px;
left: 0px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #ffffff55;
width: 100%;
height: 68px;
gap: 20px;
.cancelDiv {
display: flex;
align-items: center;
justify-content: center;
background: #3c3c3c;
width: 40%;
height: 32px;
border-radius: 5px;
&:hover {
background: #5d5f61;
}
}
.startDiv {
display: flex;
align-items: center;
justify-content: center;
background: #0960bd;
width: 40%;
height: 32px;
border-radius: 5px;
&:hover {
background: #2a7dc9;
}
}
.startDiv.disabled {
cursor: not-allowed;
opacity: 0.5;
background-color: #27486b;
// color:
}
}
</style>

File diff suppressed because it is too large Load Diff

@ -93,6 +93,9 @@
<a-button type="text">
<EditOutlined @click="renameRecord(record)" />
</a-button>
<a-button type="text">
<BorderInnerOutlined @click="openPathModal(record)" />
</a-button>
</template>
</div>
</template>
@ -203,7 +206,6 @@
:keyboard="false"
:mask="false"
:maskClosable="false"
@ok="handleOk"
>
<Preview
v-if="nowPreviewRecord.type == 'img' || nowPreviewRecord.type == 'video'"
@ -213,14 +215,6 @@
@closeModal="closeModal"
@reloadTable="reload"
/>
<ModelModal
v-if="nowPreviewRecord.type.includes('model')"
:nowPreviewRecord="nowPreviewRecord"
:previewRecordList="previewRecordList"
@chooseNowPreviewRecord="chooseNowPreviewRecord"
@closeModal="closeModal"
@reloadTable="reload"
/>
</a-modal>
<!-- 变化检测弹窗 -->
<a-modal
@ -234,10 +228,24 @@
:keyboard="false"
:mask="false"
:maskClosable="false"
@ok="handleChangeOk"
>
<Comparison @closeComparisonModal="closeComparisonModal" />
</a-modal>
<!-- 路径地图弹窗 -->
<a-modal
v-model:open="pathOpen"
width="100%"
wrap-class-name="full-modal"
:centered="true"
:closable="false"
:footer="null"
:destroyOnClose="true"
:keyboard="false"
:mask="false"
:maskClosable="false"
>
<Path @closePathModal="closePathModal" />
</a-modal>
</PageWrapper>
</template>
<script lang="ts" setup>
@ -261,11 +269,12 @@
EyeOutlined,
PlaySquareTwoTone,
FileOutlined,
BorderInnerOutlined,
} from '@ant-design/icons-vue';
import LeftTree from './LeftTree.vue';
import Preview from './preview/preview.vue';
import ModelModal from './priview2D3D/modelModal.vue';
import Comparison from './comparison/comparison.vue';
import Preview from './preview/index.vue';
import Comparison from './comparison/index.vue';
import Path from './path/index.vue';
import { AddFolderModal } from './modal/modal';
import { MoveFileModal } from './modal/modal';
import { CompressFileModal } from './modal/modal';
@ -274,6 +283,7 @@
import { columns, searchFormSchema } from './modal.data';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
import dataJson from './data.json';
const { createConfirm, createMessage } = useMessage();
@ -439,42 +449,49 @@
},
{
id: '4',
name: '模型',
createtime: '2025-03-24 18:13:17',
name: '新建计划1 2025-06-11 16:28:23 (UTC+08)',
createtime: '2025-06-11 16:28:23',
type: 'folder',
children: [
{
id: '4-1',
name: '天空之城 二维模型',
createtime: '2025-03-24 18:13:17',
type: 'model2D',
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
boundary: {
end_lat: 22.57965964566081,
end_lng: 113.93899440765381,
max_level: 23,
min_level: 12,
start_lat: 22.578193485606185,
start_lng: 113.93697738647461,
},
},
{
id: '4-2',
name: '天空之城 三维模型',
createtime: '2025-03-24 18:13:17',
type: 'model3D',
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
boundary: {
end_lat: 22.57965964566081,
end_lng: 113.93899440765381,
max_level: 23,
min_level: 12,
start_lat: 22.578193485606185,
start_lng: 113.93697738647461,
},
},
],
children: dataJson,
},
// {
// id: '4',
// name: '',
// createtime: '2025-03-24 18:13:17',
// type: 'folder',
// children: [
// {
// id: '4-1',
// name: ' ',
// createtime: '2025-03-24 18:13:17',
// type: 'model2D',
// url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
// boundary: {
// end_lat: 22.57965964566081,
// end_lng: 113.93899440765381,
// max_level: 23,
// min_level: 12,
// start_lat: 22.578193485606185,
// start_lng: 113.93697738647461,
// },
// },
// {
// id: '4-2',
// name: ' ',
// createtime: '2025-03-24 18:13:17',
// type: 'model3D',
// url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
// boundary: {
// end_lat: 22.57965964566081,
// end_lng: 113.93899440765381,
// max_level: 23,
// min_level: 12,
// start_lat: 22.578193485606185,
// start_lng: 113.93697738647461,
// },
// },
// ],
// },
]);
//
const showTableData = ref(cloneDeep(tableData.value));
@ -797,7 +814,18 @@
}
//
function closeComparisonModal() {
comparisonOpen.value = true;
comparisonOpen.value = false;
}
// ----------------------------------------------------------------------
const pathOpen = ref(false);
//
function openPathModal(record) {
pathOpen.value = true;
}
//
function closePathModal() {
pathOpen.value = false;
}
</script>
<style lang="less" scoped>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,114 @@
<template>
<div class="pathModal">
<div
class="leftMenuDiv"
:style="{
width: leftMenuShow ? '300px' : '40px',
}"
>
<PathLeftMenu
:leftMenuShow="leftMenuShow"
:allImageDataList="dataJson"
:nowShowImageData="nowShowImageData"
@changeLeftMenuShow="changeLeftMenuShow"
@closePathModal="closePathModal"
/>
</div>
<div class="mapDiv" :style="{ width: dynamicWidth }">
<PathMap
ref="pathMapRef"
:allImageDataList="dataJson"
:nowShowImageData="nowShowImageData"
@setNowShowImageData="setNowShowImageData"
@closePathImage="closePathImage"
/>
</div>
<div class="imageShowDiv" v-if="pathImageShow">
<PathImageShow
:allImageDataList="dataJson"
:nowShowImageData="nowShowImageData"
@setNowShowImageData="setNowShowImageData"
@closePathImage="closePathImage"
@handlerLocation="handlerLocation"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { PathLeftMenu, PathMap, PathImageShow } from './path';
import dataJson from './data.json';
const props = defineProps(['']);
const emits = defineEmits(['closePathModal']);
//
const dynamicWidth = computed(() => {
if (leftMenuShow.value) {
return pathImageShow.value ? 'calc(100% - 1020px)' : 'calc(100% - 300px)';
} else {
return pathImageShow.value ? 'calc(100% - 760px)' : 'calc(100% - 40px)';
}
});
//
const nowShowImageData = ref();
const pathMapRef = ref();
//
function setNowShowImageData(value) {
if (value) {
pathImageShow.value = true;
} else {
pathImageShow.value = false;
}
nowShowImageData.value = value;
pathMapRef.value.setNowShowImageByRight();
}
//
const leftMenuShow = ref(false);
function changeLeftMenuShow() {
leftMenuShow.value = !leftMenuShow.value;
}
//
const pathImageShow = ref(false);
//
function closePathImage() {
pathImageShow.value = false;
}
//
function closePathModal() {
emits('closePathModal');
}
//
function handlerLocation(position) {
pathMapRef.value.handlerLocation([position.lng, position.lat]);
}
</script>
<style lang="less" scoped>
.pathModal {
position: relative;
display: flex;
width: 100%;
height: 100vh;
.leftMenuDiv {
position: relative;
height: 100%;
width: 300px;
}
.mapDiv {
position: relative;
height: 100%;
// width: auto;
}
.imageShowDiv {
position: relative;
height: 100%;
// width: 37%;
width: 720px;
// min-width: 720px;
}
}
</style>

@ -0,0 +1,3 @@
export { default as PathLeftMenu } from './pathLeftMenu.vue';
export { default as PathImageShow } from './pathImageShow.vue';
export { default as PathMap } from './pathMap.vue';

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
<template>
<div class="leftMenu">
<div>
<CloseOutlined style="font-size: 24px; margin: 8px; color: white" @click="closePathModal" />
</div>
<div class="suojinButton" @click="changeLeftMenuShow">
<DoubleLeftOutlined v-if="leftMenuShow" style="font-size: 16px" />
<DoubleRightOutlined v-if="!leftMenuShow" style="font-size: 16px" />
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { CloseOutlined, DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
const props = defineProps(['leftMenuShow', 'allImageDataList', 'nowShowImageData']);
const emits = defineEmits(['changeLeftMenuShow', 'closePathModal']);
//
function changeLeftMenuShow() {
emits('changeLeftMenuShow');
}
//
function closePathModal() {
emits('closePathModal');
}
</script>
<style lang="less" scoped>
.leftMenu {
position: relative;
width: 100%;
height: 100%;
background: #232323;
}
.suojinButton {
position: absolute;
top: 45%;
right: -18px;
height: 90px;
width: 18px;
background: #ffffff;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
}
</style>

@ -0,0 +1,548 @@
<template>
<div ref="vChartRef" id="mars3d-container" class="mars3d-container"></div>
</template>
<script lang="ts" setup>
import { ref, onMounted, defineEmits } from 'vue';
import * as mars3d from 'mars3d';
import * as Cesium from 'mars3d-cesium';
const props = defineProps(['allImageDataList', 'nowShowImageData']);
const emits = defineEmits(['mapOnLoad', 'setNowShowImageData', 'closePathImage']);
const vChartRef: any = ref<HTMLElement>();
let map: mars3d.Map; //
let graphicLayers = new mars3d.layer.GraphicLayer();
//
let bottomPointGraphicData: any = [];
//
let flightointGraphicData: any = [];
// 线
let bottomImagePolylineGraphicData: any = [];
//
let imageGraphicData: any = [];
onMounted(() => {
initMap();
});
const initMap = () => {
map = new mars3d.Map(vChartRef.value, {
scene: {
center: {
lat: 35.134608,
lng: 118.29853,
alt: 8306.3,
heading: 360,
pitch: -45,
},
scene3DOnly: false,
shadows: false,
removeDblClick: true,
sceneMode: 3,
showSun: true,
showMoon: true,
showSkyBox: true,
showSkyAtmosphere: true,
fog: true,
fxaa: true,
requestRenderMode: true,
contextOptions: {
requestWebgl1: false,
webgl: {
preserveDrawingBuffer: true,
alpha: false,
stencil: true,
powerPreference: 'high-performance',
},
},
globe: {
depthTestAgainstTerrain: false,
baseColor: '#546a53',
showGroundAtmosphere: true,
enableLighting: false,
},
cameraController: {
zoomFactor: 3,
minimumZoomDistance: 1,
maximumZoomDistance: 50000000,
enableRotate: true,
enableTranslate: true,
enableTilt: true,
enableZoom: true,
enableCollisionDetection: true,
minimumCollisionTerrainHeight: 15000,
},
},
control: {
homeButton: true,
baseLayerPicker: false,
sceneModePicker: true,
vrButton: false,
fullscreenButton: true,
navigationHelpButton: true,
animation: false,
timeline: false,
infoBox: false,
geocoder: false,
selectionIndicator: false,
showRenderLoopErrors: true,
contextmenu: {
hasDefault: true,
},
mouseDownView: true,
zoom: {
insertIndex: 1,
},
compass: {
bottom: 'toolbar',
left: '5px',
rotation: true,
},
distanceLegend: {
left: '10px',
bottom: '2px',
},
locationBar: {
crs: 'CGCS2000_GK_Zone_3',
crsDecimal: 0,
template:
"<div>经度:{lng}</div> <div>纬度:{lat}</div> <div class='hide1000'>横{crsx} 纵{crsy}</div> <div>海拔:{alt}米</div> <div class='hide700'>层级:{level}</div><div>方向:{heading}°</div> <div>俯仰角:{pitch}°</div><div class='hide700'>视高:{cameraHeight}米</div>",
cacheTime: 50,
},
},
method: {
templateValues: {
dataServer: '//data.mars3d.cn',
gltfServerUrl: '//data.mars3d.cn/gltf',
},
},
terrain: {
url: '//data.mars3d.cn/terrain',
show: true,
clip: true,
},
basemaps: [
{
id: 10,
name: '地图底图',
type: 'group',
opacity: 1,
},
{
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
eventParent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
private: false,
id: 'm-770c35e7-9054-4259-b5ee-c15f108becd0',
opacity: 1,
pid: 2021,
parent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
zIndex: 1,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
eventParent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
private: false,
id: 'm-3b881368-574b-48a5-88b2-8b3c2c48fd62',
opacity: 1,
pid: 2021,
parent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
zIndex: 2,
},
],
show: true,
opacity: 1,
},
],
layers: [],
});
addImage();
handlerLocation([118.29853, 35.134608]);
};
//
// const nowShowImageData = ref();
const addImage = async () => {
//
bottomPointGraphicData?.forEach((graphicLayer) => {
graphicLayers.removeGraphic(graphicLayer);
});
flightointGraphicData?.forEach((graphicLayer) => {
graphicLayers.removeGraphic(graphicLayer);
});
bottomImagePolylineGraphicData?.forEach((graphicLayer) => {
graphicLayers.removeGraphic(graphicLayer);
});
imageGraphicData?.forEach((graphicLayer) => {
graphicLayers.removeGraphic(graphicLayer);
});
//
map.addLayer(graphicLayers);
//
bottomPointGraphicData = [];
flightointGraphicData = [];
bottomImagePolylineGraphicData = [];
imageGraphicData = [];
//
props.allImageDataList.forEach((item) => {
if (
item.photo_position &&
item.photo_position.lng &&
item.photo_position.lat &&
item.photo_position.abs_alt
) {
const image = new Image();
image.crossOrigin = 'Anonymous';
// image.src = item.preview_url;
image.src =
'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871';
image.onload = () => {
// Canvas
const canvas = document.createElement('canvas');
canvas.width = 34;
canvas.height = 29;
// Canvas
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';
ctx?.fillRect(0, 0, canvas.width, canvas.height);
// Canvas
ctx.drawImage(image, 2, 2, 30, 25);
// CanvasURL
const dataURL1 = canvas.toDataURL('image/png');
// CanvasimageImage
ctx.drawImage(image, 2, 2, 30, 25);
//
ctx.strokeStyle = '#2d8cef'; //
ctx.lineWidth = 1; //
//
ctx.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1); //
// CanvasURL
const dataURL2 = canvas.toDataURL('image/png');
//
let position = [
parseFloat(item?.photo_position?.lng),
parseFloat(item.photo_position.lat),
parseFloat(item.photo_position.abs_alt),
];
//
mars3d.PointUtil.getSurfaceHeight(
map.scene,
Cesium.Cartesian3.fromDegrees(position[0], position[1]),
).then((point) => {
//
let bottomPointGraphic = new mars3d.graphic.BillboardEntity({
id: item.id + '_bottom',
position: [position[0], position[1], point.height],
style: {
image: '/src/assets/images/flightoperation/mediaLibraryPoint.png',
clampToGround: true,
scale: 1,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
});
//
let flightointGraphic = new mars3d.graphic.BillboardEntity({
id: item.id + '_flight',
position: [position[0], position[1], position[2] * 2],
style: {
image: '/src/assets/images/flightoperation/mediaLibraryFlight.png',
clampToGround: false,
scale: 1,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
},
show: false,
});
// 线
let bottomImagePolylineGraphic = new mars3d.graphic.PolylineEntity({
id: item.id + '_polyline',
positions: [
Cesium.Cartesian3.fromDegrees(position[0], position[1], point.height),
Cesium.Cartesian3.fromDegrees(position[0], position[1], position[2]),
],
style: {
color: '#ffffff',
width: 1, // 线
clampToGround: false,
},
//
defaultPosition: position,
//
surfaceHeight: point.height,
});
//
let imageGraphic = new mars3d.graphic.BillboardEntity({
id: item.id + '_image',
position: [position[0], position[1], position[2]],
style: {
image: dataURL1,
clampToGround: false,
scale: 1,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
//
defaultImage: dataURL1,
//
chooseImage: dataURL2,
});
// mouseover
imageGraphic.on(mars3d.EventType.mouseOver, function () {
if (!props.nowShowImageData || props.nowShowImageData.id !== item.id) {
//
imageGraphic.setStyle({
image: dataURL2,
scale: 1.1,
});
}
});
// mouseout
imageGraphic.on(mars3d.EventType.mouseOut, function () {
if (!props.nowShowImageData || props.nowShowImageData.id !== item.id) {
//
imageGraphic.setStyle({
image: dataURL1,
scale: 1,
});
}
});
//
imageGraphic.on(mars3d.EventType.click, function () {
//
if (props.nowShowImageData && props.nowShowImageData.id == item.id) {
//
restoreDefault();
emits('setNowShowImageData', {});
emits('closePathImage');
} else if (!props.nowShowImageData || props.nowShowImageData.id !== item.id) {
//
//
restoreDefault();
//
// PolylineEntity
bottomImagePolylineGraphic.positions = [
Cesium.Cartesian3.fromDegrees(position[0], position[1], point.height),
Cesium.Cartesian3.fromDegrees(position[0], position[1], position[2] * 2),
];
// PolylineEntity
bottomImagePolylineGraphic.setStyle({
color: '#2d8cf0',
});
//
flightointGraphic.show = true;
//
imageGraphic.setStyle({
image: dataURL2,
});
emits('setNowShowImageData', item);
// //
// handlerLocation([item.photo_position.lng, item.photo_position.lat, point.height]);
}
});
//
graphicLayers.addGraphic(bottomPointGraphic);
//
graphicLayers.addGraphic(flightointGraphic);
// 线
graphicLayers.addGraphic(bottomImagePolylineGraphic);
//
graphicLayers.addGraphic(imageGraphic);
//
bottomPointGraphicData.push(bottomPointGraphic);
flightointGraphicData.push(flightointGraphic);
bottomImagePolylineGraphicData.push(bottomImagePolylineGraphic);
imageGraphicData.push(imageGraphic);
});
};
}
});
};
//
function restoreDefault() {
//
for (let i = 0; i < flightointGraphicData.length; i++) {
let flightointGraphic = flightointGraphicData[i];
flightointGraphic.show = false;
}
// 线
for (let i = 0; i < bottomImagePolylineGraphicData.length; i++) {
let bottomImagePolylineGraphic = bottomImagePolylineGraphicData[i];
let defaultPosition = bottomImagePolylineGraphic.options.defaultPosition;
let surfaceHeight = bottomImagePolylineGraphic.options.surfaceHeight;
bottomImagePolylineGraphic.setStyle({
color: '#ffffff',
});
bottomImagePolylineGraphic.positions = [
Cesium.Cartesian3.fromDegrees(defaultPosition[0], defaultPosition[1], surfaceHeight),
Cesium.Cartesian3.fromDegrees(defaultPosition[0], defaultPosition[1], defaultPosition[2]),
];
}
//
for (let i = 0; i < imageGraphicData.length; i++) {
let imageGraphic = imageGraphicData[i];
let defaultImage = imageGraphic.options.defaultImage;
imageGraphic.setStyle({
image: defaultImage,
scale: 1,
});
}
}
//
function setNowShowImageByRight() {
setTimeout(() => {
//
restoreDefault();
//
for (let i = 0; i < flightointGraphicData.length; i++) {
if (flightointGraphicData[i].options.id.includes(props.nowShowImageData.id)) {
let flightointGraphic = flightointGraphicData[i];
flightointGraphic.show = true;
}
}
// 线
for (let i = 0; i < bottomImagePolylineGraphicData.length; i++) {
if (bottomImagePolylineGraphicData[i].options.id.includes(props.nowShowImageData.id)) {
let bottomImagePolylineGraphic = bottomImagePolylineGraphicData[i];
let defaultPosition = bottomImagePolylineGraphic.options.defaultPosition;
let surfaceHeight = bottomImagePolylineGraphic.options.surfaceHeight;
bottomImagePolylineGraphic.setStyle({
color: '#2d8cf0',
});
bottomImagePolylineGraphic.positions = [
Cesium.Cartesian3.fromDegrees(defaultPosition[0], defaultPosition[1], surfaceHeight),
Cesium.Cartesian3.fromDegrees(
defaultPosition[0],
defaultPosition[1],
defaultPosition[2] * 2,
),
];
}
}
//
for (let i = 0; i < imageGraphicData.length; i++) {
if (imageGraphicData[i].options.id.includes(props.nowShowImageData.id)) {
let imageGraphic = imageGraphicData[i];
let chooseImage = imageGraphic.options.chooseImage;
imageGraphic.setStyle({
image: chooseImage,
});
}
}
}, 500);
}
//
const handlerLocation = (lngLat) => {
const position = Cesium.Cartesian3.fromDegrees(lngLat[0], lngLat[1]);
map.flyToPoint(position);
};
defineExpose({
setNowShowImageByRight,
handlerLocation,
});
</script>
<style scoped>
.mars3d-container {
width: 100%;
height: 100%;
background: red;
}
</style>

@ -1,54 +0,0 @@
<template>
<div id="my-three"></div>
</template>
<script lang="ts" setup>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { onMounted } from 'vue';
//
const scene = new THREE.Scene();
//
const geometry = new THREE.SphereGeometry(300, 50, 50);
const texture = new THREE.TextureLoader().load('./天空盒全景图.png');
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.BackSide, //
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
//
const width = window.innerWidth,
height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
//
camera.position.set(300, 300, 300);
//
camera.lookAt(0, 0, 0);
//
const axesHelper = new THREE.AxesHelper(200); //200
scene.add(axesHelper);
//WebGL
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //
renderer.render(scene, camera); //
const controls = new OrbitControls(camera, renderer.domElement); //
controls.addEventListener('change', () => {
renderer.render(scene, camera); //
});
onMounted(() => {
document.getElementById('my-three')?.appendChild(renderer.domElement);
});
</script>
<style lang="less" scoped>
body {
margin: 0;
padding: 0;
}
</style>

@ -27,10 +27,6 @@
@setHideOrShowTextboxFlag="setHideOrShowTextboxFlag"
/>
</div>
<!-- VR全景 -->
<!-- <div v-if="props.nowPreviewRecord.type == 'vr'">
<PanoViewer />
</div> -->
<!-- 视频 -->
<div class="videoDiv" v-if="props.nowPreviewRecord.type == 'video'">
<PreviewVideo
@ -69,14 +65,11 @@
PreviewImageInformation,
PreviewVideo,
PreviewVideoInformation,
PanoViewer,
} from './preview';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);
console.log(props.previewRecordList);
const hideOrShowTextboxFlag = ref(true);
function setHideOrShowTextboxFlag(value) {
hideOrShowTextboxFlag.value = value;

@ -0,0 +1,321 @@
<template>
<div ref="vChartRef" id="mars3d-container" class="mars3d-container">
<div class="mapInfo">
<span> {{ props.nowPreviewRecord.lat }}°N</span>
<span> {{ props.nowPreviewRecord.lng }}°E</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, defineEmits } from 'vue';
import * as mars3d from 'mars3d';
import * as Cesium from 'mars3d-cesium';
const props = defineProps(['nowPreviewRecord', 'previewRecordList', 'hideOrShowTextboxFlag']);
const emits = defineEmits(['chooseNowPreviewRecord', 'reloadTable', 'mapOnLoad']);
let map: mars3d.Map; //
let graphicData: any = [];
let graphicLayers = new mars3d.layer.GraphicLayer();
const vChartRef: any = ref<HTMLElement>();
onMounted(() => {
initMap();
addImage();
});
const initMap = () => {
map = new mars3d.Map(vChartRef.value, {
scene: {
center: {
lat: parseFloat(props.nowPreviewRecord.lat),
lng: parseFloat(props.nowPreviewRecord.lng),
alt: 8306.3,
heading: 360,
pitch: -45,
},
scene3DOnly: false,
shadows: false,
removeDblClick: true,
sceneMode: 3,
showSun: true,
showMoon: true,
showSkyBox: true,
showSkyAtmosphere: true,
fog: true,
fxaa: true,
requestRenderMode: true,
contextOptions: {
requestWebgl1: false,
webgl: {
preserveDrawingBuffer: true,
alpha: false,
stencil: true,
powerPreference: 'high-performance',
},
},
globe: {
depthTestAgainstTerrain: false,
baseColor: '#546a53',
showGroundAtmosphere: true,
enableLighting: false,
},
cameraController: {
zoomFactor: 3,
minimumZoomDistance: 1,
maximumZoomDistance: 50000000,
enableRotate: true,
enableTranslate: true,
enableTilt: true,
enableZoom: true,
enableCollisionDetection: true,
minimumCollisionTerrainHeight: 15000,
},
},
control: {
homeButton: true,
baseLayerPicker: false,
sceneModePicker: false,
vrButton: false,
fullscreenButton: false,
navigationHelpButton: false,
animation: false,
timeline: false,
infoBox: false,
geocoder: false,
selectionIndicator: false,
showRenderLoopErrors: false,
contextmenu: {
hasDefault: false,
},
mouseDownView: false,
zoom: false,
compass: false,
distanceLegend: false,
},
method: {
templateValues: {
dataServer: '//data.mars3d.cn',
gltfServerUrl: '//data.mars3d.cn/gltf',
},
},
terrain: {
url: '//data.mars3d.cn/terrain',
show: true,
clip: true,
},
basemaps: [
{
id: 10,
name: '地图底图',
type: 'group',
opacity: 1,
},
{
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
eventParent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
private: false,
id: 'm-770c35e7-9054-4259-b5ee-c15f108becd0',
opacity: 1,
pid: 2021,
parent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
zIndex: 1,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
eventParent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
private: false,
id: 'm-3b881368-574b-48a5-88b2-8b3c2c48fd62',
opacity: 1,
pid: 2021,
parent: {
id: 2021,
pid: 10,
name: '天地图影像',
icon: 'https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png',
type: 'group',
layers: [
{
name: '底图',
type: 'tdt',
layer: 'img_d',
show: true,
},
{
name: '注记',
type: 'tdt',
layer: 'img_z',
show: true,
},
],
show: true,
},
zIndex: 2,
},
],
show: true,
opacity: 1,
},
],
layers: [],
});
emits('mapOnLoad', map);
};
const addImage = () => {
//
graphicData?.forEach((graphicLayer) => {
graphicLayers.removeGraphic(graphicLayer);
});
//
map.addLayer(graphicLayers);
//
graphicData = [];
props.previewRecordList.forEach((item) => {
let graphic = new mars3d.graphic.BillboardEntity({
id: item.id,
position: [parseFloat(item.lng), parseFloat(item.lat)],
style: {
// image: '@/assets/images/point.png',
image: '../point.png',
clampToGround: true,
scale: 1,
label: {
text: item.name,
font_size: 12,
color: '#ffffff',
pixelOffsetY: -30,
pixelOffsetX: 0,
distanceDisplayCondition: true,
distanceDisplayCondition_far: 6000,
distanceDisplayCondition_near: 0,
},
},
popup: `<div class="marsTiltPanel marsTiltPanel-theme-green">
<div style="postion: relative; padding: 60px; max-width: 500px; overflow: hidden;">
</div>
<div style="position: absolute; bottom: 0; left: 0; width: 85px; height: 2px; transform: rotate(-45deg) translate(15px, -25px); background-color: #15babc;" ></div>
</div>`,
popupOptions: {
offsetY: 0,
offsetX: 0,
template: '{content}',
horizontalOrigin: 'Cesium.HorizontalOrigin.LEFT',
verticalOrigin: 'Cesium.VerticalOrigin.CENTER',
},
pointerEvents: true,
});
//
graphicLayers.addGraphic(graphic);
graphicData.push(graphic);
});
};
</script>
<style lang="less" scoped>
.mars3d-container {
position: relative;
width: 100%;
height: 100%;
}
.mapInfo {
position: absolute;
width: 100%;
height: 30px;
bottom: 0px;
left: 0px;
background: #3d3f3aaa;
z-index: 1000;
display: flex;
justify-content: flex-start;
span {
height: 100%;
width: 30%;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 15px;
margin-left: 10px;
}
}
</style>

@ -2,9 +2,5 @@ export { default as PreviewImage } from './previewImage.vue';
export { default as PreviewVideo } from './previewVideo.vue';
export { default as PreviewImageInformation } from './previewImageInformation.vue';
export { default as PreviewVideoInformation } from './previewVideoInformation.vue';
export { default as MonitorHK } from './video/monitorHK.vue';
export { default as MonitorLC } from './video/monitorLC.vue';
export { default as MonitorQX } from './video/monitorQX.vue';
export { default as MonitorTX } from './video/monitorTX.vue';
export { default as PanoViewer } from './PanoViewer.vue';
export { default as Map } from './Map.vue';
// export { default as Map } from './Map.vue';
export { default as Map } from './map2.vue';

@ -1,6 +1,6 @@
<template>
<div class="previewImage">
<div id="imageDiv" class="imageDiv">
<div id="imageDiv" class="imageDiv" style="overflow: hidden">
<div
ref="mouseCanvasRef"
@mousedown="onMouseDown"
@ -680,7 +680,7 @@
}
//
const isDrawing = ref(false);
const isDragging = ref(false);
let startX = 0;
let startY = 0;
let endX = 0;
@ -741,7 +741,7 @@
const rect = mouseCanvasRef.value.getBoundingClientRect();
startX = e.x - rect.x;
startY = e.y - rect.y;
isDrawing.value = true;
isDragging.value = true;
//
graffitis.value.push({
@ -760,7 +760,7 @@
//
function onMouseMove(e) {
const rect = mouseCanvasRef.value.getBoundingClientRect();
if (!isDrawing.value) return;
if (!isDragging.value) return;
endX = e.x - rect.x;
endY = e.y - rect.y;
if (endX < 0 && endY < 0) {
@ -771,16 +771,15 @@
}
//
function onMouseUp(e) {
if (!isDrawing.value) return;
if (!isDragging.value) return;
const rect = mouseCanvasRef.value.getBoundingClientRect();
endX = e.x - rect.x;
endY = e.y - rect.y;
if (endX < 0 && endY < 0) {
endX = startX;
endY = startY;
}
isDrawing.value = false;
isDragging.value = false;
if (e.x > rect.right || e.y > rect.bottom) {
graffitis.value.splice(graffitis.value.length - 1, 1);
nowGraffiti.value = -1;
@ -843,7 +842,7 @@
const rect = mouseCanvasRef.value.getBoundingClientRect();
startX = e.x - rect.x;
startY = e.y - rect.y;
isDrawing.value = true;
isDragging.value = true;
nowGraffiti.value = index;
mouseEditType.value = type;
window.addEventListener('mousemove', funMouseMoveEdit);
@ -852,7 +851,7 @@
//
function funMouseMoveEdit(e) {
const rect = mouseCanvasRef.value.getBoundingClientRect();
if (!isDrawing.value) return;
if (!isDragging.value) return;
endX = e.x - rect.x;
endY = e.y - rect.y;
if (endX < 0 && endY < 0) {
@ -863,7 +862,7 @@
}
//
function funMouseUpEdit(e) {
if (!isDrawing.value) return;
if (!isDragging.value) return;
const rect = mouseCanvasRef.value.getBoundingClientRect();
endX = e.x - rect.x;
endY = e.y - rect.y;
@ -871,7 +870,7 @@
endX = startX;
endY = startY;
}
isDrawing.value = false;
isDragging.value = false;
if (e.x > rect.right || e.y > rect.bottom) {
graffitis.value[nowGraffiti.value] = graffitisClone.value[nowGraffiti.value];
} else {
@ -1138,13 +1137,13 @@
.escTip {
position: absolute;
left: 0%;
bottom: 100px;
bottom: 0px;
left: 0px;
width: 120px;
height: 40px;
background: #9c9c9c77;
border-radius: 5px;
z-index: 200;
display: flex;
align-items: center;
justify-content: flex-start;

@ -1,35 +1,6 @@
<template>
<div class="videoDiv">
<div class="showVideo">
<!-- <MonitorHK
v-if="props.nowPreviewRecord.manufacturer == '海康'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
/>
<MonitorLC
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
:deviceId="props.nowPreviewRecord.url"
:channelId="0"
:width="1300"
:height="800"
:videoMuted="true"
/>
<MonitorTX
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="820"
/>
<MonitorQX
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
:videoLoop="false"
:videoMuted="true"
:videoFit="'contain'"
/> -->
<video :src="props.nowPreviewRecord.url" class="video-player" controls muted autoplay></video>
</div>
@ -82,10 +53,6 @@
LeftOutlined,
RightOutlined,
} from '@ant-design/icons-vue';
import { MonitorHK } from './preview';
import { MonitorLC } from './preview';
import { MonitorTX } from './preview';
import { MonitorQX } from './preview';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);

@ -1,267 +0,0 @@
<template>
<div id="camera-box" class="camera-box">
<!-- 视口区域 -->
<div :id="'playWnd'" class="playWnd"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, watch, defineProps, defineExpose, ref, computed } from 'vue';
import { JSEncrypt } from 'jsencrypt';
const props = defineProps(['serialNumberValue', 'width', 'height']);
//
let initCount = 0;
let pubKey = '';
let oWebControl: any = null;
//
const initPlugin = () => {
oWebControl = new window.WebControl({
szPluginContainer: 'playWnd', // id
iServicePortStart: 15900, // 使
iServicePortEnd: 15909,
szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // IE10使ActiveXclsid
cbConnectSuccess: function () {
// WebControl
oWebControl
.JS_StartService('window', {
// WebControlVideoPluginPlugin.exe
dllPath: './VideoPluginConnect.dll', // "./VideoPluginConnect.dll"
})
.then(
function () {
//
oWebControl.JS_SetWindowControlCallback({
//
cbIntegrationCallBack: cbIntegrationCallBack,
});
//
let width = props.width;
let height = props.height;
const divElement = document.getElementById('camera-box');
if (divElement) {
const rect = divElement.getBoundingClientRect();
const rectWidth = rect.width;
const rectHeight = rect.height;
if (rectWidth < width) {
width = rectWidth;
}
if (rectHeight < height) {
height = rectHeight;
}
}
oWebControl.JS_CreateWnd('playWnd', width, height).then(function () {
init(); //
});
},
function () {
//
console.log('创建播放实例失败!!!');
},
);
},
cbConnectError: function () {
// WebControl
oWebControl = null;
let d = document.getElementById('playWnd');
if (d) {
d.innerHTML = '插件未启动,正在尝试启动,请稍候...';
}
window.WebControl.JS_WakeUp('VideoWebPlugin://'); // errorwakeup
initCount++;
if (initCount < 3) {
setTimeout(function () {
initPlugin();
}, 3000);
} else {
let d = document.getElementById('playWnd');
if (d) {
d.innerHTML = '插件启动失败,请检查插件是否安装!';
}
}
},
cbConnectClose: function (bNormalClose) {
// bNormalClose = false
// JS_DisconnectbNormalClose = true
oWebControl = null;
},
});
};
//
function init() {
getPubKey(function () {
let appkey = '23604396'; //appkey
let ip = '221.2.83.54'; //IP
let port = 1443;
let appSecret = 'NZJ8L3bxCOOV6rtTFjsx';
let secret = setEncrypt(appSecret); //secret
let layerOut = '1x1';
let playMode = 0; //0-1-
let snapDir = 'D:\\SnapDir'; //
let videoDir = 'D:\\VideoDir'; //
let layout = layerOut; //playMode 1*1
let enableHTTPS = 1; //HTTPS1
let encryptedFields = 'secret'; //secret
let showToolbar = 1; //0-0-
let showSmart = 1; //线0-0-
let buttonIDs = ''; //
oWebControl
.JS_RequestInterface({
funcName: 'init',
argument: JSON.stringify({
appkey: appkey, //APIappkey
secret: secret, //APIsecret
ip: ip, //APIIP
playMode: playMode, //
port: port, //
snapDir: snapDir, //
videoDir: videoDir, //
layout: layout, //
enableHTTPS: enableHTTPS, //HTTPS
encryptedFields: encryptedFields, //
showToolbar: showToolbar, //
showSmart: showSmart, //
buttonIDs: buttonIDs, //
}),
})
.then(function (oData) {
//
reSizeVideo();
});
//
oWebControl.JS_RequestInterface({
funcName: 'startPreview',
argument: {
cameraIndexCode: props.serialNumberValue,
streamMode: 0,
transMode: 1,
gpuMode: 0,
wndId: 1,
},
});
});
}
//
function closeHkVideo() {
if (oWebControl != null) {
//
oWebControl.JS_HideWnd();
//
oWebControl.JS_RequestInterface({ funcName: 'destroyWnd' });
//
oWebControl.JS_Disconnect();
}
}
//
function reSizeVideo() {
//
let width = props.width;
let height = props.height;
const divElement = document.getElementById('camera-box');
if (divElement) {
const rect = divElement.getBoundingClientRect();
const rectWidth = rect.width;
const rectHeight = rect.height;
if (rectWidth < width) {
width = rectWidth;
}
if (rectHeight < height) {
height = rectHeight;
}
}
oWebControl.JS_Resize(width, height); // resizefirefoxDIV
}
watch(
() => props.serialNumberValue,
() => {
init();
},
);
//
function getPubKey(callback) {
oWebControl
.JS_RequestInterface({
funcName: 'getRSAPubKey',
argument: JSON.stringify({
keyLength: 1024,
}),
})
.then(function (oData) {
if (oData.responseMsg.data) {
pubKey = oData.responseMsg.data;
callback();
}
});
}
// RSA
function setEncrypt(value) {
let encrypt = new JSEncrypt();
encrypt.setPublicKey(pubKey);
return encrypt.encrypt(value);
}
//
function cbIntegrationCallBack(oData) {
// showCBInfo(JSON.stringify(oData.responseMsg));
}
onMounted(() => {
//
initPlugin();
//
const elements = document.querySelectorAll('.ModalVideo');
if (elements.length > 0) {
// scroll
elements.forEach((element) => {
element.addEventListener('scroll', reSizeVideo);
});
}
});
onUnmounted(() => {
//
closeHkVideo();
//
const elements = document.querySelectorAll('.ModalVideo');
if (elements.length > 0) {
// scroll
elements.forEach((element) => {
element.removeEventListener('scroll', reSizeVideo);
});
}
});
defineExpose({
initPlugin,
init,
closeHkVideo,
});
</script>
<style lang="less" scoped>
.camera-box {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');
border-radius: 5px;
position: relative;
z-index: 10 !important;
position: fixed;
left: 50%;
top: 50%;
z-index: 10;
transform: translate(-62%, -51%);
padding: 0px;
}
.playWnd {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');
}
</style>

@ -1,207 +0,0 @@
<template>
<div class="box">
<div class="box-container">
<div :id="'root' + props.timestamp"></div>
</div>
<div class="box-controls">
<div class="left-controls">
<div>
<n-button quaternary @click="playOrPauseClick">
<template #icon>
<n-icon>
<Pause
v-if="control_playOrPause"
:style="{
fontSize: '20px',
color: '#ffffff',
}"
/>
<CaretForward
v-if="!control_playOrPause"
:style="{
fontSize: '20px',
color: '#ffffff',
}"
/>
</n-icon>
</template>
</n-button>
</div>
<div>
<n-button quaternary @click="volumeClick">
<template #icon>
<n-icon>
<VolumeHigh
v-if="!control_volume"
:style="{
fontSize: '20px',
color: '#ffffff',
}"
/>
<VolumeMute
v-if="control_volume"
:style="{
fontSize: '20px',
color: '#ffffff',
}"
/>
</n-icon>
</template>
</n-button>
</div>
</div>
<div class="right-controls">
<n-button quaternary @click="fullScreenClick">
<template #icon>
<n-icon>
<ExpandOutlined
:style="{
fontSize: '20px',
color: '#ffffff',
}"
/>
</n-icon>
</template>
</n-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, watch, ref } from 'vue';
import axios from 'axios';
import { ExpandOutlined } from '@ant-design/icons-vue';
import { Pause, CaretForward, VolumeHigh, VolumeMute } from '@vicons/ionicons5';
let BASE_URL = 'http://111.17.207.220:9001/api';
const props = defineProps([
'deviceId',
'channelId',
'width',
'height',
'timestamp',
'videoMuted',
]);
let clPlayer: any = null;
// /
const control_playOrPause = ref(true);
// :true
const control_volume = ref(true);
//
const control_fullScreen = ref(false);
// /
function playOrPauseClick() {
control_playOrPause.value = !control_playOrPause.value;
if (control_playOrPause.value) {
clPlayer.play();
} else {
clPlayer.pause();
}
}
//
function volumeClick() {
control_volume.value = !control_volume.value;
if (control_volume.value) {
clPlayer.volume(0);
} else {
clPlayer.volume(1);
}
}
//
function fullScreenClick() {
clPlayer.fullScreen();
}
// kitToken
function getKitToken(deviceId, channelId) {
axios({
method: 'post',
url: BASE_URL + '/Camera/getKitToken?deviceId=' + deviceId + '&channelId=0' + '&type=0',
}).then((res) => {
let kitToken = res.data.result.data.kitToken;
loadMonitorVideo(deviceId, kitToken, channelId);
});
}
//
function loadMonitorVideo(deviceId, kitToken, channelId) {
//
closeMonitorVideo();
clPlayer = new imouPlayer({
id: 'root' + props.timestamp,
width: props.width,
height: props.height - 35,
deviceId: deviceId,
token: kitToken,
channelId: channelId,
type: 1,
streamId: 0,
recordType: 'cloud',
code: '',
controls: true,
});
clPlayer.volume(0);
}
//
function closeMonitorVideo() {
if (clPlayer != null) {
clPlayer.destroy();
clPlayer = null;
}
}
watch(
() => props.deviceId,
() => {
getKitToken(props.deviceId, props.channelId);
},
);
onMounted(() => {
getKitToken(props.deviceId, props.channelId);
control_volume.value = props.videoMuted;
});
onUnmounted(() => {
closeMonitorVideo();
});
</script>
<style lang="less" scoped>
.box {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');
z-index: 99;
}
.box-container {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height-35}px`');
}
.box-controls {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
background: #000000;
height: 35px;
width: 100%;
gap: 10px;
}
.left-controls {
display: flex;
justify-self: start;
}
.right-controls {
justify-self: end;
}
</style>

@ -1,40 +0,0 @@
<template>
<div class="box">
<easy-player
:video-url="getUrl(props.serialNumberValue)"
live
autoplay
fluent="true"
:style="{ width: props.width + 'px', height: props.height + 'px' }"
:stretch="props.videoFit"
:muted="props.videoMuted"
:loop="props.videoLoop"
/>
</div>
</template>
<script setup lang="ts">
const props = defineProps([
'serialNumberValue',
'width',
'height',
'videoMuted',
'videoLoop',
'videoFit',
]);
function getUrl(value) {
return 'http://221.2.83.254:7012/live/' + value + '.m3u8';
}
</script>
<style lang="scss" scoped>
.box {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');
z-index: 99;
}
::v-deep .vjs-bitrate-control {
display: none !important;
}
</style>

@ -1,76 +0,0 @@
<template>
<div>
<video
:id="'TCPlayerVideo'"
class="TCPlayer-video-container"
preload="auto"
crossOrigin="anonymous"
playsinline
autoplay
:loop="false"
:muted="true"
:style="{
width: props.width + 'px',
height: props.height + 'px',
}"
/>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
const props = defineProps(['serialNumberValue', 'width', 'height']);
//
let player: any = null;
function handlerPlayVideo() {
nextTick(() => {
if (player) {
player.src(props.serialNumberValue);
} else {
player = TCPlayer('TCPlayerVideo', {});
player.src(props.serialNumberValue);
}
});
}
function closePlayerVideo() {
if (player) {
player.dispose();
player = null;
}
}
watch(
() => props.serialNumberValue,
() => {
handlerPlayVideo();
},
);
onMounted(() => {
handlerPlayVideo();
});
onUnmounted(() => {
closePlayerVideo();
});
defineExpose({
handlerPlayVideo,
closePlayerVideo,
});
</script>
<style lang="scss" scoped>
video {
display: block;
object-fit: contain;
}
::v-deep .vjs-live-control .vjs-live-display {
width: 100px !important;
}
</style>

@ -1,428 +0,0 @@
<template>
<div class="model2D">
<div :id="mapId" class="map">
<!-- 全屏 -->
<div class="expandButton" @click="clickExpandButton">
<ExpandOutlined v-if="!fullscreenFlag" style="color: #3c3c3c; font-size: 25px" />
<CompressOutlined v-if="fullscreenFlag" style="color: #3c3c3c; font-size: 25px" />
</div>
<div class="angleViewButton">
<div class="button_child" @click="lookTopView">
<span style="color: #3c3c3c; font-size: 16px">顶视</span>
</div>
<div class="button_child" @click="lookDownView">
<span style="color: #3c3c3c; font-size: 16px">俯视</span>
</div>
</div>
<div class="plusAndMinusButton">
<div class="button_child" @click="clickPlusButton">
<PlusOutlined style="color: #3c3c3c; font-size: 25px" />
</div>
<div class="button_child" @click="clickMinusButton">
<MinusOutlined style="color: #3c3c3c; font-size: 25px" />
</div>
</div>
</div>
<div class="info">
<div class="left">键盘</div>
<div class="right4">123</div>
<div class="right3"> ASL: {{ asl }}m</div>
<div class="right2"> HAE: {{ hae }}m</div>
<div class="right1">
<HeatMapOutlined style="margin-right: 15px; font-size: 20px" />
WGS 84
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, onMounted, onUnmounted, defineEmits, computed } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import mapboxgl, { Map } from 'mapbox-gl';
import { MapboxConfig, MapboxDefaultStyle } from '@/components/MapboxMaps/src/config';
import axios from 'axios';
import {
ExpandOutlined,
CompressOutlined,
PlusOutlined,
MinusOutlined,
HeatMapOutlined,
} from '@ant-design/icons-vue';
const props = defineProps(['nowPreviewRecord']);
const emits = defineEmits(['getMap']);
const mapId = `modal-map-${uuidv4()}`;
const networkType = ref('WAN');
let map: Map;
const initMap = () => {
// let mapDataSources: any = {
// 'raster-tiles': {
// type: 'raster',
// tiles: [
// `http://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${MapboxConfig.TDT_TOKEN}`,
// ],
// tileSize: 256,
// minzoom: 1,
// maxzoom: 17,
// },
// 'raster-tiles-font': {
// type: 'raster',
// tiles: [
// `https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${MapboxConfig.TDT_TOKEN}`,
// ],
// tileSize: 256,
// },
// };
// let mapDataLayers: any = [
// {
// id: 'tdt-vec-tiles',
// type: 'raster',
// source: 'raster-tiles-font',
// },
// {
// id: 'tdt-img-tiles',
// type: 'raster',
// source: 'raster-tiles',
// },
// ];
return new mapboxgl.Map({
container: mapId,
language: 'zh-cmn',
projection: 'equirectangular', // wgs84
// style: {
// glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
// version: 8,
// sources: mapDataSources,
// layers: mapDataLayers,
// },
style: {
version: 8,
sources: {},
layers: [
{
id: 'background',
type: 'background',
paint: {
'background-color': '#000', //
},
},
],
},
maxZoom: 22,
minZoom: 8,
pitch: 0,
zoom: 18,
center: props.nowPreviewRecord.boundary
? [
(props.nowPreviewRecord.boundary.start_lng + props.nowPreviewRecord.boundary.end_lng) /
2,
(props.nowPreviewRecord.boundary.start_lat + props.nowPreviewRecord.boundary.end_lat) /
2,
]
: [117.984425, 35.270654],
});
};
// 线 GeoJSON
function createGridFeatures() {
const features: any = [];
const gridSize = 10; // 10 线
for (let lat = -90; lat <= 90; lat += gridSize) {
features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[-180, lat],
[180, lat],
],
},
});
}
for (let lng = -180; lng <= 180; lng += gridSize) {
features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[lng, -90],
[lng, 90],
],
},
});
}
return { type: 'FeatureCollection', features: features };
}
onMounted(() => {
mapboxgl.accessToken = MapboxConfig.ACCESS_TOKEN;
//
// loadMapInfo();
map = initMap();
map.on('load', () => {
setImage(props.nowPreviewRecord.boundary);
move();
emits('getMap', map);
});
});
function setImage(boundary) {
let fourpoint: any = [
[parseFloat(boundary.start_lng), parseFloat(boundary.end_lat)],
[parseFloat(boundary.end_lng), parseFloat(boundary.end_lat)],
[parseFloat(boundary.end_lng), parseFloat(boundary.start_lat)],
[parseFloat(boundary.start_lng), parseFloat(boundary.start_lat)],
];
//
let image = new Image();
image.crossOrigin = 'anonymous';
image.src = props.nowPreviewRecord.url;
image.onload = () => {
// Canvas
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
// Canvas
const ctx = canvas.getContext('2d');
// Canvas
ctx.drawImage(image, 0, 0, image.width, image.height);
// CanvasURL
const dataURL = canvas.toDataURL('image/png');
//
if (map) {
if (map.getLayer('radar-layer')) {
map.removeLayer('radar-layer');
}
if (map.getSource('radar')) {
map.removeSource('radar');
}
}
//
map.addSource('radar', {
type: 'image',
url: dataURL,
coordinates: fourpoint,
});
map.addLayer({
id: 'radar-layer',
type: 'raster',
source: 'radar',
});
};
}
//
const fullscreenFlag = ref(false);
function clickExpandButton() {
const mapContainer = document.getElementById(mapId);
if (mapContainer) {
if (!document.fullscreenElement) {
mapContainer.requestFullscreen();
fullscreenFlag.value = true;
} else {
document.exitFullscreen();
fullscreenFlag.value = false;
}
}
}
//
function lookTopView() {
map.setPitch(0);
map.setBearing(0);
map.setZoom(18);
map.setCenter([
(parseFloat(props.nowPreviewRecord.boundary.start_lng) +
parseFloat(props.nowPreviewRecord.boundary.end_lng)) /
2,
(parseFloat(props.nowPreviewRecord.boundary.start_lat) +
parseFloat(props.nowPreviewRecord.boundary.end_lat)) /
2,
]);
}
//
function lookDownView() {
map.setPitch(60);
map.setBearing(0);
map.setZoom(18);
map.setCenter([
(parseFloat(props.nowPreviewRecord.boundary.start_lng) +
parseFloat(props.nowPreviewRecord.boundary.end_lng)) /
2,
(parseFloat(props.nowPreviewRecord.boundary.start_lat) +
parseFloat(props.nowPreviewRecord.boundary.end_lat)) /
2,
]);
}
//
function clickPlusButton() {
map.setZoom(map.getZoom() + 0.2);
}
//
function clickMinusButton() {
map.setZoom(map.getZoom() - 0.2);
}
const asl = ref(0);
const hae = ref(0);
function move() {
map.on('mousemove', (e) => {
const lngLat = e.lngLat;
asl.value = map.queryTerrainElevation(lngLat, { exaggerated: false }) || 0;
const geoidSeparation = 3.5;
hae.value = asl.value ? asl.value + geoidSeparation : 0;
});
}
onUnmounted(() => {
map && map.remove();
});
</script>
<style lang="scss" scoped>
.model2D {
position: relative;
width: 100%;
height: 100%;
.map {
position: relative;
width: 100%;
height: 100%;
//
.expandButton {
position: absolute;
right: 20px;
bottom: 29%;
z-index: 500;
background: #ffffff;
border-radius: 5px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
//
.angleViewButton {
z-index: 500;
position: absolute;
right: 20px;
bottom: 18%;
background: #ffffff;
border-radius: 5px;
width: 40px;
height: 80px;
display: block;
align-items: center;
justify-content: center;
}
//
.plusAndMinusButton {
z-index: 500;
position: absolute;
right: 20px;
bottom: 7%;
background: #ffffff;
border-radius: 5px;
width: 40px;
height: 80px;
display: block;
align-items: center;
justify-content: center;
}
}
.info {
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
position: absolute;
width: 100%;
height: 4%;
bottom: 0px;
left: 0px;
display: flex;
.left {
position: absolute;
top: 0px;
left: 0px;
width: 150px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right4 {
position: absolute;
top: 0px;
right: 360px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right3 {
position: absolute;
top: 0px;
right: 240px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right2 {
position: absolute;
top: 0px;
right: 120px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right1 {
position: absolute;
top: 0px;
right: 0px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.button_child {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
::v-deep .mapboxgl-ctrl {
display: none !important;
}
</style>

@ -1,232 +0,0 @@
<template>
<div class="model3D">
<div id="mapContainer" class="map_container"></div>
<div class="info">
<div class="right4">123</div>
<div class="right3"> ASL: {{ asl }}m</div>
<div class="right2"> HAE: {{ hae }}m</div>
<div class="right1">
<HeatMapOutlined style="margin-right: 15px; font-size: 20px" />
WGS 84
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as Cesium from 'cesium';
//
window.CESIUM_BASE_URL = '/node_modules/cesium/Build/Cesium/';
const props = defineProps(['nowPreviewRecord']);
//
let originalModelMatrix: any = null;
// Cesium Viewer
let viewer,
tileset: Cesium.Viewer | null = null;
const asl = ref(0);
const hae = ref(0);
const initMap = async () => {
const container = document.getElementById('mapContainer');
if (!container) {
console.error('地图容器未找到');
return;
}
viewer = new Cesium.Viewer(container, {
//
animation: false, //
baseLayerPicker: false, //
fullscreenButton: false, //
vrButton: false, // VR
geocoder: false, //
homeButton: false, //
infoBox: false, // -
sceneModePicker: false, //
selectionIndicator: false, //
timeline: false, //
navigationHelpButton: false, //
navigationInstructionsInitiallyVisible: false, //
scene3DOnly: false, // 3D
});
// logo
viewer.cesiumWidget.creditContainer.style.display = 'none';
viewer.scene.globe.enableLighting = true;
//
// viewer.scene.skyBox.show = false;
//
viewer.scene.skyAtmosphere.show = false;
viewer.scene.sun.show = false;
viewer.scene.moon.show = false;
//
viewer.scene.backgroundColor = Cesium.Color.BLACK;
//
// viewer.scene.globe.baseColor = Cesium.Color.BLACK; //
viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.1, 0.2, 1.0); //
// 齿
viewer.scene.postProcessStages.fxaa.enabled = true;
//
viewer.imageryLayers.remove(viewer.imageryLayers.get(0));
// -
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 1000.0), //
orientation: {
heading: 0.0,
pitch: -Cesium.Math.PI_OVER_TWO,
roll: 0.0,
},
});
//
// viewer.scene.globe.depthTestAgainstTerrain = true;
// //
const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(3956);
viewer.terrainProvider = terrainProvider;
//
viewer.scene.debugShowFramesPerSecond = false;
//
viewer.imageryLayers.addImageryProvider(
new Cesium.GridImageryProvider({
color: Cesium.Color.WHITE,
tileWidth: 1024,
tileHeight: 1024,
}),
);
viewer.scene.canvas.addEventListener('mousemove', function (event) {
const position = {
x: event.clientX,
y: event.clientY,
};
// 使 pickPosition
const cartesian = viewer.scene.pickPosition(position);
if (Cesium.defined(cartesian)) {
// ()
const cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
// (HAE)
hae.value = Math.round(cartographic.height * 10) / 10;
asl.value = hae.value + 3.5;
}
});
try {
const token: any =
'MTc0NzAzNTY0MXxqTllDaTdBbWZ3V2xfeDVpdG9zeXBkMHhTdzFwTW52NVRVN1d0SlUzc0NtNEtoWjhvWHZoemZHMTdMSjNzV21oR2N2eG1DWTB0cWRNT0pHalVwZU44dDR2THQ0ZUoxNmdUNTYzanJHY0xDc1pHU21VaTRHaVRqSmRfTXRNWWpoUFZhSWlmM1ZxSF9mbmxpS0hnX01oM1AwNURXSW42bEF4ejhJakJHY0JrMVU3SW9DeUJjdjVPRjFSM2RXVWJxUGhxSG1zdmdzNGJkRUlqazg5RHR2ZU1ReUJoR2s1QUlza1ZsOHU3X09uZ0JXZ0JlRllGcGhYTm5GYVJHQ2xsSHFkUlNtT0ZIS0o5WVhUNXd4a2lRU2l3bEFBZy1lN2xQV1B6NDREVEw4VXUzbGhyTURZemhwdzBTWkx4VE4teUticnZ3Z1I2NDNTa2RPOXdJN2otTkl1d3Z6NW1WZUhBV2EtTEtkVTlNNUgwZlo2VWhmVHhpTTM5ZHhuSUpVcVdrZ25EbFJwX3hObVJDdXNjZV9YblZTMlIzQmJqVGVxeXBLTTZ0VjNiLUxSZTZlTDJwRGY3c2tzWkhDNnFYbjY4alF6ZWhWTGl2NVcxQ2puMmdZZnNLaWRpeEFFM1Bwc29fSHpnSG1JZlZJN1lpNHpySUkxQzJzQlpJblBxZUl1SWo1QnNYdFQzZz09fF0gZYXPxsQ97VL9k-DRWUeNh3Prfq8ceE2jnmaNJjuq';
const resource = new Cesium.Resource({
url: '/public/Project1 2/B3DM/Model_0/tileset.json',
// url: 'https://file-storage.djicdn.com/model/19ba187f-7675-49de-b576-b00ffffba78d/dafdb629-18bf-4eb5-b33f-55fa27f1483d/dd091ab4-d8c9-4e14-9c05-bce61e8a0338/%E5%A4%A7%E7%96%86%E5%A4%A9%E7%A9%BA%E4%B9%8B%E5%9F%8E_%E4%B8%89%E7%BB%B4%E6%A8%A1%E5%9E%8B%20%281%29/Project1%202/B3DM/ktx2_b3dms/BlockA/tileset-with-auth-key.json?auth_key=1746638980-0-0-4a3cf9893bb2e579e00633c51677db98&response-content-disposition=attachment&filename=%22tileset.json%22&filename*=utf-8%27%27tileset.json',
// headers: {
// 'x-auth-token': token,
// },
});
const tileset = await Cesium.Cesium3DTileset.fromUrl(resource);
console.log(tileset);
// 3DTilesID
tileset.style = new Cesium.Cesium3DTileStyle({
// 使
color: "color('white')",
});
//
originalModelMatrix = Cesium.Matrix4.clone(tileset.modelMatrix);
//
//
// tileset.clampToGround = true;
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
} catch (error) {
console.error('加载3D Tiles数据集失败:', error);
}
};
onMounted(() => {
initMap();
});
</script>
<style lang="less" scoped>
.model3D {
position: relative;
width: 100%;
height: 100%;
}
.map_container {
position: relative;
width: 100%;
height: 100%;
}
.info {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
position: absolute;
width: 100%;
height: 4%;
bottom: 0px;
left: 0px;
display: flex;
.left {
position: absolute;
top: 0px;
left: 0px;
width: 150px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right4 {
position: absolute;
top: 0px;
right: 360px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right3 {
position: absolute;
top: 0px;
right: 240px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right2 {
position: absolute;
top: 0px;
right: 120px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right1 {
position: absolute;
top: 0px;
right: 0px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
}
::v-deep .cesium-widget-credits {
display: none !important;
}
</style>

@ -1,263 +0,0 @@
<template>
<div class="modelDiv">
<div class="title">
<div class="title-1">{{ props.nowPreviewRecord.name }}</div>
<div class="title-2">
{{ props.nowPreviewRecord.createTime }}
</div>
</div>
<div class="closeButton">
<CloseOutlined @click="closeModal" style="font-size: 25px; color: white" />
</div>
<div class="modelDiv_2D3D">
<PreviewModel2D
v-if="props.nowPreviewRecord.type.includes('2D')"
:nowPreviewRecord="nowPreviewRecord"
/>
<PreviewModel3D
v-if="props.nowPreviewRecord.type.includes('3D')"
:nowPreviewRecord="nowPreviewRecord"
/>
</div>
<div class="modelDiv_bottom">
<div class="buttonList">
<!-- 下载 -->
<div class="button">
<DownloadOutlined @click="fetchAndDownloadImage(props.nowPreviewRecord.url)" />
</div>
<!-- 删除 -->
<div class="button"> <DeleteOutlined @click="deleteCanvas" /> </div>
</div>
<div class="imageList">
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
<div
v-if="li.type.includes('model')"
:class="li.id == props.nowPreviewRecord.id ? 'bottom_div_choose' : 'bottom_div'"
>
<FileOutlined style="font-size: 60px; color: white" />
<div
v-if="li.type.includes('2D')"
style="
position: absolute;
top: 20px;
left: 35px;
font-size: 25px;
color: #ffffff;
pointer-events: none;
"
>
2D
</div>
<div
v-if="li.type.includes('3D')"
style="
position: absolute;
top: 20px;
left: 35px;
justify-content: center;
font-size: 25px;
color: #ffffff;
pointer-events: none;
"
>
3D
</div>
</div>
</div>
</div>
</div>
<!-- 上一张下一张图片 -->
<div class="leftButton" @click="clickLeftOrRightButton('left')">
<LeftOutlined style="color: #ffffff; font-size: 30px" />
</div>
<div class="rightButton" @click="clickLeftOrRightButton('right')">
<RightOutlined style="color: #ffffff; font-size: 30px" />
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch, computed } from 'vue';
import {
CloseOutlined,
RightOutlined,
LeftOutlined,
DownloadOutlined,
DeleteOutlined,
FileOutlined,
} from '@ant-design/icons-vue';
import { PreviewModel2D, PreviewModel3D } from './priview2D3D';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);
//
function clickLeftOrRightButton(direction) {
const list = props.previewRecordList.filter((item) => item.type.includes('model'));
for (let index = 0; index < list.length; index++) {
if (list[index].id == props.nowPreviewRecord.id) {
if (direction == 'left') {
if (index == 0) {
chooseNowPreviewRecord(list[list.length - 1]);
} else {
chooseNowPreviewRecord(list[index - 1]);
}
}
if (direction == 'right') {
if (index == list.length - 1) {
chooseNowPreviewRecord(list[0]);
} else {
chooseNowPreviewRecord(list[index + 1]);
}
}
}
}
}
//
function chooseNowPreviewRecord(value) {
emit('chooseNowPreviewRecord', value);
}
//
function closeModal() {
emit('closeModal');
}
</script>
<style lang="less" scoped>
.modelDiv {
position: relative;
width: 100%;
height: 920px;
display: block;
}
.title {
pointer-events: none;
position: absolute;
top: 10px;
left: 15px;
z-index: 100;
.title-1 {
font-size: 20px;
color: #ffffff;
}
.title-2 {
font-size: 16px;
color: #f0f3f3;
}
}
.closeButton {
position: absolute;
top: 10px;
right: 15px;
z-index: 50;
}
.modelDiv_2D3D {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 800px;
background: #000000;
}
.modelDiv_bottom {
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 130px;
background: #1c1c1c;
display: block;
.buttonList {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 50px;
.button {
position: relative;
padding-left: 10px;
padding-right: 10px;
color: #ffffff;
font-size: 22px;
}
}
.imageList {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 60px;
.bottom_div {
position: relative;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 20px;
padding-right: 20px;
margin-left: 5px;
margin-right: 5px;
border-radius: 10px;
background: #282828;
}
.bottom_div_choose {
position: relative;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 20px;
padding-right: 20px;
outline: 3px solid #1088f2;
margin-left: 5px;
margin-right: 5px;
border-radius: 10px;
background: #282828;
}
}
}
//
.leftButton {
position: absolute;
left: 40px;
top: 40%;
z-index: 200;
background: #9c9c9c;
border-radius: 80px;
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
}
.rightButton {
position: absolute;
right: 40px;
top: 40%;
z-index: 200;
background: #9c9c9c;
border-radius: 80px;
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
}
</style>

@ -1,2 +0,0 @@
export { default as PreviewModel2D } from './model2D.vue';
export { default as PreviewModel3D } from './model3D.vue';
Loading…
Cancel
Save