|
|
@@ -1,25 +1,25 @@
|
|
|
<template>
|
|
|
<div>
|
|
|
- <v-stage :config="stageConfig" @click="handleStageClick">
|
|
|
+ <v-stage ref="stageAll" :config="stageConfig" @click="handleStageClick">
|
|
|
<v-layer ref="layer">
|
|
|
- <v-image :config="bgConfig" v-show="props.bgImgUrl" />
|
|
|
+ <v-image :config="bgConfig" v-show="bgImgUrl" />
|
|
|
<v-group
|
|
|
v-for="camera in cameras"
|
|
|
:key="camera.id"
|
|
|
:id="camera.id"
|
|
|
:attrs="camera"
|
|
|
- :config="groupConfig"
|
|
|
- @click="(e) => handleCameraClick(camera, e)"
|
|
|
- @mouseover="(e) => handleMouseOver(camera, e)"
|
|
|
- @mouseleave="(e) => handleMouseLeave(camera, e)"
|
|
|
- @dragstart="(e) => handleDragStart(camera, e)"
|
|
|
+ :config="camera.groupConfig"
|
|
|
+ @click="handleCameraClick(camera)"
|
|
|
+ @mouseover="(e) => handleMouseOver(e)"
|
|
|
+ @mouseleave="handleMouseLeave()"
|
|
|
+ @dragstart="handleDragStart()"
|
|
|
>
|
|
|
- <v-image :config="camera.config" @dragend="(e) => handleCameraDragEnd(camera, e)" />
|
|
|
+ <v-image :config="camera.config" />
|
|
|
<!-- <v-image :config="defaultIconConfig2" /> -->
|
|
|
<v-image v-if="camera.isDefault" ref="defaultIcon" :config="defaultIconConfig" />
|
|
|
</v-group>
|
|
|
- <v-image ref="defaultIcon" :config="defaultIconConfig" />
|
|
|
- <v-transformer ref="transformer" />
|
|
|
+ <!-- <v-image ref="defaultIcon" :config="defaultIconConfig" /> -->
|
|
|
+ <v-transformer :config="transformerConfig" ref="transformer" />
|
|
|
</v-layer>
|
|
|
</v-stage>
|
|
|
<!-- <OptBar :disabled="defaultShow" /> -->
|
|
|
@@ -39,55 +39,46 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
|
|
- import Konva from 'konva';
|
|
|
+ import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
|
|
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;
|
|
|
- bar?: number;
|
|
|
- }>();
|
|
|
|
|
|
const globSetting = useGlobSetting();
|
|
|
|
|
|
+ const emit = defineEmits(['changeDefaultCarema', 'delete']);
|
|
|
+
|
|
|
interface caremasImgType {
|
|
|
- width: number;
|
|
|
- height: number;
|
|
|
- image: HTMLImageElement;
|
|
|
- name: string;
|
|
|
+ x?: number;
|
|
|
+ y?: number;
|
|
|
+ width?: number;
|
|
|
+ height?: number;
|
|
|
+ draggable?: boolean;
|
|
|
+ rotation?: number;
|
|
|
+ scaleX?: number;
|
|
|
+ scaleY?: number;
|
|
|
+ image?: HTMLImageElement;
|
|
|
+ name?: string;
|
|
|
+ url?: string;
|
|
|
}
|
|
|
|
|
|
interface caremasGroupType {
|
|
|
id?: string;
|
|
|
+ groupConfig: caremasImgType;
|
|
|
config: caremasImgType;
|
|
|
isDefault?: boolean;
|
|
|
}
|
|
|
|
|
|
- // 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();
|
|
|
favoritesImg.src = favoritesImgSrc;
|
|
|
|
|
|
@@ -98,15 +89,13 @@
|
|
|
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 bgImgUrl = ref<string | null>('');
|
|
|
const cameras = ref<caremasGroupType[]>([]);
|
|
|
//默认相机id
|
|
|
const defaultCameraId = ref('');
|
|
|
- const defaultIconVisible = ref(false);
|
|
|
const tipShow = ref(false);
|
|
|
|
|
|
//标签
|
|
|
@@ -125,41 +114,13 @@
|
|
|
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 transformerConfig = ref({ keepRatio: true, rotateAnchorOffset: 30 });
|
|
|
+
|
|
|
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 = ref({
|
|
|
x: 18,
|
|
|
y: -16,
|
|
|
@@ -167,14 +128,9 @@
|
|
|
height: 16,
|
|
|
id: 'defaultIcon',
|
|
|
image: favoritesImg,
|
|
|
- // visible: defaultIconVisible.value,
|
|
|
});
|
|
|
|
|
|
- // const setDefaultCamera = () => {
|
|
|
- // defaultShow.value = false;
|
|
|
- // };
|
|
|
-
|
|
|
- const handleMouseOver = (camera, e) => {
|
|
|
+ const handleMouseOver = (e) => {
|
|
|
document.oncontextmenu = () => {
|
|
|
return false;
|
|
|
};
|
|
|
@@ -210,77 +166,54 @@
|
|
|
// createDefaultTip(x, y, pos);
|
|
|
}
|
|
|
};
|
|
|
- const handleMouseLeave = (camera, e) => {
|
|
|
+ const handleMouseLeave = () => {
|
|
|
document.oncontextmenu = () => {
|
|
|
return true;
|
|
|
};
|
|
|
tipShow.value = false;
|
|
|
};
|
|
|
|
|
|
- const handleDragStart = (camera, e) => {
|
|
|
- // document.oncontextmenu = () => {
|
|
|
- // return true;
|
|
|
- // };
|
|
|
+ const handleDragStart = () => {
|
|
|
tipShow.value = false;
|
|
|
};
|
|
|
|
|
|
const handleStageClick = (e: any) => {
|
|
|
- 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');
|
|
|
+ // 判断是否为右键点击
|
|
|
+ if (e.evt.button === 2) {
|
|
|
+ lastClickedGroupId.value = parent.id();
|
|
|
+ posX.value = e.evt.offsetX + 20;
|
|
|
+ posY.value = e.evt.offsetY;
|
|
|
+ defaultShow.value = defaultCameraId.value !== parent.id();
|
|
|
+ }
|
|
|
} else {
|
|
|
- console.log('stage');
|
|
|
+ lastClickedGroupId.value = null;
|
|
|
//取消transformer选择
|
|
|
const transformerNode = transformer.value.getNode();
|
|
|
transformerNode.nodes([]);
|
|
|
}
|
|
|
-
|
|
|
- // 判断是否为右键点击
|
|
|
- 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 handleCameraClick = (camera: any, event) => {
|
|
|
+ const handleCameraClick = (camera) => {
|
|
|
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 = () => {
|
|
|
- const index = cameras.value.findIndex((camera) => camera.id === activeCameraId.value);
|
|
|
- if (index === -1) return;
|
|
|
- const updatedCameras = [...cameras.value];
|
|
|
- updatedCameras.splice(index, 1);
|
|
|
- cameras.value = updatedCameras;
|
|
|
};
|
|
|
|
|
|
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 = {
|
|
|
@@ -288,14 +221,23 @@
|
|
|
height: 37,
|
|
|
image: camImg,
|
|
|
name: 'image',
|
|
|
+ id: id,
|
|
|
+ };
|
|
|
+ const groupConfig = {
|
|
|
+ x: 50,
|
|
|
+ y: 50,
|
|
|
+ draggable: true,
|
|
|
+ name: 'group',
|
|
|
};
|
|
|
const cameraDetail = {
|
|
|
id,
|
|
|
+ groupConfig,
|
|
|
config,
|
|
|
isDefault: false,
|
|
|
};
|
|
|
cameras.value.push(cameraDetail);
|
|
|
- //可以加入判断,当只有一个相机时,自动设置默认相机
|
|
|
+
|
|
|
+ //当只有一个相机时,自动设置默认相机
|
|
|
if (cameras.value.length === 1) {
|
|
|
cameras.value[0].isDefault = true;
|
|
|
defaultCameraId.value = id;
|
|
|
@@ -304,7 +246,7 @@
|
|
|
|
|
|
const setDefaultCamera = () => {
|
|
|
//选中的相机id号
|
|
|
- defaultCameraId.value = caremaIdMay.value;
|
|
|
+ defaultCameraId.value = lastClickedGroupId.value!;
|
|
|
cameras.value.forEach((item) => {
|
|
|
if (item.id === defaultCameraId.value) {
|
|
|
item.isDefault = true;
|
|
|
@@ -315,124 +257,85 @@
|
|
|
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();
|
|
|
- // }
|
|
|
- };
|
|
|
-
|
|
|
- const handleFileChange = (e: any) => {
|
|
|
- const file = e.target.files[0];
|
|
|
- if (!file) return;
|
|
|
- const reader = new FileReader();
|
|
|
- reader.onload = (event) => {
|
|
|
- bgImgUrl.value = event.target?.result;
|
|
|
- // addBg();
|
|
|
- };
|
|
|
- reader.readAsDataURL(file);
|
|
|
- };
|
|
|
-
|
|
|
const addBg = (imgBg) => {
|
|
|
- bgImgUrl.value = urlJoin(globSetting.imgUrl!, imgBg) as string;
|
|
|
-
|
|
|
- bgImg.src = bgImgUrl.value;
|
|
|
- console.log('bgmin', bgImg);
|
|
|
-
|
|
|
+ bgImgUrl.value = imgBg;
|
|
|
+ bgImg.src = urlJoin(globSetting.imgUrl!, imgBg) as string;
|
|
|
bgImg.onload = () => {
|
|
|
- // const bgNode = layer.value?.findOne('#bgImg') as any;
|
|
|
bgConfig.value.width = bgImg.width;
|
|
|
bgConfig.value.height = bgImg.height;
|
|
|
- // 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);
|
|
|
};
|
|
|
};
|
|
|
|
|
|
const saveLayout = () => {
|
|
|
+ const stage = transformer.value.getNode().getStage();
|
|
|
+ const groups = stage.find('.group');
|
|
|
+ const camerasList = groups.map((item, index) => {
|
|
|
+ cameras.value[index].groupConfig.x = Math.round(item.attrs.x | 0);
|
|
|
+ cameras.value[index].groupConfig.y = Math.round(item.attrs.y | 0);
|
|
|
+ cameras.value[index].groupConfig.rotation = Number((item.attrs.rotation | 0).toFixed(2));
|
|
|
+ cameras.value[index].groupConfig.scaleX = Number((item.attrs.scaleX | 1).toFixed(1));
|
|
|
+ cameras.value[index].groupConfig.scaleY = Number((item.attrs.scaleY | 1).toFixed(1));
|
|
|
+ cameras.value[index].config.url = cameraImgSrc;
|
|
|
+ return cameras.value[index];
|
|
|
+ });
|
|
|
+ cameras.value = camerasList;
|
|
|
const layout = {
|
|
|
- bgInfo: {
|
|
|
- bgImg: bgImgUrl.value,
|
|
|
- width: bgImg.width,
|
|
|
- height: bgImg.height,
|
|
|
- },
|
|
|
+ stageConfig: stageConfig.value,
|
|
|
+ bgConfig: bgConfig.value,
|
|
|
+ bgImgUrl: bgImgUrl.value,
|
|
|
defaultCameraId: defaultCameraId.value,
|
|
|
cameraList: cameras.value,
|
|
|
};
|
|
|
- const jsonString = JSON.stringify(layout);
|
|
|
- console.log(jsonString);
|
|
|
- // Here you can do something with the layout JSON string, such as sending it to a server or saving it locally
|
|
|
+ return JSON.stringify(layout);
|
|
|
+ };
|
|
|
+
|
|
|
+ //删除相机
|
|
|
+ const handleKeyDown = (e) => {
|
|
|
+ if (e.keyCode === 46 || e.code === 'Delete') {
|
|
|
+ if (lastClickedGroupId.value === defaultCameraId.value) {
|
|
|
+ ElMessage.error({
|
|
|
+ message: '无法删除默认相机',
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const index = cameras.value.findIndex((item) => item.id === lastClickedGroupId.value);
|
|
|
+ index >= 0 && cameras.value.splice(index, 1);
|
|
|
+ //取消transformer
|
|
|
+ const transformerNode = transformer.value.getNode();
|
|
|
+ transformerNode.nodes([]);
|
|
|
+ }
|
|
|
};
|
|
|
- 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 = '';
|
|
|
+ bgImgUrl.value = null;
|
|
|
+ addBg('');
|
|
|
+ cameras.value = [];
|
|
|
+ lastClickedGroupId.value = null;
|
|
|
+ defaultCameraId.value = '';
|
|
|
};
|
|
|
|
|
|
/** 导入布局json */
|
|
|
const createMap = (layout) => {
|
|
|
- // bgImgUrl.value = layout.bgInfo.bgImg;
|
|
|
-
|
|
|
- // console.log('bgImgUrl.value11 ', bgImgUrl.value);
|
|
|
- console.log('layout.bgInfo.bgImg', layout.bgInfo.bgImg);
|
|
|
-
|
|
|
- addBg(layout.bgInfo.bgImg);
|
|
|
- // layout.cameraList.forEach((camera) => {
|
|
|
- // const group = new Konva.Group({
|
|
|
- // x: camera.x,
|
|
|
- // y: camera.y,
|
|
|
- // id: camera.cameraId,
|
|
|
- // rotation: camera.rotation,
|
|
|
- // scaleX: camera.scaleX,
|
|
|
- // scaleY: camera.scaleY,
|
|
|
- // draggable: false,
|
|
|
- // name: 'group',
|
|
|
- // });
|
|
|
- // const camImg = new Image();
|
|
|
- // camImg.onload = () => {
|
|
|
- // const cameraIcon = new Konva.Image({
|
|
|
- // width: 52,
|
|
|
- // height: 37,
|
|
|
- // image: camImg,
|
|
|
- // name: 'image',
|
|
|
- // });
|
|
|
- // group.add(cameraIcon);
|
|
|
- // layer?.add(group);
|
|
|
- // bindBaseEvt(cameraIcon);
|
|
|
- // addedCameras.value.push(camera.cameraId);
|
|
|
-
|
|
|
- // if (camera.cameraId === layout.defaultCameraId) {
|
|
|
- // setDefaultCamera(group);
|
|
|
- // defaultIcon?.show();
|
|
|
- // }
|
|
|
-
|
|
|
- // if (addedCameras.value.length === layout.cameraList.length) {
|
|
|
- // layer?.batchDraw();
|
|
|
- // }
|
|
|
- // };
|
|
|
- // camImg.src = cameraImg;
|
|
|
- // });
|
|
|
+ addBg(layout.bgImgUrl);
|
|
|
+ stageConfig.value = layout.stageConfig;
|
|
|
+ const camImg = new Image();
|
|
|
+ camImg.src = cameraImgSrc;
|
|
|
+ defaultCameraId.value = layout.defaultCameraId;
|
|
|
+ layout.cameraList = layout.cameraList?.map((item) => {
|
|
|
+ item.config.image = camImg;
|
|
|
+ return item;
|
|
|
+ });
|
|
|
+ cameras.value = layout.cameraList;
|
|
|
};
|
|
|
|
|
|
- defineExpose({ addBg, createMap, resetMap, addCamera, resizeContainer });
|
|
|
+ watch();
|
|
|
+
|
|
|
+ defineExpose({ addBg, createMap, resetMap, addCamera, resizeContainer, saveLayout });
|
|
|
|
|
|
onMounted(() => {
|
|
|
- // Add initial cameras, if needed
|
|
|
+ window.addEventListener('keydown', handleKeyDown);
|
|
|
});
|
|
|
|
|
|
onBeforeUnmount(() => {
|