|
@@ -59,8 +59,11 @@
|
|
|
<div
|
|
<div
|
|
|
v-for="item in filterShopCameraList"
|
|
v-for="item in filterShopCameraList"
|
|
|
:key="item.code"
|
|
:key="item.code"
|
|
|
- class="camera-item flex justify-start items-center"
|
|
|
|
|
- :class="{ isAdded: isAddedToMap(item.code), 'camera-item-disabled': !hasBg }"
|
|
|
|
|
|
|
+ class="camera-item flex justify-start"
|
|
|
|
|
+ :class="{
|
|
|
|
|
+ isAdded: isAddedToMap(item.code),
|
|
|
|
|
+ isActive: item.code === selectedCamera?.cameraId,
|
|
|
|
|
+ }"
|
|
|
@click="handleAddCamera(item.code)"
|
|
@click="handleAddCamera(item.code)"
|
|
|
>
|
|
>
|
|
|
<span class="camera-id">{{ item.name }}</span>
|
|
<span class="camera-id">{{ item.name }}</span>
|
|
@@ -78,44 +81,58 @@
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="draw-container">
|
|
<div class="draw-container">
|
|
|
- <!-- <div style="display: flex; margin-bottom: 20px">
|
|
|
|
|
- <div>
|
|
|
|
|
- 默认摄像头:
|
|
|
|
|
- <ElSelect v-model="defaultCameraId">
|
|
|
|
|
- <ElOption
|
|
|
|
|
- :key="item.value"
|
|
|
|
|
- :label="item.value"
|
|
|
|
|
- :value="item.value"
|
|
|
|
|
- v-for="item in cameraOptions"
|
|
|
|
|
- />
|
|
|
|
|
- </ElSelect>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div> -->
|
|
|
|
|
- <!-- <div>
|
|
|
|
|
|
|
+ <div style="display: flex; margin-bottom: 20px">
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ class="avatar-uploader flex justify-center items-center"
|
|
|
|
|
+ action="/temp/api/layout/uploadPicture"
|
|
|
|
|
+ :show-file-list="false"
|
|
|
|
|
+ :on-success="handleAvatarSuccess"
|
|
|
|
|
+ :with-credentials="true"
|
|
|
|
|
+ name="file"
|
|
|
|
|
+ :data="{ workshopId: selectedShopDetail?.id }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-button style="font-size: 12px">+ 更换/上传背景图片</el-button>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ @click="handleSave"
|
|
|
|
|
+ style="margin-left: 40px"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ :disabled="!selectedShopCode"
|
|
|
|
|
+ >保存布局</el-button
|
|
|
|
|
+ >
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
<div style="height: 20px; margin-bottom: 10px">
|
|
<div style="height: 20px; margin-bottom: 10px">
|
|
|
<div v-if="selectedCamera">
|
|
<div v-if="selectedCamera">
|
|
|
- 已选中相机<span>: {{ selectedCamera?.cameraId }}</span>
|
|
|
|
|
- <span style="margin-left: 20px">
|
|
|
|
|
- 工位:{{ selectedCameraDetail?.workSpaceName }}
|
|
|
|
|
- </span>
|
|
|
|
|
|
|
+ <SelectedCameraToolbar
|
|
|
|
|
+ @render-map="renderMap"
|
|
|
|
|
+ :selected-camera="selectedCamera!"
|
|
|
|
|
+ :key="selectedPositionHash"
|
|
|
|
|
+ />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div> -->
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ -->
|
|
|
<div style="overflow: auto">
|
|
<div style="overflow: auto">
|
|
|
<canvas width="400" height="400" id="mapEditCanvas"></canvas>
|
|
<canvas width="400" height="400" id="mapEditCanvas"></canvas>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-upload
|
|
|
|
|
- v-if="!hasBg"
|
|
|
|
|
- class="upload-icon flex justify-center items-center"
|
|
|
|
|
- action="/temp/api/layout/uploadPicture"
|
|
|
|
|
- :show-file-list="false"
|
|
|
|
|
- :on-success="handleAvatarSuccess"
|
|
|
|
|
- :with-credentials="true"
|
|
|
|
|
- name="file"
|
|
|
|
|
- :data="{ workshopId: selectedShopCode }"
|
|
|
|
|
- >
|
|
|
|
|
- <img src="~@/assets/images/img-upload.png" />
|
|
|
|
|
- </el-upload>
|
|
|
|
|
|
|
+ <div style="overflow: auto; position: relative">
|
|
|
|
|
+ <canvas
|
|
|
|
|
+ width="400"
|
|
|
|
|
+ height="400"
|
|
|
|
|
+ id="mapEditCanvas"
|
|
|
|
|
+ style="border: 1px solid #ccc"
|
|
|
|
|
+ ></canvas>
|
|
|
|
|
+ <ContextMenu
|
|
|
|
|
+ :visible="menuVisible"
|
|
|
|
|
+ :position="mousePosition"
|
|
|
|
|
+ :set-default="handleSetDefault"
|
|
|
|
|
+ />
|
|
|
|
|
+ <DefaultCameraIcon :position="favPosition" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <CameraPreview :json="cameraJSON" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -126,33 +143,80 @@
|
|
|
import { storeToRefs } from 'pinia';
|
|
import { storeToRefs } from 'pinia';
|
|
|
import { ElMessage, ElInput } from 'element-plus';
|
|
import { ElMessage, ElInput } from 'element-plus';
|
|
|
import urlJoin from 'url-join';
|
|
import urlJoin from 'url-join';
|
|
|
- import { onMounted, ref, toRaw } from 'vue';
|
|
|
|
|
- import CameraMap, { CameraImage } from './MapBase/CameraMap';
|
|
|
|
|
- // import { ElSelect, ElOption } from 'element-plus';
|
|
|
|
|
|
|
+ import { onMounted, ref } from 'vue';
|
|
|
|
|
+ import useCameraMap from './MapBase/useCameraMap';
|
|
|
import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
|
|
import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
|
|
|
- import { onUnmounted } from 'vue';
|
|
|
|
|
|
|
+ import { onUnmounted, watchEffect } from 'vue';
|
|
|
import { useGlobSetting } from '@/hooks/setting';
|
|
import { useGlobSetting } from '@/hooks/setting';
|
|
|
import { computed } from 'vue';
|
|
import { computed } from 'vue';
|
|
|
import ContextMenu from './MapBase/ContextMenu.vue';
|
|
import ContextMenu from './MapBase/ContextMenu.vue';
|
|
|
- import { Search, ArrowLeft, Refresh } from '@element-plus/icons-vue';
|
|
|
|
|
|
|
+ import DefaultCameraIcon from './MapBase/DefaultCameraIcon.vue';
|
|
|
|
|
+ import { CameraImage } from './MapBase/types';
|
|
|
|
|
+ import { createSelectedPositionHash, getFavPositionByCamera } from './MapBase/utils';
|
|
|
|
|
+ import SelectedCameraToolbar from './components/SelectedCameraToolbar.vue';
|
|
|
|
|
+ import CameraPreview from './MapBase/CameraPreview.vue';
|
|
|
|
|
|
|
|
const miniMap = useMiniMap();
|
|
const miniMap = useMiniMap();
|
|
|
const globSetting = useGlobSetting();
|
|
const globSetting = useGlobSetting();
|
|
|
const { scenesTree, shopCameraList, selectedShopCode, selectedShopDetail } = storeToRefs(miniMap);
|
|
const { scenesTree, shopCameraList, selectedShopCode, selectedShopDetail } = storeToRefs(miniMap);
|
|
|
const { getScenesTree, getShowCameras, getMapLayout } = miniMap;
|
|
const { getScenesTree, getShowCameras, getMapLayout } = miniMap;
|
|
|
|
|
+ const selectedPositionHash = ref('');
|
|
|
|
|
+ const allObjects = ref<CameraImage[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+ const onSelectCamera = () => {
|
|
|
|
|
+ menuVisible.value = false;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleRightClick = (e) => {
|
|
|
|
|
+ mousePosition.value = { left: e.pointer.x, top: e.pointer.y };
|
|
|
|
|
+ menuVisible.value = true;
|
|
|
|
|
+ rightSelectedCamera.value = e.target;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleMoving = (e) => {
|
|
|
|
|
+ const target = e.transform.target;
|
|
|
|
|
+ selectedPositionHash.value = createSelectedPositionHash(target);
|
|
|
|
|
+ if (target?.cameraId === defaultCamera.value?.cameraId) {
|
|
|
|
|
+ favPosition.value = getFavPositionByCamera(target);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let map: CameraMap;
|
|
|
|
|
- const selectedCamera = ref<CameraImage | null>(null);
|
|
|
|
|
|
|
+ const handleRoating = (e) => {
|
|
|
|
|
+ const target = e.transform.target;
|
|
|
|
|
+ selectedPositionHash.value = createSelectedPositionHash(target);
|
|
|
|
|
+ if (target.cameraId === defaultCamera.value?.cameraId) {
|
|
|
|
|
+ favPosition.value = getFavPositionByCamera(target);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleSetSelectedCameara = (target: CameraImage | null) => {
|
|
|
|
|
+ selectedCamera.value = target;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleObjectsAdded = () => {
|
|
|
|
|
+ allObjects.value = map.getObjects();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const map = useCameraMap({
|
|
|
|
|
+ onSelect: onSelectCamera,
|
|
|
|
|
+ onRightClick: handleRightClick,
|
|
|
|
|
+ onMoving: handleMoving,
|
|
|
|
|
+ onRotating: handleRoating,
|
|
|
|
|
+ setSelectedCamera: handleSetSelectedCameara,
|
|
|
|
|
+ onObjectsAdded: handleObjectsAdded,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const selectedCamera = ref<CameraImage | null>();
|
|
|
/** 右键选中的摄像机 */
|
|
/** 右键选中的摄像机 */
|
|
|
const rightSelectedCamera = ref<CameraImage | null>(null);
|
|
const rightSelectedCamera = ref<CameraImage | null>(null);
|
|
|
- const cameraOptions = ref<{ label: string; value: string }[]>([]);
|
|
|
|
|
- const defaultCameraId = ref('');
|
|
|
|
|
|
|
+ const defaultCamera = ref<CameraImage | null>();
|
|
|
const searchKey = ref('');
|
|
const searchKey = ref('');
|
|
|
// 是否已有背景图
|
|
// 是否已有背景图
|
|
|
const hasBg = ref(false);
|
|
const hasBg = ref(false);
|
|
|
|
|
|
|
|
const mousePosition = ref<{ left: number; top: number }>({ left: 0, top: 0 });
|
|
const mousePosition = ref<{ left: number; top: number }>({ left: 0, top: 0 });
|
|
|
const menuVisible = ref(false);
|
|
const menuVisible = ref(false);
|
|
|
|
|
+ const favPosition = ref<{ left: number; top: number } | null>(null);
|
|
|
|
|
|
|
|
const handleAvatarSuccess = (e) => {
|
|
const handleAvatarSuccess = (e) => {
|
|
|
const imgPath = e.data;
|
|
const imgPath = e.data;
|
|
@@ -161,24 +225,37 @@
|
|
|
map.uploadBg(imgUrl);
|
|
map.uploadBg(imgUrl);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const renderMap = () => {
|
|
|
|
|
+ map.renderCamera();
|
|
|
|
|
+ favPosition.value = getFavPositionByCamera(defaultCamera.value);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const changeShop = (code: string) => {
|
|
const changeShop = (code: string) => {
|
|
|
getShopContent(code);
|
|
getShopContent(code);
|
|
|
hasBg.value = false;
|
|
hasBg.value = false;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const cameraJSON = ref();
|
|
|
|
|
+
|
|
|
const getShopContent = (code: string) => {
|
|
const getShopContent = (code: string) => {
|
|
|
getShowCameras(code);
|
|
getShowCameras(code);
|
|
|
getMapLayout(code).then((res) => {
|
|
getMapLayout(code).then((res) => {
|
|
|
if (!res) {
|
|
if (!res) {
|
|
|
map.clear();
|
|
map.clear();
|
|
|
|
|
+ defaultCamera.value = null;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- defaultCameraId.value = res.defaultCameraId;
|
|
|
|
|
|
|
+ cameraJSON.value = res;
|
|
|
|
|
+
|
|
|
map.loadFromJSON(res).then(() => {
|
|
map.loadFromJSON(res).then(() => {
|
|
|
- mapJSONToOptions();
|
|
|
|
|
- if (!defaultCameraId.value) {
|
|
|
|
|
- defaultCameraId.value = cameraOptions.value[0]?.value;
|
|
|
|
|
|
|
+ console.log('loadFromJSON', res);
|
|
|
|
|
+ if (res.defaultCameraId) {
|
|
|
|
|
+ defaultCamera.value = map.getCameraById(res.defaultCameraId);
|
|
|
|
|
+ console.log('defaultCamera', defaultCamera.value);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ defaultCamera.value = map.getObjects()?.[0] as CameraImage;
|
|
|
}
|
|
}
|
|
|
|
|
+ renderMap();
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
@@ -188,19 +265,13 @@
|
|
|
if (selectedShopCode.value) {
|
|
if (selectedShopCode.value) {
|
|
|
getShopContent(selectedShopCode.value);
|
|
getShopContent(selectedShopCode.value);
|
|
|
}
|
|
}
|
|
|
- map = new CameraMap({
|
|
|
|
|
- canvasId: 'mapEditCanvas',
|
|
|
|
|
- onSelect: onSelectCamera,
|
|
|
|
|
- onRightClick: handleRightClick,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ map.createMap('mapEditCanvas');
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const keyupListener = (e) => {
|
|
const keyupListener = (e) => {
|
|
|
const keyCode = e.code;
|
|
const keyCode = e.code;
|
|
|
if (keyCode === 'Delete') {
|
|
if (keyCode === 'Delete') {
|
|
|
- if (selectedCamera.value) {
|
|
|
|
|
- handleDeleteCamera();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ handleDeleteCamera();
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -212,10 +283,6 @@
|
|
|
document.removeEventListener('keyup', keyupListener);
|
|
document.removeEventListener('keyup', keyupListener);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- const selectedCameraDetail = computed(() => {
|
|
|
|
|
- return shopCameraList.value.find((item) => item.code === selectedCamera.value?.cameraId);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
const filterShopCameraList = computed(() => {
|
|
const filterShopCameraList = computed(() => {
|
|
|
const k = searchKey.value.trim();
|
|
const k = searchKey.value.trim();
|
|
|
if (!k) return shopCameraList.value;
|
|
if (!k) return shopCameraList.value;
|
|
@@ -224,7 +291,7 @@
|
|
|
|
|
|
|
|
/** 摄像机是否已添加到底图 */
|
|
/** 摄像机是否已添加到底图 */
|
|
|
const isAddedToMap = (cameraId: string): boolean => {
|
|
const isAddedToMap = (cameraId: string): boolean => {
|
|
|
- return cameraOptions.value.some((x) => x.value === cameraId);
|
|
|
|
|
|
|
+ return !!allObjects.value.find((item) => item.cameraId === cameraId);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleAddCamera = (cameraId: string) => {
|
|
const handleAddCamera = (cameraId: string) => {
|
|
@@ -234,38 +301,12 @@
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
map.addCamera(cameraId).then((cameraImg) => {
|
|
map.addCamera(cameraId).then((cameraImg) => {
|
|
|
- onSelectCamera(cameraImg);
|
|
|
|
|
- mapJSONToOptions();
|
|
|
|
|
- if (!defaultCameraId.value) {
|
|
|
|
|
- defaultCameraId.value = cameraOptions.value[0]?.value;
|
|
|
|
|
|
|
+ if (!defaultCamera.value?.cameraId) {
|
|
|
|
|
+ defaultCamera.value = cameraImg;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const onSelectCamera = (cameraImg: CameraImage) => {
|
|
|
|
|
- console.log('onSelectCamera', cameraImg);
|
|
|
|
|
- selectedCamera.value = cameraImg;
|
|
|
|
|
- menuVisible.value = false;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleRightClick = (e) => {
|
|
|
|
|
- mousePosition.value = { left: e.pointer.x, top: e.pointer.y };
|
|
|
|
|
- menuVisible.value = true;
|
|
|
|
|
- rightSelectedCamera.value = e.target;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const mapJSONToOptions = () => {
|
|
|
|
|
- const objects = map.toJSON()?.objects || [];
|
|
|
|
|
- if (map.toJSON()?.backgroundImage.src) {
|
|
|
|
|
- hasBg.value = true;
|
|
|
|
|
- }
|
|
|
|
|
- console.log('objects', objects);
|
|
|
|
|
- cameraOptions.value = objects?.map((x) => ({
|
|
|
|
|
- label: x.cameraId,
|
|
|
|
|
- value: x.cameraId,
|
|
|
|
|
- }));
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
const handleSave = () => {
|
|
const handleSave = () => {
|
|
|
const json = map.toJSON();
|
|
const json = map.toJSON();
|
|
|
console.log('save json', json);
|
|
console.log('save json', json);
|
|
@@ -273,31 +314,40 @@
|
|
|
ElMessage.error('背景图片未添加');
|
|
ElMessage.error('背景图片未添加');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const layout = JSON.stringify({ ...json, defaultCameraId: defaultCameraId.value });
|
|
|
|
|
- updateMinMapViewLayoutApi({ layout, targetId: selectedShopCode.value + '' }).then((res) => {
|
|
|
|
|
- console.log('updateMinMapViewLayoutApi', res);
|
|
|
|
|
- ElMessage.success('保存成功');
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const layout = JSON.stringify({ ...json, defaultCameraId: defaultCamera.value?.cameraId });
|
|
|
|
|
+ updateMinMapViewLayoutApi({ layout, targetId: String(selectedShopDetail.value?.id) }).then(
|
|
|
|
|
+ (res) => {
|
|
|
|
|
+ console.log('updateMinMapViewLayoutApi', res);
|
|
|
|
|
+ ElMessage.success('保存成功');
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleDeleteCamera = () => {
|
|
const handleDeleteCamera = () => {
|
|
|
if (!selectedCamera.value) return;
|
|
if (!selectedCamera.value) return;
|
|
|
- map.removeCamera(toRaw(selectedCamera.value!));
|
|
|
|
|
/** 如果删除的是默认选中的摄像头,那么先清空默认的摄像头再 */
|
|
/** 如果删除的是默认选中的摄像头,那么先清空默认的摄像头再 */
|
|
|
- mapJSONToOptions();
|
|
|
|
|
- if (selectedCamera.value.cameraId === defaultCameraId.value) {
|
|
|
|
|
- defaultCameraId.value = cameraOptions.value[0]?.value;
|
|
|
|
|
|
|
+ if (selectedCamera.value?.cameraId === defaultCamera.value?.cameraId) {
|
|
|
|
|
+ defaultCamera.value = map.getObjects()?.[0];
|
|
|
}
|
|
}
|
|
|
- selectedCamera.value = null;
|
|
|
|
|
|
|
+ map.removeActiveCamera();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleSetDefault = () => {
|
|
const handleSetDefault = () => {
|
|
|
const cameraId = rightSelectedCamera.value?.cameraId;
|
|
const cameraId = rightSelectedCamera.value?.cameraId;
|
|
|
if (!cameraId) return;
|
|
if (!cameraId) return;
|
|
|
- defaultCameraId.value = cameraId;
|
|
|
|
|
|
|
+ defaultCamera.value = rightSelectedCamera.value;
|
|
|
|
|
+
|
|
|
/** 选择完成后隐藏 */
|
|
/** 选择完成后隐藏 */
|
|
|
menuVisible.value = false;
|
|
menuVisible.value = false;
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ watchEffect(() => {
|
|
|
|
|
+ if (!defaultCamera.value) {
|
|
|
|
|
+ favPosition.value = null;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ favPosition.value = getFavPositionByCamera(defaultCamera.value);
|
|
|
|
|
+ });
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
@@ -396,4 +446,8 @@
|
|
|
.isAdded {
|
|
.isAdded {
|
|
|
color: #409eff;
|
|
color: #409eff;
|
|
|
}
|
|
}
|
|
|
|
|
+ .isActive {
|
|
|
|
|
+ background-color: #eee;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
|
|
+./MapBase/useCameraMap ./MapBase/CameraMapBak
|