|
@@ -0,0 +1,213 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="cameraTreeWrapper">
|
|
|
|
|
+ <div class="cameraTreeTitle">
|
|
|
|
|
+ <span>场景树</span>
|
|
|
|
|
+ <span class="detail-num" v-if="props.total"
|
|
|
|
|
+ >(总相机:{{ props.total }} 未联网:{{ props.noNetworkingNum }} 未进入平台:{{
|
|
|
|
|
+ props.noIntegrationNum
|
|
|
|
|
+ }})</span
|
|
|
|
|
+ ></div
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="cameraTreeInputWrapper">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="filterText"
|
|
|
|
|
+ placeholder="请输入相机的名称进行搜索"
|
|
|
|
|
+ :suffix-icon="Search"
|
|
|
|
|
+ class="filterTextInput"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-scrollbar class="tree-scroll">
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ :data="props.cameraTree"
|
|
|
|
|
+ :props="defaultProps"
|
|
|
|
|
+ @node-click="handleNodeClick"
|
|
|
|
|
+ node-key="code"
|
|
|
|
|
+ :default-expand-all="true"
|
|
|
|
|
+ :filter-node-method="filterNode"
|
|
|
|
|
+ ref="treeRef"
|
|
|
|
|
+ v-loading="props.loading"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #default="{ node, data }">
|
|
|
|
|
+ <span class="flexCenter" :class="{ integrationState: data.integrationState === 1 }">
|
|
|
|
|
+ <span
|
|
|
|
|
+ v-if="data.nodeType === CameraTreeNodeType.camera"
|
|
|
|
|
+ class="iconWrapper flexCenter"
|
|
|
|
|
+ >
|
|
|
|
|
+ <span
|
|
|
|
|
+ class="cameraCommon"
|
|
|
|
|
+ :class="{
|
|
|
|
|
+ cameraSelect: isSelect(data),
|
|
|
|
|
+ }"
|
|
|
|
|
+ ></span>
|
|
|
|
|
+
|
|
|
|
|
+ <el-icon
|
|
|
|
|
+ class="cameraIcon"
|
|
|
|
|
+ :class="{
|
|
|
|
|
+ iconSelect: isSelect(data),
|
|
|
|
|
+ }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <VideoCamera />
|
|
|
|
|
+ </el-icon>
|
|
|
|
|
+ <el-icon class="invalidCamera" v-if="isInvalid(data)"><WarningFilled /></el-icon>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ {{ node.label }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-tree>
|
|
|
|
|
+ </el-scrollbar>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+ import { ElMessage, ElTree } from 'element-plus';
|
|
|
|
|
+ import { ref, watch } from 'vue';
|
|
|
|
|
+ import { useRouteQuery } from '@vueuse/router';
|
|
|
|
|
+ import { Search, VideoCamera, WarningFilled } from '@element-plus/icons-vue';
|
|
|
|
|
+
|
|
|
|
|
+ import useCameraDetail from '../../store/useCameraDetailStore';
|
|
|
|
|
+ import { CameraTree, CameraTreeNodeType } from '@/api/camera/camera-preview';
|
|
|
|
|
+
|
|
|
|
|
+ const props = defineProps<{
|
|
|
|
|
+ cameraTree: Tree[];
|
|
|
|
|
+ loading: boolean;
|
|
|
|
|
+ total: number;
|
|
|
|
|
+ noNetworkingNum?: number;
|
|
|
|
|
+ noIntegrationNum?: number;
|
|
|
|
|
+ }>();
|
|
|
|
|
+
|
|
|
|
|
+ interface Tree {
|
|
|
|
|
+ [key: string]: any;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const { setDetail } = useCameraDetail();
|
|
|
|
|
+
|
|
|
|
|
+ const cameraId = useRouteQuery('cameraId');
|
|
|
|
|
+ const defaultProps = {
|
|
|
|
|
+ children: 'children',
|
|
|
|
|
+ label: 'name',
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleNodeClick = (e: CameraTree) => {
|
|
|
|
|
+ console.log('e', e);
|
|
|
|
|
+ if (e.integrationState === 1) {
|
|
|
|
|
+ ElMessage.error('该相机未进入平台');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (e.nodeType === CameraTreeNodeType.camera) {
|
|
|
|
|
+ cameraId.value = String(e.id);
|
|
|
|
|
+ setDetail(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const isSelect = (data) =>
|
|
|
|
|
+ data.nodeType === CameraTreeNodeType.camera && data.id === Number(cameraId.value);
|
|
|
|
|
+
|
|
|
|
|
+ const filterText = ref('');
|
|
|
|
|
+ const treeRef = ref<InstanceType<typeof ElTree>>();
|
|
|
|
|
+
|
|
|
|
|
+ watch(filterText, (val) => {
|
|
|
|
|
+ childrenNodeList.value = [];
|
|
|
|
|
+ treeRef.value!.filter(val);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const childrenNodeList = ref<string[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+ function extractCodes(data: any[], codes: string[] = []) {
|
|
|
|
|
+ data.forEach((item) => {
|
|
|
|
|
+ codes.push(item.data.code);
|
|
|
|
|
+ if (item.childNodes) {
|
|
|
|
|
+ extractCodes(item.childNodes, codes);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return codes;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const filterNode = (value: string, data: Tree, node) => {
|
|
|
|
|
+ if (!value) return true;
|
|
|
|
|
+ // 检查当前节点的 label 是否包含关键词
|
|
|
|
|
+ const labelMatch = data.name.includes(value);
|
|
|
|
|
+
|
|
|
|
|
+ if (labelMatch) {
|
|
|
|
|
+ if (node.childNodes && node.childNodes.length > 0) {
|
|
|
|
|
+ childrenNodeList.value = extractCodes(node.childNodes, []);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (childrenNodeList.value.includes(data.code)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return labelMatch;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const isInvalid = (data) => {
|
|
|
|
|
+ return data.networkingState !== 0;
|
|
|
|
|
+ };
|
|
|
|
|
+</script>
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+ .cameraCommon {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-right: 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tree-scroll {
|
|
|
|
|
+ height: calc(100vh - 64px - 170px);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cameraSelect {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ background: #0052d9;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ margin-right: 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cameraTreeTitle {
|
|
|
|
|
+ background: #f0f2f5;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .detail-num {
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+ margin-left: 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cameraTreeInputWrapper {
|
|
|
|
|
+ padding: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .filterTextInput {
|
|
|
|
|
+ margin: 8px 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .flexCenter {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .integrationState {
|
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
|
+ color: #ccc;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cameraIcon {
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .iconSelect {
|
|
|
|
|
+ color: #0052d9;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .iconWrapper {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .invalidCamera {
|
|
|
|
|
+ color: #dd5869;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 2px;
|
|
|
|
|
+ top: -4px;
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|