Merge branch 'main' of http://123.132.248.154:10000/gitY/DiKongGanZhiPingTai
commit
8e55946a32
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,23 @@
|
|||
import { clientReizePublish, clientReizeSubscribe } from '@/utils/mqtt';
|
||||
|
||||
export const eventsTopic = (data) => {
|
||||
// 发送消息
|
||||
clientReizePublish('thing/product/8UUXN5400A079H/events', data);
|
||||
};
|
||||
export const events_replyTopic = () => {
|
||||
// 订阅消息
|
||||
clientReizeSubscribe('thing/product/8UUXN5400A079H/events_reply');
|
||||
};
|
||||
|
||||
export const servicesTopic = (data) => {
|
||||
// 发送消息
|
||||
clientReizePublish('thing/product/8UUXN5400A079H/services', data);
|
||||
};
|
||||
export const services_replyTopic = () => {
|
||||
// 订阅消息
|
||||
clientReizeSubscribe('thing/product/8UUXN5400A079H/services_reply');
|
||||
};
|
||||
export const drcDownTopic = (data) => {
|
||||
// 发送消息thing/product/{gateway_sn}/drc/down
|
||||
clientReizePublish('thing/product/8UUXN5400A079H/drc/down', data);
|
||||
};
|
||||
|
|
@ -18,6 +18,7 @@ const connection = {
|
|||
username: 'sdhc',
|
||||
password: '',
|
||||
};
|
||||
|
||||
let client: any = {
|
||||
connected: false,
|
||||
};
|
||||
|
|
@ -105,6 +106,51 @@ const clientPublish = (topic: string, querys: any) => {
|
|||
// subscribe 事件 订阅
|
||||
// unsubscribe 事件 取消订阅
|
||||
|
||||
// 抢夺负载权、飞行控制权的时候使用
|
||||
const client_seize: any = {
|
||||
connected: false,
|
||||
};
|
||||
const createSeizeConnection = () => {
|
||||
const seizeConnection = connection;
|
||||
seizeConnection.clientId = 'mqtt_client_1581F8HGX254V00A0BUY_seize';
|
||||
try {
|
||||
const { protocol, host, port, endpoint, ...options } = seizeConnection;
|
||||
const connectUrl = `${protocol}://${host}:${port}${endpoint}`;
|
||||
client_seize = mqtt.connect(connectUrl, options);
|
||||
if (client.on) {
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('mqtt.connect error', error);
|
||||
}
|
||||
};
|
||||
const getReizeClient = () => {
|
||||
if (!client_seize || !client_seize.connected) {
|
||||
getReizeClient();
|
||||
}
|
||||
return client_seize;
|
||||
};
|
||||
// 订阅事件
|
||||
const clientReizeSubscribe = (topic: string, options?: any) => {
|
||||
console.log(client_seize);
|
||||
if (!client_seize || !client_seize.connected) {
|
||||
createConnection();
|
||||
}
|
||||
getReizeClient().subscribe(topic, { qos: 0 }, (error, res) => {
|
||||
console.log('订阅');
|
||||
console.log(error, res);
|
||||
});
|
||||
};
|
||||
// 发送消息
|
||||
const clientReizePublish = (topic: string, querys: any) => {
|
||||
if (!client_seize || !client_seize.connected) {
|
||||
createConnection();
|
||||
}
|
||||
getReizeClient().publish(topic, JSON.stringify(querys), { qos: 0 }, (err) => {
|
||||
if (err) {
|
||||
console.error('Publish error:', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
export {
|
||||
// 连接
|
||||
createConnection,
|
||||
|
|
@ -112,4 +158,8 @@ export {
|
|||
getClient,
|
||||
clientSubscribe,
|
||||
clientPublish,
|
||||
createSeizeConnection,
|
||||
getReizeClient,
|
||||
clientReizeSubscribe,
|
||||
clientReizePublish,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,12 +18,10 @@
|
|||
|
||||
const emit = defineEmits(['select']);
|
||||
|
||||
const treeData = ref([]);
|
||||
const treeData: any = ref([]);
|
||||
const asyncExpandTreeRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
async function fetch() {
|
||||
// treeData.value = (await getChildrenTree({ parentId: 0 })) as unknown as TreeItem[];
|
||||
// console.log(treeData.value);
|
||||
treeData.value = [
|
||||
{
|
||||
id: 'meitiku',
|
||||
|
|
@ -65,14 +63,14 @@
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'gas',
|
||||
name: '气体探测',
|
||||
},
|
||||
{
|
||||
id: 'vr',
|
||||
name: 'VR全景',
|
||||
},
|
||||
// {
|
||||
// id: 'gas',
|
||||
// name: '气体探测',
|
||||
// },
|
||||
// {
|
||||
// id: 'vr',
|
||||
// name: 'VR全景',
|
||||
// },
|
||||
];
|
||||
|
||||
// 显示到一级
|
||||
|
|
|
|||
|
|
@ -1,42 +1,186 @@
|
|||
<template>
|
||||
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<LeftTree ref="childRef" class="w-1/5 xl:w-1/6" @select="handleSelect" />
|
||||
<BasicTable class="w-4/5 xl:w-5/6" @register="registerTable" :searchInfo="searchInfo">
|
||||
<template #toolbar>
|
||||
<!-- <PermissionBtn @btnEvent="onBtnClicked"></PermissionBtn> -->
|
||||
<a-button :icon="h(PlusOutlined)" type="primary" @click="addFolder">新建文件夹</a-button>
|
||||
<a-button :icon="h(ColumnHeightOutlined)" @click="moveFolderOrFile">移动</a-button>
|
||||
<a-button :icon="h(DeleteOutlined)" @click="deleteFolderOrFile">删除 </a-button>
|
||||
<a-button :icon="h(DownloadOutlined)" @click="compressFolderOrFile">压缩</a-button>
|
||||
<a-radio-group v-model:value="tableType">
|
||||
<a-radio-button value="table"><BarsOutlined /></a-radio-button>
|
||||
<a-radio-button value="store"><AppstoreOutlined /></a-radio-button>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<FolderOpenOutlined v-if="record.type == 'folder'" style="font-size: 20px" />
|
||||
<img v-if="record.type == 'img'" :src="record.url" :width="30" :height="20" />
|
||||
{{ record.name }}
|
||||
</template>
|
||||
<template v-if="column.key === 'size'">
|
||||
{{ record.size ? record.size : '-' }}
|
||||
</template>
|
||||
<template v-if="column.key === 'label'">
|
||||
<div v-if="record.label && record.label.length > 0">
|
||||
<a-tag color="success" v-for="la in record.label" :key="la">{{ la }}</a-tag>
|
||||
<div class="w-4/5 xl:w-5/6">
|
||||
<BasicTable @register="registerTable" :searchInfo="searchInfo">
|
||||
<template #toolbar>
|
||||
<span v-for="f in floders" :key="f" class="floderTitle">
|
||||
<span v-if="f != '全部文件'" style="margin-right: 10px"> / </span>
|
||||
<span @click="getChildrenByProp(tableData, f)"> {{ f }}</span>
|
||||
</span>
|
||||
<div class="floderOtherButton">
|
||||
<!-- <PermissionBtn @btnEvent="onBtnClicked"></PermissionBtn> -->
|
||||
<a-button :icon="h(PlusOutlined)" type="primary" @click="addFolder">
|
||||
新建文件夹
|
||||
</a-button>
|
||||
<a-button :icon="h(ColumnHeightOutlined)" @click="moveFolderOrFile">移动</a-button>
|
||||
<a-button :icon="h(DeleteOutlined)" @click="deleteFolderOrFile">删除 </a-button>
|
||||
<a-button :icon="h(DownloadOutlined)" @click="compressFolderOrFile">压缩</a-button>
|
||||
<a-radio-group v-model:value="tableType">
|
||||
<a-radio-button value="table"><BarsOutlined /></a-radio-button>
|
||||
<a-radio-button value="store"><AppstoreOutlined /></a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-button type="text">
|
||||
<EditOutlined @click="renameRecord(record)" />
|
||||
</a-button>
|
||||
<a-button type="text" v-if="record.type != 'folder'">
|
||||
<EyeOutlined @click="lookRecord(record)" />
|
||||
</a-button>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<div v-if="tableType == 'table'">
|
||||
<template v-if="column.key === 'name'">
|
||||
<FolderOpenOutlined v-if="record.type == 'folder'" style="font-size: 20px" />
|
||||
<!-- <PlaySquareTwoTone v-if="record.type == 'video'" style="font-size: 20px" /> -->
|
||||
<img v-if="record.type == 'img'" :src="record.url" :width="30" :height="20" />
|
||||
<img
|
||||
v-if="record.type == 'video'"
|
||||
:src="record.gifZoomImage"
|
||||
:width="30"
|
||||
:height="20"
|
||||
/>
|
||||
<FileOutlined v-if="record.type.includes('model')" style="font-size: 20px" />
|
||||
<div
|
||||
v-if="record.type.includes('2D')"
|
||||
style="
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 11px;
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
background: #000000;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
2D
|
||||
</div>
|
||||
<div
|
||||
v-if="record.type.includes('3D')"
|
||||
style="
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 11px;
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
pointer-events: none;
|
||||
background: #000000;
|
||||
"
|
||||
>
|
||||
3D
|
||||
</div>
|
||||
<span
|
||||
@click="lookRecord(record)"
|
||||
@mouseover="record.isHovered = true"
|
||||
@mouseout="record.isHovered = false"
|
||||
:style="{
|
||||
textDecoration: record.isHovered ? 'underline' : 'none',
|
||||
marginLeft: '5px',
|
||||
}"
|
||||
>
|
||||
{{ record.name }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'size'">
|
||||
{{ record.size ? record.size : '-' }}
|
||||
</template>
|
||||
<template v-if="column.key === 'label'">
|
||||
<div v-if="record.label && record.label.length > 0">
|
||||
<a-tag color="success" v-for="la in record.label" :key="la">{{ la }}</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-button type="text">
|
||||
<EditOutlined @click="renameRecord(record)" />
|
||||
</a-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicTable>
|
||||
<div v-if="tableType == 'store'" class="storeDivsAllChoose">
|
||||
<a-checkbox v-model:checked="checkNameChecked" @change="changeStore($event, 'allChoose')">
|
||||
全选
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<div v-if="tableType == 'store'" class="storeDivs">
|
||||
<div
|
||||
v-for="record in showTableData"
|
||||
:key="record.id"
|
||||
class="storeDiv"
|
||||
:style="{ background: record.isHovered ? '#D5E8FC' : '#ffffff' }"
|
||||
@mouseover="record.isHovered = true"
|
||||
@mouseout="record.isHovered = false"
|
||||
>
|
||||
<div style="position: absolute; top: 0px; left: 5px">
|
||||
<a-checkbox v-model:checked="record.checked" @change="changeStore($event, record)" />
|
||||
</div>
|
||||
<FolderOpenOutlined
|
||||
v-if="record.type == 'folder'"
|
||||
style="font-size: 40px"
|
||||
@click="lookRecord(record)"
|
||||
/>
|
||||
<PlaySquareTwoTone
|
||||
v-if="record.type == 'video'"
|
||||
style="font-size: 40px"
|
||||
@click="lookRecord(record)"
|
||||
/>
|
||||
<img
|
||||
v-if="record.type == 'img'"
|
||||
:src="record.url"
|
||||
:width="60"
|
||||
:height="40"
|
||||
@click="lookRecord(record)"
|
||||
/>
|
||||
<FileOutlined
|
||||
v-if="record.type.includes('model')"
|
||||
style="font-size: 40px"
|
||||
@click="lookRecord(record)"
|
||||
/>
|
||||
<div
|
||||
v-if="record.type.includes('2D')"
|
||||
style="
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
left: 60px;
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
pointer-events: none;
|
||||
background: #000000;
|
||||
"
|
||||
@click="lookRecord(record)"
|
||||
>
|
||||
2D
|
||||
</div>
|
||||
<div
|
||||
v-if="record.type.includes('3D')"
|
||||
style="
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
left: 60px;
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
pointer-events: none;
|
||||
background: #000000;
|
||||
"
|
||||
@click="lookRecord(record)"
|
||||
>
|
||||
3D
|
||||
</div>
|
||||
<span
|
||||
:style="{
|
||||
textDecoration: record.isHovered ? 'underline' : 'none',
|
||||
position: 'absolute',
|
||||
bottom: '10px',
|
||||
left: '0px',
|
||||
width: '100%',
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="lookRecord(record)"
|
||||
>
|
||||
{{ record.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新建文件夹 -->
|
||||
<AddFolderModal @register="addFolderModal" @success="handleSuccess" />
|
||||
<!-- 移动 -->
|
||||
|
|
@ -54,6 +198,7 @@
|
|||
:closable="false"
|
||||
:footer="null"
|
||||
:destroyOnClose="true"
|
||||
:keyboard="false"
|
||||
:mask="false"
|
||||
:maskClosable="false"
|
||||
@ok="handleOk"
|
||||
|
|
@ -78,7 +223,7 @@
|
|||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h } from 'vue';
|
||||
import { reactive, ref, watch, h } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '@/components/Table';
|
||||
import { getOrgList, deleteDept } from '@/api/demo/system';
|
||||
import { PageWrapper } from '@/components/Page';
|
||||
|
|
@ -95,6 +240,8 @@
|
|||
FolderOutlined,
|
||||
FolderOpenOutlined,
|
||||
EyeOutlined,
|
||||
PlaySquareTwoTone,
|
||||
FileOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import LeftTree from './LeftTree.vue';
|
||||
import { AddFolderModal } from './modal/modal';
|
||||
|
|
@ -106,51 +253,11 @@
|
|||
import { PermissionBtn } from '@/components/PermissionBtn/index';
|
||||
import { columns, searchFormSchema } from './modal.data';
|
||||
import dayjs from 'dayjs';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { createConfirm, createMessage } = useMessage();
|
||||
const data = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '普通飞行1',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: '21',
|
||||
name: '普通飞行2-1',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: '31',
|
||||
name: '普通飞行3-1',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'img',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '22',
|
||||
name: '普通飞行2-2',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: '32',
|
||||
name: '普通飞行3-2',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'img',
|
||||
},
|
||||
{
|
||||
id: '33',
|
||||
name: '普通飞行3-3',
|
||||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'img',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{
|
||||
id: '2',
|
||||
name: '图片',
|
||||
|
|
@ -160,9 +267,9 @@
|
|||
{
|
||||
id: '1-2',
|
||||
name: '南山风景照.jpg',
|
||||
createTime: '2020-10-22 17:33:22',
|
||||
createtime: '2020-10-22 17:33:22',
|
||||
type: 'img',
|
||||
url: 'https://cdn.colorhub.me/QgpUMkZxNhU/rs:auto:0:500:0/g:ce/fn:colorhub/bG9jYWw6Ly8vZmIvNmYvMjlkMTE1NjRkNmI5ZmRhOTczYmU3ZmUyNmMyMDkwM2MwZjU5ZmI2Zi5qcGVn.webp',
|
||||
url: 'https://img2.baidu.com/it/u=257681495,312745373&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500',
|
||||
imgtype: '原片',
|
||||
taskname: '佛山大火救援项目',
|
||||
airlineName: '火灾救援勘查航线',
|
||||
|
|
@ -176,13 +283,11 @@
|
|||
label: ['人', '车'],
|
||||
lat: 35.362625,
|
||||
lng: 118.033886,
|
||||
canvasJson:
|
||||
'{"version":"4.6.0","objects":[{"type":"path","version":"4.6.0","originX":"left","originY":"top","left":520.5,"top":634,"width":0,"height":0,"fill":null,"stroke":"#ffffff","strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"round","strokeDashOffset":0,"strokeLineJoin":"round","strokeUniform":false,"strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"path":[["M",520.999,634.5],["L",521.001,634.5]]},{"type":"rect","version":"4.6.0","originX":"left","originY":"top","left":523,"top":352.5,"width":100,"height":100,"fill":"#ffffff00","stroke":"#ffffff","strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":0.73,"scaleY":0.73,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0},{"type":"textbox","version":"4.6.0","originX":"left","originY":"top","left":523,"top":426.5,"width":200,"height":22.6,"fill":"#ffffff","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"fontFamily":"Times New Roman","fontWeight":"normal","fontSize":20,"text":"太阳","underline":false,"overline":false,"linethrough":false,"textAlign":"left","fontStyle":"normal","lineHeight":1.16,"textBackgroundColor":"","charSpacing":0,"styles":{},"direction":"ltr","path":null,"pathStartOffset":0,"pathSide":"left","minWidth":20,"splitByGrapheme":false},{"type":"path","version":"4.6.0","originX":"left","originY":"top","left":784,"top":436.5,"width":190.01,"height":85.01,"fill":null,"stroke":"#ffffff","strokeWidth":4,"strokeDashArray":null,"strokeLineCap":"round","strokeDashOffset":0,"strokeLineJoin":"round","strokeUniform":false,"strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"path":[["M",785.996,438.496],["Q",786,438.5,786.5,438.5],["Q",787,438.5,790.5,440],["Q",794,441.5,798.5,443.5],["Q",803,445.5,808,448],["Q",813,450.5,820,453.5],["Q",827,456.5,880,480.5],["Q",933,504.5,940,508],["Q",947,511.5,952.5,514],["Q",958,516.5,962.5,518],["Q",967,519.5,970,521],["Q",973,522.5,974.5,523],["L",976.004,523.504]]},{"type":"path","version":"4.6.0","originX":"left","originY":"top","left":808,"top":417.5,"width":123.01,"height":124.01,"fill":null,"stroke":"#ffffff","strokeWidth":4,"strokeDashArray":null,"strokeLineCap":"round","strokeDashOffset":0,"strokeLineJoin":"round","strokeUniform":false,"strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"path":[["M",933.004,419.496],["Q",933,419.5,931.5,420.5],["Q",930,421.5,928,425],["Q",926,428.5,921.5,433],["Q",917,437.5,914,441.5],["Q",911,445.5,905.5,450.5],["Q",900,455.5,894,462],["Q",888,468.5,883,473.5],["Q",878,478.5,872,484.5],["Q",866,490.5,861.5,495],["Q",857,499.5,851,504.5],["Q",845,509.5,841.5,513],["Q",838,516.5,833.5,520],["Q",829,523.5,826,526.5],["Q",823,529.5,820,533],["Q",817,536.5,815,538],["Q",813,539.5,812.5,540.5],["Q",812,541.5,811,542.5],["L",809.996,543.504]]}],"backgroundImage":{"type":"image","version":"4.6.0","originX":"left","originY":"top","left":0,"top":0,"width":889,"height":500,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1.46,"scaleY":1.6,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"cropX":0,"cropY":0,"src":"https://cdn.colorhub.me/QgpUMkZxNhU/rs:auto:0:500:0/g:ce/fn:colorhub/bG9jYWw6Ly8vZmIvNmYvMjlkMTE1NjRkNmI5ZmRhOTczYmU3ZmUyNmMyMDkwM2MwZjU5ZmI2Zi5qcGVn.webp","crossOrigin":null,"filters":[]}}',
|
||||
},
|
||||
{
|
||||
id: '1-3',
|
||||
name: '风景图.jpg',
|
||||
createTime: '2020-10-22 17:33:22',
|
||||
createtime: '2020-10-22 17:33:22',
|
||||
type: 'img',
|
||||
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
|
||||
imgtype: '原片2',
|
||||
|
|
@ -196,15 +301,13 @@
|
|||
photographMan: 'zachzhou',
|
||||
photographTime: '2020-10-22 00:00:00',
|
||||
label: ['人', '车'],
|
||||
lat: 35.362625,
|
||||
lat: 35.362825,
|
||||
lng: 118.033886,
|
||||
canvasJson:
|
||||
'{"version":"4.6.0","objects":[],"backgroundImage":{"type":"image","version":"4.6.0","originX":"left","originY":"top","left":0,"top":0,"width":2000,"height":1334,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":0.65,"scaleY":0.6,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"cropX":0,"cropY":0,"src":"https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871","crossOrigin":null,"filters":[]}}',
|
||||
},
|
||||
{
|
||||
id: '1-4',
|
||||
name: '红外照片.jpg',
|
||||
createTime: '2020-10-22 17:33:22',
|
||||
createtime: '2020-10-22 17:33:22',
|
||||
type: 'img',
|
||||
url: 'https://pic.rmb.bdstatic.com/bjh/gallery/8c885a0e3cf0647b60548535e2e9ca39.jpeg',
|
||||
imgtype: '红外照片',
|
||||
|
|
@ -218,13 +321,13 @@
|
|||
photographMan: 'zachzhou',
|
||||
photographTime: '2020-10-22 00:00:00',
|
||||
label: ['人', '车'],
|
||||
lat: 35.362625,
|
||||
lat: 35.362925,
|
||||
lng: 118.033886,
|
||||
},
|
||||
{
|
||||
id: '1-5',
|
||||
name: '广角照片.jpg',
|
||||
createTime: '2020-10-22 17:33:22',
|
||||
createtime: '2020-10-22 17:33:22',
|
||||
type: 'img',
|
||||
url: 'https://img2.baidu.com/it/u=2490853491,3226002419&fm=253&fmt=auto&app=138&f=JPEG?w=749&h=500',
|
||||
imgtype: '广角照片',
|
||||
|
|
@ -239,12 +342,12 @@
|
|||
photographTime: '2020-10-22 00:00:00',
|
||||
label: ['人', '车'],
|
||||
lat: 35.362625,
|
||||
lng: 118.033886,
|
||||
lng: 118.033286,
|
||||
},
|
||||
{
|
||||
id: '1-6',
|
||||
name: '变焦照片.jpg',
|
||||
createTime: '2020-10-22 17:33:22',
|
||||
createtime: '2020-10-22 17:33:22',
|
||||
type: 'img',
|
||||
url: 'https://img2.baidu.com/it/u=3778652155,475195343&fm=253&fmt=auto&app=138&f=PNG?w=500&h=518',
|
||||
imgtype: '变焦照片',
|
||||
|
|
@ -258,8 +361,8 @@
|
|||
photographMan: 'zachzhou',
|
||||
photographTime: '2020-10-22 00:00:00',
|
||||
label: ['人', '车'],
|
||||
lat: 35.362625,
|
||||
lng: 118.033886,
|
||||
lat: 35.362725,
|
||||
lng: 118.033086,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -269,38 +372,48 @@
|
|||
createtime: '2025-03-24 18:13:17',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: '3-4',
|
||||
name: 'XZD153狼窝沟西南',
|
||||
createTime: '',
|
||||
type: 'video',
|
||||
url: '74b95e6575d741489b9a9061bb646467',
|
||||
manufacturer: '海康',
|
||||
},
|
||||
// {
|
||||
// id: '3-4',
|
||||
// name: 'XZD153狼窝沟西南',
|
||||
// createtime: '',
|
||||
// type: 'video',
|
||||
// url: '74b95e6575d741489b9a9061bb646467',
|
||||
// manufacturer: '海康',
|
||||
// },
|
||||
{
|
||||
id: '3-5',
|
||||
name: '费县马庄镇陈家鱼后村南斜坡后村',
|
||||
createTime: '',
|
||||
createtime: '',
|
||||
type: 'video',
|
||||
url: 'http://111.36.45.20:18000/flv/hls/H-dcb1ea7388588111.flv',
|
||||
gifZoomImage: 'https://img.soogif.com/mrGHcO3xjFJnJ986TeL9oAr2BYfPIaM7.gif',
|
||||
manufacturer: '腾讯',
|
||||
},
|
||||
{
|
||||
id: '3-6',
|
||||
name: '可落',
|
||||
createTime: '',
|
||||
id: '3-8',
|
||||
name: 'movie',
|
||||
createtime: '',
|
||||
type: 'video',
|
||||
url: '8H03AA1PAG8D9BF',
|
||||
manufacturer: '乐橙',
|
||||
},
|
||||
{
|
||||
id: '3-7',
|
||||
name: '费县薛庄镇东张林村村南可见光',
|
||||
createTime: '',
|
||||
type: 'video',
|
||||
url: '37130100181328000392',
|
||||
manufacturer: '青犀',
|
||||
url: 'https://www.runoob.com/try/demo_source/mov_bbb.mp4',
|
||||
gifZoomImage: 'https://img.soogif.com/mrGHcO3xjFJnJ986TeL9oAr2BYfPIaM7.gif',
|
||||
manufacturer: '腾讯',
|
||||
},
|
||||
// {
|
||||
// id: '3-6',
|
||||
// name: '可落',
|
||||
// createtime: '',
|
||||
// type: 'video',
|
||||
// url: '8H03AA1PAG8D9BF',
|
||||
// manufacturer: '乐橙',
|
||||
// },
|
||||
// {
|
||||
// id: '3-7',
|
||||
// name: '费县薛庄镇东张林村村南可见光',
|
||||
// createtime: '',
|
||||
// type: 'video',
|
||||
// url: '37130100181328000392',
|
||||
// manufacturer: '青犀',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -342,52 +455,83 @@
|
|||
],
|
||||
},
|
||||
]);
|
||||
|
||||
// 展示数据
|
||||
const showTableData = ref(cloneDeep(tableData.value));
|
||||
const floders = ref(['全部文件']);
|
||||
// 表格还是文件夹
|
||||
const tableType = ref('table');
|
||||
const tableHeight: any = ref(0);
|
||||
watch(
|
||||
() => tableType.value,
|
||||
(newval) => {
|
||||
// 表格
|
||||
const containers = document.querySelectorAll('.ant-table-container');
|
||||
if (newval) {
|
||||
tableHeight.value = containers[0] ? containers[0]?.scrollHeight : 0;
|
||||
} else {
|
||||
tableHeight.value = 0;
|
||||
}
|
||||
if (containers) {
|
||||
containers.forEach((container) => {
|
||||
container.style.display = tableType.value === 'table' ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
// 分页
|
||||
const paginations = document.querySelectorAll('.ant-pagination');
|
||||
if (paginations) {
|
||||
paginations.forEach((pagination) => {
|
||||
pagination.style.display = tableType.value === 'table' ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const searchInfo = reactive<Recordable>({});
|
||||
const searchParams = ref();
|
||||
const [registerTable, { reload, getSelectRows, getDataSource, clearSelectedRowKeys }] = useTable({
|
||||
// api: getOrgList,
|
||||
dataSource: data,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
striped: false,
|
||||
bordered: false,
|
||||
inset: false,
|
||||
tableSetting: {
|
||||
redo: false,
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showIndexColumn: false,
|
||||
showTableSetting: true,
|
||||
handleSearchInfoFn(info) {
|
||||
console.log(info);
|
||||
console.log(searchInfo.value);
|
||||
|
||||
searchParams.value = info;
|
||||
return info;
|
||||
},
|
||||
beforeFetch: (data) => {
|
||||
// 接口请求前 参数处理
|
||||
var temp = {
|
||||
startTime: dayjs(data.startTime).startOf('month').format('YYYY-MM-DD'),
|
||||
endTime: dayjs(data.endTime).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
return temp;
|
||||
},
|
||||
afterFetch: (res) => {
|
||||
console.log(res);
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload, getSelectRows, setSelectedRows, clearSelectedRowKeys }] =
|
||||
useTable({
|
||||
// api: getOrgList,
|
||||
// title: '全部文件',
|
||||
dataSource: showTableData,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
isTreeTable: false,
|
||||
striped: false,
|
||||
bordered: false,
|
||||
inset: false,
|
||||
tableSetting: {
|
||||
redo: false,
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showIndexColumn: false,
|
||||
showTableSetting: true,
|
||||
handleSearchInfoFn(info) {
|
||||
console.log(info);
|
||||
console.log(searchInfo.value);
|
||||
searchParams.value = info;
|
||||
return info;
|
||||
},
|
||||
beforeFetch: (data) => {
|
||||
// 接口请求前 参数处理
|
||||
var temp = {
|
||||
startTime: dayjs(data.startTime).startOf('month').format('YYYY-MM-DD'),
|
||||
endTime: dayjs(data.endTime).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
return temp;
|
||||
},
|
||||
afterFetch: (res) => {
|
||||
console.log(res);
|
||||
},
|
||||
});
|
||||
|
||||
function handleSelect(orgId = '') {
|
||||
searchInfo.orgId = orgId;
|
||||
|
|
@ -419,7 +563,7 @@
|
|||
if (rows[0].type == 'folder') {
|
||||
record = rows[0];
|
||||
} else {
|
||||
record = findParentIdById(getDataSource(), rows[0].id);
|
||||
record = findParentIdById(tableData.value, rows[0].id);
|
||||
}
|
||||
}
|
||||
openAddFolderModal(true, {
|
||||
|
|
@ -452,7 +596,7 @@
|
|||
if (rows.length > 0) {
|
||||
const record = rows;
|
||||
openMoveFileModal(true, {
|
||||
tableData: getDataSource(),
|
||||
tableData: tableData.value,
|
||||
record,
|
||||
});
|
||||
} else {
|
||||
|
|
@ -490,7 +634,7 @@
|
|||
if (rows.length > 0) {
|
||||
const record = rows;
|
||||
openCompressFileModal(true, {
|
||||
tableData: getDataSource(),
|
||||
tableData: tableData.value,
|
||||
record,
|
||||
});
|
||||
} else {
|
||||
|
|
@ -511,13 +655,46 @@
|
|||
const nowPreviewRecord: any = ref();
|
||||
const previewRecordList: any = ref([]);
|
||||
function lookRecord(record) {
|
||||
open.value = true;
|
||||
nowPreviewRecord.value = record;
|
||||
previewRecordList.value = findParentIdById(getDataSource(), record.id)?.children;
|
||||
if (!previewRecordList.value) {
|
||||
previewRecordList.value = getDataSource();
|
||||
if (record.type == 'folder') {
|
||||
showTableData.value = record.children;
|
||||
floders.value.push(record.name);
|
||||
} else {
|
||||
open.value = true;
|
||||
nowPreviewRecord.value = record;
|
||||
previewRecordList.value = findParentIdById(tableData.value, record.id)?.children;
|
||||
if (!previewRecordList.value) {
|
||||
previewRecordList.value = tableData.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 目录跳跃
|
||||
function getChildrenByProp(children, name) {
|
||||
if (name == '全部文件') {
|
||||
showTableData.value = cloneDeep(tableData.value);
|
||||
floders.value = ['全部文件'];
|
||||
clearSelectedRowKeys();
|
||||
return;
|
||||
}
|
||||
for (const node of children) {
|
||||
if (node.name === name) {
|
||||
showTableData.value = cloneDeep(node.children);
|
||||
const index = floders.value.indexOf(name);
|
||||
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
|
||||
clearSelectedRowKeys();
|
||||
return node.children || [];
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
const result = getChildrenByProp(node.children, node.name);
|
||||
if (result) {
|
||||
const index = floders.value.indexOf(name);
|
||||
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
|
||||
clearSelectedRowKeys();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 图片选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
|
|
@ -525,6 +702,108 @@
|
|||
}
|
||||
// 关闭
|
||||
function closeModal() {
|
||||
document.body.style.cursor = 'auto';
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
const checkNameList: any = ref([]);
|
||||
const checkNameChecked = ref(false);
|
||||
function changeStore(e, record) {
|
||||
if (typeof record == 'string') {
|
||||
if (e.target.checked) {
|
||||
checkNameChecked.value = true;
|
||||
showTableData.value = showTableData.value.map((item) => ({
|
||||
...item,
|
||||
checked: true,
|
||||
}));
|
||||
} else {
|
||||
checkNameChecked.value = false;
|
||||
showTableData.value = showTableData.value.map((item) => ({
|
||||
...item,
|
||||
checked: false,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if (showTableData.value.filter((item) => item.checked).length == showTableData.value.length) {
|
||||
checkNameChecked.value = true;
|
||||
} else {
|
||||
checkNameChecked.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.floderTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::v-deep .vben-basic-table-header__toolbar {
|
||||
display: flex !important;
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
.floderOtherButton {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
::v-deep .floderTitle .ant-btn {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
::v-deep .floderTitle .ant-btn-text {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
::v-deep .ant-table-row-expand-icon {
|
||||
display: none !important;
|
||||
}
|
||||
::v-deep .ant-table-row-expand-icon-collapsed {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.storeDivsAllChoose {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
margin: 0px 0px 16px 16px;
|
||||
padding-left: 16px;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.storeDivs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
height: 566px;
|
||||
background: #ffffff;
|
||||
margin: 0px 16px 16px 16px;
|
||||
// gap: 20px;
|
||||
|
||||
.storeDiv {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 120px;
|
||||
outline: 1px solid #000000;
|
||||
margin: 16px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .vben-basic-table {
|
||||
height: fit-content !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const searchFormSchema: FormSchema[] = [
|
|||
field: 'label',
|
||||
label: '标签',
|
||||
component: 'Select',
|
||||
colProps: { span: 8 },
|
||||
colProps: { span: 6 },
|
||||
componentProps: {
|
||||
mode: 'multiple',
|
||||
options: [
|
||||
|
|
@ -61,19 +61,19 @@ export const searchFormSchema: FormSchema[] = [
|
|||
field: 'equipmentName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 5 },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
field: '[startTime, endTime]',
|
||||
label: '时间选择',
|
||||
component: 'RangePicker',
|
||||
colProps: { span: 6 },
|
||||
colProps: { span: 6},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '文件名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 5 },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
export const formGroupSchema: FormSchema[] = [
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
<template>
|
||||
<div :id="mapId" class="map"> </div>
|
||||
<div :id="mapId" class="map">
|
||||
<div class="mapInfo">
|
||||
<span> {{ props.nowPreviewRecord.lat }}°N</span>
|
||||
<span> {{ props.nowPreviewRecord.lng }}°E</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, ref, onMounted, onUnmounted, defineEmits, computed } from 'vue';
|
||||
import { defineProps, ref, onMounted, onUnmounted, defineEmits, watch } from 'vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import mapboxgl, { Map } from 'mapbox-gl';
|
||||
import { MapboxConfig, MapboxDefaultStyle } from '@/components/MapboxMaps/src/config';
|
||||
|
|
@ -15,9 +20,10 @@
|
|||
MinusOutlined,
|
||||
HeatMapOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const props = defineProps(['nowPreviewRecord']);
|
||||
const emits = defineEmits(['getMap']);
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList', 'hideOrShowTextboxFlag']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
const mapId = `modal-map-${uuidv4()}`;
|
||||
const networkType = ref('WAN');
|
||||
|
|
@ -54,8 +60,7 @@
|
|||
source: 'raster-tiles',
|
||||
},
|
||||
];
|
||||
|
||||
return new mapboxgl.Map({
|
||||
map = new mapboxgl.Map({
|
||||
container: mapId,
|
||||
language: 'zh-cmn',
|
||||
projection: 'equirectangular', // wgs84参考系
|
||||
|
|
@ -69,19 +74,133 @@
|
|||
minZoom: 8,
|
||||
pitch: 0,
|
||||
zoom: 14,
|
||||
center: [117.984425, 35.270654],
|
||||
center: [parseFloat(props.nowPreviewRecord.lng), parseFloat(props.nowPreviewRecord.lat)],
|
||||
});
|
||||
setImage();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.hideOrShowTextboxFlag,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
props.previewRecordList.forEach((item) => {
|
||||
if (!map.getLayer(item.id)) {
|
||||
const coordinates = [parseFloat(item.lng), parseFloat(item.lat)];
|
||||
// 图片
|
||||
map.addLayer({
|
||||
id: item.id,
|
||||
type: 'symbol',
|
||||
source: {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: coordinates,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
'icon-image': item.id,
|
||||
'icon-size': 40 / item.width,
|
||||
// 关键配置:始终显示
|
||||
'icon-allow-overlap': true, // 允许图标重叠时也显示
|
||||
'icon-ignore-placement': true, // 忽略自动布局规则,强制显示
|
||||
visibility: 'visible', // 显式设置为可见
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let filters = cloneDeep(props.previewRecordList).filter(
|
||||
(item) => item.id != props.nowPreviewRecord.id,
|
||||
);
|
||||
filters.forEach((fl) => {
|
||||
if (map.getLayer(fl.id)) {
|
||||
map.removeLayer(fl.id);
|
||||
}
|
||||
if (map.getSource(fl.id)) {
|
||||
map.removeSource(fl.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function setImage() {
|
||||
props.previewRecordList.forEach((item) => {
|
||||
map.on('style.load', () => {
|
||||
map.loadImage(item.url, (error, image) => {
|
||||
if (error) {
|
||||
console.error('加载图片失败:', error.message);
|
||||
return;
|
||||
}
|
||||
if (!map.hasImage(item.id)) {
|
||||
map.addImage(item.id, image);
|
||||
}
|
||||
const coordinates = [parseFloat(item.lng), parseFloat(item.lat)];
|
||||
if (!map.getLayer(item.id)) {
|
||||
// 图片
|
||||
map.addLayer({
|
||||
id: item.id,
|
||||
type: 'symbol',
|
||||
source: {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: coordinates,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
'icon-image': item.id,
|
||||
'icon-size': 40 / item.width,
|
||||
// 关键配置:始终显示
|
||||
'icon-allow-overlap': true, // 允许图标重叠时也显示
|
||||
'icon-ignore-placement': true, // 忽略自动布局规则,强制显示
|
||||
visibility: 'visible', // 显式设置为可见
|
||||
},
|
||||
});
|
||||
map.on('click', item.id, function (e) {
|
||||
clickHandler(item);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
mapboxgl.accessToken = MapboxConfig.ACCESS_TOKEN;
|
||||
// 获取网络环境后加载地图
|
||||
map = initMap();
|
||||
initMap();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
map && map.remove();
|
||||
props.previewRecordList.forEach((item) => {
|
||||
map.off('click', item.id, function (e) {
|
||||
clickHandler(item);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 点击事件
|
||||
function clickHandler(item) {
|
||||
emit('chooseNowPreviewRecord', item);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -91,6 +210,30 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .mapboxgl-ctrl {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export { default as PreviewImage } from './previewImage.vue';
|
||||
export { default as PreviewCanvas } from './previewCanvas.vue';
|
||||
export { default as PreviewVideo } from './previewVideo.vue';
|
||||
export { default as PreviewInformation } from './previewInformation.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';
|
||||
|
|
|
|||
|
|
@ -18,18 +18,13 @@
|
|||
<div class="mainBody">
|
||||
<div class="imgOrVideo">
|
||||
<!-- 图片 -->
|
||||
<div class="image" v-if="props.nowPreviewRecord.type == 'img'">
|
||||
<!-- <PreviewCanvas
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
@reloadTable="reloadTable"
|
||||
/> -->
|
||||
<div class="imageDiv" v-if="props.nowPreviewRecord.type == 'img'">
|
||||
<PreviewImage
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
@reloadTable="reloadTable"
|
||||
@setHideOrShowTextboxFlag="setHideOrShowTextboxFlag"
|
||||
/>
|
||||
</div>
|
||||
<!-- VR全景 -->
|
||||
|
|
@ -37,12 +32,31 @@
|
|||
<PanoViewer />
|
||||
</div> -->
|
||||
<!-- 视频 -->
|
||||
<div class="video" v-if="props.nowPreviewRecord.type == 'video'">
|
||||
<PreviewVideo :nowPreviewRecord="props.nowPreviewRecord" />
|
||||
<div class="videoDiv" v-if="props.nowPreviewRecord.type == 'video'">
|
||||
<PreviewVideo
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
@reloadTable="reloadTable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="information">
|
||||
<PreviewInformation :nowPreviewRecord="props.nowPreviewRecord" @reloadTable="reloadTable" />
|
||||
<PreviewImageInformation
|
||||
v-if="props.nowPreviewRecord.type == 'img'"
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
@reloadTable="reloadTable"
|
||||
:hideOrShowTextboxFlag="hideOrShowTextboxFlag"
|
||||
/>
|
||||
<PreviewVideoInformation
|
||||
v-if="props.nowPreviewRecord.type == 'video'"
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
@reloadTable="reloadTable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -51,15 +65,20 @@
|
|||
import { ref } from 'vue';
|
||||
import { CloseOutlined } from '@ant-design/icons-vue';
|
||||
import { PreviewImage } from './preview';
|
||||
import { PreviewCanvas } from './preview';
|
||||
import { PreviewVideo } from './preview';
|
||||
import { PreviewInformation } from './preview';
|
||||
import { PreviewImageInformation } from './preview';
|
||||
import { PreviewVideoInformation } from './preview';
|
||||
import { PanoViewer } from './preview';
|
||||
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
console.log(props.nowPreviewRecord);
|
||||
console.log(props.previewRecordList);
|
||||
|
||||
const hideOrShowTextboxFlag = ref(true);
|
||||
function setHideOrShowTextboxFlag(value) {
|
||||
hideOrShowTextboxFlag.value = value;
|
||||
}
|
||||
|
||||
// 选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
|
|
@ -71,13 +90,14 @@
|
|||
}
|
||||
// 关闭弹窗
|
||||
function closeModal() {
|
||||
document.body.style.cursor = 'default';
|
||||
emit('closeModal');
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.modal {
|
||||
width: 100%;
|
||||
height: 1020px;
|
||||
height: 920px;
|
||||
|
||||
// 页面不能被选中
|
||||
-webkit-user-select: none; /* Safari */
|
||||
|
|
@ -109,7 +129,7 @@
|
|||
}
|
||||
.mainBody {
|
||||
width: 100%;
|
||||
height: 1020px;
|
||||
height: 920px;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
|
@ -117,10 +137,10 @@
|
|||
|
||||
.imgOrVideo {
|
||||
width: 80%;
|
||||
height: 1020px;
|
||||
height: 920px;
|
||||
background: #101010;
|
||||
|
||||
.image {
|
||||
.imageDiv {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
position: relative;
|
||||
|
|
@ -128,6 +148,13 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.videoDiv {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.information {
|
||||
|
|
|
|||
|
|
@ -1,583 +0,0 @@
|
|||
<template>
|
||||
<div class="image">
|
||||
<div class="canvas">
|
||||
<canvas
|
||||
width="1300"
|
||||
height="800"
|
||||
id="canvas"
|
||||
:style="{
|
||||
transform: `scale(${scale}) rotate(${rotationAngle}deg)`,
|
||||
transition: 'transform 0.2s',
|
||||
}"
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="buttonList">
|
||||
<!-- 放大 -->
|
||||
<div class="button"> <ZoomInOutlined @click="zoomIn" /> </div>
|
||||
<!-- 缩小 -->
|
||||
<div class="button"> <ZoomOutOutlined @click="zoomOut" /> </div>
|
||||
<!-- 顺时针旋转 -->
|
||||
<div class="button"> <RotateRightOutlined @click="rotateClockwise" /> </div>
|
||||
<!-- 逆时针旋转 -->
|
||||
<div class="button"> <RotateLeftOutlined @click="rotateCounterClockwise" /> </div>
|
||||
|
|
||||
<!-- 刷新 -->
|
||||
<div class="button"> <RedoOutlined @click="refreshCanvas" /> </div>
|
||||
<!-- 涂鸦 -->
|
||||
<div class="tipChoose" v-if="graffitiFlag">
|
||||
<n-color-picker size="small" :modes="['rgb']" v-model:value="graffitiColor" />
|
||||
<a-slider v-model:value="graffitiWidth" :min="1" :max="10" />
|
||||
</div>
|
||||
<div class="button" :style="{ background: graffitiFlag ? 'blue' : '' }">
|
||||
<HighlightOutlined
|
||||
:style="{
|
||||
color: graffitiColor,
|
||||
}"
|
||||
@click="
|
||||
graffitiFlag = !graffitiFlag;
|
||||
rectFlag = false;
|
||||
textboxFlag = false;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<!-- 标注 -->
|
||||
<div class="tipChoose" v-if="rectFlag">
|
||||
<n-color-picker size="small" :modes="['rgb']" v-model:value="rectColor" />
|
||||
</div>
|
||||
<div class="button" :style="{ background: rectFlag ? 'blue' : '' }">
|
||||
<BorderOutlined
|
||||
@click="
|
||||
graffitiFlag = false;
|
||||
rectFlag = !rectFlag;
|
||||
textboxFlag = false;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<!-- 文字 -->
|
||||
<div class="tipChoose" v-if="textboxFlag">
|
||||
<n-color-picker size="small" :modes="['rgb']" v-model:value="textboxColor" />
|
||||
<a-slider v-model:value="textboxFontSize" :min="10" :max="50" />
|
||||
</div>
|
||||
<div class="button" :style="{ background: textboxFlag ? 'blue' : '' }">
|
||||
<FontSizeOutlined
|
||||
@click="
|
||||
graffitiFlag = false;
|
||||
rectFlag = false;
|
||||
textboxFlag = !textboxFlag;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<!-- 保存 -->
|
||||
<div class="button"> <CheckOutlined @click="checkCanvas" /> </div>
|
||||
<!-- 删除 -->
|
||||
<div class="button"> <CloseOutlined @click="deleteCanvas" /> </div>
|
||||
<!-- 隐藏or显示涂鸦和标签 -->
|
||||
<div class="button">
|
||||
<EyeOutlined @click="hideOrShowGraffiti(false)" v-if="hideOrShowGraffitiFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowGraffiti(true)" v-if="!hideOrShowGraffitiFlag" />
|
||||
<div style="position: absolute; bottom: 0px; right: 0px; font-size: 10px">涂鸦</div>
|
||||
</div>
|
||||
<div class="button">
|
||||
<EyeOutlined @click="hideOrShowTextbox(false)" v-if="hideOrShowTextboxFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowTextbox(true)" v-if="!hideOrShowTextboxFlag" />
|
||||
<div style="position: absolute; bottom: 0px; right: 0px; font-size: 10px">标签</div>
|
||||
</div>
|
||||
|
|
||||
<!-- 复制到剪贴板 -->
|
||||
<div class="button">
|
||||
<ExportOutlined @click="copyToClipboard(props.nowPreviewRecord.url)" />
|
||||
</div>
|
||||
<!-- 下载 -->
|
||||
<div class="button">
|
||||
<DownloadOutlined @click="fetchAndDownloadImage(props.nowPreviewRecord.url)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="imageList">
|
||||
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
||||
<div
|
||||
:class="li.id == props.nowPreviewRecord.id ? 'bottom_div_choose' : 'bottom_div'"
|
||||
v-if="li.type == 'img'"
|
||||
>
|
||||
<img :src="li.url" :width="60" :height="40" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="leftButton" @click="clickLeftOrRightButton('left')">
|
||||
<LeftOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
<div class="rightButton" @click="clickLeftOrRightButton('right')">
|
||||
<RightOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch, computed } from 'vue';
|
||||
import { fabric } from 'fabric';
|
||||
import {
|
||||
CloseOutlined,
|
||||
RightOutlined,
|
||||
LeftOutlined,
|
||||
ZoomOutOutlined,
|
||||
ZoomInOutlined,
|
||||
RotateLeftOutlined,
|
||||
RotateRightOutlined,
|
||||
RedoOutlined,
|
||||
FileImageOutlined,
|
||||
ExportOutlined,
|
||||
DownloadOutlined,
|
||||
DeleteOutlined,
|
||||
HighlightOutlined,
|
||||
CheckOutlined,
|
||||
FontSizeOutlined,
|
||||
BorderOutlined,
|
||||
EyeOutlined,
|
||||
EyeInvisibleOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { json } from 'stream/consumers';
|
||||
|
||||
const { createConfirm, createMessage } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
// 上一张、下一张图片
|
||||
function clickLeftOrRightButton(direction) {
|
||||
const list = props.previewRecordList.filter((item) => item.type == 'img');
|
||||
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);
|
||||
// 初始化
|
||||
scale.value = 1;
|
||||
rotationAngle.value = 0;
|
||||
setBackgroudUrl(value);
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
function reloadTable() {
|
||||
emit('reloadTable');
|
||||
}
|
||||
|
||||
// 画布
|
||||
const nowCanvasJson = ref(props.nowPreviewRecord.canvasJson);
|
||||
let canvas: any = null;
|
||||
|
||||
// 设置背景图
|
||||
function setBackgroudUrl(value) {
|
||||
nowCanvasJson.value = value.canvasJson;
|
||||
canvas.loadFromJSON(value.canvasJson);
|
||||
fabric.Image.fromURL(value.url, (img) => {
|
||||
// 设置背景图
|
||||
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
|
||||
scaleX: canvas.width / img.width,
|
||||
scaleY: canvas.height / img.height,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 设置画笔---------------------------------------------------------------
|
||||
const graffitiFlag = ref(false);
|
||||
const graffitiColor = ref('#ffffff');
|
||||
const graffitiWidth = ref(1);
|
||||
|
||||
watch(
|
||||
() => (graffitiFlag.value, graffitiColor.value, graffitiWidth.value),
|
||||
() => {
|
||||
setGraffiti();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
// 设置画笔
|
||||
function setGraffiti() {
|
||||
scale.value = 1;
|
||||
rotationAngle.value = 0;
|
||||
// 设置画笔开启
|
||||
canvas.isDrawingMode = graffitiFlag.value;
|
||||
// 设置画笔颜色
|
||||
canvas.freeDrawingBrush.color = graffitiColor.value;
|
||||
// 设置画笔粗细
|
||||
canvas.freeDrawingBrush.width = graffitiWidth.value;
|
||||
}
|
||||
|
||||
// 设置文本---------------------------------------------------------------
|
||||
const textboxFlag = ref(false);
|
||||
const textboxColor = ref('#ffffff');
|
||||
const textboxFontSize = ref(20);
|
||||
|
||||
watch(
|
||||
() => textboxFlag.value,
|
||||
() => {
|
||||
if (textboxFlag.value) {
|
||||
setTextbox();
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => (textboxColor.value, textboxFontSize.value),
|
||||
() => {
|
||||
setTextboxStyle();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
// 新增文本
|
||||
function setTextbox() {
|
||||
scale.value = 1;
|
||||
rotationAngle.value = 0;
|
||||
addClickEvent();
|
||||
}
|
||||
// 设置文本样式
|
||||
function setTextboxStyle() {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
if (activeObj) {
|
||||
console.log(activeObj);
|
||||
activeObj.fill = textboxColor.value;
|
||||
activeObj.fontSize = textboxFontSize.value;
|
||||
canvas.setActiveObject(activeObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置矩形---------------------------------------------------------------
|
||||
const rectFlag = ref(false);
|
||||
const rectColor = ref('#ffffff');
|
||||
|
||||
watch(
|
||||
() => rectFlag.value,
|
||||
() => {
|
||||
if (rectFlag.value) {
|
||||
setRect();
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => rectColor.value,
|
||||
() => {
|
||||
setRectStyle();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
// 新增矩形
|
||||
function setRect() {
|
||||
scale.value = 1;
|
||||
rotationAngle.value = 0;
|
||||
addClickEvent();
|
||||
}
|
||||
// 设置矩形样式
|
||||
function setRectStyle() {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
if (activeObj) {
|
||||
activeObj.stroke = rectColor.value;
|
||||
canvas.setActiveObject(activeObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加画布点击事件
|
||||
function addClickEvent() {
|
||||
canvas.on('mouse:down', (options) => {
|
||||
if (textboxFlag.value) {
|
||||
// 将文本框添加到画布中
|
||||
const textbox = new fabric.Textbox('标注', {
|
||||
width: 200,
|
||||
fill: textboxColor.value,
|
||||
fontSize: textboxFontSize.value,
|
||||
top: options.absolutePointer.y,
|
||||
left: options.absolutePointer.x,
|
||||
});
|
||||
canvas.add(textbox);
|
||||
removeClickEvent();
|
||||
}
|
||||
if (rectFlag.value) {
|
||||
// 将矩形添加到画布中
|
||||
const rect = new fabric.Rect({
|
||||
top: options.absolutePointer.y,
|
||||
left: options.absolutePointer.x,
|
||||
fill: '#ffffff00',
|
||||
stroke: rectColor.value,
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
canvas.add(rect);
|
||||
removeClickEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
// 移除画布点击事件
|
||||
function removeClickEvent() {
|
||||
canvas.off('mouse:down');
|
||||
}
|
||||
|
||||
// 缩放比例-----------------------------------
|
||||
const scale = ref(1);
|
||||
// 放大函数
|
||||
function zoomIn() {
|
||||
if (scale.value < 3) {
|
||||
// 设置最大缩放倍数为3倍
|
||||
scale.value += 0.1;
|
||||
}
|
||||
}
|
||||
// 缩小函数
|
||||
function zoomOut() {
|
||||
if (scale.value > 0.5) {
|
||||
// 设置最小缩放倍数为0.5倍
|
||||
scale.value -= 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转-----------------------------------
|
||||
const rotationAngle = ref(0);
|
||||
// 顺时针旋转函数
|
||||
function rotateClockwise() {
|
||||
rotationAngle.value += 90; // 每次旋转90度
|
||||
}
|
||||
// 逆时针旋转
|
||||
function rotateCounterClockwise() {
|
||||
rotationAngle.value -= 90; // 每次旋转-90度
|
||||
}
|
||||
|
||||
// 保存-----------------------------------
|
||||
function checkCanvas() {
|
||||
nowCanvasJson.value = JSON.stringify(canvas);
|
||||
props.nowPreviewRecord.canvasJson = JSON.stringify(canvas);
|
||||
textboxFlag.value = false;
|
||||
graffitiFlag.value = false;
|
||||
rectFlag.value = false;
|
||||
console.log(JSON.stringify(canvas));
|
||||
createMessage.success('保存成功!');
|
||||
}
|
||||
|
||||
// 删除
|
||||
function deleteCanvas() {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
canvas.remove(activeObj);
|
||||
textboxFlag.value = false;
|
||||
graffitiFlag.value = false;
|
||||
rectFlag.value = false;
|
||||
}
|
||||
|
||||
// 刷新
|
||||
function refreshCanvas() {
|
||||
scale.value = 1;
|
||||
rotationAngle.value = 0;
|
||||
setBackgroudUrl(props.nowPreviewRecord);
|
||||
}
|
||||
|
||||
// 复制到剪贴板-----------------------------------
|
||||
const copyToClipboard = async (url) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
createMessage.success('图片链接已复制到剪贴板');
|
||||
} catch (err) {
|
||||
createMessage.error('无法复制图片链接');
|
||||
}
|
||||
};
|
||||
|
||||
// 下载
|
||||
async function fetchAndDownloadImage(url) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
mode: 'cors',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const urlObject = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = urlObject;
|
||||
link.download = props.nowPreviewRecord.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(urlObject);
|
||||
} catch (error) {
|
||||
console.error('Error downloading image:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏涂鸦
|
||||
const hideOrShowGraffitiFlag = ref(true);
|
||||
function hideOrShowGraffiti(value) {
|
||||
hideOrShowGraffitiFlag.value = value;
|
||||
let json = JSON.parse(nowCanvasJson.value);
|
||||
json.objects.forEach((item) => {
|
||||
if (item.type === 'path') {
|
||||
item.visible = value;
|
||||
}
|
||||
});
|
||||
nowCanvasJson.value = JSON.stringify(json);
|
||||
canvas.loadFromJSON(nowCanvasJson.value);
|
||||
}
|
||||
|
||||
// 隐藏标签
|
||||
const hideOrShowTextboxFlag = ref(true);
|
||||
function hideOrShowTextbox(value) {
|
||||
hideOrShowTextboxFlag.value = value;
|
||||
let json = JSON.parse(nowCanvasJson.value);
|
||||
json.objects.forEach((item) => {
|
||||
if (item.type === 'textbox' || item.type === 'rect') {
|
||||
item.visible = value;
|
||||
}
|
||||
});
|
||||
nowCanvasJson.value = JSON.stringify(json);
|
||||
canvas.loadFromJSON(nowCanvasJson.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
canvas = new fabric.Canvas('canvas');
|
||||
// 背景图片
|
||||
setBackgroudUrl(props.nowPreviewRecord);
|
||||
// json
|
||||
if (!nowCanvasJson.value) {
|
||||
nowCanvasJson.value = JSON.stringify(canvas);
|
||||
}
|
||||
// 涂鸦
|
||||
setGraffiti();
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 900px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: #1c1c1c;
|
||||
display: block;
|
||||
|
||||
.buttonList {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
|
||||
.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 {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bottom_div_choose {
|
||||
padding: 4px;
|
||||
border: 1px solid yellow;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.leftButton {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 45%;
|
||||
z-index: 1000;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.rightButton {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 45%;
|
||||
z-index: 1000;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.eyeButton {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 45px;
|
||||
background: #ffffff;
|
||||
width: 50px;
|
||||
height: 100px;
|
||||
}
|
||||
.tipChoose {
|
||||
position: absolute;
|
||||
background: #1c1c1c;
|
||||
color: #ffffff;
|
||||
padding: 5px;
|
||||
top: 35px;
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="image">
|
||||
<div class="canvas" @mouseleave="document.body.style.cursor = 'auto'">
|
||||
<div class="previewImage">
|
||||
<div id="imageDiv" class="imageDiv">
|
||||
<div
|
||||
ref="mouseCanvasRef"
|
||||
@mousedown="onMouseDown"
|
||||
|
|
@ -43,12 +43,18 @@
|
|||
<a-input
|
||||
v-model:value="rect.text"
|
||||
style="width: 110px; height: 30px; margin-right: 10px"
|
||||
@keypress.enter="
|
||||
nowGraffiti = -1;
|
||||
rect.status = 'success';
|
||||
addGraffiti();
|
||||
"
|
||||
/>
|
||||
<CheckOutlined
|
||||
style="margin-right: 10px; color: green"
|
||||
@click="
|
||||
nowGraffiti = -1;
|
||||
rect.status = 'success';
|
||||
addGraffiti();
|
||||
"
|
||||
/>
|
||||
<CloseOutlined
|
||||
|
|
@ -62,7 +68,7 @@
|
|||
<DeleteOutlined
|
||||
style="margin-right: 10px"
|
||||
@click="
|
||||
graffitis.splice(index, 1);
|
||||
deleteGraffiti(index, rect.text);
|
||||
nowGraffiti = -1;
|
||||
"
|
||||
/>
|
||||
|
|
@ -243,34 +249,135 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 全屏 -->
|
||||
<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>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="buttonList">
|
||||
<!-- 放大 -->
|
||||
<div class="button"> <ZoomInOutlined @click="zoomIn" /> </div>
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>放大</span>
|
||||
</template>
|
||||
<ZoomInOutlined @click="zoomIn" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 缩小 -->
|
||||
<div class="button"> <ZoomOutOutlined @click="zoomOut" /> </div>
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>缩小</span>
|
||||
</template>
|
||||
<ZoomOutOutlined @click="zoomOut" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 顺时针旋转 -->
|
||||
<div class="button"> <RotateRightOutlined @click="rotateClockwise" /> </div>
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>顺时针旋转</span>
|
||||
</template>
|
||||
<RotateRightOutlined @click="rotateClockwise" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 逆时针旋转 -->
|
||||
<div class="button"> <RotateLeftOutlined @click="rotateCounterClockwise" /> </div>
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>逆时针旋转</span>
|
||||
</template>
|
||||
<RotateLeftOutlined @click="rotateCounterClockwise" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 刷新 -->
|
||||
<div class="button"> <RedoOutlined @click="refresh" /> </div>
|
||||
|
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>刷新</span>
|
||||
</template>
|
||||
<RedoOutlined @click="refresh" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<span style="color: #ffffff; margin-left: 5px; margin-right: 5px">|</span>
|
||||
<!-- 全屏 -->
|
||||
<!-- <div class="button"> <RedoOutlined @click="refresh" /> </div> -->
|
||||
|
|
||||
<!-- 对比 -->
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>对比</span>
|
||||
</template>
|
||||
<BorderHorizontalOutlined @click="c" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 加载到地图上 -->
|
||||
<div class="button2" @click="funAddOrRemoveToMap">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>
|
||||
{{ props.nowPreviewRecord.addOrRemoveToMap ? '在地图上取消加载' : '在地图上加载' }}
|
||||
</span>
|
||||
</template>
|
||||
<svg
|
||||
v-if="props.nowPreviewRecord.addOrRemoveToMap"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 24 24"
|
||||
width="25"
|
||||
height="25"
|
||||
>
|
||||
<path
|
||||
d="M19 5v11.17l2 2V5c0-1.1-.9-2-2-2H5.83l2 2H19zM2.81 2.81L1.39 4.22L3 5.83V19c0 1.1.9 2 2 2h13.17l1.61 1.61l1.41-1.41L2.81 2.81zM5 19V7.83l7.07 7.07l-.82 1.1L9 13l-3 4h8.17l2 2H5z"
|
||||
fill="#ffffff"
|
||||
></path>
|
||||
</svg>
|
||||
<svg
|
||||
v-if="!props.nowPreviewRecord.addOrRemoveToMap"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 24 24"
|
||||
width="25"
|
||||
height="25"
|
||||
>
|
||||
<path
|
||||
d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14L6 17h12l-3.86-5.14z"
|
||||
fill="#ffffff"
|
||||
></path>
|
||||
</svg>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 复制到剪贴板 -->
|
||||
<div class="button">
|
||||
<ExportOutlined @click="copyToClipboard(props.nowPreviewRecord.url)" />
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>图片链接复制到剪贴板</span>
|
||||
</template>
|
||||
<ExportOutlined @click="copyToClipboard(props.nowPreviewRecord.url)" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 下载 -->
|
||||
<div class="button">
|
||||
<DownloadOutlined @click="fetchAndDownloadImage(props.nowPreviewRecord.url)" />
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>下载</span>
|
||||
</template>
|
||||
<DownloadOutlined @click="fetchAndDownloadImage(props.nowPreviewRecord.url)" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 删除 -->
|
||||
<div class="button"> <DeleteOutlined @click="deleteCanvas" /> </div>
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>删除</span>
|
||||
</template>
|
||||
<DeleteOutlined @click="deleteImage" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="imageList">
|
||||
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
||||
|
|
@ -293,11 +400,7 @@
|
|||
</div>
|
||||
|
||||
<!-- 涂鸦颜色选择-提示 -->
|
||||
<div
|
||||
class="graffitiButton"
|
||||
@click="setGraffiti"
|
||||
:style="graffitiFlag ? 'outline: 2px solid #2B85E4' : ''"
|
||||
>
|
||||
<div @click="setGraffiti" :class="graffitiFlag ? 'graffitiButton_choose' : 'graffitiButton'">
|
||||
<a-popover placement="left">
|
||||
<template #content>
|
||||
<div style="display: flex; gap: 5px">
|
||||
|
|
@ -323,42 +426,64 @@
|
|||
<!-- 隐藏or显示涂鸦和标签 -->
|
||||
<div class="showTextboxClass">
|
||||
<div class="button">
|
||||
<EyeOutlined @click="hideOrShowGraffiti(false)" v-if="hideOrShowGraffitiFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowGraffiti(true)" v-if="!hideOrShowGraffitiFlag" />
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
font-size: 10px;
|
||||
color: #000000;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
涂鸦
|
||||
</div>
|
||||
<a-tooltip placement="left">
|
||||
<template #title>
|
||||
<span>{{ hideOrShowGraffitiFlag ? '点击隐藏涂鸦信息' : '点击显示涂鸦信息' }}</span>
|
||||
</template>
|
||||
<EyeOutlined @click="hideOrShowGraffiti(false)" v-if="hideOrShowGraffitiFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowGraffiti(true)" v-if="!hideOrShowGraffitiFlag" />
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
font-size: 10px;
|
||||
color: #000000;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
涂鸦
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="button">
|
||||
<EyeOutlined @click="hideOrShowTextbox(false)" v-if="hideOrShowTextboxFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowTextbox(true)" v-if="!hideOrShowTextboxFlag" />
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
font-size: 10px;
|
||||
color: #000000;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
标注
|
||||
</div>
|
||||
<a-tooltip placement="left">
|
||||
<template #title>
|
||||
<span>
|
||||
{{
|
||||
hideOrShowTextboxFlag
|
||||
? '点击隐藏地图上的其他标注信息'
|
||||
: '点击显示地图上的其他标注信息'
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<EyeOutlined @click="hideOrShowTextbox(false)" v-if="hideOrShowTextboxFlag" />
|
||||
<EyeInvisibleOutlined @click="hideOrShowTextbox(true)" v-if="!hideOrShowTextboxFlag" />
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
font-size: 10px;
|
||||
color: #000000;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
标注
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 退出涂鸦 -->
|
||||
<div class="escTip" v-if="graffitiFlag">
|
||||
<div class="whiteEsc">Esc</div>
|
||||
<div class="blackTip">退出涂鸦</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch, computed } from 'vue';
|
||||
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
|
||||
import {
|
||||
CloseOutlined,
|
||||
RightOutlined,
|
||||
|
|
@ -376,6 +501,10 @@
|
|||
EditOutlined,
|
||||
FontColorsOutlined,
|
||||
RedoOutlined,
|
||||
FileImageOutlined,
|
||||
BorderHorizontalOutlined,
|
||||
ExpandOutlined,
|
||||
CompressOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
|
@ -383,7 +512,7 @@
|
|||
const { createConfirm, createMessage } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable', 'setHideOrShowTextboxFlag']);
|
||||
|
||||
// 宽高
|
||||
const getImageWidthAndHeight = computed(() => {
|
||||
|
|
@ -477,7 +606,7 @@
|
|||
}
|
||||
|
||||
// 删除
|
||||
function deleteCanvas() {}
|
||||
function deleteImage() {}
|
||||
|
||||
// 复制到剪贴板-----------------------------------
|
||||
const copyToClipboard = async (url) => {
|
||||
|
|
@ -489,6 +618,16 @@
|
|||
}
|
||||
};
|
||||
|
||||
// 加载到地图上
|
||||
function funAddOrRemoveToMap() {
|
||||
props.nowPreviewRecord.addOrRemoveToMap = !props.nowPreviewRecord.addOrRemoveToMap;
|
||||
if (props.nowPreviewRecord.addOrRemoveToMap) {
|
||||
createMessage.success('在地图上加载成功');
|
||||
} else {
|
||||
createMessage.success('在地图上取消加载成功');
|
||||
}
|
||||
}
|
||||
|
||||
// 下载
|
||||
async function fetchAndDownloadImage(url) {
|
||||
try {
|
||||
|
|
@ -522,6 +661,7 @@
|
|||
const hideOrShowTextboxFlag = ref(true);
|
||||
function hideOrShowTextbox(value) {
|
||||
hideOrShowTextboxFlag.value = value;
|
||||
emit('setHideOrShowTextboxFlag', value);
|
||||
}
|
||||
|
||||
// 设置画笔---------------------------------------------------------------
|
||||
|
|
@ -529,7 +669,7 @@
|
|||
const graffitiColor = ref('#E23C39');
|
||||
const nowGraffiti = ref(-1);
|
||||
const nowMouseGraffiti = ref(0);
|
||||
|
||||
// 设置鼠标格式
|
||||
function setGraffiti() {
|
||||
graffitiFlag.value = !graffitiFlag.value;
|
||||
if (graffitiFlag.value) {
|
||||
|
|
@ -564,14 +704,33 @@
|
|||
);
|
||||
watch(
|
||||
() => graffitis.value,
|
||||
() => {
|
||||
props.nowPreviewRecord.graffitiJson = JSON.stringify(graffitis.value);
|
||||
},
|
||||
() => {},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
// 添加
|
||||
function addGraffiti() {
|
||||
props.nowPreviewRecord.graffitiJson = JSON.stringify(graffitis.value);
|
||||
graffitis.value.forEach((item) => {
|
||||
if (!props.nowPreviewRecord.label.includes(item.text)) {
|
||||
if (item.text) {
|
||||
props.nowPreviewRecord.label.push(item.text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 删除
|
||||
function deleteGraffiti(index = undefined, value = undefined) {
|
||||
graffitis.value.splice(index, 1);
|
||||
// 刷新or删除标签
|
||||
props.nowPreviewRecord.graffitiJson = JSON.stringify(graffitis.value);
|
||||
if (!graffitis.value.some((item) => item.text == value)) {
|
||||
props.nowPreviewRecord.label = props.nowPreviewRecord.label.filter((la) => la != value);
|
||||
}
|
||||
}
|
||||
|
||||
const mouseCanvasRef = ref();
|
||||
// 鼠标按下
|
||||
function onMouseDown(e) {
|
||||
|
|
@ -741,23 +900,61 @@
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(() => {});
|
||||
// 添加键盘事件监听器
|
||||
const addEventListener = () => {
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
};
|
||||
// 移除键盘事件监听器
|
||||
const removeEventListener = () => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
};
|
||||
// 处理按键按下事件
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.code === 'Escape' && graffitiFlag.value) {
|
||||
setGraffiti();
|
||||
}
|
||||
};
|
||||
|
||||
// 全屏
|
||||
const fullscreenFlag = ref(false);
|
||||
function clickExpandButton() {
|
||||
const mapContainer = document.getElementById('imageDiv');
|
||||
if (mapContainer) {
|
||||
if (!document.fullscreenElement) {
|
||||
mapContainer.requestFullscreen();
|
||||
fullscreenFlag.value = true;
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
fullscreenFlag.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在组件挂载时添加监听器
|
||||
onMounted(() => {
|
||||
addEventListener();
|
||||
});
|
||||
// 在组件卸载时移除监听器
|
||||
onBeforeUnmount(() => {
|
||||
removeEventListener();
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.image {
|
||||
.previewImage {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 900px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
.imageDiv {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 920px;
|
||||
height: 820px;
|
||||
background: #101010;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
|
|
@ -781,9 +978,22 @@
|
|||
position: relative;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
color: #ffffff;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.button2 {
|
||||
padding-top: 3px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.imageList {
|
||||
|
|
@ -854,6 +1064,26 @@
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.graffitiButton_choose {
|
||||
background: linear-gradient(to bottom left, #2b85e4 10px, transparent 1px), #ffffff;
|
||||
background-size:
|
||||
100% 100%,
|
||||
auto;
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 5%;
|
||||
z-index: 200;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 3px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
outline: 2px solid #2b85e4;
|
||||
}
|
||||
|
||||
.popoverClass {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
|
@ -883,11 +1113,58 @@
|
|||
font-size: 22px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: 1px solid #000000;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 全屏
|
||||
.expandButton {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 15%;
|
||||
z-index: 500;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.escTip {
|
||||
position: absolute;
|
||||
left: 0%;
|
||||
bottom: 100px;
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: #9c9c9c77;
|
||||
border-radius: 5px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.whiteEsc {
|
||||
height: 30px;
|
||||
background: #ffffff;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
margin-left: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.blackTip {
|
||||
display: flex;
|
||||
margin-left: 5px;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,459 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="title"> 详细信息 </div>
|
||||
<div class="infoDiv">
|
||||
<a-row>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">图片名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue_name" v-if="editNameFlag">
|
||||
{{ props.nowPreviewRecord.name }}
|
||||
<EditOutlined style="font-size: 20px; color: #07aaed" @click="editNameChange" />
|
||||
</span>
|
||||
<span class="infovalue_name" v-if="!editNameFlag">
|
||||
<a-input v-model:value="editName" size="small" />
|
||||
<CheckOutlined
|
||||
style="margin-left: 10px; color: green"
|
||||
@click="pressEnterNameFunction"
|
||||
/>
|
||||
<CloseOutlined style="margin-left: 10px; color: red" @click="editNameBlur" />
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片类型</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.imgtype }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">任务名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.taskname }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">航线名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.airlineName }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片分辨率</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.width + '*' + props.nowPreviewRecord.height }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片大小</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.size }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄飞机</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographFeiji }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄负载</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographNumber }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄人员</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographMan }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄时间</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographTime }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">标签</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue_graffitiNum">
|
||||
<a-tag color="success" v-for="la in props.nowPreviewRecord.label" :key="la">
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<PlusSquareOutlined style="font-size: 20px; color: #07aaed" @click="addLabelChange" />
|
||||
</span>
|
||||
<a-modal
|
||||
title="标签设置"
|
||||
:open="addLabelFlag"
|
||||
:mask="false"
|
||||
:maskClosable="false"
|
||||
:closable="false"
|
||||
:footer="null"
|
||||
>
|
||||
<div style="display: block">
|
||||
<div
|
||||
style="display: flex; flex-wrap: wrap; gap: 5px; width: 96%; margin: 10px"
|
||||
v-if="props.nowPreviewRecord.label.length > 0"
|
||||
>
|
||||
已添加的标签:
|
||||
<div
|
||||
v-for="la in props.nowPreviewRecord.label"
|
||||
:key="la"
|
||||
style="border: 1px solid #595959; border-radius: 3px"
|
||||
>
|
||||
<span style="margin-left: 5px">{{ la }}</span>
|
||||
<CloseOutlined
|
||||
style="margin-left: 5px; margin-right: 5px"
|
||||
@click="deleteLabel(la)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: inline-flex; gap: 5px; width: 96%; margin: 10px">
|
||||
<a-input v-model:value="newLabelName" size="small" placeholder="请输入标签" />
|
||||
<a-button type="primary" @click="pressEnterLabelFunction">添加</a-button>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
display: inline-flex;
|
||||
gap: 5px;
|
||||
width: 96%;
|
||||
margin: 5px 10px 20px 10px;
|
||||
justify-content: flex-end;
|
||||
"
|
||||
>
|
||||
<a-button
|
||||
@click="
|
||||
addLabelFlag = false;
|
||||
newLabelName = '';
|
||||
"
|
||||
>
|
||||
关闭
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">涂鸦总数</span>
|
||||
</a-col>
|
||||
<a-col
|
||||
:span="17"
|
||||
style="
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
"
|
||||
@mouseenter="showGraffitiNum = true"
|
||||
@mouseleave="showGraffitiNum = false"
|
||||
>
|
||||
<div class="graffitiNum">
|
||||
<EditOutlined style="color: #ffffff" />
|
||||
<span>
|
||||
{{
|
||||
props.nowPreviewRecord.graffitiJson
|
||||
? JSON.parse(props.nowPreviewRecord.graffitiJson).length
|
||||
: 0
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="showGraffitiNum"
|
||||
style="position: absolute; top: 36px; left: 5px; width: 60%; z-index: 1000"
|
||||
:style="{
|
||||
height: `${options.length * 30}px`,
|
||||
}"
|
||||
>
|
||||
<div v-for="op in options" :key="op.text" style="width: 100%; height: 30px">
|
||||
<div class="graffitiNum2">
|
||||
<span>
|
||||
{{ op.text }}
|
||||
</span>
|
||||
<span>
|
||||
{{ op.num }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片位置</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infobutton">
|
||||
<EnvironmentOutlined
|
||||
v-if="props.nowPreviewRecord.addOrRemoveToMap"
|
||||
style="font-size: 20px; color: #07aaed"
|
||||
@click="flyPoint"
|
||||
/>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div class="map">
|
||||
<Map
|
||||
ref="mapRef"
|
||||
:nowPreviewRecord="props.nowPreviewRecord"
|
||||
:previewRecordList="props.previewRecordList"
|
||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||
:hideOrShowTextboxFlag="props.hideOrShowTextboxFlag"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import {
|
||||
EditOutlined,
|
||||
PlusSquareOutlined,
|
||||
EnvironmentOutlined,
|
||||
CheckOutlined,
|
||||
CloseOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { orgPosGroup } from '@/api/demo/system';
|
||||
import { Map } from './preview';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList', 'hideOrShowTextboxFlag']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
// 修改名称--------------------------------
|
||||
const editNameFlag = ref(true);
|
||||
const editName = ref(props.nowPreviewRecord.name.split('.').slice(0, -1).join('.'));
|
||||
function editNameChange() {
|
||||
if (props.nowPreviewRecord.name.split('.').length <= 1) {
|
||||
editName.value = props.nowPreviewRecord.name;
|
||||
}
|
||||
editNameFlag.value = false;
|
||||
}
|
||||
// 修改名称方法
|
||||
async function pressEnterNameFunction() {
|
||||
let newName: any = null;
|
||||
if (props.nowPreviewRecord.name.split('.').length <= 1) {
|
||||
newName = editName.value;
|
||||
} else {
|
||||
newName = editName.value + '.' + props.nowPreviewRecord.name.split('.').pop();
|
||||
}
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newName: newName,
|
||||
};
|
||||
props.nowPreviewRecord.name = newName;
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
}
|
||||
function editNameBlur() {
|
||||
editNameFlag.value = true;
|
||||
editName.value = props.nowPreviewRecord.name.split('.').slice(0, -1).join('.');
|
||||
}
|
||||
|
||||
// 标签--------------------------------
|
||||
const addLabelFlag = ref(false);
|
||||
const newLabelName = ref('');
|
||||
function addLabelChange() {
|
||||
addLabelFlag.value = true;
|
||||
}
|
||||
// 添加标签方法
|
||||
async function pressEnterLabelFunction() {
|
||||
if (!newLabelName.value) {
|
||||
return;
|
||||
}
|
||||
if (!props.nowPreviewRecord.label.includes(newLabelName.value)) {
|
||||
props.nowPreviewRecord.label.push(newLabelName.value);
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newLabel: props.nowPreviewRecord.label,
|
||||
};
|
||||
addLabelFlag.value = true;
|
||||
chooseNowPreviewRecord({
|
||||
...props.nowPreviewRecord,
|
||||
label: props.nowPreviewRecord.label,
|
||||
});
|
||||
|
||||
emit('reloadTable');
|
||||
newLabelName.value = '';
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
} else {
|
||||
return createMessage.error('此标签已存在!');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
function deleteLabel(value) {
|
||||
createConfirm({
|
||||
iconType: 'info',
|
||||
title: '提醒',
|
||||
content: '删除标签【' + value + '】,同标签名的涂鸦标记都会被删除!',
|
||||
onOk: () => {
|
||||
props.nowPreviewRecord.label = props.nowPreviewRecord.label.filter(
|
||||
(item) => item !== value,
|
||||
);
|
||||
let json = JSON.parse(props.nowPreviewRecord.graffitiJson);
|
||||
if (json.some((item) => item.text == value)) {
|
||||
json = json.filter((item) => item.text !== value);
|
||||
props.nowPreviewRecord.graffitiJson = JSON.stringify(json);
|
||||
}
|
||||
chooseNowPreviewRecord({
|
||||
...props.nowPreviewRecord,
|
||||
label: props.nowPreviewRecord.label,
|
||||
graffitiJson: JSON.stringify(json),
|
||||
});
|
||||
emit('reloadTable');
|
||||
},
|
||||
onCancel: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
const showGraffitiNum = ref(false);
|
||||
const options = computed(() => {
|
||||
if (props.nowPreviewRecord.graffitiJson) {
|
||||
const map = {};
|
||||
// 遍历数组,统计每个 text 出现的次数
|
||||
JSON.parse(props.nowPreviewRecord.graffitiJson).forEach((item) => {
|
||||
const key = item.text;
|
||||
if (map[key]) {
|
||||
map[key].num += 1;
|
||||
} else {
|
||||
map[key] = { text: key, num: 1 };
|
||||
}
|
||||
});
|
||||
// 将 map 的值转成数组返回
|
||||
return Object.values(map);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
// 选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
emit('chooseNowPreviewRecord', value);
|
||||
}
|
||||
|
||||
// 地图--------------------------------
|
||||
const mapRef = ref();
|
||||
function flyPoint() {
|
||||
mapRef.value?.flyToPoint([props.nowPreviewRecord.lng, props.nowPreviewRecord.lat]);
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 60px;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.infoDiv {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
height: 480px;
|
||||
|
||||
.infotitle {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.infovalue {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
max-width: 250px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.infovalue_name {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.infovalue_graffitiNum {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
max-width: 250px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.infobutton {
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.graffitiNum {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
width: 60%;
|
||||
background: #3c3c3c;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.graffitiNum2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
width: 95%;
|
||||
background: #1c1c1c;
|
||||
height: 40px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,271 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="title"> 详细信息 </div>
|
||||
<div class="info">
|
||||
<a-row>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">图片名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue" v-if="editNameFlag">
|
||||
{{ props.nowPreviewRecord.name }}
|
||||
<EditOutlined style="font-size: 20px; color: #07aaed" @click="editNameChange" />
|
||||
</span>
|
||||
<span class="infovalue" v-if="!editNameFlag">
|
||||
<a-input
|
||||
v-model:value="editName"
|
||||
size="small"
|
||||
@keypress.enter="pressEnterNameFunction"
|
||||
@blur="editNameBlur"
|
||||
/>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片类型</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.imgtype }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">任务名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.taskname }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">航线名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.airlineName }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片分辨率</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.width + '*' + props.nowPreviewRecord.height }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片大小</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.size }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄飞机</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographFeiji }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄负载</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographNumber }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄人员</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographMan }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄时间</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographTime }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">标签</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue" v-if="addLabelFlag">
|
||||
<a-tag color="success" v-for="la in props.nowPreviewRecord.label" :key="la">
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<PlusSquareOutlined style="font-size: 20px; color: #07aaed" @click="addLabelChange" />
|
||||
</span>
|
||||
<span class="infovalue" v-if="!addLabelFlag">
|
||||
<a-tag
|
||||
color="success"
|
||||
v-for="la in props.nowPreviewRecord.label"
|
||||
:key="la"
|
||||
closable
|
||||
@close="deleteLabel(la)"
|
||||
>
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<a-input
|
||||
v-model:value="newLabelName"
|
||||
size="small"
|
||||
placeholder=""
|
||||
@keypress.enter="pressEnterLabelFunction"
|
||||
@blur="addLabelBlur"
|
||||
/>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">涂鸦总数</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.label }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片位置</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infobutton">
|
||||
<EnvironmentOutlined style="font-size: 20px; color: #07aaed" @click="flyPoint" />
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div class="map">
|
||||
<Map ref="mapRef" :nowPreviewRecord="props.nowPreviewRecord" />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { EditOutlined, PlusSquareOutlined, EnvironmentOutlined } from '@ant-design/icons-vue';
|
||||
import { orgPosGroup } from '@/api/demo/system';
|
||||
import { Map } from './preview';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord']);
|
||||
const emit = defineEmits(['reloadTable']);
|
||||
|
||||
// 修改名称--------------------------------
|
||||
const editNameFlag = ref(true);
|
||||
const editName = ref(props.nowPreviewRecord.name.split('.').slice(0, -1).join('.'));
|
||||
function editNameChange() {
|
||||
editNameFlag.value = false;
|
||||
}
|
||||
// 修改名称方法
|
||||
async function pressEnterNameFunction() {
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newName: editName.value + '.' + props.nowPreviewRecord.name.split('.').pop(),
|
||||
};
|
||||
props.nowPreviewRecord.name =
|
||||
editName.value + '.' + props.nowPreviewRecord.name.split('.').pop();
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
}
|
||||
function editNameBlur() {
|
||||
editNameFlag.value = true;
|
||||
editName.value = props.nowPreviewRecord.name.split('.').slice(0, -1).join('.');
|
||||
}
|
||||
|
||||
// 标签--------------------------------
|
||||
const addLabelFlag = ref(true);
|
||||
const newLabelName = ref('');
|
||||
function addLabelChange() {
|
||||
addLabelFlag.value = false;
|
||||
}
|
||||
// 添加标签方法
|
||||
async function pressEnterLabelFunction() {
|
||||
if (!newLabelName.value) {
|
||||
return;
|
||||
}
|
||||
if (!props.nowPreviewRecord.label.includes(newLabelName.value)) {
|
||||
props.nowPreviewRecord.label.push(newLabelName.value);
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newLabel: props.nowPreviewRecord.label,
|
||||
};
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
newLabelName.value = '';
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
} else {
|
||||
return createMessage.error('此标签已存在!');
|
||||
}
|
||||
}
|
||||
|
||||
function deleteLabel(value) {
|
||||
props.nowPreviewRecord.label = props.nowPreviewRecord.label.filter((item) => item !== value);
|
||||
emit('reloadTable');
|
||||
}
|
||||
function addLabelBlur() {
|
||||
addLabelFlag.value = true;
|
||||
newLabelName.value = '';
|
||||
}
|
||||
|
||||
// 地图--------------------------------
|
||||
const mapRef = ref();
|
||||
function flyPoint() {
|
||||
mapRef.value?.flyToPoint([props.nowPreviewRecord.lng, props.nowPreviewRecord.lat]);
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 60px;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.info {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
height: 480px;
|
||||
|
||||
.infotitle {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.infovalue {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.infobutton {
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,46 +1,245 @@
|
|||
<template>
|
||||
<div>
|
||||
<MonitorHK
|
||||
<div class="videoDiv">
|
||||
<div class="showVideo">
|
||||
<!-- <MonitorHK
|
||||
v-if="props.nowPreviewRecord.manufacturer == '海康'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1380"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
/>
|
||||
<MonitorLC
|
||||
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
|
||||
:deviceId="props.nowPreviewRecord.url"
|
||||
:channelId="0"
|
||||
:width="1396"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
:videoMuted="true"
|
||||
/>
|
||||
<MonitorTX
|
||||
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1396"
|
||||
:height="900"
|
||||
:videoLoop="false"
|
||||
:videoMuted="true"
|
||||
:videoFit="'contain'"
|
||||
/>
|
||||
<MonitorQX
|
||||
/> -->
|
||||
<!-- <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="1396"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
:videoLoop="false"
|
||||
:videoMuted="true"
|
||||
:videoFit="'contain'"
|
||||
/>
|
||||
/> -->
|
||||
<video :src="props.nowPreviewRecord.url" class="video-player" controls muted autoplay></video>
|
||||
</div>
|
||||
|
||||
<div class="bottomDiv">
|
||||
<div class="buttonList">
|
||||
<!-- 下载 -->
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>下载</span>
|
||||
</template>
|
||||
<DownloadOutlined @click="fetchAndDownloadVideo(props.nowPreviewRecord.url)" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 删除 -->
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>删除</span>
|
||||
</template>
|
||||
<DeleteOutlined @click="deleteVideo" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="imageList">
|
||||
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
||||
<div
|
||||
:class="li.id == props.nowPreviewRecord.id ? 'bottom_div_choose' : 'bottom_div'"
|
||||
v-if="li.type == 'video'"
|
||||
>
|
||||
<img :src="li.gifZoomImage" :width="60" :height="40" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 上一张、下一张图片 -->
|
||||
<div class="leftButton" @click="clickLeftOrRightButton('left')">
|
||||
<LeftOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
<div class="rightButton" @click="clickLeftOrRightButton('right')">
|
||||
<RightOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
DownloadOutlined,
|
||||
DeleteOutlined,
|
||||
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']);
|
||||
console.log(props.nowPreviewRecord);
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
// 上一张、下一张图片
|
||||
function clickLeftOrRightButton(direction) {
|
||||
const list = props.previewRecordList.filter((item) => item.type == 'video');
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 下载
|
||||
async function fetchAndDownloadVideo(url) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
mode: 'cors',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const urlObject = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = urlObject;
|
||||
link.download = props.nowPreviewRecord.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(urlObject);
|
||||
} catch (error) {
|
||||
console.error('Error downloading image:', error);
|
||||
}
|
||||
}
|
||||
// 删除
|
||||
function deleteVideo() {}
|
||||
// 选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
emit('chooseNowPreviewRecord', value);
|
||||
}
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
<style lang="less">
|
||||
.videoDiv {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.showVideo {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 820px;
|
||||
}
|
||||
|
||||
.bottomDiv {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: #1c1c1c;
|
||||
display: block;
|
||||
|
||||
.buttonList {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.imageList {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
|
||||
.bottom_div {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bottom_div_choose {
|
||||
padding: 4px;
|
||||
border: 1px solid yellow;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 上一张、下一张
|
||||
.leftButton {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 45%;
|
||||
z-index: 200;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.rightButton {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 45%;
|
||||
z-index: 200;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.video-player {
|
||||
height: 820px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,376 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="title"> 详细信息 </div>
|
||||
<div class="infoDiv">
|
||||
<a-row>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">视频名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue_name" v-if="editNameFlag">
|
||||
{{ props.nowPreviewRecord.name }}
|
||||
<EditOutlined style="font-size: 20px; color: #07aaed" @click="editNameChange" />
|
||||
</span>
|
||||
<span class="infovalue_name" v-if="!editNameFlag">
|
||||
<a-input v-model:value="editName" size="small" style="width: 80%" />
|
||||
<CheckOutlined
|
||||
style="margin-left: 10px; color: green"
|
||||
@click="pressEnterNameFunction"
|
||||
/>
|
||||
<CloseOutlined style="margin-left: 10px; color: red" @click="editNameBlur" />
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">任务名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.taskname ? props.nowPreviewRecord.taskname : '--' }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">航线名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.airlineName ? props.nowPreviewRecord.airlineName : '--' }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">视频时长</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.airlineName ? props.nowPreviewRecord.airlineName : '--' }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">视频分辨率</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{
|
||||
props.nowPreviewRecord.width && props.nowPreviewRecord.height
|
||||
? props.nowPreviewRecord.width + '*' + props.nowPreviewRecord.height
|
||||
: '--'
|
||||
}}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">视频大小</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.size ? props.nowPreviewRecord.size : '--' }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄负载</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{
|
||||
props.nowPreviewRecord.photographNumber
|
||||
? props.nowPreviewRecord.photographNumber
|
||||
: '--'
|
||||
}}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">文件来源</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.photographMan ? props.nowPreviewRecord.photographMan : '--' }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄时间</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{
|
||||
props.nowPreviewRecord.photographTime ? props.nowPreviewRecord.photographTime : '--'
|
||||
}}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">标签</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue_graffitiNum">
|
||||
<a-tag color="success" v-for="la in props.nowPreviewRecord.label" :key="la">
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<PlusSquareOutlined style="font-size: 20px; color: #07aaed" @click="addLabelChange" />
|
||||
</span>
|
||||
<a-modal
|
||||
title="标签设置"
|
||||
:open="addLabelFlag"
|
||||
:mask="false"
|
||||
:maskClosable="false"
|
||||
:closable="false"
|
||||
:footer="null"
|
||||
>
|
||||
<div style="display: block">
|
||||
<div
|
||||
style="display: flex; flex-wrap: wrap; gap: 5px; width: 96%; margin: 10px"
|
||||
v-if="props.nowPreviewRecord.label && props.nowPreviewRecord.label.length > 0"
|
||||
>
|
||||
已添加的标签:
|
||||
<div
|
||||
v-for="la in props.nowPreviewRecord.label"
|
||||
:key="la"
|
||||
style="border: 1px solid #595959; border-radius: 3px"
|
||||
>
|
||||
<span style="margin-left: 5px">{{ la }}</span>
|
||||
<CloseOutlined
|
||||
style="margin-left: 5px; margin-right: 5px"
|
||||
@click="deleteLabel(la)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: inline-flex; gap: 5px; width: 96%; margin: 10px">
|
||||
<a-input v-model:value="newLabelName" size="small" placeholder="请输入标签" />
|
||||
<a-button type="primary" @click="pressEnterLabelFunction">添加</a-button>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
display: inline-flex;
|
||||
gap: 5px;
|
||||
width: 96%;
|
||||
margin: 5px 10px 20px 10px;
|
||||
justify-content: flex-end;
|
||||
"
|
||||
>
|
||||
<a-button
|
||||
@click="
|
||||
addLabelFlag = false;
|
||||
newLabelName = '';
|
||||
"
|
||||
>
|
||||
关闭
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import {
|
||||
EditOutlined,
|
||||
PlusSquareOutlined,
|
||||
EnvironmentOutlined,
|
||||
CheckOutlined,
|
||||
CloseOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { orgPosGroup } from '@/api/demo/system';
|
||||
import { Map } from './preview';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList', 'hideOrShowTextboxFlag']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
// 修改名称--------------------------------
|
||||
const editNameFlag = ref(true);
|
||||
const editName = ref(props.nowPreviewRecord.name.split('.').slice(0, -1).join('.'));
|
||||
function editNameChange() {
|
||||
if (props.nowPreviewRecord.name.split('.').length <= 1) {
|
||||
editName.value = props.nowPreviewRecord.name;
|
||||
}
|
||||
editNameFlag.value = false;
|
||||
}
|
||||
// 修改名称方法
|
||||
async function pressEnterNameFunction() {
|
||||
let newName: any = null;
|
||||
if (props.nowPreviewRecord.name.split('.').length <= 1) {
|
||||
newName = editName.value;
|
||||
} else {
|
||||
newName = editName.value + '.' + props.nowPreviewRecord.name.split('.').pop();
|
||||
}
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newName: newName,
|
||||
};
|
||||
props.nowPreviewRecord.name = newName;
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
}
|
||||
function editNameBlur() {
|
||||
editNameFlag.value = true;
|
||||
editName.value = props.nowPreviewRecord.name.split('.').slice(0, -1).join('.');
|
||||
}
|
||||
|
||||
// 标签--------------------------------
|
||||
const addLabelFlag = ref(false);
|
||||
const newLabelName = ref('');
|
||||
function addLabelChange() {
|
||||
addLabelFlag.value = true;
|
||||
}
|
||||
// 添加标签方法
|
||||
async function pressEnterLabelFunction() {
|
||||
if (!newLabelName.value) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!props.nowPreviewRecord.label ||
|
||||
!props.nowPreviewRecord.label.includes(newLabelName.value)
|
||||
) {
|
||||
if (!props.nowPreviewRecord.label) {
|
||||
props.nowPreviewRecord.label = [];
|
||||
}
|
||||
props.nowPreviewRecord.label.push(newLabelName.value);
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newLabel: props.nowPreviewRecord.label,
|
||||
};
|
||||
addLabelFlag.value = true;
|
||||
chooseNowPreviewRecord({
|
||||
...props.nowPreviewRecord,
|
||||
label: props.nowPreviewRecord.label,
|
||||
});
|
||||
|
||||
emit('reloadTable');
|
||||
newLabelName.value = '';
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
} else {
|
||||
return createMessage.error('此标签已存在!');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
function deleteLabel(value) {
|
||||
props.nowPreviewRecord.label = props.nowPreviewRecord.label.filter((item) => item !== value);
|
||||
chooseNowPreviewRecord({
|
||||
...props.nowPreviewRecord,
|
||||
label: props.nowPreviewRecord.label,
|
||||
});
|
||||
emit('reloadTable');
|
||||
}
|
||||
|
||||
// 选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
emit('chooseNowPreviewRecord', value);
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 60px;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.infoDiv {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
height: 480px;
|
||||
|
||||
.infotitle {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.infovalue {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
max-width: 250px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.infovalue_name {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.infovalue_graffitiNum {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
max-width: 250px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.infobutton {
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.graffitiNum {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
width: 60%;
|
||||
background: #3c3c3c;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.graffitiNum2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
width: 95%;
|
||||
background: #1c1c1c;
|
||||
height: 40px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -218,7 +218,7 @@
|
|||
// 启动视频窗口
|
||||
initPlugin();
|
||||
// 添加监听滚动事件
|
||||
const elements = document.querySelectorAll('.ZhiGan_ModalVideo');
|
||||
const elements = document.querySelectorAll('.ModalVideo');
|
||||
if (elements.length > 0) {
|
||||
// 遍历每个元素并绑定 scroll 事件
|
||||
elements.forEach((element) => {
|
||||
|
|
@ -230,7 +230,7 @@
|
|||
// 销毁视频窗口
|
||||
closeHkVideo();
|
||||
// 移除监听滚动事件
|
||||
const elements = document.querySelectorAll('.ZhiGan_ModalVideo');
|
||||
const elements = document.querySelectorAll('.ModalVideo');
|
||||
if (elements.length > 0) {
|
||||
// 遍历每个元素并绑定 scroll 事件
|
||||
elements.forEach((element) => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<div>
|
||||
<video
|
||||
:id="'ZhiGan_ModalVideo' + props.timestamp"
|
||||
:id="'TCPlayerVideo'"
|
||||
class="TCPlayer-video-container"
|
||||
preload="auto"
|
||||
crossOrigin="anonymous"
|
||||
playsinline
|
||||
autoplay
|
||||
:loop="props.videoLoop"
|
||||
:muted="props.videoMuted"
|
||||
:loop="false"
|
||||
:muted="true"
|
||||
:style="{
|
||||
width: props.width + 'px',
|
||||
height: props.height + 'px',
|
||||
|
|
@ -20,15 +20,7 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
|
||||
|
||||
const props = defineProps([
|
||||
'serialNumberValue',
|
||||
'width',
|
||||
'height',
|
||||
'timestamp',
|
||||
'videoLoop',
|
||||
'videoMuted',
|
||||
'videoFit',
|
||||
]);
|
||||
const props = defineProps(['serialNumberValue', 'width', 'height']);
|
||||
|
||||
// 视频控件初始化
|
||||
let player: any = null;
|
||||
|
|
@ -38,7 +30,7 @@
|
|||
if (player) {
|
||||
player.src(props.serialNumberValue);
|
||||
} else {
|
||||
player = TCPlayer('ZhiGan_ModalVideo' + props.timestamp, {});
|
||||
player = TCPlayer('TCPlayerVideo', {});
|
||||
player.src(props.serialNumberValue);
|
||||
}
|
||||
});
|
||||
|
|
@ -75,7 +67,7 @@
|
|||
<style lang="scss" scoped>
|
||||
video {
|
||||
display: block;
|
||||
object-fit: v-bind('props.videoFit');
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
::v-deep .vjs-live-control .vjs-live-display {
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@
|
|||
// viewer.scene.skyBox.show = false;
|
||||
// 禁用大气层和太阳
|
||||
viewer.scene.skyAtmosphere.show = false;
|
||||
// viewer.scene.sun.show = false;
|
||||
// viewer.scene.moon.show = false;
|
||||
viewer.scene.sun.show = false;
|
||||
viewer.scene.moon.show = false;
|
||||
// 设置背景为黑色
|
||||
// viewer.scene.backgroundColor = Cesium.Color.BLACK;
|
||||
//前提先把场景上的图层全部移除或者隐藏
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
},
|
||||
});
|
||||
// 启用地形深度测试,确保地形正确渲染
|
||||
viewer.scene.globe.depthTestAgainstTerrain = true;
|
||||
// viewer.scene.globe.depthTestAgainstTerrain = true;
|
||||
// // 清除默认地形
|
||||
const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(3956);
|
||||
viewer.terrainProvider = terrainProvider;
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
originalModelMatrix = Cesium.Matrix4.clone(tileset.modelMatrix);
|
||||
// 设置模型贴地
|
||||
// 启用贴地属性
|
||||
tileset.clampToGround = true;
|
||||
// tileset.clampToGround = true;
|
||||
viewer.scene.primitives.add(tileset);
|
||||
viewer.zoomTo(tileset);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
.modelDiv {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 1020px;
|
||||
height: 920px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 890px;
|
||||
height: 800px;
|
||||
background: #000000;
|
||||
}
|
||||
.modelDiv_bottom {
|
||||
|
|
|
|||
|
|
@ -4,23 +4,23 @@
|
|||
<Map :airRoute="airRoute" />
|
||||
</div>
|
||||
<SelectComponent @selectChange="changeSelect" />
|
||||
<!-- <AirportInformation
|
||||
<AirportInformation
|
||||
@changeLive="changeAirportLive"
|
||||
@changeRemote="changeRemote"
|
||||
:msgData="msgData"
|
||||
/> -->
|
||||
/>
|
||||
<UAVInformation
|
||||
:msgData="msgData"
|
||||
@changeLoadControl="changeLoadControl"
|
||||
@changeFlightControl="changeFlightControl"
|
||||
/>
|
||||
<!-- 远程调试 -->
|
||||
<div v-if="remoteVisible">
|
||||
<!-- <div v-if="remoteVisible">
|
||||
<RemoteDebugging @changeRemote="changeRemote" :msgData="msgData" />
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- 负载控制 -->
|
||||
<div v-if="loadControlVisible">
|
||||
<LoadControl @changeLoadControl="changeLoadControl" />
|
||||
<LoadControl @changeLoadControl="changeLoadControl" :msgData="msgData" />
|
||||
</div>
|
||||
<!-- 飞行控制 -->
|
||||
<div v-if="flightControlVisible">
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
// 无人机直播
|
||||
const livePreviewVisible = ref(true);
|
||||
// 远程调试
|
||||
const remoteVisible = ref(true);
|
||||
const remoteVisible = ref(false);
|
||||
// 负载控制
|
||||
const loadControlVisible = ref(false);
|
||||
// 飞行控制
|
||||
|
|
@ -103,10 +103,12 @@
|
|||
// 接收消息
|
||||
getClient().on('message', (topic, message) => {
|
||||
const rs = JSON.parse(message);
|
||||
msgData.value = {
|
||||
topic: topic,
|
||||
message: rs,
|
||||
};
|
||||
if (rs) {
|
||||
msgData.value = {
|
||||
topic: topic,
|
||||
message: rs,
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="airport-information" v-if="airportVal">
|
||||
<div class="title"
|
||||
>机场信息:<span>
|
||||
>机场信息:<span v-if="airportVal.mode_code">
|
||||
<template v-if="airportVal.mode_code == 0">作业准备中</template>
|
||||
<template v-else-if="airportVal.mode_code == 1">飞行作业中</template>
|
||||
<template v-else-if="airportVal.mode_code == 2">作业后状态恢复</template>
|
||||
|
|
@ -10,8 +10,9 @@
|
|||
<template v-else-if="airportVal.mode_code == 5">任务空闲</template>
|
||||
<template v-else-if="airportVal.mode_code == 255">飞行器异常</template>
|
||||
<template v-else-if="airportVal.mode_code == 256">未知状态</template>
|
||||
</span></div
|
||||
>
|
||||
</span>
|
||||
<span v-else>未知状态</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content-title">
|
||||
{{ time }}
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/rate.png" alt="" />
|
||||
{{ airportVal.network_state.rate }}KB/s
|
||||
{{ airportVal.network_state ? airportVal.network_state.rate : '0' }}KB/s
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
<div class="item-div">
|
||||
|
|
@ -57,17 +58,17 @@
|
|||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/voltage.png" alt="" />
|
||||
{{ airportVal.drone_charge_state.capacity_percent }} %
|
||||
{{ airportVal.drone_charge_state ? airportVal.drone_charge_state.capacity_percent : 0 }} %
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-button">
|
||||
<!-- <div class="content-button">
|
||||
<a-button type="primary" style="background: #3a57e8" @click="emits('changeLive')"
|
||||
>机场直播</a-button
|
||||
>
|
||||
<a-button type="primary" style="background: #0a99eb" @click="emits('changeRemote')"
|
||||
>远程调试</a-button
|
||||
>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -99,7 +100,8 @@
|
|||
watch(
|
||||
() => props.msgData,
|
||||
(val) => {
|
||||
if (val.topic == 'thing/product/8UUXN5400A079H/osd') {
|
||||
// network_state网络状态
|
||||
if (val.topic == 'thing/product/8UUXN5400A079H/osd' && val.message.data.network_state) {
|
||||
// console.log(val);
|
||||
airportVal.value = val.message.data;
|
||||
time.value = timestampToFormattedDate(val.message.timestamp);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<div class="content-button">
|
||||
<a-button>抢夺负载控制</a-button>
|
||||
<a-button>进入指令飞行</a-button>
|
||||
<a-button>退出指令飞行</a-button>
|
||||
<a-button>获取飞行器控制器</a-button>
|
||||
<a-button>一键起飞</a-button>
|
||||
<a-button>飞向目标点</a-button>
|
||||
<a-button>指点飞行</a-button>
|
||||
<a-button>智能环绕</a-button>
|
||||
<a-button>一键返航</a-button>
|
||||
</div>
|
||||
<div class="content-info">
|
||||
|
|
|
|||
|
|
@ -1,46 +1,54 @@
|
|||
<template>
|
||||
<div class="remote-debugging" v-if="airportVal" v-drag>
|
||||
<div class="title">
|
||||
<span> 负载控制 </span>
|
||||
<span> 云台相机控制 </span>
|
||||
<div @click="emits('changeLoadControl')">
|
||||
<CloseOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content-item">
|
||||
<span>负载控制</span>
|
||||
<a-button>抢夺负载控制</a-button>
|
||||
<span>相机控制权</span>
|
||||
<a-button @click="obtain">获取</a-button>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<!-- <div class="content-item">
|
||||
<span>切换相机模式</span>
|
||||
<a-button>抢夺负载控制</a-button>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="content-item">
|
||||
<span>拍照</span>
|
||||
<a-button>单拍</a-button>
|
||||
<a-button @click="singleShot">单拍</a-button>
|
||||
</div>
|
||||
<div class="direction-controller">
|
||||
<img src="@/assets/images/flightoperation/direction_controller.png" alt="" />
|
||||
<div class="direction-controller-top"></div>
|
||||
<div class="direction-controller-right"></div>
|
||||
<div class="direction-controller-bottom"></div>
|
||||
<div class="direction-controller-left"></div>
|
||||
<div class="direction-controller-top" @click="changeDrc('top')"></div>
|
||||
<div class="direction-controller-right" @click="changeDrc('right')"></div>
|
||||
<div class="direction-controller-bottom" @click="changeDrc('bottom')"></div>
|
||||
<div class="direction-controller-left" @click="changeDrc('left')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { getClient, createConnection } from '@/utils/mqtt';
|
||||
import { onMounted, ref, watch, reactive } from 'vue';
|
||||
import { getReizeClient, createSeizeConnection, clientReizePublish } from '@/utils/mqtt';
|
||||
import { CloseOutlined } from '@ant-design/icons-vue';
|
||||
import { vDrag } from '@/utils/drag';
|
||||
import { buildGUID } from '@/utils/uuid';
|
||||
import { eventsTopic, events_replyTopic, drcDownTopic } from '@/utils/debugging/events';
|
||||
|
||||
const emits = defineEmits(['changeLoadControl']);
|
||||
const props = defineProps({
|
||||
airportAllVal: Object,
|
||||
msgData: Object,
|
||||
});
|
||||
console.log(props);
|
||||
const checked = ref(false);
|
||||
const drcVal = reactive({
|
||||
roll: 1024,
|
||||
pitch: 1024,
|
||||
throttle: 1024,
|
||||
yaw: 1024,
|
||||
gimbal_pitch: 1024,
|
||||
});
|
||||
const airportVal: any = ref({
|
||||
mode_code: 0,
|
||||
wind_speed: 0,
|
||||
|
|
@ -56,14 +64,74 @@
|
|||
},
|
||||
});
|
||||
watch(
|
||||
() => props.airportAllVal,
|
||||
() => props.msgData,
|
||||
(val) => {
|
||||
console.log(val);
|
||||
airportVal.value = val.data;
|
||||
if (val.topic == 'thing/product/8UUXN5400A079H/events_reply') {
|
||||
console.log(val);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {});
|
||||
const obtain = () => {
|
||||
// 飞行控制权抢夺
|
||||
eventsTopic({
|
||||
bid: buildGUID(),
|
||||
method: 'flight_authority_grab',
|
||||
tid: buildGUID(),
|
||||
timestamp: new Date().getTime(),
|
||||
data: {},
|
||||
});
|
||||
// 负载控制权抢夺
|
||||
eventsTopic({
|
||||
bid: buildGUID(),
|
||||
method: 'payload_authority_grab',
|
||||
tid: buildGUID(),
|
||||
timestamp: new Date().getTime(),
|
||||
data: {
|
||||
payload_index: '99-0-0',
|
||||
},
|
||||
});
|
||||
events_replyTopic();
|
||||
};
|
||||
// 单拍
|
||||
const singleShot = () => {
|
||||
// 开始拍照
|
||||
eventsTopic({
|
||||
bid: buildGUID(),
|
||||
method: 'camera_photo_take',
|
||||
tid: buildGUID(),
|
||||
timestamp: new Date().getTime(),
|
||||
data: {
|
||||
payload_index: '99-0-0',
|
||||
},
|
||||
});
|
||||
};
|
||||
// 控制方向
|
||||
const changeDrc = (val) => {
|
||||
if (val == 'top') {
|
||||
drcVal.pitch = drcVal.pitch + 20;
|
||||
} else if (val == 'bottom') {
|
||||
drcVal.pitch = drcVal.pitch - 20;
|
||||
} else if (val == 'left') {
|
||||
drcVal.roll = drcVal.roll - 20;
|
||||
} else if (val == 'right') {
|
||||
drcVal.roll = drcVal.roll + 20;
|
||||
}
|
||||
const querys = {
|
||||
seq: 1,
|
||||
method: 'stick_control',
|
||||
data: drcVal,
|
||||
};
|
||||
drcDownTopic(querys);
|
||||
};
|
||||
onMounted(() => {
|
||||
// createSeizeConnection();
|
||||
// // 接收消息
|
||||
// getReizeClient().on('message', (topic, message) => {
|
||||
// const rs = JSON.parse(message);
|
||||
// console.log(rs);
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.remote-debugging {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
<template>
|
||||
<div class="airport-information">
|
||||
<div class="title">无人机信息:<span>未连接</span></div>
|
||||
<div class="title"
|
||||
>无人机信息:<span>
|
||||
<template
|
||||
v-if="uavInformation.sub_device && uavInformation.sub_device.device_online_status == 1"
|
||||
>开机</template
|
||||
>
|
||||
<template v-else>关机</template>
|
||||
</span></div
|
||||
>
|
||||
<div class="content">
|
||||
<div class="content-title">
|
||||
{{ time }}
|
||||
|
|
@ -9,45 +17,65 @@
|
|||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/project.png" alt="" />
|
||||
1
|
||||
{{ uavInformation.wireless_link ? uavInformation.wireless_link.dongle_number : 0 }}
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
<div class="item-div">
|
||||
<!-- <div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/arrow.png" alt="" />
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/rtk.png" alt="" />
|
||||
0
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
</div> -->
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/electricity.png" alt="" />
|
||||
89%(空闲中)
|
||||
{{
|
||||
uavInformation.drone_battery_maintenance_info
|
||||
? uavInformation.drone_battery_maintenance_info.capacity_percent
|
||||
: 0
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/agl.png" alt="" />
|
||||
0M
|
||||
<img src="@/assets/images/flightoperation/sdr.png" alt="" />
|
||||
<!-- sdr_link_state -->
|
||||
<template
|
||||
v-if="uavInformation.wireless_link && uavInformation.wireless_link.sdr_link_state == 1"
|
||||
>连接</template
|
||||
>
|
||||
<template v-else>断开</template>
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/agl.png" alt="" />
|
||||
0M
|
||||
<img src="@/assets/images/flightoperation/4g.png" alt="" />
|
||||
<template
|
||||
v-if="
|
||||
uavInformation.wireless_link && uavInformation.wireless_link['4g_link_state'] == 1
|
||||
"
|
||||
>连接</template
|
||||
>
|
||||
<template v-else>断开</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- sdr_freq_band sdr_quality 4g_freq_band 4g_quality -->
|
||||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/sdr.png" alt="" />
|
||||
{{ uavInformation.wireless_link ? uavInformation.wireless_link.sdr_quality : 0 }}
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/4g.png" alt="" />
|
||||
{{ uavInformation.wireless_link ? uavInformation.wireless_link['4g_quality'] : 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/hs.png" alt="" />
|
||||
0m/s
|
||||
<img src="@/assets/images/flightoperation/sdr.png" alt="" />
|
||||
{{ uavInformation.wireless_link ? uavInformation.wireless_link.sdr_freq_band : 0 }}
|
||||
</div>
|
||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||
<div class="item-div">
|
||||
<img src="@/assets/images/flightoperation/h.png" alt="" />
|
||||
0M
|
||||
<img src="@/assets/images/flightoperation/4g.png" alt="" />
|
||||
{{ uavInformation.wireless_link ? uavInformation.wireless_link['4g_freq_band'] : 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-edit">
|
||||
|
|
@ -89,7 +117,7 @@
|
|||
>飞行控制</a-button
|
||||
>
|
||||
<a-button type="primary" style="background: #0a99eb" @click="emits('changeLoadControl')"
|
||||
>负载控制</a-button
|
||||
>云台相机控制</a-button
|
||||
>
|
||||
</div>
|
||||
<div class="content-button">
|
||||
|
|
@ -104,7 +132,6 @@
|
|||
import { reactive, ref, watch } from 'vue';
|
||||
import { CopyOutlined, EditOutlined } from '@ant-design/icons-vue';
|
||||
import { timestampToFormattedDate } from '@/utils/index';
|
||||
import { getClient, createConnection, clientPublish, clientSubscribe } from '@/utils/mqtt';
|
||||
import { buildGUID } from '@/utils/uuid';
|
||||
import { servicesTopic, services_replyTopic } from '@/utils/debugging/remote';
|
||||
|
||||
|
|
@ -138,13 +165,41 @@
|
|||
limitedRange: '500m',
|
||||
obstacleAvoidance: '500m',
|
||||
});
|
||||
const uavInformation = ref({
|
||||
sub_device: {
|
||||
// 飞行器状态
|
||||
device_online_status: 0,
|
||||
},
|
||||
drone_battery_maintenance_info: {
|
||||
// 电池剩余电量
|
||||
capacity_percent: 0,
|
||||
},
|
||||
wireless_link: {
|
||||
// 飞行器上 Dongle 数量
|
||||
dongle_number: 0,
|
||||
sdr_quality: 0,
|
||||
'4g_quality': 0,
|
||||
'4g_uav_quality': 0,
|
||||
'4g_gnd_quality': 0,
|
||||
remain_upload: 0,
|
||||
sdr_link_state: 0,
|
||||
sdr_freq_band: 0,
|
||||
},
|
||||
});
|
||||
const time = ref(timestampToFormattedDate(new Date().getTime()));
|
||||
watch(
|
||||
() => props.msgData,
|
||||
(val) => {
|
||||
if (val.topic == 'thing/product/8UUXN5400A079H/osd') {
|
||||
// console.log(val);
|
||||
time.value = timestampToFormattedDate(val.message.timestamp);
|
||||
if (
|
||||
val.message.data.sub_device ||
|
||||
val.message.data.drone_battery_maintenance_info ||
|
||||
val.message.data.wireless_link
|
||||
) {
|
||||
// console.log(val);
|
||||
uavInformation.value = val.message.data;
|
||||
time.value = timestampToFormattedDate(val.message.timestamp);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -152,7 +207,7 @@
|
|||
<style lang="less" scoped>
|
||||
.airport-information {
|
||||
position: absolute;
|
||||
top: 360px;
|
||||
top: 320px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
width: 260px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue