refactor(vimp): 重构vimp模块的API目录与导入路径

重新梳理vimp模块的API代码结构,拆分为client、model、query、request子模块并添加统一导出入口;修正所有相关文件的导入路径,新增通用响应类型与工具函数,优化树组件的类型判断逻辑,同时新增设备查询相关API与查询hook。
This commit is contained in:
yangsy
2026-05-27 15:43:53 +08:00
parent 7467b54834
commit a92e47bc18
18 changed files with 102 additions and 68 deletions
+1
View File
@@ -0,0 +1 @@
export * from './vimp-client';
@@ -1,16 +1,8 @@
import type { VimpChannel, VimpStation } from './model';
import type { AxiosError, AxiosRequestConfig, CreateAxiosDefaults } from 'axios';
import axios from 'axios';
import type { VimpResponse, VimpResult } from '../../types';
interface VimpResult<T = unknown> {
code: number;
data: T;
msg: string;
}
type VimpResponse<T> = [err: AxiosError | null, data: T | null, resp: VimpResult<T> | null];
const createVimpClient = (config?: CreateAxiosDefaults) => {
export const createVimpClient = (config?: CreateAxiosDefaults) => {
const instance = axios.create(config);
const vimpPost = <T>(url: string, data?: AxiosRequestConfig['data'], options?: Partial<Omit<AxiosRequestConfig, 'data'>> & { retRaw?: boolean; upload?: boolean }): Promise<VimpResponse<T>> => {
@@ -38,7 +30,7 @@ const createVimpClient = (config?: CreateAxiosDefaults) => {
};
};
const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
export const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
const [err, data, result] = resp;
if (err) throw err;
if (result) {
@@ -51,21 +43,3 @@ const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
export const vimpClient = createVimpClient({
baseURL: `/vimp/api/client`,
});
export const catalogAllDeviceApi = async (options?: { signal?: AbortSignal }) => {
const { signal } = options ?? {};
const client = vimpClient;
const endpoint = `/catalog/allDevice`;
const resp = await client.post<VimpStation[]>(endpoint, {}, { signal });
const data = unwrapVimpResponse(resp);
return data;
};
export const catalogChannelApi = async (code: string, options?: { signal?: AbortSignal }) => {
const { signal } = options ?? {};
const client = vimpClient;
const endpoint = `/catalog/channel`;
const resp = await client.post<VimpChannel[]>(endpoint, { code, time: '' }, { signal });
const data = unwrapVimpResponse(resp);
return data;
};
@@ -1,3 +1,4 @@
export * from './client';
export * from './model';
export * from './query';
export * from './request';
+2
View File
@@ -0,0 +1,2 @@
export * from './vimp-channel';
export * from './vimp-station';
@@ -1,9 +1,3 @@
export interface VimpStation {
code: string;
name: string;
online: boolean;
}
export interface VimpChannel {
address: string;
block: string;
@@ -0,0 +1,5 @@
export interface VimpStation {
code: string;
name: string;
online: boolean;
}
@@ -1,13 +1,13 @@
import { useQuery } from '@tanstack/vue-query';
import { computed } from 'vue';
import { catalogChannelApi, catalogAllDeviceApi } from './request';
import { catalogChannelApi, catalogAllDeviceApi } from '../request';
import type { AxiosRequestConfig } from 'axios';
import axios from 'axios';
import type { CodeArea, CodeLines, CodeSites } from '../types';
import type { VimpChannel } from '.';
import { useCameraStore, useAlarmStore } from '../stores';
import type { CodeArea, CodeLines, CodeSites } from '../../types';
import { useCameraStore, useAlarmStore } from '../../stores';
import type { VimpChannel } from '../model';
export const useVimpDeviceQuery = () => {
export const useDeviceCenterQuery = () => {
const cameraStore = useCameraStore();
const alarmStore = useAlarmStore();
+1
View File
@@ -0,0 +1 @@
export * from './device-center-query';
@@ -0,0 +1,11 @@
import { unwrapVimpResponse, vimpClient } from '../client';
import type { VimpStation } from '../model';
export const catalogAllDeviceApi = async (options?: { signal?: AbortSignal }) => {
const { signal } = options ?? {};
const client = vimpClient;
const endpoint = `/catalog/allDevice`;
const resp = await client.post<VimpStation[]>(endpoint, {}, { signal });
const data = unwrapVimpResponse(resp);
return data;
};
@@ -0,0 +1,11 @@
import { unwrapVimpResponse, vimpClient } from '../client';
import type { VimpChannel } from '../model';
export const catalogChannelApi = async (code: string, options?: { signal?: AbortSignal }) => {
const { signal } = options ?? {};
const client = vimpClient;
const endpoint = `/catalog/channel`;
const resp = await client.post<VimpChannel[]>(endpoint, { code, time: '' }, { signal });
const data = unwrapVimpResponse(resp);
return data;
};
+2
View File
@@ -0,0 +1,2 @@
export * from './catalog.channel';
export * from './catalog.all-device';
+10 -11
View File
@@ -1,13 +1,12 @@
<script setup lang="ts">
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui';
import { useVimpDeviceQuery } from '../api/query';
import type { VimpChannel, VimpStation } from '../api/model';
import { h, type CSSProperties } from 'vue';
import { hasOwn } from '@vueuse/core';
import { useAlarmStore } from '../stores';
import { storeToRefs } from 'pinia';
import { useDeviceCenterQuery, type VimpChannel, type VimpStation } from '../apis';
import { isAlarmNode, isAlarmSiteNode, isAlarmAreaNode } from '../types';
const { isLoading } = useVimpDeviceQuery();
const { isLoading } = useDeviceCenterQuery();
const alarmStore = useAlarmStore();
const { lineTabPanes } = storeToRefs(alarmStore);
@@ -25,8 +24,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
// 是车站节点
if (hasOwn(option, 'online')) {
const siteOnline = option['online'] as boolean;
if (isAlarmSiteNode(option)) {
const siteOnline = option.online;
const siteNodeStyle: CSSProperties = {
opacity: siteOnline ? 1 : 0.5,
};
@@ -34,8 +33,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
}
// 是中间节点(一级/二级区域)
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) {
const site = option['site'] as VimpStation;
if (isAlarmAreaNode(option)) {
const site = option.site;
const nodeStyle: CSSProperties = {
opacity: site.online ? 1 : 0.5,
};
@@ -43,9 +42,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
}
// 是警报器节点
if (hasOwn(option, 'alarm') && hasOwn(option, 'site')) {
const alarm = option['alarm'] as VimpChannel;
const site = option['site'] as VimpStation;
if (isAlarmNode(option)) {
const alarm = option.alarm;
const site = option.site;
const alarmOnline = () => {
return alarm.status === 1 && site.online;
+13 -14
View File
@@ -1,13 +1,12 @@
<script setup lang="ts">
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui';
import { useVimpDeviceQuery } from '../api/query';
import type { VimpChannel, VimpStation } from '../api/model';
import { h, type CSSProperties } from 'vue';
import { hasOwn } from '@vueuse/core';
import { useCameraStore } from '../stores';
import { storeToRefs } from 'pinia';
import { useDeviceCenterQuery } from '../apis';
import { isCameraNode, isCameraSiteNode, isCameraAreaNode } from '../types';
const { isLoading } = useVimpDeviceQuery();
const { isLoading } = useDeviceCenterQuery();
const cameraStore = useCameraStore();
const { lineTabPanes } = storeToRefs(cameraStore);
@@ -25,8 +24,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
// 是车站节点
if (hasOwn(option, 'online')) {
const siteOnline = option['online'] as boolean;
if (isCameraSiteNode(option)) {
const siteOnline = option.online;
const siteNodeStyle: CSSProperties = {
opacity: siteOnline ? 1 : 0.5,
};
@@ -34,8 +33,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
}
// 是中间节点(一级/二级区域)
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) {
const site = option['site'] as VimpStation;
if (isCameraAreaNode(option)) {
const site = option.site;
const nodeStyle: CSSProperties = {
opacity: site.online ? 1 : 0.5,
};
@@ -43,9 +42,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
}
// 是摄像机节点
if (hasOwn(option, 'camera') && hasOwn(option, 'site')) {
const camera = option['camera'] as VimpChannel;
const site = option['site'] as VimpStation;
if (isCameraNode(option)) {
const camera = option.camera;
const site = option.site;
const cameraOnline = () => {
return camera.status === 1 && site.online;
@@ -59,14 +58,14 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
'div',
{
style: cameraNodeStyle,
draggable: camera.status === 1,
draggable: cameraOnline(),
onDblclick() {
if (camera.status === 0) return;
if (!cameraOnline()) return;
selectedDeviceGbCode.value = [camera.code];
window.$message.info(`播放:${JSON.stringify({ code: camera.code, name: camera.name })}`);
},
onDragstart(event) {
if (camera.status === 0) return;
if (!cameraOnline()) return;
console.log(event);
event.dataTransfer?.setData('type', 'camera');
event.dataTransfer?.setData('code', camera.code);
+1 -1
View File
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import type { VimpChannel, VimpStation } from '../api';
import type { VimpChannel, VimpStation } from '../apis';
import { h, ref } from 'vue';
import type { AlarmAreaNodeOption, AlarmNodeOption, CodeArea, CodeLines, CodeSites, AlarmLineTabPane, AlarmSiteNodeOption, AlarmSubAreaNodeOption } from '../types';
+1 -1
View File
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import type { VimpChannel, VimpStation } from '../api';
import type { VimpChannel, VimpStation } from '../apis';
import { h, ref } from 'vue';
import type { CameraAreaNodeOption, CameraNodeOption, CodeArea, CodeLines, CodeSites, CameraLineTabPane, CameraSiteNodeOption, CameraSubAreaNodeOption } from '../types';
+9
View File
@@ -0,0 +1,9 @@
import type { AxiosError } from 'axios';
export interface VimpResult<T = unknown> {
code: number;
data: T;
msg: string;
}
export type VimpResponse<T> = [err: AxiosError | null, data: T | null, resp: VimpResult<T> | null];
+1
View File
@@ -1 +1,2 @@
export * from './axios';
export * from './tree';
+25 -1
View File
@@ -1,5 +1,5 @@
import type { TabPaneProps, TreeOption } from 'naive-ui';
import type { VimpChannel, VimpStation } from '../api';
import type { VimpChannel, VimpStation } from '../apis/model';
export type SiteType = 'station' | 'parking' | 'occ' | 'train';
export type CodeLines = Record<string, { name: string; color: string }>;
@@ -39,6 +39,18 @@ export interface CameraSiteNodeOption extends TreeOption {
online: boolean;
}
export function isCameraSiteNode(option: TreeOption): option is CameraSiteNodeOption {
return 'online' in option && !('camera' in option);
}
export function isCameraAreaNode(option: TreeOption): option is CameraAreaNodeOption | CameraSubAreaNodeOption {
return 'site' in option && !('camera' in option) && !('online' in option);
}
export function isCameraNode(option: TreeOption): option is CameraNodeOption {
return 'camera' in option && 'site' in option;
}
export interface CameraLineTabPane extends TabPaneProps {
lineCode: string;
lineName: string;
@@ -72,6 +84,18 @@ export interface AlarmSiteNodeOption extends TreeOption {
online: boolean;
}
export function isAlarmSiteNode(option: TreeOption): option is AlarmSiteNodeOption {
return 'online' in option && !('alarm' in option);
}
export function isAlarmAreaNode(option: TreeOption): option is AlarmAreaNodeOption | AlarmSubAreaNodeOption {
return 'site' in option && !('alarm' in option) && !('online' in option);
}
export function isAlarmNode(option: TreeOption): option is AlarmNodeOption {
return 'alarm' in option && 'site' in option;
}
export interface AlarmLineTabPane extends TabPaneProps {
lineCode: string;
lineName: string;