Prechádzať zdrojové kódy

feat: 重构算法管理页面

louhangfei 1 rok pred
rodič
commit
5517893f01
57 zmenil súbory, kde vykonal 598 pridanie a 932 odobranie
  1. 8 0
      src/assets/icons/electronic-fence.svg
  2. 38 54
      src/views/cameras/config/components/AlgorithmsSetting/AlgoParamsCard.vue
  3. 10 37
      src/views/cameras/config/components/AlgorithmsSetting/AlgoPeriodCard.vue
  4. 182 227
      src/views/cameras/config/components/AlgorithmsSetting/AlgoSettingCard.vue
  5. 28 0
      src/views/cameras/config/components/AlgorithmsSetting/types.ts
  6. 60 0
      src/modules/algo/algo-params-edit/use-algo-params-edit-store.ts
  7. 34 1
      src/views/cameras/config/components/AlgorithmsSetting/utils.ts
  8. 12 1
      src/router/full-routes.ts
  9. 3 6
      src/views/cameras/algo-management/algoManagement.vue
  10. 6 0
      src/views/cameras/config/CameraPreview.vue
  11. 0 0
      src/views/cameras/algo-params-setting/components/AddPresetModal/AddPresetModal.vue
  12. 79 0
      src/views/cameras/algo-params-setting/components/AlgoCanSelect/AlgoCanSelect.vue
  13. 0 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoAddBtn.vue
  14. 0 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoDeleteIcon.vue
  15. 0 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoSettingIcon.vue
  16. 15 23
      src/views/cameras/config/components/AlgoSwitchCard/AlgoSwitchCard.vue
  17. 0 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoSwitchCardBase.vue
  18. 2 6
      src/views/cameras/config/components/AlgoSwitchCard/AlgoSwitchIcon.vue
  19. 20 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/ElectronicFenceIcon.vue
  20. 0 0
      src/views/cameras/algo-params-setting/components/AlgoSwitchCard/WithTooltip.vue
  21. 0 0
      src/views/cameras/algo-params-setting/components/AlgorithmsSetting/AddAlgoDialog.vue
  22. 0 0
      src/views/cameras/algo-params-setting/components/AlgorithmsSetting/AlgoTag.vue
  23. 44 98
      src/views/cameras/config/components/AlgorithmsSetting/AlgorithmsSetting.vue
  24. 0 0
      src/views/cameras/algo-params-setting/components/CameraDirectionControl/CameraDirectionControl.vue
  25. 0 0
      src/views/cameras/algo-params-setting/components/CameraDirectionControl/DirectionItem.vue
  26. 0 0
      src/views/cameras/algo-params-setting/components/CameraLiveVideo/CameraLiveVideo.vue
  27. 0 0
      src/views/cameras/algo-params-setting/components/CameraParams/CameraParams.vue
  28. 0 0
      src/views/cameras/algo-params-setting/components/CameraParams/types.ts
  29. 0 0
      src/views/cameras/algo-params-setting/components/CameraTree/CameraTree.vue
  30. 0 0
      src/views/cameras/algo-params-setting/components/CameraTree/CameraTreeOldVersion.vue
  31. 0 0
      src/views/cameras/algo-params-setting/components/CameraViewSetting/CameraViewScale.vue
  32. 54 33
      src/views/cameras/config/components/CameraViewSetting/CameraViewSetting.vue
  33. 0 0
      src/views/cameras/algo-params-setting/components/CameraViewSetting/constants.ts
  34. 0 0
      src/views/cameras/algo-params-setting/components/FenceAppSetting/FenceAppSetting.vue
  35. 0 0
      src/views/cameras/algo-params-setting/components/FenceAppSetting/constants.ts
  36. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditor/FenceEditor.vue
  37. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditor/constants.ts
  38. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditor/utils.ts
  39. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditorV2/FenceEditor.vue
  40. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditorV2/FenceItem.vue
  41. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditorV2/constants.ts
  42. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditorV2/types.ts
  43. 0 0
      src/views/cameras/algo-params-setting/components/FenceEditorV2/utils.ts
  44. 0 0
      src/views/cameras/algo-params-setting/components/FenceToolbar/FenceToolbar.vue
  45. 1 1
      src/views/cameras/config/components/FenceToolbar/ToggleFenceStatus.vue
  46. 0 0
      src/views/cameras/algo-params-setting/components/FenceToolbar/constants.ts
  47. 0 0
      src/views/cameras/algo-params-setting/components/PresetSelect/PresetSelect.vue
  48. 0 0
      src/views/cameras/algo-params-setting/components/RenderSwitch/RenderSwitch.vue
  49. 0 0
      src/views/cameras/algo-params-setting/components/ToolbarIcon/ToolbarIcon.vue
  50. 0 0
      src/views/cameras/algo-params-setting/components/ViewWindowSetting/ViewWindowSetting.vue
  51. 0 0
      src/views/cameras/algo-params-setting/components/ViewWindowSetting/types.ts
  52. 2 37
      src/views/cameras/config/store/useCameraAlgoStore.ts
  53. 0 0
      src/views/cameras/algo-params-setting/store/useCameraDetailStore.ts
  54. 0 0
      src/views/cameras/algo-params-setting/store/useCameraStatus.ts
  55. 0 0
      src/views/cameras/algo-params-setting/store/useFenceStore.ts
  56. 0 0
      src/views/cameras/algo-params-setting/store/usePresetListStore.ts
  57. 0 408
      src/views/cameras/config/components/AlgorithmsSetting/AlgoPeriodCard copy.vue

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 8 - 0
src/assets/icons/electronic-fence.svg


+ 38 - 54
src/views/cameras/config/components/AlgorithmsSetting/AlgoParamsCard.vue

@@ -1,15 +1,6 @@
 <template>
-  <div
-    class="algo-params-card"
-    :class="{ 'algo-params-card-active': markedParamCardIds.includes(props.id) }"
-  >
-    <el-form
-      ref="ruleFormRef"
-      :model="algoParams"
-      :inline="true"
-      :rules="rules"
-      label-width="120px"
-    >
+  <div class="algo-params-card" :class="{ 'algo-params-card-active': markedParamCardIds.includes(props.id) }">
+    <el-form ref="ruleFormRef" :model="algoParams" :inline="true" :rules="rules" label-width="120px">
       <div v-for="item in paramItems">
         <el-form-item v-if="item.type === 'label'" label="检测对象:" prop="label">
           <el-select v-model="algoParams.label" style="width: 186px" @change="handleLabelChange">
@@ -24,7 +15,7 @@
         </el-form-item>
         <el-form-item
           v-if="item.type === 'criticalCount'"
-          :label="labelNameMap[item.label] + '检测数量:'"
+          :label="labelNameMap[item.label] || '' + '检测数量:'"
           :prop="item.prop"
           required
         >
@@ -36,11 +27,12 @@
             style="width: 186px; margin-right: 5px"
             :disabled="!algoParams.label"
             placeholder="请输入检测数量"
+            @change="handleChange"
           />
         </el-form-item>
         <el-form-item
           v-if="item.type === 'confidence'"
-          :label="labelNameMap[item.label] + '置信度:'"
+          :label="labelNameMap[item.label] || '' + '置信度:'"
           :prop="item.prop"
           required
         >
@@ -52,6 +44,7 @@
               :step="1"
               style="width: 128px; margin-right: 6px"
               :disabled="!algoParams.label"
+              @change="handleChange"
             />
 
             <ElInputNumber
@@ -61,13 +54,14 @@
               :step="1"
               style="width: 88px; margin-right: 5px"
               :disabled="!algoParams.label"
+              @change="handleChange"
             />
             <span>%</span>
           </div>
         </el-form-item>
         <el-form-item
           v-if="item.type === 'minArea'"
-          :label="labelNameMap[item.label] + '最小检测面积:'"
+          :label="labelNameMap[item.label] || '' + '最小检测面积:'"
           required
         >
           <el-form-item :prop="item.label ? item.label + '.' + 'min_width' : 'min_width'">
@@ -78,13 +72,11 @@
               :step="1"
               style="width: 88px; margin-right: 5px"
               :disabled="!algoParams.label"
+              @change="handleChange"
             />
             <span>px</span>
           </el-form-item>
-          <el-form-item
-            :prop="item.label ? item.label + '.' + 'min_height' : 'min_height'"
-            style="margin-left: 17px"
-          >
+          <el-form-item :prop="item.label ? item.label + '.' + 'min_height' : 'min_height'" style="margin-left: 17px">
             <ElInputNumber
               v-model="algoParams[item.label ? item.label + '.' + 'min_height' : 'min_height']"
               controls-position="right"
@@ -92,6 +84,7 @@
               :step="1"
               style="width: 88px; margin-right: 5px"
               :disabled="!algoParams.label"
+              @change="handleChange"
             />
             <span>px</span>
           </el-form-item>
@@ -99,37 +92,44 @@
       </div>
     </el-form>
     <div class="paramOptIcons">
-      <!-- <el-icon v-if="isEdit" size="16px" @click="handleSaveParam(ruleFormRef)">
-        <SaveOutline />
-      </el-icon>
-      <el-icon v-else size="16px" @click="isEdit = true"><Edit /></el-icon> -->
-      <el-icon size="16px" @click="deleteParam(props.id)"><Delete /></el-icon>
+      <el-icon size="16px" @click="handleDelete(props.id)"><Delete /></el-icon>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
   import { computed, ref, watch } from 'vue';
-  import useCameraAlgoStore, { AlgoParamMetaItem } from '../../store/useCameraAlgoStore';
+  import useCameraAlgoStore from './use-algo-params-edit-store';
   import { storeToRefs } from 'pinia';
-  import { ElInputNumber, ElMessage, FormInstance } from 'element-plus';
-  import { Delete, Edit } from '@element-plus/icons-vue';
-  import { SaveOutline } from '@vicons/ionicons5';
-  import { labelNameMap } from './types';
+  import { ElInputNumber, FormInstance } from 'element-plus';
+  import { Delete } from '@element-plus/icons-vue';
+  import { labelNameMap, AlgoParamMetaItem } from './types';
   import { getCriticalCounts } from './utils';
 
   const props = defineProps<{
     id: string;
   }>();
 
+  const emits = defineEmits<{
+    (e: 'change'): unknown;
+  }>();
+
+  const handleChange = () => {
+    emits('change');
+  };
+
   const cameraAlgoStore = useCameraAlgoStore();
   const { selectedAlgoDetail, metaObjList, markedParamCardIds } = storeToRefs(cameraAlgoStore);
   const { deleteParam } = cameraAlgoStore;
 
   const ruleFormRef = ref<FormInstance>();
-  //是否在编辑
-  const isEdit = ref(false);
 
+  const handleDelete = (id: string) => {
+    handleChange();
+    deleteParam(id);
+  };
+
+  //get label list for select, exclude current label and label with nextObj
   const getLabelList = () => {
     return metaObjList.value.map((item) => {
       const entry = selectedAlgoDetail.value.metaValues.find(
@@ -144,14 +144,12 @@
   };
 
   const handleLabelChange = () => {
-    markedParamCardIds.value = selectedAlgoDetail.value.metaValues
-      .filter((item) => !item.label)
-      .map((item) => item.id);
+    handleChange();
+    markedParamCardIds.value = selectedAlgoDetail.value.metaValues.filter((item) => !item.label).map((item) => item.id);
   };
 
   const algoParams = ref<AlgoParamMetaItem>(
-    selectedAlgoDetail.value.metaValues?.find((item) => item.id === props.id) ||
-      ({} as AlgoParamMetaItem),
+    selectedAlgoDetail.value.metaValues?.find((item) => item.id === props.id) || ({} as AlgoParamMetaItem),
   );
 
   const paramItems = ref([
@@ -192,8 +190,7 @@
       if (selectedAlgoDetail.value.metaValues[index]['min_width']) {
         const countList = getCriticalCounts(selectedAlgoDetail.value.extra);
         algoParams.value = selectedAlgoDetail.value.metaValues[index];
-        algoParams.value.criticalCount =
-          countList && countList.length > index ? countList[index] : 0;
+        algoParams.value.criticalCount = countList && countList.length > index ? countList[index] : 0;
       } else {
         algoParams.value.confidence = Number((meta.confidence * 100).toFixed(0));
         algoParams.value['min_width'] = meta['min_width'];
@@ -259,7 +256,7 @@
     return rule;
   });
 
-  const integerJudge = (rule, value, callback) => {
+  const integerJudge = (_, value, callback) => {
     // 整数校验逻辑
     const pattern = /^-?\d+$/;
     if (pattern.test(value)) {
@@ -269,21 +266,10 @@
     }
   };
 
-  const handleSaveParam = async (formEl: FormInstance | undefined) => {
-    if (!formEl) return;
-    await formEl.validate((valid, fields) => {
-      if (valid) {
-        isEdit.value = false;
-      } else {
-        ElMessage.error('保存失败,请检查填写');
-      }
-    });
-  };
-
   const checkValid = async () => {
     if (!ruleFormRef.value) return false;
     let isValid = true;
-    await ruleFormRef.value.validate((valid, fields) => {
+    await ruleFormRef.value.validate((valid) => {
       if (valid) {
       } else {
         isValid = false;
@@ -292,10 +278,6 @@
     return isValid;
   };
 
-  const setEditable = (value: boolean) => {
-    isEdit.value = value;
-  };
-
   defineExpose({
     checkValid,
   });
@@ -333,11 +315,13 @@
   :deep(.el-form--inline .el-form-item) {
     margin-bottom: 10px;
     margin-right: 10px;
+    align-items: center;
   }
 
   :deep(.el-form-item__label) {
     line-height: 16px;
     font-size: 14px;
+    align-items: center;
   }
 
   :deep(.el-form--inline .el-form-item) {

+ 10 - 37
src/views/cameras/config/components/AlgorithmsSetting/AlgoPeriodCard.vue

@@ -1,12 +1,7 @@
 <template>
   <div class="periodCard" :class="{ 'periodCard-active': markedTimeRangeIds.includes(props.id) }">
     <div class="dayRrange">
-      <el-select
-        class="daySelect"
-        v-model="timeItem.startDay"
-        placeholder="开始日期"
-        @change="changeDay"
-      >
+      <el-select class="daySelect" v-model="timeItem.startDay" placeholder="开始日期" @change="changeDay">
         <el-option
           v-for="item in dayOptions"
           :key="item.value"
@@ -17,12 +12,7 @@
         </el-option>
       </el-select>
       <div class="divider">-</div>
-      <el-select
-        class="daySelect"
-        v-model="timeItem.endDay"
-        placeholder="结束日期"
-        @change="changeDay"
-      >
+      <el-select class="daySelect" v-model="timeItem.endDay" placeholder="结束日期" @change="changeDay">
         <el-option
           v-for="item in dayOptions"
           :key="item.value"
@@ -39,7 +29,6 @@
           v-model="item.startTime"
           format="HH:mm"
           value-format="HH:mm"
-          :teleported="false"
           :editable="false"
           placeholder="开始时间"
           :disabled-hours="() => disabledHours(item.id, 0)"
@@ -52,7 +41,6 @@
           v-model="item.endTime"
           format="HH:mm"
           value-format="HH:mm"
-          :teleported="false"
           :editable="false"
           placeholder="结束时间"
           :disabled-hours="() => disabledHours(item.id, 1)"
@@ -61,12 +49,7 @@
           @blur="handleBlur(item.id)"
         />
       </div>
-      <el-icon
-        size="18px"
-        color="#d0d0d0"
-        @click="handleDeleteTimeRange(item.id)"
-        style="cursor: pointer"
-      >
+      <el-icon size="18px" color="#d0d0d0" @click="handleDeleteTimeRange(item.id)" style="cursor: pointer">
         <CircleClose />
       </el-icon>
     </div>
@@ -83,7 +66,7 @@
   import { computed, ref } from 'vue';
   import { TimePeriodItem } from './types';
   import { Plus, Delete, CircleClose } from '@element-plus/icons-vue';
-  import useCameraAlgoStore from '../../store/useCameraAlgoStore';
+  import useCameraAlgoStore from './use-algo-params-edit-store';
   import { storeToRefs } from 'pinia';
   import { ElMessage } from 'element-plus';
   import { uid } from 'uid';
@@ -130,18 +113,14 @@
   const openTimeType = ref<number | null>();
 
   const timeItem = computed(
-    () =>
-      selectedAlgoDetail.value.timeRangeArr.find((item) => item.id === props.id) ||
-      ({} as TimePeriodItem),
+    () => selectedAlgoDetail.value.timeRangeArr.find((item) => item.id === props.id) || ({} as TimePeriodItem),
   );
 
   const changeDay = () => {
     if (timeItem.value.startDay && timeItem.value.endDay) {
       let isConflict = false;
       //判断日期是否冲突
-      const otherDays = selectedAlgoDetail.value.timeRangeArr.filter(
-        (item) => item.id !== props.id,
-      );
+      const otherDays = selectedAlgoDetail.value.timeRangeArr.filter((item) => item.id !== props.id);
       for (let i = 0; i < otherDays.length; i++) {
         const dayRange = otherDays[i];
         if (
@@ -151,10 +130,7 @@
           isConflict = true;
           break;
         }
-        if (
-          timeItem.value.startDay < dayRange.startDay! &&
-          timeItem.value.endDay > dayRange.endDay!
-        ) {
+        if (timeItem.value.startDay < dayRange.startDay! && timeItem.value.endDay > dayRange.endDay!) {
           isConflict = true;
           break;
         }
@@ -294,14 +270,13 @@
   };
 
   const handleDeleteTimeRange = (curId: string) => {
+    debugger;
     timeItem.value.timeRangeList = timeItem.value.timeRangeList.filter((item) => item.id !== curId);
     markedTimes.value = markedTimes.value.filter((id) => id !== curId);
   };
 
   const handleAddTimeRange = () => {
-    const emptyList = timeItem.value.timeRangeList.filter(
-      (item) => item.startTime === '' || item.endTime === '',
-    );
+    const emptyList = timeItem.value.timeRangeList.filter((item) => item.startTime === '' || item.endTime === '');
     if (emptyList && emptyList.length > 0) {
       emptyList.forEach((time) => markedTimes.value.push(time.id));
       ElMessage.error('请填写完整时间段');
@@ -353,9 +328,7 @@
     if (markedTimes.value.length > 0) {
       return 0; // 存在冲突时间段
     }
-    const emptyTimes = timeItem.value.timeRangeList.filter(
-      (item) => !item.startTime || !item.endTime,
-    );
+    const emptyTimes = timeItem.value.timeRangeList.filter((item) => !item.startTime || !item.endTime);
     if (emptyTimes && emptyTimes.length > 0) {
       emptyTimes.forEach((val) => markedTimes.value.push(val.id));
       return 1; // 存在缺失时间段

+ 182 - 227
src/views/cameras/config/components/AlgorithmsSetting/AlgoSettingCard.vue

@@ -1,158 +1,114 @@
 <template>
   <div id="algoCardWrapper" class="algoCardWrapper">
     <div class="algoCardTitle">
-      <div>{{ selectedAlgoDetail?.algoInfo?.name }} 算法参数配置</div>
-      <div style="display: flex; align-items: center">
-        <ElSwitch
-          v-model="selectedAlgoDetail.enableCardBool"
-          size="small"
-          @change="handleAlgoEnable"
-        />
-        <el-tooltip class="box-item" effect="dark" placement="top">
-          <template #content> 关闭后该算法对<br />此台相机不生效</template>
-          <el-icon color="#d4d5d8" :size="16" class="tipIcon"><InfoCircleOutlined /></el-icon>
-        </el-tooltip>
-      </div>
+      <div>算法参数</div>
     </div>
     <div class="algoCardMain">
-      <!-- <div class="algoRow" style="display: flex; align-items: center">
-        <div class="algoLabel">绘制电子围栏:</div>
-        <div>
-          <div style="display: flex; align-items: center">
-            <ElSwitch v-model="selectedAlgoDetail.electronicFenceBool" size="small" />
-            <el-tooltip class="box-item" effect="dark" placement="top">
-              <template #content>
-                默认检测全部范围,如需<br />指定范围,可打开开关,<br />在相机界面完成绘制
-              </template>
-              <el-icon color="#e2e2e2" :size="16" class="tipIcon"><InfoCircleOutlined /></el-icon>
-            </el-tooltip>
-            <el-radio-group
-              v-model="selectedAlgoDetail.regionJudge"
-              :disabled="!selectedAlgoDetail.electronicFenceBool"
-              class="ml-3"
+      <div style="display: flex">
+        <div style="margin-right: 30px; display: flex; margin-top: 16px">
+          <div class="algoLabel">检测元数据:</div>
+          <div class="algoTimeContent">
+            <div
+              class="timeAdd"
+              :class="{ addDisable: unEmptyLabels.length >= metaObjList.length }"
+              @click="handleAddMetaObj"
             >
-              <el-radio :label="0">内</el-radio>
-              <el-radio :label="1">外</el-radio>
-            </el-radio-group>
+              <el-icon color="#d0d0d0"><Plus /></el-icon>
+            </div>
+            <div class="metaValuesList">
+              <AlgoParamsCard
+                v-for="(v, index) in selectedAlgoDetail.metaValues"
+                :key="v.id"
+                :ref="(el) => (paramCardRefs[index] = el)"
+                :id="v.id"
+                @change="handleChange"
+              />
+            </div>
           </div>
-          <div class="presetList"> 预置位1 预置位2 预置位3 </div>
         </div>
-      </div> -->
-      <div class="algoRow">
-        <div class="algoLabel">检测时间段:</div>
-        <div class="algoTimeContent">
-          <div class="timeAdd" @click="handleAddTimePeriod">
-            <el-icon color="#d0d0d0"><Plus /></el-icon>
+        <div>
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">检测数量范围:</div>
+            <el-radio-group v-model="selectedAlgoDetail.judge" class="ml-4" @change="handleChange">
+              <el-radio :label="0">小于</el-radio>
+              <el-radio :label="1">大于</el-radio>
+              <el-radio :label="2">等于</el-radio>
+            </el-radio-group>
           </div>
-          <div class="timeList">
-            <PeriodCard
-              v-for="(x, index) in selectedAlgoDetail.timeRangeArr"
-              :key="x.id"
-              :ref="(el) => (periodCardRefs[index] = el)"
-              :id="x.id"
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">检测频率:</div>
+            <ElInputNumber
+              v-model="selectedAlgoDetail.detectionFrequency"
+              controls-position="right"
+              :min="1"
+              :step="1"
+              size="small"
+              style="width: 186px"
+              @blur="checkFrequencyValid"
+              @change="handleChange"
             />
+            <span style="font-size: 12px; margin-left: 5px">S/次</span>
           </div>
-        </div>
-      </div>
-      <div class="algoRow">
-        <div class="algoLabel">检测元数据:</div>
-        <div class="algoTimeContent">
-          <div
-            class="timeAdd"
-            :class="{ addDisable: unEmptyLabels.length >= metaObjList.length }"
-            @click="handleAddMetaObj"
-          >
-            <el-icon color="#d0d0d0"><Plus /></el-icon>
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">持续时间:</div>
+            <ElInputNumber
+              v-model="selectedAlgoDetail.eventDurationMinMs"
+              controls-position="right"
+              :min="1"
+              :step="1"
+              size="small"
+              style="width: 186px"
+              @blur="checkDurationMinMsValid"
+              @change="handleChange"
+            />
+            <span style="font-size: 12px; margin-left: 5px">毫秒</span>
+          </div>
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">持续帧数:</div>
+            <ElInputNumber
+              v-model="selectedAlgoDetail.eventDurationMinFrames"
+              controls-position="right"
+              :min="1"
+              :step="1"
+              size="small"
+              style="width: 186px"
+              @blur="checkDurationMinFramesValid"
+              @change="handleChange"
+            />
+            <span style="font-size: 12px; margin-left: 5px">帧</span>
+          </div>
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">报警时间间隔:</div>
+            <ElInputNumber
+              v-model="selectedAlgoDetail.eventAlarmIntervalMs"
+              controls-position="right"
+              :min="1"
+              :step="1"
+              size="small"
+              style="width: 186px"
+              @blur="checkAlarmIntervalMsValid"
+              @change="handleChange"
+            />
+            <span style="font-size: 12px; margin-left: 5px">秒</span>
           </div>
-          <div class="timeList">
-            <ParamCard
-              v-for="(v, index) in selectedAlgoDetail.metaValues"
-              :key="v.id"
-              :ref="(el) => (paramCardRefs[index] = el)"
-              :id="v.id"
+          <div class="algoRow" style="align-items: center">
+            <div class="algoLabel">报警帧间隔:</div>
+            <ElInputNumber
+              v-model="selectedAlgoDetail.eventAlarmIntervalFrames"
+              controls-position="right"
+              :min="1"
+              :step="1"
+              size="small"
+              style="width: 186px"
+              @blur="checkAlarmIntervalFramesValid"
+              @change="handleChange"
             />
+            <span style="font-size: 12px; margin-left: 5px">帧</span>
           </div>
         </div>
       </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">检测数量范围:</div>
-        <el-radio-group v-model="selectedAlgoDetail.judge" class="ml-4">
-          <el-radio :label="0">小于</el-radio>
-          <el-radio :label="1">大于</el-radio>
-          <el-radio :label="2">等于</el-radio>
-        </el-radio-group>
-      </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">检测频率:</div>
-        <ElInputNumber
-          v-model="selectedAlgoDetail.detectionFrequency"
-          controls-position="right"
-          :min="1"
-          :step="1"
-          size="small"
-          style="width: 186px"
-          @blur="checkFrequencyValid"
-        />
-        <span style="font-size: 12px; margin-left: 5px">S/次</span>
-      </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">持续时间:</div>
-        <ElInputNumber
-          v-model="selectedAlgoDetail.eventDurationMinMs"
-          controls-position="right"
-          :min="1"
-          :step="1"
-          size="small"
-          style="width: 186px"
-          @blur="checkDurationMinMsValid"
-        />
-        <span style="font-size: 12px; margin-left: 5px">毫秒</span>
-      </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">持续帧数:</div>
-        <ElInputNumber
-          v-model="selectedAlgoDetail.eventDurationMinFrames"
-          controls-position="right"
-          :min="1"
-          :step="1"
-          size="small"
-          style="width: 186px"
-          @blur="checkDurationMinFramesValid"
-        />
-        <span style="font-size: 12px; margin-left: 5px">帧</span>
-      </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">报警时间间隔:</div>
-        <ElInputNumber
-          v-model="selectedAlgoDetail.eventAlarmIntervalMs"
-          controls-position="right"
-          :min="1"
-          :step="1"
-          size="small"
-          style="width: 186px"
-          @blur="checkAlarmIntervalMsValid"
-        />
-        <span style="font-size: 12px; margin-left: 5px">秒</span>
-      </div>
-      <div class="algoRow" style="align-items: center">
-        <div class="algoLabel">报警帧间隔:</div>
-        <ElInputNumber
-          v-model="selectedAlgoDetail.eventAlarmIntervalFrames"
-          controls-position="right"
-          :min="1"
-          :step="1"
-          size="small"
-          style="width: 186px"
-          @blur="checkAlarmIntervalFramesValid"
-        />
-        <span style="font-size: 12px; margin-left: 5px">帧</span>
-      </div>
 
-      <div
-        v-if="selectedAlgoDetail.timeWindow !== undefined"
-        class="algoRow"
-        style="align-items: center"
-      >
+      <div v-if="selectedAlgoDetail.timeWindow !== undefined" class="algoRow" style="align-items: center">
         <div class="algoLabel">检测窗口时长:</div>
         <ElInputNumber
           v-model="selectedAlgoDetail.timeWindow"
@@ -162,50 +118,55 @@
           style="width: 186px"
           placeholder="请输入允许的最长时间"
           @blur="checkTimeWindowValid"
+          @change="handleChange"
         />
         <span style="font-size: 12px; margin-left: 5px">S</span>
       </div>
+
+      <div class="algoRow" style="margin-top: 20px">
+        <div class="algoLabel">检测时间段:</div>
+        <div class="algoTimeContent">
+          <div class="timeAdd" @click="handleAddTimePeriod">
+            <el-icon color="#d0d0d0"><Plus /></el-icon>
+          </div>
+          <div class="timeList" v-if="selectedAlgoDetail.timeRangeArr?.length > 0">
+            <PeriodCard
+              v-for="(x, index) in selectedAlgoDetail.timeRangeArr"
+              :key="x.id"
+              :ref="(el) => (periodCardRefs[index] = el)"
+              :id="x.id"
+              @change="handleChange"
+            />
+          </div>
+        </div>
+      </div>
       <div style="display: flex; justify-content: flex-end">
-        <ElButton size="small" :disabled="!selectedAlgoId" @click="handleRemoveAlgo">取消</ElButton>
-        <ElButton size="small" type="primary" @click="handleSave(false)" :disabled="!selectedAlgoId"
-          >保存</ElButton
-        >
+        <ElButton size="small" @click="handleRemoveAlgo">取消</ElButton>
+        <ElButton size="small" type="primary" @click="handleSave">保存</ElButton>
       </div>
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-  import {
-    ElSelect,
-    ElOption,
-    ElSwitch,
-    ElInputNumber,
-    ElTimePicker,
-    ElMessage,
-    ElMessageBox,
-  } from 'element-plus';
-  import { CircleCloseFilled, Plus } from '@element-plus/icons-vue';
+  import { ElInputNumber, ElMessage, ElMessageBox } from 'element-plus';
+  import { Plus } from '@element-plus/icons-vue';
   import { storeToRefs } from 'pinia';
-  import dayjs, { Dayjs } from 'dayjs';
-  import addTimeIcon from '@/assets/icons/addTimeIcon.png';
-  import useCameraAlgoStore, { AlgoParamMetaItem } from '../../store/useCameraAlgoStore';
-  import { createDefaultTime, frequencyOptions, getTimeCompletion } from './utils';
-  import { InfoCircleOutlined } from '@vicons/antd';
+  // import useCameraAlgoStore, { AlgoParamMetaItem } from './store/useCameraAlgoStore';
+  import useAlgoParamsSettingStore from './use-algo-params-edit-store';
+  import { AlgoParamMetaItem, CameraAlgoItemInCard } from './types';
+
+  import { createDefaultTime, getTimeCompletion } from './utils';
+
   import { ALGO_ENABLED_STATUS, FENCE_ENBALED_STATUS } from '@/api/camera/camera-preview';
   import PeriodCard from './AlgoPeriodCard.vue';
-  import ParamCard from './AlgoParamsCard.vue';
+  import AlgoParamsCard from './AlgoParamsCard.vue';
   import { uid } from 'uid';
-  import { computed, ref } from 'vue';
+  import { computed, onMounted, onUnmounted, ref } from 'vue';
 
   // const { data: algoList, loading } = useAllAlgos();
-  const cameraAlgoStore = useCameraAlgoStore();
-  const {
-    selectedAlgoId,
-    selectedAlgoDetail,
-    metaObjList,
-    markedTimeRangeIds,
-    markedParamCardIds,
-  } = storeToRefs(cameraAlgoStore);
+  const algoParamsSettingStore = useAlgoParamsSettingStore();
+  const { selectedAlgoDetail, metaObjList, markedTimeRangeIds, markedParamCardIds } =
+    storeToRefs(algoParamsSettingStore);
 
   interface Param {
     /** 算法id */
@@ -220,38 +181,50 @@
     status: ALGO_ENABLED_STATUS;
   }
 
+  const props = defineProps<{
+    isChanged: boolean;
+    algoDetail: CameraAlgoItemInCard;
+  }>();
+
   const emits = defineEmits<{
     (e: 'onSubmit', param: Param): Promise<unknown>;
-    (e: 'onCancel', algoId: number): Promise<unknown>;
+    (e: 'onCancel'): Promise<unknown>;
+    (e: 'change'): unknown;
   }>();
 
   const periodCardRefs = ref<any>([]);
   const paramCardRefs = ref<any>([]);
 
+  onMounted(() => {
+    selectedAlgoDetail.value = { ...props.algoDetail };
+  });
+  const handleChange = () => {
+    emits('change');
+  };
   const handleAddTimePeriod = () => {
-    const emptyList = selectedAlgoDetail.value.timeRangeArr.filter(
-      (item) => !getTimeCompletion(item),
-    );
+    const emptyList = selectedAlgoDetail.value.timeRangeArr?.filter((item) => !getTimeCompletion(item));
     if (emptyList && emptyList.length > 0) {
       emptyList.forEach((item) => markedTimeRangeIds.value.push(item.id));
       ElMessage.error('请先完善检测时间段数据');
       return;
     }
-    selectedAlgoDetail.value.timeRangeArr.push(createDefaultTime());
+    handleChange();
+    selectedAlgoDetail.value.timeRangeArr?.push(createDefaultTime());
   };
 
   const unEmptyLabels = computed(() => {
-    return selectedAlgoDetail.value.metaValues.filter((item) => item.label);
+    return selectedAlgoDetail.value.metaValues?.filter((item) => item.label) || [];
   });
 
   const handleAddMetaObj = () => {
-    if (unEmptyLabels.value.length < selectedAlgoDetail.value.metaValues.length) {
+    if (unEmptyLabels.value.length < selectedAlgoDetail.value.metaValues?.length) {
       selectedAlgoDetail.value.metaValues
         .filter((item) => !item.label)
         .forEach((val) => markedParamCardIds.value.push(val.id));
       ElMessage.error('存在未完善的检测元数据');
       return;
     }
+    handleChange();
     // if (unemptyList.length == metaObjList.value.length) {
     //   ElMessage.warning('暂无更多检测对象');
     //   return;
@@ -259,61 +232,33 @@
     selectedAlgoDetail.value.metaValues.push({ id: uid() } as AlgoParamMetaItem);
   };
 
-  const removeTime = (id: string) => {
-    const timeRangeArr = selectedAlgoDetail.value.timeRangeArr;
-    selectedAlgoDetail.value.timeRangeArr = timeRangeArr.filter((x) => x.id !== id);
-  };
-
-  const getTimeStr = (d: Dayjs) => {
-    return dayjs(d).format('HH:mm:ss');
-  };
-  const getTimeStrs = (d: [Dayjs, Dayjs]) => {
-    return getTimeStr(d[0]) + '-' + getTimeStr(d[1]);
-  };
-
-  const handleAlgoEnable = () => {
-    handleSave(true);
-  };
-
   const checkFrequencyValid = () => {
-    selectedAlgoDetail.value.detectionFrequency = Math.ceil(
-      selectedAlgoDetail.value.detectionFrequency,
-    );
+    selectedAlgoDetail.value.detectionFrequency = Math.ceil(selectedAlgoDetail.value.detectionFrequency);
   };
   const checkDurationMinMsValid = () => {
-    selectedAlgoDetail.value.eventDurationMinMs = Math.ceil(
-      selectedAlgoDetail.value.eventDurationMinMs,
-    );
+    selectedAlgoDetail.value.eventDurationMinMs = Math.ceil(selectedAlgoDetail.value.eventDurationMinMs);
   };
   const checkDurationMinFramesValid = () => {
-    selectedAlgoDetail.value.eventDurationMinFrames = Math.ceil(
-      selectedAlgoDetail.value.eventDurationMinFrames,
-    );
+    selectedAlgoDetail.value.eventDurationMinFrames = Math.ceil(selectedAlgoDetail.value.eventDurationMinFrames);
   };
   const checkAlarmIntervalMsValid = () => {
-    selectedAlgoDetail.value.eventAlarmIntervalMs = Math.ceil(
-      selectedAlgoDetail.value.eventAlarmIntervalMs,
-    );
+    selectedAlgoDetail.value.eventAlarmIntervalMs = Math.ceil(selectedAlgoDetail.value.eventAlarmIntervalMs);
   };
   const checkAlarmIntervalFramesValid = () => {
-    selectedAlgoDetail.value.eventAlarmIntervalFrames = Math.ceil(
-      selectedAlgoDetail.value.eventAlarmIntervalFrames,
-    );
+    selectedAlgoDetail.value.eventAlarmIntervalFrames = Math.ceil(selectedAlgoDetail.value.eventAlarmIntervalFrames);
   };
 
   const checkTimeWindowValid = () => {
     selectedAlgoDetail.value.timeWindow = Math.ceil(selectedAlgoDetail.value.timeWindow || 1);
   };
 
-  const handleSave = async (isSwitch: boolean) => {
+  const handleSave = async () => {
     //判断时间段是否合格
     if (markedTimeRangeIds.value.length > 0) {
       ElMessage.error('请正确填写检测时间段');
       return;
     }
-    const emptyTimes = selectedAlgoDetail.value.timeRangeArr.filter(
-      (item) => !item.startDay || !item.endDay,
-    );
+    const emptyTimes = selectedAlgoDetail.value.timeRangeArr.filter((item) => !item.startDay || !item.endDay);
     if (emptyTimes && emptyTimes.length > 0) {
       emptyTimes.forEach((item) => {
         markedTimeRangeIds.value.push(item.id);
@@ -355,7 +300,7 @@
     //判断时间段和元数据都有值
     const detail = selectedAlgoDetail.value;
     if (!detail) return;
-    const timeRanges = detail.timeRangeArr;
+    // const timeRanges = detail.timeRangeArr;
     // if (timeRanges.length == 0) {
     //   ElMessage.error('至少添加一个检测时间段');
     //   return;
@@ -393,7 +338,6 @@
     });
     const param = {
       id: detail.id,
-      isSwitch,
       inferCode: detail.inferCode,
       algoId: detail.algoId,
       detectionFrequency: detail.detectionFrequency,
@@ -406,9 +350,7 @@
       metaObjs,
       criticalCounts: metaValues.map((item) => item.criticalCount),
       judge: detail.judge,
-      electronicFence: detail.electronicFenceBool
-        ? FENCE_ENBALED_STATUS.enabled
-        : FENCE_ENBALED_STATUS.disabled,
+      electronicFence: detail.electronicFenceBool ? FENCE_ENBALED_STATUS.enabled : FENCE_ENBALED_STATUS.disabled,
       status: detail.enableCardBool ? ALGO_ENABLED_STATUS.enabled : ALGO_ENABLED_STATUS.disabled,
     } as any;
     if (detail.timeWindow) {
@@ -419,31 +361,34 @@
   };
 
   const handleRemoveAlgo = () => {
-    if (!selectedAlgoId.value) return;
     const el = document.getElementById('algoCardWrapper') as HTMLElement;
-    ElMessageBox.confirm(
-      '<strong>确认取消算法配置吗?</strong><br />取消后配置的参数将不会被保存。',
-      '',
-      {
+    if (props.isChanged) {
+      ElMessageBox.confirm('<strong>确认取消算法配置吗?</strong><br />取消后配置的参数将不会被保存。', '', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning',
         dangerouslyUseHTMLString: true,
         appendTo: el,
-      },
-    )
-      .then(() => {
-        emits('onCancel', selectedAlgoId.value!);
       })
-      .catch(() => {});
+        .then(() => {
+          emits('onCancel');
+        })
+        .catch(() => {});
+    } else {
+      emits('onCancel');
+    }
   };
+
+  onUnmounted(() => {
+    algoParamsSettingStore.clear();
+  });
 </script>
 <style scoped>
   .algoCardWrapper {
     border: 1px solid #ccc;
     border-radius: 4px;
     /* padding: 10px; */
-    width: 780px;
+    width: 990px;
   }
 
   .algoRow {
@@ -455,6 +400,8 @@
     margin-right: 10px;
     width: 90px;
     text-align: right;
+    flex-shrink: 0;
+    flex-grow: 0;
   }
   .algoTimeContent {
     display: flex;
@@ -487,7 +434,7 @@
 
   .timeAdd {
     width: 28px;
-    height: 90px;
+    height: 50px;
     background: #ebebeb;
     border-radius: 4px;
     border: 1px dashed #00000026;
@@ -503,7 +450,15 @@
   }
 
   .timeList {
-    width: 610px;
+    overflow-y: auto;
+    display: flex;
+    flex-wrap: wrap;
+    align-items: flex-start;
+  }
+  .metaValuesList {
+    width: 425px;
+    max-height: 350px;
+    overflow-y: auto;
     display: flex;
     flex-wrap: wrap;
     align-items: flex-start;

+ 28 - 0
src/views/cameras/config/components/AlgorithmsSetting/types.ts

@@ -1,4 +1,5 @@
 import { Dayjs } from 'dayjs';
+import { CameraAlgoItem } from '@/api/camera/camera-preview';
 
 export interface TimeRangeItem {
   id: string;
@@ -44,3 +45,30 @@ export const labelNameMap = {
   planeWing: '机翼',
   planeTail: '机尾',
 };
+
+export interface CameraAlgoItemInCard extends CameraAlgoItem {
+  // detectionJSON: DetectionJSON;
+  inferCode: string;
+  enableCardBool: boolean;
+  electronicFenceBool: boolean;
+  regionJudge: number;
+  timeRangeArr: TimePeriodItem[];
+  metaValues: AlgoParamMetaItem[];
+  judge: number; // 0-小于 1-大于 2-等于
+  detectionFrequency: number;
+  eventDurationMinMs: number;
+  eventDurationMinFrames: number;
+  eventAlarmIntervalMs: number;
+  eventAlarmIntervalFrames: number;
+  timeWindow?: number;
+}
+
+export interface AlgoParamMetaItem {
+  id: string;
+  label: string;
+  criticalCount: number;
+  confidence: number;
+  min_width: number;
+  max_width: number;
+  nextObjs: AlgoParamMetaItem[];
+}

+ 60 - 0
src/modules/algo/algo-params-edit/use-algo-params-edit-store.ts

@@ -0,0 +1,60 @@
+import { defineStore } from 'pinia';
+import { computed, ref } from 'vue';
+import { CameraAlgoItemInCard } from '@/modules/algo/algo-params-edit/types';
+
+const defaultSelectedAlgoDetail = {
+  // detectionJSON: { detectionNum: 0, detectionUnit: 1 },
+  regionJudge: 0,
+};
+
+const useAlgoParamsEditStore = defineStore('algoParamsEdit', () => {
+  // 标记的paramCard集合
+  const markedParamCardIds = ref<string[]>([]);
+
+  // 标记的timeRange集合
+  const markedTimeRangeIds = ref<string[]>([]);
+
+  const selectedAlgoDetail = ref<CameraAlgoItemInCard>({
+    ...defaultSelectedAlgoDetail,
+  } as CameraAlgoItemInCard);
+
+  //计算原始模板数据
+  const metaObjList = computed(() => {
+    const extra = selectedAlgoDetail.value.algoInfo?.extra;
+    if (!extra) return [];
+    const extraObj = JSON.parse(extra);
+    const params = extraObj?.inferParams;
+    if (!params || (params && params.length == 0)) return [];
+    const metaObjs = params[0]?.metaObjs;
+
+    return metaObjs ? metaObjs : [];
+  });
+
+  const deleteParam = (id: string) => {
+    selectedAlgoDetail.value.metaValues = selectedAlgoDetail.value.metaValues.filter((x) => x.id !== id);
+  };
+
+  const deleteTimeRange = (id: string) => {
+    const newTimeRangeArr = selectedAlgoDetail.value.timeRangeArr.filter((item) => item.id !== id);
+    selectedAlgoDetail.value.timeRangeArr = newTimeRangeArr;
+    markedTimeRangeIds.value = markedTimeRangeIds.value.filter((x) => newTimeRangeArr.find((y) => y.id === x));
+  };
+
+  const clear = () => {
+    selectedAlgoDetail.value = { ...defaultSelectedAlgoDetail } as CameraAlgoItemInCard;
+    markedParamCardIds.value = [];
+    markedTimeRangeIds.value = [];
+  };
+
+  return {
+    metaObjList,
+    markedParamCardIds,
+    markedTimeRangeIds,
+    selectedAlgoDetail,
+    clear,
+    deleteParam,
+    deleteTimeRange,
+  };
+});
+
+export default useAlgoParamsEditStore;

+ 34 - 1
src/views/cameras/config/components/AlgorithmsSetting/utils.ts

@@ -3,7 +3,8 @@ import { uid } from 'uid';
 import NP from 'number-precision';
 import { isEqual } from 'lodash-es';
 
-import { TimeRangeItem, TimePeriodItem } from './types';
+import { TimeRangeItem, TimePeriodItem } from '@/modules/algo/algo-params-edit/types';
+
 import { CameraAlgoItem } from '@/api/camera/camera-preview';
 import safeParse from '@/utils/safeParse';
 
@@ -196,3 +197,35 @@ export const getTimeCompletion = (time: TimePeriodItem) => {
   });
   return true;
 };
+
+/** 生成算法相关的提交参数 */
+export const createAlgoSubmitParams = (param, initialAlgoDetail) => {
+  const inferParams = getInferParam(initialAlgoDetail.extra);
+  inferParams.metaObjs = param.metaObjs;
+  inferParams.regionJudge = param.regionJudge;
+  inferParams.criticalCounts = param.criticalCounts;
+  inferParams.judge = param.judge;
+  inferParams.eventDurationMinMs = param.eventDurationMinMs;
+  inferParams.eventDurationMinFrames = param.eventDurationMinFrames;
+  inferParams.eventAlarmIntervalMs = param.eventAlarmIntervalMs;
+  inferParams.eventAlarmIntervalFrames = param.eventAlarmIntervalFrames;
+  inferParams.algoCode = initialAlgoDetail.algoInfo.code;
+  inferParams.algoType = getAlgoType(initialAlgoDetail.algoInfo.extra);
+  if (param.timeWindow) {
+    inferParams.timeWindow = param.timeWindow;
+  }
+  const extraValue = {
+    inferCode: param.inferCode,
+    inferParams: [inferParams],
+  } as any;
+
+  const newParam = {
+    electronicFence: initialAlgoDetail.electronicFence,
+    algoId: initialAlgoDetail.algoId,
+    detectionFrequency: param.detectionFrequency,
+    detectionTime: param.detectionTime,
+    status: initialAlgoDetail.status,
+    extra: JSON.stringify(extraValue),
+  };
+  return newParam;
+};

+ 12 - 1
src/router/full-routes.ts

@@ -219,12 +219,23 @@ const fullRoutes: AppRouteRecordRaw[] = [
         // 算法配置
         path: 'config',
         name: 'AlgorithmConfig',
-        component: '/cameras/AlogoConfig/',
+        component: '/cameras/preview/CameraPreview',
         meta: {
           ico: '',
           title: '算法配置',
         },
       },
+      {
+        // 算法参数设置,也就是设置的详情页
+        path: 'params',
+        name: 'AlgorithmParamsSetting',
+        component: '/cameras/algo-params-setting/AlgoParamsSetting',
+        meta: {
+          ico: '',
+          title: '算法参数设置',
+          activeMenu: 'AlgorithmConfig',
+        },
+      },
     ],
   },
 

+ 3 - 6
src/views/cameras/algo-management/algoManagement.vue

@@ -55,8 +55,7 @@
             </el-scrollbar>
           </div>
           <div class="top_right">
-            <video :src="currentRow?.url" controls autoplay muted style="width: 100%; height: 100%">
-            </video>
+            <video :src="currentRow?.url" controls autoplay muted style="width: 100%; height: 100%"> </video>
           </div>
         </div>
         <div class="right_bottom">
@@ -82,9 +81,7 @@
               />
             </el-form-item>
             <el-form-item>
-              <el-button type="primary" :loading="isSending" @click="onSubmit"
-                >保&nbsp;&nbsp;存</el-button
-              >
+              <el-button type="primary" :loading="isSending" @click="onSubmit">保&nbsp;&nbsp;存</el-button>
             </el-form-item>
           </el-form>
         </div>
@@ -99,7 +96,7 @@
   import useAlgo from './useAlgoData';
   import { Search } from '@element-plus/icons-vue';
   import type { FormInstance, FormRules } from 'element-plus';
-  import { labelNameMap } from '../preview/components/AlgorithmsSetting/types';
+  import { labelNameMap } from '@/modules/algo/algo-params-edit/types';
 
   //调用后端数据
   const algoDatas = useAlgo();

+ 6 - 0
src/views/cameras/config/CameraPreview.vue

@@ -1,3 +1,4 @@
+<!-- 算法参数配置页面 -->
 <template>
   <div>
     <div class="cameraMain">
@@ -93,6 +94,7 @@
   .cameraMain {
     display: flex;
     background: #fff;
+    overflow-x: auto;
     // height: calc(100vh - 90px);
   }
   .cameraTree {
@@ -128,4 +130,8 @@
     color: #fff;
     background-color: #0052d9;
   }
+
+  .cameraView {
+    width: 1230px;
+  }
 </style>

src/views/cameras/config/components/AddPresetModal/AddPresetModal.vue → src/views/cameras/algo-params-setting/components/AddPresetModal/AddPresetModal.vue


+ 79 - 0
src/views/cameras/algo-params-setting/components/AlgoCanSelect/AlgoCanSelect.vue

@@ -0,0 +1,79 @@
+<template>
+  <!-- 模板部分保持不变 -->
+  <div class="algo-select-container">
+    <el-card>
+      <template #header> <div class="card-title">选择相机关联的算法</div> </template>
+      <div class="algo-list">
+        <div
+          v-for="(item, index) in props.algoList"
+          :key="index"
+          class="algo-item"
+          :class="{ active: selectedIds.includes(item.id) }"
+          @click="handleAlgoSelect(item)"
+        >
+          <span class="algo-name">{{ item.name }}</span>
+        </div>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ElCard } from 'element-plus';
+  import { defineEmits } from 'vue';
+  const emit = defineEmits<{
+    (e: 'select', data: number): void;
+  }>();
+  interface AlgoItemLabel {
+    name: string;
+    id: number;
+  }
+
+  const props = defineProps<{ algoList: AlgoItemLabel[]; selectedIds: number[] }>();
+
+  // 处理方法
+  const handleAlgoSelect = (item: AlgoItemLabel) => {
+    const hasId = props.selectedIds.includes(item.id);
+    if (!hasId) {
+      emit('select', item.id);
+    }
+  };
+</script>
+
+<style lang="less" scoped>
+  /* 样式保持不变 */
+  .algo-select-container {
+    margin-left: 10px;
+    width: 250px;
+    .algo-list {
+      // display: flex;
+      // flex-wrap: wrap;
+      // gap: 12px;
+      max-height: 442px;
+      overflow-y: auto;
+    }
+
+    .algo-item {
+      padding: 4px 16px;
+      // border: 1px solid #d9d9d9;
+      margin: 10px 0;
+      border-radius: 4px;
+      cursor: pointer;
+      transition: all 0.3s;
+
+      &:hover {
+        border-color: #1890ff;
+        color: #1890ff;
+      }
+
+      &.active {
+        background: #1890ff;
+        color: #fff;
+        border-color: #1890ff;
+      }
+    }
+  }
+  .card-title {
+    font-weight: bold;
+  }
+</style>

src/views/cameras/config/components/AlgoSwitchCard/AlgoAddBtn.vue → src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoAddBtn.vue


src/views/cameras/config/components/AlgoSwitchCard/AlgoDeleteIcon.vue → src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoDeleteIcon.vue


src/views/cameras/config/components/AlgoSwitchCard/AlgoSettingIcon.vue → src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoSettingIcon.vue


+ 15 - 23
src/views/cameras/config/components/AlgoSwitchCard/AlgoSwitchCard.vue

@@ -4,30 +4,22 @@
       <div>
         <!-- <div>开关 </div> -->
         <AlgoSwitchIcon :active="isAlgoOpen" @click.stop="toggleAlgoStatus" />
-        <el-tooltip :content="label">
+        <el-tooltip :content="label" v-if="label.length > 12">
           <div class="algoName">{{ label }}</div>
         </el-tooltip>
+        <div class="algoName" v-else>{{ label }}</div>
         <div class="toolbar">
+          <div @click.stop="() => {}">
+            <ElectronicFenceIcon :active="isFenceOpen" @click="updateFenceStatus" />
+          </div>
           <AlgoSettingIcon
             class="divideLine algoSettingIcon"
             v-if="hasAlgoSettingMaxPermisson()"
             @click.stop="emits('toggleSetting')"
           />
           <!-- <AlgoSettingIcon class="divideLine algoSettingIcon" /> -->
-          <div @click.stop="() => {}">
-            <el-tooltip :content="isFenceOpen ? '关闭电子围栏' : '开启电子围栏'">
-              <ElSwitch
-                size="small"
-                :modelValue="isFenceOpen"
-                @update:modelValue="updateFenceStatus"
-              />
-            </el-tooltip>
-          </div>
-          <div
-            class="divideLine deleteIcon"
-            @click.stop="emits('remove')"
-            v-if="hasDeletePermission()"
-          >
+
+          <div class="divideLine deleteIcon" @click.stop="emits('remove')" v-if="hasDeletePermission()">
             <el-tooltip content="删除算法">
               <img :src="deletePng" alt="删除" style="width: 16px; height: 16px" />
             </el-tooltip>
@@ -42,9 +34,9 @@
   import AlgoSettingIcon from './AlgoSettingIcon.vue';
   import AlgoSwitchIcon from './AlgoSwitchIcon.vue';
   import deletePng from '@/assets/icons/delete.png';
-  import { ElSwitch } from 'element-plus';
   import { useUserStore } from '@/store/modules/user';
   import { PERM_ALGO } from '@/types/permission/constants';
+  import ElectronicFenceIcon from './ElectronicFenceIcon.vue';
 
   interface Props {
     /** 当前算法是否选中 */
@@ -71,8 +63,8 @@
     emits('toggleAlgo', !props.isAlgoOpen);
   };
 
-  const updateFenceStatus = (fenceStatus: boolean) => {
-    emits('toggleFence', fenceStatus);
+  const updateFenceStatus = () => {
+    emits('toggleFence', !props.isFenceOpen);
   };
 
   const hasDeletePermission = () => userStore.checkPermission(PERM_ALGO.CONFIG_DELETE);
@@ -132,18 +124,18 @@
   }
 
   .deleteIcon {
-    margin-left: 33px;
+    margin-left: 31px;
     flex-shrink: 0;
     cursor: pointer;
     &::before {
-      left: -18px;
+      left: -19px;
     }
   }
   .algoSettingIcon {
-    margin-right: 33px;
-    margin-left: 6px;
+    margin-right: 0;
+    margin-left: 40px;
     &::before {
-      left: 28px;
+      left: -20px;
     }
   }
 </style>

src/views/cameras/config/components/AlgoSwitchCard/AlgoSwitchCardBase.vue → src/views/cameras/algo-params-setting/components/AlgoSwitchCard/AlgoSwitchCardBase.vue


+ 2 - 6
src/views/cameras/config/components/AlgoSwitchCard/AlgoSwitchIcon.vue

@@ -1,20 +1,16 @@
 <template>
   <span class="algo-switch-wrapper">
     <el-tooltip :content="tip" placement="top">
-      <svg-icon icon-name="algo-switch" :color="color" class="algo-switch" />
+      <ElSwitch v-model="props.active" size="small" />
     </el-tooltip>
   </span>
 </template>
 <script lang="ts" setup>
   import { computed } from 'vue';
-  import { ElTooltip } from 'element-plus';
+  import { ElTooltip, ElSwitch } from 'element-plus';
 
   const props = defineProps<{ active: boolean }>();
 
-  const color = computed(() => {
-    return props.active ? '#1890ff' : '#A8ABB2';
-  });
-
   const tip = computed(() => {
     return props.active ? '关闭算法' : '开启算法';
   });

+ 20 - 0
src/views/cameras/algo-params-setting/components/AlgoSwitchCard/ElectronicFenceIcon.vue

@@ -0,0 +1,20 @@
+<!-- 电子围栏开关按钮 -->
+<template>
+  <div>
+    <el-tooltip :content="props.active ? '关闭电子围栏' : '开启电子围栏'">
+      <svg-icon icon-name="electronic-fence" :color="color" />
+    </el-tooltip>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ElTooltip } from 'element-plus';
+  import { computed } from 'vue';
+  const props = defineProps<{
+    active: boolean;
+  }>();
+
+  const color = computed(() => {
+    return props.active ? '#409EFF' : '#CDCDCD';
+  });
+</script>
+<style scoped></style>

src/views/cameras/config/components/AlgoSwitchCard/WithTooltip.vue → src/views/cameras/algo-params-setting/components/AlgoSwitchCard/WithTooltip.vue


src/views/cameras/config/components/AlgorithmsSetting/AddAlgoDialog.vue → src/views/cameras/algo-params-setting/components/AlgorithmsSetting/AddAlgoDialog.vue


src/views/cameras/config/components/AlgorithmsSetting/AlgoTag.vue → src/views/cameras/algo-params-setting/components/AlgorithmsSetting/AlgoTag.vue


+ 44 - 98
src/views/cameras/config/components/AlgorithmsSetting/AlgorithmsSetting.vue

@@ -1,28 +1,15 @@
 <template>
   <div id="algoSetting">
-    <div style="font-size: 14px; font-weight: bold">算法开关</div>
     <div>
       <div class="algoTagWrapper">
-        <AlgoAddBtn @click="showDialog" v-if="hasAddPermission()" />
-        <AddAlgoDialog v-if="algoDialogVisible" @close="closeDialog" />
-
-        <!-- <AlgoTag
-          v-for="item in cameraAlgoList"
-          :key="item.code"
-          :label="item.algoInfo?.name"
-          :algo-id="item.algoInfo.id"
-          :is-active="item.algoId === selectedAlgoId"
-          @on-hit="handleSelectAlgo(item.algoId)"
-          @on-remove="handleRemove"
-          :is-open="item.status === ALGO_ENABLED_STATUS.enabled"
-        /> -->
+        <!-- <AddAlgoDialog v-if="algoDialogVisible" @close="closeDialog" /> -->
         <AlgoSwitchCard
           v-for="item in cameraAlgoList"
           :key="item.code"
           :label="item.algoInfo?.name"
           :is-selected="item.algoId === selectedAlgoId"
           :is-algo-open="item.status === ALGO_ENABLED_STATUS.enabled"
-          @click="handleSelectAlgo(item.algoId)"
+          @click.capture="handleSelectAlgo(item.algoId, $event)"
           @remove="confirmRemove(item.algoId, item)"
           @toggle-algo="confirmToggleAlgoOpen(item, $event)"
           :is-fence-open="item.electronicFence === FENCE_ENBALED_STATUS.enabled"
@@ -31,19 +18,21 @@
         />
       </div>
       <div>
-        <AlgoSettingCard
+        <AlgoParamsSetting
           @on-submit="handleSubmit"
           @on-cancel="handleCancel"
+          @change="handleSettingChange"
+          :is-changed="isChanged"
+          :algo-detail="selectedAlgoDetail"
           v-if="selectedAlgoId && algoSettingIsOpen"
         />
-        <!-- <div style="color: #ccc; margin-top: 20px" v-else>请选择算法</div> -->
       </div>
     </div>
   </div>
 </template>
 <script lang="ts" setup>
   import useCameraAlgoStore from '../../store/useCameraAlgoStore';
-  import AlgoSettingCard from './AlgoSettingCard.vue';
+  import AlgoParamsSetting from '@/modules/algo/algo-params-edit/index.vue';
   import { storeToRefs } from 'pinia';
   import {
     deleteCameraAlgoApi,
@@ -58,7 +47,8 @@
   import useFenceStore from '../../store/useFenceStore';
   import useCameraDetailStore from '../../store/useCameraDetailStore';
   import usePresetListStore from '../../store/usePresetListStore';
-  import AddAlgoDialog from './AddAlgoDialog.vue';
+  // import AddAlgoDialog from './AddAlgoDialog.vue';
+  import { createAlgoSubmitParams } from '@/modules/algo/algo-params-edit/utils';
   import {
     getInferCode,
     getAlgoType,
@@ -66,12 +56,10 @@
     getExtraCommonInfo,
     getDetectionTime,
     getInferParam,
-  } from './utils';
+  } from '@/modules/algo/algo-params-edit/utils';
+
   import { ALGO_ENABLED_STATUS } from '@/api/camera/camera-preview';
   import { ref, watchEffect } from 'vue';
-  import { useUserStore } from '@/store/modules/user';
-  import { PERM_ALGO } from '@/types/permission/constants';
-  import AlgoAddBtn from '../AlgoSwitchCard/AlgoAddBtn.vue';
 
   const cameraAlgoStore = useCameraAlgoStore();
   const fenceStore = useFenceStore();
@@ -80,11 +68,16 @@
   const { getCameraAlgoList, getAlgoDetail } = cameraAlgoStore;
   const { cameraAlgoList, selectedAlgoId, selectedAlgoDetail } = storeToRefs(cameraAlgoStore);
   const cameraDetailStore = useCameraDetailStore();
-  const userStore = useUserStore();
 
-  const hasAddPermission = () => userStore.checkPermission(PERM_ALGO.CONFIG_ADD);
+  // const hasAddPermission = () => userStore.checkPermission(PERM_ALGO.CONFIG_ADD);
   const algoSettingIsOpen = ref(false);
-  const algoDialogVisible = ref(false);
+
+  // 是否修改过参数配置
+  const isChanged = ref(false);
+
+  const handleSettingChange = () => {
+    isChanged.value = true;
+  };
 
   const handleToggleSetting = (algoId: number) => {
     // 如果是在当前选中的卡片上切换设置开关,那么反选即可
@@ -92,33 +85,29 @@
       algoSettingIsOpen.value = !algoSettingIsOpen.value;
       return;
     }
-    // 如果是在其他卡片上切换设置开关,等同于直接切卡片,并且显示设置。
-    // 如果原先设置开关是打开的,那么要先alert提示,否则就直接切
-    if (selectedAlgoId.value !== algoId && algoSettingIsOpen.value) {
-      confirmSwitchAlgo().then(() => {
-        selectedAlgoId.value = algoId;
-        algoSettingIsOpen.value = true;
-      });
-    } else {
-      selectedAlgoId.value = algoId;
-      algoSettingIsOpen.value = true;
-    }
   };
 
-  const handleSelectAlgo = (algoId: number) => {
+  const handleSelectAlgo = (algoId: number, e) => {
     if (selectedAlgoId.value === algoId) {
       return;
     }
-    if (algoSettingIsOpen.value) {
+    // 如果点击的是其他卡片,那么先切换到这个卡片,不执行按钮的默认操作
+    e.stopPropagation();
+    const switchToNewAlgo = (_algoId: number) => {
+      selectedAlgoId.value = _algoId;
+      algoSettingIsOpen.value = false;
+      isChanged.value = false;
+    };
+
+    // 如果算法设置有修改,切换到其他卡片时,要先确认提示再切换
+    if (isChanged.value) {
       confirmSwitchAlgo()
         .then(() => {
-          selectedAlgoId.value = algoId;
-          algoSettingIsOpen.value = false;
+          switchToNewAlgo(algoId);
         })
         .catch(() => {});
     } else {
-      selectedAlgoId.value = algoId;
-      algoSettingIsOpen.value = false;
+      switchToNewAlgo(algoId);
     }
   };
 
@@ -199,6 +188,7 @@
     const initialStatus = detail.status;
     detail.status = status;
     selectedAlgoId.value = algoId;
+    isChanged.value = false;
     algoSettingIsOpen.value = false;
     updateCameraAlgoRelStatus(params)
       .then(() => {
@@ -233,6 +223,7 @@
     const initialStatus = detail.electronicFence;
     detail.electronicFence = status;
     selectedAlgoId.value = algoId;
+    isChanged.value = false;
     algoSettingIsOpen.value = false;
     updateCameraAlgoApi(params)
       .then(() => {
@@ -244,51 +235,15 @@
   };
 
   const handleSubmit = (param) => {
-    console.log('submitParam', param);
     const cameraId = cameraDetailStore.cameraId;
-    const inferParams = getInferParam(selectedAlgoDetail.value.extra);
-    inferParams.metaObjs = param.metaObjs;
-    inferParams.regionJudge = param.regionJudge;
-    inferParams.criticalCounts = param.criticalCounts;
-    inferParams.judge = param.judge;
-    inferParams.eventDurationMinMs = param.eventDurationMinMs;
-    inferParams.eventDurationMinFrames = param.eventDurationMinFrames;
-    inferParams.eventAlarmIntervalMs = param.eventAlarmIntervalMs;
-    inferParams.eventAlarmIntervalFrames = param.eventAlarmIntervalFrames;
-    inferParams.algoCode = selectedAlgoDetail.value.algoInfo.code;
-    inferParams.algoType = getAlgoType(selectedAlgoDetail.value.algoInfo.extra);
-    if (param.timeWindow) {
-      inferParams.timeWindow = param.timeWindow;
-    }
-    const extraValue = {
-      inferCode: param.inferCode,
-      inferParams: [inferParams],
-    } as any;
-    const newParam = {
-      cameraId: cameraId,
-      electronicFence: param.electronicFence,
-      algoId: param.algoId,
-      detectionFrequency: param.detectionFrequency,
-      detectionTime: param.detectionTime,
-      status: param.status,
-      extra: JSON.stringify(extraValue),
-    };
+
+    const newParam = { cameraId: cameraId, ...createAlgoSubmitParams(param, selectedAlgoDetail.value) };
     if (param.id) {
-      if (param.isSwitch) {
-        updateCameraAlgoStatusApi({ ...newParam, id: param.id }).then(() => {
-          ElMessage.success('更新成功');
-          getCameraAlgoList(cameraId);
-          selectedAlgoId.value = undefined;
-          algoSettingIsOpen.value = false;
-        });
-      } else {
-        updateCameraAlgoApi({ ...newParam, id: param.id }).then(() => {
-          ElMessage.success('更新成功');
-          getCameraAlgoList(cameraId);
-          selectedAlgoId.value = undefined;
-          algoSettingIsOpen.value = false;
-        });
-      }
+      updateCameraAlgoApi({ ...newParam, id: param.id }).then(() => {
+        ElMessage.success('更新成功');
+        getCameraAlgoList(cameraId);
+        isChanged.value = false;
+      });
     }
   };
 
@@ -316,23 +271,14 @@
       ElMessage.success('删除成功');
       getCameraAlgoList(cameraDetailStore.cameraId);
       selectedAlgoId.value = undefined;
+      isChanged.value = false;
     });
   };
 
-  const handleCancel = (algoId: number) => {
-    if (selectedAlgoId.value !== algoId) {
-      return;
-    }
+  const handleCancel = () => {
+    isChanged.value = false;
     selectedAlgoId.value = undefined;
   };
-
-  const showDialog = () => {
-    algoDialogVisible.value = true;
-  };
-
-  const closeDialog = () => {
-    algoDialogVisible.value = false;
-  };
 </script>
 <style scoped>
   .algoTagWrapper {

src/views/cameras/config/components/CameraDirectionControl/CameraDirectionControl.vue → src/views/cameras/algo-params-setting/components/CameraDirectionControl/CameraDirectionControl.vue


src/views/cameras/config/components/CameraDirectionControl/DirectionItem.vue → src/views/cameras/algo-params-setting/components/CameraDirectionControl/DirectionItem.vue


src/views/cameras/config/components/CameraLiveVideo/CameraLiveVideo.vue → src/views/cameras/algo-params-setting/components/CameraLiveVideo/CameraLiveVideo.vue


src/views/cameras/config/components/CameraParams/CameraParams.vue → src/views/cameras/algo-params-setting/components/CameraParams/CameraParams.vue


src/views/cameras/config/components/CameraParams/types.ts → src/views/cameras/algo-params-setting/components/CameraParams/types.ts


src/views/cameras/config/components/CameraTree/CameraTree.vue → src/views/cameras/algo-params-setting/components/CameraTree/CameraTree.vue


src/views/cameras/config/components/CameraTree/CameraTreeOldVersion.vue → src/views/cameras/algo-params-setting/components/CameraTree/CameraTreeOldVersion.vue


src/views/cameras/config/components/CameraViewSetting/CameraViewScale.vue → src/views/cameras/algo-params-setting/components/CameraViewSetting/CameraViewScale.vue


+ 54 - 33
src/views/cameras/config/components/CameraViewSetting/CameraViewSetting.vue

@@ -7,38 +7,33 @@
           <FullscreenExitOutlined role="full" @click="enterFullscreen" />
         </el-icon>
       </el-tooltip> -->
-      <RenderSwitch />
-      <FenceAppSetting />
+      <!-- <RenderSwitch />
+      <FenceAppSetting /> -->
     </div>
-    <div
-      class="cameraViewSettingWrapper"
-      :style="{ width: domWidth + 'px', height: domHeight + 'px' }"
-    >
-      <div class="fenceEditorWrapper" v-if="cameraAlgoStore.selectedAlgoDetail.electronicFenceBool">
-        <FenceEditor
-          ref="fenceEditorRef"
-          :dom-width="domWidth"
-          :canvas-size="{ width: canvasWidth, height: canvasHeight }"
-          :line-points="fenceStore.serverFencePoints || []"
-        />
-      </div>
-
-      <div class="cameraVideo">
-        <CameraLiveVideo />
+    <div class="videoAlgoListWrapper">
+      <div class="cameraViewSettingWrapper" :style="{ width: domWidth + 'px', height: domHeight + 'px' }">
+        <div class="fenceEditorWrapper" v-if="cameraAlgoStore.selectedAlgoDetail.electronicFenceBool">
+          <FenceEditor
+            ref="fenceEditorRef"
+            :dom-width="domWidth"
+            :canvas-size="{ width: canvasWidth, height: canvasHeight }"
+            :line-points="fenceStore.serverFencePoints || []"
+          />
+        </div>
+
+        <div class="cameraVideo">
+          <CameraLiveVideo />
+        </div>
       </div>
+      <div>
+        <AlgoCanSelect :algo-list="cameraAllAlgoList" :selected-ids="cameraAlgoIds" @select="handleApplyAlgo"
+      /></div>
     </div>
-    <div
-      class="presetAddWrapper"
-      :class="{ hidePresetControlCls: isEdit }"
-      v-if="!!cameraDetailStore.detail?.isPtz"
-    >
+
+    <div class="presetAddWrapper" :class="{ hidePresetControlCls: isEdit }" v-if="!!cameraDetailStore.detail?.isPtz">
       <CameraViewScale />
       <CameraDirectionControl />
-      <ElButton
-        type="primary"
-        @click="handleAddPreset"
-        size="small"
-        style="margin-top: 20px; width: 100px"
+      <ElButton type="primary" @click="handleAddPreset" size="small" style="margin-top: 20px; width: 100px"
         >添加预置位</ElButton
       >
       <AddPresetModal v-if="addPresetModalVisible" @close="handleClose" @ok="handleAddPresetOk" />
@@ -64,7 +59,7 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { computed, ref, watchEffect } from 'vue';
+  import { computed, onMounted, ref, watchEffect } from 'vue';
   import FenceToolbar from '../FenceToolbar/FenceToolbar.vue';
   import FenceEditor from '../FenceEditorV2/FenceEditor.vue';
   import CameraLiveVideo from '../CameraLiveVideo/CameraLiveVideo.vue';
@@ -85,8 +80,10 @@
   import CameraViewScale from './CameraViewScale.vue';
   import { canvasHeight, canvasWidth, domHeight, domWidth } from './constants';
   import useFullscreen from 'vue-hooks-plus/lib/useFullscreen';
-  import { updateCameraAlgoApi } from '@/api/camera/camera-preview';
+  import { createCameraAlgoApi, updateCameraAlgoApi } from '@/api/camera/camera-preview';
   import { RegionJudge } from '../FenceToolbar/constants';
+  import AlgoCanSelect from '../AlgoCanSelect/AlgoCanSelect.vue';
+  import { AlgoDetail, queryAlgoInfoAllByCameraId } from '@/api/algo/algo';
 
   const emits = defineEmits<{
     (e: 'changeTreeRender', render: number | string): unknown;
@@ -108,6 +105,17 @@
 
   const addPresetModalVisible = ref(false);
 
+  const cameraAllAlgoList = ref<AlgoDetail[]>([]);
+
+  const cameraAlgoIds = computed(() => {
+    return cameraAlgoStore.cameraAlgoList?.map((item) => item.algoId) || [];
+  });
+
+  onMounted(() => {
+    queryAlgoInfoAllByCameraId(cameraDetailStore.cameraId).then((res) => {
+      cameraAllAlgoList.value = res;
+    });
+  });
   const handleClose = () => {
     addPresetModalVisible.value = false;
   };
@@ -140,9 +148,7 @@
     const extraStr = selectedAlgoDetail.extra;
     const extraJSON = JSON.parse(extraStr);
     const nextRegionJudge =
-      extraJSON.inferParams?.[0]?.regionJudge === RegionJudge.out
-        ? RegionJudge.in
-        : RegionJudge.out;
+      extraJSON.inferParams?.[0]?.regionJudge === RegionJudge.out ? RegionJudge.in : RegionJudge.out;
 
     extraJSON.inferParams[0].regionJudge = nextRegionJudge;
 
@@ -226,10 +232,22 @@
   const handleAddPreset = () => {
     addPresetModalVisible.value = true;
   };
+
+  const handleApplyAlgo = (id: number) => {
+    createCameraAlgoApi({
+      algoIds: [id],
+      cameraId: cameraDetailStore.cameraId,
+    }).then((res) => {
+      getCameraAlgoList(cameraDetailStore.cameraId);
+      ElMessage.success('添加成功,请完成算法参数配置后生效');
+    });
+  };
 </script>
 <style scoped>
   .cameraViewSettingWrapper {
     position: relative;
+    flex-shrink: 0;
+    flex-grow: 0;
     /* border: 1px solid #ccc; */
   }
   .cameraViewOverflow {
@@ -288,7 +306,7 @@
   }
 
   .presetWrapper {
-    width: 962px;
+    /* width: 962px; */
     padding: 20px;
     padding-left: 15px;
     border-bottom: 1px solid #ccc;
@@ -297,4 +315,7 @@
     display: flex;
     justify-content: space-between;
   }
+  .videoAlgoListWrapper {
+    display: flex;
+  }
 </style>

src/views/cameras/config/components/CameraViewSetting/constants.ts → src/views/cameras/algo-params-setting/components/CameraViewSetting/constants.ts


src/views/cameras/config/components/FenceAppSetting/FenceAppSetting.vue → src/views/cameras/algo-params-setting/components/FenceAppSetting/FenceAppSetting.vue


src/views/cameras/config/components/FenceAppSetting/constants.ts → src/views/cameras/algo-params-setting/components/FenceAppSetting/constants.ts


src/views/cameras/config/components/FenceEditor/FenceEditor.vue → src/views/cameras/algo-params-setting/components/FenceEditor/FenceEditor.vue


src/views/cameras/config/components/FenceEditor/constants.ts → src/views/cameras/algo-params-setting/components/FenceEditor/constants.ts


src/views/cameras/config/components/FenceEditor/utils.ts → src/views/cameras/algo-params-setting/components/FenceEditor/utils.ts


src/views/cameras/config/components/FenceEditorV2/FenceEditor.vue → src/views/cameras/algo-params-setting/components/FenceEditorV2/FenceEditor.vue


src/views/cameras/config/components/FenceEditorV2/FenceItem.vue → src/views/cameras/algo-params-setting/components/FenceEditorV2/FenceItem.vue


src/views/cameras/config/components/FenceEditorV2/constants.ts → src/views/cameras/algo-params-setting/components/FenceEditorV2/constants.ts


src/views/cameras/config/components/FenceEditorV2/types.ts → src/views/cameras/algo-params-setting/components/FenceEditorV2/types.ts


src/views/cameras/config/components/FenceEditorV2/utils.ts → src/views/cameras/algo-params-setting/components/FenceEditorV2/utils.ts


src/views/cameras/config/components/FenceToolbar/FenceToolbar.vue → src/views/cameras/algo-params-setting/components/FenceToolbar/FenceToolbar.vue


+ 1 - 1
src/views/cameras/config/components/FenceToolbar/ToggleFenceStatus.vue

@@ -5,7 +5,7 @@
   import ToolbarIcon from '../ToolbarIcon/ToolbarIcon.vue';
   import fenceOut from '@/assets/icons/box-select-outer.png';
   import fenceInner from '@/assets/icons/box-select-inner.svg';
-  import useCameraAlgoStore, { AlgoParamMetaItem } from '../../store/useCameraAlgoStore';
+  import useCameraAlgoStore from '../../store/useCameraAlgoStore';
 
   import { computed } from 'vue';
   import { storeToRefs } from 'pinia';

src/views/cameras/config/components/FenceToolbar/constants.ts → src/views/cameras/algo-params-setting/components/FenceToolbar/constants.ts


src/views/cameras/config/components/PresetSelect/PresetSelect.vue → src/views/cameras/algo-params-setting/components/PresetSelect/PresetSelect.vue


src/views/cameras/config/components/RenderSwitch/RenderSwitch.vue → src/views/cameras/algo-params-setting/components/RenderSwitch/RenderSwitch.vue


src/views/cameras/config/components/ToolbarIcon/ToolbarIcon.vue → src/views/cameras/algo-params-setting/components/ToolbarIcon/ToolbarIcon.vue


src/views/cameras/config/components/ViewWindowSetting/ViewWindowSetting.vue → src/views/cameras/algo-params-setting/components/ViewWindowSetting/ViewWindowSetting.vue


src/views/cameras/config/components/ViewWindowSetting/types.ts → src/views/cameras/algo-params-setting/components/ViewWindowSetting/types.ts


+ 2 - 37
src/views/cameras/config/store/useCameraAlgoStore.ts

@@ -2,8 +2,7 @@ import { getAllAlgosApi, getCameraAlgoListApi, CameraAlgoItem } from '@/api/came
 import { defineStore } from 'pinia';
 import { computed, ref } from 'vue';
 import { useRequest } from 'vue-hooks-plus';
-import { TimePeriodItem } from '../components/AlgorithmsSetting/types';
-import { DetectionJSON } from '../components/AlgorithmsSetting/utils';
+import { TimePeriodItem } from '@/modules/algo/algo-params-edit/types';
 
 interface CameraAlgoItemInCard extends CameraAlgoItem {
   // detectionJSON: DetectionJSON;
@@ -60,12 +59,6 @@ const useCameraAlgoStore = defineStore('cameraAlgo', () => {
     manual: true,
   });
 
-  // 标记的paramCard集合
-  const markedParamCardIds = ref<string[]>([]);
-
-  // 标记的timeRange集合
-  const markedTimeRangeIds = ref<string[]>([]);
-
   /** 所有算法列表中选定的算法id */
   const selectedAlgoId = ref<number | null | undefined>();
 
@@ -73,18 +66,6 @@ const useCameraAlgoStore = defineStore('cameraAlgo', () => {
     ...defaultSelectedAlgoDetail,
   } as CameraAlgoItemInCard);
 
-  //计算原始模板数据
-  const metaObjList = computed(() => {
-    const extra = selectedAlgoDetail.value.algoInfo?.extra;
-    if (!extra) return [];
-    const extraObj = JSON.parse(extra);
-    const params = extraObj?.inferParams;
-    if (!params || (params && params.length == 0)) return [];
-    const metaObjs = params[0]?.metaObjs;
-
-    return metaObjs ? metaObjs : [];
-  });
-
   const getAlgoDetail = (algoId: number): null | CameraAlgoItem => {
     const detail = cameraAlgoList.value?.find((x) => x.algoId === algoId);
     if (!detail) return null;
@@ -96,18 +77,6 @@ const useCameraAlgoStore = defineStore('cameraAlgo', () => {
     return cameraAlgoList.value?.find((x) => x.algoId === algoId);
   };
 
-  const deleteParam = (id: string) => {
-    selectedAlgoDetail.value.metaValues = selectedAlgoDetail.value.metaValues.filter(
-      (x) => x.id !== id,
-    );
-  };
-
-  const deleteTimeRange = (id: string) => {
-    selectedAlgoDetail.value.timeRangeArr = selectedAlgoDetail.value.timeRangeArr.filter(
-      (item) => item.id !== id,
-    );
-  };
-
   const clear = () => {
     mutateCameraAlgoList();
     mutateAllAlgoList();
@@ -120,17 +89,13 @@ const useCameraAlgoStore = defineStore('cameraAlgo', () => {
     getCameraAlgoList,
     selectedAlgoId,
     selectedAlgoList,
-    metaObjList,
     allAlgoList,
-    markedParamCardIds,
-    markedTimeRangeIds,
+
     getAllAlgoList,
     getAlgoDetail,
     selectedAlgoDetail,
     isAlgoBind,
     clear,
-    deleteParam,
-    deleteTimeRange,
   };
 });
 

src/views/cameras/config/store/useCameraDetailStore.ts → src/views/cameras/algo-params-setting/store/useCameraDetailStore.ts


src/views/cameras/config/store/useCameraStatus.ts → src/views/cameras/algo-params-setting/store/useCameraStatus.ts


src/views/cameras/config/store/useFenceStore.ts → src/views/cameras/algo-params-setting/store/useFenceStore.ts


src/views/cameras/config/store/usePresetListStore.ts → src/views/cameras/algo-params-setting/store/usePresetListStore.ts


+ 0 - 408
src/views/cameras/config/components/AlgorithmsSetting/AlgoPeriodCard copy.vue

@@ -1,408 +0,0 @@
-<template>
-  <div class="periodCard">
-    <div class="dayRrange">
-      <el-select class="daySelect" v-model="startDay" placeholder="开始日期">
-        <el-option
-          v-for="item in dayOptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-          :disabled="endDay && item.value > endDay"
-        >
-        </el-option>
-      </el-select>
-      <div class="divider">-</div>
-      <el-select class="daySelect" v-model="endDay" placeholder="结束日期">
-        <el-option
-          v-for="item in dayOptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-          :disabled="startDay && item.value < startDay"
-        >
-        </el-option>
-      </el-select>
-    </div>
-    <div class="timeRange" v-for="(item, index) in timeRangeList">
-      <el-time-picker
-        v-model="item.startTime"
-        format="HH:mm"
-        value-format="HH:mm"
-        :teleported="false"
-        :editable="true"
-        placeholder="开始时间"
-        :disabled-hours="() => disabledHours(index, false)"
-        :disabled-minutes="(hour:number)=>disabledMinutes(hour, index, false)"
-      />
-      <div class="divider">-</div>
-      <el-time-picker
-        v-model="item.endTime"
-        format="HH:mm"
-        value-format="HH:mm"
-        :teleported="false"
-        :editable="true"
-        placeholder="结束时间"
-        :disabled-hours="() => disabledHours(index, true)"
-        :disabled-minutes="(hour:number)=>disabledMinutes(hour, index, true)"
-      />
-      <div v-if="index == timeRangeList.length - 1" class="timeAdd" @click="handleAddTimeRange">
-        <el-icon color="#d0d0d0"><Plus /></el-icon>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue';
-  import { TimePeriodItem } from './types';
-  import { Plus } from '@element-plus/icons-vue';
-
-  const props = defineProps<{ periodData: TimePeriodItem }>();
-
-  const dayOptions = [
-    {
-      value: 1,
-      label: '周一',
-    },
-    {
-      value: 2,
-      label: '周二',
-    },
-    {
-      value: 3,
-      label: '周三',
-    },
-    {
-      value: 4,
-      label: '周四',
-    },
-    {
-      value: 5,
-      label: '周五',
-    },
-    {
-      value: 6,
-      label: '周六',
-    },
-    {
-      value: 7,
-      label: '周日',
-    },
-  ];
-
-  const makeRange = (start: number, end: number): number[] => {
-    let result: number[] = [];
-    for (let i = start; i <= end; i++) {
-      result.push(i);
-    }
-    return result;
-  };
-
-  const startDay = ref(props.periodData.startDay);
-  const endDay = ref(props.periodData.endDay);
-  const timeRangeList = ref<{ startTime: Date | string; endTime: Date | string }[]>(
-    props.periodData.timeRangeList,
-  );
-
-  const handleAddTimeRange = () => {
-    if (!timeRangeList.value.find((item) => item.startTime === '' || item.endTime === '')) {
-      timeRangeList.value.push({ startTime: '', endTime: '' });
-      console.log(timeRangeList.value);
-    }
-  };
-
-  const disabledHours = (index: number, isEnd: boolean) => {
-    const newList = timeRangeList.value.filter(
-      (item, i) => item.startTime && item.endTime && i !== index,
-    );
-    let result: number[] = [];
-    // 选开始时间,结束时间被约束
-    if (!isEnd && timeRangeList.value[index].endTime) {
-      const curEnd = timeRangeList.value[index].endTime;
-      const cureh = Number(String(curEnd).split(':')[0]);
-      const curem = Number(String(curEnd).split(':')[1]);
-      if (newList.length == 0) {
-        if (curem == 0) {
-          result = result.concat(makeRange(cureh, 23));
-        } else {
-          result = result.concat(makeRange(getH(cureh + 1), 23));
-        }
-      }
-      for (let i = 0; i < newList.length; i++) {
-        if (compareHM(newList[i].startTime, curEnd)) {
-          if (i == 0) {
-            if (curem == 0) {
-              result = result.concat(makeRange(cureh, 23));
-              break;
-            } else {
-              result = result.concat(makeRange(getH(cureh + 1), 23));
-              break;
-            }
-          } else {
-            let sh = 0;
-            const lastEnd = newList[i - 1].endTime;
-            const lasteh = Number(String(lastEnd).split(':')[0]);
-            const lastem = Number(String(lastEnd).split(':')[1]);
-            if (lastem == 59) {
-              sh = lasteh;
-            } else {
-              sh = getH(lasteh - 1);
-            }
-            let eh = 0;
-            if (curem == 0) {
-              eh = cureh;
-            } else {
-              eh = getH(cureh + 1);
-            }
-            result = result.concat(makeRange(0, sh)).concat(makeRange(eh, 23));
-            break;
-          }
-        }
-        if (i == newList.length - 1) {
-          let sh = 0;
-          const lastEnd = newList[i].endTime;
-          const lasteh = Number(String(lastEnd).split(':')[0]);
-          const lastem = Number(String(lastEnd).split(':')[1]);
-          if (lastem == 59) {
-            sh = lasteh;
-          } else {
-            sh = getH(lasteh - 1);
-          }
-          let eh = 0;
-          if (curem == 0) {
-            eh = cureh;
-          } else {
-            eh = getH(cureh + 1);
-          }
-          result = result.concat(makeRange(0, sh)).concat(makeRange(eh, 23));
-        }
-      }
-    }
-    // 选结束时间,开始时间被约束
-    if (isEnd && timeRangeList.value[index].startTime) {
-      const curStart = timeRangeList.value[index].startTime;
-      const cursh = Number(String(curStart).split(':')[0]);
-      const cursm = Number(String(curStart).split(':')[1]);
-      if (newList.length == 0) {
-        if (cursm == 59) {
-          result = result.concat(makeRange(0, cursh));
-        } else {
-          result = result.concat(makeRange(0, getH(cursh - 1)));
-        }
-      }
-      for (let i = 0; i < newList.length; i++) {
-        if (compareHM(curStart, newList[i].endTime)) {
-          if (i == newList.length - 1) {
-            if (cursm == 59) {
-              result = result.concat(makeRange(0, cursh));
-              break;
-            } else {
-              result = result.concat(makeRange(0, getH(cursh - 1)));
-              break;
-            }
-          } else {
-            let sh = 0;
-            if (cursm == 59) {
-              sh = cursh;
-            } else {
-              sh = getH(cursh - 1);
-            }
-            let eh = 0;
-            const nextStart = newList[i + 1].startTime;
-            const nextsh = Number(String(nextStart).split(':')[0]);
-            const nextsm = Number(String(nextStart).split(':')[1]);
-            if (nextsm == 0) {
-              eh = nextsh;
-            } else {
-              eh = getH(nextsh + 1);
-            }
-            result = result.concat(makeRange(0, sh)).concat(makeRange(eh, 23));
-            break;
-          }
-        }
-        if (i == newList.length - 1) {
-          let sh = 0;
-          if (cursm == 59) {
-            sh = cursh;
-          } else {
-            sh = getH(cursh - 1);
-          }
-          let eh = 0;
-          const nextStart = newList[0].startTime;
-          const nextsh = Number(String(nextStart).split(':')[0]);
-          const nextsm = Number(String(nextStart).split(':')[1]);
-          if (nextsm == 0) {
-            eh = nextsh;
-          } else {
-            eh = getH(nextsh + 1);
-          }
-          result = result.concat(makeRange(0, sh)).concat(makeRange(eh, 23));
-        }
-      }
-    }
-    // 另一个时间为空
-    if (
-      (!isEnd && !timeRangeList.value[index].endTime) ||
-      (isEnd && !timeRangeList.value[index].startTime)
-    ) {
-      for (let i = 0; i < newList.length; i++) {
-        const timeRange = newList[i];
-        const s = timeRange.startTime;
-        const e = timeRange.endTime;
-        const sh = Number(String(s).split(':')[0]);
-        const sm = Number(String(s).split(':')[1]);
-        const eh = Number(String(e).split(':')[0]);
-        const em = Number(String(e).split(':')[1]);
-        if (eh - sh > 1) {
-          result = result.concat(makeRange(getH(sh + 1), getH(eh - 1)));
-          if (sm == 0) result.push(sh);
-          if (em == 59) result.push(eh);
-        }
-        if (sm == 0 && em == 59) result.push(sh);
-      }
-    }
-
-    return result;
-  };
-
-  const disabledMinutes = (hour: number, index: number, isEnd: boolean) => {
-    const newList = timeRangeList.value.filter(
-      (item, i) => item.startTime && item.endTime && i !== index,
-    );
-    let result: number[] = [];
-    for (let i = 0; i < newList.length; i++) {
-      const timeRange = newList[i];
-      const s = timeRange.startTime;
-      const e = timeRange.endTime;
-      const sh = Number(String(s).split(':')[0]);
-      const sm = Number(String(s).split(':')[1]);
-      const eh = Number(String(e).split(':')[0]);
-      const em = Number(String(e).split(':')[1]);
-      if (hour == sh) {
-        if (sh == eh) {
-          result = result.concat(makeRange(sm, em));
-        } else {
-          result = result.concat(makeRange(sm, 59));
-        }
-        continue;
-      }
-      if (hour == eh) {
-        result = result.concat(makeRange(0, em));
-      }
-    }
-    // 选开始时间,结束时间被约束
-    if (!isEnd && timeRangeList.value[index].endTime) {
-      const curEnd = timeRangeList.value[index].endTime;
-      const cureh = Number(String(curEnd).split(':')[0]);
-      const curem = Number(String(curEnd).split(':')[1]);
-      if (hour === cureh) {
-        result = result.concat(makeRange(curem, 59));
-      }
-    }
-    // 选结束时间,开始时间被约束
-    if (isEnd && timeRangeList.value[index].startTime) {
-      const curStart = timeRangeList.value[index].startTime;
-      const cursh = Number(String(curStart).split(':')[0]);
-      const cursm = Number(String(curStart).split(':')[1]);
-      if (hour === cursh) {
-        result = result.concat(makeRange(0, cursm));
-      }
-    }
-
-    return result;
-  };
-
-  const getH = (h) => {
-    if (h < 0) {
-      return 0;
-    } else if (h > 23) {
-      return 23;
-    } else {
-      return h;
-    }
-  };
-
-  const getM = (m) => {
-    if (m < 0) {
-      return 0;
-    } else if (m > 59) {
-      return 23;
-    } else {
-      return m;
-    }
-  };
-
-  const compareHM = (s, e) => {
-    const sh = Number(String(s).split(':')[0]);
-    const sm = Number(String(s).split(':')[1]);
-    const eh = Number(String(e).split(':')[0]);
-    const em = Number(String(e).split(':')[1]);
-
-    if (sh === eh) {
-      return sm - em > 0 ? true : false;
-    } else if (sh > eh) {
-      return true;
-    } else {
-      return false;
-    }
-  };
-</script>
-
-<style scoped lang="scss">
-  .periodCard {
-    width: 294px;
-    max-height: 174px;
-    background: #0000000a;
-    border-radius: 5px;
-    border: 1px solid #e8ecf2;
-    padding: 3px;
-    margin: 0 5px 5px 5px;
-  }
-
-  .dayRrange {
-    display: flex;
-    align-items: center;
-  }
-
-  .timeRange {
-    display: flex;
-    align-items: center;
-  }
-
-  .divider {
-    color: #00000040;
-  }
-
-  .daySelect {
-    width: 112px;
-    margin: 5px;
-  }
-
-  .timeAdd {
-    width: 28px;
-    height: 28px;
-    border-radius: 50%;
-    background: #ebebeb;
-    border: 1px dashed #00000026;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  :deep(.el-date-editor) {
-    width: 112px;
-    margin: 5px;
-
-    .el-input__prefix {
-      order: 1;
-    }
-
-    .el-input__icon {
-      margin: 0;
-    }
-  }
-
-  :deep(.el-time-panel__footer) {
-    display: none;
-  }
-</style>