|
|
@@ -10,62 +10,96 @@
|
|
|
</div>
|
|
|
<div class="algoCardMain">
|
|
|
<div class="algoRow">
|
|
|
- <div class="algoLabel">电子围栏:</div>
|
|
|
+ <div class="algoLabel">绘制电子围栏:</div>
|
|
|
<div>
|
|
|
- <div>
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
<ElSwitch v-model="selectedAlgoDetail.electronicFenceBool" size="small" />
|
|
|
- <span style="font-size: 10px; margin-left: 20px; color: #262626"
|
|
|
- >备注:请绘制电子围栏</span
|
|
|
+ <el-tooltip
|
|
|
+ class="box-item"
|
|
|
+ effect="dark"
|
|
|
+ content="打开开关,绘制电子围栏。"
|
|
|
+ placement="top"
|
|
|
>
|
|
|
+ <el-icon color="#e2e2e2" class="tipIcon"><InfoCircleOutlined /></el-icon>
|
|
|
+ </el-tooltip>
|
|
|
+ <!-- <span style="font-size: 10px; margin-left: 20px; color: #262626"
|
|
|
+ >备注:请绘制电子围栏</span
|
|
|
+ > -->
|
|
|
+ <el-radio-group v-model="selectedAlgoDetail.regionJudge" class="ml-4">
|
|
|
+ <el-radio :label="0">内</el-radio>
|
|
|
+ <el-radio :label="1">外</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</div>
|
|
|
<div class="presetList"> 预置位1 预置位2 预置位3 </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="algoRow">
|
|
|
- <div class="algoLabel">检测时间:</div>
|
|
|
- <div>
|
|
|
- <div v-for="x in selectedAlgoDetail.timeRangeArr" :key="x.id">
|
|
|
- <el-time-picker
|
|
|
- v-model="x.value"
|
|
|
- is-range
|
|
|
- range-separator="-"
|
|
|
- start-placeholder="Start time"
|
|
|
- end-placeholder="End time"
|
|
|
- :clearable="false"
|
|
|
- size="small"
|
|
|
- style="width: 180px; margin-bottom: 10px"
|
|
|
+ <div class="algoLabel">检测时间段:</div>
|
|
|
+ <div class="algoTimeContent">
|
|
|
+ <div class="timeAdd" @click="handleAddTimePeriod">
|
|
|
+ <el-icon color="#d0d0d0"><Plus /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="timeList">
|
|
|
+ <PeriodCard
|
|
|
+ v-for="(x, index) in selectedAlgoDetail.timeRangeArr"
|
|
|
+ :key="x.id"
|
|
|
+ :ref="(el) => (periodCardRefs[index] = el)"
|
|
|
+ :id="x.id"
|
|
|
/>
|
|
|
- <span @click="removeTime(x.id)" v-if="selectedAlgoDetail.timeRangeArr.length > 1">
|
|
|
- <el-icon class="removeIcon"><CircleCloseFilled /></el-icon
|
|
|
- ></span>
|
|
|
</div>
|
|
|
- <div @click="handleAddTimeRange" class="addTimeIcon">
|
|
|
- <img :src="addTimeIcon" width="28" height="28" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="algoRow">
|
|
|
+ <div class="algoLabel">检测元数据:</div>
|
|
|
+ <div class="algoTimeContent">
|
|
|
+ <div class="timeAdd" @click="handleAddMetaObj">
|
|
|
+ <el-icon color="#d0d0d0"><Plus /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="timeList">
|
|
|
+ <ParamCard
|
|
|
+ v-for="(v, index) in selectedAlgoDetail.metaValues"
|
|
|
+ :key="v.id"
|
|
|
+ :ref="(el) => (paramCardRefs[index] = el)"
|
|
|
+ :id="v.id"
|
|
|
+ />
|
|
|
</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.detectionJSON.detectionNum"
|
|
|
+ v-model="selectedAlgoDetail.detectionFrequency"
|
|
|
controls-position="right"
|
|
|
- :min="0"
|
|
|
+ :min="1"
|
|
|
+ :step="1"
|
|
|
size="small"
|
|
|
- style="width: 80px"
|
|
|
+ style="width: 186px"
|
|
|
+ @blur="checkFrequencyValid"
|
|
|
/>
|
|
|
- <ElSelect
|
|
|
+ <span style="font-size: 12px; margin-left: 5px">S/次</span>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="selectedAlgoDetail.timeWindow !== undefined"
|
|
|
+ class="algoRow"
|
|
|
+ style="align-items: center"
|
|
|
+ >
|
|
|
+ <div class="algoLabel">检测窗口时长:</div>
|
|
|
+ <ElInputNumber
|
|
|
+ v-model="selectedAlgoDetail.timeWindow"
|
|
|
+ controls-position="right"
|
|
|
+ :min="1"
|
|
|
size="small"
|
|
|
- style="width: 60px; margin-left: 10px"
|
|
|
- v-model="selectedAlgoDetail.detectionJSON.detectionUnit"
|
|
|
- >
|
|
|
- <ElOption
|
|
|
- v-for="x in frequencyOptions"
|
|
|
- :key="x.value"
|
|
|
- :value="x.value"
|
|
|
- :label="x.label"
|
|
|
- />
|
|
|
- </ElSelect>
|
|
|
- <span style="font-size: 12px; margin-left: 5px">/次</span>
|
|
|
+ style="width: 186px"
|
|
|
+ />
|
|
|
+ <span style="font-size: 12px; margin-left: 5px">S</span>
|
|
|
</div>
|
|
|
<div style="display: flex; justify-content: flex-end">
|
|
|
<ElButton size="small" @click="handleRemoveAlgo" :disabled="!selectedAlgoId">删除</ElButton>
|
|
|
@@ -77,18 +111,36 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
<script lang="ts" setup>
|
|
|
- import { ElSelect, ElOption, ElSwitch, ElInputNumber, ElTimePicker } from 'element-plus';
|
|
|
- import { CircleCloseFilled } from '@element-plus/icons-vue';
|
|
|
+ import {
|
|
|
+ ElSelect,
|
|
|
+ ElOption,
|
|
|
+ ElSwitch,
|
|
|
+ ElInputNumber,
|
|
|
+ ElTimePicker,
|
|
|
+ ElMessage,
|
|
|
+ } from 'element-plus';
|
|
|
+ import { CircleCloseFilled, Plus } from '@element-plus/icons-vue';
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
|
import addTimeIcon from '@/assets/icons/addTimeIcon.png';
|
|
|
- import useCameraAlgoStore from '../../store/useCameraAlgoStore';
|
|
|
- import { createDefaultTime, frequencyOptions } from './utils';
|
|
|
+ import useCameraAlgoStore, { AlgoParamMetaItem } from '../../store/useCameraAlgoStore';
|
|
|
+ import { createDefaultTime, frequencyOptions, getTimeCompletion } from './utils';
|
|
|
+ import { InfoCircleOutlined } from '@vicons/antd';
|
|
|
import { ALGO_ENABLED_STATUS, FENCE_ENBALED_STATUS } from '@/api/camera/camera-preview';
|
|
|
+ import PeriodCard from './AlgoPeriodCard.vue';
|
|
|
+ import ParamCard from './AlgoParamsCard.vue';
|
|
|
+ import { uid } from 'uid';
|
|
|
+ import { ref } from 'vue';
|
|
|
|
|
|
// const { data: algoList, loading } = useAllAlgos();
|
|
|
const cameraAlgoStore = useCameraAlgoStore();
|
|
|
- const { selectedAlgoId, selectedAlgoDetail } = storeToRefs(cameraAlgoStore);
|
|
|
+ const {
|
|
|
+ selectedAlgoId,
|
|
|
+ selectedAlgoDetail,
|
|
|
+ metaObjList,
|
|
|
+ markedTimeRangeIds,
|
|
|
+ markedParamCardIds,
|
|
|
+ } = storeToRefs(cameraAlgoStore);
|
|
|
|
|
|
interface Param {
|
|
|
/** 算法id */
|
|
|
@@ -108,10 +160,37 @@
|
|
|
(e: 'onRemove', algoId: number): Promise<unknown>;
|
|
|
}>();
|
|
|
|
|
|
- const handleAddTimeRange = () => {
|
|
|
+ const periodCardRefs = ref<any>([]);
|
|
|
+ const paramCardRefs = ref<any>([]);
|
|
|
+
|
|
|
+ const handleAddTimePeriod = () => {
|
|
|
+ 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());
|
|
|
};
|
|
|
|
|
|
+ const handleAddMetaObj = () => {
|
|
|
+ const unemptyList = selectedAlgoDetail.value.metaValues.filter((item) => item.label);
|
|
|
+ if (unemptyList.length < selectedAlgoDetail.value.metaValues.length) {
|
|
|
+ selectedAlgoDetail.value.metaValues
|
|
|
+ .filter((item) => !item.label)
|
|
|
+ .forEach((val) => markedParamCardIds.value.push(val.id));
|
|
|
+ ElMessage.error('存在未完善的检测元数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (unemptyList.length == metaObjList.value.length) {
|
|
|
+ ElMessage.warning('暂无更多检测对象');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ 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);
|
|
|
@@ -128,23 +207,113 @@
|
|
|
handleSave();
|
|
|
};
|
|
|
|
|
|
- const handleSave = () => {
|
|
|
+ const checkFrequencyValid = () => {
|
|
|
+ selectedAlgoDetail.value.detectionFrequency = Math.ceil(
|
|
|
+ selectedAlgoDetail.value.detectionFrequency,
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSave = async () => {
|
|
|
+ //判断时间段是否合格
|
|
|
+ if (markedTimeRangeIds.value.length > 0) {
|
|
|
+ ElMessage.error('请正确填写检测时间段');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const emptyTimes = selectedAlgoDetail.value.timeRangeArr.filter(
|
|
|
+ (item) => !item.startDay || !item.endDay,
|
|
|
+ );
|
|
|
+ if (emptyTimes && emptyTimes.length > 0) {
|
|
|
+ emptyTimes.forEach((item) => {
|
|
|
+ markedTimeRangeIds.value.push(item.id);
|
|
|
+ });
|
|
|
+ ElMessage.error('请完善检测时间段');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (let i = 0; i < periodCardRefs.value.length; i++) {
|
|
|
+ const item = periodCardRefs.value[i];
|
|
|
+ switch (item.checkValid()) {
|
|
|
+ case 0:
|
|
|
+ ElMessage.error('请正确填写检测时间段');
|
|
|
+ return;
|
|
|
+ case 1:
|
|
|
+ ElMessage.error('请完善检测时间段');
|
|
|
+ return;
|
|
|
+ case 2:
|
|
|
+ ElMessage.error('至少有一个时间段');
|
|
|
+ return;
|
|
|
+ case 3:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断元数据是否合格
|
|
|
+ for (let i = 0; i < paramCardRefs.value.length; i++) {
|
|
|
+ const item = paramCardRefs.value[i];
|
|
|
+ await item.checkValid().then((res) => {
|
|
|
+ if (res) {
|
|
|
+ } else {
|
|
|
+ ElMessage.error('请正确填写检测元数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断时间段和元数据都有值
|
|
|
const detail = selectedAlgoDetail.value;
|
|
|
if (!detail) return;
|
|
|
+ const timeRanges = detail.timeRangeArr;
|
|
|
+ if (timeRanges.length == 0) {
|
|
|
+ ElMessage.error('至少添加一个检测时间段');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const metaValues = detail.metaValues;
|
|
|
+ if (metaValues.length == 0) {
|
|
|
+ ElMessage.error('至少添加一个检测元数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //数据处理
|
|
|
+ const metaObjs = metaValues.map((meta) => {
|
|
|
+ const obj = metaObjList.value.find((item) => item.label === meta.label);
|
|
|
+ const val = {
|
|
|
+ label: meta.label,
|
|
|
+ confidence: meta.confidence / 100,
|
|
|
+ 'min-width': meta['min-width'],
|
|
|
+ 'min-height': meta['min-height'],
|
|
|
+ } as any;
|
|
|
+ const nextValues = [] as any[];
|
|
|
+ obj.nextObjs.forEach((next) => {
|
|
|
+ if (meta[`${next.label}.confidence`]) {
|
|
|
+ nextValues.push({
|
|
|
+ label: next.label,
|
|
|
+ confidence: meta[`${next.label}.confidence`] / 100,
|
|
|
+ 'min-width': meta[next.label + '.' + 'min-width'],
|
|
|
+ 'min-height': meta[next.label + '.' + 'min-height'],
|
|
|
+ nextObjs: [],
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ val.nextObjs = nextValues;
|
|
|
+ return val;
|
|
|
+ });
|
|
|
const param = {
|
|
|
id: detail.id,
|
|
|
+ inferCode: detail.inferCode,
|
|
|
algoId: detail.algoId,
|
|
|
- detectionFrequency: detail.detectionJSON.detectionNum * detail.detectionJSON.detectionUnit,
|
|
|
- detectionTime: detail.timeRangeArr
|
|
|
- .map((x) => {
|
|
|
- return getTimeStrs(x.value);
|
|
|
- })
|
|
|
- .join(';'),
|
|
|
+ detectionFrequency: detail.detectionFrequency,
|
|
|
+ regionJudge: detail.regionJudge,
|
|
|
+ detectionTime: JSON.stringify(detail.timeRangeArr),
|
|
|
+ metaObjs,
|
|
|
+ criticalCounts: metaValues.map((item) => item.criticalCount),
|
|
|
+ judge: detail.judge,
|
|
|
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) {
|
|
|
+ param.timeWindow = detail.timeWindow;
|
|
|
+ }
|
|
|
emits('onSubmit', param);
|
|
|
console.log('param', param);
|
|
|
};
|
|
|
@@ -159,7 +328,7 @@
|
|
|
border: 1px solid #ccc;
|
|
|
border-radius: 4px;
|
|
|
/* padding: 10px; */
|
|
|
- width: 400px;
|
|
|
+ width: 770px;
|
|
|
}
|
|
|
|
|
|
.algoRow {
|
|
|
@@ -170,6 +339,9 @@
|
|
|
.algoLabel {
|
|
|
margin-right: 10px;
|
|
|
}
|
|
|
+ .algoTimeContent {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
.presetList {
|
|
|
font-size: 12px;
|
|
|
display: none;
|
|
|
@@ -195,4 +367,25 @@
|
|
|
.algoCardMain {
|
|
|
padding: 10px 20px;
|
|
|
}
|
|
|
+
|
|
|
+ .timeAdd {
|
|
|
+ width: 28px;
|
|
|
+ height: 90px;
|
|
|
+ background: #ebebeb;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px dashed #00000026;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+ .timeList {
|
|
|
+ width: 608px;
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+ .tipIcon {
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
</style>
|