Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 67dccc5011 | |||
| a0048411a4 | |||
| d177956edd | |||
| d1b973be15 | |||
| 65603d469d | |||
| a92e47bc18 |
@@ -0,0 +1 @@
|
||||
export * from './vimp-client';
|
||||
@@ -1,16 +1,8 @@
|
||||
import type { VimpChannel, VimpStation } from './model';
|
||||
import type { AxiosError, AxiosRequestConfig, CreateAxiosDefaults } from 'axios';
|
||||
import axios from 'axios';
|
||||
import type { VimpResponse, VimpResult } from '../../types';
|
||||
|
||||
interface VimpResult<T = unknown> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
type VimpResponse<T> = [err: AxiosError | null, data: T | null, resp: VimpResult<T> | null];
|
||||
|
||||
const createVimpClient = (config?: CreateAxiosDefaults) => {
|
||||
export const createVimpClient = (config?: CreateAxiosDefaults) => {
|
||||
const instance = axios.create(config);
|
||||
|
||||
const vimpPost = <T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>> & { retRaw?: boolean; upload?: boolean }): Promise<VimpResponse<T>> => {
|
||||
@@ -38,7 +30,7 @@ const createVimpClient = (config?: CreateAxiosDefaults) => {
|
||||
};
|
||||
};
|
||||
|
||||
const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
|
||||
export const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
|
||||
const [err, data, result] = resp;
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
@@ -51,21 +43,3 @@ const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
|
||||
export const vimpClient = createVimpClient({
|
||||
baseURL: `/vimp/api/client`,
|
||||
});
|
||||
|
||||
export const catalogAllDeviceApi = async (options?: { signal?: AbortSignal }) => {
|
||||
const { signal } = options ?? {};
|
||||
const client = vimpClient;
|
||||
const endpoint = `/catalog/allDevice`;
|
||||
const resp = await client.post<VimpStation[]>(endpoint, {}, { signal });
|
||||
const data = unwrapVimpResponse(resp);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const catalogChannelApi = async (code: string, options?: { signal?: AbortSignal }) => {
|
||||
const { signal } = options ?? {};
|
||||
const client = vimpClient;
|
||||
const endpoint = `/catalog/channel`;
|
||||
const resp = await client.post<VimpChannel[]>(endpoint, { code, time: '' }, { signal });
|
||||
const data = unwrapVimpResponse(resp);
|
||||
return data;
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './client';
|
||||
export * from './model';
|
||||
export * from './query';
|
||||
export * from './request';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './vimp-channel';
|
||||
export * from './vimp-station';
|
||||
@@ -1,9 +1,3 @@
|
||||
export interface VimpStation {
|
||||
code: string;
|
||||
name: string;
|
||||
online: boolean;
|
||||
}
|
||||
|
||||
export interface VimpChannel {
|
||||
address: string;
|
||||
block: string;
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface VimpStation {
|
||||
code: string;
|
||||
name: string;
|
||||
online: boolean;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useQuery } from '@tanstack/vue-query';
|
||||
import { computed } from 'vue';
|
||||
import { catalogChannelApi, catalogAllDeviceApi } from './request';
|
||||
import { catalogChannelApi, catalogAllDeviceApi } from '../request';
|
||||
import type { AxiosRequestConfig } from 'axios';
|
||||
import axios from 'axios';
|
||||
import type { CodeArea, CodeLines, CodeSites } from '../types';
|
||||
import type { VimpChannel } from '.';
|
||||
import { useCameraStore, useAlarmStore } from '../stores';
|
||||
import type { CodeArea, CodeLines, CodeSites } from '../../types';
|
||||
import { useCameraStore, useAlarmStore } from '../../stores';
|
||||
import type { VimpChannel } from '../model';
|
||||
|
||||
export const useVimpDeviceQuery = () => {
|
||||
export const useDeviceCenterQuery = () => {
|
||||
const cameraStore = useCameraStore();
|
||||
const alarmStore = useAlarmStore();
|
||||
|
||||
@@ -68,12 +68,8 @@ export const useVimpDeviceQuery = () => {
|
||||
}
|
||||
});
|
||||
|
||||
if (cameras.length > 0) {
|
||||
siteCamerasMap[site.code] = cameras;
|
||||
}
|
||||
if (alarms.length > 0) {
|
||||
siteAlarmsMap[site.code] = alarms;
|
||||
}
|
||||
siteCamerasMap[site.code] = cameras;
|
||||
siteAlarmsMap[site.code] = alarms;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './device-center-query';
|
||||
@@ -0,0 +1,11 @@
|
||||
import { unwrapVimpResponse, vimpClient } from '../client';
|
||||
import type { VimpStation } from '../model';
|
||||
|
||||
export const catalogAllDeviceApi = async (options?: { signal?: AbortSignal }) => {
|
||||
const { signal } = options ?? {};
|
||||
const client = vimpClient;
|
||||
const endpoint = `/catalog/allDevice`;
|
||||
const resp = await client.post<VimpStation[]>(endpoint, {}, { signal });
|
||||
const data = unwrapVimpResponse(resp);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { unwrapVimpResponse, vimpClient } from '../client';
|
||||
import type { VimpChannel } from '../model';
|
||||
|
||||
export const catalogChannelApi = async (code: string, options?: { signal?: AbortSignal }) => {
|
||||
const { signal } = options ?? {};
|
||||
const client = vimpClient;
|
||||
const endpoint = `/catalog/channel`;
|
||||
const resp = await client.post<VimpChannel[]>(endpoint, { code, time: '' }, { signal });
|
||||
const data = unwrapVimpResponse(resp);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './catalog.channel';
|
||||
export * from './catalog.all-device';
|
||||
@@ -1,19 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui';
|
||||
import { useVimpDeviceQuery } from '../api/query';
|
||||
import type { VimpChannel, VimpStation } from '../api/model';
|
||||
import { h, type CSSProperties } from 'vue';
|
||||
import { hasOwn } from '@vueuse/core';
|
||||
import { useAlarmStore } from '../stores';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useDeviceCenterQuery } from '../apis';
|
||||
import { isAlarmNode, isAlarmSiteNode, isAlarmAreaNode } from '../types';
|
||||
|
||||
const { isLoading } = useVimpDeviceQuery();
|
||||
const { isLoading } = useDeviceCenterQuery();
|
||||
|
||||
const alarmStore = useAlarmStore();
|
||||
const { lineTabPanes } = storeToRefs(alarmStore);
|
||||
|
||||
const selectedDeviceGbCode = defineModel<[string]>('selectedDeviceGbCode', { default: () => [''] });
|
||||
|
||||
const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) => {
|
||||
const hasChildren = (option.children?.length ?? 0) > 0;
|
||||
if (hasChildren) {
|
||||
@@ -25,8 +22,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
|
||||
|
||||
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
// 是车站节点
|
||||
if (hasOwn(option, 'online')) {
|
||||
const siteOnline = option['online'] as boolean;
|
||||
if (isAlarmSiteNode(option)) {
|
||||
const siteOnline = option.online;
|
||||
const siteNodeStyle: CSSProperties = {
|
||||
opacity: siteOnline ? 1 : 0.5,
|
||||
};
|
||||
@@ -34,8 +31,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
}
|
||||
|
||||
// 是中间节点(一级/二级区域)
|
||||
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) {
|
||||
const site = option['site'] as VimpStation;
|
||||
if (isAlarmAreaNode(option)) {
|
||||
const site = option.site;
|
||||
const nodeStyle: CSSProperties = {
|
||||
opacity: site.online ? 1 : 0.5,
|
||||
};
|
||||
@@ -43,9 +40,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
}
|
||||
|
||||
// 是警报器节点
|
||||
if (hasOwn(option, 'alarm') && hasOwn(option, 'site')) {
|
||||
const alarm = option['alarm'] as VimpChannel;
|
||||
const site = option['site'] as VimpStation;
|
||||
if (isAlarmNode(option)) {
|
||||
const alarm = option.alarm;
|
||||
const site = option.site;
|
||||
|
||||
const alarmOnline = () => {
|
||||
return alarm.status === 1 && site.online;
|
||||
@@ -59,14 +56,13 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
'div',
|
||||
{
|
||||
style: alarmNodeStyle,
|
||||
draggable: alarm.status === 1,
|
||||
draggable: alarmOnline(),
|
||||
onDblclick() {
|
||||
if (alarm.status === 0) return;
|
||||
selectedDeviceGbCode.value = [alarm.code];
|
||||
if (!alarmOnline()) return;
|
||||
window.$message.info(`查看警报器:${JSON.stringify({ code: alarm.code, name: alarm.name })}`);
|
||||
},
|
||||
onDragstart(event) {
|
||||
if (alarm.status === 0) return;
|
||||
if (!alarmOnline()) return;
|
||||
console.log(event);
|
||||
event.dataTransfer?.setData('type', 'alarm');
|
||||
event.dataTransfer?.setData('code', alarm.code);
|
||||
@@ -93,7 +89,6 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
show-line
|
||||
virtual-scroll
|
||||
style="height: 100%"
|
||||
v-model:selected-keys="selectedDeviceGbCode"
|
||||
:data="lineTabPanes.at(0)?.alarmTree"
|
||||
:override-default-node-click-behavior="overrideNodeClickBehavior"
|
||||
:render-label="renderNodeLabel"
|
||||
@@ -108,7 +103,6 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
show-line
|
||||
virtual-scroll
|
||||
style="height: 100%"
|
||||
v-model:selected-keys="selectedDeviceGbCode"
|
||||
:data="alarmTree"
|
||||
:override-default-node-click-behavior="overrideNodeClickBehavior"
|
||||
:render-label="renderNodeLabel"
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui';
|
||||
import { useVimpDeviceQuery } from '../api/query';
|
||||
import type { VimpChannel, VimpStation } from '../api/model';
|
||||
import { h, type CSSProperties } from 'vue';
|
||||
import { hasOwn } from '@vueuse/core';
|
||||
import { useCameraStore } from '../stores';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useDeviceCenterQuery } from '../apis';
|
||||
import { isCameraNode, isCameraSiteNode, isCameraAreaNode } from '../types';
|
||||
|
||||
const { isLoading } = useVimpDeviceQuery();
|
||||
const { isLoading } = useDeviceCenterQuery();
|
||||
|
||||
const cameraStore = useCameraStore();
|
||||
const { lineTabPanes } = storeToRefs(cameraStore);
|
||||
|
||||
const selectedDeviceGbCode = defineModel<[string]>('selectedDeviceGbCode', { default: () => [''] });
|
||||
|
||||
const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) => {
|
||||
const hasChildren = (option.children?.length ?? 0) > 0;
|
||||
if (hasChildren) {
|
||||
@@ -25,8 +22,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
|
||||
|
||||
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
// 是车站节点
|
||||
if (hasOwn(option, 'online')) {
|
||||
const siteOnline = option['online'] as boolean;
|
||||
if (isCameraSiteNode(option)) {
|
||||
const siteOnline = option.online;
|
||||
const siteNodeStyle: CSSProperties = {
|
||||
opacity: siteOnline ? 1 : 0.5,
|
||||
};
|
||||
@@ -34,8 +31,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
}
|
||||
|
||||
// 是中间节点(一级/二级区域)
|
||||
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) {
|
||||
const site = option['site'] as VimpStation;
|
||||
if (isCameraAreaNode(option)) {
|
||||
const site = option.site;
|
||||
const nodeStyle: CSSProperties = {
|
||||
opacity: site.online ? 1 : 0.5,
|
||||
};
|
||||
@@ -43,9 +40,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
}
|
||||
|
||||
// 是摄像机节点
|
||||
if (hasOwn(option, 'camera') && hasOwn(option, 'site')) {
|
||||
const camera = option['camera'] as VimpChannel;
|
||||
const site = option['site'] as VimpStation;
|
||||
if (isCameraNode(option)) {
|
||||
const camera = option.camera;
|
||||
const site = option.site;
|
||||
|
||||
const cameraOnline = () => {
|
||||
return camera.status === 1 && site.online;
|
||||
@@ -59,14 +56,13 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
'div',
|
||||
{
|
||||
style: cameraNodeStyle,
|
||||
draggable: camera.status === 1,
|
||||
draggable: cameraOnline(),
|
||||
onDblclick() {
|
||||
if (camera.status === 0) return;
|
||||
selectedDeviceGbCode.value = [camera.code];
|
||||
if (!cameraOnline()) return;
|
||||
window.$message.info(`播放:${JSON.stringify({ code: camera.code, name: camera.name })}`);
|
||||
},
|
||||
onDragstart(event) {
|
||||
if (camera.status === 0) return;
|
||||
if (!cameraOnline()) return;
|
||||
console.log(event);
|
||||
event.dataTransfer?.setData('type', 'camera');
|
||||
event.dataTransfer?.setData('code', camera.code);
|
||||
@@ -93,7 +89,6 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
show-line
|
||||
virtual-scroll
|
||||
style="height: 100%"
|
||||
v-model:selected-keys="selectedDeviceGbCode"
|
||||
:data="lineTabPanes.at(0)?.cameraTree"
|
||||
:override-default-node-click-behavior="overrideNodeClickBehavior"
|
||||
:render-label="renderNodeLabel"
|
||||
@@ -108,7 +103,6 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
|
||||
show-line
|
||||
virtual-scroll
|
||||
style="height: 100%"
|
||||
v-model:selected-keys="selectedDeviceGbCode"
|
||||
:data="cameraTree"
|
||||
:override-default-node-click-behavior="overrideNodeClickBehavior"
|
||||
:render-label="renderNodeLabel"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { VimpChannel, VimpStation } from '../api';
|
||||
import type { VimpChannel, VimpStation } from '../apis';
|
||||
import { h, ref } from 'vue';
|
||||
import type { AlarmAreaNodeOption, AlarmNodeOption, CodeArea, CodeLines, CodeSites, AlarmLineTabPane, AlarmSiteNodeOption, AlarmSubAreaNodeOption } from '../types';
|
||||
import type { AlarmMainAreaNodeOption, AlarmNodeOption, CodeArea, CodeLines, CodeSites, AlarmLineTabPane, AlarmSiteNodeOption, AlarmSubAreaNodeOption } from '../types';
|
||||
|
||||
interface BuildLineTabPanesParams {
|
||||
sites: VimpStation[] | null;
|
||||
@@ -79,22 +79,22 @@ export const useAlarmStore = defineStore('vimp-alarm', () => {
|
||||
}
|
||||
if (!siteArea) continue; // 如果还是未找到区域,则跳过该警报器
|
||||
|
||||
// 构造区域节点
|
||||
// 构造1级区域节点
|
||||
if (!siteNode.children?.find((areaNode) => areaNode.key === `${alarmSiteCode}${alarmMainAreaCode}`)) {
|
||||
const areaNode: AlarmAreaNodeOption = {
|
||||
const mainAreaNode: AlarmMainAreaNodeOption = {
|
||||
key: `${alarmSiteCode}${alarmMainAreaCode}`,
|
||||
label: siteArea.name,
|
||||
children: [],
|
||||
stats: { online: 0, offline: 0, total: 0 },
|
||||
site: site,
|
||||
};
|
||||
siteNode.children?.push(areaNode);
|
||||
siteNode.children?.push(mainAreaNode);
|
||||
}
|
||||
const areaNode = siteNode.children?.find((areaNode) => areaNode.key === `${alarmSiteCode}${alarmMainAreaCode}`);
|
||||
if (!areaNode) continue; // 如果区域节点不存在,则跳过该警报器
|
||||
const targetMainAreaNode = siteNode.children?.find((areaNode) => areaNode.key === `${alarmSiteCode}${alarmMainAreaCode}`);
|
||||
if (!targetMainAreaNode) continue; // 如果1级区域节点不存在,则跳过该警报器
|
||||
|
||||
// 构造子区域节点
|
||||
if (!areaNode.children?.find((subAreaNode) => subAreaNode.key === `${alarmSiteCode}${alarmAreaCode}`)) {
|
||||
// 构造2级区域节点
|
||||
if (!targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${alarmSiteCode}${alarmAreaCode}`)) {
|
||||
let subArea: CodeArea['subs'][number] | undefined = undefined;
|
||||
if (alarmSiteType === 'station') {
|
||||
subArea = codeStationAreas.find((area) => area.code === alarmMainAreaCode)?.subs.find((subArea) => subArea.code === alarmAreaCode);
|
||||
@@ -116,9 +116,9 @@ export const useAlarmStore = defineStore('vimp-alarm', () => {
|
||||
stats: { online: 0, offline: 0, total: 0 },
|
||||
site: site,
|
||||
};
|
||||
areaNode.children?.push(subAreaNode);
|
||||
targetMainAreaNode.children?.push(subAreaNode);
|
||||
}
|
||||
const subAreaNode = areaNode.children?.find((subAreaNode) => subAreaNode.key === `${alarmSiteCode}${alarmAreaCode}`);
|
||||
const subAreaNode = targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${alarmSiteCode}${alarmAreaCode}`);
|
||||
if (!subAreaNode) continue; // 如果子区域节点不存在,则跳过该警报器
|
||||
|
||||
// 构造警报器节点
|
||||
@@ -141,16 +141,16 @@ export const useAlarmStore = defineStore('vimp-alarm', () => {
|
||||
|
||||
// 统计站点、区域、子区域的在线/离线/总警报器数量
|
||||
siteNode.stats.total++;
|
||||
areaNode.stats.total++;
|
||||
targetMainAreaNode.stats.total++;
|
||||
subAreaNode.stats.total++;
|
||||
if (alarm.status === 1) {
|
||||
siteNode.stats.online++;
|
||||
areaNode.stats.online++;
|
||||
targetMainAreaNode.stats.online++;
|
||||
subAreaNode.stats.online++;
|
||||
}
|
||||
if (alarm.status === 0) {
|
||||
siteNode.stats.offline++;
|
||||
areaNode.stats.offline++;
|
||||
targetMainAreaNode.stats.offline++;
|
||||
subAreaNode.stats.offline++;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { VimpChannel, VimpStation } from '../api';
|
||||
import type { VimpChannel, VimpStation } from '../apis';
|
||||
import { h, ref } from 'vue';
|
||||
import type { CameraAreaNodeOption, CameraNodeOption, CodeArea, CodeLines, CodeSites, CameraLineTabPane, CameraSiteNodeOption, CameraSubAreaNodeOption } from '../types';
|
||||
import type { CameraMainAreaNodeOption, CameraNodeOption, CodeArea, CodeLines, CodeSites, CameraLineTabPane, CameraSiteNodeOption, CameraSubAreaNodeOption } from '../types';
|
||||
|
||||
interface BuildLineTabPanesParams {
|
||||
sites: VimpStation[] | null;
|
||||
@@ -79,22 +79,22 @@ export const useCameraStore = defineStore('vimp-camera', () => {
|
||||
}
|
||||
if (!siteArea) continue; // 如果还是未找到区域,则跳过该摄像机
|
||||
|
||||
// 构造区域节点
|
||||
// 构造1级区域节点
|
||||
if (!siteNode.children?.find((areaNode) => areaNode.key === `${cameraSiteCode}${cameraMainAreaCode}`)) {
|
||||
const areaNode: CameraAreaNodeOption = {
|
||||
const mainAreaNode: CameraMainAreaNodeOption = {
|
||||
key: `${cameraSiteCode}${cameraMainAreaCode}`,
|
||||
label: siteArea.name,
|
||||
children: [],
|
||||
stats: { online: 0, offline: 0, total: 0 },
|
||||
site: site,
|
||||
};
|
||||
siteNode.children?.push(areaNode);
|
||||
siteNode.children?.push(mainAreaNode);
|
||||
}
|
||||
const areaNode = siteNode.children?.find((areaNode) => areaNode.key === `${cameraSiteCode}${cameraMainAreaCode}`);
|
||||
if (!areaNode) continue; // 如果区域节点不存在,则跳过该摄像机
|
||||
const targetMainAreaNode = siteNode.children?.find((areaNode) => areaNode.key === `${cameraSiteCode}${cameraMainAreaCode}`);
|
||||
if (!targetMainAreaNode) continue; // 如果1级区域节点不存在,则跳过该摄像机
|
||||
|
||||
// 构造子区域节点
|
||||
if (!areaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`)) {
|
||||
// 构造2级区域节点
|
||||
if (!targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`)) {
|
||||
let subArea: CodeArea['subs'][number] | undefined = undefined;
|
||||
if (cameraSiteType === 'station') {
|
||||
subArea = codeStationAreas.find((area) => area.code === cameraMainAreaCode)?.subs.find((subArea) => subArea.code === cameraAreaCode);
|
||||
@@ -107,7 +107,7 @@ export const useCameraStore = defineStore('vimp-camera', () => {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (!subArea) continue; // 如果还是未找到子区域,则跳过该摄像机
|
||||
if (!subArea) continue; // 如果还是未找到2级区域,则跳过该摄像机
|
||||
|
||||
const subAreaNode: CameraSubAreaNodeOption = {
|
||||
key: `${cameraSiteCode}${cameraAreaCode}`,
|
||||
@@ -116,9 +116,9 @@ export const useCameraStore = defineStore('vimp-camera', () => {
|
||||
stats: { online: 0, offline: 0, total: 0 },
|
||||
site: site,
|
||||
};
|
||||
areaNode.children?.push(subAreaNode);
|
||||
targetMainAreaNode.children?.push(subAreaNode);
|
||||
}
|
||||
const subAreaNode = areaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`);
|
||||
const subAreaNode = targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`);
|
||||
if (!subAreaNode) continue; // 如果子区域节点不存在,则跳过该摄像机
|
||||
|
||||
// 构造摄像机节点
|
||||
@@ -143,16 +143,16 @@ export const useCameraStore = defineStore('vimp-camera', () => {
|
||||
|
||||
// 统计站点、区域、子区域的在线/离线/总摄像机数量
|
||||
siteNode.stats.total++;
|
||||
areaNode.stats.total++;
|
||||
targetMainAreaNode.stats.total++;
|
||||
subAreaNode.stats.total++;
|
||||
if (camera.status === 1) {
|
||||
siteNode.stats.online++;
|
||||
areaNode.stats.online++;
|
||||
targetMainAreaNode.stats.online++;
|
||||
subAreaNode.stats.online++;
|
||||
}
|
||||
if (camera.status === 0) {
|
||||
siteNode.stats.offline++;
|
||||
areaNode.stats.offline++;
|
||||
targetMainAreaNode.stats.offline++;
|
||||
subAreaNode.stats.offline++;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './camera';
|
||||
export * from './alarm';
|
||||
export * from './camera-store';
|
||||
export * from './alarm-store';
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { AxiosError } from 'axios';
|
||||
|
||||
export interface VimpResult<T = unknown> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
export type VimpResponse<T> = [err: AxiosError | null, data: T | null, resp: VimpResult<T> | null];
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { TabPaneProps, TreeOption } from 'naive-ui';
|
||||
import type { VimpChannel, VimpStation } from '../api';
|
||||
import type { VimpChannel, VimpStation } from '../apis/model';
|
||||
|
||||
export type SiteType = 'station' | 'parking' | 'occ' | 'train';
|
||||
export type CodeLines = Record<string, { name: string; color: string }>;
|
||||
@@ -27,18 +27,30 @@ export interface CameraSubAreaNodeOption extends TreeOption {
|
||||
site: VimpStation;
|
||||
}
|
||||
|
||||
export interface CameraAreaNodeOption extends TreeOption {
|
||||
export interface CameraMainAreaNodeOption extends TreeOption {
|
||||
children?: CameraSubAreaNodeOption[];
|
||||
stats: CountStats;
|
||||
site: VimpStation;
|
||||
}
|
||||
|
||||
export interface CameraSiteNodeOption extends TreeOption {
|
||||
children?: CameraAreaNodeOption[];
|
||||
children?: CameraMainAreaNodeOption[];
|
||||
stats: CountStats;
|
||||
online: boolean;
|
||||
}
|
||||
|
||||
export function isCameraSiteNode(option: TreeOption): option is CameraSiteNodeOption {
|
||||
return 'online' in option && !('camera' in option);
|
||||
}
|
||||
|
||||
export function isCameraAreaNode(option: TreeOption): option is CameraMainAreaNodeOption | CameraSubAreaNodeOption {
|
||||
return 'site' in option && !('camera' in option) && !('online' in option);
|
||||
}
|
||||
|
||||
export function isCameraNode(option: TreeOption): option is CameraNodeOption {
|
||||
return 'camera' in option && 'site' in option;
|
||||
}
|
||||
|
||||
export interface CameraLineTabPane extends TabPaneProps {
|
||||
lineCode: string;
|
||||
lineName: string;
|
||||
@@ -60,18 +72,30 @@ export interface AlarmSubAreaNodeOption extends TreeOption {
|
||||
site: VimpStation;
|
||||
}
|
||||
|
||||
export interface AlarmAreaNodeOption extends TreeOption {
|
||||
export interface AlarmMainAreaNodeOption extends TreeOption {
|
||||
children?: AlarmSubAreaNodeOption[];
|
||||
stats: CountStats;
|
||||
site: VimpStation;
|
||||
}
|
||||
|
||||
export interface AlarmSiteNodeOption extends TreeOption {
|
||||
children?: AlarmAreaNodeOption[];
|
||||
children?: AlarmMainAreaNodeOption[];
|
||||
stats: CountStats;
|
||||
online: boolean;
|
||||
}
|
||||
|
||||
export function isAlarmSiteNode(option: TreeOption): option is AlarmSiteNodeOption {
|
||||
return 'online' in option && !('alarm' in option);
|
||||
}
|
||||
|
||||
export function isAlarmAreaNode(option: TreeOption): option is AlarmMainAreaNodeOption | AlarmSubAreaNodeOption {
|
||||
return 'site' in option && !('alarm' in option) && !('online' in option);
|
||||
}
|
||||
|
||||
export function isAlarmNode(option: TreeOption): option is AlarmNodeOption {
|
||||
return 'alarm' in option && 'site' in option;
|
||||
}
|
||||
|
||||
export interface AlarmLineTabPane extends TabPaneProps {
|
||||
lineCode: string;
|
||||
lineName: string;
|
||||
@@ -1 +1,2 @@
|
||||
export * from './tree';
|
||||
export * from './axios';
|
||||
export * from './device-tree';
|
||||
|
||||
@@ -15,8 +15,6 @@ const resourceTabPanes: ResourceTabPane[] = [
|
||||
{ name: 'alarm', tab: '警报器', component: AlarmTree },
|
||||
];
|
||||
|
||||
const selectedDeviceGbCode = ref<[string]>(['']);
|
||||
|
||||
const onDragover = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
if (event.dataTransfer) {
|
||||
@@ -32,13 +30,11 @@ const onDrop = (event: DragEvent) => {
|
||||
const code = event.dataTransfer?.getData('code');
|
||||
if (!code) return;
|
||||
const name = event.dataTransfer?.getData('name');
|
||||
selectedDeviceGbCode.value = [code];
|
||||
window.$message.info(`播放:${JSON.stringify({ code, name })}`);
|
||||
} else if (type === 'alarm') {
|
||||
const code = event.dataTransfer?.getData('code');
|
||||
if (!code) return;
|
||||
const name = event.dataTransfer?.getData('name');
|
||||
selectedDeviceGbCode.value = [code];
|
||||
window.$message.info(`查看警报器:${JSON.stringify({ code, name })}`);
|
||||
} else {
|
||||
}
|
||||
@@ -51,7 +47,7 @@ const onDrop = (event: DragEvent) => {
|
||||
<NTabs :type="'line'" :placement="'left'" style="height: 100%">
|
||||
<NTabPane v-for="{ name: resourceName, tab: resourceTab, component } in resourceTabPanes" :key="resourceName" :tab="resourceTab" :name="resourceName">
|
||||
<template v-if="!!component">
|
||||
<component :is="component" v-model:selected-device-gb-code="selectedDeviceGbCode" />
|
||||
<component :is="component" />
|
||||
</template>
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
@@ -59,7 +55,6 @@ const onDrop = (event: DragEvent) => {
|
||||
<div style="flex: 1">
|
||||
<div style="height: 480px; background-color: #666; display: grid; place-items: center" @dragover="onDragover" @drop="onDrop">
|
||||
<div>这里是播放器</div>
|
||||
<div>{{ selectedDeviceGbCode }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user