You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1324 lines
39 KiB
Vue

<template>
<div class="previewImage">
<div id="imageDiv" class="imageDiv" style="overflow: hidden">
<div
ref="mouseCanvasRef"
class="dragModal"
@mousedown="onMouseDown"
@wheel="onWheel"
:style="{
position: 'relative',
transform: `scale(${scale}) rotate(${rotationAngle}deg)`,
transition: 'transform 0.2s',
width: `${imageWidth}px`,
height: `${imageHeight}px`,
background: `url(${VITE_GLOB_MEDIALIBRARY_IMAGE_URL + props.nowPreviewRecord.objectKey}) no-repeat center center`,
backgroundSize: 'contain',
backgroundPosition: 'center',
}"
>
<!-- 创建的矩形 -->
<div
v-if="hideOrShowGraffitiFlag"
v-for="(rect, index) in graffitis"
:key="index"
:style="{
position: 'absolute',
left: rect.x + 'px',
top: rect.y + 'px',
width: rect.width + 'px',
height: rect.height + 'px',
zIndex: rect.status == 'edit' ? 1000 : 201,
}"
>
<!-- 文字标签 -->
<div
v-if="nowGraffiti == index || rect.text"
:style="{
position: 'absolute',
left: '0px',
top: rect.height + 'px',
width: '235px',
height: 30 + 'px',
background: nowGraffiti == index ? '#ffffff' : '#ffffff00',
}"
>
<div style="display: block" v-if="nowGraffiti == index">
<a-input
v-model:value="rect.text"
style="width: 110px; height: 30px; margin-right: 10px"
@keypress.enter="
nowGraffiti = -1;
rect.status = 'success';
addGraffiti();
"
/>
<CheckOutlined
style="margin-right: 10px; padding: 3px; color: green"
@click="
nowGraffiti = -1;
rect.status = 'success';
addGraffiti();
"
/>
<CloseOutlined
style="margin-right: 10px; padding: 3px; color: red"
@click="
rect.text
? ((rect.x = graffitisClone[index].x),
(rect.y = graffitisClone[index].y),
(rect.width = graffitisClone[index].width),
(rect.height = graffitisClone[index].height),
(rect.color = graffitisClone[index].color),
(rect.text = graffitisClone[index].text),
(rect.status = 'success'))
: graffitis.splice(index, 1);
nowGraffiti = -1;
"
/>
<DeleteOutlined
style="margin-right: 10px; padding: 3px"
@click="
deleteGraffiti(index, rect.text);
nowGraffiti = -1;
"
/>
<a-popover placement="top">
<template #content>
<div style="display: flex; gap: 3px">
<div
class="popoverClass"
v-for="color in ['#2D8CF0', '#19BE6B', '#FFBB00', '#E23C39', '#B620E0']"
:key="color"
:style="{ background: color }"
@click="rect.color = color"
>
<CheckOutlined
v-if="rect.color == color"
style="color: white; padding: 3px"
/>
</div>
</div>
</template>
<FontColorsOutlined style="" />
</a-popover>
</div>
<div
v-if="nowGraffiti != index"
style="
font-size: 20px;
font-weight: bold;
-webkit-text-stroke: 0.1px white;
width: fit-content;
"
:style="{
textDecoration: rect.status == 'mouse' ? 'underline' : '',
}"
@mouseenter="graffitiFlag && rect.status != 'edit' ? (rect.status = 'mouse') : ''"
@mouseleave="graffitiFlag && rect.status == 'mouse' ? (rect.status = 'success') : ''"
@click="
graffitiFlag
? ((rect.status = 'edit'),
(graffitisClone = cloneDeep(graffitis)),
(nowGraffiti = index))
: ''
"
>
{{ rect.text }}
</div>
</div>
<!-- 上 -->
<div
:style="{
position: 'absolute',
left: rect.status == 'edit' ? '6px' : '0px',
top: '0px',
width: rect.status == 'edit' ? `${rect.width - 14}px` : `${rect.width}px`,
height: '4px',
background: `${rect.color}`,
outline: rect.status != 'success' ? `2px solid #ffffff` : '',
}"
@mouseenter="mouseenter(rect, 'top')"
@mouseleave="mouseleave(rect)"
@click="graffitiFlag ? ((rect.status = 'edit'), (nowGraffiti = index)) : ''"
@mousedown="funMouseDownEdit($event, index, 'top')"
/>
<!-- 下 -->
<div
:style="{
position: 'absolute',
left: rect.status == 'edit' ? '6px' : '0px',
bottom: `0px`,
width: rect.status == 'edit' ? `${rect.width - 14}px` : `${rect.width}px`,
height: '4px',
background: `${rect.color}`,
outline: rect.status != 'success' ? `2px solid #ffffff` : '',
}"
@mouseenter="mouseenter(rect, 'bottom')"
@mouseleave="mouseleave(rect)"
@click="graffitiFlag ? ((rect.status = 'edit'), (nowGraffiti = index)) : ''"
@mousedown="funMouseDownEdit($event, index, 'bottom')"
/>
<!-- 右 -->
<div
:style="{
position: 'absolute',
right: '0px',
top: rect.status == 'edit' ? '6px' : '0px',
width: '4px',
height: rect.status == 'edit' ? `${rect.height - 14}px` : `${rect.height}px`,
background: `${rect.color}`,
outline: rect.status != 'success' ? `2px solid #ffffff` : '',
}"
@mouseenter="mouseenter(rect, 'right')"
@mouseleave="mouseleave(rect)"
@click="graffitiFlag ? ((rect.status = 'edit'), (nowGraffiti = index)) : ''"
@mousedown="funMouseDownEdit($event, index, 'right')"
/>
<!-- 左 -->
<div
:style="{
position: 'absolute',
left: '0px',
top: rect.status == 'edit' ? '6px' : '0px',
width: '4px',
height: rect.status == 'edit' ? `${rect.height - 14}px` : `${rect.height}px`,
background: `${rect.color}`,
outline: rect.status != 'success' ? `2px solid #ffffff` : '',
}"
@mouseenter="mouseenter(rect, 'left')"
@mouseleave="mouseleave(rect)"
@click="graffitiFlag ? ((rect.status = 'edit'), (nowGraffiti = index)) : ''"
@mousedown="funMouseDownEdit($event, index, 'left')"
/>
<!-- 左上 -->
<div
v-if="rect.status == 'edit'"
:style="{
position: 'absolute',
left: '-4px',
top: '-4px',
width: '12px',
height: '12px',
background: `#ffffff`,
}"
@mouseenter="mouseenter(rect, 'leftTop')"
@mouseleave="mouseleave(rect)"
@mousedown="funMouseDownEdit($event, index, 'leftTop')"
/>
<!-- 右下 -->
<div
v-if="rect.status == 'edit'"
:style="{
position: 'absolute',
right: '-4px',
bottom: '-4px',
width: '12px',
height: '12px',
background: `#ffffff`,
}"
@mouseenter="mouseenter(rect, 'rightBottom')"
@mouseleave="mouseleave(rect)"
@mousedown="funMouseDownEdit($event, index, 'rightBottom')"
/>
<!-- 右上 -->
<div
v-if="rect.status == 'edit'"
:style="{
position: 'absolute',
right: '-4px',
top: '-4px',
width: '12px',
height: '12px',
background: `#ffffff`,
}"
@mouseenter="mouseenter(rect, 'rightTop')"
@mouseleave="mouseleave(rect)"
@mousedown="funMouseDownEdit($event, index, 'rightTop')"
/>
<!-- 左下 -->
<div
v-if="rect.status == 'edit'"
:style="{
position: 'absolute',
left: '-4px',
bottom: '-4px',
width: '12px',
height: '12px',
background: `#ffffff`,
}"
@mouseenter="mouseenter(rect, 'leftBottom')"
@mouseleave="mouseleave(rect)"
@mousedown="funMouseDownEdit($event, index, 'leftBottom')"
/>
</div>
</div>
<!-- 全屏 -->
<div class="expandButton" @click="clickExpandButton">
<ExpandOutlined v-if="!fullscreenFlag" style="color: #3c3c3c; font-size: 25px" />
<CompressOutlined v-if="fullscreenFlag" style="color: #3c3c3c; font-size: 25px" />
</div>
</div>
<div class="bottomDiv">
<div class="buttonList">
<!-- 放大 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>放大</span>
</template>
<ZoomInOutlined @click="zoomIn" />
</a-tooltip>
</div>
<!-- 缩小 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>缩小</span>
</template>
<ZoomOutOutlined @click="zoomOut" />
</a-tooltip>
</div>
<!-- 顺时针旋转 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>顺时针旋转</span>
</template>
<RotateRightOutlined @click="rotateClockwise" />
</a-tooltip>
</div>
<!-- 逆时针旋转 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>逆时针旋转</span>
</template>
<RotateLeftOutlined @click="rotateCounterClockwise" />
</a-tooltip>
</div>
<!-- 100%比例 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>100%比例</span>
</template>
<OneToOneOutlined @click="setProportion" />
</a-tooltip>
</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">
<a-tooltip placement="top">
<template #title>
<span>对比</span>
</template>
<BorderHorizontalOutlined @click="c" />
</a-tooltip>
</div>
<!-- 复制到剪贴板 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>图片链接复制到剪贴板</span>
</template>
<ExportOutlined @click="copyToClipboard(props.nowPreviewRecord.objectKey)" />
</a-tooltip>
</div>
<!-- 下载 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>下载</span>
</template>
<DownloadOutlined @click="fetchAndDownloadImage(props.nowPreviewRecord.objectKey)" />
</a-tooltip>
</div>
<!-- 删除 -->
<div class="button">
<a-tooltip placement="top">
<template #title>
<span>删除</span>
</template>
<DeleteOutlined @click="deleteImage" />
</a-tooltip>
</div>
</div>
<div class="imageChooseList">
<div v-for="li in props.previewRecordList" :key="li.id" @click="chooseNowPreviewRecord(li)">
<div
v-if="li.objectKey"
:class="li.id == props.nowPreviewRecord.id ? 'bottom_div_choose' : 'bottom_div'"
>
<img
:src="VITE_GLOB_MEDIALIBRARY_IMAGE_URL + li.minipic"
loading="lazy"
:width="60"
:height="35"
/>
</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 @click="setGraffiti" :class="graffitiFlag ? 'graffitiButton_choose' : 'graffitiButton'">
<a-popover placement="left">
<template #content>
<div style="display: flex; gap: 5px">
<div
class="popoverClass"
v-for="color in ['#2D8CF0', '#19BE6B', '#FFBB00', '#E23C39', '#B620E0']"
:key="color"
:style="{ background: color }"
@click="graffitiColor = color"
>
<CheckOutlined v-if="graffitiColor == color" style="color: white" />
</div>
</div>
</template>
<EditOutlined
:style="{
color: graffitiColor,
fontSize: '20px',
}"
/>
</a-popover>
</div>
<!-- 隐藏or显示涂鸦和标签 -->
<div class="showTextboxClass">
<div class="button">
<a-tooltip placement="left">
<template #title>
<span>{{ hideOrShowGraffitiFlag ? '点击隐藏涂鸦信息' : '点击显示涂鸦信息' }}</span>
</template>
<EyeOutlined
@click="hideOrShowGraffiti(false)"
v-if="hideOrShowGraffitiFlag"
style="color: #2d8cf0"
/>
<EyeInvisibleOutlined
@click="hideOrShowGraffiti(true)"
v-if="!hideOrShowGraffitiFlag"
style="color: #595959"
/>
<div style="position: absolute; bottom: -12px; right: -4px; pointer-events: none">
<div
v-html="
`${
hideOrShowGraffitiFlag
? graffiti_svg.replaceAll('currentColor', '#2d8cf0')
: graffiti_svg.replaceAll('currentColor', '#595959')
}`
"
/>
</div>
</a-tooltip>
</div>
<div class="button">
<a-tooltip placement="left">
<template #title>
<span>
{{
hideOrShowTextboxFlag
? '点击隐藏地图上的其他标注信息'
: '点击显示地图上的其他标注信息'
}}
</span>
</template>
<EyeOutlined
@click="hideOrShowTextbox(false)"
v-if="hideOrShowTextboxFlag"
style="color: #2d8cf0"
/>
<EyeInvisibleOutlined
@click="hideOrShowTextbox(true)"
v-if="!hideOrShowTextboxFlag"
style="color: #595959"
/>
<div style="position: absolute; bottom: -10px; right: -3px; pointer-events: none">
<div
v-html="
`${
hideOrShowTextboxFlag
? otherimage_svg.replaceAll('currentColor', '#2d8cf0')
: otherimage_svg.replaceAll('currentColor', '#595959')
}`
"
/>
</div>
</a-tooltip>
</div>
</div>
<!-- 退出涂鸦 -->
<div class="escTip" v-if="graffitiFlag">
<div class="whiteEsc">Esc</div>
<div class="blackTip">退</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
import {
CloseOutlined,
RightOutlined,
LeftOutlined,
ZoomOutOutlined,
ZoomInOutlined,
RotateLeftOutlined,
RotateRightOutlined,
ExportOutlined,
DownloadOutlined,
DeleteOutlined,
CheckOutlined,
EyeOutlined,
EyeInvisibleOutlined,
EditOutlined,
FontColorsOutlined,
RedoOutlined,
OneToOneOutlined,
BorderHorizontalOutlined,
ExpandOutlined,
CompressOutlined,
} from '@ant-design/icons-vue';
import { UpdatePicStatus, Deletepic } from '@/api/demo/mediaLibrary';
import { graffiti_svg, otherimage_svg } from './preview';
import { useMessage } from '@/hooks/web/useMessage';
import { cloneDeep } from 'lodash-es';
import { getAppEnvConfig } from '@/utils/env';
const { VITE_GLOB_MEDIALIBRARY_IMAGE_URL } = getAppEnvConfig();
const { createConfirm, createMessage } = useMessage();
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emits = defineEmits([
'chooseNowPreviewRecord',
'handleSuccessPreview',
'setHideOrShowTextboxFlag',
]);
// 上一张、下一张图片
function clickLeftOrRightButton(direction) {
const list = props.previewRecordList.filter(
(item) => item.objectKey && item.objectKey.includes('jpeg'),
);
graffitiFlag.value = false;
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) {
emits('chooseNowPreviewRecord', value);
graffitiFlag.value = false;
refresh();
}
// 刷新表格
function handleSuccessPreview(record = null) {
emits('handleSuccessPreview', record);
}
// 缩放比例
const scale = ref(1);
// 放大函数
function zoomIn() {
if (scale.value < 4) {
scale.value += 0.1;
}
}
// 缩小函数
function zoomOut() {
if (scale.value > 0.5) {
scale.value -= 0.1;
}
}
// 旋转
const rotationAngle = ref(0);
// 顺时针旋转函数
function rotateClockwise() {
rotationAngle.value += 90; // 每次旋转90度
}
// 逆时针旋转
function rotateCounterClockwise() {
rotationAngle.value -= 90; // 每次旋转-90度
}
// 宽高
const imageWidth = ref(1040);
const imageHeight = ref(800);
// 设置高度和宽度
function getImageWidthAndHeight() {
if (
props.nowPreviewRecord.width &&
props.nowPreviewRecord.height &&
props.nowPreviewRecord.width > 1040 &&
props.nowPreviewRecord.height > 800
) {
imageWidth.value = 1040;
imageHeight.value = (1040 / props.nowPreviewRecord.width) * props.nowPreviewRecord.height;
} else {
imageHeight.value = 800;
imageWidth.value = 1040;
}
}
onMounted(() => {
getImageWidthAndHeight();
});
// 100%比例
function setProportion() {
if (
props.nowPreviewRecord.width &&
props.nowPreviewRecord.height &&
props.nowPreviewRecord.width > 1040 &&
props.nowPreviewRecord.height > 800
) {
scale.value = props.nowPreviewRecord.width / 1040;
// imageWidth.value = props.nowPreviewRecord.width;
// imageHeight.value = props.nowPreviewRecord.height;
} else {
scale.value = 2;
// imageWidth.value = 2080;
// imageHeight.value = 1600;
}
}
// 刷新
function refresh() {
scale.value = 1;
rotationAngle.value = 0;
// graffitiFlag.value = false;
getImageWidthAndHeight();
// 复位
const dragDocument: any = document.querySelector('.dragModal');
if (dragDocument) {
dragDocument.style.left = 0 + 'px';
dragDocument.style.top = 0 + 'px';
}
document.body.style.cursor = 'default';
}
// 删除
function deleteImage() {
let content = '确定要删除选择的文件吗?';
if (props.previewRecordList.length == 1) {
content = '下列图片只有一个,删除此图片后将会关闭此弹窗,确定要删除选择的文件吗?';
}
createConfirm({
iconType: 'info',
title: '删除',
content: content,
onOk: async () => {
let ids = props.nowPreviewRecord.id;
Deletepic({
ids: ids,
}).then((res) => {
handleSuccessPreview(props.nowPreviewRecord);
createMessage.success('删除成功');
});
},
});
}
// 复制到剪贴板
const copyToClipboard = async (url) => {
try {
await navigator.clipboard.writeText(VITE_GLOB_MEDIALIBRARY_IMAGE_URL + url);
createMessage.success('图片链接已复制到剪贴板');
} catch (err) {
createMessage.error('无法复制图片链接');
}
};
// 下载
async function fetchAndDownloadImage(url) {
try {
const response = await fetch(VITE_GLOB_MEDIALIBRARY_IMAGE_URL + 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 || 'download';
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;
}
// 隐藏标签
const hideOrShowTextboxFlag = ref(true);
function hideOrShowTextbox(value) {
hideOrShowTextboxFlag.value = value;
emits('setHideOrShowTextboxFlag', value);
}
// 设置画笔---------------------------------------------------------------
const graffitiFlag = ref(false);
const graffitiColor = ref('#E23C39');
const nowGraffiti = ref(-1);
const nowMouseGraffiti = ref(0);
// 设置鼠标格式
function setGraffiti() {
refresh();
graffitiFlag.value = !graffitiFlag.value;
if (graffitiFlag.value) {
document.body.style.cursor = 'crosshair';
} else {
document.body.style.cursor = 'pointer';
}
}
// 临时选框状态
const isDragging = ref(false);
let startX = 0;
let startY = 0;
let endX = 0;
let endY = 0;
// 拖动
let initialMouseX;
let initialMouseY;
let initialDocumentX;
let initialDocumentY;
// 所有已创建的矩形
const graffitis: any = ref([]);
const graffitisClone: any = ref([]);
const fileTags: any = ref([]);
watch(
() => props.nowPreviewRecord,
() => {
graffitis.value = props.nowPreviewRecord.graffitiJson
? props.nowPreviewRecord.graffitiJson
: [];
fileTags.value = props.nowPreviewRecord.fileTags ? props.nowPreviewRecord.fileTags : [];
// 调整滑动条的位置
setScrollLeft();
},
{
deep: true,
immediate: true,
},
);
onMounted(() => {
// 调整滑动条的位置
setScrollLeft();
});
// 调整滑动条的位置
function setScrollLeft() {
const imageChooseListDocument: any = document.querySelector('.imageChooseList');
if (imageChooseListDocument) {
let index = props.previewRecordList.findIndex((item) => item.id == props.nowPreviewRecord.id);
let length = props.previewRecordList.length;
if (index < 5) {
imageChooseListDocument.scrollLeft = 0;
} else if (index + 7 > length) {
imageChooseListDocument.scrollLeft = 80 * length;
} else {
imageChooseListDocument.scrollLeft = 80 * (index - 5);
}
}
}
// 添加标签
function addGraffiti() {
graffitis.value.forEach((item) => {
if (item.text && !fileTags.value.includes(item.text)) {
if (item.text) {
fileTags.value.push(item.text);
}
}
});
UpdatePicStatus({
id: props.nowPreviewRecord.id,
fileTags: JSON.stringify(fileTags.value),
graffitiJson: JSON.stringify(graffitis.value),
display: props.nowPreviewRecord.display0,
showOnMap: props.nowPreviewRecord.showOnMap,
}).then((res) => {
emits('handleSuccessPreview');
});
}
// 删除标签
function deleteGraffiti(index = undefined, value = undefined) {
graffitis.value.splice(index, 1);
// 刷新or删除标签
if (!graffitis.value.some((item) => item.text == value)) {
fileTags.value = fileTags.value.filter((la) => la != value);
}
UpdatePicStatus({
id: props.nowPreviewRecord.id,
fileTags: JSON.stringify(fileTags.value),
graffitiJson: JSON.stringify(graffitis.value),
display: props.nowPreviewRecord.display,
showOnMap: props.nowPreviewRecord.showOnMap,
}).then((res) => {
emits('handleSuccessPreview');
});
}
const mouseCanvasRef = ref();
// 鼠标按下
function onMouseDown(event) {
if (!graffitiFlag.value) {
// 拖动
const dragDocument: any = document.querySelector('.dragModal');
isDragging.value = true;
initialMouseX = event.clientX;
initialMouseY = event.clientY;
initialDocumentX = dragDocument.offsetLeft;
initialDocumentY = dragDocument.offsetTop;
dragDocument.style.cursor = 'grabbing';
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
} else if (
graffitiFlag.value &&
(graffitis.value.length == 0 || !graffitis.value?.some((item) => item.status != 'success'))
) {
// 涂鸦
if (nowGraffiti.value != -1) return;
if (graffitis.value.findIndex((item) => item.status == 'mouse') != -1) return;
// 获取相对于容器的坐标
const rect = mouseCanvasRef.value.getBoundingClientRect();
startX = event.x - rect.x;
startY = event.y - rect.y;
isDragging.value = true;
// 添加矩形到数组中
graffitis.value.push({
x: startX,
y: startY,
width: 0,
height: 0,
color: graffitiColor.value,
text: '',
status: 'edit',
});
graffitisClone.value = cloneDeep(graffitis.value);
window.addEventListener('mousemove', onMouseMoveGraffit);
window.addEventListener('mouseup', onMouseUpGraffit);
}
}
// 鼠标移动-涂鸦
function onMouseMoveGraffit(event) {
// 涂鸦
const rect = mouseCanvasRef.value.getBoundingClientRect();
if (!isDragging.value) return;
endX = event.x - rect.x;
endY = event.y - rect.y;
if (endX < 0 && endY < 0) {
endX = startX;
endY = startY;
}
setMouseData();
}
// 鼠标松开-涂鸦
function onMouseUpGraffit(event) {
// 涂鸦
if (!isDragging.value) return;
const rect = mouseCanvasRef.value.getBoundingClientRect();
endX = event.x - rect.x;
endY = event.y - rect.y;
if (endX < 0 && endY < 0) {
endX = startX;
endY = startY;
}
isDragging.value = false;
if (event.x > rect.right || event.y > rect.bottom) {
graffitis.value.splice(graffitis.value.length - 1, 1);
nowGraffiti.value = -1;
} else {
setMouseData();
nowGraffiti.value = graffitis.value.length - 1;
}
window.removeEventListener('mousemove', onMouseMoveGraffit);
window.removeEventListener('mouseup', onMouseUpGraffit);
}
// 鼠标移动-拖动
function onMouseMove(event) {
// 拖动
const dragDocument: any = document.querySelector('.dragModal');
if (isDragging.value) {
const deltaX = event.clientX - initialMouseX;
const deltaY = event.clientY - initialMouseY;
dragDocument.style.left = initialDocumentX + deltaX + 'px';
dragDocument.style.top = initialDocumentY + deltaY + 'px';
}
}
// 鼠标松开-拖动
function onMouseUp(event) {
// 拖动
const dragDocument: any = document.querySelector('.dragModal');
isDragging.value = false;
if (dragDocument) {
dragDocument.style.cursor = 'default';
}
}
// 鼠标滚轮
function onWheel(event) {
if (!graffitiFlag.value) {
const delta = event.deltaY || event.detail || event.wheelDelta;
// 根据滚轮方向调整缩放比例
if (delta < 0) {
// 向上滚动,放大
scale.value += 0.1;
} else if (scale.value > 0.5) {
// 向下滚动,缩小
scale.value -= 0.1;
}
}
}
// 设置鼠标涂鸦绘画出的数据
function setMouseData() {
graffitis.value[graffitis.value.length - 1].x = Math.min(startX, endX);
graffitis.value[graffitis.value.length - 1].width = Math.abs(endX - startX);
graffitis.value[graffitis.value.length - 1].y = Math.min(startY, endY);
graffitis.value[graffitis.value.length - 1].height = Math.abs(endY - startY);
}
//------------------------------------------------------------------------
// 鼠标聚焦
function mouseenter(rect, type) {
if (graffitiFlag.value) {
// 鼠标聚焦状态
if (rect.status != 'edit') {
rect.status = 'mouse';
document.body.style.cursor = 'pointer';
}
// 编辑状态
if (rect.status == 'edit') {
if (type == 'top' || type == 'bottom') {
document.body.style.cursor = 'ns-resize';
}
if (type == 'left' || type == 'right') {
document.body.style.cursor = 'ew-resize';
}
if (type == 'leftTop' || type == 'rightBottom') {
document.body.style.cursor = 'nwse-resize';
}
if (type == 'leftBottom' || type == 'rightTop') {
document.body.style.cursor = 'nesw-resize';
}
}
}
}
// 鼠标离开
function mouseleave(rect) {
if (graffitiFlag.value) {
if (rect.status == 'mouse' || rect.status == 'edit') {
if (rect.status == 'mouse') {
rect.status = 'success';
}
if (rect.status == 'edit') {
document.body.style.cursor = 'crosshair';
}
}
}
}
// 编辑状态下的鼠标按下
const mouseEditType = ref('');
function funMouseDownEdit(e, index, type) {
if (graffitiFlag.value) {
// 获取相对于容器的坐标
graffitisClone.value = cloneDeep(graffitis.value);
const rect = mouseCanvasRef.value.getBoundingClientRect();
startX = e.x - rect.x;
startY = e.y - rect.y;
isDragging.value = true;
nowGraffiti.value = index;
mouseEditType.value = type;
window.addEventListener('mousemove', funMouseMoveEdit);
window.addEventListener('mouseup', funMouseUpEdit);
}
}
// 编辑状态下的鼠标移动
function funMouseMoveEdit(e) {
if (graffitiFlag.value) {
const rect = mouseCanvasRef.value.getBoundingClientRect();
if (!isDragging.value) return;
endX = e.x - rect.x;
endY = e.y - rect.y;
if (endX < 0 && endY < 0) {
endX = startX;
endY = startY;
}
funSetMouseDataEdit();
}
}
// 编辑状态下的鼠标松开
function funMouseUpEdit(e) {
if (graffitiFlag.value) {
if (!isDragging.value) return;
const rect = mouseCanvasRef.value.getBoundingClientRect();
endX = e.x - rect.x;
endY = e.y - rect.y;
if (endX < 0 && endY < 0) {
endX = startX;
endY = startY;
}
isDragging.value = false;
if (e.x > rect.right || e.y > rect.bottom) {
graffitis.value[nowGraffiti.value] = graffitisClone.value[nowGraffiti.value];
} else {
funSetMouseDataEdit();
}
window.removeEventListener('mousemove', funMouseMoveEdit);
window.removeEventListener('mouseup', funMouseUpEdit);
}
}
// 编辑状态下的数据
function funSetMouseDataEdit() {
if (['top', 'leftTop', 'rightTop'].includes(mouseEditType.value)) {
graffitis.value[nowGraffiti.value].height =
graffitis.value[nowGraffiti.value].height + graffitis.value[nowGraffiti.value].y - endY;
graffitis.value[nowGraffiti.value].y = endY;
}
if (['bottom', 'leftBottom', 'rightBottom'].includes(mouseEditType.value)) {
graffitis.value[nowGraffiti.value].height = endY - graffitis.value[nowGraffiti.value].y;
}
if (['left', 'leftTop', 'leftBottom'].includes(mouseEditType.value)) {
graffitis.value[nowGraffiti.value].width =
graffitis.value[nowGraffiti.value].width + graffitis.value[nowGraffiti.value].x - endX;
graffitis.value[nowGraffiti.value].x = endX;
}
if (['right', 'rightTop', 'rightBottom'].includes(mouseEditType.value)) {
graffitis.value[nowGraffiti.value].width = endX - graffitis.value[nowGraffiti.value].x;
}
}
// 添加键盘事件监听器
const addEventListener = () => {
window.addEventListener('keydown', handleKeydown);
};
// 移除键盘事件监听器
const removeEventListener = () => {
window.removeEventListener('keydown', handleKeydown);
};
// 处理按键按下事件
const handleKeydown = (event: KeyboardEvent) => {
if (event.code === 'Escape' && graffitiFlag.value) {
setGraffiti();
}
};
// 全屏
const fullscreenFlag = ref(false);
function clickExpandButton() {
const mapContainer = document.getElementById('imageDiv');
if (mapContainer) {
if (!document.fullscreenElement) {
mapContainer.requestFullscreen();
fullscreenFlag.value = true;
} else {
document.exitFullscreen();
fullscreenFlag.value = false;
}
}
}
// 在组件挂载时添加监听器
onMounted(() => {
addEventListener();
});
// 在组件卸载时移除监听器
onBeforeUnmount(() => {
removeEventListener();
});
</script>
<style lang="less" scoped>
.previewImage {
position: relative;
width: 100%;
height: 100%;
display: block;
}
.imageDiv {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 89%;
background: #101010;
}
.bottomDiv {
// position: absolute;
// bottom: 0px;
// left: 0px;
position: relative;
width: 100%;
height: 10%;
background: #1c1c1c;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
.buttonList {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 35px;
.button {
position: relative;
padding-left: 10px;
padding-right: 10px;
margin-left: 6px;
margin-right: 6px;
color: #ffffff;
font-size: 22px;
}
.button2 {
padding-top: 3px;
padding-left: 10px;
padding-right: 10px;
margin-left: 6px;
margin-right: 6px;
display: flex;
align-items: center;
justify-content: center;
}
}
.imageChooseList {
display: inline-flex;
align-items: center;
// justify-content: center;
width: 900px;
height: 65px;
overflow-x: auto;
white-space: nowrap;
position: relative;
.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;
}
}
.imageChooseList::-webkit-scrollbar {
display: block;
height: 2px;
}
.imageChooseList {
scrollbar-width: auto;
scrollbar-color: #888 #f1f1f100;
}
}
// 上一张、下一张
.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;
}
// 涂鸦颜色选择-提示
.graffitiButton {
background: #ffffff;
position: absolute;
right: 40px;
top: 5%;
z-index: 200;
width: 30px;
height: 30px;
border-radius: 3px;
display: flex;
align-items: 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 {
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
}
// 标签颜色选择
.showTextboxClass {
position: absolute;
right: 40px;
top: 25%;
width: 30px;
height: 70px;
display: block;
align-items: center;
justify-content: center;
.button {
border-radius: 3px;
position: relative;
color: #2d8cf0;
background: #ffffff;
font-size: 22px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
}
// 全屏
.expandButton {
position: absolute;
right: 30px;
bottom: 15%;
z-index: 500;
background: #ffffff;
border-radius: 5px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.escTip {
position: absolute;
bottom: 0px;
left: 0px;
width: 120px;
height: 40px;
background: #9c9c9c77;
border-radius: 5px;
z-index: 200;
display: flex;
align-items: center;
justify-content: flex-start;
.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>