| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- <template>
- <div class="cameraTreeWrapper">
- <div class="cameraTreeTitle">
- <span>场景树</span>
- <span class="detail-num" v-if="totalNum">
- (总相机:{{ totalNum }} 未联网:{{ noNetworkingNum }} 未进入平台:{{ noIntegrationNum }})
- </span>
- </div>
- <div class="cameraTreeInputWrapper">
- <el-input
- class="filterTextInput"
- v-model="queryForm.queryString"
- placeholder="请输入相机名称/设备ID/算法名称"
- @keyup.enter.native="handleSearchCamera"
- clearable
- @clear="handleSearchCamera"
- >
- <template #suffix>
- <el-icon class="el-input__icon" @click="handleSearchCamera"><search /></el-icon>
- </template>
- </el-input>
- <div class="cameraTreeCheckboxWrapper">
- <el-checkbox
- v-model="queryForm.isEnableAlgo"
- label="添加算法"
- @change="handleSearchCamera"
- />
- <el-checkbox
- v-model="queryForm.isEnableRender"
- label="开启渲染"
- @change="handleSearchCamera"
- />
- <el-button v-if="isSearch" text type="primary" @click="handleCollapseTree">收起</el-button>
- <el-button v-if="!isSearch" text type="primary" @click="handleExpandTree">展开</el-button>
- </div>
- <el-scrollbar class="tree-scroll">
- <el-tree
- v-if="treeCollapse"
- ref="treeRef"
- node-key="code"
- :data="cameraTreeTemp"
- :props="defaultProps"
- :default-expand-all="isSearch"
- :default-expanded-keys="workSpaceKeys"
- @node-click="handleNodeClick"
- >
- <template #default="{ node, data }">
- <span
- class="flexCenter"
- :class="{ integrationState: data.integrationState === 1, nodeSelect: isSelect(data) }"
- >
- <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 { nextTick, onMounted, onUnmounted, ref } from 'vue';
- import { ElMessage, ElTree } from 'element-plus';
- import { Search, VideoCamera, WarningFilled } from '@element-plus/icons-vue';
- import { useRouteQuery } from '@vueuse/router';
- import useCameraStatus from '../../store/useCameraStatus';
- import {
- CameraTree,
- CameraTreeNodeType,
- CameraQueryForm,
- getCameraTree,
- } from '@/api/camera/camera-preview';
- const cameraId = useRouteQuery('cameraId');
- const cameraStatus = useCameraStatus();
- const { noNetworkingNum, openInterval, closeInterval } = cameraStatus;
- const queryForm = ref<CameraQueryForm>({
- isEnableAlgo: false,
- isEnableRender: false,
- queryString: '',
- });
- const totalNum = ref(0); // 总相机数
- const noIntegrationNum = ref(0); // 未进入平台相机数
- const cameraTree = ref<CameraTree[]>([]); // 保存从接口获取的所有树节点信息
- const cameraTreeTemp = ref<CameraTree[]>([]); // 保存修改name之后的树
- const codeShowList = ref<string[]>([]); // 保存当前所有相机code列表
- const isSearch = ref(true); // 默认展开全部
- const treeCollapse = ref(true);
- const workSpaceKeys = ref<string[]>([]); // 当前要收起的工位code
- const treeRef = ref<InstanceType<typeof ElTree>>();
- const defaultProps = {
- children: 'children',
- label: 'name',
- };
- const handleNodeClick = (e: CameraTree) => {
- if (e.integrationState === 1) {
- ElMessage.error('该相机未进入平台');
- } else {
- if (e.nodeType === CameraTreeNodeType.camera) {
- cameraId.value = String(e.id);
- }
- }
- };
- const isSelect = (data) =>
- data.nodeType === CameraTreeNodeType.camera && data.id === Number(cameraId.value);
- const isInvalid = (data) => {
- return data.networkingState !== 0;
- };
- // 把树节点中所有 nodeType = camera 的 name 替换成 name + code
- function getCameraNameCode(data) {
- const cameraNameCode = data;
- for (let i = 0; i < data.length; i++) {
- const node = cameraNameCode[i];
- if (node.nodeType === 'camera') {
- node.name = node.name + ` [${node.code}] `;
- }
- if (node.children && node.children.length > 0) {
- getCameraNameCode(node.children);
- }
- }
- return cameraNameCode;
- }
- // 获取当前树结构下总相机code列表
- function getCameraList(data) {
- let cameraList = [] as string[];
- for (let i = 0; i < data.length; i++) {
- const node = data[i];
- if (node.nodeType === 'camera') {
- cameraList.push(node.code);
- }
- if (node.children && node.children.length > 0) {
- const childCameraList = getCameraList(node.children);
- cameraList.push(...childCameraList);
- }
- }
- return cameraList;
- }
- // 获取当前树结构下nodeType=workshop的节点code集合
- function getWorkShopCodeList(data) {
- let tempCodeList = [] as string[];
- for (let i = 0; i < data.length; i++) {
- const node = data[i];
- if (node.nodeType === 'workshop') tempCodeList.push(node.code);
- if (node.children && node.children.length > 0) {
- const childList = getWorkShopCodeList(node.children);
- tempCodeList.push(...childList);
- }
- }
- return tempCodeList;
- }
- // 更新/获取未进入平台相机数量
- function updateNetworkingState(data, targetData) {
- let integrationCount = 0;
- for (let i = 0; i < data.length; i++) {
- const node = data[i];
- const matchedNode = targetData.find((item) => item.cameraCode === node.code);
- if (matchedNode) {
- node.networkingState = matchedNode.status;
- node.integrationState = matchedNode.integrationState;
- }
- if (node.integrationState === 1) {
- integrationCount++;
- }
- if (node.children && node.children.length > 0) {
- const childIntegrationCount = updateNetworkingState(node.children, targetData);
- integrationCount += childIntegrationCount;
- }
- }
- noIntegrationNum.value = integrationCount;
- return integrationCount;
- }
- // 输入框回车搜索 + checkbox 搜索
- const handleSearchCamera = async () => {
- treeCollapse.value = false;
- workSpaceKeys.value = [];
- await getCameraData(queryForm.value);
- nextTick(() => {
- isSearch.value = true;
- treeCollapse.value = true;
- });
- };
- // 收起相机,收起到工位
- const handleCollapseTree = () => {
- treeCollapse.value = false;
- workSpaceKeys.value = getWorkShopCodeList(cameraTreeTemp.value);
- nextTick(() => {
- isSearch.value = false;
- treeCollapse.value = true;
- });
- };
- // 展开相机
- const handleExpandTree = () => {
- treeCollapse.value = false;
- workSpaceKeys.value = [];
- nextTick(() => {
- isSearch.value = true;
- treeCollapse.value = true;
- });
- };
- const getCameraData = async (tempQuery) => {
- await getCameraTree(tempQuery).then((res) => {
- cameraTree.value = res;
- cameraTreeTemp.value = getCameraNameCode(res);
- codeShowList.value = getCameraList(res);
- totalNum.value = codeShowList.value.length;
- openInterval(codeShowList.value, (targetData) => {
- updateNetworkingState(cameraTree.value, targetData);
- });
- });
- };
- onMounted(() => {
- getCameraData(queryForm.value);
- });
- onUnmounted(() => {
- closeInterval();
- });
- </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;
- }
- .nodeSelect {
- color: #0052d9;
- }
- .el-input__icon {
- cursor: pointer;
- }
- .el-input__icon:hover {
- color: #0052d9;
- }
- .cameraTreeCheckboxWrapper {
- display: flex;
- justify-content: space-between;
- .el-checkbox {
- margin-right: 0;
- }
- }
- </style>
|