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',
|
username: 'sdhc',
|
||||||
password: '',
|
password: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
let client: any = {
|
let client: any = {
|
||||||
connected: false,
|
connected: false,
|
||||||
};
|
};
|
||||||
|
|
@ -105,6 +106,51 @@ const clientPublish = (topic: string, querys: any) => {
|
||||||
// subscribe 事件 订阅
|
// subscribe 事件 订阅
|
||||||
// unsubscribe 事件 取消订阅
|
// 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 {
|
export {
|
||||||
// 连接
|
// 连接
|
||||||
createConnection,
|
createConnection,
|
||||||
|
|
@ -112,4 +158,8 @@ export {
|
||||||
getClient,
|
getClient,
|
||||||
clientSubscribe,
|
clientSubscribe,
|
||||||
clientPublish,
|
clientPublish,
|
||||||
|
createSeizeConnection,
|
||||||
|
getReizeClient,
|
||||||
|
clientReizeSubscribe,
|
||||||
|
clientReizePublish,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,10 @@
|
||||||
|
|
||||||
const emit = defineEmits(['select']);
|
const emit = defineEmits(['select']);
|
||||||
|
|
||||||
const treeData = ref([]);
|
const treeData: any = ref([]);
|
||||||
const asyncExpandTreeRef = ref<Nullable<TreeActionType>>(null);
|
const asyncExpandTreeRef = ref<Nullable<TreeActionType>>(null);
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
// treeData.value = (await getChildrenTree({ parentId: 0 })) as unknown as TreeItem[];
|
|
||||||
// console.log(treeData.value);
|
|
||||||
treeData.value = [
|
treeData.value = [
|
||||||
{
|
{
|
||||||
id: 'meitiku',
|
id: 'meitiku',
|
||||||
|
|
@ -65,14 +63,14 @@
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
id: 'gas',
|
// id: 'gas',
|
||||||
name: '气体探测',
|
// name: '气体探测',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
id: 'vr',
|
// id: 'vr',
|
||||||
name: 'VR全景',
|
// name: 'VR全景',
|
||||||
},
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 显示到一级
|
// 显示到一级
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,186 @@
|
||||||
<template>
|
<template>
|
||||||
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
||||||
<LeftTree ref="childRef" class="w-1/5 xl:w-1/6" @select="handleSelect" />
|
<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">
|
<div class="w-4/5 xl:w-5/6">
|
||||||
<template #toolbar>
|
<BasicTable @register="registerTable" :searchInfo="searchInfo">
|
||||||
<!-- <PermissionBtn @btnEvent="onBtnClicked"></PermissionBtn> -->
|
<template #toolbar>
|
||||||
<a-button :icon="h(PlusOutlined)" type="primary" @click="addFolder">新建文件夹</a-button>
|
<span v-for="f in floders" :key="f" class="floderTitle">
|
||||||
<a-button :icon="h(ColumnHeightOutlined)" @click="moveFolderOrFile">移动</a-button>
|
<span v-if="f != '全部文件'" style="margin-right: 10px"> / </span>
|
||||||
<a-button :icon="h(DeleteOutlined)" @click="deleteFolderOrFile">删除 </a-button>
|
<span @click="getChildrenByProp(tableData, f)"> {{ f }}</span>
|
||||||
<a-button :icon="h(DownloadOutlined)" @click="compressFolderOrFile">压缩</a-button>
|
</span>
|
||||||
<a-radio-group v-model:value="tableType">
|
<div class="floderOtherButton">
|
||||||
<a-radio-button value="table"><BarsOutlined /></a-radio-button>
|
<!-- <PermissionBtn @btnEvent="onBtnClicked"></PermissionBtn> -->
|
||||||
<a-radio-button value="store"><AppstoreOutlined /></a-radio-button>
|
<a-button :icon="h(PlusOutlined)" type="primary" @click="addFolder">
|
||||||
</a-radio-group>
|
新建文件夹
|
||||||
</template>
|
</a-button>
|
||||||
<template #bodyCell="{ column, record }">
|
<a-button :icon="h(ColumnHeightOutlined)" @click="moveFolderOrFile">移动</a-button>
|
||||||
<template v-if="column.key === 'name'">
|
<a-button :icon="h(DeleteOutlined)" @click="deleteFolderOrFile">删除 </a-button>
|
||||||
<FolderOpenOutlined v-if="record.type == 'folder'" style="font-size: 20px" />
|
<a-button :icon="h(DownloadOutlined)" @click="compressFolderOrFile">压缩</a-button>
|
||||||
<img v-if="record.type == 'img'" :src="record.url" :width="30" :height="20" />
|
<a-radio-group v-model:value="tableType">
|
||||||
{{ record.name }}
|
<a-radio-button value="table"><BarsOutlined /></a-radio-button>
|
||||||
</template>
|
<a-radio-button value="store"><AppstoreOutlined /></a-radio-button>
|
||||||
<template v-if="column.key === 'size'">
|
</a-radio-group>
|
||||||
{{ 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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'action'">
|
<template #bodyCell="{ column, record }">
|
||||||
<a-button type="text">
|
<div v-if="tableType == 'table'">
|
||||||
<EditOutlined @click="renameRecord(record)" />
|
<template v-if="column.key === 'name'">
|
||||||
</a-button>
|
<FolderOpenOutlined v-if="record.type == 'folder'" style="font-size: 20px" />
|
||||||
<a-button type="text" v-if="record.type != 'folder'">
|
<!-- <PlaySquareTwoTone v-if="record.type == 'video'" style="font-size: 20px" /> -->
|
||||||
<EyeOutlined @click="lookRecord(record)" />
|
<img v-if="record.type == 'img'" :src="record.url" :width="30" :height="20" />
|
||||||
</a-button>
|
<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>
|
||||||
</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" />
|
<AddFolderModal @register="addFolderModal" @success="handleSuccess" />
|
||||||
<!-- 移动 -->
|
<!-- 移动 -->
|
||||||
|
|
@ -54,6 +198,7 @@
|
||||||
:closable="false"
|
:closable="false"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
:destroyOnClose="true"
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
:mask="false"
|
:mask="false"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
|
|
@ -78,7 +223,7 @@
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<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 { BasicTable, useTable, TableAction } from '@/components/Table';
|
||||||
import { getOrgList, deleteDept } from '@/api/demo/system';
|
import { getOrgList, deleteDept } from '@/api/demo/system';
|
||||||
import { PageWrapper } from '@/components/Page';
|
import { PageWrapper } from '@/components/Page';
|
||||||
|
|
@ -95,6 +240,8 @@
|
||||||
FolderOutlined,
|
FolderOutlined,
|
||||||
FolderOpenOutlined,
|
FolderOpenOutlined,
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
|
PlaySquareTwoTone,
|
||||||
|
FileOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import LeftTree from './LeftTree.vue';
|
import LeftTree from './LeftTree.vue';
|
||||||
import { AddFolderModal } from './modal/modal';
|
import { AddFolderModal } from './modal/modal';
|
||||||
|
|
@ -106,51 +253,11 @@
|
||||||
import { PermissionBtn } from '@/components/PermissionBtn/index';
|
import { PermissionBtn } from '@/components/PermissionBtn/index';
|
||||||
import { columns, searchFormSchema } from './modal.data';
|
import { columns, searchFormSchema } from './modal.data';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
const { createConfirm, createMessage } = useMessage();
|
const { createConfirm, createMessage } = useMessage();
|
||||||
const data = ref([
|
// 表格数据
|
||||||
{
|
const tableData = 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',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
name: '图片',
|
name: '图片',
|
||||||
|
|
@ -160,9 +267,9 @@
|
||||||
{
|
{
|
||||||
id: '1-2',
|
id: '1-2',
|
||||||
name: '南山风景照.jpg',
|
name: '南山风景照.jpg',
|
||||||
createTime: '2020-10-22 17:33:22',
|
createtime: '2020-10-22 17:33:22',
|
||||||
type: 'img',
|
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: '原片',
|
imgtype: '原片',
|
||||||
taskname: '佛山大火救援项目',
|
taskname: '佛山大火救援项目',
|
||||||
airlineName: '火灾救援勘查航线',
|
airlineName: '火灾救援勘查航线',
|
||||||
|
|
@ -176,13 +283,11 @@
|
||||||
label: ['人', '车'],
|
label: ['人', '车'],
|
||||||
lat: 35.362625,
|
lat: 35.362625,
|
||||||
lng: 118.033886,
|
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',
|
id: '1-3',
|
||||||
name: '风景图.jpg',
|
name: '风景图.jpg',
|
||||||
createTime: '2020-10-22 17:33:22',
|
createtime: '2020-10-22 17:33:22',
|
||||||
type: 'img',
|
type: 'img',
|
||||||
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
|
url: 'https://m.tuniucdn.com/fb2/t1/G5/M00/44/52/Cii-s1soezyIF2UxABn76u-yKl8AAIwBgB34jAAGfwC3020871',
|
||||||
imgtype: '原片2',
|
imgtype: '原片2',
|
||||||
|
|
@ -196,15 +301,13 @@
|
||||||
photographMan: 'zachzhou',
|
photographMan: 'zachzhou',
|
||||||
photographTime: '2020-10-22 00:00:00',
|
photographTime: '2020-10-22 00:00:00',
|
||||||
label: ['人', '车'],
|
label: ['人', '车'],
|
||||||
lat: 35.362625,
|
lat: 35.362825,
|
||||||
lng: 118.033886,
|
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',
|
id: '1-4',
|
||||||
name: '红外照片.jpg',
|
name: '红外照片.jpg',
|
||||||
createTime: '2020-10-22 17:33:22',
|
createtime: '2020-10-22 17:33:22',
|
||||||
type: 'img',
|
type: 'img',
|
||||||
url: 'https://pic.rmb.bdstatic.com/bjh/gallery/8c885a0e3cf0647b60548535e2e9ca39.jpeg',
|
url: 'https://pic.rmb.bdstatic.com/bjh/gallery/8c885a0e3cf0647b60548535e2e9ca39.jpeg',
|
||||||
imgtype: '红外照片',
|
imgtype: '红外照片',
|
||||||
|
|
@ -218,13 +321,13 @@
|
||||||
photographMan: 'zachzhou',
|
photographMan: 'zachzhou',
|
||||||
photographTime: '2020-10-22 00:00:00',
|
photographTime: '2020-10-22 00:00:00',
|
||||||
label: ['人', '车'],
|
label: ['人', '车'],
|
||||||
lat: 35.362625,
|
lat: 35.362925,
|
||||||
lng: 118.033886,
|
lng: 118.033886,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1-5',
|
id: '1-5',
|
||||||
name: '广角照片.jpg',
|
name: '广角照片.jpg',
|
||||||
createTime: '2020-10-22 17:33:22',
|
createtime: '2020-10-22 17:33:22',
|
||||||
type: 'img',
|
type: 'img',
|
||||||
url: 'https://img2.baidu.com/it/u=2490853491,3226002419&fm=253&fmt=auto&app=138&f=JPEG?w=749&h=500',
|
url: 'https://img2.baidu.com/it/u=2490853491,3226002419&fm=253&fmt=auto&app=138&f=JPEG?w=749&h=500',
|
||||||
imgtype: '广角照片',
|
imgtype: '广角照片',
|
||||||
|
|
@ -239,12 +342,12 @@
|
||||||
photographTime: '2020-10-22 00:00:00',
|
photographTime: '2020-10-22 00:00:00',
|
||||||
label: ['人', '车'],
|
label: ['人', '车'],
|
||||||
lat: 35.362625,
|
lat: 35.362625,
|
||||||
lng: 118.033886,
|
lng: 118.033286,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1-6',
|
id: '1-6',
|
||||||
name: '变焦照片.jpg',
|
name: '变焦照片.jpg',
|
||||||
createTime: '2020-10-22 17:33:22',
|
createtime: '2020-10-22 17:33:22',
|
||||||
type: 'img',
|
type: 'img',
|
||||||
url: 'https://img2.baidu.com/it/u=3778652155,475195343&fm=253&fmt=auto&app=138&f=PNG?w=500&h=518',
|
url: 'https://img2.baidu.com/it/u=3778652155,475195343&fm=253&fmt=auto&app=138&f=PNG?w=500&h=518',
|
||||||
imgtype: '变焦照片',
|
imgtype: '变焦照片',
|
||||||
|
|
@ -258,8 +361,8 @@
|
||||||
photographMan: 'zachzhou',
|
photographMan: 'zachzhou',
|
||||||
photographTime: '2020-10-22 00:00:00',
|
photographTime: '2020-10-22 00:00:00',
|
||||||
label: ['人', '车'],
|
label: ['人', '车'],
|
||||||
lat: 35.362625,
|
lat: 35.362725,
|
||||||
lng: 118.033886,
|
lng: 118.033086,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -269,38 +372,48 @@
|
||||||
createtime: '2025-03-24 18:13:17',
|
createtime: '2025-03-24 18:13:17',
|
||||||
type: 'folder',
|
type: 'folder',
|
||||||
children: [
|
children: [
|
||||||
{
|
// {
|
||||||
id: '3-4',
|
// id: '3-4',
|
||||||
name: 'XZD153狼窝沟西南',
|
// name: 'XZD153狼窝沟西南',
|
||||||
createTime: '',
|
// createtime: '',
|
||||||
type: 'video',
|
// type: 'video',
|
||||||
url: '74b95e6575d741489b9a9061bb646467',
|
// url: '74b95e6575d741489b9a9061bb646467',
|
||||||
manufacturer: '海康',
|
// manufacturer: '海康',
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
id: '3-5',
|
id: '3-5',
|
||||||
name: '费县马庄镇陈家鱼后村南斜坡后村',
|
name: '费县马庄镇陈家鱼后村南斜坡后村',
|
||||||
createTime: '',
|
createtime: '',
|
||||||
type: 'video',
|
type: 'video',
|
||||||
url: 'http://111.36.45.20:18000/flv/hls/H-dcb1ea7388588111.flv',
|
url: 'http://111.36.45.20:18000/flv/hls/H-dcb1ea7388588111.flv',
|
||||||
|
gifZoomImage: 'https://img.soogif.com/mrGHcO3xjFJnJ986TeL9oAr2BYfPIaM7.gif',
|
||||||
manufacturer: '腾讯',
|
manufacturer: '腾讯',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3-6',
|
id: '3-8',
|
||||||
name: '可落',
|
name: 'movie',
|
||||||
createTime: '',
|
createtime: '',
|
||||||
type: 'video',
|
type: 'video',
|
||||||
url: '8H03AA1PAG8D9BF',
|
url: 'https://www.runoob.com/try/demo_source/mov_bbb.mp4',
|
||||||
manufacturer: '乐橙',
|
gifZoomImage: 'https://img.soogif.com/mrGHcO3xjFJnJ986TeL9oAr2BYfPIaM7.gif',
|
||||||
},
|
manufacturer: '腾讯',
|
||||||
{
|
|
||||||
id: '3-7',
|
|
||||||
name: '费县薛庄镇东张林村村南可见光',
|
|
||||||
createTime: '',
|
|
||||||
type: 'video',
|
|
||||||
url: '37130100181328000392',
|
|
||||||
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 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 searchInfo = reactive<Recordable>({});
|
||||||
const searchParams = ref();
|
const searchParams = ref();
|
||||||
const [registerTable, { reload, getSelectRows, getDataSource, clearSelectedRowKeys }] = useTable({
|
const [registerTable, { reload, getSelectRows, setSelectedRows, clearSelectedRowKeys }] =
|
||||||
// api: getOrgList,
|
useTable({
|
||||||
dataSource: data,
|
// api: getOrgList,
|
||||||
rowKey: 'id',
|
// title: '全部文件',
|
||||||
columns,
|
dataSource: showTableData,
|
||||||
formConfig: {
|
rowKey: 'id',
|
||||||
labelWidth: 120,
|
columns,
|
||||||
schemas: searchFormSchema,
|
formConfig: {
|
||||||
},
|
labelWidth: 120,
|
||||||
rowSelection: {
|
schemas: searchFormSchema,
|
||||||
type: 'checkbox',
|
},
|
||||||
},
|
rowSelection: {
|
||||||
striped: false,
|
type: 'checkbox',
|
||||||
bordered: false,
|
},
|
||||||
inset: false,
|
isTreeTable: false,
|
||||||
tableSetting: {
|
striped: false,
|
||||||
redo: false,
|
bordered: false,
|
||||||
size: false,
|
inset: false,
|
||||||
setting: false,
|
tableSetting: {
|
||||||
},
|
redo: false,
|
||||||
useSearchForm: true,
|
size: false,
|
||||||
showIndexColumn: false,
|
setting: false,
|
||||||
showTableSetting: true,
|
},
|
||||||
handleSearchInfoFn(info) {
|
useSearchForm: true,
|
||||||
console.log(info);
|
showIndexColumn: false,
|
||||||
console.log(searchInfo.value);
|
showTableSetting: true,
|
||||||
|
handleSearchInfoFn(info) {
|
||||||
searchParams.value = info;
|
console.log(info);
|
||||||
return info;
|
console.log(searchInfo.value);
|
||||||
},
|
searchParams.value = info;
|
||||||
beforeFetch: (data) => {
|
return info;
|
||||||
// 接口请求前 参数处理
|
},
|
||||||
var temp = {
|
beforeFetch: (data) => {
|
||||||
startTime: dayjs(data.startTime).startOf('month').format('YYYY-MM-DD'),
|
// 接口请求前 参数处理
|
||||||
endTime: dayjs(data.endTime).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
var temp = {
|
||||||
};
|
startTime: dayjs(data.startTime).startOf('month').format('YYYY-MM-DD'),
|
||||||
return temp;
|
endTime: dayjs(data.endTime).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
},
|
};
|
||||||
afterFetch: (res) => {
|
return temp;
|
||||||
console.log(res);
|
},
|
||||||
},
|
afterFetch: (res) => {
|
||||||
});
|
console.log(res);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function handleSelect(orgId = '') {
|
function handleSelect(orgId = '') {
|
||||||
searchInfo.orgId = orgId;
|
searchInfo.orgId = orgId;
|
||||||
|
|
@ -419,7 +563,7 @@
|
||||||
if (rows[0].type == 'folder') {
|
if (rows[0].type == 'folder') {
|
||||||
record = rows[0];
|
record = rows[0];
|
||||||
} else {
|
} else {
|
||||||
record = findParentIdById(getDataSource(), rows[0].id);
|
record = findParentIdById(tableData.value, rows[0].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openAddFolderModal(true, {
|
openAddFolderModal(true, {
|
||||||
|
|
@ -452,7 +596,7 @@
|
||||||
if (rows.length > 0) {
|
if (rows.length > 0) {
|
||||||
const record = rows;
|
const record = rows;
|
||||||
openMoveFileModal(true, {
|
openMoveFileModal(true, {
|
||||||
tableData: getDataSource(),
|
tableData: tableData.value,
|
||||||
record,
|
record,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -490,7 +634,7 @@
|
||||||
if (rows.length > 0) {
|
if (rows.length > 0) {
|
||||||
const record = rows;
|
const record = rows;
|
||||||
openCompressFileModal(true, {
|
openCompressFileModal(true, {
|
||||||
tableData: getDataSource(),
|
tableData: tableData.value,
|
||||||
record,
|
record,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -511,13 +655,46 @@
|
||||||
const nowPreviewRecord: any = ref();
|
const nowPreviewRecord: any = ref();
|
||||||
const previewRecordList: any = ref([]);
|
const previewRecordList: any = ref([]);
|
||||||
function lookRecord(record) {
|
function lookRecord(record) {
|
||||||
open.value = true;
|
if (record.type == 'folder') {
|
||||||
nowPreviewRecord.value = record;
|
showTableData.value = record.children;
|
||||||
previewRecordList.value = findParentIdById(getDataSource(), record.id)?.children;
|
floders.value.push(record.name);
|
||||||
if (!previewRecordList.value) {
|
} else {
|
||||||
previewRecordList.value = getDataSource();
|
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) {
|
function chooseNowPreviewRecord(value) {
|
||||||
|
|
@ -525,6 +702,108 @@
|
||||||
}
|
}
|
||||||
// 关闭
|
// 关闭
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
|
document.body.style.cursor = 'auto';
|
||||||
open.value = false;
|
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>
|
</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',
|
field: 'label',
|
||||||
label: '标签',
|
label: '标签',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
colProps: { span: 8 },
|
colProps: { span: 6 },
|
||||||
componentProps: {
|
componentProps: {
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
options: [
|
options: [
|
||||||
|
|
@ -61,19 +61,19 @@ export const searchFormSchema: FormSchema[] = [
|
||||||
field: 'equipmentName',
|
field: 'equipmentName',
|
||||||
label: '设备名称',
|
label: '设备名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
colProps: { span: 5 },
|
colProps: { span: 6 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: '[startTime, endTime]',
|
field: '[startTime, endTime]',
|
||||||
label: '时间选择',
|
label: '时间选择',
|
||||||
component: 'RangePicker',
|
component: 'RangePicker',
|
||||||
colProps: { span: 6 },
|
colProps: { span: 6},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
label: '文件名称',
|
label: '文件名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
colProps: { span: 5 },
|
colProps: { span: 6 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
export const formGroupSchema: FormSchema[] = [
|
export const formGroupSchema: FormSchema[] = [
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
<template>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { v4 as uuidv4 } from 'uuid';
|
||||||
import mapboxgl, { Map } from 'mapbox-gl';
|
import mapboxgl, { Map } from 'mapbox-gl';
|
||||||
import { MapboxConfig, MapboxDefaultStyle } from '@/components/MapboxMaps/src/config';
|
import { MapboxConfig, MapboxDefaultStyle } from '@/components/MapboxMaps/src/config';
|
||||||
|
|
@ -15,9 +20,10 @@
|
||||||
MinusOutlined,
|
MinusOutlined,
|
||||||
HeatMapOutlined,
|
HeatMapOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
const props = defineProps(['nowPreviewRecord']);
|
const props = defineProps(['nowPreviewRecord', 'previewRecordList', 'hideOrShowTextboxFlag']);
|
||||||
const emits = defineEmits(['getMap']);
|
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||||
|
|
||||||
const mapId = `modal-map-${uuidv4()}`;
|
const mapId = `modal-map-${uuidv4()}`;
|
||||||
const networkType = ref('WAN');
|
const networkType = ref('WAN');
|
||||||
|
|
@ -54,8 +60,7 @@
|
||||||
source: 'raster-tiles',
|
source: 'raster-tiles',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
map = new mapboxgl.Map({
|
||||||
return new mapboxgl.Map({
|
|
||||||
container: mapId,
|
container: mapId,
|
||||||
language: 'zh-cmn',
|
language: 'zh-cmn',
|
||||||
projection: 'equirectangular', // wgs84参考系
|
projection: 'equirectangular', // wgs84参考系
|
||||||
|
|
@ -69,19 +74,133 @@
|
||||||
minZoom: 8,
|
minZoom: 8,
|
||||||
pitch: 0,
|
pitch: 0,
|
||||||
zoom: 14,
|
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(() => {
|
onMounted(() => {
|
||||||
mapboxgl.accessToken = MapboxConfig.ACCESS_TOKEN;
|
mapboxgl.accessToken = MapboxConfig.ACCESS_TOKEN;
|
||||||
// 获取网络环境后加载地图
|
// 获取网络环境后加载地图
|
||||||
map = initMap();
|
initMap();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
map && map.remove();
|
map && map.remove();
|
||||||
|
props.previewRecordList.forEach((item) => {
|
||||||
|
map.off('click', item.id, function (e) {
|
||||||
|
clickHandler(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 点击事件
|
||||||
|
function clickHandler(item) {
|
||||||
|
emit('chooseNowPreviewRecord', item);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
@ -91,6 +210,30 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapInfo {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
background: #3d3f3aaa;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
span {
|
||||||
|
height: 100%;
|
||||||
|
width: 30%;
|
||||||
|
color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
font-size: 15px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .mapboxgl-ctrl {
|
::v-deep .mapboxgl-ctrl {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export { default as PreviewImage } from './previewImage.vue';
|
export { default as PreviewImage } from './previewImage.vue';
|
||||||
export { default as PreviewCanvas } from './previewCanvas.vue';
|
|
||||||
export { default as PreviewVideo } from './previewVideo.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 MonitorHK } from './video/monitorHK.vue';
|
||||||
export { default as MonitorLC } from './video/monitorLC.vue';
|
export { default as MonitorLC } from './video/monitorLC.vue';
|
||||||
export { default as MonitorQX } from './video/monitorQX.vue';
|
export { default as MonitorQX } from './video/monitorQX.vue';
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,13 @@
|
||||||
<div class="mainBody">
|
<div class="mainBody">
|
||||||
<div class="imgOrVideo">
|
<div class="imgOrVideo">
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
<div class="image" v-if="props.nowPreviewRecord.type == 'img'">
|
<div class="imageDiv" v-if="props.nowPreviewRecord.type == 'img'">
|
||||||
<!-- <PreviewCanvas
|
|
||||||
:nowPreviewRecord="props.nowPreviewRecord"
|
|
||||||
:previewRecordList="props.previewRecordList"
|
|
||||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
|
||||||
@reloadTable="reloadTable"
|
|
||||||
/> -->
|
|
||||||
<PreviewImage
|
<PreviewImage
|
||||||
:nowPreviewRecord="props.nowPreviewRecord"
|
:nowPreviewRecord="props.nowPreviewRecord"
|
||||||
:previewRecordList="props.previewRecordList"
|
:previewRecordList="props.previewRecordList"
|
||||||
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||||
@reloadTable="reloadTable"
|
@reloadTable="reloadTable"
|
||||||
|
@setHideOrShowTextboxFlag="setHideOrShowTextboxFlag"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- VR全景 -->
|
<!-- VR全景 -->
|
||||||
|
|
@ -37,12 +32,31 @@
|
||||||
<PanoViewer />
|
<PanoViewer />
|
||||||
</div> -->
|
</div> -->
|
||||||
<!-- 视频 -->
|
<!-- 视频 -->
|
||||||
<div class="video" v-if="props.nowPreviewRecord.type == 'video'">
|
<div class="videoDiv" v-if="props.nowPreviewRecord.type == 'video'">
|
||||||
<PreviewVideo :nowPreviewRecord="props.nowPreviewRecord" />
|
<PreviewVideo
|
||||||
|
:nowPreviewRecord="props.nowPreviewRecord"
|
||||||
|
:previewRecordList="props.previewRecordList"
|
||||||
|
@chooseNowPreviewRecord="chooseNowPreviewRecord"
|
||||||
|
@reloadTable="reloadTable"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="information">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -51,15 +65,20 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { CloseOutlined } from '@ant-design/icons-vue';
|
import { CloseOutlined } from '@ant-design/icons-vue';
|
||||||
import { PreviewImage } from './preview';
|
import { PreviewImage } from './preview';
|
||||||
import { PreviewCanvas } from './preview';
|
|
||||||
import { PreviewVideo } from './preview';
|
import { PreviewVideo } from './preview';
|
||||||
import { PreviewInformation } from './preview';
|
import { PreviewImageInformation } from './preview';
|
||||||
|
import { PreviewVideoInformation } from './preview';
|
||||||
import { PanoViewer } from './preview';
|
import { PanoViewer } from './preview';
|
||||||
|
|
||||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||||
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);
|
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) {
|
function chooseNowPreviewRecord(value) {
|
||||||
|
|
@ -71,13 +90,14 @@
|
||||||
}
|
}
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
|
document.body.style.cursor = 'default';
|
||||||
emit('closeModal');
|
emit('closeModal');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1020px;
|
height: 920px;
|
||||||
|
|
||||||
// 页面不能被选中
|
// 页面不能被选中
|
||||||
-webkit-user-select: none; /* Safari */
|
-webkit-user-select: none; /* Safari */
|
||||||
|
|
@ -109,7 +129,7 @@
|
||||||
}
|
}
|
||||||
.mainBody {
|
.mainBody {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1020px;
|
height: 920px;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
|
@ -117,10 +137,10 @@
|
||||||
|
|
||||||
.imgOrVideo {
|
.imgOrVideo {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 1020px;
|
height: 920px;
|
||||||
background: #101010;
|
background: #101010;
|
||||||
|
|
||||||
.image {
|
.imageDiv {
|
||||||
// display: flex;
|
// display: flex;
|
||||||
// align-items: center;
|
// align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -128,6 +148,13 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.videoDiv {
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.information {
|
.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>
|
<template>
|
||||||
<div class="image">
|
<div class="previewImage">
|
||||||
<div class="canvas" @mouseleave="document.body.style.cursor = 'auto'">
|
<div id="imageDiv" class="imageDiv">
|
||||||
<div
|
<div
|
||||||
ref="mouseCanvasRef"
|
ref="mouseCanvasRef"
|
||||||
@mousedown="onMouseDown"
|
@mousedown="onMouseDown"
|
||||||
|
|
@ -43,12 +43,18 @@
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="rect.text"
|
v-model:value="rect.text"
|
||||||
style="width: 110px; height: 30px; margin-right: 10px"
|
style="width: 110px; height: 30px; margin-right: 10px"
|
||||||
|
@keypress.enter="
|
||||||
|
nowGraffiti = -1;
|
||||||
|
rect.status = 'success';
|
||||||
|
addGraffiti();
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<CheckOutlined
|
<CheckOutlined
|
||||||
style="margin-right: 10px; color: green"
|
style="margin-right: 10px; color: green"
|
||||||
@click="
|
@click="
|
||||||
nowGraffiti = -1;
|
nowGraffiti = -1;
|
||||||
rect.status = 'success';
|
rect.status = 'success';
|
||||||
|
addGraffiti();
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<CloseOutlined
|
<CloseOutlined
|
||||||
|
|
@ -62,7 +68,7 @@
|
||||||
<DeleteOutlined
|
<DeleteOutlined
|
||||||
style="margin-right: 10px"
|
style="margin-right: 10px"
|
||||||
@click="
|
@click="
|
||||||
graffitis.splice(index, 1);
|
deleteGraffiti(index, rect.text);
|
||||||
nowGraffiti = -1;
|
nowGraffiti = -1;
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
@ -243,34 +249,135 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<div class="buttonList">
|
<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"> <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">
|
<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>
|
||||||
<!-- 下载 -->
|
<!-- 下载 -->
|
||||||
<div class="button">
|
<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>
|
||||||
<!-- 删除 -->
|
<!-- 删除 -->
|
||||||
<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>
|
||||||
<div class="imageList">
|
<div class="imageList">
|
||||||
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
||||||
|
|
@ -293,11 +400,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 涂鸦颜色选择-提示 -->
|
<!-- 涂鸦颜色选择-提示 -->
|
||||||
<div
|
<div @click="setGraffiti" :class="graffitiFlag ? 'graffitiButton_choose' : 'graffitiButton'">
|
||||||
class="graffitiButton"
|
|
||||||
@click="setGraffiti"
|
|
||||||
:style="graffitiFlag ? 'outline: 2px solid #2B85E4' : ''"
|
|
||||||
>
|
|
||||||
<a-popover placement="left">
|
<a-popover placement="left">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div style="display: flex; gap: 5px">
|
<div style="display: flex; gap: 5px">
|
||||||
|
|
@ -323,42 +426,64 @@
|
||||||
<!-- 隐藏or显示涂鸦和标签 -->
|
<!-- 隐藏or显示涂鸦和标签 -->
|
||||||
<div class="showTextboxClass">
|
<div class="showTextboxClass">
|
||||||
<div class="button">
|
<div class="button">
|
||||||
<EyeOutlined @click="hideOrShowGraffiti(false)" v-if="hideOrShowGraffitiFlag" />
|
<a-tooltip placement="left">
|
||||||
<EyeInvisibleOutlined @click="hideOrShowGraffiti(true)" v-if="!hideOrShowGraffitiFlag" />
|
<template #title>
|
||||||
<div
|
<span>{{ hideOrShowGraffitiFlag ? '点击隐藏涂鸦信息' : '点击显示涂鸦信息' }}</span>
|
||||||
style="
|
</template>
|
||||||
position: absolute;
|
<EyeOutlined @click="hideOrShowGraffiti(false)" v-if="hideOrShowGraffitiFlag" />
|
||||||
bottom: 0px;
|
<EyeInvisibleOutlined @click="hideOrShowGraffiti(true)" v-if="!hideOrShowGraffitiFlag" />
|
||||||
right: 0px;
|
<div
|
||||||
font-size: 10px;
|
style="
|
||||||
color: #000000;
|
position: absolute;
|
||||||
pointer-events: none;
|
bottom: 0px;
|
||||||
"
|
right: 0px;
|
||||||
>
|
font-size: 10px;
|
||||||
涂鸦
|
color: #000000;
|
||||||
</div>
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
涂鸦
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="button">
|
<div class="button">
|
||||||
<EyeOutlined @click="hideOrShowTextbox(false)" v-if="hideOrShowTextboxFlag" />
|
<a-tooltip placement="left">
|
||||||
<EyeInvisibleOutlined @click="hideOrShowTextbox(true)" v-if="!hideOrShowTextboxFlag" />
|
<template #title>
|
||||||
<div
|
<span>
|
||||||
style="
|
{{
|
||||||
position: absolute;
|
hideOrShowTextboxFlag
|
||||||
bottom: 0px;
|
? '点击隐藏地图上的其他标注信息'
|
||||||
right: 0px;
|
: '点击显示地图上的其他标注信息'
|
||||||
font-size: 10px;
|
}}
|
||||||
color: #000000;
|
</span>
|
||||||
pointer-events: none;
|
</template>
|
||||||
"
|
<EyeOutlined @click="hideOrShowTextbox(false)" v-if="hideOrShowTextboxFlag" />
|
||||||
>
|
<EyeInvisibleOutlined @click="hideOrShowTextbox(true)" v-if="!hideOrShowTextboxFlag" />
|
||||||
标注
|
<div
|
||||||
</div>
|
style="
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 0px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #000000;
|
||||||
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
标注
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 退出涂鸦 -->
|
||||||
|
<div class="escTip" v-if="graffitiFlag">
|
||||||
|
<div class="whiteEsc">Esc</div>
|
||||||
|
<div class="blackTip">退出涂鸦</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, watch, computed } from 'vue';
|
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
|
||||||
import {
|
import {
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
RightOutlined,
|
RightOutlined,
|
||||||
|
|
@ -376,6 +501,10 @@
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
FontColorsOutlined,
|
FontColorsOutlined,
|
||||||
RedoOutlined,
|
RedoOutlined,
|
||||||
|
FileImageOutlined,
|
||||||
|
BorderHorizontalOutlined,
|
||||||
|
ExpandOutlined,
|
||||||
|
CompressOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { useMessage } from '@/hooks/web/useMessage';
|
import { useMessage } from '@/hooks/web/useMessage';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
@ -383,7 +512,7 @@
|
||||||
const { createConfirm, createMessage } = useMessage();
|
const { createConfirm, createMessage } = useMessage();
|
||||||
|
|
||||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable', 'setHideOrShowTextboxFlag']);
|
||||||
|
|
||||||
// 宽高
|
// 宽高
|
||||||
const getImageWidthAndHeight = computed(() => {
|
const getImageWidthAndHeight = computed(() => {
|
||||||
|
|
@ -477,7 +606,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
function deleteCanvas() {}
|
function deleteImage() {}
|
||||||
|
|
||||||
// 复制到剪贴板-----------------------------------
|
// 复制到剪贴板-----------------------------------
|
||||||
const copyToClipboard = async (url) => {
|
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) {
|
async function fetchAndDownloadImage(url) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -522,6 +661,7 @@
|
||||||
const hideOrShowTextboxFlag = ref(true);
|
const hideOrShowTextboxFlag = ref(true);
|
||||||
function hideOrShowTextbox(value) {
|
function hideOrShowTextbox(value) {
|
||||||
hideOrShowTextboxFlag.value = value;
|
hideOrShowTextboxFlag.value = value;
|
||||||
|
emit('setHideOrShowTextboxFlag', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置画笔---------------------------------------------------------------
|
// 设置画笔---------------------------------------------------------------
|
||||||
|
|
@ -529,7 +669,7 @@
|
||||||
const graffitiColor = ref('#E23C39');
|
const graffitiColor = ref('#E23C39');
|
||||||
const nowGraffiti = ref(-1);
|
const nowGraffiti = ref(-1);
|
||||||
const nowMouseGraffiti = ref(0);
|
const nowMouseGraffiti = ref(0);
|
||||||
|
// 设置鼠标格式
|
||||||
function setGraffiti() {
|
function setGraffiti() {
|
||||||
graffitiFlag.value = !graffitiFlag.value;
|
graffitiFlag.value = !graffitiFlag.value;
|
||||||
if (graffitiFlag.value) {
|
if (graffitiFlag.value) {
|
||||||
|
|
@ -564,14 +704,33 @@
|
||||||
);
|
);
|
||||||
watch(
|
watch(
|
||||||
() => graffitis.value,
|
() => graffitis.value,
|
||||||
() => {
|
() => {},
|
||||||
props.nowPreviewRecord.graffitiJson = JSON.stringify(graffitis.value);
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
deep: true,
|
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();
|
const mouseCanvasRef = ref();
|
||||||
// 鼠标按下
|
// 鼠标按下
|
||||||
function onMouseDown(e) {
|
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>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.image {
|
.previewImage {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 900px;
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas {
|
.imageDiv {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 920px;
|
height: 820px;
|
||||||
|
background: #101010;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
|
|
@ -781,9 +978,22 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 22px;
|
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 {
|
.imageList {
|
||||||
|
|
@ -854,6 +1064,26 @@
|
||||||
justify-content: center;
|
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 {
|
.popoverClass {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
|
@ -883,11 +1113,58 @@
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border: 1px solid #000000;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: 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>
|
</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>
|
<template>
|
||||||
<div>
|
<div class="videoDiv">
|
||||||
<MonitorHK
|
<div class="showVideo">
|
||||||
|
<!-- <MonitorHK
|
||||||
v-if="props.nowPreviewRecord.manufacturer == '海康'"
|
v-if="props.nowPreviewRecord.manufacturer == '海康'"
|
||||||
:serialNumberValue="props.nowPreviewRecord.url"
|
:serialNumberValue="props.nowPreviewRecord.url"
|
||||||
:width="1380"
|
:width="1300"
|
||||||
:height="900"
|
:height="800"
|
||||||
/>
|
/>
|
||||||
<MonitorLC
|
<MonitorLC
|
||||||
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
|
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
|
||||||
:deviceId="props.nowPreviewRecord.url"
|
:deviceId="props.nowPreviewRecord.url"
|
||||||
:channelId="0"
|
:channelId="0"
|
||||||
:width="1396"
|
:width="1300"
|
||||||
:height="900"
|
:height="800"
|
||||||
:videoMuted="true"
|
:videoMuted="true"
|
||||||
/>
|
/> -->
|
||||||
<MonitorTX
|
<!-- <MonitorTX
|
||||||
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
|
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
|
||||||
:serialNumberValue="props.nowPreviewRecord.url"
|
:serialNumberValue="props.nowPreviewRecord.url"
|
||||||
:width="1396"
|
:width="1300"
|
||||||
:height="900"
|
:height="820"
|
||||||
:videoLoop="false"
|
/> -->
|
||||||
:videoMuted="true"
|
<!-- <MonitorQX
|
||||||
:videoFit="'contain'"
|
|
||||||
/>
|
|
||||||
<MonitorQX
|
|
||||||
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
|
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
|
||||||
:serialNumberValue="props.nowPreviewRecord.url"
|
:serialNumberValue="props.nowPreviewRecord.url"
|
||||||
:width="1396"
|
:width="1300"
|
||||||
:height="900"
|
:height="800"
|
||||||
:videoLoop="false"
|
:videoLoop="false"
|
||||||
:videoMuted="true"
|
:videoMuted="true"
|
||||||
:videoFit="'contain'"
|
: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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
DownloadOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
import { MonitorHK } from './preview';
|
import { MonitorHK } from './preview';
|
||||||
import { MonitorLC } from './preview';
|
import { MonitorLC } from './preview';
|
||||||
import { MonitorTX } from './preview';
|
import { MonitorTX } from './preview';
|
||||||
import { MonitorQX } from './preview';
|
import { MonitorQX } from './preview';
|
||||||
|
|
||||||
const props = defineProps(['nowPreviewRecord']);
|
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||||
console.log(props.nowPreviewRecord);
|
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>
|
</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();
|
initPlugin();
|
||||||
// 添加监听滚动事件
|
// 添加监听滚动事件
|
||||||
const elements = document.querySelectorAll('.ZhiGan_ModalVideo');
|
const elements = document.querySelectorAll('.ModalVideo');
|
||||||
if (elements.length > 0) {
|
if (elements.length > 0) {
|
||||||
// 遍历每个元素并绑定 scroll 事件
|
// 遍历每个元素并绑定 scroll 事件
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
|
|
@ -230,7 +230,7 @@
|
||||||
// 销毁视频窗口
|
// 销毁视频窗口
|
||||||
closeHkVideo();
|
closeHkVideo();
|
||||||
// 移除监听滚动事件
|
// 移除监听滚动事件
|
||||||
const elements = document.querySelectorAll('.ZhiGan_ModalVideo');
|
const elements = document.querySelectorAll('.ModalVideo');
|
||||||
if (elements.length > 0) {
|
if (elements.length > 0) {
|
||||||
// 遍历每个元素并绑定 scroll 事件
|
// 遍历每个元素并绑定 scroll 事件
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<video
|
<video
|
||||||
:id="'ZhiGan_ModalVideo' + props.timestamp"
|
:id="'TCPlayerVideo'"
|
||||||
class="TCPlayer-video-container"
|
class="TCPlayer-video-container"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
playsinline
|
playsinline
|
||||||
autoplay
|
autoplay
|
||||||
:loop="props.videoLoop"
|
:loop="false"
|
||||||
:muted="props.videoMuted"
|
:muted="true"
|
||||||
:style="{
|
:style="{
|
||||||
width: props.width + 'px',
|
width: props.width + 'px',
|
||||||
height: props.height + 'px',
|
height: props.height + 'px',
|
||||||
|
|
@ -20,15 +20,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
|
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
|
||||||
|
|
||||||
const props = defineProps([
|
const props = defineProps(['serialNumberValue', 'width', 'height']);
|
||||||
'serialNumberValue',
|
|
||||||
'width',
|
|
||||||
'height',
|
|
||||||
'timestamp',
|
|
||||||
'videoLoop',
|
|
||||||
'videoMuted',
|
|
||||||
'videoFit',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 视频控件初始化
|
// 视频控件初始化
|
||||||
let player: any = null;
|
let player: any = null;
|
||||||
|
|
@ -38,7 +30,7 @@
|
||||||
if (player) {
|
if (player) {
|
||||||
player.src(props.serialNumberValue);
|
player.src(props.serialNumberValue);
|
||||||
} else {
|
} else {
|
||||||
player = TCPlayer('ZhiGan_ModalVideo' + props.timestamp, {});
|
player = TCPlayer('TCPlayerVideo', {});
|
||||||
player.src(props.serialNumberValue);
|
player.src(props.serialNumberValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -75,7 +67,7 @@
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
video {
|
video {
|
||||||
display: block;
|
display: block;
|
||||||
object-fit: v-bind('props.videoFit');
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .vjs-live-control .vjs-live-display {
|
::v-deep .vjs-live-control .vjs-live-display {
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@
|
||||||
// viewer.scene.skyBox.show = false;
|
// viewer.scene.skyBox.show = false;
|
||||||
// 禁用大气层和太阳
|
// 禁用大气层和太阳
|
||||||
viewer.scene.skyAtmosphere.show = false;
|
viewer.scene.skyAtmosphere.show = false;
|
||||||
// viewer.scene.sun.show = false;
|
viewer.scene.sun.show = false;
|
||||||
// viewer.scene.moon.show = false;
|
viewer.scene.moon.show = false;
|
||||||
// 设置背景为黑色
|
// 设置背景为黑色
|
||||||
// viewer.scene.backgroundColor = Cesium.Color.BLACK;
|
// 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);
|
const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(3956);
|
||||||
viewer.terrainProvider = terrainProvider;
|
viewer.terrainProvider = terrainProvider;
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
originalModelMatrix = Cesium.Matrix4.clone(tileset.modelMatrix);
|
originalModelMatrix = Cesium.Matrix4.clone(tileset.modelMatrix);
|
||||||
// 设置模型贴地
|
// 设置模型贴地
|
||||||
// 启用贴地属性
|
// 启用贴地属性
|
||||||
tileset.clampToGround = true;
|
// tileset.clampToGround = true;
|
||||||
viewer.scene.primitives.add(tileset);
|
viewer.scene.primitives.add(tileset);
|
||||||
viewer.zoomTo(tileset);
|
viewer.zoomTo(tileset);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
.modelDiv {
|
.modelDiv {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1020px;
|
height: 920px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 890px;
|
height: 800px;
|
||||||
background: #000000;
|
background: #000000;
|
||||||
}
|
}
|
||||||
.modelDiv_bottom {
|
.modelDiv_bottom {
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@
|
||||||
<Map :airRoute="airRoute" />
|
<Map :airRoute="airRoute" />
|
||||||
</div>
|
</div>
|
||||||
<SelectComponent @selectChange="changeSelect" />
|
<SelectComponent @selectChange="changeSelect" />
|
||||||
<!-- <AirportInformation
|
<AirportInformation
|
||||||
@changeLive="changeAirportLive"
|
@changeLive="changeAirportLive"
|
||||||
@changeRemote="changeRemote"
|
@changeRemote="changeRemote"
|
||||||
:msgData="msgData"
|
:msgData="msgData"
|
||||||
/> -->
|
/>
|
||||||
<UAVInformation
|
<UAVInformation
|
||||||
:msgData="msgData"
|
:msgData="msgData"
|
||||||
@changeLoadControl="changeLoadControl"
|
@changeLoadControl="changeLoadControl"
|
||||||
@changeFlightControl="changeFlightControl"
|
@changeFlightControl="changeFlightControl"
|
||||||
/>
|
/>
|
||||||
<!-- 远程调试 -->
|
<!-- 远程调试 -->
|
||||||
<div v-if="remoteVisible">
|
<!-- <div v-if="remoteVisible">
|
||||||
<RemoteDebugging @changeRemote="changeRemote" :msgData="msgData" />
|
<RemoteDebugging @changeRemote="changeRemote" :msgData="msgData" />
|
||||||
</div>
|
</div> -->
|
||||||
<!-- 负载控制 -->
|
<!-- 负载控制 -->
|
||||||
<div v-if="loadControlVisible">
|
<div v-if="loadControlVisible">
|
||||||
<LoadControl @changeLoadControl="changeLoadControl" />
|
<LoadControl @changeLoadControl="changeLoadControl" :msgData="msgData" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 飞行控制 -->
|
<!-- 飞行控制 -->
|
||||||
<div v-if="flightControlVisible">
|
<div v-if="flightControlVisible">
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
// 无人机直播
|
// 无人机直播
|
||||||
const livePreviewVisible = ref(true);
|
const livePreviewVisible = ref(true);
|
||||||
// 远程调试
|
// 远程调试
|
||||||
const remoteVisible = ref(true);
|
const remoteVisible = ref(false);
|
||||||
// 负载控制
|
// 负载控制
|
||||||
const loadControlVisible = ref(false);
|
const loadControlVisible = ref(false);
|
||||||
// 飞行控制
|
// 飞行控制
|
||||||
|
|
@ -103,10 +103,12 @@
|
||||||
// 接收消息
|
// 接收消息
|
||||||
getClient().on('message', (topic, message) => {
|
getClient().on('message', (topic, message) => {
|
||||||
const rs = JSON.parse(message);
|
const rs = JSON.parse(message);
|
||||||
msgData.value = {
|
if (rs) {
|
||||||
topic: topic,
|
msgData.value = {
|
||||||
message: rs,
|
topic: topic,
|
||||||
};
|
message: rs,
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="airport-information" v-if="airportVal">
|
<div class="airport-information" v-if="airportVal">
|
||||||
<div class="title"
|
<div class="title"
|
||||||
>机场信息:<span>
|
>机场信息:<span v-if="airportVal.mode_code">
|
||||||
<template v-if="airportVal.mode_code == 0">作业准备中</template>
|
<template v-if="airportVal.mode_code == 0">作业准备中</template>
|
||||||
<template v-else-if="airportVal.mode_code == 1">飞行作业中</template>
|
<template v-else-if="airportVal.mode_code == 1">飞行作业中</template>
|
||||||
<template v-else-if="airportVal.mode_code == 2">作业后状态恢复</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 == 5">任务空闲</template>
|
||||||
<template v-else-if="airportVal.mode_code == 255">飞行器异常</template>
|
<template v-else-if="airportVal.mode_code == 255">飞行器异常</template>
|
||||||
<template v-else-if="airportVal.mode_code == 256">未知状态</template>
|
<template v-else-if="airportVal.mode_code == 256">未知状态</template>
|
||||||
</span></div
|
</span>
|
||||||
>
|
<span v-else>未知状态</span>
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="content-title">
|
<div class="content-title">
|
||||||
{{ time }}
|
{{ time }}
|
||||||
|
|
@ -46,7 +47,7 @@
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/rate.png" alt="" />
|
<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>
|
</div>
|
||||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
|
|
@ -57,17 +58,17 @@
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/voltage.png" alt="" />
|
<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>
|
</div>
|
||||||
<div class="content-button">
|
<!-- <div class="content-button">
|
||||||
<a-button type="primary" style="background: #3a57e8" @click="emits('changeLive')"
|
<a-button type="primary" style="background: #3a57e8" @click="emits('changeLive')"
|
||||||
>机场直播</a-button
|
>机场直播</a-button
|
||||||
>
|
>
|
||||||
<a-button type="primary" style="background: #0a99eb" @click="emits('changeRemote')"
|
<a-button type="primary" style="background: #0a99eb" @click="emits('changeRemote')"
|
||||||
>远程调试</a-button
|
>远程调试</a-button
|
||||||
>
|
>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -99,7 +100,8 @@
|
||||||
watch(
|
watch(
|
||||||
() => props.msgData,
|
() => props.msgData,
|
||||||
(val) => {
|
(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);
|
// console.log(val);
|
||||||
airportVal.value = val.message.data;
|
airportVal.value = val.message.data;
|
||||||
time.value = timestampToFormattedDate(val.message.timestamp);
|
time.value = timestampToFormattedDate(val.message.timestamp);
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="content-button">
|
<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>
|
||||||
<a-button>一键返航</a-button>
|
<a-button>一键返航</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-info">
|
<div class="content-info">
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="remote-debugging" v-if="airportVal" v-drag>
|
<div class="remote-debugging" v-if="airportVal" v-drag>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span> 负载控制 </span>
|
<span> 云台相机控制 </span>
|
||||||
<div @click="emits('changeLoadControl')">
|
<div @click="emits('changeLoadControl')">
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<span>负载控制</span>
|
<span>相机控制权</span>
|
||||||
<a-button>抢夺负载控制</a-button>
|
<a-button @click="obtain">获取</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-item">
|
<!-- <div class="content-item">
|
||||||
<span>切换相机模式</span>
|
<span>切换相机模式</span>
|
||||||
<a-button>抢夺负载控制</a-button>
|
<a-button>抢夺负载控制</a-button>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<span>拍照</span>
|
<span>拍照</span>
|
||||||
<a-button>单拍</a-button>
|
<a-button @click="singleShot">单拍</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="direction-controller">
|
<div class="direction-controller">
|
||||||
<img src="@/assets/images/flightoperation/direction_controller.png" alt="" />
|
<img src="@/assets/images/flightoperation/direction_controller.png" alt="" />
|
||||||
<div class="direction-controller-top"></div>
|
<div class="direction-controller-top" @click="changeDrc('top')"></div>
|
||||||
<div class="direction-controller-right"></div>
|
<div class="direction-controller-right" @click="changeDrc('right')"></div>
|
||||||
<div class="direction-controller-bottom"></div>
|
<div class="direction-controller-bottom" @click="changeDrc('bottom')"></div>
|
||||||
<div class="direction-controller-left"></div>
|
<div class="direction-controller-left" @click="changeDrc('left')"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch, reactive } from 'vue';
|
||||||
import { getClient, createConnection } from '@/utils/mqtt';
|
import { getReizeClient, createSeizeConnection, clientReizePublish } from '@/utils/mqtt';
|
||||||
import { CloseOutlined } from '@ant-design/icons-vue';
|
import { CloseOutlined } from '@ant-design/icons-vue';
|
||||||
import { vDrag } from '@/utils/drag';
|
import { vDrag } from '@/utils/drag';
|
||||||
|
import { buildGUID } from '@/utils/uuid';
|
||||||
|
import { eventsTopic, events_replyTopic, drcDownTopic } from '@/utils/debugging/events';
|
||||||
|
|
||||||
const emits = defineEmits(['changeLoadControl']);
|
const emits = defineEmits(['changeLoadControl']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
airportAllVal: Object,
|
msgData: Object,
|
||||||
});
|
});
|
||||||
console.log(props);
|
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({
|
const airportVal: any = ref({
|
||||||
mode_code: 0,
|
mode_code: 0,
|
||||||
wind_speed: 0,
|
wind_speed: 0,
|
||||||
|
|
@ -56,14 +64,74 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
watch(
|
watch(
|
||||||
() => props.airportAllVal,
|
() => props.msgData,
|
||||||
(val) => {
|
(val) => {
|
||||||
console.log(val);
|
if (val.topic == 'thing/product/8UUXN5400A079H/events_reply') {
|
||||||
airportVal.value = val.data;
|
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>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.remote-debugging {
|
.remote-debugging {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="airport-information">
|
<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">
|
||||||
<div class="content-title">
|
<div class="content-title">
|
||||||
{{ time }}
|
{{ time }}
|
||||||
|
|
@ -9,45 +17,65 @@
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/project.png" alt="" />
|
<img src="@/assets/images/flightoperation/project.png" alt="" />
|
||||||
1
|
{{ uavInformation.wireless_link ? uavInformation.wireless_link.dongle_number : 0 }}
|
||||||
</div>
|
</div>
|
||||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||||
<div class="item-div">
|
<!-- <div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/arrow.png" alt="" />
|
<img src="@/assets/images/flightoperation/arrow.png" alt="" />
|
||||||
0%
|
0%
|
||||||
</div>
|
</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 class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/electricity.png" alt="" />
|
<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>
|
</div>
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/agl.png" alt="" />
|
<img src="@/assets/images/flightoperation/sdr.png" alt="" />
|
||||||
0M
|
<!-- sdr_link_state -->
|
||||||
|
<template
|
||||||
|
v-if="uavInformation.wireless_link && uavInformation.wireless_link.sdr_link_state == 1"
|
||||||
|
>连接</template
|
||||||
|
>
|
||||||
|
<template v-else>断开</template>
|
||||||
</div>
|
</div>
|
||||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/agl.png" alt="" />
|
<img src="@/assets/images/flightoperation/4g.png" alt="" />
|
||||||
0M
|
<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>
|
</div>
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/hs.png" alt="" />
|
<img src="@/assets/images/flightoperation/sdr.png" alt="" />
|
||||||
0m/s
|
{{ uavInformation.wireless_link ? uavInformation.wireless_link.sdr_freq_band : 0 }}
|
||||||
</div>
|
</div>
|
||||||
<a-divider type="vertical" style="border-color: #4e5778" />
|
<a-divider type="vertical" style="border-color: #4e5778" />
|
||||||
<div class="item-div">
|
<div class="item-div">
|
||||||
<img src="@/assets/images/flightoperation/h.png" alt="" />
|
<img src="@/assets/images/flightoperation/4g.png" alt="" />
|
||||||
0M
|
{{ uavInformation.wireless_link ? uavInformation.wireless_link['4g_freq_band'] : 0 }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-edit">
|
<div class="content-edit">
|
||||||
|
|
@ -89,7 +117,7 @@
|
||||||
>飞行控制</a-button
|
>飞行控制</a-button
|
||||||
>
|
>
|
||||||
<a-button type="primary" style="background: #0a99eb" @click="emits('changeLoadControl')"
|
<a-button type="primary" style="background: #0a99eb" @click="emits('changeLoadControl')"
|
||||||
>负载控制</a-button
|
>云台相机控制</a-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-button">
|
<div class="content-button">
|
||||||
|
|
@ -104,7 +132,6 @@
|
||||||
import { reactive, ref, watch } from 'vue';
|
import { reactive, ref, watch } from 'vue';
|
||||||
import { CopyOutlined, EditOutlined } from '@ant-design/icons-vue';
|
import { CopyOutlined, EditOutlined } from '@ant-design/icons-vue';
|
||||||
import { timestampToFormattedDate } from '@/utils/index';
|
import { timestampToFormattedDate } from '@/utils/index';
|
||||||
import { getClient, createConnection, clientPublish, clientSubscribe } from '@/utils/mqtt';
|
|
||||||
import { buildGUID } from '@/utils/uuid';
|
import { buildGUID } from '@/utils/uuid';
|
||||||
import { servicesTopic, services_replyTopic } from '@/utils/debugging/remote';
|
import { servicesTopic, services_replyTopic } from '@/utils/debugging/remote';
|
||||||
|
|
||||||
|
|
@ -138,13 +165,41 @@
|
||||||
limitedRange: '500m',
|
limitedRange: '500m',
|
||||||
obstacleAvoidance: '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()));
|
const time = ref(timestampToFormattedDate(new Date().getTime()));
|
||||||
watch(
|
watch(
|
||||||
() => props.msgData,
|
() => props.msgData,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val.topic == 'thing/product/8UUXN5400A079H/osd') {
|
if (val.topic == 'thing/product/8UUXN5400A079H/osd') {
|
||||||
// console.log(val);
|
if (
|
||||||
time.value = timestampToFormattedDate(val.message.timestamp);
|
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>
|
<style lang="less" scoped>
|
||||||
.airport-information {
|
.airport-information {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 360px;
|
top: 320px;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue