| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- <template>
- <div>
- <v-stage ref="stageAll" :config="stageConfig" @click="handleStageClick">
- <v-layer ref="layer">
- <v-image :config="bgConfig" v-show="bgImgUrl" />
- <v-group
- v-for="camera in cameras"
- :key="camera.id"
- :id="camera.id"
- :config="camera.groupConfig"
- @click="handleCameraClick(camera)"
- @mouseover="(e) => handleMouseOver(e)"
- @mouseleave="handleMouseLeave()"
- @dragstart="handleDragStart()"
- >
- <v-image :config="camera.config" />
- <v-image v-if="camera.isDefault" ref="defaultIcon" :config="defaultIconConfig" />
- </v-group>
- <v-transformer :config="transformerConfig" ref="transformer" />
- </v-layer>
- </v-stage>
- <div
- @click="setDefaultCamera"
- v-show="defaultShow"
- class="opt-container"
- :style="{ position: 'absolute', left: posX + 'px', top: posY + 'px' }"
- ><div class="opt-item" :class="{ disabled: disabledSet }">设为默认相机</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, watch } from 'vue';
- import { ElMessage, ElMessageBox } from 'element-plus';
- import { useGlobSetting } from '@/hooks/setting';
- import DefaultTip from '../components/DefaultTip.vue';
- import urlJoin from 'url-join';
- import cameraImgSrc from '@/assets/camera/cameraImg.png';
- import favoritesImgSrc from '@/assets/camera/favorites.png';
- import { TipPositionEnum, camerasGroupType } from '../type';
- import { cloneDeep } from 'lodash-es';
- import useMiniMap from '../use-mini-map';
- import { storeToRefs } from 'pinia';
- const globSetting = useGlobSetting();
- const miniMap = useMiniMap();
- const { shopCameraList } = storeToRefs(miniMap);
- const emit = defineEmits(['changeDefaultCamera', 'sendCameraId']);
- const camImg = new Image();
- const stageConfig = ref({
- width: 800,
- height: 600,
- });
- const bgImg = new Image();
- const favoritesImg = new Image();
- 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 lastClickedGroupId = ref<string | null>(null);
- const bgImgUrl = ref<string | null>('');
- const cameras = ref<camerasGroupType[]>([]);
- //默认相机id
- const defaultCameraId = ref('');
- const tipShow = ref(false);
- const disabledSet = ref(false);
- //标签
- const layer = ref();
- const transformer = ref();
- const defaultIcon = ref();
- const bgConfig = ref({
- x: 0,
- y: 0,
- id: 'bgImg',
- width: bgImg.width,
- height: bgImg.height,
- image: bgImg,
- name: 'bgImg',
- backgroundSize: 'cover',
- });
- //待修改
- const transformerConfig = ref({
- keepRatio: true,
- rotateAnchorOffset: 30,
- enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
- });
- //背景大小变换
- const resizeContainer = (width, height) => {
- stageConfig.value.width = width;
- stageConfig.value.height = height;
- layer.value.getNode().draw();
- };
- const defaultIconConfig = ref({
- x: 18,
- y: -16,
- width: 16,
- height: 16,
- id: 'defaultIcon',
- image: favoritesImg,
- });
- //取消
- const handleMouseOver = (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;
- }
- }
- };
- // 还原浏览器默认鼠标事件
- const handleMouseLeave = () => {
- document.oncontextmenu = () => {
- return true;
- };
- tipShow.value = false;
- };
- const handleDragStart = () => {
- tipShow.value = false;
- };
- const handleStageClick = (e: any) => {
- defaultShow.value = false;
- disabledSet.value = false;
- document.oncontextmenu = () => {
- return false;
- };
- const parent = e.target.parent;
- //判断是否点击相机组
- if (parent.hasName('group')) {
- lastClickedGroupId.value = parent.id();
- // 判断是否为右键点击
- if (e.evt.button === 2) {
- lastClickedGroupId.value = parent.id();
- posX.value = e.evt.offsetX + 20;
- posY.value = e.evt.offsetY;
- disabledSet.value = defaultCameraId.value === parent.id();
- defaultShow.value = true;
- }
- } else {
- lastClickedGroupId.value = null;
- //取消transformer选择
- const transformerNode = transformer.value.getNode();
- transformerNode.nodes([]);
- }
- };
- const handleCameraClick = (camera) => {
- tipShow.value = false;
- const transformerNode = transformer.value.getNode();
- const stage = transformerNode.getStage();
- const cameraNode = stage.findOne('#' + camera.id);
- // 将变换器附加到点击的相机
- transformerNode.nodes([cameraNode]);
- transformerNode.moveToTop();
- };
- //添加相机
- const addCamera = (id: string) => {
- const existingCamera = cameras.value.find((camera) => camera.id === id);
- if (existingCamera) return;
- const config = {
- width: 52,
- 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;
- }
- };
- //设置默认相机
- const setDefaultCamera = () => {
- //选中的相机id号
- defaultCameraId.value = lastClickedGroupId.value!;
- cameras.value.forEach((item) => {
- if (item.id === defaultCameraId.value) {
- item.isDefault = true;
- } else {
- item.isDefault = false;
- }
- });
- const transformerNode = transformer.value.getNode();
- const stage = transformerNode.getStage();
- transformerNode.nodes([]);
- const cameraNode = stage.findOne('#' + defaultCameraId.value);
- // 将变换器附加到点击的相机
- transformerNode.nodes([cameraNode]);
- defaultShow.value = false;
- };
- watch(
- lastClickedGroupId,
- () => {
- emit('changeDefaultCamera', lastClickedGroupId.value);
- },
- { immediate: true },
- );
- watch(
- () => cameras.value,
- () => {
- emit('sendCameraId', cameras.value);
- },
- { immediate: true, deep: true },
- );
- const clearBg = () => {
- bgImg.src = null as any as string;
- layer.value.getNode().draw();
- };
- //添加背景
- const addBg = (imgBg) => {
- return new Promise((resolve) => {
- bgImgUrl.value = imgBg;
- bgImg.src = urlJoin(globSetting.imgUrl!, imgBg) as string;
- bgImg.onload = () => {
- bgConfig.value.width = bgImg.width;
- bgConfig.value.height = bgImg.height;
- resizeContainer(bgImg.width, bgImg.height);
- resolve(null);
- };
- });
- };
- //保存布局
- const saveLayout = () => {
- const stage = transformer.value.getNode().getStage();
- const groups = stage.find('.group');
- const tempList = cloneDeep(cameras.value);
- // console.log('cameras.value', cameras.value);
- const camerasLists = tempList.map((item, index) => {
- item.groupConfig.x = groups[index].attrs.x;
- item.groupConfig.y = groups[index].attrs.y;
- item.groupConfig.rotation = groups[index].attrs.rotation || 0;
- item.groupConfig.scaleX = groups[index].attrs.scaleX || 1;
- item.groupConfig.scaleY = groups[index].attrs.scaleY || 1;
- // item.config.url = cameraImgSrc;
- return item;
- });
- const layout = {
- stageConfig: stageConfig.value,
- bgConfig: bgConfig.value,
- bgImgUrl: bgImgUrl.value,
- defaultCameraId: defaultCameraId.value,
- cameraList: camerasLists,
- };
- return JSON.stringify(layout);
- };
- //删除相机
- const handleKeyDown = (e) => {
- if (e.keyCode === 46 || e.code === 'Delete' || e.keyCode === 8 || e.code === 'Backspace') {
- if (lastClickedGroupId.value === defaultCameraId.value) {
- // ElMessage.error({
- // message: '无法删除默认相机',
- // });
- // return;
- ElMessageBox.confirm('此相机为默认相机,您确认要删除此相机?', 'Warning', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- const index = cameras.value.findIndex((item) => item.id === lastClickedGroupId.value);
- index >= 0 && cameras.value.splice(index, 1);
- lastClickedGroupId.value = '';
- //取消transformer
- const transformerNode = transformer.value.getNode();
- transformerNode.nodes([]);
- })
- .catch(() => {});
- } else {
- const index = cameras.value.findIndex((item) => item.id === lastClickedGroupId.value);
- index >= 0 && cameras.value.splice(index, 1);
- lastClickedGroupId.value = '';
- //取消transformer
- const transformerNode = transformer.value.getNode();
- transformerNode.nodes([]);
- }
- }
- };
- //重置
- const resetMap = () => {
- bgImgUrl.value = null;
- clearBg();
- cameras.value = [];
- lastClickedGroupId.value = null;
- defaultCameraId.value = '';
- };
- /** 导入布局json */
- const createMap = (layout) => {
- addBg(layout.bgImgUrl).then((_res) => {
- const unExitList = [] as any[];
- stageConfig.value = layout.stageConfig;
- defaultCameraId.value = layout.defaultCameraId;
- layout.cameraList = layout.cameraList
- ?.map((item) => {
- item.config.image = camImg;
- return item;
- })
- ?.filter((cam) => {
- if (shopCameraList.value.findIndex((x) => x.code === cam.id) >= 0) {
- return true;
- } else {
- unExitList.push(cam.code);
- return false;
- }
- });
- if (unExitList.length > 0) {
- ElMessage.warning('部分相机不存在,已为您删除!');
- }
- cameras.value = layout.cameraList;
- });
- };
- defineExpose({ addBg, createMap, resetMap, addCamera, resizeContainer, saveLayout });
- onMounted(() => {
- window.addEventListener('keydown', handleKeyDown);
- camImg.src = cameraImgSrc;
- });
- onBeforeUnmount(() => {
- window.removeEventListener('keydown', handleKeyDown);
- });
- </script>
- <style scoped lang="scss">
- .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;
- &:hover {
- background-color: #f1f2f5;
- }
- }
- .disabled {
- background-color: #f1f2f5;
- color: #bcbdc0;
- cursor: not-allowed;
- }
- </style>
|