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 type { AxiosError, AxiosRequestConfig, CreateAxiosDefaults } from 'axios';
import axios from 'axios'; import axios from 'axios';
import type { VimpResponse, VimpResult } from '../../types';
interface VimpResult<T = unknown> { export const createVimpClient = (config?: CreateAxiosDefaults) => {
code: number;
data: T;
msg: string;
}
type VimpResponse<T> = [err: AxiosError | null, data: T | null, resp: VimpResult<T> | null];
const createVimpClient = (config?: CreateAxiosDefaults) => {
const instance = axios.create(config); 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>> => { 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; const [err, data, result] = resp;
if (err) throw err; if (err) throw err;
if (result) { if (result) {
@@ -51,21 +43,3 @@ const unwrapVimpResponse = <T>(resp: VimpResponse<T>) => {
export const vimpClient = createVimpClient({ export const vimpClient = createVimpClient({
baseURL: `/vimp/api/client`, 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 './model';
export * from './query'; export * from './query';
export * from './request'; 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 { export interface VimpChannel {
address: string; address: string;
block: 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 { useQuery } from '@tanstack/vue-query';
import { computed } from 'vue'; import { computed } from 'vue';
import { catalogChannelApi, catalogAllDeviceApi } from './request'; import { catalogChannelApi, catalogAllDeviceApi } from '../request';
import type { AxiosRequestConfig } from 'axios'; import type { AxiosRequestConfig } from 'axios';
import axios from 'axios'; import axios from 'axios';
import type { CodeArea, CodeLines, CodeSites } from '../types'; import type { CodeArea, CodeLines, CodeSites } from '../../types';
import type { VimpChannel } from '.'; import { useCameraStore, useAlarmStore } from '../../stores';
import { useCameraStore, useAlarmStore } from '../stores'; import type { VimpChannel } from '../model';
export const useVimpDeviceQuery = () => { export const useDeviceCenterQuery = () => {
const cameraStore = useCameraStore(); const cameraStore = useCameraStore();
const alarmStore = useAlarmStore(); 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"> <script setup lang="ts">
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui'; 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 { h, type CSSProperties } from 'vue';
import { hasOwn } from '@vueuse/core';
import { useAlarmStore } from '../stores'; import { useAlarmStore } from '../stores';
import { storeToRefs } from 'pinia'; 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 alarmStore = useAlarmStore();
const { lineTabPanes } = storeToRefs(alarmStore); const { lineTabPanes } = storeToRefs(alarmStore);
@@ -25,8 +24,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => { const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
// 是车站节点 // 是车站节点
if (hasOwn(option, 'online')) { if (isAlarmSiteNode(option)) {
const siteOnline = option['online'] as boolean; const siteOnline = option.online;
const siteNodeStyle: CSSProperties = { const siteNodeStyle: CSSProperties = {
opacity: siteOnline ? 1 : 0.5, opacity: siteOnline ? 1 : 0.5,
}; };
@@ -34,8 +33,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
} }
// 是中间节点(一级/二级区域) // 是中间节点(一级/二级区域)
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) { if (isAlarmAreaNode(option)) {
const site = option['site'] as VimpStation; const site = option.site;
const nodeStyle: CSSProperties = { const nodeStyle: CSSProperties = {
opacity: site.online ? 1 : 0.5, opacity: site.online ? 1 : 0.5,
}; };
@@ -43,9 +42,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
} }
// 是警报器节点 // 是警报器节点
if (hasOwn(option, 'alarm') && hasOwn(option, 'site')) { if (isAlarmNode(option)) {
const alarm = option['alarm'] as VimpChannel; const alarm = option.alarm;
const site = option['site'] as VimpStation; const site = option.site;
const alarmOnline = () => { const alarmOnline = () => {
return alarm.status === 1 && site.online; return alarm.status === 1 && site.online;
+13 -14
View File
@@ -1,13 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { NTabPane, NTabs, NTree, type TreeOverrideNodeClickBehavior, type TreeProps } from 'naive-ui'; 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 { h, type CSSProperties } from 'vue';
import { hasOwn } from '@vueuse/core';
import { useCameraStore } from '../stores'; import { useCameraStore } from '../stores';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useDeviceCenterQuery } from '../apis';
import { isCameraNode, isCameraSiteNode, isCameraAreaNode } from '../types';
const { isLoading } = useVimpDeviceQuery(); const { isLoading } = useDeviceCenterQuery();
const cameraStore = useCameraStore(); const cameraStore = useCameraStore();
const { lineTabPanes } = storeToRefs(cameraStore); const { lineTabPanes } = storeToRefs(cameraStore);
@@ -25,8 +24,8 @@ const overrideNodeClickBehavior: TreeOverrideNodeClickBehavior = ({ option }) =>
const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => { const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
// 是车站节点 // 是车站节点
if (hasOwn(option, 'online')) { if (isCameraSiteNode(option)) {
const siteOnline = option['online'] as boolean; const siteOnline = option.online;
const siteNodeStyle: CSSProperties = { const siteNodeStyle: CSSProperties = {
opacity: siteOnline ? 1 : 0.5, opacity: siteOnline ? 1 : 0.5,
}; };
@@ -34,8 +33,8 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
} }
// 是中间节点(一级/二级区域) // 是中间节点(一级/二级区域)
if (!hasOwn(option, 'device') && hasOwn(option, 'site')) { if (isCameraAreaNode(option)) {
const site = option['site'] as VimpStation; const site = option.site;
const nodeStyle: CSSProperties = { const nodeStyle: CSSProperties = {
opacity: site.online ? 1 : 0.5, opacity: site.online ? 1 : 0.5,
}; };
@@ -43,9 +42,9 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
} }
// 是摄像机节点 // 是摄像机节点
if (hasOwn(option, 'camera') && hasOwn(option, 'site')) { if (isCameraNode(option)) {
const camera = option['camera'] as VimpChannel; const camera = option.camera;
const site = option['site'] as VimpStation; const site = option.site;
const cameraOnline = () => { const cameraOnline = () => {
return camera.status === 1 && site.online; return camera.status === 1 && site.online;
@@ -59,14 +58,14 @@ const renderNodeLabel: TreeProps['renderLabel'] = ({ option }) => {
'div', 'div',
{ {
style: cameraNodeStyle, style: cameraNodeStyle,
draggable: camera.status === 1, draggable: cameraOnline(),
onDblclick() { onDblclick() {
if (camera.status === 0) return; if (!cameraOnline()) return;
selectedDeviceGbCode.value = [camera.code]; selectedDeviceGbCode.value = [camera.code];
window.$message.info(`播放:${JSON.stringify({ code: camera.code, name: camera.name })}`); window.$message.info(`播放:${JSON.stringify({ code: camera.code, name: camera.name })}`);
}, },
onDragstart(event) { onDragstart(event) {
if (camera.status === 0) return; if (!cameraOnline()) return;
console.log(event); console.log(event);
event.dataTransfer?.setData('type', 'camera'); event.dataTransfer?.setData('type', 'camera');
event.dataTransfer?.setData('code', camera.code); event.dataTransfer?.setData('code', camera.code);
+1 -1
View File
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import type { VimpChannel, VimpStation } from '../api'; import type { VimpChannel, VimpStation } from '../apis';
import { h, ref } from 'vue'; import { h, ref } from 'vue';
import type { AlarmAreaNodeOption, AlarmNodeOption, CodeArea, CodeLines, CodeSites, AlarmLineTabPane, AlarmSiteNodeOption, AlarmSubAreaNodeOption } from '../types'; 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 { defineStore } from 'pinia';
import type { VimpChannel, VimpStation } from '../api'; import type { VimpChannel, VimpStation } from '../apis';
import { h, ref } from 'vue'; import { h, ref } from 'vue';
import type { CameraAreaNodeOption, CameraNodeOption, CodeArea, CodeLines, CodeSites, CameraLineTabPane, CameraSiteNodeOption, CameraSubAreaNodeOption } from '../types'; 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'; export * from './tree';
+25 -1
View File
@@ -1,5 +1,5 @@
import type { TabPaneProps, TreeOption } from 'naive-ui'; 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 SiteType = 'station' | 'parking' | 'occ' | 'train';
export type CodeLines = Record<string, { name: string; color: string }>; export type CodeLines = Record<string, { name: string; color: string }>;
@@ -39,6 +39,18 @@ export interface CameraSiteNodeOption extends TreeOption {
online: boolean; 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 { export interface CameraLineTabPane extends TabPaneProps {
lineCode: string; lineCode: string;
lineName: string; lineName: string;
@@ -72,6 +84,18 @@ export interface AlarmSiteNodeOption extends TreeOption {
online: boolean; 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 { export interface AlarmLineTabPane extends TabPaneProps {
lineCode: string; lineCode: string;
lineName: string; lineName: string;