Compare commits
17 Commits
main
...
ff35fda046
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff35fda046 | ||
|
|
b238af2c77 | ||
|
|
5b989fab0f | ||
|
|
24a7881b94 | ||
|
|
ee019c44a0 | ||
|
|
fbe79c4dfc | ||
|
|
eb0ee841cf | ||
|
|
d0b065d6ba | ||
|
|
fee9c5ef6c | ||
|
|
d565fd6a5f | ||
|
|
5b47734c3b | ||
|
|
3b13b93cec | ||
|
|
742d561c33 | ||
|
|
ec4dd8917f | ||
|
|
03d5fb3fcd | ||
|
|
073a29a83a | ||
|
|
495dc001a1 |
@@ -1,8 +1,8 @@
|
|||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
import { createHttpClient, getAppEnvConfig } from '@/utils';
|
import { getAppEnvConfig, RequestClient } from '@/utils';
|
||||||
import type { AxiosError } from 'axios';
|
import type { AxiosError } from 'axios';
|
||||||
|
|
||||||
export const ndmClient = createHttpClient({
|
export const ndmClient = new RequestClient({
|
||||||
requestInterceptor: async (config) => {
|
requestInterceptor: async (config) => {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { lampAuthorization, lampClientId, lampClientSecret } = getAppEnvConfig();
|
const { lampAuthorization, lampClientId, lampClientSecret } = getAppEnvConfig();
|
||||||
@@ -25,7 +25,7 @@ export const ndmClient = createHttpClient({
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
await userStore.lampLogin(stationCode);
|
await userStore.lampLogin(stationCode);
|
||||||
error.config.headers.token = userStore.lampLoginResultRecord?.[stationCode]?.token ?? '';
|
error.config.headers.token = userStore.lampLoginResultRecord?.[stationCode]?.token ?? '';
|
||||||
return ndmClient.clientInstance(error.config);
|
return ndmClient.requestInstance(error.config);
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
import { createHttpClient, getAppEnvConfig } from '@/utils';
|
import { getAppEnvConfig, RequestClient } from '@/utils';
|
||||||
import type { AxiosError } from 'axios';
|
import type { AxiosError } from 'axios';
|
||||||
|
|
||||||
export const userClient = createHttpClient({
|
export const userClient = new RequestClient({
|
||||||
requestInterceptor: (config) => {
|
requestInterceptor: (config) => {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { lampAuthorization, lampClientId, lampClientSecret } = getAppEnvConfig();
|
const { lampAuthorization, lampClientId, lampClientSecret } = getAppEnvConfig();
|
||||||
|
|||||||
@@ -19,12 +19,4 @@ export interface NdmSwitchPortInfo {
|
|||||||
outFlow: number;
|
outFlow: number;
|
||||||
portName: string;
|
portName: string;
|
||||||
upDown: number;
|
upDown: number;
|
||||||
|
|
||||||
lastChangeTime: string;
|
|
||||||
|
|
||||||
opticalTemperature?: number;
|
|
||||||
opticalVoltage?: number;
|
|
||||||
opticalBiasCurrent?: number;
|
|
||||||
opticalReceivePower?: number;
|
|
||||||
opticalTransmitPower?: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './diag';
|
export * from './diag';
|
||||||
export * from './link-description';
|
|
||||||
export * from './station';
|
export * from './station';
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import type { NdmCameraLinkDescription } from './ndm-camera-link-description';
|
|
||||||
import type { NdmSecurityBoxLinkDescription } from './ndm-security-box-link-description';
|
|
||||||
import type { NdmSwitchLinkDescription } from './ndm-switch-link-description';
|
|
||||||
|
|
||||||
export * from './link-description';
|
|
||||||
export * from './ndm-camera-link-description';
|
|
||||||
export * from './ndm-security-box-link-description';
|
|
||||||
export * from './ndm-switch-link-description';
|
|
||||||
|
|
||||||
export type NdmDeviceLinkDescription = NdmCameraLinkDescription | NdmSecurityBoxLinkDescription | NdmSwitchLinkDescription;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { DeviceStoreIndex } from '@/apis';
|
|
||||||
|
|
||||||
export interface LinkDescription {
|
|
||||||
upstream?: DeviceStoreIndex[];
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import type { LinkDescription } from './link-description';
|
|
||||||
|
|
||||||
export interface NdmCameraLinkDescription extends LinkDescription {}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { DeviceStoreIndex } from '@/apis';
|
|
||||||
import type { LinkDescription } from './link-description';
|
|
||||||
|
|
||||||
export interface NdmSecurityBoxLinkDescription extends LinkDescription {
|
|
||||||
downstream?: {
|
|
||||||
[circuitIndex: number]: DeviceStoreIndex;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { DeviceStoreIndex } from '@/apis';
|
|
||||||
import type { LinkDescription } from './link-description';
|
|
||||||
|
|
||||||
export interface NdmSwitchLinkDescription extends LinkDescription {
|
|
||||||
downstream?: {
|
|
||||||
[portName: string]: DeviceStoreIndex;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,13 +10,7 @@ import type {
|
|||||||
NdmVideoServerResultVO,
|
NdmVideoServerResultVO,
|
||||||
Station,
|
Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { DEVICE_TYPE_LITERALS, type DeviceType } from '@/enums';
|
import { DEVICE_TYPE_LITERALS } from '@/enums';
|
||||||
|
|
||||||
export interface DeviceStoreIndex {
|
|
||||||
stationCode: Station['code'];
|
|
||||||
deviceType: DeviceType;
|
|
||||||
deviceDbId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StationDevices {
|
export interface StationDevices {
|
||||||
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: NdmAlarmHostResultVO[];
|
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: NdmAlarmHostResultVO[];
|
||||||
|
|||||||
@@ -1,21 +1,45 @@
|
|||||||
import type { Nullable, Optional } from '@/types';
|
import type { Nullable } from '@/types';
|
||||||
import type { ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO } from '../../base';
|
import type { NdmAlarmHost, NdmAlarmHostPageQuery } from './alarm';
|
||||||
import type { NdmAlarmHost } from './alarm';
|
import type { NdmSecurityBox, NdmSecurityBoxPageQuery, NdmSwitch, NdmSwitchPageQuery } from './other';
|
||||||
import type { NdmSecurityBox, NdmSwitch } from './other';
|
import type { NdmNvr, NdmNvrPageQuery } from './storage';
|
||||||
import type { NdmNvr } from './storage';
|
import type {
|
||||||
import type { NdmCamera, NdmDecoder, NdmKeyboard, NdmMediaServer, NdmVideoServer } from './video';
|
NdmCamera,
|
||||||
|
NdmCameraPageQuery,
|
||||||
|
NdmDecoder,
|
||||||
|
NdmDecoderPageQuery,
|
||||||
|
NdmKeyboard,
|
||||||
|
NdmKeyboardPageQuery,
|
||||||
|
NdmMediaServer,
|
||||||
|
NdmMediaServerPageQuery,
|
||||||
|
NdmMediaServerResultVO,
|
||||||
|
NdmMediaServerSaveVO,
|
||||||
|
NdmMediaServerUpdateVO,
|
||||||
|
NdmVideoServer,
|
||||||
|
NdmVideoServerPageQuery,
|
||||||
|
NdmVideoServerResultVO,
|
||||||
|
NdmVideoServerSaveVO,
|
||||||
|
NdmVideoServerUpdateVO,
|
||||||
|
} from './video';
|
||||||
|
|
||||||
export type NdmDevice = NdmAlarmHost | NdmCamera | NdmDecoder | NdmKeyboard | NdmMediaServer | NdmNvr | NdmSecurityBox | NdmSwitch | NdmVideoServer;
|
export type NdmDevice = NdmAlarmHost | NdmCamera | NdmDecoder | NdmKeyboard | NdmMediaServer | NdmNvr | NdmSecurityBox | NdmSwitch | NdmVideoServer;
|
||||||
|
export type NdmDevicePageQuery =
|
||||||
|
| NdmAlarmHostPageQuery
|
||||||
|
| NdmCameraPageQuery
|
||||||
|
| NdmDecoderPageQuery
|
||||||
|
| NdmKeyboardPageQuery
|
||||||
|
| NdmMediaServerPageQuery
|
||||||
|
| NdmNvrPageQuery
|
||||||
|
| NdmSecurityBoxPageQuery
|
||||||
|
| NdmSwitchPageQuery
|
||||||
|
| NdmVideoServerPageQuery;
|
||||||
|
|
||||||
export type NdmDeviceResultVO = Nullable<NdmDevice>;
|
export type NdmDeviceResultVO = Nullable<NdmDevice>;
|
||||||
export type NdmDeviceSaveVO = Partial<Omit<NdmDevice, ReduceForSaveVO>>;
|
|
||||||
export type NdmDeviceUpdateVO = Optional<Omit<NdmDevice, ReduceForUpdateVO>>;
|
|
||||||
export type NdmDevicePageQuery = Partial<Omit<NdmDevice, ReduceForPageQuery>>;
|
|
||||||
|
|
||||||
export type NdmServer = NdmMediaServer | NdmVideoServer;
|
export type NdmServer = NdmMediaServer | NdmVideoServer;
|
||||||
export type NdmServerResultVO = Nullable<NdmServer>;
|
export type NdmServerResultVO = NdmMediaServerResultVO | NdmVideoServerResultVO;
|
||||||
export type NdmServerSaveVO = Partial<Omit<NdmServer, ReduceForSaveVO>>;
|
export type NdmServerSaveVO = NdmMediaServerSaveVO | NdmVideoServerSaveVO;
|
||||||
export type NdmServerUpdateVO = Optional<Omit<NdmServer, ReduceForUpdateVO>>;
|
export type NdmServerUpdateVO = NdmMediaServerUpdateVO | NdmVideoServerUpdateVO;
|
||||||
export type NdmServerPageQuery = Partial<Omit<NdmServer, ReduceForPageQuery>>;
|
export type NdmServerPageQuery = NdmMediaServerPageQuery | NdmVideoServerPageQuery;
|
||||||
|
|
||||||
export * from './alarm';
|
export * from './alarm';
|
||||||
export * from './icmp';
|
export * from './icmp';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from './ndm-call-log';
|
export * from './ndm-call-log';
|
||||||
export * from './ndm-device-alarm-log';
|
export * from './ndm-device-alarm-log';
|
||||||
export * from './ndm-device-alarm-snap-log';
|
|
||||||
export * from './ndm-icmp-log';
|
export * from './ndm-icmp-log';
|
||||||
export * from './ndm-record-check';
|
export * from './ndm-record-check';
|
||||||
export * from './ndm-snmp-log';
|
export * from './ndm-snmp-log';
|
||||||
|
|||||||
@@ -2,38 +2,11 @@ import type { BaseModel, ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO
|
|||||||
import type { Nullable } from '@/types';
|
import type { Nullable } from '@/types';
|
||||||
|
|
||||||
export interface NdmCallLog extends BaseModel {
|
export interface NdmCallLog extends BaseModel {
|
||||||
/**
|
|
||||||
* 调用者国标码
|
|
||||||
*/
|
|
||||||
sourceGbId: string;
|
sourceGbId: string;
|
||||||
/**
|
|
||||||
* 用户所属类别
|
|
||||||
*/
|
|
||||||
sourceType: string;
|
|
||||||
/**
|
|
||||||
* 被调用设备国标码
|
|
||||||
*/
|
|
||||||
targetGbId: string;
|
targetGbId: string;
|
||||||
/**
|
|
||||||
* 被调用设备名称
|
|
||||||
*/
|
|
||||||
targetName: string;
|
|
||||||
/**
|
|
||||||
* 调用方法
|
|
||||||
*/
|
|
||||||
method: string;
|
method: string;
|
||||||
/**
|
|
||||||
* message类型
|
|
||||||
*/
|
|
||||||
messageType: string;
|
messageType: string;
|
||||||
/**
|
|
||||||
* 操作类型
|
|
||||||
*/
|
|
||||||
cmdType: string;
|
cmdType: string;
|
||||||
/**
|
|
||||||
* 日志类型
|
|
||||||
*/
|
|
||||||
logType: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NdmCallLogResultVO = Nullable<NdmCallLog>;
|
export type NdmCallLogResultVO = Nullable<NdmCallLog>;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { BaseModel, ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO } from '@/apis';
|
|
||||||
import type { Nullable } from '@/types';
|
|
||||||
|
|
||||||
export interface NdmDeviceAlarmSnapLog extends BaseModel {
|
|
||||||
absoluteFilePath: string;
|
|
||||||
path: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NdmDeviceAlarmSnapLogResultVO = Nullable<NdmDeviceAlarmSnapLog>;
|
|
||||||
|
|
||||||
export type NdmDeviceAlarmSnapLogSaveVO = Partial<Omit<NdmDeviceAlarmSnapLog, ReduceForSaveVO>>;
|
|
||||||
|
|
||||||
export type NdmDeviceAlarmSnapLogUpdateVO = Partial<Omit<NdmDeviceAlarmSnapLog, ReduceForUpdateVO>>;
|
|
||||||
|
|
||||||
export type NdmDeviceAlarmSnapLogPageQuery = Partial<Omit<NdmDeviceAlarmSnapLog, ReduceForPageQuery>>;
|
|
||||||
@@ -2,57 +2,18 @@ import type { BaseModel, ReduceForSaveVO, ReduceForUpdateVO, ReduceForPageQuery
|
|||||||
import type { Nullable } from '@/types';
|
import type { Nullable } from '@/types';
|
||||||
|
|
||||||
export interface NdmVimpLog extends BaseModel {
|
export interface NdmVimpLog extends BaseModel {
|
||||||
/**
|
|
||||||
* 请求IP
|
|
||||||
*/
|
|
||||||
requestIp: string;
|
requestIp: string;
|
||||||
/**
|
|
||||||
* 操作内容
|
|
||||||
*/
|
|
||||||
description: string;
|
description: string;
|
||||||
/**
|
|
||||||
* 类路径
|
|
||||||
*/
|
|
||||||
classPath: string;
|
classPath: string;
|
||||||
/**
|
|
||||||
* 函数名
|
|
||||||
*/
|
|
||||||
methodName: string;
|
methodName: string;
|
||||||
/**
|
|
||||||
* 起始时间
|
|
||||||
*/
|
|
||||||
startTime: string;
|
startTime: string;
|
||||||
/**
|
|
||||||
* 结束时间
|
|
||||||
*/
|
|
||||||
endTime: string;
|
endTime: string;
|
||||||
/**
|
|
||||||
* 消耗时间
|
|
||||||
*/
|
|
||||||
consumedTime: string;
|
consumedTime: string;
|
||||||
/**
|
|
||||||
* 操作参数
|
|
||||||
*/
|
|
||||||
params: string;
|
params: string;
|
||||||
/**
|
|
||||||
* 操作结果
|
|
||||||
*/
|
|
||||||
result: string;
|
result: string;
|
||||||
/**
|
|
||||||
* 请求类型
|
|
||||||
*/
|
|
||||||
httpMethod: string;
|
httpMethod: string;
|
||||||
/**
|
|
||||||
* 请求用户
|
|
||||||
*/
|
|
||||||
userId: string;
|
userId: string;
|
||||||
/**
|
|
||||||
* 日志类型
|
|
||||||
*/
|
|
||||||
logType: number;
|
logType: number;
|
||||||
/**
|
|
||||||
* 目标国标码
|
|
||||||
*/
|
|
||||||
targetCode: string;
|
targetCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
export * from './invite-stream-type';
|
|
||||||
export * from './send-rtp-info';
|
|
||||||
export * from './snap-result';
|
export * from './snap-result';
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export type InviteStreamType = 'PLAY' | 'PLAYBACK' | 'DOWNLOAD' | 'PUSH' | 'PROXY' | 'CLOUD_RECORD_PUSH' | 'CLOUD_RECORD_PROXY';
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import type { InviteStreamType } from '@/apis';
|
|
||||||
import type { Nullable } from '@/types';
|
|
||||||
|
|
||||||
export type SendRtpInfo = Nullable<{
|
|
||||||
ip: string;
|
|
||||||
port: number;
|
|
||||||
ssrc: string;
|
|
||||||
platformId: string;
|
|
||||||
deviceId: string;
|
|
||||||
channelId: string;
|
|
||||||
app: string;
|
|
||||||
streamId: string;
|
|
||||||
/**
|
|
||||||
* 推流状态
|
|
||||||
* 0 等待设备推流上来
|
|
||||||
* 1 等待上级平台回复ack
|
|
||||||
* 2 推流中
|
|
||||||
*/
|
|
||||||
status: number;
|
|
||||||
/**
|
|
||||||
* 是否为tcp
|
|
||||||
*/
|
|
||||||
tcp: boolean;
|
|
||||||
/**
|
|
||||||
* 是否为tcp主动模式
|
|
||||||
*/
|
|
||||||
tcpActive: boolean;
|
|
||||||
localPort: number;
|
|
||||||
mediaServerId: string;
|
|
||||||
serverId: string;
|
|
||||||
callId: string;
|
|
||||||
fromTag: string;
|
|
||||||
toTag: string;
|
|
||||||
/**
|
|
||||||
* 发送时,rtp的pt(uint8_t),不传时默认为96
|
|
||||||
*/
|
|
||||||
pt: number;
|
|
||||||
/**
|
|
||||||
* 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es;
|
|
||||||
*/
|
|
||||||
usePs: boolean;
|
|
||||||
/**
|
|
||||||
* 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0
|
|
||||||
*/
|
|
||||||
onlyAudio: boolean;
|
|
||||||
/**
|
|
||||||
* 是否开启rtcp保活
|
|
||||||
*/
|
|
||||||
rtcp: boolean;
|
|
||||||
playType: InviteStreamType;
|
|
||||||
}>;
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
export interface SnapResult {
|
export interface SnapResult {
|
||||||
code: number;
|
|
||||||
msg: string;
|
|
||||||
data: {
|
|
||||||
absoluteFilePath: string;
|
absoluteFilePath: string;
|
||||||
path: string;
|
path: string;
|
||||||
url: string;
|
url: string;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { initStationDevices, ndmClient, userClient, type Station, type StationDevices } from '@/apis';
|
import { initStationDevices, ndmClient, userClient, type StationDevices } from '@/apis';
|
||||||
import { unwrapResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const getAllDevicesApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const getAllDevicesApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
|
|||||||
@@ -20,31 +20,40 @@ export const detailDeviceApi = async (device: NdmDeviceResultVO, options?: { sta
|
|||||||
const deviceType = tryGetDeviceType(deviceTypeCode);
|
const deviceType = tryGetDeviceType(deviceTypeCode);
|
||||||
if (!deviceType) throw new Error('未知的设备');
|
if (!deviceType) throw new Error('未知的设备');
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmAlarmHost) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmAlarmHost) {
|
||||||
return await detailAlarmHostApi(id, { stationCode, signal });
|
await detailAlarmHostApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmCamera) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmCamera) {
|
||||||
return await detailCameraApi(id, { stationCode, signal });
|
await detailCameraApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmDecoder) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmDecoder) {
|
||||||
return await detailDecoderApi(id, { stationCode, signal });
|
await detailDecoderApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmKeyboard) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmKeyboard) {
|
||||||
return await detailKeyboardApi(id, { stationCode, signal });
|
await detailKeyboardApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer) {
|
||||||
return await detailMediaServerApi(id, { stationCode, signal });
|
await detailMediaServerApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
||||||
return await detailNvrApi(id, { stationCode, signal });
|
await detailNvrApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmSecurityBox) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmSecurityBox) {
|
||||||
return await detailSecurityBoxApi(id, { stationCode, signal });
|
await detailSecurityBoxApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmSwitch) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmSwitch) {
|
||||||
return await detailSwitchApi(id, { stationCode, signal });
|
await detailSwitchApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer) {
|
||||||
return await detailVideoServerApi(id, { stationCode, signal });
|
await detailVideoServerApi(id, { stationCode, signal });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ export * from './detail-device';
|
|||||||
export * from './export-device';
|
export * from './export-device';
|
||||||
export * from './import-device';
|
export * from './import-device';
|
||||||
export * from './probe-device';
|
export * from './probe-device';
|
||||||
export * from './update-device';
|
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import {
|
|
||||||
updateAlarmHostApi,
|
|
||||||
updateCameraApi,
|
|
||||||
updateDecoderApi,
|
|
||||||
updateKeyboardApi,
|
|
||||||
updateMediaServerApi,
|
|
||||||
updateNvrApi,
|
|
||||||
updateSecurityBoxApi,
|
|
||||||
updateSwitchApi,
|
|
||||||
updateVideoServerApi,
|
|
||||||
type NdmDeviceResultVO,
|
|
||||||
type Station,
|
|
||||||
} from '@/apis';
|
|
||||||
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
|
||||||
|
|
||||||
export const updateDeviceApi = async (device: NdmDeviceResultVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }): Promise<NdmDeviceResultVO | undefined> => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const { id, deviceType: deviceTypeCode } = device;
|
|
||||||
if (!id || !deviceTypeCode) throw new Error('未知的设备');
|
|
||||||
const deviceType = tryGetDeviceType(deviceTypeCode);
|
|
||||||
if (!deviceType) throw new Error('未知的设备');
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmAlarmHost) {
|
|
||||||
await updateAlarmHostApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmCamera) {
|
|
||||||
await updateCameraApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmDecoder) {
|
|
||||||
await updateDecoderApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmKeyboard) {
|
|
||||||
await updateKeyboardApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer) {
|
|
||||||
await updateMediaServerApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
|
||||||
await updateNvrApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmSecurityBox) {
|
|
||||||
await updateSecurityBoxApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmSwitch) {
|
|
||||||
await updateSwitchApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer) {
|
|
||||||
await updateVideoServerApi(device, { stationCode, signal });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ndmClient, userClient, type Station } from '@/apis';
|
import { ndmClient, userClient, type Station } from '@/apis';
|
||||||
import { unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const resetMonitorScheduleApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const resetMonitorScheduleApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -7,5 +7,5 @@ export const resetMonitorScheduleApi = async (options?: { stationCode?: Station[
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmConstant/anyTenant/resetMonitorSchedule`;
|
const endpoint = `${prefix}/api/ndm/ndmConstant/anyTenant/resetMonitorSchedule`;
|
||||||
const resp = await client.get<void>(endpoint, { signal });
|
const resp = await client.get<void>(endpoint, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ndmClient, userClient, type Station } from '@/apis';
|
import { ndmClient, userClient, type Station } from '@/apis';
|
||||||
import { unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const verifyApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const verifyApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -7,5 +7,5 @@ export const verifyApi = async (options?: { stationCode?: Station['code']; signa
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmKeepAlive/verify`;
|
const endpoint = `${prefix}/api/ndm/ndmKeepAlive/verify`;
|
||||||
const resp = await client.post<void>(endpoint, {}, { retRaw: true, timeout: 5000, signal });
|
const resp = await client.post<void>(endpoint, {}, { retRaw: true, timeout: 5000, signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,5 +6,4 @@ export * from './icmp';
|
|||||||
export * from './log';
|
export * from './log';
|
||||||
export * from './storage';
|
export * from './storage';
|
||||||
export * from './other';
|
export * from './other';
|
||||||
export * from './upper-ndm';
|
|
||||||
export * from './video';
|
export * from './video';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from './ndm-call-log';
|
export * from './ndm-call-log';
|
||||||
export * from './ndm-device-alarm-log';
|
export * from './ndm-device-alarm-log';
|
||||||
export * from './ndm-device-alarm-snap-log';
|
|
||||||
export * from './ndm-icmp-log';
|
export * from './ndm-icmp-log';
|
||||||
export * from './ndm-snmp-log';
|
export * from './ndm-snmp-log';
|
||||||
export * from './ndm-record-check';
|
export * from './ndm-record-check';
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import {
|
|
||||||
ndmClient,
|
|
||||||
userClient,
|
|
||||||
type NdmDeviceAlarmSnapLogPageQuery,
|
|
||||||
type NdmDeviceAlarmSnapLogResultVO,
|
|
||||||
type NdmDeviceAlarmSnapLogSaveVO,
|
|
||||||
type NdmDeviceAlarmSnapLogUpdateVO,
|
|
||||||
type PageParams,
|
|
||||||
type PageResult,
|
|
||||||
type Station,
|
|
||||||
} from '@/apis';
|
|
||||||
import { unwrapResponse } from '@/utils';
|
|
||||||
|
|
||||||
export const pageDeviceAlarmSnapLogApi = async (pageQuery: PageParams<NdmDeviceAlarmSnapLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmSnapLog/page`;
|
|
||||||
const resp = await client.post<PageResult<NdmDeviceAlarmSnapLogResultVO>>(endpoint, pageQuery, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const detailDeviceAlarmSnapLogApi = async (id: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmSnapLog/detail`;
|
|
||||||
const resp = await client.get<NdmDeviceAlarmSnapLogResultVO>(endpoint, { params: { id }, signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const saveDeviceAlarmSnapLogApi = async (saveVO: NdmDeviceAlarmSnapLogSaveVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmSnapLog`;
|
|
||||||
const resp = await client.post<NdmDeviceAlarmSnapLogResultVO>(endpoint, saveVO, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateDeviceAlarmSnapLogApi = async (updateVO: NdmDeviceAlarmSnapLogUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmSnapLog`;
|
|
||||||
const resp = await client.put<NdmDeviceAlarmSnapLogResultVO>(endpoint, updateVO, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteDeviceAlarmSnapLogApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmSnapLog`;
|
|
||||||
const resp = await client.delete<boolean>(endpoint, ids, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ndmClient, userClient, type ClientChannel, type NdmNvrResultVO, type NdmRecordCheck, type Station } from '@/apis';
|
import { ndmClient, userClient, type ClientChannel, type NdmNvrResultVO, type NdmRecordCheck } from '@/apis';
|
||||||
import { unwrapResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export const getChannelListApi = async (ndmNvr: NdmNvrResultVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const getChannelListApi = async (ndmNvr: NdmNvrResultVO, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -12,7 +12,7 @@ export const getChannelListApi = async (ndmNvr: NdmNvrResultVO, options?: { stat
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRecordCheckApi = async (ndmNvr: NdmNvrResultVO, lastDays: number, gbCodeList: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const getRecordCheckApi = async (ndmNvr: NdmNvrResultVO, lastDays: number, gbCodeList: string[], options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -27,7 +27,7 @@ export const getRecordCheckApi = async (ndmNvr: NdmNvrResultVO, lastDays: number
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reloadRecordCheckApi = async (channel: ClientChannel, dayOffset: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const reloadRecordCheckApi = async (channel: ClientChannel, dayOffset: number, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -37,7 +37,7 @@ export const reloadRecordCheckApi = async (channel: ClientChannel, dayOffset: nu
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reloadAllRecordCheckApi = async (dayOffset: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const reloadAllRecordCheckApi = async (dayOffset: number, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type PageResult,
|
type PageResult,
|
||||||
type Station,
|
type Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageSecurityBoxApi = async (pageQuery: PageParams<NdmSecurityBoxPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const pageSecurityBoxApi = async (pageQuery: PageParams<NdmSecurityBoxPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -90,7 +90,7 @@ export const probeSecurityBoxApi = async (ids: string[], options?: { stationCode
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const turnCitcuitStatusApi = async (ipAddress: string, circuitIndex: number, status: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const turnCitcuitStatusApi = async (ipAddress: string, circuitIndex: number, status: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
|
|||||||
@@ -1,16 +1,6 @@
|
|||||||
import { ndmClient, userClient, type MediaServerStatus, type SendRtpInfo, type Station } from '@/apis';
|
import { ndmClient, userClient, type MediaServerStatus, type Station } from '@/apis';
|
||||||
import { unwrapResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const getAllPushApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmServiceAvailable/mediaServer/getAllPush`;
|
|
||||||
const resp = await client.get<SendRtpInfo[]>(endpoint, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isMediaServerAliveApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const isMediaServerAliveApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import {
|
|||||||
type PageResult,
|
type PageResult,
|
||||||
type Station,
|
type Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const pageSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch/page`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch/page`;
|
||||||
const resp = await client.post<PageResult<NdmSwitchResultVO>>(endpoint, pageQuery, { signal });
|
const resp = await client.post<PageResult<NdmSwitchResultVO>>(endpoint, pageQuery, { signal });
|
||||||
@@ -24,7 +24,7 @@ export const pageSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, o
|
|||||||
|
|
||||||
export const detailSwitchApi = async (id: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const detailSwitchApi = async (id: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch/detail`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch/detail`;
|
||||||
const resp = await client.get<NdmSwitchResultVO>(endpoint, { params: { id }, signal });
|
const resp = await client.get<NdmSwitchResultVO>(endpoint, { params: { id }, signal });
|
||||||
@@ -34,7 +34,7 @@ export const detailSwitchApi = async (id: string, options?: { stationCode?: Stat
|
|||||||
|
|
||||||
export const saveSwitchApi = async (saveVO: NdmSwitchSaveVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const saveSwitchApi = async (saveVO: NdmSwitchSaveVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
||||||
const resp = await client.post<NdmSwitchResultVO>(endpoint, saveVO, { signal });
|
const resp = await client.post<NdmSwitchResultVO>(endpoint, saveVO, { signal });
|
||||||
@@ -44,7 +44,7 @@ export const saveSwitchApi = async (saveVO: NdmSwitchSaveVO, options?: { station
|
|||||||
|
|
||||||
export const updateSwitchApi = async (updateVO: NdmSwitchUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const updateSwitchApi = async (updateVO: NdmSwitchUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
||||||
const resp = await client.put<NdmSwitchResultVO>(endpoint, updateVO, { signal });
|
const resp = await client.put<NdmSwitchResultVO>(endpoint, updateVO, { signal });
|
||||||
@@ -54,7 +54,7 @@ export const updateSwitchApi = async (updateVO: NdmSwitchUpdateVO, options?: { s
|
|||||||
|
|
||||||
export const deleteSwitchApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const deleteSwitchApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
|
||||||
const resp = await client.delete<boolean>(endpoint, ids, { signal });
|
const resp = await client.delete<boolean>(endpoint, ids, { signal });
|
||||||
@@ -64,7 +64,7 @@ export const deleteSwitchApi = async (ids: string[], options?: { stationCode?: S
|
|||||||
|
|
||||||
export const exportSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const exportSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch/defaultExportByTemplate`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch/defaultExportByTemplate`;
|
||||||
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
|
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
|
||||||
@@ -86,9 +86,9 @@ export const importSwitchApi = async (file: File, options?: { stationCode?: Stat
|
|||||||
|
|
||||||
export const probeSwitchApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const probeSwitchApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : ndmClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSwitch/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmSwitch/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ndmClient, userClient, type ImportMsg, type NdmNvrPageQuery, type NdmNvrResultVO, type NdmNvrSaveVO, type NdmNvrUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
|
import { ndmClient, userClient, type ImportMsg, type NdmNvrPageQuery, type NdmNvrResultVO, type NdmNvrSaveVO, type NdmNvrUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageNvrPageApi = async (pageQuery: PageParams<NdmNvrPageQuery>, options?: { stationCode: Station['code']; signal?: AbortSignal }) => {
|
export const pageNvrPageApi = async (pageQuery: PageParams<NdmNvrPageQuery>, options?: { stationCode: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -79,7 +79,7 @@ export const probeNvrApi = async (ids: string[], options?: { stationCode?: Stati
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmNvr/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmNvr/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const syncNvrChannelsApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
|
export const syncNvrChannelsApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
@@ -88,5 +88,5 @@ export const syncNvrChannelsApi = async (options?: { stationCode?: string; signa
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmNvr/syncNvrChannels`;
|
const endpoint = `${prefix}/api/ndm/ndmNvr/syncNvrChannels`;
|
||||||
const resp = await client.get<void>(endpoint, { signal });
|
const resp = await client.get<void>(endpoint, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from './upper-ndm';
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { ndmClient, userClient, type Station } from '@/apis';
|
|
||||||
import { unwrapResponse } from '@/utils';
|
|
||||||
|
|
||||||
export async function snapStatusApi(method: 'get', options?: { stationCode?: Station['code']; signal?: AbortSignal }): Promise<boolean>;
|
|
||||||
export async function snapStatusApi(method: 'post', options: { doSnap: boolean; stationCode?: Station['code']; signal?: AbortSignal }): Promise<boolean>;
|
|
||||||
export async function snapStatusApi(method: 'get' | 'post', options?: { doSnap?: boolean; stationCode?: Station['code']; signal?: AbortSignal }) {
|
|
||||||
const { doSnap, stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/anyTenant/snapStatus`;
|
|
||||||
if (method === 'get') {
|
|
||||||
const resp = await client.get<boolean>(endpoint, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
} else {
|
|
||||||
const resp = await client.post<boolean>(endpoint, doSnap, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,5 +3,4 @@ export * from './ndm-camera-ignore';
|
|||||||
export * from './ndm-decoder';
|
export * from './ndm-decoder';
|
||||||
export * from './ndm-keyboard';
|
export * from './ndm-keyboard';
|
||||||
export * from './ndm-media-server';
|
export * from './ndm-media-server';
|
||||||
export * from './ndm-snap';
|
|
||||||
export * from './ndm-video-server';
|
export * from './ndm-video-server';
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
import {
|
import { ndmClient, userClient, type NdmCameraIgnorePageQuery, type NdmCameraIgnoreResultVO, type NdmCameraIgnoreSaveVO, type NdmCameraIgnoreUpdateVO, type PageParams, type PageResult } from '@/apis';
|
||||||
ndmClient,
|
|
||||||
userClient,
|
|
||||||
type NdmCameraIgnorePageQuery,
|
|
||||||
type NdmCameraIgnoreResultVO,
|
|
||||||
type NdmCameraIgnoreSaveVO,
|
|
||||||
type NdmCameraIgnoreUpdateVO,
|
|
||||||
type PageParams,
|
|
||||||
type PageResult,
|
|
||||||
type Station,
|
|
||||||
} from '@/apis';
|
|
||||||
import { unwrapResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageCameraIgnoreApi = async (pageQuery: PageParams<NdmCameraIgnorePageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const pageCameraIgnoreApi = async (pageQuery: PageParams<NdmCameraIgnorePageQuery>, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -21,7 +11,7 @@ export const pageCameraIgnoreApi = async (pageQuery: PageParams<NdmCameraIgnoreP
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const detailCameraIgnoreApi = async (id: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const detailCameraIgnoreApi = async (id: string, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -31,7 +21,7 @@ export const detailCameraIgnoreApi = async (id: string, options?: { stationCode?
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveCameraIgnoreApi = async (saveVO: NdmCameraIgnoreSaveVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const saveCameraIgnoreApi = async (saveVO: NdmCameraIgnoreSaveVO, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -41,7 +31,7 @@ export const saveCameraIgnoreApi = async (saveVO: NdmCameraIgnoreSaveVO, options
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCameraIgnoreApi = async (updateVO: NdmCameraIgnoreUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const updateCameraIgnoreApi = async (updateVO: NdmCameraIgnoreUpdateVO, options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -51,7 +41,7 @@ export const updateCameraIgnoreApi = async (updateVO: NdmCameraIgnoreUpdateVO, o
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteCameraIgnoreApi = async (ids: string[], options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const deleteCameraIgnoreApi = async (ids: string[], options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
|
|||||||
@@ -88,12 +88,12 @@ export const importCameraApi = async (file: File, options?: { stationCode?: Stat
|
|||||||
export const getCameraSnapApi = async (deviceId: string, options?: { signal?: AbortSignal }) => {
|
export const getCameraSnapApi = async (deviceId: string, options?: { signal?: AbortSignal }) => {
|
||||||
const { signal } = options ?? {};
|
const { signal } = options ?? {};
|
||||||
const endpoint = `/api/ndm/ndmCamera/getSnapByDeviceId`;
|
const endpoint = `/api/ndm/ndmCamera/getSnapByDeviceId`;
|
||||||
const resp = await ndmClient.get<SnapResult>(endpoint, { params: { deviceId }, retRaw: true, signal });
|
const resp = await ndmClient.get<SnapResult>(endpoint, { params: { deviceId }, signal });
|
||||||
const data = unwrapResponse(resp);
|
const data = unwrapResponse(resp);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const syncCameraApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const syncCameraApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type PageResult,
|
type PageResult,
|
||||||
type Station,
|
type Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageDecoderApi = async (pageQuery: PageParams<NdmDecoderPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const pageDecoderApi = async (pageQuery: PageParams<NdmDecoderPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -90,5 +90,5 @@ export const probeDecoderApi = async (ids: string[], options?: { stationCode?: S
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmDecoder/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmDecoder/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type PageResult,
|
type PageResult,
|
||||||
type Station,
|
type Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const postNdmMediaServerPage = async (pageQuery: PageParams<NdmMediaServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const postNdmMediaServerPage = async (pageQuery: PageParams<NdmMediaServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -90,5 +90,5 @@ export const probeMediaServerApi = async (ids: string[], options?: { stationCode
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmMediaServer/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmMediaServer/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { ndmClient, userClient, type Station } from '@/apis';
|
|
||||||
import { unwrapResponse } from '@/utils';
|
|
||||||
|
|
||||||
export async function retentionDaysApi(method: 'get', options?: { stationCode?: Station['code']; signal?: AbortSignal }): Promise<number>;
|
|
||||||
export async function retentionDaysApi(method: 'post', options: { days: number; stationCode?: Station['code']; signal?: AbortSignal }): Promise<number>;
|
|
||||||
export async function retentionDaysApi(method: 'get' | 'post', options?: { days?: number; stationCode?: Station['code']; signal?: AbortSignal }) {
|
|
||||||
const { days, stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmSnap/retentionDays`;
|
|
||||||
if (method === 'get') {
|
|
||||||
const resp = await client.get<number>(endpoint, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
} else {
|
|
||||||
const resp = await client.post<number>(endpoint, days, { signal });
|
|
||||||
const data = unwrapResponse(resp);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type PageResult,
|
type PageResult,
|
||||||
type Station,
|
type Station,
|
||||||
} from '@/apis';
|
} from '@/apis';
|
||||||
import { unwrapResponse, unwrapVoidResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
|
|
||||||
export const pageVideoServerApi = async (pageQuery: PageParams<NdmVideoServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const pageVideoServerApi = async (pageQuery: PageParams<NdmVideoServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
@@ -42,7 +42,7 @@ export const saveVideoServerApi = async (saveVO: NdmVideoServerSaveVO, options?:
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateVideoServerApi = async (updateVO: NdmVideoServerUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const updateVideoServerApi = async (id: string, updateVO: NdmVideoServerUpdateVO, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
const client = stationCode ? ndmClient : userClient;
|
const client = stationCode ? ndmClient : userClient;
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
@@ -90,5 +90,5 @@ export const probeVideoServerApi = async (ids: string[], options?: { stationCode
|
|||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
const endpoint = `${prefix}/api/ndm/ndmVideoServer/probeByIds`;
|
const endpoint = `${prefix}/api/ndm/ndmVideoServer/probeByIds`;
|
||||||
const resp = await client.post<void>(endpoint, ids, { signal });
|
const resp = await client.post<void>(endpoint, ids, { signal });
|
||||||
unwrapVoidResponse(resp);
|
unwrapResponse(resp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,29 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { detailDeviceApi, probeDeviceApi, type LinkDescription, type NdmDeviceResultVO, type Station } from '@/apis';
|
import { detailDeviceApi, probeDeviceApi, type NdmDeviceResultVO, type Station } from '@/apis';
|
||||||
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
|
import { DEVICE_TYPE_NAMES, tryGetDeviceType } from '@/enums';
|
||||||
import { DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, tryGetDeviceType } from '@/enums';
|
|
||||||
import { useDeviceStore } from '@/stores';
|
import { useDeviceStore } from '@/stores';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { ApiOutlined, ReloadOutlined } from '@vicons/antd';
|
import { ApiOutlined, ReloadOutlined } from '@vicons/antd';
|
||||||
import { isCancel } from 'axios';
|
import { isCancel } from 'axios';
|
||||||
import destr from 'destr';
|
|
||||||
import { NButton, NCard, NFlex, NIcon, NTag, NTooltip } from 'naive-ui';
|
import { NButton, NCard, NFlex, NIcon, NTag, NTooltip } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { computed, onBeforeUnmount, ref, toRefs } from 'vue';
|
||||||
import { computed, inject, onBeforeUnmount, ref, toRefs } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
ndmDevice: NdmDeviceResultVO;
|
ndmDevice: NdmDeviceResultVO;
|
||||||
station: Station;
|
station: Station;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineSlots<{
|
|
||||||
'append-info': () => any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
|
|
||||||
const { ndmDevice, station } = toRefs(props);
|
const { ndmDevice, station } = toRefs(props);
|
||||||
|
|
||||||
const type = computed(() => {
|
const type = computed(() => {
|
||||||
@@ -36,44 +26,6 @@ const status = computed(() => ndmDevice.value.deviceStatus);
|
|||||||
const ipAddr = computed(() => ndmDevice.value.ipAddress ?? '-');
|
const ipAddr = computed(() => ndmDevice.value.ipAddress ?? '-');
|
||||||
const gbCode = computed(() => Reflect.get(ndmDevice.value, 'gbCode') as string | undefined);
|
const gbCode = computed(() => Reflect.get(ndmDevice.value, 'gbCode') as string | undefined);
|
||||||
|
|
||||||
const linkDescription = computed(() => {
|
|
||||||
const result = destr<any>(ndmDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result as LinkDescription;
|
|
||||||
});
|
|
||||||
|
|
||||||
const upperDevices = computed(() => {
|
|
||||||
const devices: NdmDeviceResultVO[] = [];
|
|
||||||
if (!linkDescription.value) return devices;
|
|
||||||
if (!linkDescription.value.upstream) return devices;
|
|
||||||
linkDescription.value.upstream.forEach((deviceStoreIndex) => {
|
|
||||||
const { stationCode, deviceType, deviceDbId } = deviceStoreIndex;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return;
|
|
||||||
const classified = stationDevices[deviceType];
|
|
||||||
const device = classified.find((device) => device.id === deviceDbId);
|
|
||||||
if (device) devices.push(device);
|
|
||||||
});
|
|
||||||
return devices.sort((aDevice, bDevice) => {
|
|
||||||
// 按在DEVICE_TYPE_LITERALS中的顺序排序
|
|
||||||
const aDeviceType = tryGetDeviceType(aDevice.deviceType);
|
|
||||||
const bDeviceType = tryGetDeviceType(bDevice.deviceType);
|
|
||||||
if (!aDeviceType || !bDeviceType) return 0;
|
|
||||||
const deviceTypes = Object.values(DEVICE_TYPE_LITERALS);
|
|
||||||
return deviceTypes.indexOf(aDeviceType) - deviceTypes.indexOf(bDeviceType);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取从父组件注入的 `selectDevice` 函数
|
|
||||||
const selectDeviceFn = inject(SELECT_DEVICE_FN_INJECTION_KEY);
|
|
||||||
// 跳转到上游设备
|
|
||||||
const navigateToUpperDevice = (upperDevice: NdmDeviceResultVO) => {
|
|
||||||
if (!!selectDeviceFn && !!selectDeviceFn.value) {
|
|
||||||
selectDeviceFn.value(upperDevice, station.value.code);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const canOpenMgmtPage = computed(() => {
|
const canOpenMgmtPage = computed(() => {
|
||||||
return Object.keys(ndmDevice.value).includes('manageUrl');
|
return Object.keys(ndmDevice.value).includes('manageUrl');
|
||||||
});
|
});
|
||||||
@@ -140,14 +92,15 @@ onBeforeUnmount(() => {
|
|||||||
<template>
|
<template>
|
||||||
<NCard hoverable size="small">
|
<NCard hoverable size="small">
|
||||||
<template #header>
|
<template #header>
|
||||||
<NFlex vertical>
|
|
||||||
<NFlex align="center">
|
<NFlex align="center">
|
||||||
<NTag v-if="status === '10'" size="small" type="success">在线</NTag>
|
<NTag v-if="status === '10'" size="small" type="success">在线</NTag>
|
||||||
<NTag v-else-if="status === '20'" size="small" type="error">离线</NTag>
|
<NTag v-else-if="status === '20'" size="small" type="error">离线</NTag>
|
||||||
<NTag v-else size="small" type="warning">-</NTag>
|
<NTag v-else size="small" type="warning">-</NTag>
|
||||||
<div>{{ name }}</div>
|
<div>{{ name }}</div>
|
||||||
<NButton v-if="canOpenMgmtPage" ghost size="tiny" type="default" :focusable="false" @click="onClickOpenMgmtPage">管理</NButton>
|
<NButton v-if="canOpenMgmtPage" ghost size="tiny" type="default" :focusable="false" @click="onClickOpenMgmtPage">管理</NButton>
|
||||||
<div style="margin-left: auto">
|
</NFlex>
|
||||||
|
</template>
|
||||||
|
<template #header-extra>
|
||||||
<NTooltip v-if="canProbe" trigger="hover">
|
<NTooltip v-if="canProbe" trigger="hover">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NButton size="small" quaternary circle :loading="probing" @click="() => probeDevice()">
|
<NButton size="small" quaternary circle :loading="probing" @click="() => probeDevice()">
|
||||||
@@ -172,16 +125,6 @@ onBeforeUnmount(() => {
|
|||||||
<span>刷新设备</span>
|
<span>刷新设备</span>
|
||||||
</template>
|
</template>
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
</div>
|
|
||||||
</NFlex>
|
|
||||||
<div v-if="upperDevices.length > 0" style="font-size: 0.85rem">
|
|
||||||
<span>上游设备:</span>
|
|
||||||
<template v-for="(device, index) in upperDevices" :key="index">
|
|
||||||
<span style="text-decoration: underline; cursor: pointer" @click="() => navigateToUpperDevice(device)">{{ device.name }}</span>
|
|
||||||
<span v-if="index < upperDevices.length - 1">、</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div style="font-size: small; color: #666">
|
<div style="font-size: small; color: #666">
|
||||||
@@ -207,9 +150,6 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
|
||||||
<slot name="append-info"></slot>
|
|
||||||
</template>
|
|
||||||
</NCard>
|
</NCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -4,20 +4,7 @@ import DeviceHeaderCard from './device-header-card.vue';
|
|||||||
import NvrDiskCard from './nvr-disk-card.vue';
|
import NvrDiskCard from './nvr-disk-card.vue';
|
||||||
import NvrRecordCard from './nvr-record-card.vue';
|
import NvrRecordCard from './nvr-record-card.vue';
|
||||||
import SecurityBoxCircuitCard from './security-box-circuit-card.vue';
|
import SecurityBoxCircuitCard from './security-box-circuit-card.vue';
|
||||||
import SecurityBoxCircuitLinkModal from './security-box-circuit-link-modal.vue';
|
|
||||||
import SecurityBoxEnvCard from './security-box-env-card.vue';
|
import SecurityBoxEnvCard from './security-box-env-card.vue';
|
||||||
import SwitchPortCard from './switch-port-card.vue';
|
import SwitchPortCard from './switch-port-card.vue';
|
||||||
import SwitchPortLinkModal from './switch-port-link-modal.vue';
|
|
||||||
|
|
||||||
export {
|
export { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, NvrDiskCard, NvrRecordCard, SecurityBoxCircuitCard, SecurityBoxEnvCard, SwitchPortCard };
|
||||||
DeviceCommonCard,
|
|
||||||
DeviceHardwareCard,
|
|
||||||
DeviceHeaderCard,
|
|
||||||
NvrDiskCard,
|
|
||||||
NvrRecordCard,
|
|
||||||
SecurityBoxCircuitCard,
|
|
||||||
SecurityBoxCircuitLinkModal,
|
|
||||||
SecurityBoxEnvCard,
|
|
||||||
SwitchPortCard,
|
|
||||||
SwitchPortLinkModal,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,53 +1,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { probeDeviceApi, rebootSecurityBoxApi, turnCitcuitStatusApi, type NdmSecurityBoxCircuit, type NdmSecurityBoxResultVO, type Station } from '@/apis';
|
||||||
detailDeviceApi,
|
|
||||||
probeDeviceApi,
|
|
||||||
rebootSecurityBoxApi,
|
|
||||||
turnCitcuitStatusApi,
|
|
||||||
updateDeviceApi,
|
|
||||||
type LinkDescription,
|
|
||||||
type NdmDeviceResultVO,
|
|
||||||
type NdmSecurityBoxCircuit,
|
|
||||||
type NdmSecurityBoxLinkDescription,
|
|
||||||
type NdmSecurityBoxResultVO,
|
|
||||||
type Station,
|
|
||||||
} from '@/apis';
|
|
||||||
import { SecurityBoxCircuitLinkModal } from '@/components';
|
|
||||||
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
|
|
||||||
import { useDeviceStore, useSettingStore } from '@/stores';
|
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { PoweroffOutlined } from '@vicons/antd';
|
import { PoweroffOutlined } from '@vicons/antd';
|
||||||
import { watchImmediate } from '@vueuse/core';
|
import { watchImmediate } from '@vueuse/core';
|
||||||
import { isCancel } from 'axios';
|
import { NButton, NCard, NDescriptions, NDescriptionsItem, NFlex, NIcon, NPopconfirm, NPopover, NSwitch, NTag, useThemeVars, type TagProps } from 'naive-ui';
|
||||||
import destr from 'destr';
|
import { computed, ref, toRefs } from 'vue';
|
||||||
import { cloneDeep, isFunction } from 'es-toolkit';
|
|
||||||
import { NButton, NCard, NDescriptions, NDescriptionsItem, NDropdown, NFlex, NIcon, NPopconfirm, NPopover, NSwitch, NTag, useThemeVars, type DropdownOption, type TagProps } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, inject, ref, toRefs } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
circuits?: NdmSecurityBoxCircuit[];
|
||||||
ndmDevice: NdmSecurityBoxResultVO;
|
ndmDevice: NdmSecurityBoxResultVO;
|
||||||
station: Station;
|
station: Station;
|
||||||
circuits?: NdmSecurityBoxCircuit[];
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
const { circuits, ndmDevice, station } = toRefs(props);
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const { ndmDevice, station, circuits } = toRefs(props);
|
|
||||||
|
|
||||||
const showCard = computed(() => !!circuits.value && circuits.value.length > 0);
|
const showCard = computed(() => !!circuits.value && circuits.value.length > 0);
|
||||||
|
|
||||||
const localCircuits = ref<NdmSecurityBoxCircuit[]>([]);
|
const boxCircuits = ref<NdmSecurityBoxCircuit[]>([]);
|
||||||
|
|
||||||
watchImmediate(circuits, (newCircuits) => {
|
watchImmediate(circuits, (newCircuits) => {
|
||||||
localCircuits.value = newCircuits?.map((circuit) => ({ ...circuit })) ?? [];
|
boxCircuits.value = newCircuits?.map((circuit) => ({ ...circuit })) ?? [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const getCircuitStatusTagType = (circuit: NdmSecurityBoxCircuit): TagProps['type'] => {
|
const getCircuitStatusTagType = (circuit: NdmSecurityBoxCircuit): TagProps['type'] => {
|
||||||
@@ -65,34 +40,22 @@ const getCircuitStatusClassName = (circuit: NdmSecurityBoxCircuit) => {
|
|||||||
return status === 0 ? 'circuit-off' : status === 1 ? 'circuit-on' : 'circuit-unknown';
|
return status === 0 ? 'circuit-off' : status === 1 ? 'circuit-on' : 'circuit-unknown';
|
||||||
};
|
};
|
||||||
|
|
||||||
const abortController = ref<AbortController>(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: turnStatus, isPending: turning } = useMutation({
|
const { mutate: turnStatus, isPending: turning } = useMutation({
|
||||||
mutationFn: async (params: { circuitIndex: number; newStatus: boolean }) => {
|
mutationFn: async (params: { circuitIndex: number; newStatus: boolean }) => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
const { circuitIndex, newStatus } = params;
|
const { circuitIndex, newStatus } = params;
|
||||||
if (!ndmDevice.value.ipAddress) {
|
if (!ndmDevice.value.ipAddress) {
|
||||||
throw new Error('设备IP地址不存在');
|
throw new Error('设备IP地址不存在');
|
||||||
}
|
}
|
||||||
const status = newStatus ? 1 : 0;
|
const status = newStatus ? 1 : 0;
|
||||||
const stationCode = station.value.code;
|
await turnCitcuitStatusApi(ndmDevice.value.ipAddress, circuitIndex, status, { stationCode: station.value.code });
|
||||||
const signal = abortController.value.signal;
|
await probeDeviceApi(ndmDevice.value, { stationCode: station.value.code });
|
||||||
await turnCitcuitStatusApi(ndmDevice.value.ipAddress, circuitIndex, status, { stationCode, signal });
|
|
||||||
await probeDeviceApi(ndmDevice.value, { stationCode, signal });
|
|
||||||
return status;
|
return status;
|
||||||
},
|
},
|
||||||
onSuccess: (status, { circuitIndex }) => {
|
onSuccess: (status, { circuitIndex }) => {
|
||||||
window.$loadingBar.finish();
|
const circuit = boxCircuits.value.at(circuitIndex);
|
||||||
const circuit = localCircuits.value.at(circuitIndex);
|
|
||||||
if (circuit) circuit.status = status;
|
if (circuit) circuit.status = status;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -101,188 +64,15 @@ const { mutate: turnStatus, isPending: turning } = useMutation({
|
|||||||
|
|
||||||
const { mutate: reboot, isPending: rebooting } = useMutation({
|
const { mutate: reboot, isPending: rebooting } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
if (!ndmDevice.value.ipAddress) {
|
if (!ndmDevice.value.ipAddress) {
|
||||||
throw new Error('设备IP地址不存在');
|
throw new Error('设备IP地址不存在');
|
||||||
}
|
}
|
||||||
|
await rebootSecurityBoxApi(ndmDevice.value.ipAddress, { stationCode: station.value.code });
|
||||||
const stationCode = station.value.code;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
await rebootSecurityBoxApi(ndmDevice.value.ipAddress, { stationCode, signal });
|
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
window.$loadingBar.finish();
|
|
||||||
window.$message.success('设备重启成功');
|
window.$message.success('设备重启成功');
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const upperDeviceLinkDescription = computed(() => {
|
|
||||||
const result = destr<any>(ndmDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result as NdmSecurityBoxLinkDescription;
|
|
||||||
});
|
|
||||||
|
|
||||||
const getLowerDeviceByCircuitIndex = (circuitIndex: number) => {
|
|
||||||
if (!upperDeviceLinkDescription.value) return null;
|
|
||||||
const downstream = upperDeviceLinkDescription.value.downstream;
|
|
||||||
if (!downstream) return null;
|
|
||||||
const deviceStoreIndex = downstream[circuitIndex];
|
|
||||||
if (!deviceStoreIndex) return null;
|
|
||||||
const { stationCode, deviceType, deviceDbId } = deviceStoreIndex;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return null;
|
|
||||||
const devices = stationDevices[deviceType];
|
|
||||||
const lowerDevice = devices.find((device) => device.id === deviceDbId);
|
|
||||||
if (!lowerDevice) {
|
|
||||||
// 下游设备不存在时解除关联
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
|
|
||||||
delete modifiedUpperLinkDescription.downstream?.[circuitIndex];
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);
|
|
||||||
// 不需要等待异步
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
updateDeviceApi(modifiedUpperDevice, { stationCode }).then(() => {
|
|
||||||
detailDeviceApi(modifiedUpperDevice, { stationCode }).then((upperDevice) => {
|
|
||||||
if (!upperDevice) return;
|
|
||||||
deviceStore.patchDevice(stationCode, { ...upperDevice });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return lowerDevice;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取从父组件注入的selectDevice函数
|
|
||||||
const selectDeviceFn = inject(SELECT_DEVICE_FN_INJECTION_KEY);
|
|
||||||
// 跳转到下游设备
|
|
||||||
const navigateToLowerDevice = (circuitIndex: number) => {
|
|
||||||
const lowerDevice = getLowerDeviceByCircuitIndex(circuitIndex);
|
|
||||||
if (!lowerDevice) return;
|
|
||||||
if (!!selectDeviceFn && !!selectDeviceFn.value) {
|
|
||||||
selectDeviceFn.value(lowerDevice, station.value.code);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showModal = ref(false);
|
|
||||||
const contextmenu = ref<{ x: number; y: number; circuitIndex?: number }>({ x: 0, y: 0 });
|
|
||||||
const showContextmenu = ref(false);
|
|
||||||
const contextmenuOptions = computed<DropdownOption[]>(() => [
|
|
||||||
{
|
|
||||||
label: '关联设备',
|
|
||||||
key: 'link-device',
|
|
||||||
onSelect: () => {
|
|
||||||
showContextmenu.value = false;
|
|
||||||
showModal.value = true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '解除关联',
|
|
||||||
key: 'unlink-device',
|
|
||||||
onSelect: () => {
|
|
||||||
showContextmenu.value = false;
|
|
||||||
const circuitIndex = contextmenu.value.circuitIndex;
|
|
||||||
if (circuitIndex === undefined) return;
|
|
||||||
const lowerDevice = getLowerDeviceByCircuitIndex(circuitIndex);
|
|
||||||
if (!lowerDevice) return;
|
|
||||||
window.$dialog.warning({
|
|
||||||
title: '确认解除关联吗?',
|
|
||||||
content: `将解除【电路${circuitIndex + 1}】与【${lowerDevice.name}】的关联关系。`,
|
|
||||||
style: { width: '600px' },
|
|
||||||
contentStyle: { height: '60px' },
|
|
||||||
negativeText: '取消',
|
|
||||||
positiveText: '确认',
|
|
||||||
onNegativeClick: () => {
|
|
||||||
window.$dialog.destroyAll();
|
|
||||||
},
|
|
||||||
onPositiveClick: () => {
|
|
||||||
window.$dialog.destroyAll();
|
|
||||||
unlinkDevice({ circuitIndex, lowerDevice });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
|
|
||||||
const onSelect = option['onSelect'];
|
|
||||||
if (isFunction(onSelect)) {
|
|
||||||
onSelect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const onContextmenu = (payload: PointerEvent, circuitIndex: number) => {
|
|
||||||
payload.stopPropagation();
|
|
||||||
payload.preventDefault();
|
|
||||||
const { clientX, clientY } = payload;
|
|
||||||
contextmenu.value = { x: clientX, y: clientY, circuitIndex };
|
|
||||||
showContextmenu.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const { mutate: unlinkDevice } = useMutation({
|
|
||||||
mutationFn: async (params: { circuitIndex: number; lowerDevice: NdmDeviceResultVO }) => {
|
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
const { circuitIndex, lowerDevice } = params;
|
|
||||||
|
|
||||||
if (!upperDeviceLinkDescription.value) return;
|
|
||||||
|
|
||||||
// 1.从下游设备的linkDescription的upstream字段中删除当前上游设备
|
|
||||||
const modifiedLowerDevice = cloneDeep(lowerDevice);
|
|
||||||
// 解除关联时下游设备的linkDescription一定存在
|
|
||||||
const modifiedLowerDeviceLinkDescription = destr<LinkDescription>(modifiedLowerDevice.linkDescription);
|
|
||||||
// upstream字段存在时才进行删除操作
|
|
||||||
if (modifiedLowerDeviceLinkDescription.upstream) {
|
|
||||||
const index = modifiedLowerDeviceLinkDescription.upstream.findIndex((deviceStoreIndex) => deviceStoreIndex.deviceDbId === ndmDevice.value.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
modifiedLowerDeviceLinkDescription.upstream.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 2. 修改上游设备的linkDescription的downstream字段
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
|
|
||||||
delete modifiedUpperLinkDescription.downstream?.[circuitIndex];
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);
|
|
||||||
|
|
||||||
// 3. 发起update请求并获取最新的设备详情(离线模式下直接修改本地数据)
|
|
||||||
if (offlineDev.value) {
|
|
||||||
return { upperDevice: modifiedUpperDevice, lowerDevice: modifiedLowerDevice };
|
|
||||||
}
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
await updateDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
await updateDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
const latestUpperDevice = await detailDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
const latestLowerDevice = await detailDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
return { upperDevice: latestUpperDevice, lowerDevice: latestLowerDevice };
|
|
||||||
},
|
|
||||||
onSuccess: (data) => {
|
|
||||||
window.$loadingBar.finish();
|
|
||||||
window.$message.success('解除成功');
|
|
||||||
if (!data) return;
|
|
||||||
const { upperDevice, lowerDevice } = data;
|
|
||||||
if (!!upperDevice && !!lowerDevice) {
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...upperDevice });
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...lowerDevice });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -304,11 +94,11 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
</NFlex>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div style="display: grid" :style="{ 'grid-template-columns': `repeat(${Math.min(localCircuits.length, 4)}, 1fr)` }">
|
<div style="display: grid" :style="{ 'grid-template-columns': `repeat(${Math.min(boxCircuits.length, 4)}, 1fr)` }">
|
||||||
<template v-for="(circuit, circuitIndex) in localCircuits" :key="circuitIndex">
|
<template v-for="(circuit, index) in boxCircuits" :key="index">
|
||||||
<NPopover :delay="300">
|
<NPopover :delay="300">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div style="display: flex; justify-content: center; align-items: center" @contextmenu="(payload) => onContextmenu(payload, circuitIndex)">
|
<NFlex justify="center" align="center" :size="0">
|
||||||
<NFlex vertical class="pointer-cursor circuit" style="padding: 12px" :class="getCircuitStatusClassName(circuit)">
|
<NFlex vertical class="pointer-cursor circuit" style="padding: 12px" :class="getCircuitStatusClassName(circuit)">
|
||||||
<NFlex align="center">
|
<NFlex align="center">
|
||||||
<NTag class="pointer-cursor" size="small" :type="getCircuitStatusTagType(circuit)">
|
<NTag class="pointer-cursor" size="small" :type="getCircuitStatusTagType(circuit)">
|
||||||
@@ -319,31 +109,25 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
<span>{{ getCircuitStatusText(circuit) }}</span>
|
<span>{{ getCircuitStatusText(circuit) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</NTag>
|
</NTag>
|
||||||
<span>电路{{ circuitIndex + 1 }}</span>
|
<span>电路{{ index + 1 }}</span>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
<NFlex justify="end" align="center">
|
<NFlex justify="end" align="center">
|
||||||
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => turnStatus({ circuitIndex: circuitIndex, newStatus: circuit.status !== 1 })">
|
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => turnStatus({ circuitIndex: index, newStatus: circuit.status !== 1 })">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NSwitch size="small" :value="circuit.status === 1" :loading="turning" />
|
<NSwitch size="small" :value="circuit.status === 1" :loading="turning" />
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<span>确定要{{ circuit.status === 1 ? '关闭' : '开启' }}电路{{ circuitIndex + 1 }}吗?</span>
|
<span>确定要{{ circuit.status === 1 ? '关闭' : '开启' }}电路{{ index + 1 }}吗?</span>
|
||||||
</template>
|
</template>
|
||||||
</NPopconfirm>
|
</NPopconfirm>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</div>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<NDescriptions bordered size="small" label-placement="left" :column="1">
|
<NDescriptions bordered size="small" label-placement="left" :column="1">
|
||||||
<NDescriptionsItem label="电压">{{ circuit.voltage }}V</NDescriptionsItem>
|
<NDescriptionsItem label="电压">{{ circuit.voltage }}V</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="电流">{{ circuit.current }}A</NDescriptionsItem>
|
<NDescriptionsItem label="电流">{{ circuit.current }}A</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="关联设备">
|
|
||||||
<span v-if="getLowerDeviceByCircuitIndex(circuitIndex)" style="text-decoration: underline; cursor: pointer" @click="() => navigateToLowerDevice(circuitIndex)">
|
|
||||||
{{ getLowerDeviceByCircuitIndex(circuitIndex)?.name || '-' }}
|
|
||||||
</span>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</NDescriptionsItem>
|
|
||||||
</NDescriptions>
|
</NDescriptions>
|
||||||
</template>
|
</template>
|
||||||
</NPopover>
|
</NPopover>
|
||||||
@@ -351,19 +135,6 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
|
||||||
<SecurityBoxCircuitLinkModal v-model:show="showModal" :ndm-device="ndmDevice" :station="station" :circuit-index="contextmenu.circuitIndex" />
|
|
||||||
|
|
||||||
<NDropdown
|
|
||||||
placement="bottom-start"
|
|
||||||
trigger="manual"
|
|
||||||
:show="showContextmenu"
|
|
||||||
:x="contextmenu.x"
|
|
||||||
:y="contextmenu.y"
|
|
||||||
:options="contextmenuOptions"
|
|
||||||
@select="onSelectDropdownOption"
|
|
||||||
@clickoutside="() => (showContextmenu = false)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { detailDeviceApi, updateDeviceApi, type NdmCameraLinkDescription, type NdmDeviceResultVO, type NdmSecurityBoxLinkDescription, type NdmSecurityBoxResultVO, type Station } from '@/apis';
|
|
||||||
import { DeviceTree } from '@/components';
|
|
||||||
import { tryGetDeviceType } from '@/enums';
|
|
||||||
import { useDeviceStore, useSettingStore } from '@/stores';
|
|
||||||
import { parseErrorFeedback } from '@/utils';
|
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import destr from 'destr';
|
|
||||||
import { cloneDeep } from 'es-toolkit';
|
|
||||||
import { NButton, NFlex, NModal } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, ref, toRefs } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
ndmDevice: NdmSecurityBoxResultVO;
|
|
||||||
station: Station;
|
|
||||||
circuitIndex?: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const show = defineModel<boolean>('show', { default: false });
|
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const { ndmDevice, station, circuitIndex } = toRefs(props);
|
|
||||||
|
|
||||||
const upperDeviceLinkDescription = computed(() => {
|
|
||||||
const result = destr<any>(ndmDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result as NdmSecurityBoxLinkDescription;
|
|
||||||
});
|
|
||||||
|
|
||||||
const lowerDevice = ref<NdmDeviceResultVO>();
|
|
||||||
const lowerDeviceLinkDescription = computed<NdmCameraLinkDescription | null>(() => {
|
|
||||||
if (!lowerDevice.value) return null;
|
|
||||||
const result = destr<any>(lowerDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
const onAfterSelectDevice = (device: NdmDeviceResultVO) => {
|
|
||||||
lowerDevice.value = device;
|
|
||||||
};
|
|
||||||
|
|
||||||
const abortController = ref<AbortController>(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: linkPortToDevice, isPending: linking } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
const upperDeviceType = tryGetDeviceType(ndmDevice.value.deviceType);
|
|
||||||
if (!upperDeviceType) throw new Error('本设备类型未知');
|
|
||||||
const upperDeviceDbId = ndmDevice.value.id;
|
|
||||||
if (!upperDeviceDbId) throw new Error('本设备没有ID');
|
|
||||||
|
|
||||||
if (circuitIndex.value === undefined) throw new Error('该电路不存在');
|
|
||||||
|
|
||||||
if (!lowerDevice.value) throw new Error('请选择要关联的设备');
|
|
||||||
const lowerDeviceType = tryGetDeviceType(lowerDevice.value?.deviceType);
|
|
||||||
if (!lowerDeviceType) throw new Error('该设备类型未知');
|
|
||||||
const lowerDeviceDbId = lowerDevice.value?.id;
|
|
||||||
if (!lowerDeviceDbId) throw new Error('该设备没有ID');
|
|
||||||
|
|
||||||
// 0. 检查是否会导致循环关联,
|
|
||||||
if (upperDeviceDbId === lowerDeviceDbId) throw new Error('不能关联到自身');
|
|
||||||
// 以及检查上游设备的linkDescription的downstream字段是否存在某个端口已经关联下游设备
|
|
||||||
const duplicated = Object.entries(upperDeviceLinkDescription.value?.downstream ?? {}).find(([, deviceStoreIndex]) => {
|
|
||||||
return deviceStoreIndex.deviceDbId === lowerDeviceDbId;
|
|
||||||
});
|
|
||||||
if (duplicated) {
|
|
||||||
const [portName] = duplicated;
|
|
||||||
throw new Error(`该设备已关联到端口${portName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 修改上游设备的linkDescription的downstream字段
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
let modifiedUpperDeviceLinkDescription: NdmSecurityBoxLinkDescription;
|
|
||||||
if (!upperDeviceLinkDescription.value) {
|
|
||||||
modifiedUpperDeviceLinkDescription = {
|
|
||||||
downstream: {
|
|
||||||
[circuitIndex.value]: {
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: lowerDeviceType,
|
|
||||||
deviceDbId: lowerDeviceDbId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
modifiedUpperDeviceLinkDescription = {
|
|
||||||
...upperDeviceLinkDescription.value,
|
|
||||||
downstream: {
|
|
||||||
...upperDeviceLinkDescription.value.downstream,
|
|
||||||
[circuitIndex.value]: {
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: lowerDeviceType,
|
|
||||||
deviceDbId: lowerDeviceDbId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 2. 修改下游设备的linkDescription的upstream字段
|
|
||||||
const modifiedLowerDevice = cloneDeep(lowerDevice.value);
|
|
||||||
let modifiedLowerDeviceLinkDescription: NdmCameraLinkDescription;
|
|
||||||
if (!lowerDeviceLinkDescription.value) {
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
upstream: [
|
|
||||||
{
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const upstream = cloneDeep(lowerDeviceLinkDescription.value.upstream);
|
|
||||||
if (!upstream) {
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
...lowerDeviceLinkDescription.value,
|
|
||||||
upstream: [
|
|
||||||
{
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const deviceStoreIndex = upstream.find((deviceStoreIndex) => deviceStoreIndex.deviceDbId === upperDeviceDbId);
|
|
||||||
if (!deviceStoreIndex) {
|
|
||||||
upstream.push({
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
...lowerDeviceLinkDescription.value,
|
|
||||||
upstream,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 3. 发起update请求并获取最新的设备详情(离线模式下直接修改本地数据)
|
|
||||||
if (offlineDev.value) {
|
|
||||||
return { upperDevice: modifiedUpperDevice, lowerDevice: modifiedLowerDevice };
|
|
||||||
}
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
await updateDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
await updateDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
const latestUpperDevice = await detailDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
const latestLowerDevice = await detailDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
return { upperDevice: latestUpperDevice, lowerDevice: latestLowerDevice };
|
|
||||||
},
|
|
||||||
onSuccess: ({ upperDevice, lowerDevice }) => {
|
|
||||||
show.value = false;
|
|
||||||
window.$loadingBar.finish();
|
|
||||||
window.$message.success('关联成功');
|
|
||||||
if (!!upperDevice && !!lowerDevice) {
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...upperDevice });
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...lowerDevice });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onLink = () => {
|
|
||||||
linkPortToDevice();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
abortController.value.abort();
|
|
||||||
show.value = false;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="show" preset="card" style="width: 600px; height: 600px" :content-style="{ height: '100%', overflow: 'hidden' }" @close="onCancel" @esc="onCancel">
|
|
||||||
<template #header>
|
|
||||||
<span>{{ ndmDevice.name }}</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>电路{{ circuitIndex ? circuitIndex + 1 : '-' }}</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>关联设备</span>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<DeviceTree :station="station" :events="['select']" :device-prefix-label="'选择'" @after-select-device="onAfterSelectDevice" />
|
|
||||||
</template>
|
|
||||||
<template #action>
|
|
||||||
<NFlex justify="end">
|
|
||||||
<NButton size="small" quaternary @click="onCancel">取消</NButton>
|
|
||||||
<NButton size="small" type="primary" :disabled="!lowerDevice" :loading="linking" @click="onLink">关联</NButton>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
@@ -1,33 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { detailDeviceApi, updateDeviceApi, type LinkDescription, type NdmDeviceResultVO, type NdmSwitchLinkDescription, type NdmSwitchPortInfo, type NdmSwitchResultVO, type Station } from '@/apis';
|
import type { NdmSwitchPortInfo } from '@/apis';
|
||||||
import { SwitchPortLinkModal } from '@/components';
|
|
||||||
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
|
|
||||||
import { getPortStatusValue, transformPortSpeed } from '@/helpers';
|
import { getPortStatusValue, transformPortSpeed } from '@/helpers';
|
||||||
import { useDeviceStore, useSettingStore } from '@/stores';
|
import { NCard, NDescriptions, NDescriptionsItem, NPopover, useThemeVars } from 'naive-ui';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { computed, toRefs } from 'vue';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import destr from 'destr';
|
|
||||||
import { cloneDeep, isFunction } from 'es-toolkit';
|
|
||||||
import { NCard, NDescriptions, NDescriptionsItem, NDropdown, NPopover, useThemeVars, type DropdownOption } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, inject, onBeforeUnmount, ref, toRefs } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
ndmDevice: NdmSwitchResultVO;
|
|
||||||
station: Station;
|
|
||||||
ports?: NdmSwitchPortInfo[];
|
ports?: NdmSwitchPortInfo[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
const { ports } = toRefs(props);
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const { ndmDevice, station, ports } = toRefs(props);
|
|
||||||
|
|
||||||
const showCard = computed(() => !!ports.value);
|
const showCard = computed(() => !!ports.value);
|
||||||
|
|
||||||
@@ -75,172 +58,6 @@ const getPortClassName = (port: NdmSwitchPortInfo) => {
|
|||||||
}
|
}
|
||||||
return 'port-unknown';
|
return 'port-unknown';
|
||||||
};
|
};
|
||||||
|
|
||||||
const upperDeviceLinkDescription = computed(() => {
|
|
||||||
const result = destr<any>(ndmDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result as NdmSwitchLinkDescription;
|
|
||||||
});
|
|
||||||
|
|
||||||
const getLowerDeviceByPort = (port: NdmSwitchPortInfo) => {
|
|
||||||
if (!upperDeviceLinkDescription.value) return null;
|
|
||||||
const downstream = upperDeviceLinkDescription.value.downstream;
|
|
||||||
if (!downstream) return null;
|
|
||||||
const deviceStoreIndex = downstream[port.portName];
|
|
||||||
if (!deviceStoreIndex) return null;
|
|
||||||
const { stationCode, deviceType, deviceDbId } = deviceStoreIndex;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return null;
|
|
||||||
const devices = stationDevices[deviceType];
|
|
||||||
const lowerDevice = devices.find((device) => device.id === deviceDbId);
|
|
||||||
if (!lowerDevice) {
|
|
||||||
// 下游设备不存在时解除关联
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
|
|
||||||
delete modifiedUpperLinkDescription.downstream?.[port.portName];
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);
|
|
||||||
// 不需要等待异步
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
updateDeviceApi(modifiedUpperDevice, { stationCode }).then(() => {
|
|
||||||
detailDeviceApi(modifiedUpperDevice, { stationCode }).then((upperDevice) => {
|
|
||||||
if (!upperDevice) return;
|
|
||||||
deviceStore.patchDevice(stationCode, { ...upperDevice });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return lowerDevice;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取从父组件注入的selectDevice函数
|
|
||||||
const selectDeviceFn = inject(SELECT_DEVICE_FN_INJECTION_KEY);
|
|
||||||
// 跳转到下游设备
|
|
||||||
const navigateToLowerDevice = (port: NdmSwitchPortInfo) => {
|
|
||||||
const lowerDevice = getLowerDeviceByPort(port);
|
|
||||||
if (!lowerDevice) return;
|
|
||||||
if (!!selectDeviceFn && !!selectDeviceFn.value) {
|
|
||||||
selectDeviceFn.value(lowerDevice, station.value.code);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showModal = ref(false);
|
|
||||||
const contextmenu = ref<{ x: number; y: number; port?: NdmSwitchPortInfo }>({ x: 0, y: 0 });
|
|
||||||
const showContextmenu = ref(false);
|
|
||||||
const contextmenuOptions = computed<DropdownOption[]>(() => [
|
|
||||||
{
|
|
||||||
label: '关联设备',
|
|
||||||
key: 'link-device',
|
|
||||||
onSelect: () => {
|
|
||||||
showContextmenu.value = false;
|
|
||||||
showModal.value = true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '解除关联',
|
|
||||||
key: 'unlink-device',
|
|
||||||
onSelect: () => {
|
|
||||||
showContextmenu.value = false;
|
|
||||||
const port = contextmenu.value.port;
|
|
||||||
if (!port) return;
|
|
||||||
const lowerDevice = getLowerDeviceByPort(port);
|
|
||||||
if (!lowerDevice) return;
|
|
||||||
window.$dialog.warning({
|
|
||||||
title: '确认解除关联吗?',
|
|
||||||
content: `将解除【${port.portName}】与【${lowerDevice.name}】的关联关系。`,
|
|
||||||
style: { width: '600px' },
|
|
||||||
contentStyle: { height: '60px' },
|
|
||||||
negativeText: '取消',
|
|
||||||
positiveText: '确认',
|
|
||||||
onNegativeClick: () => {
|
|
||||||
window.$dialog.destroyAll();
|
|
||||||
},
|
|
||||||
onPositiveClick: () => {
|
|
||||||
window.$dialog.destroyAll();
|
|
||||||
unlinkDevice({ port, lowerDevice });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
|
|
||||||
const onSelect = option['onSelect'];
|
|
||||||
if (isFunction(onSelect)) {
|
|
||||||
onSelect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const onContextmenu = (payload: PointerEvent, port: NdmSwitchPortInfo) => {
|
|
||||||
payload.stopPropagation();
|
|
||||||
payload.preventDefault();
|
|
||||||
const { clientX, clientY } = payload;
|
|
||||||
contextmenu.value = { x: clientX, y: clientY, port };
|
|
||||||
showContextmenu.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const abortController = ref<AbortController>(new AbortController());
|
|
||||||
const { mutate: unlinkDevice } = useMutation({
|
|
||||||
mutationFn: async (params: { port: NdmSwitchPortInfo; lowerDevice: NdmDeviceResultVO }) => {
|
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
const { port, lowerDevice } = params;
|
|
||||||
|
|
||||||
if (!upperDeviceLinkDescription.value) return;
|
|
||||||
|
|
||||||
// 1. 从下游设备的linkDescription的upstream字段中删除当前上游设备
|
|
||||||
const modifiedLowerDevice = cloneDeep(lowerDevice);
|
|
||||||
// 解除关联时下游设备的linkDescription一定存在
|
|
||||||
const modifiedLowerDeviceLinkDescription = destr<LinkDescription>(modifiedLowerDevice.linkDescription);
|
|
||||||
// upstream字段存在时才进行删除操作
|
|
||||||
if (modifiedLowerDeviceLinkDescription.upstream) {
|
|
||||||
const index = modifiedLowerDeviceLinkDescription.upstream.findIndex((deviceStoreIndex) => deviceStoreIndex.deviceDbId === ndmDevice.value.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
modifiedLowerDeviceLinkDescription.upstream.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 2. 修改上游设备的linkDescription的downstream字段
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
|
|
||||||
delete modifiedUpperLinkDescription.downstream?.[port.portName];
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);
|
|
||||||
|
|
||||||
// 3. 发起update请求并获取最新的设备详情(离线模式下直接修改本地数据)
|
|
||||||
if (offlineDev.value) {
|
|
||||||
return { upperDevice: modifiedUpperDevice, lowerDevice: modifiedLowerDevice };
|
|
||||||
}
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
await updateDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
await updateDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
const latestUpperDevice = await detailDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
const latestLowerDevice = await detailDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
return { upperDevice: latestUpperDevice, lowerDevice: latestLowerDevice };
|
|
||||||
},
|
|
||||||
onSuccess: (data) => {
|
|
||||||
window.$loadingBar.finish();
|
|
||||||
window.$message.success('解除成功');
|
|
||||||
if (!data) return;
|
|
||||||
const { upperDevice, lowerDevice } = data;
|
|
||||||
if (!!upperDevice && !!lowerDevice) {
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...upperDevice });
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...lowerDevice });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
abortController.value.abort();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -260,12 +77,7 @@ onBeforeUnmount(() => {
|
|||||||
<NPopover :delay="300">
|
<NPopover :delay="300">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<!-- 最外层div宽度100% -->
|
<!-- 最外层div宽度100% -->
|
||||||
<div
|
<div class="port" style="height: 40px; box-sizing: border-box; display: flex; cursor: pointer" :class="getPortClassName(port)">
|
||||||
class="port"
|
|
||||||
style="height: 40px; box-sizing: border-box; display: flex; cursor: pointer"
|
|
||||||
:class="getPortClassName(port)"
|
|
||||||
@contextmenu="(payload) => onContextmenu(payload, port)"
|
|
||||||
>
|
|
||||||
<!-- 将端口号和状态指示器包裹起来 用于居中布局 -->
|
<!-- 将端口号和状态指示器包裹起来 用于居中布局 -->
|
||||||
<div style="margin: auto; display: flex; flex-direction: column; align-items: center">
|
<div style="margin: auto; display: flex; flex-direction: column; align-items: center">
|
||||||
<div style="font-size: xx-small">{{ index }}</div>
|
<div style="font-size: xx-small">{{ index }}</div>
|
||||||
@@ -277,21 +89,9 @@ onBeforeUnmount(() => {
|
|||||||
<NDescriptions bordered size="small" label-placement="left" :column="1">
|
<NDescriptions bordered size="small" label-placement="left" :column="1">
|
||||||
<NDescriptionsItem label="端口名称">{{ port.portName }}</NDescriptionsItem>
|
<NDescriptionsItem label="端口名称">{{ port.portName }}</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="状态">{{ getPortStatusValue(port) }}</NDescriptionsItem>
|
<NDescriptionsItem label="状态">{{ getPortStatusValue(port) }}</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="状态变更时间">{{ port.lastChangeTime.replace('days', '天') }} 前</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="上行速率">{{ transformPortSpeed(port, 'in') }}</NDescriptionsItem>
|
<NDescriptionsItem label="上行速率">{{ transformPortSpeed(port, 'in') }}</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="下行速率">{{ transformPortSpeed(port, 'out') }}</NDescriptionsItem>
|
<NDescriptionsItem label="下行速率">{{ transformPortSpeed(port, 'out') }}</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="总速率">{{ transformPortSpeed(port, 'total') }}</NDescriptionsItem>
|
<NDescriptionsItem label="总速率">{{ transformPortSpeed(port, 'total') }}</NDescriptionsItem>
|
||||||
<NDescriptionsItem v-if="!!port.opticalTemperature && port.opticalTemperature >= 0" label="光模块温度">{{ port.opticalTemperature }} ℃</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem v-if="!!port.opticalVoltage && port.opticalVoltage >= 0" label="光模块电压">{{ port.opticalVoltage }} mV</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem v-if="!!port.opticalBiasCurrent && port.opticalBiasCurrent >= 0" label="光模块偏置电流">{{ port.opticalBiasCurrent }} μA</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem v-if="!!port.opticalReceivePower && port.opticalReceivePower >= 0" label="光模块接收功率">{{ port.opticalReceivePower / 100 }} dBm</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem v-if="!!port.opticalTransmitPower && port.opticalTransmitPower >= 0" label="光模块发送功率">{{ port.opticalTransmitPower / 100 }} dBm</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="关联设备">
|
|
||||||
<span v-if="getLowerDeviceByPort(port)" style="text-decoration: underline; cursor: pointer" @click="() => navigateToLowerDevice(port)">
|
|
||||||
{{ getLowerDeviceByPort(port)?.name || '-' }}
|
|
||||||
</span>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</NDescriptionsItem>
|
|
||||||
</NDescriptions>
|
</NDescriptions>
|
||||||
</template>
|
</template>
|
||||||
</NPopover>
|
</NPopover>
|
||||||
@@ -301,19 +101,6 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
|
||||||
<SwitchPortLinkModal v-model:show="showModal" :ndm-device="ndmDevice" :station="station" :port="contextmenu.port" />
|
|
||||||
|
|
||||||
<NDropdown
|
|
||||||
placement="bottom-start"
|
|
||||||
trigger="manual"
|
|
||||||
:show="showContextmenu"
|
|
||||||
:x="contextmenu.x"
|
|
||||||
:y="contextmenu.y"
|
|
||||||
:options="contextmenuOptions"
|
|
||||||
@select="onSelectDropdownOption"
|
|
||||||
@clickoutside="() => (showContextmenu = false)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import {
|
|
||||||
detailDeviceApi,
|
|
||||||
updateDeviceApi,
|
|
||||||
type NdmCameraLinkDescription,
|
|
||||||
type NdmDeviceResultVO,
|
|
||||||
type NdmSwitchLinkDescription,
|
|
||||||
type NdmSwitchPortInfo,
|
|
||||||
type NdmSwitchResultVO,
|
|
||||||
type Station,
|
|
||||||
} from '@/apis';
|
|
||||||
import { DeviceTree } from '@/components';
|
|
||||||
import { tryGetDeviceType } from '@/enums';
|
|
||||||
import { useDeviceStore, useSettingStore } from '@/stores';
|
|
||||||
import { parseErrorFeedback } from '@/utils';
|
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import destr from 'destr';
|
|
||||||
import { cloneDeep } from 'es-toolkit';
|
|
||||||
import { NButton, NFlex, NModal } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, ref, toRefs } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
ndmDevice: NdmSwitchResultVO;
|
|
||||||
station: Station;
|
|
||||||
port?: NdmSwitchPortInfo;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const show = defineModel<boolean>('show', { default: false });
|
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const { ndmDevice, station, port } = toRefs(props);
|
|
||||||
|
|
||||||
const upperDeviceLinkDescription = computed(() => {
|
|
||||||
const result = destr<any>(ndmDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result as NdmSwitchLinkDescription;
|
|
||||||
});
|
|
||||||
|
|
||||||
const lowerDevice = ref<NdmDeviceResultVO>();
|
|
||||||
const lowerDeviceLinkDescription = computed<NdmCameraLinkDescription | NdmSwitchLinkDescription | null>(() => {
|
|
||||||
if (!lowerDevice.value) return null;
|
|
||||||
const result = destr<any>(lowerDevice.value.linkDescription);
|
|
||||||
if (!result) return null;
|
|
||||||
if (typeof result !== 'object') return null;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
const onAfterSelectDevice = (device: NdmDeviceResultVO) => {
|
|
||||||
lowerDevice.value = device;
|
|
||||||
};
|
|
||||||
|
|
||||||
const abortController = ref<AbortController>(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: linkPortToDevice, isPending: linking } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
window.$loadingBar.start();
|
|
||||||
|
|
||||||
const upperDeviceType = tryGetDeviceType(ndmDevice.value.deviceType);
|
|
||||||
if (!upperDeviceType) throw new Error('本设备类型未知');
|
|
||||||
const upperDeviceDbId = ndmDevice.value.id;
|
|
||||||
if (!upperDeviceDbId) throw new Error('本设备没有ID');
|
|
||||||
|
|
||||||
const { portName } = port.value ?? {};
|
|
||||||
if (!portName) throw new Error('该端口没有名称');
|
|
||||||
|
|
||||||
if (!lowerDevice.value) throw new Error('请选择要关联的设备');
|
|
||||||
const lowerDeviceType = tryGetDeviceType(lowerDevice.value?.deviceType);
|
|
||||||
if (!lowerDeviceType) throw new Error('该设备类型未知');
|
|
||||||
const lowerDeviceDbId = lowerDevice.value?.id;
|
|
||||||
if (!lowerDeviceDbId) throw new Error('该设备没有ID');
|
|
||||||
|
|
||||||
// 0. 检查是否会导致循环关联,
|
|
||||||
if (upperDeviceDbId === lowerDeviceDbId) throw new Error('不能关联到自身');
|
|
||||||
// 以及检查上游设备的linkDescription的downstream字段是否存在某个端口已经关联下游设备
|
|
||||||
const duplicated = Object.entries(upperDeviceLinkDescription.value?.downstream ?? {}).find(([, deviceStoreIndex]) => {
|
|
||||||
return deviceStoreIndex.deviceDbId === lowerDeviceDbId;
|
|
||||||
});
|
|
||||||
if (duplicated) {
|
|
||||||
const [portName] = duplicated;
|
|
||||||
throw new Error(`该设备已关联到端口${portName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 修改上游设备的linkDescription的downstream字段
|
|
||||||
const modifiedUpperDevice = cloneDeep(ndmDevice.value);
|
|
||||||
let modifiedUpperDeviceLinkDescription: NdmSwitchLinkDescription;
|
|
||||||
if (!upperDeviceLinkDescription.value) {
|
|
||||||
modifiedUpperDeviceLinkDescription = {
|
|
||||||
downstream: {
|
|
||||||
[portName]: {
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: lowerDeviceType,
|
|
||||||
deviceDbId: lowerDeviceDbId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
modifiedUpperDeviceLinkDescription = {
|
|
||||||
...upperDeviceLinkDescription.value,
|
|
||||||
downstream: {
|
|
||||||
...upperDeviceLinkDescription.value.downstream,
|
|
||||||
[portName]: {
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: lowerDeviceType,
|
|
||||||
deviceDbId: lowerDeviceDbId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 2. 修改下游设备的linkDescription的upstream字段
|
|
||||||
const modifiedLowerDevice = cloneDeep(lowerDevice.value);
|
|
||||||
let modifiedLowerDeviceLinkDescription: NdmSwitchLinkDescription | NdmCameraLinkDescription;
|
|
||||||
if (!lowerDeviceLinkDescription.value) {
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
upstream: [
|
|
||||||
{
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const upstream = cloneDeep(lowerDeviceLinkDescription.value.upstream);
|
|
||||||
if (!upstream) {
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
...lowerDeviceLinkDescription.value,
|
|
||||||
upstream: [
|
|
||||||
{
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const deviceStoreIndex = upstream.find((deviceStoreIndex) => deviceStoreIndex.deviceDbId === upperDeviceDbId);
|
|
||||||
if (!deviceStoreIndex) {
|
|
||||||
upstream.push({
|
|
||||||
stationCode: station.value.code,
|
|
||||||
deviceType: upperDeviceType,
|
|
||||||
deviceDbId: upperDeviceDbId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
modifiedLowerDeviceLinkDescription = {
|
|
||||||
...lowerDeviceLinkDescription.value,
|
|
||||||
upstream,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);
|
|
||||||
|
|
||||||
// 3. 发起update请求并获取最新的设备详情(离线模式下直接修改本地数据)
|
|
||||||
if (offlineDev.value) {
|
|
||||||
return { upperDevice: modifiedUpperDevice, lowerDevice: modifiedLowerDevice };
|
|
||||||
}
|
|
||||||
const stationCode = station.value.code;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
await updateDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
await updateDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
const latestUpperDevice = await detailDeviceApi(modifiedUpperDevice, { stationCode, signal });
|
|
||||||
const latestLowerDevice = await detailDeviceApi(modifiedLowerDevice, { stationCode, signal });
|
|
||||||
return { upperDevice: latestUpperDevice, lowerDevice: latestLowerDevice };
|
|
||||||
},
|
|
||||||
onSuccess: ({ upperDevice, lowerDevice }) => {
|
|
||||||
show.value = false;
|
|
||||||
window.$loadingBar.finish();
|
|
||||||
window.$message.success('关联成功');
|
|
||||||
if (!!upperDevice && !!lowerDevice) {
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...upperDevice });
|
|
||||||
deviceStore.patchDevice(station.value.code, { ...lowerDevice });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onLink = () => {
|
|
||||||
linkPortToDevice();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
abortController.value.abort();
|
|
||||||
show.value = false;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="show" preset="card" style="width: 600px; height: 600px" :content-style="{ height: '100%', overflow: 'hidden' }" @close="onCancel" @esc="onCancel">
|
|
||||||
<template #header>
|
|
||||||
<span>{{ ndmDevice.name }}</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>{{ port?.portName }}</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>关联设备</span>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<DeviceTree :station="station" :events="['select']" :device-prefix-label="'选择'" @after-select-device="onAfterSelectDevice" />
|
|
||||||
</template>
|
|
||||||
<template #action>
|
|
||||||
<NFlex justify="end">
|
|
||||||
<NButton size="small" quaternary @click="onCancel">取消</NButton>
|
|
||||||
<NButton size="small" type="primary" :disabled="!lowerDevice" :loading="linking" @click="onLink">关联</NButton>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="ts"></style>
|
|
||||||
@@ -139,6 +139,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -1,118 +1,16 @@
|
|||||||
<script lang="ts">
|
|
||||||
const CAMERA_TYPES = {
|
|
||||||
'001': '模拟彩色云台摄像机',
|
|
||||||
'002': '模拟彩色半球摄像机',
|
|
||||||
'003': '模拟彩色固定摄像机',
|
|
||||||
'004': '数字高清云台摄像机',
|
|
||||||
'005': '数字高清半球摄像机',
|
|
||||||
'006': '数字高清固定摄像机',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
type CameraType = keyof typeof CAMERA_TYPES;
|
|
||||||
|
|
||||||
const isCameraTypeCode = (code: string): code is CameraType => {
|
|
||||||
return code in CAMERA_TYPES;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NdmCameraResultVO, Station } from '@/apis';
|
import type { NdmCameraResultVO, Station } from '@/apis';
|
||||||
import { DeviceCommonCard, DeviceHeaderCard } from '@/components';
|
import { DeviceCommonCard, DeviceHeaderCard } from '@/components';
|
||||||
import { useSettingStore } from '@/stores';
|
import { NFlex } from 'naive-ui';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/vue-query';
|
import { computed, toRefs } from 'vue';
|
||||||
import axios from 'axios';
|
|
||||||
import { NDescriptions, NDescriptionsItem, NFlex } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, toRefs, watch } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
ndmDevice: NdmCameraResultVO;
|
ndmDevice: NdmCameraResultVO;
|
||||||
station: Station;
|
station: Station;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const { ndmDevice, station } = toRefs(props);
|
const { ndmDevice, station } = toRefs(props);
|
||||||
|
|
||||||
const cameraType = computed(() => {
|
|
||||||
const gbCode = ndmDevice.value.gbCode;
|
|
||||||
if (!gbCode) return '-';
|
|
||||||
const cameraTypeCode = gbCode.slice(11, 14);
|
|
||||||
if (!isCameraTypeCode(cameraTypeCode)) return '-';
|
|
||||||
return CAMERA_TYPES[cameraTypeCode];
|
|
||||||
});
|
|
||||||
|
|
||||||
const QUERY_KEY = 'camera-installation-area-query';
|
|
||||||
|
|
||||||
const { data: installationArea } = useQuery({
|
|
||||||
queryKey: computed(() => [QUERY_KEY, ndmDevice.value.gbCode, station.value.code]),
|
|
||||||
enabled: computed(() => !offlineDev.value),
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async ({ signal }) => {
|
|
||||||
const UNKNOWN_NAME = '-';
|
|
||||||
|
|
||||||
const gbCode = ndmDevice.value.gbCode;
|
|
||||||
if (!gbCode) return UNKNOWN_NAME;
|
|
||||||
|
|
||||||
const MINIO_PREFIX = `/minio`;
|
|
||||||
const CDN_VIMP_CODES_PREFIX = `${MINIO_PREFIX}/cdn/vimp/codes`;
|
|
||||||
const CODE_STATIONS_JSON_PATH = `${CDN_VIMP_CODES_PREFIX}/codeStations.json`;
|
|
||||||
const CODE_STATION_AREAS_JSON_PATH = `${CDN_VIMP_CODES_PREFIX}/codeStationAreas.json`;
|
|
||||||
const CODE_PARKING_AREAS_JSON_PATH = `${CDN_VIMP_CODES_PREFIX}/codeParkingAreas.json`;
|
|
||||||
const CODE_OCC_AREAS_JSON_PATH = `${CDN_VIMP_CODES_PREFIX}/codeOccAreas.json`;
|
|
||||||
|
|
||||||
// minio中的编码表结构
|
|
||||||
type Unit = { name: string; type: 'train' | 'station' | 'parking' | 'occ' };
|
|
||||||
type Area = { code: string; name: string; subs: Array<{ code: string; name: string }> };
|
|
||||||
|
|
||||||
const { data: unitCodes } = await axios.get<Record<string, Unit>>(CODE_STATIONS_JSON_PATH, { signal });
|
|
||||||
|
|
||||||
// 根据国标编码的前6位匹配minio中的编码表
|
|
||||||
const unitCode = gbCode.slice(0, 6);
|
|
||||||
const unit = unitCodes[unitCode];
|
|
||||||
if (!unit) return UNKNOWN_NAME;
|
|
||||||
// 获取编码表中的线路/单位类型
|
|
||||||
const unitType = unit.type;
|
|
||||||
// 国标编码的第7位到第8位为1级区域编码
|
|
||||||
const tier1AreaCode = gbCode.slice(6, 8);
|
|
||||||
// 国标编码的第9位到第11位为2级区域编码
|
|
||||||
const tier2AreaCode = gbCode.slice(8, 11);
|
|
||||||
|
|
||||||
if (unitType === 'train') {
|
|
||||||
return unit.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const areaJsonPaths: Record<string, string> = {
|
|
||||||
station: CODE_STATION_AREAS_JSON_PATH,
|
|
||||||
parking: CODE_PARKING_AREAS_JSON_PATH,
|
|
||||||
occ: CODE_OCC_AREAS_JSON_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsonPath = areaJsonPaths[unitType];
|
|
||||||
if (!jsonPath) return UNKNOWN_NAME;
|
|
||||||
|
|
||||||
// 获取1级区域
|
|
||||||
const { data: areaCodes } = await axios.get<Area[]>(jsonPath, { signal });
|
|
||||||
const tier1Area = areaCodes.find((area) => area.code === tier1AreaCode);
|
|
||||||
if (!tier1Area) return UNKNOWN_NAME;
|
|
||||||
|
|
||||||
// 获取2级区域
|
|
||||||
const tier2Area = tier1Area.subs.find((area) => area.code === `${tier1AreaCode}${tier2AreaCode}`);
|
|
||||||
if (!tier2Area) return UNKNOWN_NAME;
|
|
||||||
|
|
||||||
// 拼接1级和2级区域名称
|
|
||||||
return `${tier1Area.name}-${tier2Area.name}`;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
watch(offlineDev, (offline) => {
|
|
||||||
if (offline) {
|
|
||||||
queryClient.cancelQueries({ queryKey: [QUERY_KEY] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const commonInfo = computed(() => {
|
const commonInfo = computed(() => {
|
||||||
const {
|
const {
|
||||||
createdTime,
|
createdTime,
|
||||||
@@ -146,14 +44,7 @@ const commonInfo = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station">
|
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
||||||
<template #append-info>
|
|
||||||
<NDescriptions bordered size="small" label-placement="left" :columns="1" style="width: 60%; min-width: 400px">
|
|
||||||
<NDescriptionsItem label="摄像机类型" :span="1">{{ cameraType }}</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="建议安装区域" :span="1">{{ installationArea ?? '-' }}</NDescriptionsItem>
|
|
||||||
</NDescriptions>
|
|
||||||
</template>
|
|
||||||
</DeviceHeaderCard>
|
|
||||||
<DeviceCommonCard :common-info="commonInfo" />
|
<DeviceCommonCard :common-info="commonInfo" />
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -129,6 +129,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const circuits = computed(() => lastDiagInfo.value?.info?.at(0)?.circuits);
|
|||||||
<DeviceCommonCard :common-info="commonInfo" />
|
<DeviceCommonCard :common-info="commonInfo" />
|
||||||
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
|
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
|
||||||
<SecurityBoxEnvCard :fan-speeds="fanSpeeds" :temperature="temperature" :humidity="humidity" :switches="switches" />
|
<SecurityBoxEnvCard :fan-speeds="fanSpeeds" :temperature="temperature" :humidity="humidity" :switches="switches" />
|
||||||
<SecurityBoxCircuitCard :ndm-device="ndmDevice" :station="station" :circuits="circuits" />
|
<SecurityBoxCircuitCard :circuits="circuits" :ndm-device="ndmDevice" :station="station" />
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import ServerAlive from './server-alive.vue';
|
|||||||
import ServerCard from './server-card.vue';
|
import ServerCard from './server-card.vue';
|
||||||
import ServerCurrentDiag from './server-current-diag.vue';
|
import ServerCurrentDiag from './server-current-diag.vue';
|
||||||
import ServerHistoryDiag from './server-history-diag.vue';
|
import ServerHistoryDiag from './server-history-diag.vue';
|
||||||
import ServerStreamPush from './server-stream-push.vue';
|
|
||||||
import ServerUpdate from './server-update.vue';
|
import ServerUpdate from './server-update.vue';
|
||||||
|
|
||||||
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHistoryDiag, ServerUpdate, ServerStreamPush };
|
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHistoryDiag, ServerUpdate };
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
import { isMediaServerAliveApi, isSipServerAliveApi, type NdmServerResultVO, type Station } from '@/apis';
|
import { isMediaServerAliveApi, isSipServerAliveApi, type NdmServerResultVO, type Station } from '@/apis';
|
||||||
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
||||||
import { useSettingStore } from '@/stores';
|
import { useSettingStore } from '@/stores';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/vue-query';
|
import { useQuery } from '@tanstack/vue-query';
|
||||||
import { NCard, NTag } from 'naive-ui';
|
import { NCard, NTag } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, toRefs, watch } from 'vue';
|
import { computed, toRefs } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
ndmDevice: NdmServerResultVO;
|
ndmDevice: NdmServerResultVO;
|
||||||
@@ -15,38 +15,26 @@ const props = defineProps<{
|
|||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
const { offlineDev } = storeToRefs(settingStore);
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const { ndmDevice, station } = toRefs(props);
|
const { ndmDevice, station } = toRefs(props);
|
||||||
|
|
||||||
const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
||||||
|
|
||||||
const MEDIA_SERVER_ALIVE_QUERY_KEY = 'media-server-alive-query';
|
|
||||||
const VIDEO_SERVER_ALIVE_QUERY_KEY = 'video-server-alive-query';
|
|
||||||
const { data: isMediaServerAlive } = useQuery({
|
const { data: isMediaServerAlive } = useQuery({
|
||||||
queryKey: computed(() => [MEDIA_SERVER_ALIVE_QUERY_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => ['media-server-alive-query', ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => !offlineDev.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmMediaServer),
|
enabled: computed(() => !offlineDev.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmMediaServer),
|
||||||
refetchInterval: 30 * 1000,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal }) => {
|
||||||
const alives = await isMediaServerAliveApi({ stationCode: station.value.code, signal });
|
const alives = await isMediaServerAliveApi({ stationCode: station.value.code, signal });
|
||||||
return alives.find((alive) => alive.ip === ndmDevice.value.ipAddress);
|
return alives.find((alive) => alive.ip === ndmDevice.value.ipAddress);
|
||||||
},
|
},
|
||||||
|
refetchInterval: 30 * 1000,
|
||||||
});
|
});
|
||||||
const { data: isSipServerAlive } = useQuery({
|
const { data: isSipServerAlive } = useQuery({
|
||||||
queryKey: computed(() => [VIDEO_SERVER_ALIVE_QUERY_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => ['video-server-alive-query', ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => !offlineDev.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer),
|
enabled: computed(() => !offlineDev.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer),
|
||||||
refetchInterval: 30 * 1000,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal }) => {
|
||||||
return await isSipServerAliveApi({ stationCode: station.value.code, signal });
|
return await isSipServerAliveApi({ stationCode: station.value.code, signal });
|
||||||
},
|
},
|
||||||
});
|
refetchInterval: 30 * 1000,
|
||||||
watch(offlineDev, (offline) => {
|
|
||||||
if (offline) {
|
|
||||||
queryClient.cancelQueries({ queryKey: [MEDIA_SERVER_ALIVE_QUERY_KEY] });
|
|
||||||
queryClient.cancelQueries({ queryKey: [VIDEO_SERVER_ALIVE_QUERY_KEY] });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -56,29 +44,20 @@ watch(offlineDev, (offline) => {
|
|||||||
<span>服务状态</span>
|
<span>服务状态</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<template v-if="offlineDev">
|
<div v-if="deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer">
|
||||||
<span>-</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<template v-if="deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer">
|
|
||||||
<span>流媒体服务状态:</span>
|
<span>流媒体服务状态:</span>
|
||||||
<template v-if="!!isMediaServerAlive">
|
<template v-if="isMediaServerAlive">
|
||||||
<NTag size="small" :type="isMediaServerAlive.online ? 'success' : 'error'">{{ isMediaServerAlive.online ? '在线' : '离线' }}</NTag>
|
<NTag size="small" :type="isMediaServerAlive.online ? 'success' : 'error'">{{ isMediaServerAlive.online ? '在线' : '离线' }}</NTag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<span v-else>-</span>
|
||||||
<span>-</span>
|
</div>
|
||||||
</template>
|
<div v-if="deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer">
|
||||||
</template>
|
|
||||||
<template v-if="deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer">
|
|
||||||
<span>信令服务状态:</span>
|
<span>信令服务状态:</span>
|
||||||
<template v-if="isSipServerAlive !== undefined">
|
<template v-if="isSipServerAlive">
|
||||||
<NTag size="small" :type="isSipServerAlive ? 'success' : 'error'">{{ isSipServerAlive ? '在线' : '离线' }}</NTag>
|
<NTag size="small" :type="isSipServerAlive ? 'success' : 'error'">{{ isSipServerAlive ? '在线' : '离线' }}</NTag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<span v-else>-</span>
|
||||||
<span>-</span>
|
</div>
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
</NCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
||||||
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive, ServerStreamPush } from '@/components';
|
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive } from '@/components';
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { NFlex } from 'naive-ui';
|
import { NFlex } from 'naive-ui';
|
||||||
import { computed, toRefs } from 'vue';
|
import { computed, toRefs } from 'vue';
|
||||||
@@ -30,7 +30,6 @@ const runningTime = computed(() => lastDiagInfo.value?.commInfo?.系统运行时
|
|||||||
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
||||||
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
||||||
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
||||||
<ServerStreamPush :ndm-device="ndmDevice" :station="station" />
|
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { getAllPushApi, type NdmServerResultVO, type Station } from '@/apis';
|
|
||||||
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
|
||||||
import { useSettingStore } from '@/stores';
|
|
||||||
import { useQuery, useQueryClient } from '@tanstack/vue-query';
|
|
||||||
import { NCard, NCollapse, NCollapseItem, NFlex, NTag, NText } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, toRefs, watch } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
ndmDevice: NdmServerResultVO;
|
|
||||||
station: Station;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { offlineDev } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const { ndmDevice, station } = toRefs(props);
|
|
||||||
|
|
||||||
const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
|
||||||
|
|
||||||
const showCard = computed(() => deviceType.value === DEVICE_TYPE_LITERALS.ndmMediaServer);
|
|
||||||
|
|
||||||
const SERVER_STREAM_PUSH_KEY = 'server-stream-push-query';
|
|
||||||
|
|
||||||
const { data: streamPushes } = useQuery({
|
|
||||||
queryKey: computed(() => [SERVER_STREAM_PUSH_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
|
||||||
enabled: computed(() => !offlineDev.value && showCard.value),
|
|
||||||
refetchInterval: 30 * 1000,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async ({ signal }) => {
|
|
||||||
const streamPushes = await getAllPushApi({ stationCode: station.value.code, signal });
|
|
||||||
return streamPushes;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
watch(offlineDev, (offline) => {
|
|
||||||
if (offline) {
|
|
||||||
queryClient.cancelQueries({ queryKey: [SERVER_STREAM_PUSH_KEY] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
interface StreamPushStat {
|
|
||||||
ip: string;
|
|
||||||
port: number | null;
|
|
||||||
ssrc: string | null;
|
|
||||||
channelIds: string[];
|
|
||||||
}
|
|
||||||
const streamPushStat = computed(() => {
|
|
||||||
const stat: StreamPushStat[] = [];
|
|
||||||
streamPushes.value?.forEach((push) => {
|
|
||||||
if (!push.ip || !push.channelId) return;
|
|
||||||
const existIndex = stat.findIndex((item) => item.ip === push.ip);
|
|
||||||
if (existIndex === -1) {
|
|
||||||
stat.push({ ip: push.ip, port: push.port, ssrc: push.ssrc, channelIds: [push.channelId] });
|
|
||||||
} else {
|
|
||||||
const statItem = stat[existIndex];
|
|
||||||
if (!statItem) return;
|
|
||||||
statItem.channelIds.push(push.channelId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return stat;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard v-if="showCard" hoverable size="small">
|
|
||||||
<template #header>
|
|
||||||
<span>推流统计</span>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<template v-if="offlineDev">
|
|
||||||
<span>-</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<NFlex vertical>
|
|
||||||
<NText depth="3">共 {{ streamPushStat.length }} 个推流目标</NText>
|
|
||||||
<NCollapse v-if="streamPushStat.length > 0">
|
|
||||||
<NCollapseItem v-for="{ ip, port, ssrc, channelIds } in streamPushStat" :key="`${ip}:${port}:${ssrc}`" :name="`${ip}:${port}:${ssrc}`">
|
|
||||||
<template #header>
|
|
||||||
<span>{{ ip }}</span>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<span>{{ channelIds.length }} 路</span>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<NFlex>
|
|
||||||
<NTag v-for="channelId in channelIds" :key="channelId" size="small" type="info" :bordered="false">{{ channelId }}</NTag>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { detailMediaServerApi, detailVideoServerApi, icmpEntityByDeviceId, updateMediaServerApi, updateVideoServerApi, type NdmServerResultVO, type NdmServerUpdateVO, type Station } from '@/apis';
|
import {
|
||||||
|
detailMediaServerApi,
|
||||||
|
detailVideoServerApi,
|
||||||
|
icmpEntityByDeviceId,
|
||||||
|
updateMediaServerApi,
|
||||||
|
updateVideoServerApi,
|
||||||
|
type NdmMediaServerUpdateVO,
|
||||||
|
type NdmServerResultVO,
|
||||||
|
type NdmServerUpdateVO,
|
||||||
|
type NdmVideoServerUpdateVO,
|
||||||
|
type Station,
|
||||||
|
} from '@/apis';
|
||||||
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
||||||
import { useDeviceStore } from '@/stores';
|
import { useDeviceStore } from '@/stores';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { parseErrorFeedback } from '@/utils';
|
||||||
@@ -82,10 +93,10 @@ const { mutate: updateDevice, isPending } = useMutation({
|
|||||||
const stationCode = station.value.code;
|
const stationCode = station.value.code;
|
||||||
const signal = abortController.value.signal;
|
const signal = abortController.value.signal;
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmMediaServer) {
|
||||||
await updateMediaServerApi(localDevice.value, { stationCode, signal });
|
await updateMediaServerApi(localDevice.value as NdmMediaServerUpdateVO, { stationCode, signal });
|
||||||
return await detailMediaServerApi(`${localDevice.value.id}`, { stationCode, signal });
|
return await detailMediaServerApi(`${localDevice.value.id}`, { stationCode, signal });
|
||||||
} else if (deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer) {
|
} else if (deviceType === DEVICE_TYPE_LITERALS.ndmVideoServer) {
|
||||||
await updateVideoServerApi(localDevice.value, { stationCode, signal });
|
await updateVideoServerApi(`${localDevice.value.id}`, localDevice.value as NdmVideoServerUpdateVO, { stationCode, signal });
|
||||||
return await detailVideoServerApi(`${localDevice.value.id}`, { stationCode, signal });
|
return await detailVideoServerApi(`${localDevice.value.id}`, { stationCode, signal });
|
||||||
} else {
|
} else {
|
||||||
throw new Error('不是服务器设备');
|
throw new Error('不是服务器设备');
|
||||||
@@ -140,6 +151,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const ports = computed(() => lastDiagInfo.value?.info?.portInfoList);
|
|||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
||||||
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
|
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
|
||||||
<SwitchPortCard :ndm-device="ndmDevice" :station="station" :ports="ports" />
|
<SwitchPortCard :ports="ports" />
|
||||||
</NFlex>
|
</NFlex>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ onBeforeUnmount(() => {
|
|||||||
<NFormItem label-placement="left" label="设备描述">
|
<NFormItem label-placement="left" label="设备描述">
|
||||||
<NInput v-model:value="localDevice.description" />
|
<NInput v-model:value="localDevice.description" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem label-placement="left" label="上游设备">
|
||||||
|
<NInput v-model:value="localDevice.linkDescription" />
|
||||||
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
|
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
|
||||||
import { useDeviceTree, type UseDeviceTreeReturn } from '@/composables';
|
import { useDeviceTree } from '@/composables';
|
||||||
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType } from '@/enums';
|
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType } from '@/enums';
|
||||||
import { isNvrCluster } from '@/helpers';
|
import { isNvrCluster } from '@/helpers';
|
||||||
import { useDeviceStore, useStationStore } from '@/stores';
|
import { useDeviceStore, useStationStore } from '@/stores';
|
||||||
@@ -30,33 +30,14 @@ import { storeToRefs } from 'pinia';
|
|||||||
import { computed, h, nextTick, onBeforeUnmount, ref, toRefs, useTemplateRef, watch, type CSSProperties } from 'vue';
|
import { computed, h, nextTick, onBeforeUnmount, ref, toRefs, useTemplateRef, watch, type CSSProperties } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/**
|
station?: Station; // 支持渲染指定车站的设备树
|
||||||
* 支持渲染指定车站的设备树
|
|
||||||
*/
|
|
||||||
station?: Station;
|
|
||||||
/**
|
|
||||||
* 允许的事件类型
|
|
||||||
*
|
|
||||||
* - `select`:允许选择设备
|
|
||||||
* - `manage`:允许右键菜单管理设备
|
|
||||||
*/
|
|
||||||
events?: ('select' | 'manage')[];
|
|
||||||
/**
|
|
||||||
* 是否同步路由参数
|
|
||||||
*/
|
|
||||||
syncRoute?: boolean;
|
|
||||||
/**
|
|
||||||
* 设备节点的前缀按钮文字
|
|
||||||
*/
|
|
||||||
devicePrefixLabel?: string;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
afterSelectDevice: [device: NdmDeviceResultVO, stationCode: Station['code']];
|
selectDevice: [device: NdmDeviceResultVO, stationCode: Station['code']];
|
||||||
exposeSelectDeviceFn: [selectDeviceFn: UseDeviceTreeReturn['selectDevice']];
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { station, events, syncRoute, devicePrefixLabel } = toRefs(props);
|
const { station } = toRefs(props);
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
|
||||||
@@ -65,26 +46,24 @@ const {
|
|||||||
selectedStationCode,
|
selectedStationCode,
|
||||||
selectedDeviceType,
|
selectedDeviceType,
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
|
hasFromPage,
|
||||||
selectDevice,
|
selectDevice,
|
||||||
|
routeDevice,
|
||||||
// 设备管理
|
// 设备管理
|
||||||
exportDevice,
|
exportDevice,
|
||||||
exportDeviceTemplate,
|
exportDeviceTemplate,
|
||||||
importDevice,
|
importDevice,
|
||||||
deleteDevice,
|
deleteDevice,
|
||||||
} = useDeviceTree({
|
} = useDeviceTree();
|
||||||
syncRoute: computed(() => !!syncRoute.value),
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将 `selectDevice` 函数暴露给父组件
|
|
||||||
emit('exposeSelectDeviceFn', selectDevice);
|
|
||||||
|
|
||||||
const onSelectDevice = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
const onSelectDevice = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
||||||
// 仅当事件列表包含 `select` 时才触发选择事件
|
|
||||||
if (!events.value) return;
|
|
||||||
if (!events.value.includes('select')) return;
|
|
||||||
|
|
||||||
selectDevice(device, stationCode);
|
selectDevice(device, stationCode);
|
||||||
emit('afterSelectDevice', device, stationCode);
|
emit('selectDevice', device, stationCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRouteDevice = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
||||||
|
routeDevice(device, stationCode, { path: '/device' });
|
||||||
|
emit('selectDevice', device, stationCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stationStore = useStationStore();
|
const stationStore = useStationStore();
|
||||||
@@ -209,20 +188,20 @@ const nodeProps: TreeProps['nodeProps'] = ({ option }) => {
|
|||||||
onDblclick: (payload) => {
|
onDblclick: (payload) => {
|
||||||
if (option['device']) {
|
if (option['device']) {
|
||||||
payload.stopPropagation();
|
payload.stopPropagation();
|
||||||
|
|
||||||
const device = option['device'] as NdmDeviceResultVO;
|
const device = option['device'] as NdmDeviceResultVO;
|
||||||
const stationCode = option['stationCode'] as Station['code'];
|
const stationCode = option['stationCode'] as Station['code'];
|
||||||
|
// 区分是否需要跳转路由
|
||||||
|
// 当 props.station 存在时,说明当前是单独渲染车站的设备树,需要跳转路由到设备诊断页面
|
||||||
|
if (!station.value) {
|
||||||
onSelectDevice(device, stationCode);
|
onSelectDevice(device, stationCode);
|
||||||
|
} else {
|
||||||
|
onRouteDevice(device, stationCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onContextmenu: (payload) => {
|
onContextmenu: (payload) => {
|
||||||
payload.stopPropagation();
|
payload.stopPropagation();
|
||||||
payload.preventDefault();
|
payload.preventDefault();
|
||||||
|
|
||||||
// 仅当事件列表包含 `manage` 时才显示右键菜单
|
|
||||||
if (!events.value?.includes('manage')) return;
|
|
||||||
|
|
||||||
const { clientX, clientY } = payload;
|
const { clientX, clientY } = payload;
|
||||||
const stationCode = option['stationCode'] as Station['code'];
|
const stationCode = option['stationCode'] as Station['code'];
|
||||||
const deviceType = option['deviceType'] as DeviceType | undefined;
|
const deviceType = option['deviceType'] as DeviceType | undefined;
|
||||||
@@ -252,8 +231,7 @@ const renderIcmpStatistics = (onlineCount: number, offlineCount: number, count:
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
||||||
const renderViewDeviceButton = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
const renderViewDeviceButton = (device: NdmDeviceResultVO, stationCode: string) => {
|
||||||
if (!devicePrefixLabel.value) return null;
|
|
||||||
return h(
|
return h(
|
||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
@@ -265,11 +243,17 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station[
|
|||||||
} as CSSProperties,
|
} as CSSProperties,
|
||||||
onClick: (e: MouseEvent) => {
|
onClick: (e: MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
// 选择设备
|
||||||
|
// 区分是否需要跳转路由
|
||||||
|
// 当 props.station 存在时,说明当前是单独渲染车站的设备树,需要跳转路由到设备诊断页面
|
||||||
|
if (!station.value) {
|
||||||
onSelectDevice(device, stationCode);
|
onSelectDevice(device, stationCode);
|
||||||
|
} else {
|
||||||
|
onRouteDevice(device, stationCode);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
() => devicePrefixLabel.value,
|
() => '查看',
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const renderDeviceStatusTag = (device: NdmDeviceResultVO) => {
|
const renderDeviceStatusTag = (device: NdmDeviceResultVO) => {
|
||||||
@@ -456,7 +440,7 @@ const onLocateDeviceTree = async () => {
|
|||||||
activeTab.value = deviceType;
|
activeTab.value = deviceType;
|
||||||
|
|
||||||
// 展开选择的车站
|
// 展开选择的车站
|
||||||
expandedKeys.value.push(selectedStationCode.value);
|
expandedKeys.value = [selectedStationCode.value];
|
||||||
|
|
||||||
// 当选择录像机时,如果不是集群,进一步展开该录像机所在的集群节点
|
// 当选择录像机时,如果不是集群,进一步展开该录像机所在的集群节点
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
||||||
@@ -477,11 +461,16 @@ const onLocateDeviceTree = async () => {
|
|||||||
|
|
||||||
animated.value = true;
|
animated.value = true;
|
||||||
};
|
};
|
||||||
// 渲染全线设备树时,当选择的设备发生变化,则定位设备树
|
// 渲染全线设备树时,若是首次经过路由跳转而来选择设备,则定位设备树
|
||||||
// 暂时不考虑多次执行的问题,因为当选择的设备在设备树视口内时,不会发生滚动
|
const unwatchLocate = watch(selectedDevice, async (newDevice, oldDevice) => {
|
||||||
watch(selectedDevice, async () => {
|
|
||||||
if (!!station.value) return;
|
if (!!station.value) return;
|
||||||
|
if (!hasFromPage.value) return;
|
||||||
|
if (!!newDevice && !oldDevice) {
|
||||||
|
if (!!deviceTreeInst.value) {
|
||||||
await onLocateDeviceTree();
|
await onLocateDeviceTree();
|
||||||
|
unwatchLocate();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -546,7 +535,6 @@ watch(selectedDevice, async () => {
|
|||||||
virtual-scroll
|
virtual-scroll
|
||||||
:data="stationDeviceTreeData"
|
:data="stationDeviceTreeData"
|
||||||
:animated="animated"
|
:animated="animated"
|
||||||
:selected-keys="selectedKeys"
|
|
||||||
:show-irrelevant-nodes="false"
|
:show-irrelevant-nodes="false"
|
||||||
:pattern="searchPattern"
|
:pattern="searchPattern"
|
||||||
:filter="searchFilter"
|
:filter="searchFilter"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { retentionDaysApi, snapStatusApi, type LineAlarms, type LineDevices, type Station, type VersionInfo } from '@/apis';
|
import type { LineAlarms, LineDevices, Station, VersionInfo } from '@/apis';
|
||||||
import { ThemeSwitch } from '@/components';
|
import { ThemeSwitch } from '@/components';
|
||||||
import { NDM_ALARM_STORE_ID, NDM_DEVICE_STORE_ID, NDM_STATION_STORE_ID } from '@/constants';
|
import { NDM_ALARM_STORE_ID, NDM_DEVICE_STORE_ID, NDM_STATION_STORE_ID } from '@/constants';
|
||||||
import { usePollingStore, useSettingStore } from '@/stores';
|
import { usePollingStore, useSettingStore } from '@/stores';
|
||||||
@@ -7,13 +7,16 @@ import { downloadByData, getAppEnvConfig, parseErrorFeedback, sleep } from '@/ut
|
|||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { DeleteOutlined, ExportOutlined, ImportOutlined } from '@vicons/antd';
|
import { DeleteOutlined, ExportOutlined, ImportOutlined } from '@vicons/antd';
|
||||||
import { useEventListener } from '@vueuse/core';
|
import { useEventListener } from '@vueuse/core';
|
||||||
import axios, { isCancel } from 'axios';
|
import axios from 'axios';
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { isFunction } from 'es-toolkit';
|
import { isFunction } from 'es-toolkit';
|
||||||
import localforage from 'localforage';
|
import localforage from 'localforage';
|
||||||
import { NButton, NButtonGroup, NDivider, NDrawer, NDrawerContent, NDropdown, NFlex, NFormItem, NIcon, NInput, NInputNumber, NModal, NSwitch, NText, type DropdownOption } from 'naive-ui';
|
import { NButton, NDivider, NDrawer, NDrawerContent, NDropdown, NFlex, NFormItem, NIcon, NInput, NInputNumber, NModal, NSwitch, NText, type DropdownOption } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ref, watch } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
const show = defineModel<boolean>('show', { default: false });
|
const show = defineModel<boolean>('show', { default: false });
|
||||||
|
|
||||||
@@ -37,83 +40,6 @@ const { mutate: getVersionInfo } = useMutation({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const abortControllers = ref({
|
|
||||||
retentionDays: new AbortController(),
|
|
||||||
snapStatus: new AbortController(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const retentionDays = ref(0);
|
|
||||||
const { mutate: getRetentionDays, isPending: retentionDaysLoading } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortControllers.value.retentionDays.abort();
|
|
||||||
abortControllers.value.retentionDays = new AbortController();
|
|
||||||
const signal = abortControllers.value.retentionDays.signal;
|
|
||||||
const days = await retentionDaysApi('get', { signal });
|
|
||||||
return days;
|
|
||||||
},
|
|
||||||
onSuccess: (days) => {
|
|
||||||
retentionDays.value = days;
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { mutate: saveRetentionDays, isPending: retentionDaysSaving } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortControllers.value.retentionDays.abort();
|
|
||||||
abortControllers.value.retentionDays = new AbortController();
|
|
||||||
const signal = abortControllers.value.retentionDays.signal;
|
|
||||||
await retentionDaysApi('post', { days: retentionDays.value, signal });
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
// 修改失败,刷新 retentionDays
|
|
||||||
getRetentionDays();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const snapStatus = ref(false);
|
|
||||||
const { mutate: getSnapStatus, isPending: snapStatusLoading } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortControllers.value.snapStatus.abort();
|
|
||||||
abortControllers.value.snapStatus = new AbortController();
|
|
||||||
const signal = abortControllers.value.snapStatus.signal;
|
|
||||||
const status = await snapStatusApi('get', { signal });
|
|
||||||
return status;
|
|
||||||
},
|
|
||||||
onSuccess: (status) => {
|
|
||||||
snapStatus.value = status;
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { mutate: saveSnapStatus, isPending: snapStatusSaving } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortControllers.value.snapStatus.abort();
|
|
||||||
abortControllers.value.snapStatus = new AbortController();
|
|
||||||
const signal = abortControllers.value.snapStatus.signal;
|
|
||||||
await snapStatusApi('post', { doSnap: snapStatus.value, signal });
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
// 修改失败,刷新 snapStatus
|
|
||||||
getSnapStatus();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const showDebugCodeModal = ref(false);
|
const showDebugCodeModal = ref(false);
|
||||||
const debugCode = ref('');
|
const debugCode = ref('');
|
||||||
const enableDebugMode = () => {
|
const enableDebugMode = () => {
|
||||||
@@ -266,28 +192,13 @@ const onSelectDropdownOption = (key: string, option: DropdownOption) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch([offlineDev, show], ([offline, entered]) => {
|
onMounted(() => {
|
||||||
if (!offline) {
|
|
||||||
if (entered) {
|
|
||||||
getRetentionDays();
|
|
||||||
getSnapStatus();
|
|
||||||
} else {
|
|
||||||
abortControllers.value.retentionDays.abort();
|
|
||||||
abortControllers.value.snapStatus.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const onDrawerAfterEnter = () => {
|
|
||||||
getVersionInfo();
|
getVersionInfo();
|
||||||
};
|
});
|
||||||
const onDrawerAfterLeave = () => {
|
|
||||||
abortControllers.value.retentionDays.abort();
|
|
||||||
abortControllers.value.snapStatus.abort();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDrawer v-model:show="show" :width="560" :auto-focus="false" @after-enter="onDrawerAfterEnter" @after-leave="onDrawerAfterLeave">
|
<NDrawer v-model:show="show" :width="560" :auto-focus="false">
|
||||||
<NDrawerContent closable title="系统设置" :native-scrollbar="false">
|
<NDrawerContent closable title="系统设置" :native-scrollbar="false">
|
||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
<NDivider>主题</NDivider>
|
<NDivider>主题</NDivider>
|
||||||
@@ -299,29 +210,11 @@ const onDrawerAfterLeave = () => {
|
|||||||
<NFormItem label="折叠菜单" label-placement="left">
|
<NFormItem label="折叠菜单" label-placement="left">
|
||||||
<NSwitch size="small" v-model:value="menuCollpased" />
|
<NSwitch size="small" v-model:value="menuCollpased" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<template v-if="route.path === '/station'">
|
||||||
<NFormItem label="车站列数" label-placement="left">
|
<NFormItem label="车站列数" label-placement="left">
|
||||||
<NInputNumber v-model:value="stationGridCols" :min="1" :max="10" />
|
<NInputNumber v-model:value="stationGridCols" :min="1" :max="10" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
</template>
|
||||||
<NDivider>告警</NDivider>
|
|
||||||
<NFormItem label="告警画面截图保留天数" label-placement="left">
|
|
||||||
<NFlex justify="space-between" align="center" style="width: 100%">
|
|
||||||
<NInputNumber v-model:value="retentionDays" :min="1" :max="15" />
|
|
||||||
<NButtonGroup>
|
|
||||||
<NButton secondary size="small" :disabled="retentionDaysSaving" :loading="retentionDaysLoading" @click="() => getRetentionDays()">刷新</NButton>
|
|
||||||
<NButton secondary size="small" :disabled="retentionDaysLoading" :loading="retentionDaysSaving" @click="() => saveRetentionDays()">保存</NButton>
|
|
||||||
</NButtonGroup>
|
|
||||||
</NFlex>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="自动获取告警画面截图" label-placement="left">
|
|
||||||
<NFlex justify="space-between" align="center" style="width: 100%">
|
|
||||||
<NSwitch size="small" v-model:value="snapStatus" />
|
|
||||||
<NButtonGroup>
|
|
||||||
<NButton secondary size="small" :disabled="snapStatusSaving" :loading="snapStatusLoading" @click="() => getSnapStatus()">刷新</NButton>
|
|
||||||
<NButton secondary size="small" :disabled="snapStatusLoading" :loading="snapStatusSaving" @click="() => saveSnapStatus()">保存</NButton>
|
|
||||||
</NButtonGroup>
|
|
||||||
</NFlex>
|
|
||||||
</NFormItem>
|
|
||||||
|
|
||||||
<template v-if="debugModeEnabled">
|
<template v-if="debugModeEnabled">
|
||||||
<NDivider title-placement="center">调试</NDivider>
|
<NDivider title-placement="center">调试</NDivider>
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
import type { NdmDeviceAlarmLogResultVO, Station } from '@/apis';
|
import type { NdmDeviceAlarmLogResultVO, Station } from '@/apis';
|
||||||
import { ALARM_TYPES, DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, FAULT_LEVELS, tryGetDeviceType } from '@/enums';
|
import { ALARM_TYPES, DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, FAULT_LEVELS, tryGetDeviceType } from '@/enums';
|
||||||
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/helpers';
|
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/helpers';
|
||||||
import { useAlarmStore, useDeviceStore } from '@/stores';
|
import { useAlarmStore } from '@/stores';
|
||||||
import { downloadByData } from '@/utils';
|
import { downloadByData } from '@/utils';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { NButton, NDataTable, NFlex, NGrid, NGridItem, NModal, NStatistic, NTag, type DataTableBaseColumn, type DataTableRowData, type PaginationProps } from 'naive-ui';
|
import { NButton, NDataTable, NFlex, NGrid, NGridItem, NModal, NStatistic, NTag, type DataTableBaseColumn, type DataTableRowData, type PaginationProps } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, h, reactive, ref, toRefs, type CSSProperties } from 'vue';
|
import { computed, h, reactive, ref, toRefs } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
station?: Station;
|
station?: Station;
|
||||||
@@ -16,13 +15,8 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const show = defineModel<boolean>('show', { default: false });
|
const show = defineModel<boolean>('show', { default: false });
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { station } = toRefs(props);
|
const { station } = toRefs(props);
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { lineAlarms } = storeToRefs(alarmStore);
|
const { lineAlarms } = storeToRefs(alarmStore);
|
||||||
|
|
||||||
@@ -43,40 +37,7 @@ const tableColumns = ref<DataTableBaseColumn<NdmDeviceAlarmLogResultVO>[]>([
|
|||||||
{ title: '告警流水号', key: 'alarmNo' },
|
{ title: '告警流水号', key: 'alarmNo' },
|
||||||
{ title: '告警时间', key: 'alarmDate', render: renderAlarmDateCell },
|
{ title: '告警时间', key: 'alarmDate', render: renderAlarmDateCell },
|
||||||
{ title: '设备类型', key: 'deviceType', render: renderDeviceTypeCell },
|
{ title: '设备类型', key: 'deviceType', render: renderDeviceTypeCell },
|
||||||
{
|
{ title: '设备名称', key: 'deviceName' },
|
||||||
title: '设备名称',
|
|
||||||
key: 'deviceName',
|
|
||||||
render: (rowData) => {
|
|
||||||
return h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
style: { textDecoration: 'underline', cursor: 'pointer' } as CSSProperties,
|
|
||||||
onClick: () => {
|
|
||||||
const stationCode = rowData.stationCode;
|
|
||||||
if (!stationCode) return;
|
|
||||||
const deviceType = tryGetDeviceType(rowData.deviceType);
|
|
||||||
if (!deviceType) return;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return;
|
|
||||||
const classified = stationDevices[deviceType];
|
|
||||||
const device = classified.find((device) => !!device.deviceId && device.deviceId === rowData.deviceId);
|
|
||||||
if (!device) return;
|
|
||||||
const deviceDbId = device.id;
|
|
||||||
router.push({
|
|
||||||
path: '/device',
|
|
||||||
query: {
|
|
||||||
stationCode,
|
|
||||||
deviceType,
|
|
||||||
deviceDbId,
|
|
||||||
fromPage: route.path,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
`${rowData.deviceName}`,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ title: '告警类型', key: 'alarmType', align: 'center', render: renderAlarmTypeCell },
|
{ title: '告警类型', key: 'alarmType', align: 'center', render: renderAlarmTypeCell },
|
||||||
{ title: '故障级别', key: 'faultLevel', align: 'center', render: renderFaultLevelCell },
|
{ title: '故障级别', key: 'faultLevel', align: 'center', render: renderFaultLevelCell },
|
||||||
// { title: '故障编码', key: 'faultCode', align: 'center' },
|
// { title: '故障编码', key: 'faultCode', align: 'center' },
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Station } from '@/apis';
|
import type { Station } from '@/apis';
|
||||||
import { DeviceTree, type DeviceTreeProps } from '@/components';
|
import { DeviceTree } from '@/components';
|
||||||
import { tryGetDeviceType } from '@/enums';
|
|
||||||
import { NModal } from 'naive-ui';
|
import { NModal } from 'naive-ui';
|
||||||
import { toRefs } from 'vue';
|
import { toRefs } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
station?: Station;
|
station?: Station;
|
||||||
@@ -12,30 +10,13 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const show = defineModel<boolean>('show', { default: false });
|
const show = defineModel<boolean>('show', { default: false });
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { station } = toRefs(props);
|
const { station } = toRefs(props);
|
||||||
|
|
||||||
const onAfterSelectDevice: DeviceTreeProps['onAfterSelectDevice'] = (device, stationCode) => {
|
|
||||||
const deviceDbId = device.id;
|
|
||||||
const deviceType = tryGetDeviceType(device.deviceType);
|
|
||||||
router.push({
|
|
||||||
path: '/device',
|
|
||||||
query: {
|
|
||||||
stationCode,
|
|
||||||
deviceType,
|
|
||||||
deviceDbId,
|
|
||||||
fromPage: route.path,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NModal v-model:show="show" preset="card" style="width: 600px; height: 600px" :title="`${station?.name} - 设备详情`" :content-style="{ height: '100%', overflow: 'hidden' }">
|
<NModal v-model:show="show" preset="card" style="width: 600px; height: 600px" :title="`${station?.name} - 设备详情`" :content-style="{ height: '100%', overflow: 'hidden' }">
|
||||||
<template #default>
|
<template #default>
|
||||||
<DeviceTree :station="station" :events="['select', 'manage']" :device-prefix-label="'查看'" @after-select-device="onAfterSelectDevice" />
|
<DeviceTree :station="station" />
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Station, StationAlarms, StationDevices } from '@/apis';
|
|||||||
import { DEVICE_TYPE_LITERALS } from '@/enums';
|
import { DEVICE_TYPE_LITERALS } from '@/enums';
|
||||||
import { EllipsisOutlined, MoreOutlined } from '@vicons/antd';
|
import { EllipsisOutlined, MoreOutlined } from '@vicons/antd';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { isFunction } from 'es-toolkit';
|
import { isFunction } from 'es-toolkit';
|
||||||
import { NButton, NCard, NCheckbox, NDropdown, NFlex, NIcon, NTag, NTooltip, useThemeVars, type DropdownOption } from 'naive-ui';
|
import { NButton, NCard, NCheckbox, NDropdown, NFlex, NIcon, NTag, NTooltip, useThemeVars, type DropdownOption } from 'naive-ui';
|
||||||
import { computed, toRefs } from 'vue';
|
import { computed, toRefs } from 'vue';
|
||||||
@@ -50,7 +49,7 @@ const alarmCount = computed(() => {
|
|||||||
|
|
||||||
const openVideoPlatform = async () => {
|
const openVideoPlatform = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get<Record<string, string>>(`/minio/ndm/ndm-vimps.json?_t=${dayjs().unix()}`);
|
const response = await axios.get<Record<string, string>>('/minio/ndm/ndm-vimps.json');
|
||||||
const url = response.data[station.value.code];
|
const url = response.data[station.value.code];
|
||||||
if (!url) {
|
if (!url) {
|
||||||
window.$message.warning(`未找到车站编码 ${station.value.code} 对应的视频平台URL`);
|
window.$message.warning(`未找到车站编码 ${station.value.code} 对应的视频平台URL`);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { detailDeviceAlarmSnapLogApi, type NdmDeviceAlarmLogResultVO } from '@/apis';
|
import { getCameraSnapApi, type NdmDeviceAlarmLogResultVO } from '@/apis';
|
||||||
import { tryGetDeviceType, DEVICE_TYPE_LITERALS } from '@/enums';
|
import { tryGetDeviceType, DEVICE_TYPE_LITERALS } from '@/enums';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
@@ -7,9 +7,11 @@ import { h, ref, watch, type Ref } from 'vue';
|
|||||||
|
|
||||||
export const useCameraSnapColumn = (tableData: Ref<DataTableRowData[]>) => {
|
export const useCameraSnapColumn = (tableData: Ref<DataTableRowData[]>) => {
|
||||||
const { mutateAsync: getSnapByDeviceId } = useMutation({
|
const { mutateAsync: getSnapByDeviceId } = useMutation({
|
||||||
mutationFn: async (params: { id: string }) => {
|
mutationFn: async (params: { deviceAlarmLog: NdmDeviceAlarmLogResultVO }) => {
|
||||||
const { id } = params;
|
const { deviceAlarmLog } = params;
|
||||||
const snap = await detailDeviceAlarmSnapLogApi(id);
|
const { deviceId } = deviceAlarmLog;
|
||||||
|
if (!deviceId) throw new Error('设备ID不能为空');
|
||||||
|
const snap = await getCameraSnapApi(deviceId);
|
||||||
return snap;
|
return snap;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -26,15 +28,15 @@ export const useCameraSnapColumn = (tableData: Ref<DataTableRowData[]>) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cameraSnapColumn: DataTableColumn<NdmDeviceAlarmLogResultVO & { snapUrl?: string }> = {
|
const cameraSnapColumn: DataTableColumn<NdmDeviceAlarmLogResultVO & { snapUrl?: string }> = {
|
||||||
title: '告警画面截图',
|
title: '实时画面截图',
|
||||||
key: 'snapUrl',
|
key: 'snapUrl',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (rowData) => {
|
render: (rowData) => {
|
||||||
const { id, deviceType: deviceTypeCode, snapUrl } = rowData;
|
const { deviceType: deviceTypeCode, snapUrl } = rowData;
|
||||||
if (!id) return null;
|
|
||||||
const deviceType = tryGetDeviceType(deviceTypeCode);
|
const deviceType = tryGetDeviceType(deviceTypeCode);
|
||||||
if (deviceType !== DEVICE_TYPE_LITERALS.ndmCamera) return null;
|
if (deviceType !== DEVICE_TYPE_LITERALS.ndmCamera) return null;
|
||||||
if (!snapUrl) {
|
if (!snapUrl) {
|
||||||
|
const id = rowData.id ?? '';
|
||||||
return h(
|
return h(
|
||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
@@ -44,8 +46,7 @@ export const useCameraSnapColumn = (tableData: Ref<DataTableRowData[]>) => {
|
|||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
loadingMap.value[id] = true;
|
loadingMap.value[id] = true;
|
||||||
try {
|
try {
|
||||||
const snap = await getSnapByDeviceId({ id });
|
const snap = await getSnapByDeviceId({ deviceAlarmLog: rowData });
|
||||||
if (!snap.url) return;
|
|
||||||
rowData.snapUrl = snap.url;
|
rowData.snapUrl = snap.url;
|
||||||
} finally {
|
} finally {
|
||||||
loadingMap.value[id] = false;
|
loadingMap.value[id] = false;
|
||||||
@@ -59,7 +60,6 @@ export const useCameraSnapColumn = (tableData: Ref<DataTableRowData[]>) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cameraSnapColumn,
|
cameraSnapColumn,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { deleteDeviceApi, exportDeviceApi, importDeviceApi, type ImportMsg, type NdmDeviceLinkDescription, type NdmDevicePageQuery, type PageParams, type Station } from '@/apis';
|
import { deleteDeviceApi, exportDeviceApi, importDeviceApi, type ImportMsg, type NdmDevicePageQuery, type PageParams, type Station } from '@/apis';
|
||||||
import { useStationDevicesMutation } from '@/composables';
|
|
||||||
import { DEVICE_TYPE_NAMES, type DeviceType } from '@/enums';
|
import { DEVICE_TYPE_NAMES, type DeviceType } from '@/enums';
|
||||||
import { useDeviceStore, useStationStore } from '@/stores';
|
import { useDeviceStore, useStationStore } from '@/stores';
|
||||||
import { downloadByData, parseErrorFeedback } from '@/utils';
|
import { downloadByData, parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import destr from 'destr';
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { h, onBeforeUnmount } from 'vue';
|
import { h, onBeforeUnmount } from 'vue';
|
||||||
|
import { useStationDevicesMutation } from '../query';
|
||||||
|
import { isCancel } from 'axios';
|
||||||
|
|
||||||
export const useDeviceManagement = () => {
|
export const useDeviceManagement = () => {
|
||||||
const stationStore = useStationStore();
|
const stationStore = useStationStore();
|
||||||
@@ -49,8 +48,8 @@ export const useDeviceManagement = () => {
|
|||||||
downloadByData(data, `${stationName}_${deviceTypeName}列表_${time}.xlsx`);
|
downloadByData(data, `${stationName}_${deviceTypeName}列表_${time}.xlsx`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
if (isCancel(error)) return;
|
||||||
|
window.$loadingBar.error();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -85,8 +84,8 @@ export const useDeviceManagement = () => {
|
|||||||
downloadByData(data, `${stationName}_${deviceTypeName}导入模板_${time}.xlsx`);
|
downloadByData(data, `${stationName}_${deviceTypeName}导入模板_${time}.xlsx`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
if (isCancel(error)) return;
|
||||||
|
window.$loadingBar.error();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -138,8 +137,8 @@ export const useDeviceManagement = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
if (isCancel(error)) return;
|
||||||
|
window.$loadingBar.error();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -153,32 +152,6 @@ export const useDeviceManagement = () => {
|
|||||||
|
|
||||||
window.$loadingBar.start();
|
window.$loadingBar.start();
|
||||||
|
|
||||||
// 检查要删除的设备是否存在关联设备
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!!stationDevices) {
|
|
||||||
const classified = stationDevices[deviceType];
|
|
||||||
if (!!classified) {
|
|
||||||
const device = classified.find((device) => device.id === id);
|
|
||||||
if (!!device) {
|
|
||||||
const maybeLinkDescription = destr<any>(device.linkDescription);
|
|
||||||
if (!!maybeLinkDescription && typeof maybeLinkDescription === 'object') {
|
|
||||||
const linkDescription = maybeLinkDescription as NdmDeviceLinkDescription;
|
|
||||||
// 只要有上游或下游设备,就不能删除
|
|
||||||
const { upstream } = linkDescription;
|
|
||||||
if (!!upstream && upstream.length > 0) {
|
|
||||||
throw new Error('该设备存在关联的上游设备,无法删除');
|
|
||||||
}
|
|
||||||
if ('downstream' in linkDescription) {
|
|
||||||
const { downstream } = linkDescription;
|
|
||||||
if (!!downstream && Object.keys(downstream).length > 0) {
|
|
||||||
throw new Error('该设备存在关联的下游设备,无法删除');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await deleteDeviceApi(deviceType, id, { stationCode, signal });
|
return await deleteDeviceApi(deviceType, id, { stationCode, signal });
|
||||||
},
|
},
|
||||||
onSuccess: (_, { stationCode, signal }) => {
|
onSuccess: (_, { stationCode, signal }) => {
|
||||||
@@ -190,8 +163,8 @@ export const useDeviceManagement = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
window.$loadingBar.error();
|
|
||||||
if (isCancel(error)) return;
|
if (isCancel(error)) return;
|
||||||
|
window.$loadingBar.error();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -209,5 +182,3 @@ export const useDeviceManagement = () => {
|
|||||||
deleteDevice,
|
deleteDevice,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UseDeviceManagementReturn = ReturnType<typeof useDeviceManagement>;
|
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import type { LineDevices, NdmDeviceResultVO, Station } from '@/apis';
|
import type { LineDevices, NdmDeviceResultVO } from '@/apis';
|
||||||
import { tryGetDeviceType, type DeviceType } from '@/enums';
|
import { tryGetDeviceType, type DeviceType } from '@/enums';
|
||||||
import { useDeviceStore } from '@/stores';
|
import { useDeviceStore } from '@/stores';
|
||||||
import { watchDebounced } from '@vueuse/core';
|
import { watchDebounced } from '@vueuse/core';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { onMounted, ref, toValue, watch, type MaybeRefOrGetter } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<boolean> }) => {
|
export const useDeviceSelection = () => {
|
||||||
const { syncRoute } = options ?? {};
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const deviceStore = useDeviceStore();
|
const deviceStore = useDeviceStore();
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
const { lineDevices } = storeToRefs(deviceStore);
|
||||||
|
|
||||||
const selectedStationCode = ref<Station['code']>();
|
const selectedStationCode = ref<string>();
|
||||||
const selectedDeviceType = ref<DeviceType>();
|
const selectedDeviceType = ref<DeviceType>();
|
||||||
const selectedDevice = ref<NdmDeviceResultVO>();
|
const selectedDevice = ref<NdmDeviceResultVO>();
|
||||||
|
|
||||||
|
const hasFromPage = computed(() => !!route.query['fromPage']);
|
||||||
|
|
||||||
const initFromRoute = (lineDevices: LineDevices) => {
|
const initFromRoute = (lineDevices: LineDevices) => {
|
||||||
const { stationCode, deviceType, deviceDbId } = route.query;
|
const { stationCode, deviceType, deviceDbId } = route.query;
|
||||||
if (stationCode) {
|
if (stationCode) {
|
||||||
selectedStationCode.value = stationCode as Station['code'];
|
selectedStationCode.value = stationCode as string;
|
||||||
}
|
}
|
||||||
if (deviceType) {
|
if (deviceType) {
|
||||||
selectedDeviceType.value = deviceType as DeviceType;
|
selectedDeviceType.value = deviceType as DeviceType;
|
||||||
@@ -42,7 +42,7 @@ export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<bool
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectDevice = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
|
const selectDevice = (device: NdmDeviceResultVO, stationCode: string) => {
|
||||||
selectedDevice.value = device;
|
selectedDevice.value = device;
|
||||||
selectedStationCode.value = stationCode;
|
selectedStationCode.value = stationCode;
|
||||||
const deviceType = tryGetDeviceType(device.deviceType);
|
const deviceType = tryGetDeviceType(device.deviceType);
|
||||||
@@ -51,6 +51,20 @@ export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<bool
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const routeDevice = (device: NdmDeviceResultVO, stationCode: string, to: { path: string }) => {
|
||||||
|
const deviceDbId = device.id;
|
||||||
|
const deviceType = tryGetDeviceType(device.deviceType);
|
||||||
|
router.push({
|
||||||
|
path: to.path,
|
||||||
|
query: {
|
||||||
|
stationCode,
|
||||||
|
deviceType,
|
||||||
|
deviceDbId,
|
||||||
|
fromPage: route.path,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const syncToRoute = () => {
|
const syncToRoute = () => {
|
||||||
const query = { ...route.query };
|
const query = { ...route.query };
|
||||||
// 当选中的设备发生变化时,删除fromPage参数
|
// 当选中的设备发生变化时,删除fromPage参数
|
||||||
@@ -69,20 +83,14 @@ export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<bool
|
|||||||
router.replace({ query });
|
router.replace({ query });
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(selectedDevice, () => {
|
watch(selectedDevice, syncToRoute);
|
||||||
if (toValue(syncRoute)) {
|
|
||||||
syncToRoute();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// lineDevices是shallowRef,因此需要深度侦听才能获取内部变化,
|
// lineDevices是shallowRef,因此需要深度侦听才能获取内部变化,
|
||||||
// 而单纯的深度侦听又可能会引发性能问题,因此尝试使用防抖侦听
|
// 而单纯的深度侦听又可能会引发性能问题,因此尝试使用防抖侦听
|
||||||
watchDebounced(
|
watchDebounced(
|
||||||
lineDevices,
|
lineDevices,
|
||||||
(newLineDevices) => {
|
(newLineDevices) => {
|
||||||
if (toValue(syncRoute)) {
|
|
||||||
initFromRoute(newLineDevices);
|
initFromRoute(newLineDevices);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
debounce: 500,
|
debounce: 500,
|
||||||
@@ -91,9 +99,7 @@ export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<bool
|
|||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (toValue(syncRoute)) {
|
|
||||||
initFromRoute(lineDevices.value);
|
initFromRoute(lineDevices.value);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -101,9 +107,10 @@ export const useDeviceSelection = (options?: { syncRoute?: MaybeRefOrGetter<bool
|
|||||||
selectedDeviceType,
|
selectedDeviceType,
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
|
|
||||||
|
hasFromPage,
|
||||||
|
|
||||||
initFromRoute,
|
initFromRoute,
|
||||||
selectDevice,
|
selectDevice,
|
||||||
|
routeDevice,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UseDeviceSelectionReturn = ReturnType<typeof useDeviceSelection>;
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import type { MaybeRefOrGetter } from 'vue';
|
|
||||||
import { useDeviceManagement } from './use-device-management';
|
import { useDeviceManagement } from './use-device-management';
|
||||||
import { useDeviceSelection } from './use-device-selection';
|
import { useDeviceSelection } from './use-device-selection';
|
||||||
|
|
||||||
export const useDeviceTree = (options?: { syncRoute?: MaybeRefOrGetter<boolean> }) => {
|
export const useDeviceTree = () => {
|
||||||
const { syncRoute } = options ?? {};
|
const deviceSelection = useDeviceSelection();
|
||||||
|
|
||||||
const deviceSelection = useDeviceSelection({ syncRoute });
|
|
||||||
const deviceManagement = useDeviceManagement();
|
const deviceManagement = useDeviceManagement();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -13,5 +10,3 @@ export const useDeviceTree = (options?: { syncRoute?: MaybeRefOrGetter<boolean>
|
|||||||
...deviceManagement,
|
...deviceManagement,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UseDeviceTreeReturn = ReturnType<typeof useDeviceTree>;
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './injection';
|
|
||||||
export * from './java';
|
export * from './java';
|
||||||
export * from './mutation';
|
export * from './mutation';
|
||||||
export * from './query';
|
export * from './query';
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { UseDeviceTreeReturn } from '@/composables';
|
|
||||||
import { createInjectionKey } from '@/utils';
|
|
||||||
import { type Ref } from 'vue';
|
|
||||||
|
|
||||||
export const SELECT_DEVICE_FN_INJECTION_KEY = createInjectionKey<Ref<UseDeviceTreeReturn['selectDevice'] | undefined>>('select-device-fn');
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SettingsDrawer, SyncCameraResultModal } from '@/components';
|
import { SettingsDrawer, SyncCameraResultModal } from '@/components';
|
||||||
import { useLineStationsQuery, useStompClient, useVerifyUserQuery } from '@/composables';
|
import { useLineStationsQuery, useStompClient, useVerifyUserQuery } from '@/composables';
|
||||||
import { LINE_ALARMS_QUERY_KEY, LINE_DEVICES_QUERY_KEY, LINE_STATIONS_MUTATION_KEY, LINE_STATIONS_QUERY_KEY, STATION_ALARMS_MUTATION_KEY, STATION_DEVICES_MUTATION_KEY } from '@/constants';
|
|
||||||
import { useAlarmStore, useSettingStore, useUserStore } from '@/stores';
|
import { useAlarmStore, useSettingStore, useUserStore } from '@/stores';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
import { parseErrorFeedback } from '@/utils';
|
||||||
import { useIsFetching, useIsMutating, useMutation } from '@tanstack/vue-query';
|
import { useIsFetching, useIsMutating, useMutation } from '@tanstack/vue-query';
|
||||||
@@ -44,22 +43,10 @@ const { syncCameraResult, afterCheckSyncCameraResult } = useStompClient();
|
|||||||
useVerifyUserQuery();
|
useVerifyUserQuery();
|
||||||
useLineStationsQuery();
|
useLineStationsQuery();
|
||||||
|
|
||||||
// 全局loading状态依赖于轮询query的queryKey以及相关的mutationKey
|
// 带key的query和mutation用于全局轮询 可用于渲染loading状态
|
||||||
const queryingCount = useIsFetching({
|
const fetchingCount = useIsFetching({ predicate: (query) => !!query.options.queryKey });
|
||||||
predicate: (query) => {
|
const mutatingCount = useIsMutating({ predicate: (mutation) => !!mutation.options.mutationKey });
|
||||||
const pollingKeys = [LINE_STATIONS_QUERY_KEY, LINE_DEVICES_QUERY_KEY, LINE_ALARMS_QUERY_KEY];
|
const loadingCount = computed(() => fetchingCount.value + mutatingCount.value);
|
||||||
const queryKey = query.options.queryKey;
|
|
||||||
return !!queryKey && Array.isArray(queryKey) && pollingKeys.some((key) => queryKey.includes(key));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const mutatingCount = useIsMutating({
|
|
||||||
predicate: (mutation) => {
|
|
||||||
const mutationKeys = [LINE_STATIONS_MUTATION_KEY, STATION_DEVICES_MUTATION_KEY, STATION_ALARMS_MUTATION_KEY];
|
|
||||||
const mutationKey = mutation.options.mutationKey;
|
|
||||||
return !!mutationKey && Array.isArray(mutationKey) && mutationKeys.some((key) => mutationKey.includes(key));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const appLoading = computed(() => queryingCount.value + mutatingCount.value > 0);
|
|
||||||
|
|
||||||
const onToggleMenuCollapsed = () => {
|
const onToggleMenuCollapsed = () => {
|
||||||
menuCollpased.value = !menuCollpased.value;
|
menuCollpased.value = !menuCollpased.value;
|
||||||
@@ -77,19 +64,9 @@ const menuOptions: MenuOption[] = [
|
|||||||
icon: renderIcon(HddFilled),
|
icon: renderIcon(HddFilled),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '设备告警',
|
label: () => h(RouterLink, { to: '/alarm' }, { default: () => '设备告警' }),
|
||||||
key: '/alarm',
|
key: '/alarm',
|
||||||
icon: renderIcon(AlertFilled),
|
icon: renderIcon(AlertFilled),
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: () => h(RouterLink, { to: '/alarm/alarm-log' }, { default: () => '设备告警记录' }),
|
|
||||||
key: '/alarm/alarm-log',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: () => h(RouterLink, { to: '/alarm/alarm-ignore' }, { default: () => '告警忽略管理' }),
|
|
||||||
key: '/alarm/alarm-ignore',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '系统日志',
|
label: '系统日志',
|
||||||
@@ -142,8 +119,8 @@ const routeToRoot = () => {
|
|||||||
|
|
||||||
const routeToAlarmPage = () => {
|
const routeToAlarmPage = () => {
|
||||||
alarmStore.clearUnreadAlarms();
|
alarmStore.clearUnreadAlarms();
|
||||||
if (route.path !== '/alarm/alarm-log') {
|
if (route.path !== '/alarm') {
|
||||||
router.push({ path: '/alarm/alarm-log' });
|
router.push({ path: '/alarm' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -192,7 +169,7 @@ function renderIcon(icon: Component): () => VNode {
|
|||||||
<NFlex justify="space-between" align="center" :size="8" style="width: 100%; height: 100%">
|
<NFlex justify="space-between" align="center" :size="8" style="width: 100%; height: 100%">
|
||||||
<NFlex align="center">
|
<NFlex align="center">
|
||||||
<h3 style="margin: 0 0 0 16px; cursor: pointer" @click="routeToRoot">网络设备管理平台</h3>
|
<h3 style="margin: 0 0 0 16px; cursor: pointer" @click="routeToRoot">网络设备管理平台</h3>
|
||||||
<NButton text size="tiny" :loading="appLoading"></NButton>
|
<NButton text size="tiny" :loading="loadingCount > 0"></NButton>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
<NFlex align="center" :size="0" style="height: 100%">
|
<NFlex align="center" :size="0" style="height: 100%">
|
||||||
<NDropdown trigger="hover" show-arrow :options="dropdownOptions" @select="onSelectDropdownOption">
|
<NDropdown trigger="hover" show-arrow :options="dropdownOptions" @select="onSelectDropdownOption">
|
||||||
|
|||||||
@@ -1,314 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
const NDM_TYPES: Record<string, DeviceType> = {
|
|
||||||
'01': DEVICE_TYPE_LITERALS.ndmAlarmHost,
|
|
||||||
// '02': '报警源',
|
|
||||||
'03': DEVICE_TYPE_LITERALS.ndmSecurityBox,
|
|
||||||
'04': DEVICE_TYPE_LITERALS.ndmSwitch,
|
|
||||||
'05': DEVICE_TYPE_LITERALS.ndmNvr,
|
|
||||||
'06': DEVICE_TYPE_LITERALS.ndmCamera,
|
|
||||||
'07': DEVICE_TYPE_LITERALS.ndmDecoder,
|
|
||||||
'08': DEVICE_TYPE_LITERALS.ndmKeyboard,
|
|
||||||
'09': DEVICE_TYPE_LITERALS.ndmMediaServer,
|
|
||||||
// '10': '显示器',
|
|
||||||
'11': DEVICE_TYPE_LITERALS.ndmVideoServer,
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { deleteCameraIgnoreApi, pageCameraIgnoreApi, type NdmCameraIgnore, type NdmCameraIgnoreResultVO, type PageQueryExtra, type Station } from '@/apis';
|
|
||||||
import { DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, type DeviceType } from '@/enums';
|
|
||||||
import { useDeviceStore, useStationStore } from '@/stores';
|
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import {
|
|
||||||
NButton,
|
|
||||||
NDataTable,
|
|
||||||
NDatePicker,
|
|
||||||
NFlex,
|
|
||||||
NForm,
|
|
||||||
NFormItemGi,
|
|
||||||
NGrid,
|
|
||||||
NGridItem,
|
|
||||||
NPopconfirm,
|
|
||||||
NSelect,
|
|
||||||
type DataTableColumns,
|
|
||||||
type DataTableRowData,
|
|
||||||
type PaginationProps,
|
|
||||||
type SelectOption,
|
|
||||||
} from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, h, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
interface SearchFields extends PageQueryExtra<NdmCameraIgnore> {
|
|
||||||
createdTime?: [string, string];
|
|
||||||
updatedTime?: [string, string];
|
|
||||||
stationCode?: Station['code'];
|
|
||||||
// deviceId_like?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stationStore = useStationStore();
|
|
||||||
const { stations } = storeToRefs(stationStore);
|
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
|
|
||||||
const stationSelectOptions = computed<SelectOption[]>(() => {
|
|
||||||
return stations.value.map((station) => ({
|
|
||||||
label: station.name,
|
|
||||||
value: station.code,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
// const deviceTypeSelectOptions = computed<SelectOption[]>(() => {
|
|
||||||
// return Object.values(DEVICE_TYPE_LITERALS).map<SelectOption>((deviceType) => ({
|
|
||||||
// label: DEVICE_TYPE_NAMES[deviceType],
|
|
||||||
// value: deviceType,
|
|
||||||
// }));
|
|
||||||
// });
|
|
||||||
|
|
||||||
const searchFields = ref<SearchFields>({});
|
|
||||||
const resetSearchFields = () => {
|
|
||||||
searchFields.value = {};
|
|
||||||
};
|
|
||||||
const getExtraFields = (): PageQueryExtra<NdmCameraIgnore> => {
|
|
||||||
const [createdTime_precisest, createdTime_preciseed] = searchFields.value.createdTime ?? [];
|
|
||||||
const stationCode = searchFields.value.stationCode;
|
|
||||||
|
|
||||||
return {
|
|
||||||
createdTime_precisest,
|
|
||||||
createdTime_preciseed,
|
|
||||||
deviceId_like: stationCode?.slice(0, 4),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchFieldsChanged = ref(false);
|
|
||||||
watch(searchFields, () => {
|
|
||||||
searchFieldsChanged.value = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableColumns: DataTableColumns<NdmCameraIgnoreResultVO> = [
|
|
||||||
{ title: '忽略时间', key: 'createdTime', align: 'center' },
|
|
||||||
// { title: '更新时间', key: 'updatedTime' },
|
|
||||||
{
|
|
||||||
title: '车站',
|
|
||||||
key: 'stationCode',
|
|
||||||
align: 'center',
|
|
||||||
render: (rowData) => {
|
|
||||||
const stationCode = rowData.deviceId?.slice(0, 4);
|
|
||||||
if (!stationCode) return '';
|
|
||||||
const station = stations.value.find((station) => station.code === stationCode);
|
|
||||||
if (!station) return '';
|
|
||||||
return station.name;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// { title: '设备ID', key: 'deviceId', align: 'center' },
|
|
||||||
// { title: '忽略类型', key: 'ignoreType', align: 'center' },
|
|
||||||
{
|
|
||||||
title: '设备类型',
|
|
||||||
key: 'deviceType',
|
|
||||||
align: 'center',
|
|
||||||
render: (rowData) => {
|
|
||||||
const UNKNOWN_NAME = '-';
|
|
||||||
const ndmType = rowData.deviceId?.slice(4, 6);
|
|
||||||
if (!ndmType) return UNKNOWN_NAME;
|
|
||||||
const deviceType = NDM_TYPES[ndmType];
|
|
||||||
if (!deviceType) return UNKNOWN_NAME;
|
|
||||||
return DEVICE_TYPE_NAMES[deviceType];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '设备名称',
|
|
||||||
key: 'deviceName',
|
|
||||||
align: 'center',
|
|
||||||
render: (rowData) => {
|
|
||||||
const UNKNOWN_NAME = '-';
|
|
||||||
const { deviceId } = rowData;
|
|
||||||
if (!deviceId) return UNKNOWN_NAME;
|
|
||||||
const stationCode = deviceId.slice(0, 4);
|
|
||||||
if (!stationCode) return UNKNOWN_NAME;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return UNKNOWN_NAME;
|
|
||||||
const ndmType = rowData.deviceId?.slice(4, 6);
|
|
||||||
if (!ndmType) return UNKNOWN_NAME;
|
|
||||||
const deviceType = NDM_TYPES[ndmType];
|
|
||||||
if (!deviceType) return UNKNOWN_NAME;
|
|
||||||
const classified = stationDevices[deviceType];
|
|
||||||
const device = classified.find((device) => device.deviceId === deviceId);
|
|
||||||
if (!device) return UNKNOWN_NAME;
|
|
||||||
return device.name ?? UNKNOWN_NAME;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'action',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: (rowData) => {
|
|
||||||
return h(
|
|
||||||
NPopconfirm,
|
|
||||||
{
|
|
||||||
onPositiveClick: () => {
|
|
||||||
console.log(rowData);
|
|
||||||
const { id } = rowData;
|
|
||||||
cancelIgnore({ id });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
trigger: () => {
|
|
||||||
return h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
secondary: true,
|
|
||||||
},
|
|
||||||
{ default: () => `取消忽略` },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
default: () => `确认取消忽略吗?`,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const { mutate: cancelIgnore } = useMutation({
|
|
||||||
mutationFn: async (params: { id?: string; signal?: AbortSignal }) => {
|
|
||||||
const { id, signal } = params;
|
|
||||||
if (!id) return;
|
|
||||||
return deleteCameraIgnoreApi([id], { signal });
|
|
||||||
},
|
|
||||||
onSuccess: (data) => {
|
|
||||||
if (!data) {
|
|
||||||
window.$message.error('取消忽略失败,请再试一次');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.$message.success('取消忽略成功');
|
|
||||||
getTableData();
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
window.$message.error(error.message);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableData = ref<DataTableRowData[]>([]);
|
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 10;
|
|
||||||
const pagination = reactive<PaginationProps>({
|
|
||||||
showSizePicker: true,
|
|
||||||
page: 1,
|
|
||||||
pageSize: DEFAULT_PAGE_SIZE,
|
|
||||||
pageSizes: [5, 10, 20, 50, 80, 100],
|
|
||||||
itemCount: 0,
|
|
||||||
prefix: ({ itemCount }) => {
|
|
||||||
return h('div', {}, { default: () => `共${itemCount}条` });
|
|
||||||
},
|
|
||||||
onUpdatePage: (page: number) => {
|
|
||||||
pagination.page = page;
|
|
||||||
getTableData();
|
|
||||||
},
|
|
||||||
onUpdatePageSize: (pageSize: number) => {
|
|
||||||
pagination.pageSize = pageSize;
|
|
||||||
pagination.page = 1;
|
|
||||||
getTableData();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const abortController = ref(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const res = await pageCameraIgnoreApi(
|
|
||||||
{
|
|
||||||
model: {},
|
|
||||||
extra: getExtraFields(),
|
|
||||||
current: pagination.page ?? 1,
|
|
||||||
size: pagination.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
||||||
sort: 'id',
|
|
||||||
order: 'descending',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
onSuccess: (res) => {
|
|
||||||
const { records, size, total } = res;
|
|
||||||
pagination.pageSize = parseInt(size);
|
|
||||||
pagination.itemCount = parseInt(total);
|
|
||||||
tableData.value = records;
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
|
||||||
window.$message.error(error.message);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onClickReset = () => {
|
|
||||||
resetSearchFields();
|
|
||||||
pagination.page = 1;
|
|
||||||
pagination.pageSize = DEFAULT_PAGE_SIZE;
|
|
||||||
pagination.itemCount = 0;
|
|
||||||
getTableData();
|
|
||||||
};
|
|
||||||
const onClickQuery = () => {
|
|
||||||
if (searchFieldsChanged.value) {
|
|
||||||
pagination.page = 1;
|
|
||||||
pagination.pageSize = DEFAULT_PAGE_SIZE;
|
|
||||||
searchFieldsChanged.value = false;
|
|
||||||
}
|
|
||||||
getTableData();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getTableData();
|
|
||||||
});
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
abortController.value.abort();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NFlex vertical :size="0" style="height: 100%">
|
|
||||||
<!-- 查询面板 -->
|
|
||||||
<NForm style="flex: 0 0 auto; padding: 8px">
|
|
||||||
<NGrid cols="3" :x-gap="24">
|
|
||||||
<NFormItemGi span="1" label="车站" label-placement="left">
|
|
||||||
<NSelect clearable placeholder="请选择车站" v-model:value="searchFields.stationCode" :options="stationSelectOptions" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="2" label="忽略时间" label-placement="left">
|
|
||||||
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<NGrid :cols="1">
|
|
||||||
<NGridItem>
|
|
||||||
<NFlex>
|
|
||||||
<NButton @click="onClickReset">重置</NButton>
|
|
||||||
<NButton type="primary" :loading="tableLoading" @click="onClickQuery">查询</NButton>
|
|
||||||
</NFlex>
|
|
||||||
</NGridItem>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
|
|
||||||
<!-- 数据表格工具栏 -->
|
|
||||||
<NFlex align="center" style="padding: 8px; flex: 0 0 auto">
|
|
||||||
<div style="font-size: medium">告警忽略列表</div>
|
|
||||||
<NFlex style="margin-left: auto">
|
|
||||||
<!-- <NButton type="primary" :loading="exporting" @click="() => exportTableData()">导出</NButton> -->
|
|
||||||
</NFlex>
|
|
||||||
</NFlex>
|
|
||||||
|
|
||||||
<!-- 数据表格 -->
|
|
||||||
<NDataTable remote :columns="tableColumns" :data="tableData" :pagination="pagination" :loading="tableLoading" :single-line="false" flex-height style="height: 100%; padding: 8px; flex: 1 1 auto" />
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { exportDeviceAlarmLogApi, pageDeviceAlarmLogApi, type NdmDeviceAlarmLog, type NdmDeviceAlarmLogResultVO, type PageQueryExtra, type Station } from '@/apis';
|
import { exportDeviceAlarmLogApi, pageDeviceAlarmLogApi, type NdmDeviceAlarmLog, type NdmDeviceAlarmLogResultVO, type PageQueryExtra, type Station } from '@/apis';
|
||||||
import { useAlarmActionColumn, useCameraSnapColumn } from '@/composables';
|
import { useAlarmActionColumn, useCameraSnapColumn } from '@/composables';
|
||||||
import { ALARM_TYPES, DEVICE_TYPE_CODES, DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, FAULT_LEVELS, tryGetDeviceType, type DeviceType } from '@/enums';
|
import { ALARM_TYPES, DEVICE_TYPE_CODES, DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, FAULT_LEVELS, type DeviceType } from '@/enums';
|
||||||
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/helpers';
|
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/helpers';
|
||||||
import { useAlarmStore, useDeviceStore, useStationStore } from '@/stores';
|
import { useAlarmStore, useStationStore } from '@/stores';
|
||||||
import { downloadByData, parseErrorFeedback } from '@/utils';
|
import { downloadByData, parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { watchDebounced } from '@vueuse/core';
|
import { watchDebounced } from '@vueuse/core';
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {
|
import {
|
||||||
NButton,
|
NButton,
|
||||||
@@ -27,8 +26,7 @@ import {
|
|||||||
type SelectOption,
|
type SelectOption,
|
||||||
} from 'naive-ui';
|
} from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, h, onBeforeMount, onBeforeUnmount, reactive, ref, watch, type CSSProperties } from 'vue';
|
import { computed, h, onBeforeMount, reactive, ref, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
|
||||||
|
|
||||||
interface SearchFields extends PageQueryExtra<NdmDeviceAlarmLog> {
|
interface SearchFields extends PageQueryExtra<NdmDeviceAlarmLog> {
|
||||||
stationCode_in: Station['code'][];
|
stationCode_in: Station['code'][];
|
||||||
@@ -39,13 +37,8 @@ interface SearchFields extends PageQueryExtra<NdmDeviceAlarmLog> {
|
|||||||
alarmDate: [number, number];
|
alarmDate: [number, number];
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const stationStore = useStationStore();
|
const stationStore = useStationStore();
|
||||||
const { stations } = storeToRefs(stationStore);
|
const { stations } = storeToRefs(stationStore);
|
||||||
const deviceStore = useDeviceStore();
|
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { unreadAlarmCount } = storeToRefs(alarmStore);
|
const { unreadAlarmCount } = storeToRefs(alarmStore);
|
||||||
|
|
||||||
@@ -139,7 +132,7 @@ const tableData = ref<DataTableRowData[]>([]);
|
|||||||
|
|
||||||
const { cameraSnapColumn } = useCameraSnapColumn(tableData);
|
const { cameraSnapColumn } = useCameraSnapColumn(tableData);
|
||||||
const { alarmActionColumn } = useAlarmActionColumn(tableData);
|
const { alarmActionColumn } = useAlarmActionColumn(tableData);
|
||||||
const tableColumns: DataTableColumns<NdmDeviceAlarmLogResultVO> = [
|
const tableColumns: DataTableColumns<NdmDeviceAlarmLogResultVO & { snapUrl?: string }> = [
|
||||||
// { title: '设备ID', key: 'deviceId' },
|
// { title: '设备ID', key: 'deviceId' },
|
||||||
// { title: '故障编码', key: 'faultCode', align: 'center' },
|
// { title: '故障编码', key: 'faultCode', align: 'center' },
|
||||||
// { title: '故障位置', key: 'faultLocation' },
|
// { title: '故障位置', key: 'faultLocation' },
|
||||||
@@ -147,40 +140,7 @@ const tableColumns: DataTableColumns<NdmDeviceAlarmLogResultVO> = [
|
|||||||
{ title: '告警时间', key: 'alarmDate', render: renderAlarmDateCell },
|
{ title: '告警时间', key: 'alarmDate', render: renderAlarmDateCell },
|
||||||
{ title: '车站', key: 'stationName', render: (rowData) => stations.value.find((station) => station.code === rowData.stationCode)?.name ?? '-' },
|
{ title: '车站', key: 'stationName', render: (rowData) => stations.value.find((station) => station.code === rowData.stationCode)?.name ?? '-' },
|
||||||
{ title: '设备类型', key: 'deviceType', render: renderDeviceTypeCell },
|
{ title: '设备类型', key: 'deviceType', render: renderDeviceTypeCell },
|
||||||
{
|
{ title: '设备名称', key: 'deviceName' },
|
||||||
title: '设备名称',
|
|
||||||
key: 'deviceName',
|
|
||||||
render: (rowData) => {
|
|
||||||
return h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
style: { textDecoration: 'underline', cursor: 'pointer' } as CSSProperties,
|
|
||||||
onClick: () => {
|
|
||||||
const stationCode = rowData.stationCode;
|
|
||||||
if (!stationCode) return;
|
|
||||||
const deviceType = tryGetDeviceType(rowData.deviceType);
|
|
||||||
if (!deviceType) return;
|
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
|
||||||
if (!stationDevices) return;
|
|
||||||
const classified = stationDevices[deviceType];
|
|
||||||
const device = classified.find((device) => !!device.deviceId && device.deviceId === rowData.deviceId);
|
|
||||||
if (!device) return;
|
|
||||||
const deviceDbId = device.id;
|
|
||||||
router.push({
|
|
||||||
path: '/device',
|
|
||||||
query: {
|
|
||||||
stationCode,
|
|
||||||
deviceType,
|
|
||||||
deviceDbId,
|
|
||||||
fromPage: route.path,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
`${rowData.deviceName}`,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ title: '告警类型', key: 'alarmType', align: 'center', render: renderAlarmTypeCell },
|
{ title: '告警类型', key: 'alarmType', align: 'center', render: renderAlarmTypeCell },
|
||||||
{ title: '故障级别', key: 'faultLevel', align: 'center', render: renderFaultLevelCell },
|
{ title: '故障级别', key: 'faultLevel', align: 'center', render: renderFaultLevelCell },
|
||||||
{ title: '故障描述', key: 'faultDescription' },
|
{ title: '故障描述', key: 'faultDescription' },
|
||||||
@@ -212,29 +172,16 @@ const pagination = reactive<PaginationProps>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const abortController = ref(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
const res = await pageDeviceAlarmLogApi({
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const res = await pageDeviceAlarmLogApi(
|
|
||||||
{
|
|
||||||
model: {},
|
model: {},
|
||||||
extra: getExtraFields(),
|
extra: getExtraFields(),
|
||||||
current: pagination.page ?? 1,
|
current: pagination.page ?? 1,
|
||||||
size: pagination.pageSize ?? DEFAULT_PAGE_SIZE,
|
size: pagination.pageSize ?? DEFAULT_PAGE_SIZE,
|
||||||
sort: 'id',
|
sort: 'id',
|
||||||
order: 'descending',
|
order: 'descending',
|
||||||
},
|
});
|
||||||
{
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
@@ -244,7 +191,6 @@ const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|||||||
tableData.value = records;
|
tableData.value = records;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -271,24 +217,14 @@ const onClickQuery = () => {
|
|||||||
|
|
||||||
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
const data = await exportDeviceAlarmLogApi({
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const data = await exportDeviceAlarmLogApi(
|
|
||||||
{
|
|
||||||
model: {},
|
model: {},
|
||||||
extra: getExtraFields(),
|
extra: getExtraFields(),
|
||||||
current: pagination.page ?? 1,
|
current: pagination.page ?? 1,
|
||||||
size: pagination.pageSize ?? 10,
|
size: pagination.pageSize ?? 10,
|
||||||
order: 'descending',
|
order: 'descending',
|
||||||
sort: 'id',
|
sort: 'id',
|
||||||
},
|
});
|
||||||
{
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@@ -296,7 +232,6 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
downloadByData(data, `设备告警记录_${time}.xlsx`);
|
downloadByData(data, `设备告警记录_${time}.xlsx`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -306,9 +241,6 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getTableData();
|
getTableData();
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
|
||||||
abortController.value.abort();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -348,7 +280,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<!-- 数据表格工具栏 -->
|
<!-- 数据表格工具栏 -->
|
||||||
<NFlex align="center" style="padding: 8px; flex: 0 0 auto">
|
<NFlex align="center" style="padding: 8px; flex: 0 0 auto">
|
||||||
<div style="font-size: medium">设备告警记录</div>
|
<div style="font-size: medium">视频平台日志</div>
|
||||||
<NFlex align="center" style="margin-left: auto">
|
<NFlex align="center" style="margin-left: auto">
|
||||||
<div>实时刷新</div>
|
<div>实时刷新</div>
|
||||||
<NSwitch size="small" v-model:value="realtimeRefresh" />
|
<NSwitch size="small" v-model:value="realtimeRefresh" />
|
||||||
@@ -1,41 +1,8 @@
|
|||||||
<script lang="ts">
|
|
||||||
const callLogTypeOptions: SelectOption[] = [
|
|
||||||
{ label: '视频点播', value: 10001 },
|
|
||||||
{ label: '视频回放', value: 10002 },
|
|
||||||
// { label: '停止视频回放', value: 10003 },
|
|
||||||
// { label: '回放暂停', value: 10004 },
|
|
||||||
// { label: '回放恢复', value: 10005 },
|
|
||||||
// { label: '回放倍速播放', value: 10006 },
|
|
||||||
// { label: '回放拖动播放', value: 10007 },
|
|
||||||
{ label: '云台指令', value: 10008 },
|
|
||||||
{ label: '查询录像', value: 10009 },
|
|
||||||
{ label: '下载录像', value: 10010 },
|
|
||||||
// { label: '停止下载录像', value: 10011 },
|
|
||||||
{ label: '获取预置位', value: 10012 },
|
|
||||||
{ label: '获取设备信息', value: 10013 },
|
|
||||||
{ label: '获取设备状态', value: 10014 },
|
|
||||||
{ label: '设置解码器分屏数量', value: 20001 },
|
|
||||||
{ label: '设置解码器rtsp流', value: 20002 },
|
|
||||||
{ label: '移除解码器rtsp流', value: 20003 },
|
|
||||||
// { label: '启动非报警时序', value: 30001 },
|
|
||||||
// { label: '停止非报警时序', value: 30002 },
|
|
||||||
// { label: '暂停非报警时序', value: 30003 },
|
|
||||||
// { label: '调用组切', value: 30004 },
|
|
||||||
// { label: '启动报警时序', value: 40001 },
|
|
||||||
// { label: '停止报警时序', value: 40002 },
|
|
||||||
// { label: '分页查询报警', value: 50001 },
|
|
||||||
// { label: '确认报警', value: 50002 },
|
|
||||||
// { label: '删除报警', value: 50004 },
|
|
||||||
{ label: '目录查询', value: 60001 },
|
|
||||||
];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { exportCallLogApi, pageCallLogApi, type NdmCallLog, type NdmCallLogResultVO, type PageQueryExtra, type Station } from '@/apis';
|
import { exportCallLogApi, pageCallLogApi, type NdmCallLog, type NdmCallLogResultVO, type PageQueryExtra, type Station } from '@/apis';
|
||||||
import { useStationStore } from '@/stores';
|
import { useStationStore } from '@/stores';
|
||||||
import { downloadByData, parseErrorFeedback } from '@/utils';
|
import { downloadByData, parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {
|
import {
|
||||||
NButton,
|
NButton,
|
||||||
@@ -54,12 +21,11 @@ import {
|
|||||||
type SelectOption,
|
type SelectOption,
|
||||||
} from 'naive-ui';
|
} from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { h, onBeforeUnmount } from 'vue';
|
import { h } from 'vue';
|
||||||
import { computed, reactive, ref, watch, watchEffect } from 'vue';
|
import { computed, reactive, ref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
interface SearchFields extends PageQueryExtra<NdmCallLog> {
|
interface SearchFields extends PageQueryExtra<NdmCallLog> {
|
||||||
stationCode?: Station['code'];
|
stationCode?: Station['code'];
|
||||||
logType_in: number[];
|
|
||||||
createdTime: [string, string];
|
createdTime: [string, string];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,13 +41,11 @@ const stationSelectOptions = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const searchFields = ref<SearchFields>({
|
const searchFields = ref<SearchFields>({
|
||||||
logType_in: [],
|
|
||||||
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')],
|
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')],
|
||||||
});
|
});
|
||||||
const resetSearchFields = () => {
|
const resetSearchFields = () => {
|
||||||
searchFields.value = {
|
searchFields.value = {
|
||||||
stationCode: onlineStations.value.at(0)?.code,
|
stationCode: stations.value.find((station) => station.online)?.code,
|
||||||
logType_in: [],
|
|
||||||
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')],
|
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -93,7 +57,6 @@ const getExtraFields = (): PageQueryExtra<NdmCallLog> => {
|
|||||||
const method_like = searchFields.value.method_like;
|
const method_like = searchFields.value.method_like;
|
||||||
const messageType_like = searchFields.value.messageType_like;
|
const messageType_like = searchFields.value.messageType_like;
|
||||||
const cmdType_like = searchFields.value.cmdType_like;
|
const cmdType_like = searchFields.value.cmdType_like;
|
||||||
const logType_in = searchFields.value.logType_in;
|
|
||||||
return {
|
return {
|
||||||
createdTime_precisest,
|
createdTime_precisest,
|
||||||
createdTime_preciseed,
|
createdTime_preciseed,
|
||||||
@@ -102,7 +65,6 @@ const getExtraFields = (): PageQueryExtra<NdmCallLog> => {
|
|||||||
method_like,
|
method_like,
|
||||||
messageType_like,
|
messageType_like,
|
||||||
cmdType_like,
|
cmdType_like,
|
||||||
logType_in,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,18 +75,8 @@ watch(searchFields, () => {
|
|||||||
|
|
||||||
const tableColumns: DataTableColumns<NdmCallLogResultVO> = [
|
const tableColumns: DataTableColumns<NdmCallLogResultVO> = [
|
||||||
{ title: '时间', key: 'createdTime' },
|
{ title: '时间', key: 'createdTime' },
|
||||||
{
|
|
||||||
title: '日志类型',
|
|
||||||
key: 'logType',
|
|
||||||
render: (rowData) => {
|
|
||||||
const option = callLogTypeOptions.find((option) => option.value === rowData.logType);
|
|
||||||
return `${option?.label ?? ''}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ title: '调用者国标码', key: 'sourceGbId' },
|
{ title: '调用者国标码', key: 'sourceGbId' },
|
||||||
{ title: '用户所属类别', key: 'sourceType' },
|
|
||||||
{ title: '被调用设备国标码', key: 'targetGbId' },
|
{ title: '被调用设备国标码', key: 'targetGbId' },
|
||||||
{ title: '被调用设备名称', key: 'targetName' },
|
|
||||||
{ title: '调用方法', key: 'method' },
|
{ title: '调用方法', key: 'method' },
|
||||||
{ title: '消息类型', key: 'messageType' },
|
{ title: '消息类型', key: 'messageType' },
|
||||||
{ title: '操作类型', key: 'cmdType' },
|
{ title: '操作类型', key: 'cmdType' },
|
||||||
@@ -153,18 +105,9 @@ const pagination = reactive<PaginationProps>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const abortController = ref(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
||||||
|
|
||||||
const stationCode = searchFields.value.stationCode;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const res = await pageCallLogApi(
|
const res = await pageCallLogApi(
|
||||||
{
|
{
|
||||||
model: {},
|
model: {},
|
||||||
@@ -175,8 +118,7 @@ const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|||||||
sort: 'id',
|
sort: 'id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stationCode,
|
stationCode: searchFields.value.stationCode,
|
||||||
signal,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return res;
|
return res;
|
||||||
@@ -188,7 +130,6 @@ const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|||||||
tableData.value = records;
|
tableData.value = records;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -213,14 +154,7 @@ const onClickQuery = () => {
|
|||||||
|
|
||||||
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
||||||
|
|
||||||
const stationCode = searchFields.value.stationCode;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const data = await exportCallLogApi(
|
const data = await exportCallLogApi(
|
||||||
{
|
{
|
||||||
model: {},
|
model: {},
|
||||||
@@ -231,8 +165,7 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
sort: 'id',
|
sort: 'id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stationCode,
|
stationCode: searchFields.value.stationCode,
|
||||||
signal,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
@@ -242,7 +175,6 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
downloadByData(data, `上级调用日志_${time}.xlsx`);
|
downloadByData(data, `上级调用日志_${time}.xlsx`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -272,10 +204,6 @@ watch(
|
|||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
abortController.value.abort();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -299,9 +227,6 @@ onBeforeUnmount(() => {
|
|||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
<NFormItemGi :span="1" label="日志类型" label-placement="left">
|
|
||||||
<NSelect v-model:value="searchFields.logType_in" :options="callLogTypeOptions" multiple clearable />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="1" label="时间" label-placement="left">
|
<NFormItemGi span="1" label="时间" label-placement="left">
|
||||||
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
|
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
@@ -319,7 +244,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<!-- 数据表格工具栏 -->
|
<!-- 数据表格工具栏 -->
|
||||||
<NFlex align="center" style="padding: 8px; flex: 0 0 auto">
|
<NFlex align="center" style="padding: 8px; flex: 0 0 auto">
|
||||||
<div style="font-size: medium">上级调用日志</div>
|
<div style="font-size: medium">视频平台日志</div>
|
||||||
<NFlex style="margin-left: auto">
|
<NFlex style="margin-left: auto">
|
||||||
<NButton type="primary" :loading="exporting" @click="() => exportTableData()">导出</NButton>
|
<NButton type="primary" :loading="exporting" @click="() => exportTableData()">导出</NButton>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NdmDeviceResultVO, Station } from '@/apis';
|
import type { NdmDeviceResultVO, Station } from '@/apis';
|
||||||
import { DeviceRenderer, DeviceTree, type DeviceTreeProps } from '@/components';
|
import { DeviceRenderer, DeviceTree, type DeviceTreeProps } from '@/components';
|
||||||
import type { UseDeviceSelectionReturn } from '@/composables';
|
|
||||||
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
|
|
||||||
import { useStationStore } from '@/stores';
|
import { useStationStore } from '@/stores';
|
||||||
import { NLayout, NLayoutContent, NLayoutSider } from 'naive-ui';
|
import { NLayout, NLayoutContent, NLayoutSider } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { provide, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const stationStore = useStationStore();
|
const stationStore = useStationStore();
|
||||||
const { stations } = storeToRefs(stationStore);
|
const { stations } = storeToRefs(stationStore);
|
||||||
@@ -14,14 +12,7 @@ const { stations } = storeToRefs(stationStore);
|
|||||||
const selectedStation = ref<Station>();
|
const selectedStation = ref<Station>();
|
||||||
const selectedDevice = ref<NdmDeviceResultVO>();
|
const selectedDevice = ref<NdmDeviceResultVO>();
|
||||||
|
|
||||||
// 获取设备树暴露出来的 `selectDevice` 函数,并将其提供给子组件
|
const onSelectDevice: DeviceTreeProps['onSelectDevice'] = (device, stationCode) => {
|
||||||
const selectDeviceFn = ref<UseDeviceSelectionReturn['selectDevice']>();
|
|
||||||
const onExposeSelectDeviceFn: DeviceTreeProps['onExposeSelectDeviceFn'] = (fn) => {
|
|
||||||
selectDeviceFn.value = fn;
|
|
||||||
};
|
|
||||||
provide(SELECT_DEVICE_FN_INJECTION_KEY, selectDeviceFn);
|
|
||||||
|
|
||||||
const onAfterSelectDevice: DeviceTreeProps['onAfterSelectDevice'] = (device, stationCode) => {
|
|
||||||
selectedDevice.value = device;
|
selectedDevice.value = device;
|
||||||
selectedStation.value = stations.value.find((station) => station.code === stationCode);
|
selectedStation.value = stations.value.find((station) => station.code === stationCode);
|
||||||
};
|
};
|
||||||
@@ -30,7 +21,7 @@ const onAfterSelectDevice: DeviceTreeProps['onAfterSelectDevice'] = (device, sta
|
|||||||
<template>
|
<template>
|
||||||
<NLayout has-sider style="height: 100%">
|
<NLayout has-sider style="height: 100%">
|
||||||
<NLayoutSider bordered :width="600" :collapsed-width="0" show-trigger="bar">
|
<NLayoutSider bordered :width="600" :collapsed-width="0" show-trigger="bar">
|
||||||
<DeviceTree :events="['select', 'manage']" :sync-route="true" :device-prefix-label="'查看'" @expose-select-device-fn="onExposeSelectDeviceFn" @after-select-device="onAfterSelectDevice" />
|
<DeviceTree @select-device="onSelectDevice" />
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
<NLayoutContent :content-style="{ padding: '8px 8px 8px 24px' }">
|
<NLayoutContent :content-style="{ padding: '8px 8px 8px 24px' }">
|
||||||
<template v-if="selectedStation && selectedDevice">
|
<template v-if="selectedStation && selectedDevice">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
const vimpLogTypeOptions: SelectOption[] = [
|
const vimpOperationTypeOptions: SelectOption[] = [
|
||||||
{ label: '视频点播', value: 10001 },
|
{ label: '视频点播', value: 10001 },
|
||||||
{ label: '视频回放', value: 10002 },
|
{ label: '视频回放', value: 10002 },
|
||||||
// { label: '停止视频回放', value: 10003 },
|
// { label: '停止视频回放', value: 10003 },
|
||||||
@@ -32,8 +32,8 @@ import { exportVimpLogApi, pageVimpLogApi, type NdmVimpLog, type NdmVimpLogResul
|
|||||||
import { useStationStore } from '@/stores';
|
import { useStationStore } from '@/stores';
|
||||||
import { downloadByData, parseErrorFeedback } from '@/utils';
|
import { downloadByData, parseErrorFeedback } from '@/utils';
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
import { isCancel } from 'axios';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import destr from 'destr';
|
||||||
import {
|
import {
|
||||||
NButton,
|
NButton,
|
||||||
NDataTable,
|
NDataTable,
|
||||||
@@ -43,6 +43,8 @@ import {
|
|||||||
NFormItemGi,
|
NFormItemGi,
|
||||||
NGrid,
|
NGrid,
|
||||||
NGridItem,
|
NGridItem,
|
||||||
|
NPopover,
|
||||||
|
NScrollbar,
|
||||||
NSelect,
|
NSelect,
|
||||||
NTag,
|
NTag,
|
||||||
type DataTableColumns,
|
type DataTableColumns,
|
||||||
@@ -51,7 +53,7 @@ import {
|
|||||||
type SelectOption,
|
type SelectOption,
|
||||||
} from 'naive-ui';
|
} from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, h, onBeforeUnmount, reactive, ref, watch, watchEffect } from 'vue';
|
import { computed, h, reactive, ref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
interface SearchFields extends PageQueryExtra<NdmVimpLog> {
|
interface SearchFields extends PageQueryExtra<NdmVimpLog> {
|
||||||
stationCode?: Station['code'];
|
stationCode?: Station['code'];
|
||||||
@@ -99,17 +101,56 @@ watch(searchFields, () => {
|
|||||||
|
|
||||||
const tableColumns: DataTableColumns<NdmVimpLogResultVO> = [
|
const tableColumns: DataTableColumns<NdmVimpLogResultVO> = [
|
||||||
{ title: '时间', key: 'createdTime' },
|
{ title: '时间', key: 'createdTime' },
|
||||||
{
|
{ title: '操作类型', key: 'description' },
|
||||||
title: '操作类型',
|
|
||||||
key: 'logType',
|
|
||||||
render: (rowData) => {
|
|
||||||
const option = vimpLogTypeOptions.find((option) => option.value === rowData.logType);
|
|
||||||
return `${option?.label ?? ''}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ title: '请求IP', key: 'requestIp' },
|
{ title: '请求IP', key: 'requestIp' },
|
||||||
{ title: '耗时(ms)', key: 'consumedTime' },
|
{ title: '耗时(ms)', key: 'consumedTime' },
|
||||||
{ title: '被调用设备', key: 'targetCode' },
|
{ title: '被调用设备', key: 'targetCode' },
|
||||||
|
{
|
||||||
|
title: '操作参数',
|
||||||
|
key: 'params',
|
||||||
|
width: 100,
|
||||||
|
render(rowData) {
|
||||||
|
const result = JSON.stringify(destr(rowData.params), null, 2);
|
||||||
|
return h(
|
||||||
|
NPopover,
|
||||||
|
{ trigger: 'click' },
|
||||||
|
{
|
||||||
|
trigger: () => h(NButton, { size: 'tiny', text: true, type: 'primary' }, { default: () => '查看' }),
|
||||||
|
default: () =>
|
||||||
|
h(
|
||||||
|
NScrollbar,
|
||||||
|
{ style: { maxHeight: '40vh' } },
|
||||||
|
{
|
||||||
|
default: () => h('pre', {}, { default: () => result }),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作结果',
|
||||||
|
key: 'result',
|
||||||
|
width: 100,
|
||||||
|
render: (rowData) => {
|
||||||
|
const result = JSON.stringify(destr(rowData.result), null, 2);
|
||||||
|
return h(
|
||||||
|
NPopover,
|
||||||
|
{ trigger: 'click' },
|
||||||
|
{
|
||||||
|
trigger: () => h(NButton, { size: 'tiny', text: true, type: 'primary' }, { default: () => '查看' }),
|
||||||
|
default: () =>
|
||||||
|
h(
|
||||||
|
NScrollbar,
|
||||||
|
{ style: { maxHeight: '40vh' } },
|
||||||
|
{
|
||||||
|
default: () => h('pre', {}, { default: () => result }),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const tableData = ref<DataTableRowData[]>([]);
|
const tableData = ref<DataTableRowData[]>([]);
|
||||||
@@ -135,18 +176,9 @@ const pagination = reactive<PaginationProps>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const abortController = ref(new AbortController());
|
|
||||||
|
|
||||||
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
||||||
|
|
||||||
const stationCode = searchFields.value.stationCode;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const res = await pageVimpLogApi(
|
const res = await pageVimpLogApi(
|
||||||
{
|
{
|
||||||
model: {},
|
model: {},
|
||||||
@@ -157,8 +189,7 @@ const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|||||||
sort: 'id',
|
sort: 'id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stationCode,
|
stationCode: searchFields.value.stationCode,
|
||||||
signal,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return res;
|
return res;
|
||||||
@@ -170,7 +201,6 @@ const { mutate: getTableData, isPending: tableLoading } = useMutation({
|
|||||||
tableData.value = records;
|
tableData.value = records;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -195,14 +225,7 @@ const onClickQuery = () => {
|
|||||||
|
|
||||||
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
const { mutate: exportTableData, isPending: exporting } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
abortController.value.abort();
|
|
||||||
abortController.value = new AbortController();
|
|
||||||
|
|
||||||
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
if (!searchFields.value.stationCode) throw Error('请选择车站');
|
||||||
|
|
||||||
const stationCode = searchFields.value.stationCode;
|
|
||||||
const signal = abortController.value.signal;
|
|
||||||
|
|
||||||
const data = await exportVimpLogApi(
|
const data = await exportVimpLogApi(
|
||||||
{
|
{
|
||||||
model: {},
|
model: {},
|
||||||
@@ -213,8 +236,7 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
sort: 'id',
|
sort: 'id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stationCode,
|
stationCode: searchFields.value.stationCode,
|
||||||
signal,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
@@ -224,7 +246,6 @@ const { mutate: exportTableData, isPending: exporting } = useMutation({
|
|||||||
downloadByData(data, `视频操作日志_${time}.xlsx`);
|
downloadByData(data, `视频操作日志_${time}.xlsx`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (isCancel(error)) return;
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
const errorFeedback = parseErrorFeedback(error);
|
||||||
window.$message.error(errorFeedback);
|
window.$message.error(errorFeedback);
|
||||||
@@ -254,10 +275,6 @@ watch(
|
|||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
abortController.value.abort();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -281,8 +298,8 @@ onBeforeUnmount(() => {
|
|||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
<NFormItemGi :span="1" label="日志类型" label-placement="left">
|
<NFormItemGi :span="1" label="操作类型" label-placement="left">
|
||||||
<NSelect v-model:value="searchFields.logType_in" :options="vimpLogTypeOptions" multiple clearable />
|
<NSelect v-model:value="searchFields.logType_in" :options="vimpOperationTypeOptions" multiple clearable />
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
<NFormItemGi span="1" label="时间" label-placement="left">
|
<NFormItemGi span="1" label="时间" label-placement="left">
|
||||||
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
|
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
|
||||||
|
|||||||
@@ -23,16 +23,7 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'alarm',
|
path: 'alarm',
|
||||||
children: [
|
component: () => import('@/pages/alarm-page.vue'),
|
||||||
{
|
|
||||||
path: 'alarm-log',
|
|
||||||
component: () => import('@/pages/alarm-log-page.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'alarm-ignore',
|
|
||||||
component: () => import('@/pages/alarm-ignore-page.vue'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'log',
|
path: 'log',
|
||||||
|
|||||||
@@ -45,16 +45,6 @@ export const useSettingStore = defineStore(
|
|||||||
if (!userStore.userLoginResult.token) {
|
if (!userStore.userLoginResult.token) {
|
||||||
userStore.userLoginResult.token = 'test';
|
userStore.userLoginResult.token = 'test';
|
||||||
}
|
}
|
||||||
if (!userStore.userInfo) {
|
|
||||||
userStore.userInfo = {
|
|
||||||
id: '2',
|
|
||||||
username: 'lamp',
|
|
||||||
nickName: '内置超管',
|
|
||||||
mobile: '15211111110',
|
|
||||||
employeeId: '2',
|
|
||||||
tenantId: '1',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (router.currentRoute.value.path === '/login') {
|
if (router.currentRoute.value.path === '/login') {
|
||||||
router.push({ path: '/' });
|
router.push({ path: '/' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { InjectionKey } from 'vue';
|
|
||||||
|
|
||||||
export const createInjectionKey = <T>(key: string): InjectionKey<T> => {
|
|
||||||
return Symbol(key);
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
export * from './cipher';
|
export * from './cipher';
|
||||||
export * from './create-injection-key';
|
|
||||||
export * from './download';
|
export * from './download';
|
||||||
export * from './env';
|
export * from './env';
|
||||||
export * from './format-duration';
|
export * from './format-duration';
|
||||||
export * from './random-num';
|
export * from './random-num';
|
||||||
export * from './http-client';
|
export * from './request-client';
|
||||||
export * from './sleep';
|
export * from './sleep';
|
||||||
|
|||||||
@@ -1,41 +1,54 @@
|
|||||||
import type { Result } from '@/types';
|
import type { Result } from '@/types';
|
||||||
import axios, { isAxiosError, type AxiosError, type AxiosRequestConfig, type AxiosResponse, type CreateAxiosDefaults, type InternalAxiosRequestConfig } from 'axios';
|
import axios, { isAxiosError, type AxiosError, type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, type CreateAxiosDefaults, type InternalAxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
export type HttpResponse<T> = [err: AxiosError | null, data: T | null, resp: Result<T> | null];
|
export type Response<T> = [err: AxiosError | null, data: T | null, resp: Result<T> | null];
|
||||||
|
|
||||||
export interface HttpRequestOptions extends CreateAxiosDefaults {
|
export interface RequestOptions extends CreateAxiosDefaults {
|
||||||
requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
|
requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
|
||||||
responseInterceptor?: (resp: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
|
responseInterceptor?: (resp: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
|
||||||
responseErrorInterceptor?: (error: any) => any;
|
responseErrorInterceptor?: (error: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createHttpClient = (config?: HttpRequestOptions) => {
|
export class RequestClient {
|
||||||
const defaultRequestInterceptor = (config: InternalAxiosRequestConfig) => config;
|
private instance: AxiosInstance;
|
||||||
const defaultResponseInterceptor = (response: AxiosResponse) => response;
|
|
||||||
const defaultResponseErrorInterceptor = (error: any) => {
|
constructor(config?: RequestOptions) {
|
||||||
if (isAxiosError(error)) {
|
this.instance = axios.create(config);
|
||||||
if (error.status === 401) {
|
|
||||||
// 处理 401 错误
|
const requestInterceptor = config?.requestInterceptor ?? RequestClient.defaultRequestInterceptor;
|
||||||
|
const responseInterceptor = config?.responseInterceptor ?? RequestClient.defaultResponseInterceptor;
|
||||||
|
const responseErrorInterceptor = config?.responseErrorInterceptor ?? RequestClient.defaultResponseErrorInterceptor;
|
||||||
|
|
||||||
|
this.instance.interceptors.request.use(requestInterceptor);
|
||||||
|
|
||||||
|
this.instance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
||||||
}
|
}
|
||||||
if (error.status === 404) {
|
|
||||||
// 处理 404 错误
|
private static defaultRequestInterceptor(config: InternalAxiosRequestConfig) {
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static defaultResponseInterceptor(response: AxiosResponse) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static defaultResponseErrorInterceptor(error: any) {
|
||||||
|
const err = error as AxiosError;
|
||||||
|
if (err.status === 401) {
|
||||||
|
}
|
||||||
|
if (err.status === 404) {
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
};
|
}
|
||||||
|
|
||||||
const requestInterceptor = config?.requestInterceptor ?? defaultRequestInterceptor;
|
public get requestInstance(): AxiosInstance {
|
||||||
const responseInterceptor = config?.responseInterceptor ?? defaultResponseInterceptor;
|
return this.instance;
|
||||||
const responseErrorInterceptor = config?.responseErrorInterceptor ?? defaultResponseErrorInterceptor;
|
}
|
||||||
|
|
||||||
const instance = axios.create(config);
|
get<T>(url: string, options?: AxiosRequestConfig & { retRaw?: boolean }): Promise<Response<T>> {
|
||||||
instance.interceptors.request.use(requestInterceptor);
|
|
||||||
instance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
|
||||||
|
|
||||||
const httpGet = <T>(url: string, options?: AxiosRequestConfig & { retRaw?: boolean }): Promise<HttpResponse<T>> => {
|
|
||||||
const { retRaw, ...reqConfig } = options ?? {};
|
const { retRaw, ...reqConfig } = options ?? {};
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
instance
|
this.instance
|
||||||
.get(url, {
|
.get(url, {
|
||||||
...reqConfig,
|
...reqConfig,
|
||||||
})
|
})
|
||||||
@@ -51,12 +64,12 @@ export const createHttpClient = (config?: HttpRequestOptions) => {
|
|||||||
resolve([err as AxiosError, null, null]);
|
resolve([err as AxiosError, null, null]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
const httpPost = <T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>> & { retRaw?: boolean; upload?: boolean }): Promise<HttpResponse<T>> => {
|
post<T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>> & { retRaw?: boolean; upload?: boolean }): Promise<Response<T>> {
|
||||||
const { retRaw, upload, ...reqConfig } = options ?? {};
|
const { retRaw, upload, ...reqConfig } = options ?? {};
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
instance
|
this.instance
|
||||||
.post(url, data, { headers: { 'content-type': upload ? 'multipart/form-data' : 'application/json' }, ...reqConfig })
|
.post(url, data, { headers: { 'content-type': upload ? 'multipart/form-data' : 'application/json' }, ...reqConfig })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const resData = res.data;
|
const resData = res.data;
|
||||||
@@ -70,12 +83,12 @@ export const createHttpClient = (config?: HttpRequestOptions) => {
|
|||||||
resolve([err as AxiosError, null, null]);
|
resolve([err as AxiosError, null, null]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
const httpPut = <T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>>): Promise<HttpResponse<T>> => {
|
put<T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>>): Promise<Response<T>> {
|
||||||
const reqConfig = options ?? {};
|
const reqConfig = options ?? {};
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
instance
|
this.instance
|
||||||
.put<Result<T>>(url, data, { ...reqConfig })
|
.put<Result<T>>(url, data, { ...reqConfig })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
resolve([null, res.data.data, res.data]);
|
resolve([null, res.data.data, res.data]);
|
||||||
@@ -84,12 +97,12 @@ export const createHttpClient = (config?: HttpRequestOptions) => {
|
|||||||
resolve([err as AxiosError, null, null]);
|
resolve([err as AxiosError, null, null]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
const httpDelete = <T>(url: string, idList: string[], options?: Partial<Omit<AxiosRequestConfig, 'data'>>): Promise<HttpResponse<T>> => {
|
delete<T>(url: string, idList: string[], options?: Partial<Omit<AxiosRequestConfig, 'data'>>): Promise<Response<T>> {
|
||||||
const reqConfig = options ?? {};
|
const reqConfig = options ?? {};
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
instance
|
this.instance
|
||||||
.delete<Result<T>>(url, { ...reqConfig, data: idList })
|
.delete<Result<T>>(url, { ...reqConfig, data: idList })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
resolve([null, res.data.data, res.data]);
|
resolve([null, res.data.data, res.data]);
|
||||||
@@ -98,21 +111,11 @@ export const createHttpClient = (config?: HttpRequestOptions) => {
|
|||||||
resolve([err as AxiosError, null, null]);
|
resolve([err as AxiosError, null, null]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}
|
||||||
return {
|
|
||||||
get clientInstance() {
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
get: httpGet,
|
|
||||||
post: httpPost,
|
|
||||||
put: httpPut,
|
|
||||||
delete: httpDelete,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 从响应中解析出数据
|
// 从响应中解析出数据
|
||||||
export const unwrapResponse = <T>(resp: HttpResponse<T>) => {
|
export const unwrapResponse = <T>(resp: Response<T>) => {
|
||||||
const [err, data, result] = resp;
|
const [err, data, result] = resp;
|
||||||
|
|
||||||
// 如果 err 存在说明有 http 错误,那么直接抛出错误,
|
// 如果 err 存在说明有 http 错误,那么直接抛出错误,
|
||||||
@@ -136,16 +139,6 @@ export const unwrapResponse = <T>(resp: HttpResponse<T>) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 针对没有数据的响应,直接判断是否存在错误
|
|
||||||
export const unwrapVoidResponse = (resp: HttpResponse<void>) => {
|
|
||||||
const [err, , result] = resp;
|
|
||||||
if (err) throw err;
|
|
||||||
if (result) {
|
|
||||||
const { isSuccess, path, msg, errorMsg } = result;
|
|
||||||
if (!isSuccess) throw new Error(`${path ? `${path}: ` : ''}${msg || errorMsg || '请求失败'}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 从错误中解析出错误信息
|
// 从错误中解析出错误信息
|
||||||
export const parseErrorFeedback = (error: Error) => {
|
export const parseErrorFeedback = (error: Error) => {
|
||||||
// 当发生 http 错误时,unwrapResponse 会直接抛出错误,
|
// 当发生 http 错误时,unwrapResponse 会直接抛出错误,
|
||||||
Reference in New Issue
Block a user