Просмотр исходного кода

Merge branch 'camera-lhf' into 'master'

相机预览功能-优化电子围栏

See merge request tian-group/skyeye-admin-fe!15
楼航飞 2 лет назад
Родитель
Сommit
4908d24bc1

+ 40 - 3
src/api/camera/camera-preview.ts

@@ -73,8 +73,8 @@ export interface CameraAlgoItem {
 }
 
 /** 查询某个camera下的所有算法 */
-export const getCameraAlgoListApi = (cameraId: number) => {
-  return http.request<CameraAlgoItem[]>({
+export const getCameraAlgoListApi = (cameraId: number): Promise<CameraAlgoItem[]> => {
+  return http.request({
     url: '/cameraPreview/getAlgo',
     method: 'get',
     params: { cameraId },
@@ -89,8 +89,14 @@ interface SaveCameraAlgoParam {
   electronicFence: string;
   status: 0 | 1;
 }
+
+interface CreateCameraAlgoParam {
+  algoIds: number[];
+  cameraId: number;
+}
+
 /** 保存相机的某个算法 */
-export const createCameraAlgoApi = (param: SaveCameraAlgoParam) => {
+export const createCameraAlgoApi = (param: CreateCameraAlgoParam) => {
   return http.request({
     url: '/cameraPreview/saveAlgo',
     data: param,
@@ -258,3 +264,34 @@ export const cameraMoveApi = (data: CameraMoveParam) => {
     data,
   });
 };
+
+interface SaveCameraParams {
+  startTime: string;
+  endTime: string;
+  imageResolution: string;
+  recordPeriod: number;
+  /** 这个要调整一下 */
+  reservation: string;
+  cameraId: number;
+}
+
+export const saveCameraParamsApi = (data: SaveCameraParams) => {
+  localStorage.setItem('cameraParams' + data.cameraId, JSON.stringify(data));
+  return Promise.resolve();
+};
+
+export const getCameraParamsApi = (cameraId: number) => {
+  const data = localStorage.getItem('cameraParams' + cameraId);
+  let jsonData = {
+    startTime: '',
+    endTime: '',
+    imageResolution: '1920',
+    recordPeriod: 20,
+    /** 这个要调整一下 */
+    reservation: '10',
+  };
+  if (data) {
+    jsonData = JSON.parse(data);
+  }
+  return Promise.resolve(jsonData);
+};

+ 6 - 3
src/utils/dateUtil.ts

@@ -1,12 +1,15 @@
-import dayjs from 'dayjs';
+import dayjs, { Dayjs } from 'dayjs';
 
 const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
 const DATE_FORMAT = 'YYYY-MM-DD ';
 
-export function formatToDateTime(date: Date, formatStr = DATE_TIME_FORMAT): string {
+export function formatToDateTime(
+  date: Date | Dayjs | string,
+  formatStr = DATE_TIME_FORMAT,
+): string {
   return dayjs(date).format(formatStr);
 }
 
-export function formatToDate(date: Date, formatStr = DATE_FORMAT): string {
+export function formatToDate(date: Date | Dayjs | string, formatStr = DATE_FORMAT): string {
   return dayjs(date).format(formatStr);
 }

+ 10 - 1
src/views/cameras/preview/CameraPreview.vue

@@ -6,7 +6,8 @@
       </div>
       <div class="cameraSettingWrapper">
         <div class="cameraView">
-          <CameraViewSetting />
+          <CameraViewSetting v-if="cameraDetailStore.cameraId" />
+          <div class="cameraPlaceholder" v-else>请选择左侧相机</div>
         </div>
       </div>
     </div>
@@ -16,6 +17,8 @@
 <script lang="ts" setup>
   import CameraTree from './components/CameraTree/CameraTree.vue';
   import CameraViewSetting from './components/CameraViewSetting/CameraViewSetting.vue';
+  import useCameraDetailStore from './store/useCameraDetailStore';
+  const cameraDetailStore = useCameraDetailStore();
 </script>
 <style lang="scss" scoped>
   .cameraView {
@@ -50,4 +53,10 @@
     // height: 800px;
     // border: 1px solid #ccc;
   }
+
+  .cameraPlaceholder {
+    color: #ccc;
+    text-align: center;
+    margin-top: 100px;
+  }
 </style>

+ 7 - 11
src/views/cameras/preview/components/AlgorithmsSetting/AddAlgoDialog.vue

@@ -4,7 +4,7 @@
   >
 
   <ElDialog v-model="visible" title="添加算法" @close="handleClose" width="500px">
-    <ElSelect v-model="selectedId" style="width: 150px" size="small">
+    <ElSelect v-model="selectedIds" multiple style="width: 100%" size="small">
       <ElOption
         v-for="item in allAlgoList"
         :key="item.id"
@@ -30,7 +30,7 @@
   import { storeToRefs } from 'pinia';
   import { createCameraAlgoApi } from '@/api/camera/camera-preview';
   import useCameraDetailStore from '../../store/useCameraDetailStore';
-  const selectedId = ref<number>();
+  const selectedIds = ref<number[]>([]);
   const cameraAlgoStore = useCameraAlgoStore();
 
   const { isAlgoBind } = cameraAlgoStore;
@@ -44,20 +44,16 @@
   };
 
   const handleSubmit = () => {
-    if (!selectedId.value) {
+    if (selectedIds.value?.length < 1) {
+      ElMessage.warning({ message: '请选择算法' });
       return;
     }
-
     createCameraAlgoApi({
-      algoId: selectedId.value,
+      algoIds: selectedIds.value || [],
       cameraId: cameraDetailStore.cameraId,
-      status: 0,
-      detectionFrequency: 0,
-      detectionTime: '',
-      electronicFence: '',
     }).then((res) => {
       console.log('createAlgo ok', res);
-      selectedAlgoId.value = selectedId.value;
+      selectedAlgoId.value = selectedIds.value?.[0];
       getCameraAlgoList(cameraDetailStore.cameraId);
       visible.value = false;
       ElMessage.success('添加算法成功');
@@ -66,7 +62,7 @@
 
   const showDialog = () => {
     visible.value = true;
-    selectedId.value = undefined;
+    selectedIds.value = undefined;
   };
 </script>
 <style scoped></style>

+ 2 - 9
src/views/cameras/preview/components/AlgorithmsSetting/AlgoSettingCard.vue

@@ -75,19 +75,12 @@
 <script lang="ts" setup>
   import { ElSelect, ElOption, ElSwitch, ElInputNumber, ElTimePicker } from 'element-plus';
   import { CircleCloseFilled } from '@element-plus/icons-vue';
-  import { ref, watch } from 'vue';
   import { storeToRefs } from 'pinia';
   import dayjs, { Dayjs } from 'dayjs';
   import addTimeIcon from '@/assets/icons/addTimeIcon.png';
   import useCameraAlgoStore from '../../store/useCameraAlgoStore';
-  import { STATUS, TimeRangeItem } from './types';
-  import {
-    FrequencyEnum,
-    createDefaultTime,
-    frequencyOptions,
-    getDetectionJSON,
-    getDetectionTimeJSON,
-  } from './utils';
+  import { STATUS } from './types';
+  import { createDefaultTime, frequencyOptions } from './utils';
 
   // const { data: algoList, loading } = useAllAlgos();
   const cameraAlgoStore = useCameraAlgoStore();

+ 29 - 30
src/views/cameras/preview/components/AlgorithmsSetting/AlgorithmsSetting.vue

@@ -20,6 +20,7 @@
           @on-remove="handleRemove"
           v-if="selectedAlgoId"
         />
+        <div style="color: #ccc; margin-top: 20px" v-else>请选择左侧算法</div>
       </div>
     </div>
   </div>
@@ -29,11 +30,7 @@
   import useCameraAlgoStore from '../../store/useCameraAlgoStore';
   import AlgoSettingCard from './AlgoSettingCard.vue';
   import { storeToRefs } from 'pinia';
-  import {
-    deleteCameraAlgoApi,
-    createCameraAlgoApi,
-    updateCameraAlgoApi,
-  } from '@/api/camera/camera-preview';
+  import { deleteCameraAlgoApi, updateCameraAlgoApi } from '@/api/camera/camera-preview';
   import { ElMessage } from 'element-plus';
   import AlgoTag from './AlgoTag.vue';
   import useFenceStore from '../../store/useFenceStore';
@@ -42,6 +39,7 @@
   import AddAlgoDialog from './AddAlgoDialog.vue';
   import { createDefaultTime, getDetectionJSON, getDetectionTimeJSON } from './utils';
   import { AlgoStatus } from '@/api/camera/camera-preview';
+  import { watchEffect } from 'vue';
 
   const cameraAlgoStore = useCameraAlgoStore();
   const fenceStore = useFenceStore();
@@ -59,29 +57,34 @@
   const handleSelectAlgo = (algoId: number) => {
     if (algoId !== selectedAlgoId.value) {
       selectedAlgoId.value = algoId;
-      const detail = getAlgoDetail(algoId);
+    }
+  };
 
-      console.log('detail change', detail);
-      const detectionJSON = getDetectionJSON(detail?.detectionFrequency);
-      const enableCard = Boolean(detail?.status);
-      const electronicFenceBool = Boolean(detail?.electronicFence);
+  watchEffect(() => {
+    const algoId = selectedAlgoId.value;
+    if (!algoId) return;
+    const detail = getAlgoDetail(algoId);
+    if (!detail) return;
+    console.log('detail change', detail);
+    const detectionJSON = getDetectionJSON(detail?.detectionFrequency);
+    const enableCard = Boolean(detail?.status);
+    const electronicFenceBool = Boolean(detail?.electronicFence);
 
-      const timeRangeArr = getDetectionTimeJSON(detail?.detectionTime) || [createDefaultTime()];
+    const timeRangeArr = getDetectionTimeJSON(detail?.detectionTime) || [createDefaultTime()];
 
-      selectedAlgoDetail.value = {
-        ...detail,
-        detectionJSON,
-        enableCardBool: enableCard,
-        electronicFenceBool,
-        timeRangeArr,
-      };
-      fenceStore.getFence({
-        algoId: algoId,
-        cameraId: cameraDetailStore.cameraId,
-        presetToken: presetStore.currentPresetToken,
-      });
-    }
-  };
+    selectedAlgoDetail.value = {
+      ...detail,
+      detectionJSON,
+      enableCardBool: enableCard,
+      electronicFenceBool,
+      timeRangeArr,
+    };
+    fenceStore.getFence({
+      algoId: algoId,
+      cameraId: cameraDetailStore.cameraId,
+      presetToken: presetStore.currentPresetToken,
+    });
+  });
 
   const handleSubmit = (param) => {
     console.log('submitParam', param);
@@ -99,11 +102,6 @@
         ElMessage.success('更新成功');
         getCameraAlgoList(cameraId);
       });
-    } else {
-      createCameraAlgoApi(newParam).then(() => {
-        ElMessage.success('保存成功');
-        getCameraAlgoList(cameraId);
-      });
     }
   };
 
@@ -112,6 +110,7 @@
     deleteCameraAlgoApi({ algoId, cameraId: cameraDetailStore.cameraId }).then(() => {
       ElMessage.success('删除成功');
       getCameraAlgoList(cameraDetailStore.cameraId);
+      selectedAlgoId.value = undefined;
     });
   };
 </script>

+ 14 - 3
src/views/cameras/preview/components/CameraLiveVideo/CameraLiveVideo.vue

@@ -1,12 +1,23 @@
 <template>
-  <img :src="bg" alt="" class="videoLive" />
+  <img
+    :src="bg"
+    alt=""
+    class="videoLive"
+    :style="{
+      width: `${cameraDetailStore.videoSize.width}px`,
+      height: `${cameraDetailStore.videoSize.height}px`,
+    }"
+  />
 </template>
 <script lang="ts" setup>
   import bg from '@/assets/images/camera/video-live.png';
+
+  import useCameraDetailStore from '../../store/useCameraDetailStore';
+  const cameraDetailStore = useCameraDetailStore();
 </script>
 <style scoped>
   .videoLive {
-    width: 720px;
-    height: 405px;
+    /* width: 720px;
+    height: 405px; */
   }
 </style>

+ 60 - 53
src/views/cameras/preview/components/CameraParams/CameraParams.vue

@@ -1,79 +1,86 @@
-<script lang="ts" setup>
-  import { watch, ref } from 'vue';
-
-  const props = defineProps<{
-    detail: any;
-  }>();
-  const cameraParams = ref({
-    imageResolution: '',
-    recordPeriod: null,
-    startTime: '',
-    endTime: '',
-    reservation: '',
-  });
-
-  //   声明时赋初始值
-  //   const form = reactive({
-  //     imageResolution: props.cameraInits.imageResolution,
-  //     recordPeriod: props.cameraInits.recordPeriod,
-  //     startTime: props.cameraInits.startTime,
-  //     endTime: props.cameraInits.endTime,
-  //     reservation: props.cameraInits.reservation,
-  //   });
-
-  watch(
-    () => {
-      return props.detail;
-    },
-    () => {
-      cameraParams.value = props.detail;
-    },
-    {
-      immediate: true,
-    },
-  );
-
-  const emits = defineEmits(['submit']);
-  const onSubmit = () => {
-    emits('submit', cameraParams);
-  };
-</script>
 <template>
-  <el-form :model="cameraParams" label-width="130px" lable-position="left">
+  <el-form :model="cameraDetailStore" label-width="130px" lable-position="left">
     <el-form-item label="分辨率:">
-      <el-select v-model="cameraParams.imageResolution" style="width: 100%">
-        <el-option label="1920" value="1920" />
-        <el-option label="1280" value="1280" />
-        <el-option label="720" value="720" />
+      <el-select
+        v-model="cameraDetailStore.params.imageResolution"
+        style="width: 100%"
+        size="small"
+      >
+        <el-option
+          v-for="x in videoResolutionList"
+          :label="x.label"
+          :value="x.value"
+          :key="x.value"
+        />
       </el-select>
     </el-form-item>
     <el-form-item label="录制周期:">
-      <el-select v-model="cameraParams.recordPeriod" style="width: 100%">
-        <el-option label="10天" value="10天" />
-        <el-option label="5天" value="5天" />
-        <el-option label="1天" value="1天" />
+      <el-select v-model="cameraDetailStore.params.recordPeriod" style="width: 100%" size="small">
+        <el-option label="10天" :value="10" />
+        <el-option label="5天" :value="5" />
+        <el-option label="1天" :value="1" />
       </el-select>
     </el-form-item>
     <el-form-item label="录制时间:">
       <el-col :span="11">
-        <el-time-picker v-model="cameraParams.startTime" style="width: 100%" />
+        <el-time-picker
+          v-model="cameraDetailStore.params.startTime"
+          style="width: 100%"
+          size="small"
+        />
       </el-col>
       <el-col :span="1">
         <span class="text-center">-</span>
       </el-col>
       <el-col :span="11">
-        <el-time-picker v-model="cameraParams.endTime" style="width: 100%" />
+        <el-time-picker
+          v-model="cameraDetailStore.params.endTime"
+          style="width: 100%"
+          size="small"
+        />
       </el-col>
     </el-form-item>
     <el-form-item label="返回预置位:">
-      <el-input v-model="cameraParams.reservation" />
+      <el-input v-model="cameraDetailStore.params.reservation" size="small" />
     </el-form-item>
     <el-form-item>
-      <el-button type="primary" @click="onSubmit" disabled>保存</el-button>
+      <el-button type="primary" @click="onSubmit">保存</el-button>
     </el-form-item>
   </el-form>
 </template>
 
+<script lang="ts" setup>
+  import { getCameraParamsApi, saveCameraParamsApi } from '@/api/camera/camera-preview';
+  import { formatToDateTime } from '@/utils/dateUtil';
+  import { ElMessage } from 'element-plus';
+  import useCameraDetailStore from '../../store/useCameraDetailStore';
+  import { onMounted } from 'vue';
+  import { videoResolutionList } from './types';
+
+  const cameraDetailStore = useCameraDetailStore();
+
+  onMounted(() => {
+    getCameraParamsApi(cameraDetailStore.cameraId).then((res) => {
+      cameraDetailStore.params = res;
+    });
+  });
+
+  const onSubmit = () => {
+    const params = cameraDetailStore.params;
+    const DATE_TIME_STR = 'YYYY-MM-DD HH:mm:ss';
+    const endTime = formatToDateTime(params.endTime, DATE_TIME_STR);
+    const startTime = formatToDateTime(params.startTime, DATE_TIME_STR);
+    saveCameraParamsApi({
+      ...params,
+      startTime,
+      endTime,
+      cameraId: cameraDetailStore.cameraId,
+    }).then((res) => {
+      ElMessage.success('保存成功');
+    });
+  };
+</script>
+
 <style scoped>
   .text-center {
     /* text-align: center; */

+ 10 - 0
src/views/cameras/preview/components/CameraParams/types.ts

@@ -0,0 +1,10 @@
+/** 分辨率的枚举值 */
+export enum VideoResolution {
+  '1920*1080' = 1,
+  '720*405' = 0.375,
+}
+
+export const videoResolutionList = [
+  { label: '1920*1080', value: VideoResolution['1920*1080'] },
+  { label: '720*405', value: VideoResolution['720*405'] },
+];

+ 28 - 18
src/views/cameras/preview/components/CameraViewSetting/CameraViewSetting.vue

@@ -12,11 +12,21 @@
       <PresetSelect />
     </div>
 
-    <div class="cameraViewSettingWrapper">
-      <FenceEditor ref="fenceEditorRef" />
-
-      <div class="cameraVideo"><CameraLiveVideo /></div>
-      <div class="presetAddWrapper" :class="{ hidePresetControlCls: hidePresetControl }">
+    <div
+      class="cameraViewSettingWrapper"
+      :style="{
+        width: `${cameraDetailStore.videoSize.width}px`,
+        height: `${cameraDetailStore.videoSize.height}px`,
+      }"
+    >
+      <FenceEditor
+        ref="fenceEditorRef"
+        :width="cameraDetailStore.videoSize.width"
+        :height="cameraDetailStore.videoSize.height" />
+      <div class="cameraVideo">
+        <CameraLiveVideo />
+      </div>
+      <div class="presetAddWrapper" :class="{ hidePresetControlCls: isEdit }">
         <CameraDirectionControl />
         <ElButton
           type="primary"
@@ -34,13 +44,13 @@
   </div>
   <div class="cameraParamsSettingWrapper">
     <div class="cameraParamsSetting">
-      <CameraParams :detail="cameraParamsDetail" />
+      <CameraParams />
     </div>
     <div class="algorithmsSetting"> <AlgorithmsSetting /> </div>
   </div>
 </template>
 <script lang="ts" setup>
-  import { computed, ref, watch, watchEffect } from 'vue';
+  import { computed, ref, watchEffect } from 'vue';
   import FenceToolbar from '../FenceToolbar/FenceToolbar.vue';
   import FenceEditor from '../FenceEditor/FenceEditor.vue';
   import CameraLiveVideo from '../CameraLiveVideo/CameraLiveVideo.vue';
@@ -69,9 +79,6 @@
   const viewType = ref<ViewType>(ViewType.window1);
 
   const addPresetModalVisible = ref(false);
-  const hidePresetControl = ref(false);
-
-  const cameraParamsDetail = ref({});
 
   const handleClose = () => {
     addPresetModalVisible.value = false;
@@ -83,7 +90,6 @@
   };
 
   const handleRemove = () => {
-    console.log('handleRemove');
     fenceEditorRef.value?.remove();
   };
 
@@ -94,11 +100,8 @@
     isEdit.value = val;
     if (val) {
       fenceEditorRef.value?.setEditMode();
-      // 将预置位的设置按钮隐藏
-      hidePresetControl.value = true;
     } else {
       fenceEditorRef.value?.exitEditMode();
-      hidePresetControl.value = false;
     }
   };
 
@@ -128,7 +131,7 @@
         presetToken,
         electronicFencePolygon: JSON.stringify(json),
       })
-      ?.then((res) => {
+      ?.then(() => {
         ElMessage.success('更新成功');
       });
   };
@@ -166,7 +169,8 @@
       /** 先清空原有的 */
       fenceEditorRef.value?.clear();
       fenceEditorRef.value?.createLines(rawLinePoints);
-      fenceEditorRef.value?.exitEditMode();
+      fenceEditorRef.value?.setEditMode();
+      isEdit.value = true;
       return;
     }
     fenceEditorRef.value?.clear();
@@ -183,12 +187,18 @@
       presetStore.getPresetList(cameraId);
     }
   });
+
+  // watchEffect(() => {
+  //   const scale = cameraDetailStore.params.imageResolution;
+  //   console.log('scale change', scale);
+  //   fenceEditorRef.value?.setScale(scale);
+  // });
 </script>
 <style scoped>
   .cameraViewSettingWrapper {
     position: relative;
-    width: 720px;
-    height: 405px;
+    /* width: 720px;
+    height: 405px; */
     border: 1px solid #ccc;
   }
 

+ 33 - 15
src/views/cameras/preview/components/FenceEditor/FenceEditor.vue

@@ -4,7 +4,7 @@
 
 <script lang="ts" setup>
   import Konva from 'konva';
-  import { ref, onMounted, onUnmounted } from 'vue';
+  import { ref, onMounted, onUnmounted, watch } from 'vue';
   import { GROUP_NAME, POLYGON_NAME, Points, ToolObjectItem, toolObject } from './constants';
   import { ElMessage } from 'element-plus';
   import { getDefaultScale } from './utils';
@@ -37,13 +37,22 @@
     stage?.destroy();
   });
 
+  const props = defineProps<{ width: number; height: number }>();
+
+  watch(
+    () => [props.width, props.height],
+    () => {
+      initKonvaStage();
+    },
+  );
+
   /**
    *初始化konva舞台
    */
   function initKonvaStage() {
     //1实例化stage层
-    stageWidth = mapRef.value?.clientWidth || 0;
-    stageHeight = mapRef.value?.clientHeight || 0;
+    stageWidth = props.width || 0;
+    stageHeight = props.height || 0;
     console.log('stageWidth', stageWidth);
     stage = new Konva.Stage({
       container: 'editorMap',
@@ -52,7 +61,9 @@
       ignoreStroke: true,
       background: '#00ff00',
     });
-    window.stage = stage;
+    if (import.meta.env.MODE === 'development') {
+      window.stage = stage;
+    }
     setStageCursor('pointer');
 
     //2实例化layer层
@@ -504,10 +515,10 @@
     layer?.draw();
   }
   /**
- *多边形
-  @param currentTool
-  * @param points 多边形绘画的各个顶点,类型数组
-  */
+  *多边形
+   @param currentTool
+   * @param points 多边形绘画的各个顶点,类型数组
+   */
   function drawPolygon(currentTool: ToolObjectItem, points: number[], group: Konva.Group) {
     let poly = new Konva.Line({
       name: currentTool.name + 'poly',
@@ -536,7 +547,7 @@
     pParent?.on('mousedown', (e) => {
       if (!isEdit) return;
 
-      console.log('group mouse down');
+      console.log('group mouse down', e);
       if (e.evt.button == 0) {
         //绘画结束
         if (!drawing) {
@@ -549,6 +560,7 @@
           stage?.find('Circle').forEach((element) => {
             element.moveToTop();
           });
+          pParent.moveToTop();
           //添加删除撤销对象
           currentDel = currentDrawingShape;
           setCurrentGroup(poly.getParent() as Konva.Group);
@@ -622,12 +634,13 @@
       currentDel.destroy();
       currentDel = null;
       currentDrawingShape = null;
-      ElMessage({
-        message: '删除成功!',
-        type: 'success',
-        center: true,
-        duration: 1000,
-      });
+      // ElMessage.success({
+      //   message: '删除成功!',
+      //   center: true,
+      //   duration: 1000,
+      // });
+    } else {
+      ElMessage.warning({ message: '请选择要删除的电子围栏' });
     }
     layer?.draw();
   };
@@ -673,6 +686,10 @@
     layer?.removeChildren();
   };
 
+  const setScale = (scale: number) => {
+    stage?.setAttr('scaleX', scale);
+  };
+
   defineExpose({
     remove: removeCurrent,
     toObject,
@@ -682,6 +699,7 @@
     exitEditMode,
     setEditMode,
     clear,
+    setScale,
   });
 </script>
 

+ 2 - 46
src/views/cameras/preview/components/FenceToolbar/FenceToolbar.vue

@@ -1,12 +1,5 @@
 <template>
   <div class="toolbar">
-    <!-- <ElButton>编辑</ElButton>
-    <ElButton @click="remove">删除</ElButton>
-    <ElButton @click="toObject">保存到localStorage</ElButton>
-    <ElButton @click="loadGroup">从local加载group</ElButton>
-    <ElButton @click="toRawObject">保存Raw</ElButton> -->
-    <ToolbarIcon :src="mousePointerIcon" :active="false" />
-    <!-- <ToolbarIcon :src="editIcon" :active="false" @click="emits('setEditable')" /> -->
     <ToolbarIcon :src="deleteIcon" :active="false" @click="emits('remove')" />
     <ToolbarIcon :src="saveIcon" :active="false" @click="emits('save')" />
     <ElButton type="primary" size="small" @click="toggleEdit">{{
@@ -15,57 +8,20 @@
   </div>
 </template>
 <script setup lang="ts">
-  import { ref, defineEmits, watch } from 'vue';
-  import { ElButton, ElSwitch } from 'element-plus';
-  import PolygonEditor from '../FenceEditor/FenceEditor.vue';
-  import { ServerLines } from '../FenceEditor/constants';
+  import { defineEmits } from 'vue';
+  import { ElButton } from 'element-plus';
   import ToolbarIcon from '../ToolbarIcon/ToolbarIcon.vue';
   import saveIcon from '@/assets/images/camera/save.png';
   import deleteIcon from '@/assets/images/camera/delete.png';
-  import mousePointerIcon from '@/assets/images/camera/mousePointer.png';
-  import editIcon from '@/assets/images/camera/pen.png';
-  import useFenceStore from '../../store/useFenceStore';
 
-  const isFenceOn = ref(true);
   const props = defineProps<{ isEdit: boolean }>();
 
-  const polygonEditorRef = ref<typeof PolygonEditor | null>(null);
-
-  const fenceStore = useFenceStore();
   const emits = defineEmits<{
     (e: 'toggleEditable', editState: boolean): unknown;
     (e: 'remove'): unknown;
     (e: 'save'): unknown;
   }>();
 
-  const toObject = () => {
-    const json = polygonEditorRef.value?.toObject();
-    console.log('toObject json', json);
-    localStorage.setItem('mapDataV2', JSON.stringify(json));
-  };
-
-  const toRawObject = () => {
-    const objects = polygonEditorRef.value?.toRawObject();
-    console.log('objects', objects);
-    localStorage.setItem('mapData', JSON.stringify(objects));
-  };
-
-  const loadGroup = () => {
-    const data = localStorage.getItem('mapDataV2');
-    console.log('loadGroup data', data);
-    if (!data) return;
-    const dataJSON = JSON.parse(data) as ServerLines;
-    const groups = dataJSON;
-    const rawLinePoints = groups[0];
-    const points: number[] = [];
-    rawLinePoints.forEach((line) => {
-      points.push(line[0], line[1]);
-    });
-    console.log('points', points);
-
-    polygonEditorRef.value?.createLines(points);
-  };
-
   const toggleEdit = () => {
     emits('toggleEditable', !props.isEdit);
   };

+ 4 - 1
src/views/cameras/preview/store/useCameraAlgoStore.ts

@@ -27,7 +27,10 @@ const useCameraAlgoStore = defineStore('cameraAlgo', () => {
   /** 所有算法列表中选定的算法id */
   const selectedAlgoId = ref<number>();
 
-  const selectedAlgoDetail = ref<CameraAlgoItemInCard>({} as CameraAlgoItemInCard);
+  const selectedAlgoDetail = ref<CameraAlgoItemInCard>({
+    // 复杂结构要加这个,否则v-model的时候会报错
+    detectionJSON: { detectionNum: 0, detectionUnit: 1 },
+  } as CameraAlgoItemInCard);
 
   const getAlgoDetail = (algoId: number): null | CameraAlgoItem => {
     const detail = cameraAlgoList.value?.find((x) => x.algoId === algoId);

+ 31 - 2
src/views/cameras/preview/store/useCameraDetailStore.ts

@@ -3,17 +3,46 @@
 import { CameraDetailServer } from '@/api/camera/camera-overview';
 import { useRouteQuery } from '@vueuse/router';
 import { defineStore } from 'pinia';
-import { ref } from 'vue';
+import { computed, ref } from 'vue';
+
+interface CameraParams {
+  startTime: string;
+  endTime: string;
+  imageResolution: number;
+  recordPeriod: number;
+  /** 这个要调整一下 */
+  reservation: string;
+}
+
+/** 宽/长的比例 */
+export const WIDTH_HEIGHT_RATIO = 0.5625;
+/** 分辨率以1920为基础 */
+export const BASE_RESOLUTION = 1920;
 
 const useCameraDetailStore = defineStore('cameraDetail', () => {
   const cameraId = useRouteQuery('cameraId', '', { transform: (str) => Number(str) });
 
   const detail = ref<CameraDetailServer | null>(null);
 
+  /** 参数设置 */
+  const params = ref<CameraParams>({
+    startTime: '',
+    endTime: '',
+    imageResolution: 1,
+    recordPeriod: 0,
+    reservation: '',
+  });
+
+  const videoSize = computed(() => {
+    const width = params.value.imageResolution * BASE_RESOLUTION;
+    const height = width * WIDTH_HEIGHT_RATIO;
+    return { width, height };
+  });
+
   const setDetail = (newDetail: CameraDetailServer) => {
     detail.value = newDetail;
   };
-  return { detail, setDetail, cameraId };
+  return { detail, setDetail, cameraId, params, videoSize };
 });
 
 export default useCameraDetailStore;