|
|
@@ -1,30 +1,57 @@
|
|
|
<template>
|
|
|
- <div id="drawContainer">
|
|
|
+ <div>
|
|
|
<v-stage :config="stageConfig" @click="handleStageClick">
|
|
|
- <v-layer>
|
|
|
- <v-image :config="bgConfig" v-if="props.bgImgUrl" />
|
|
|
+ <v-layer ref="layer">
|
|
|
+ <v-image :config="bgConfig" v-show="props.bgImgUrl" />
|
|
|
<v-group
|
|
|
v-for="camera in cameras"
|
|
|
:key="camera.id"
|
|
|
+ :id="camera.id"
|
|
|
:attrs="camera"
|
|
|
- @dblclick="() => handleCameraDblClick(camera)"
|
|
|
+ :config="groupConfig"
|
|
|
+ @click="(e) => handleCameraClick(camera, e)"
|
|
|
+ @mouseover="(e) => handleMouseOver(camera, e)"
|
|
|
+ @mouseleave="(e) => handleMouseLeave(camera, e)"
|
|
|
+ @dragstart="(e) => handleDragStart(camera, e)"
|
|
|
>
|
|
|
- <v-image :config="cameraConfig" @dragend="(e) => handleCameraDragEnd(camera, e)" />
|
|
|
+ <v-image :config="camera.config" @dragend="(e) => handleCameraDragEnd(camera, e)" />
|
|
|
+ <!-- <v-image :config="defaultIconConfig2" /> -->
|
|
|
+ <v-image v-if="camera.isDefault" ref="defaultIcon" :config="defaultIconConfig" />
|
|
|
</v-group>
|
|
|
- <v-image :config="defaultIconConfig" v-if="defaultIconVisible" />
|
|
|
+ <v-image ref="defaultIcon" :config="defaultIconConfig" />
|
|
|
+ <v-transformer ref="transformer" />
|
|
|
</v-layer>
|
|
|
</v-stage>
|
|
|
+ <!-- <OptBar :disabled="defaultShow" /> -->
|
|
|
+ <div
|
|
|
+ @click="setDefaultCamera"
|
|
|
+ v-show="defaultShow"
|
|
|
+ class="opt-container"
|
|
|
+ :style="{ position: 'absolute', left: posX + 'px', top: posY + 'px' }"
|
|
|
+ ><div class="opt-item">设为默认相机</div></div
|
|
|
+ >
|
|
|
+ <DefaultTip
|
|
|
+ v-show="tipShow"
|
|
|
+ :position="pos"
|
|
|
+ :style="{ position: 'absolute', left: posTipX + 'px', top: posTipY + 'px' }"
|
|
|
+ />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { ref, onMounted, onBeforeUnmount } from 'vue';
|
|
|
+ import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
|
|
import Konva from 'konva';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
+ import OptBar from '../components/CameraOptBar.vue';
|
|
|
import { useGlobSetting } from '@/hooks/setting';
|
|
|
+ import DefaultTip from '../components/DefaultTip.vue';
|
|
|
import urlJoin from 'url-join';
|
|
|
import cameraImgSrc from '@/assets/camera/cameraImg.png';
|
|
|
+ // import cameraImg from '@/assets/camera/cameraImg.png';
|
|
|
import favoritesImgSrc from '@/assets/camera/favorites.png';
|
|
|
+ import { TipPositionEnum } from '../type';
|
|
|
+ // import { nextTick } from 'process';
|
|
|
+ // import { watch } from 'fs';
|
|
|
|
|
|
const props = defineProps<{
|
|
|
bgImgUrl?: string;
|
|
|
@@ -33,28 +60,59 @@
|
|
|
|
|
|
const globSetting = useGlobSetting();
|
|
|
|
|
|
- interface caremasType {
|
|
|
+ interface caremasImgType {
|
|
|
+ width: number;
|
|
|
+ height: number;
|
|
|
+ image: HTMLImageElement;
|
|
|
+ name: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ interface caremasGroupType {
|
|
|
id?: string;
|
|
|
+ config: caremasImgType;
|
|
|
+ isDefault?: boolean;
|
|
|
}
|
|
|
|
|
|
- const stageConfig = {
|
|
|
+ // const stageWidth = ref<number>(800);
|
|
|
+
|
|
|
+ // const stageHeight = ref<number>(600);
|
|
|
+
|
|
|
+ const stageConfig = ref({
|
|
|
width: 800,
|
|
|
height: 600,
|
|
|
- };
|
|
|
+ fill: 'red',
|
|
|
+ // background: red,
|
|
|
+ });
|
|
|
|
|
|
const cameraConfig = {};
|
|
|
|
|
|
const bgImg = new Image();
|
|
|
const cameraImg = new Image();
|
|
|
const favoritesImg = new Image();
|
|
|
- // bgImg.src = '';
|
|
|
- cameraImg.src = cameraImgSrc;
|
|
|
favoritesImg.src = favoritesImgSrc;
|
|
|
|
|
|
+ //右键点击是否出现
|
|
|
+ const defaultShow = ref<boolean>(false);
|
|
|
+ const posX = ref<number>();
|
|
|
+ const posY = ref<number>();
|
|
|
+ const posTipX = ref<number>();
|
|
|
+ const posTipY = ref<number>();
|
|
|
+ const pos = ref(TipPositionEnum.TOP);
|
|
|
+ //可能的默认相机
|
|
|
+ const caremaIdMay = ref('');
|
|
|
+ const lastClickedGroupId = ref<string | null>(null);
|
|
|
+
|
|
|
const bgImgUrl = ref('');
|
|
|
- const cameras = ref<caremasType[]>([]);
|
|
|
+ const cameras = ref<caremasGroupType[]>([]);
|
|
|
+ //默认相机id
|
|
|
const defaultCameraId = ref('');
|
|
|
const defaultIconVisible = ref(false);
|
|
|
+ const tipShow = ref(false);
|
|
|
+
|
|
|
+ //标签
|
|
|
+ const layer = ref();
|
|
|
+ const transformer = ref();
|
|
|
+ const defaultIcon = ref();
|
|
|
|
|
|
const bgConfig = ref({
|
|
|
x: 0,
|
|
|
@@ -63,31 +121,152 @@
|
|
|
width: bgImg.width,
|
|
|
height: bgImg.height,
|
|
|
image: bgImg,
|
|
|
+ name: 'bgImg',
|
|
|
+ backgroundSize: 'cover',
|
|
|
+ });
|
|
|
+
|
|
|
+ // watch(
|
|
|
+ // () => props.bgImgUrl,
|
|
|
+ // (newValue) => {
|
|
|
+ // console.log('22', newValue);
|
|
|
+ // const stage = transformer.value.getNode().getStage();
|
|
|
+ // const bg = stage.findOne('#bgImg');
|
|
|
+ // // attrs.height;
|
|
|
+ // console.log('bg', bg);
|
|
|
+ // nextTick(() => {
|
|
|
+ // console.log('height', bg.attrs.height);
|
|
|
+ // console.log('width', bg.attrs.width);
|
|
|
+ // });
|
|
|
+ // },
|
|
|
+ // // { immediate: true },
|
|
|
+ // );
|
|
|
+ const resizeContainer = (width, height) => {
|
|
|
+ // const newWidth = width > initWidth ? width : initWidth;
|
|
|
+ // const newHeight = height > initHeight ? height : initHeight;
|
|
|
+ // stage?.width(newWidth);
|
|
|
+ // stage?.height(newHeight);
|
|
|
+ // console.log('width', width);
|
|
|
+ // console.log('height', height);
|
|
|
+ // stageWidth.value = width;
|
|
|
+ // stageHeight.value = height;
|
|
|
+ stageConfig.value.width = width;
|
|
|
+ stageConfig.value.height = height;
|
|
|
+ };
|
|
|
+
|
|
|
+ const groupConfig = ref({
|
|
|
+ x: 50,
|
|
|
+ y: 50,
|
|
|
+ draggable: true,
|
|
|
+ name: 'group',
|
|
|
});
|
|
|
|
|
|
- const defaultIconConfig = {
|
|
|
+ const defaultIconConfig = ref({
|
|
|
x: 18,
|
|
|
y: -16,
|
|
|
width: 16,
|
|
|
height: 16,
|
|
|
- visible: defaultIconVisible.value,
|
|
|
+ id: 'defaultIcon',
|
|
|
+ image: favoritesImg,
|
|
|
+ // visible: defaultIconVisible.value,
|
|
|
+ });
|
|
|
+
|
|
|
+ // const setDefaultCamera = () => {
|
|
|
+ // defaultShow.value = false;
|
|
|
+ // };
|
|
|
+
|
|
|
+ const handleMouseOver = (camera, e) => {
|
|
|
+ document.oncontextmenu = () => {
|
|
|
+ return false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const group = e.target.parent;
|
|
|
+ if (group.id() === defaultCameraId.value) {
|
|
|
+ tipShow.value = true;
|
|
|
+ const stage = transformer.value.getNode().getStage();
|
|
|
+ const defaultNode = stage.findOne('#defaultIcon');
|
|
|
+ const tipPosition = defaultNode.absolutePosition();
|
|
|
+
|
|
|
+ posTipX.value = Number(tipPosition?.x.toFixed(2)) || 0;
|
|
|
+ posTipY.value = Number(tipPosition?.y.toFixed(2)) || 0;
|
|
|
+ const angle = group.rotation() >= 0 ? group.rotation() : group.rotation() + 360;
|
|
|
+ if (angle >= 30) {
|
|
|
+ if (angle <= 150) {
|
|
|
+ pos.value = TipPositionEnum.RIGHT;
|
|
|
+ posTipX.value += 26;
|
|
|
+ posTipY.value -= 17;
|
|
|
+ } else if (angle <= 210) {
|
|
|
+ pos.value = TipPositionEnum.BOTTOM;
|
|
|
+ posTipY.value += 26;
|
|
|
+ posTipX.value -= 50;
|
|
|
+ } else {
|
|
|
+ pos.value = TipPositionEnum.LEFT;
|
|
|
+ posTipX.value -= 121;
|
|
|
+ posTipY.value -= 25;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ posTipY.value -= 61;
|
|
|
+ posTipX.value -= 43;
|
|
|
+ }
|
|
|
+ // createDefaultTip(x, y, pos);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const handleMouseLeave = (camera, e) => {
|
|
|
+ document.oncontextmenu = () => {
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+ tipShow.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleDragStart = (camera, e) => {
|
|
|
+ // document.oncontextmenu = () => {
|
|
|
+ // return true;
|
|
|
+ // };
|
|
|
+ tipShow.value = false;
|
|
|
};
|
|
|
|
|
|
const handleStageClick = (e: any) => {
|
|
|
- console.log('e', e);
|
|
|
+ console.log('e', e.target);
|
|
|
+ console.log('e', e.target.parent);
|
|
|
+ defaultShow.value = false;
|
|
|
+ // const clickedNode = e.target;
|
|
|
+ document.oncontextmenu = () => {
|
|
|
+ return false;
|
|
|
+ };
|
|
|
+ const parent = e.target.parent;
|
|
|
+ if (parent.hasName('group')) {
|
|
|
+ lastClickedGroupId.value = parent.id();
|
|
|
+ console.log('group');
|
|
|
+ } else {
|
|
|
+ console.log('stage');
|
|
|
+ //取消transformer选择
|
|
|
+ const transformerNode = transformer.value.getNode();
|
|
|
+ transformerNode.nodes([]);
|
|
|
+ }
|
|
|
|
|
|
- // if (e.target === stage) {
|
|
|
- // stage.find('Transformer').destroy();
|
|
|
- // layer.batchDraw();
|
|
|
- // }
|
|
|
+ // 判断是否为右键点击
|
|
|
+ if (e.evt.button === 2) {
|
|
|
+ console.log('右击');
|
|
|
+ console.log(parent.id());
|
|
|
+ // groupSelect =
|
|
|
+ caremaIdMay.value = parent.id();
|
|
|
+ posX.value = e.evt.offsetX + 20;
|
|
|
+ posY.value = e.evt.offsetY;
|
|
|
+ defaultShow.value = defaultCameraId.value !== parent.id();
|
|
|
+ console.log(defaultShow.value);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const handleCameraDblClick = (camera: any) => {
|
|
|
- const index = cameras.value.findIndex((c) => c.id === camera.id);
|
|
|
- if (index === -1) return;
|
|
|
- const updatedCameras = [...cameras.value];
|
|
|
- updatedCameras.splice(index, 1);
|
|
|
- cameras.value = updatedCameras;
|
|
|
+ const handleCameraClick = (camera: any, event) => {
|
|
|
+ tipShow.value = false;
|
|
|
+ const transformerNode = transformer.value.getNode();
|
|
|
+ const stage = transformerNode.getStage();
|
|
|
+ const cameraNode = stage.findOne('#' + camera.id);
|
|
|
+ console.log('cameraNode', cameraNode);
|
|
|
+
|
|
|
+ // 将变换器附加到点击的相机
|
|
|
+ transformerNode.nodes([cameraNode]);
|
|
|
+ transformerNode.moveToTop();
|
|
|
+ // camera.transformer = transformerNode;
|
|
|
};
|
|
|
|
|
|
const deleteCamera = () => {
|
|
|
@@ -98,22 +277,50 @@
|
|
|
cameras.value = updatedCameras;
|
|
|
};
|
|
|
|
|
|
- const addCamera = (x: number, y: number) => {
|
|
|
- const id = Math.random().toString(36).substr(2, 9);
|
|
|
- cameras.value.push({ id, x, y, rotation: 0, scaleX: 1, scaleY: 1 });
|
|
|
+ const addCamera = (id: string) => {
|
|
|
+ const existingCamera = cameras.value.find((camera) => camera.id === id);
|
|
|
+ if (existingCamera) return;
|
|
|
+
|
|
|
+ const camImg = new Image();
|
|
|
+ camImg.src = cameraImgSrc;
|
|
|
+ const config = {
|
|
|
+ width: 52,
|
|
|
+ height: 37,
|
|
|
+ image: camImg,
|
|
|
+ name: 'image',
|
|
|
+ };
|
|
|
+ const cameraDetail = {
|
|
|
+ id,
|
|
|
+ config,
|
|
|
+ isDefault: false,
|
|
|
+ };
|
|
|
+ cameras.value.push(cameraDetail);
|
|
|
+ //可以加入判断,当只有一个相机时,自动设置默认相机
|
|
|
+ if (cameras.value.length === 1) {
|
|
|
+ cameras.value[0].isDefault = true;
|
|
|
+ defaultCameraId.value = id;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const setDefaultCamera = (camera: any) => {
|
|
|
- defaultCameraId.value = camera.id;
|
|
|
- defaultIconVisible.value = true;
|
|
|
+ const setDefaultCamera = () => {
|
|
|
+ //选中的相机id号
|
|
|
+ defaultCameraId.value = caremaIdMay.value;
|
|
|
+ cameras.value.forEach((item) => {
|
|
|
+ if (item.id === defaultCameraId.value) {
|
|
|
+ item.isDefault = true;
|
|
|
+ } else {
|
|
|
+ item.isDefault = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ defaultShow.value = false;
|
|
|
};
|
|
|
|
|
|
const handleCameraDragEnd = (camera: any, e: any) => {
|
|
|
- if (camera.id === defaultCameraId.value) {
|
|
|
- const tr = layer.findOne(`#tr_${camera.id}`);
|
|
|
- tr.nodes([e.target]);
|
|
|
- layer.batchDraw();
|
|
|
- }
|
|
|
+ // if (camera.id === defaultCameraId.value) {
|
|
|
+ // const tr = layer.findOne(`#tr_${camera.id}`);
|
|
|
+ // tr.nodes([e.target]);
|
|
|
+ // layer.batchDraw();
|
|
|
+ // }
|
|
|
};
|
|
|
|
|
|
const handleFileChange = (e: any) => {
|
|
|
@@ -122,22 +329,28 @@
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = (event) => {
|
|
|
bgImgUrl.value = event.target?.result;
|
|
|
- addBg();
|
|
|
+ // addBg();
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
};
|
|
|
|
|
|
- const addBg = () => {
|
|
|
- // bgImgUrl.value = bgImg;
|
|
|
- bgImgUrl.value = urlJoin(globSetting.imgUrl!, props.bgImgUrl!);
|
|
|
- console.log('bgImgUrl.value22 ', bgImgUrl.value);
|
|
|
- // console.log('imgUrl', imgUrl);
|
|
|
+ const addBg = (imgBg) => {
|
|
|
+ bgImgUrl.value = urlJoin(globSetting.imgUrl!, imgBg) as string;
|
|
|
+
|
|
|
bgImg.src = bgImgUrl.value;
|
|
|
- // const img = new Image();
|
|
|
+ console.log('bgmin', bgImg);
|
|
|
+
|
|
|
bgImg.onload = () => {
|
|
|
+ // const bgNode = layer.value?.findOne('#bgImg') as any;
|
|
|
bgConfig.value.width = bgImg.width;
|
|
|
bgConfig.value.height = bgImg.height;
|
|
|
- // bgImg.src = bgImgUrl.value;
|
|
|
+ // stageConfig.value.width = bgImg.width;
|
|
|
+ // stageConfig.value.height = bgImg.height;
|
|
|
+ resizeContainer(bgImg.width, bgImg.height);
|
|
|
+ console.log('bgImg.width', bgImg.width);
|
|
|
+ console.log('bgImg.height', bgImg.height);
|
|
|
+ console.log('stageConfig', stageConfig.value);
|
|
|
+ console.log('bgConfig', bgConfig.value);
|
|
|
};
|
|
|
};
|
|
|
|
|
|
@@ -157,13 +370,28 @@
|
|
|
};
|
|
|
const handleKeyDown = () => {};
|
|
|
|
|
|
+ //重置
|
|
|
+ const resetMap = () => {
|
|
|
+ bgImgUrl.value = '';
|
|
|
+ // defaultIcon?.moveTo(copyLayer);
|
|
|
+ // layer?.clear();
|
|
|
+ // layer?.removeChildren();
|
|
|
+ // defaultIcon?.moveTo(layer);
|
|
|
+ // addedCameras.value = [];
|
|
|
+ // activeGroup.value = null;
|
|
|
+ // defaultCameraId.value = '';
|
|
|
+ // isTransform = false;
|
|
|
+ // bgImgUrl.value = '';
|
|
|
+ };
|
|
|
+
|
|
|
/** 导入布局json */
|
|
|
const createMap = (layout) => {
|
|
|
- // const layout = JSON.parse(json);
|
|
|
- bgImgUrl.value = layout.bgInfo.bgImg;
|
|
|
- console.log('bgImgUrl.value11 ', bgImgUrl.value);
|
|
|
+ // bgImgUrl.value = layout.bgInfo.bgImg;
|
|
|
+
|
|
|
+ // console.log('bgImgUrl.value11 ', bgImgUrl.value);
|
|
|
+ console.log('layout.bgInfo.bgImg', layout.bgInfo.bgImg);
|
|
|
|
|
|
- addBg();
|
|
|
+ addBg(layout.bgInfo.bgImg);
|
|
|
// layout.cameraList.forEach((camera) => {
|
|
|
// const group = new Konva.Group({
|
|
|
// x: camera.x,
|
|
|
@@ -201,7 +429,7 @@
|
|
|
// });
|
|
|
};
|
|
|
|
|
|
- defineExpose({ addBg, createMap });
|
|
|
+ defineExpose({ addBg, createMap, resetMap, addCamera, resizeContainer });
|
|
|
|
|
|
onMounted(() => {
|
|
|
// Add initial cameras, if needed
|
|
|
@@ -213,19 +441,29 @@
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
- #drawContainer {
|
|
|
+ .drawContainer {
|
|
|
position: relative;
|
|
|
}
|
|
|
-
|
|
|
- input[type='file'] {
|
|
|
- position: absolute;
|
|
|
- top: 10px;
|
|
|
- left: 10px;
|
|
|
+ .opt-container {
|
|
|
+ width: 160px;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ background-color: #ffffff;
|
|
|
+ box-shadow: 5px 5px 5px #a3a5a5;
|
|
|
}
|
|
|
+ .opt-item {
|
|
|
+ height: 30px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #404040;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: center;
|
|
|
+ padding-left: 8px;
|
|
|
+ border-radius: 3px;
|
|
|
+ cursor: pointer;
|
|
|
|
|
|
- button {
|
|
|
- position: absolute;
|
|
|
- top: 40px;
|
|
|
- left: 10px;
|
|
|
+ &:hover {
|
|
|
+ background-color: #f1f2f5;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|