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);
|
||||
};
|
@ -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,271 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title"> 详细信息 </div>
|
||||
<div class="info">
|
||||
<a-row>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">图片名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue" v-if="editNameFlag">
|
||||
{{ props.nowPreviewRecord.name }}
|
||||
<EditOutlined style="font-size: 20px; color: #07aaed" @click="editNameChange" />
|
||||
</span>
|
||||
<span class="infovalue" v-if="!editNameFlag">
|
||||
<a-input
|
||||
v-model:value="editName"
|
||||
size="small"
|
||||
@keypress.enter="pressEnterNameFunction"
|
||||
@blur="editNameBlur"
|
||||
/>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片类型</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.imgtype }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">任务名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.taskname }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">航线名称</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.airlineName }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片分辨率</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">
|
||||
{{ props.nowPreviewRecord.width + '*' + props.nowPreviewRecord.height }}
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片大小</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.size }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄飞机</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographFeiji }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄负载</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographNumber }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄人员</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographMan }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">拍摄时间</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.photographTime }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">标签</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue" v-if="addLabelFlag">
|
||||
<a-tag color="success" v-for="la in props.nowPreviewRecord.label" :key="la">
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<PlusSquareOutlined style="font-size: 20px; color: #07aaed" @click="addLabelChange" />
|
||||
</span>
|
||||
<span class="infovalue" v-if="!addLabelFlag">
|
||||
<a-tag
|
||||
color="success"
|
||||
v-for="la in props.nowPreviewRecord.label"
|
||||
:key="la"
|
||||
closable
|
||||
@close="deleteLabel(la)"
|
||||
>
|
||||
{{ la }}
|
||||
</a-tag>
|
||||
<a-input
|
||||
v-model:value="newLabelName"
|
||||
size="small"
|
||||
placeholder=""
|
||||
@keypress.enter="pressEnterLabelFunction"
|
||||
@blur="addLabelBlur"
|
||||
/>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">涂鸦总数</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infovalue">{{ props.nowPreviewRecord.label }} </span>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<span class="infotitle">照片位置</span>
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<span class="infobutton">
|
||||
<EnvironmentOutlined style="font-size: 20px; color: #07aaed" @click="flyPoint" />
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div class="map">
|
||||
<Map ref="mapRef" :nowPreviewRecord="props.nowPreviewRecord" />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { EditOutlined, PlusSquareOutlined, EnvironmentOutlined } from '@ant-design/icons-vue';
|
||||
import { orgPosGroup } from '@/api/demo/system';
|
||||
import { Map } from './preview';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
const props = defineProps(['nowPreviewRecord']);
|
||||
const emit = defineEmits(['reloadTable']);
|
||||
|
||||
// 修改名称--------------------------------
|
||||
const editNameFlag = ref(true);
|
||||
const editName = ref(props.nowPreviewRecord.name.split('.').slice(0, -1).join('.'));
|
||||
function editNameChange() {
|
||||
editNameFlag.value = false;
|
||||
}
|
||||
// 修改名称方法
|
||||
async function pressEnterNameFunction() {
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newName: editName.value + '.' + props.nowPreviewRecord.name.split('.').pop(),
|
||||
};
|
||||
props.nowPreviewRecord.name =
|
||||
editName.value + '.' + props.nowPreviewRecord.name.split('.').pop();
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
editNameFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
}
|
||||
function editNameBlur() {
|
||||
editNameFlag.value = true;
|
||||
editName.value = props.nowPreviewRecord.name.split('.').slice(0, -1).join('.');
|
||||
}
|
||||
|
||||
// 标签--------------------------------
|
||||
const addLabelFlag = ref(true);
|
||||
const newLabelName = ref('');
|
||||
function addLabelChange() {
|
||||
addLabelFlag.value = false;
|
||||
}
|
||||
// 添加标签方法
|
||||
async function pressEnterLabelFunction() {
|
||||
if (!newLabelName.value) {
|
||||
return;
|
||||
}
|
||||
if (!props.nowPreviewRecord.label.includes(newLabelName.value)) {
|
||||
props.nowPreviewRecord.label.push(newLabelName.value);
|
||||
let query = {
|
||||
id: props.nowPreviewRecord.id,
|
||||
newLabel: props.nowPreviewRecord.label,
|
||||
};
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
newLabelName.value = '';
|
||||
return;
|
||||
// 调用接口
|
||||
const data = await orgPosGroup(query);
|
||||
if (data) {
|
||||
addLabelFlag.value = true;
|
||||
emit('reloadTable');
|
||||
return createMessage.success('修改名称成功');
|
||||
} else {
|
||||
return createMessage.error('修改名称失败');
|
||||
}
|
||||
} else {
|
||||
return createMessage.error('此标签已存在!');
|
||||
}
|
||||
}
|
||||
|
||||
function deleteLabel(value) {
|
||||
props.nowPreviewRecord.label = props.nowPreviewRecord.label.filter((item) => item !== value);
|
||||
emit('reloadTable');
|
||||
}
|
||||
function addLabelBlur() {
|
||||
addLabelFlag.value = true;
|
||||
newLabelName.value = '';
|
||||
}
|
||||
|
||||
// 地图--------------------------------
|
||||
const mapRef = ref();
|
||||
function flyPoint() {
|
||||
mapRef.value?.flyToPoint([props.nowPreviewRecord.lng, props.nowPreviewRecord.lat]);
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 60px;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.info {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
height: 480px;
|
||||
|
||||
.infotitle {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.infovalue {
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.infobutton {
|
||||
font-size: 10px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
@ -1,46 +1,245 @@
|
||||
<template>
|
||||
<div>
|
||||
<MonitorHK
|
||||
<div class="videoDiv">
|
||||
<div class="showVideo">
|
||||
<!-- <MonitorHK
|
||||
v-if="props.nowPreviewRecord.manufacturer == '海康'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1380"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
/>
|
||||
<MonitorLC
|
||||
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
|
||||
:deviceId="props.nowPreviewRecord.url"
|
||||
:channelId="0"
|
||||
:width="1396"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
:videoMuted="true"
|
||||
/>
|
||||
<MonitorTX
|
||||
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1396"
|
||||
:height="900"
|
||||
:videoLoop="false"
|
||||
:videoMuted="true"
|
||||
:videoFit="'contain'"
|
||||
/>
|
||||
<MonitorQX
|
||||
/> -->
|
||||
<!-- <MonitorTX
|
||||
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1300"
|
||||
:height="820"
|
||||
/> -->
|
||||
<!-- <MonitorQX
|
||||
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
|
||||
:serialNumberValue="props.nowPreviewRecord.url"
|
||||
:width="1396"
|
||||
:height="900"
|
||||
:width="1300"
|
||||
:height="800"
|
||||
:videoLoop="false"
|
||||
:videoMuted="true"
|
||||
:videoFit="'contain'"
|
||||
/>
|
||||
/> -->
|
||||
<video :src="props.nowPreviewRecord.url" class="video-player" controls muted autoplay></video>
|
||||
</div>
|
||||
|
||||
<div class="bottomDiv">
|
||||
<div class="buttonList">
|
||||
<!-- 下载 -->
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>下载</span>
|
||||
</template>
|
||||
<DownloadOutlined @click="fetchAndDownloadVideo(props.nowPreviewRecord.url)" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 删除 -->
|
||||
<div class="button">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>删除</span>
|
||||
</template>
|
||||
<DeleteOutlined @click="deleteVideo" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="imageList">
|
||||
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
|
||||
<div
|
||||
:class="li.id == props.nowPreviewRecord.id ? 'bottom_div_choose' : 'bottom_div'"
|
||||
v-if="li.type == 'video'"
|
||||
>
|
||||
<img :src="li.gifZoomImage" :width="60" :height="40" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 上一张、下一张图片 -->
|
||||
<div class="leftButton" @click="clickLeftOrRightButton('left')">
|
||||
<LeftOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
<div class="rightButton" @click="clickLeftOrRightButton('right')">
|
||||
<RightOutlined style="color: #ffffff; font-size: 20px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
DownloadOutlined,
|
||||
DeleteOutlined,
|
||||
LeftOutlined,
|
||||
RightOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { MonitorHK } from './preview';
|
||||
import { MonitorLC } from './preview';
|
||||
import { MonitorTX } from './preview';
|
||||
import { MonitorQX } from './preview';
|
||||
|
||||
const props = defineProps(['nowPreviewRecord']);
|
||||
console.log(props.nowPreviewRecord);
|
||||
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
|
||||
const emit = defineEmits(['chooseNowPreviewRecord', 'reloadTable']);
|
||||
|
||||
// 上一张、下一张图片
|
||||
function clickLeftOrRightButton(direction) {
|
||||
const list = props.previewRecordList.filter((item) => item.type == 'video');
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
if (list[index].id == props.nowPreviewRecord.id) {
|
||||
if (direction == 'left') {
|
||||
if (index == 0) {
|
||||
chooseNowPreviewRecord(list[list.length - 1]);
|
||||
} else {
|
||||
chooseNowPreviewRecord(list[index - 1]);
|
||||
}
|
||||
}
|
||||
if (direction == 'right') {
|
||||
if (index == list.length - 1) {
|
||||
chooseNowPreviewRecord(list[0]);
|
||||
} else {
|
||||
chooseNowPreviewRecord(list[index + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 下载
|
||||
async function fetchAndDownloadVideo(url) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
mode: 'cors',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const urlObject = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = urlObject;
|
||||
link.download = props.nowPreviewRecord.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(urlObject);
|
||||
} catch (error) {
|
||||
console.error('Error downloading image:', error);
|
||||
}
|
||||
}
|
||||
// 删除
|
||||
function deleteVideo() {}
|
||||
// 选择
|
||||
function chooseNowPreviewRecord(value) {
|
||||
emit('chooseNowPreviewRecord', value);
|
||||
}
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
<style lang="less">
|
||||
.videoDiv {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.showVideo {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 820px;
|
||||
}
|
||||
|
||||
.bottomDiv {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: #1c1c1c;
|
||||
display: block;
|
||||
|
||||
.buttonList {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
|
||||
.button {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
color: #ffffff;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.imageList {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
|
||||
.bottom_div {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bottom_div_choose {
|
||||
padding: 4px;
|
||||
border: 1px solid yellow;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 上一张、下一张
|
||||
.leftButton {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 45%;
|
||||
z-index: 200;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.rightButton {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 45%;
|
||||
z-index: 200;
|
||||
background: #9c9c9c;
|
||||
border-radius: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.video-player {
|
||||
height: 820px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue