| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- <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 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>
- <!-- <span style="font-size: 10px; margin-left: 20px; color: #262626"
- >备注:请绘制电子围栏</span
- > -->
- <el-radio-group
- v-model="selectedAlgoDetail.regionJudge"
- :disabled="!selectedAlgoDetail.electronicFenceBool"
- class="ml-3"
- >
- <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 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"
- />
- </div>
- </div>
- </div>
- <div class="algoRow">
- <div class="algoLabel">检测元数据:</div>
- <div class="algoTimeContent">
- <div
- class="timeAdd"
- :class="{ addDisable: unEmptyLabels.length >= selectedAlgoDetail.metaValues.length }"
- @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.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
- 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: 186px"
- placeholder="请输入允许的最长时间"
- @blur="checkTimeWindowValid"
- />
- <span style="font-size: 12px; margin-left: 5px">S</span>
- </div>
- <div style="display: flex; justify-content: flex-end">
- <ElButton size="small" :disabled="!selectedAlgoId" @click="handleRemoveAlgo">取消</ElButton>
- <ElButton size="small" type="primary" @click="handleSave" :disabled="!selectedAlgoId"
- >保存</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 { 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 { 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 { computed, ref } from 'vue';
- // const { data: algoList, loading } = useAllAlgos();
- const cameraAlgoStore = useCameraAlgoStore();
- const {
- selectedAlgoId,
- selectedAlgoDetail,
- metaObjList,
- markedTimeRangeIds,
- markedParamCardIds,
- } = storeToRefs(cameraAlgoStore);
- interface Param {
- /** 算法id */
- algoId: number;
- /** 检测频率,单位是秒 */
- detectionFrequency: number;
- /** 检测时间段,可以有多个,转化为字符串,格式为 09:00-10:00;11:00-12:00 */
- detectionTime: string;
- /** 电子围栏是否开启 */
- electronicFence: FENCE_ENBALED_STATUS;
- /** 算法是否启用 */
- status: ALGO_ENABLED_STATUS;
- }
- const emits = defineEmits<{
- (e: 'onSubmit', param: Param): Promise<unknown>;
- (e: 'onCancel', algoId: number): Promise<unknown>;
- }>();
- 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 unEmptyLabels = computed(() => {
- return selectedAlgoDetail.value.metaValues.filter((item) => item.label);
- });
- const handleAddMetaObj = () => {
- 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;
- }
- // 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);
- };
- 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();
- };
- const checkFrequencyValid = () => {
- selectedAlgoDetail.value.detectionFrequency = Math.ceil(
- selectedAlgoDetail.value.detectionFrequency,
- );
- };
- const checkTimeWindowValid = () => {
- selectedAlgoDetail.value.timeWindow = Math.ceil(selectedAlgoDetail.value.timeWindow || 1);
- };
- 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];
- const res = await item.checkValid();
- 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.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);
- };
- const handleRemoveAlgo = () => {
- if (!selectedAlgoId.value) return;
- const el = document.getElementById('algoCardWrapper') as HTMLElement;
- ElMessageBox.confirm(
- '<strong>确认取消算法配置吗?</strong><br />取消后配置的参数将不会被保存。',
- '',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- dangerouslyUseHTMLString: true,
- appendTo: el,
- },
- )
- .then(() => {
- emits('onCancel', selectedAlgoId.value!);
- })
- .catch(() => {});
- };
- </script>
- <style scoped>
- .algoCardWrapper {
- border: 1px solid #ccc;
- border-radius: 4px;
- /* padding: 10px; */
- width: 780px;
- }
- .algoRow {
- display: flex;
- margin: 10px 0;
- }
- .algoLabel {
- margin-right: 10px;
- width: 90px;
- text-align: right;
- }
- .algoTimeContent {
- display: flex;
- }
- .presetList {
- font-size: 12px;
- display: none;
- }
- .removeIcon {
- margin-left: 10px;
- color: #8c8c8c;
- cursor: pointer;
- }
- .addTimeIcon {
- cursor: pointer;
- }
- .algoCardTitle {
- background: #f0f2f5;
- display: flex;
- justify-content: space-between;
- padding: 4px 20px;
- align-items: center;
- }
- .algoCardMain {
- padding: 10px 15px;
- }
- .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;
- cursor: pointer;
- }
- .addDisable {
- opacity: 0.5;
- cursor: not-allowed;
- }
- .timeList {
- width: 610px;
- display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
- }
- .tipIcon {
- margin-left: 10px;
- }
- :deep(.ml-4) {
- margin-left: 0 !important;
- }
- :deep(.el-message-box__status.el-icon) {
- top: 0 !important;
- transform: none !important;
- }
- </style>
|