mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-08-29 06:31:14 +08:00
refactor(web): enforce typed response envelopes and domain unions
Default the R envelope to unknown so bare wrappers surface their data shape, remove the | string tail that voided alarm/attribute unions, add explicit generics to every dashboard/point/info wrapper, and move the alert row types plus RangeKey to a single dashboard source. Also fix mock seeds to match backend enums (PENDING, DINGTALK_BOT).
This commit is contained in:
@@ -17,44 +17,64 @@
|
||||
|
||||
import {httpGet, httpPost} from '@/api/common';
|
||||
import {API_DATA_BASE} from '@/config/constant/api';
|
||||
import type {AlertPageQuery, AlertSource} from '@/config/types/dashboard';
|
||||
import type {PageResult} from '@/config/types';
|
||||
import type {
|
||||
AlertActivityRow,
|
||||
AlertEventRow,
|
||||
AlertPageQuery,
|
||||
AlertSource,
|
||||
AlertStormRow,
|
||||
AlertTopSourceRow,
|
||||
AlertTrendRow,
|
||||
AlertTypeRow,
|
||||
AgingBacklog,
|
||||
ChangeImpact,
|
||||
CorrelationPair,
|
||||
FlappingSource,
|
||||
MttaTrend,
|
||||
PeerDeviation,
|
||||
} from '@/config/types/dashboard';
|
||||
|
||||
export const alertPage = (body: AlertPageQuery = {}) => httpPost(`${API_DATA_BASE}/dashboard/alert/page`, body);
|
||||
export const alertPage = (body: AlertPageQuery = {}) =>
|
||||
httpPost<R<PageResult<AlertEventRow>>>(`${API_DATA_BASE}/dashboard/alert/page`, body);
|
||||
|
||||
export const alertConfirm = (source: AlertSource, id: string) =>
|
||||
httpPost(`${API_DATA_BASE}/dashboard/alert/confirm`, undefined, {params: {source, id}});
|
||||
httpPost<R<string>>(`${API_DATA_BASE}/dashboard/alert/confirm`, undefined, {params: {source, id}});
|
||||
|
||||
export const alertUnconfirm = (source: AlertSource, id: string) =>
|
||||
httpPost(`${API_DATA_BASE}/dashboard/alert/unconfirm`, undefined, {params: {source, id}});
|
||||
httpPost<R<string>>(`${API_DATA_BASE}/dashboard/alert/unconfirm`, undefined, {params: {source, id}});
|
||||
|
||||
export const alertBulkConfirm = (items: Array<{ source: AlertSource; id: string }>, confirm: boolean) =>
|
||||
httpPost(`${API_DATA_BASE}/dashboard/alert/bulk_confirm`, {items, confirm});
|
||||
httpPost<R<string>>(`${API_DATA_BASE}/dashboard/alert/bulk_confirm`, {items, confirm});
|
||||
|
||||
export const alertTrend = (days = 30) => httpGet(`${API_DATA_BASE}/dashboard/alert/trend`, {params: {days}});
|
||||
export const alertTrend = (days = 30) =>
|
||||
httpGet<R<AlertTrendRow[]>>(`${API_DATA_BASE}/dashboard/alert/trend`, {params: {days}});
|
||||
|
||||
export const alertTopSources = (days = 30, limit = 10) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/top_sources`, {params: {days, limit}});
|
||||
httpGet<R<AlertTopSourceRow[]>>(`${API_DATA_BASE}/dashboard/alert/top_sources`, {params: {days, limit}});
|
||||
|
||||
export const alertActivity = (days = 7) => httpGet(`${API_DATA_BASE}/dashboard/alert/activity`, {params: {days}});
|
||||
export const alertActivity = (days = 7) =>
|
||||
httpGet<R<AlertActivityRow[]>>(`${API_DATA_BASE}/dashboard/alert/activity`, {params: {days}});
|
||||
|
||||
export const alertTypeDistribution = (days = 30) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/type_distribution`, {params: {days}});
|
||||
httpGet<R<AlertTypeRow[]>>(`${API_DATA_BASE}/dashboard/alert/type_distribution`, {params: {days}});
|
||||
|
||||
export const alertStormSources = (hours = 1, minCount = 10, limit = 10) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/storm_sources`, {params: {hours, min_count: minCount, limit}});
|
||||
httpGet<R<AlertStormRow[]>>(`${API_DATA_BASE}/dashboard/alert/storm_sources`, {params: {hours, min_count: minCount, limit}});
|
||||
|
||||
export const alertFlapping = (hours = 6, minCount = 5, limit = 20) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/flapping`, {params: {hours, min_count: minCount, limit}});
|
||||
httpGet<R<FlappingSource[]>>(`${API_DATA_BASE}/dashboard/alert/flapping`, {params: {hours, min_count: minCount, limit}});
|
||||
|
||||
export const alertCorrelation = (hours = 24, windowSec = 30, limit = 15) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/correlation`, {params: {hours, window_sec: windowSec, limit}});
|
||||
httpGet<R<CorrelationPair[]>>(`${API_DATA_BASE}/dashboard/alert/correlation`, {params: {hours, window_sec: windowSec, limit}});
|
||||
|
||||
export const alertPeerDeviation = (days = 7) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/peer_deviation`, {params: {days}});
|
||||
httpGet<R<PeerDeviation[]>>(`${API_DATA_BASE}/dashboard/alert/peer_deviation`, {params: {days}});
|
||||
|
||||
export const alertAging = () => httpGet(`${API_DATA_BASE}/dashboard/alert/aging`);
|
||||
export const alertAging = () => httpGet<R<AgingBacklog>>(`${API_DATA_BASE}/dashboard/alert/aging`);
|
||||
|
||||
export const alertMtta = (days = 30) => httpGet(`${API_DATA_BASE}/dashboard/alert/mtta`, {params: {days}});
|
||||
export const alertMtta = (days = 30) =>
|
||||
httpGet<R<MttaTrend[]>>(`${API_DATA_BASE}/dashboard/alert/mtta`, {params: {days}});
|
||||
|
||||
export const alertChangeImpact = (days = 30, limit = 30) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/alert/change_impact`, {params: {days, limit}});
|
||||
httpGet<R<ChangeImpact[]>>(`${API_DATA_BASE}/dashboard/alert/change_impact`, {params: {days, limit}});
|
||||
|
||||
@@ -18,10 +18,15 @@
|
||||
import {httpGet} from '@/api/common';
|
||||
import {API_DATA_BASE, API_MANAGER_BASE} from '@/config/constant/api';
|
||||
import type {
|
||||
AlertActivityRow,
|
||||
DailyGrowthSummary,
|
||||
DeviceStats,
|
||||
DriverStats,
|
||||
Granularity,
|
||||
StatsCountBucket,
|
||||
StatsTimeBucket,
|
||||
StatsTodaySummary,
|
||||
StreamRow,
|
||||
TimeRangeParams,
|
||||
TopDimension,
|
||||
} from '@/config/types/dashboard';
|
||||
@@ -32,23 +37,24 @@ export const statsTimeseries = (params: TimeRangeParams & { granularity?: Granul
|
||||
httpGet<R<StatsTimeBucket[]>>(`${API_DATA_BASE}/dashboard/stats/timeseries`, {params: timeRangeParams(params)});
|
||||
|
||||
export const statsTop = (params: TimeRangeParams & { dimension?: TopDimension; limit?: number } = {}) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/top`, {params: timeRangeParams(params)});
|
||||
httpGet<R<StatsCountBucket[]>>(`${API_DATA_BASE}/dashboard/top`, {params: timeRangeParams(params)});
|
||||
|
||||
export const streamLatest = (size = 20) => httpGet(`${API_DATA_BASE}/dashboard/stream`, {params: {size}});
|
||||
export const streamLatest = (size = 20) =>
|
||||
httpGet<R<StreamRow[]>>(`${API_DATA_BASE}/dashboard/stream`, {params: {size}});
|
||||
|
||||
export const statsLatency = (params: TimeRangeParams = {rangeKey: '24h'}) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/stats/latency`, {params: timeRangeParams(params)});
|
||||
httpGet<R<StatsCountBucket[]>>(`${API_DATA_BASE}/dashboard/stats/latency`, {params: timeRangeParams(params)});
|
||||
|
||||
export const statsActivity = (params: TimeRangeParams = {rangeKey: '7d'}) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/stats/activity`, {params: timeRangeParams(params)});
|
||||
httpGet<R<AlertActivityRow[]>>(`${API_DATA_BASE}/dashboard/stats/activity`, {params: timeRangeParams(params)});
|
||||
|
||||
export const dailyGrowth = (days = 7) =>
|
||||
httpGet<R<DailyGrowthSummary>>(`${API_MANAGER_BASE}/dashboard/growth`, {params: {days}});
|
||||
|
||||
export const driverStats = () => httpGet(`${API_MANAGER_BASE}/dashboard/driver/stats`);
|
||||
export const driverStats = () => httpGet<R<DriverStats>>(`${API_MANAGER_BASE}/dashboard/driver/stats`);
|
||||
|
||||
export const deviceStats = (topN = 10) =>
|
||||
httpGet(`${API_MANAGER_BASE}/dashboard/device/stats`, {params: {top_n: topN}});
|
||||
httpGet<R<DeviceStats>>(`${API_MANAGER_BASE}/dashboard/device/stats`, {params: {top_n: topN}});
|
||||
|
||||
const timeRangeParams = <T extends TimeRangeParams>(params: T) => {
|
||||
const {rangeKey, rangeHours, ...rest} = params;
|
||||
|
||||
@@ -17,19 +17,21 @@
|
||||
|
||||
import {httpGet} from '@/api/common';
|
||||
import {API_DATA_BASE} from '@/config/constant/api';
|
||||
import type {AlertStatsSummary} from '@/config/types/dashboard';
|
||||
import type {AlertEventRow, AlertStatsSummary, CoverageGap, ProtocolHealth, SilentSource} from '@/config/types/dashboard';
|
||||
|
||||
export const alertStats = () => httpGet<R<AlertStatsSummary>>(`${API_DATA_BASE}/dashboard/alert/stats`);
|
||||
|
||||
export const alertLatest = (size = 10) => httpGet(`${API_DATA_BASE}/dashboard/alert/latest`, {params: {size}});
|
||||
export const alertLatest = (size = 10) =>
|
||||
httpGet<R<AlertEventRow[]>>(`${API_DATA_BASE}/dashboard/alert/latest`, {params: {size}});
|
||||
|
||||
export const systemHealth = () => httpGet(`${API_DATA_BASE}/dashboard/system/health`);
|
||||
export const systemHealth = () => httpGet<R<Record<string, unknown>>>(`${API_DATA_BASE}/dashboard/system/health`);
|
||||
|
||||
export const protocolHealth = () => httpGet(`${API_DATA_BASE}/dashboard/protocol/health`);
|
||||
export const protocolHealth = () => httpGet<R<ProtocolHealth[]>>(`${API_DATA_BASE}/dashboard/protocol/health`);
|
||||
|
||||
export const silentSources = (baselineDays = 7, silentMinutes = 15, limit = 50) =>
|
||||
httpGet(`${API_DATA_BASE}/dashboard/silent/sources`, {
|
||||
httpGet<R<SilentSource[]>>(`${API_DATA_BASE}/dashboard/silent/sources`, {
|
||||
params: {baseline_days: baselineDays, silent_minutes: silentMinutes, limit},
|
||||
});
|
||||
|
||||
export const coverageGap = (limit = 100) => httpGet(`${API_DATA_BASE}/dashboard/coverage/gap`, {params: {limit}});
|
||||
export const coverageGap = (limit = 100) =>
|
||||
httpGet<R<CoverageGap>>(`${API_DATA_BASE}/dashboard/coverage/gap`, {params: {limit}});
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
import {httpGet} from '@/api/common';
|
||||
import {API_MANAGER_BASE} from '@/config/constant/api';
|
||||
import type {TopologyMode} from '@/config/types/dashboard';
|
||||
import type {TopologyMode, TopologyResponse} from '@/config/types/dashboard';
|
||||
|
||||
export const topology = (params: { mode?: TopologyMode; rangeKey?: string } = {}) =>
|
||||
httpGet(`${API_MANAGER_BASE}/dashboard/topology`, {
|
||||
httpGet<R<TopologyResponse>>(`${API_MANAGER_BASE}/dashboard/topology`, {
|
||||
params: {
|
||||
...(params.mode ? {mode: params.mode} : {}),
|
||||
...(params.rangeKey ? {range_key: params.rangeKey} : {}),
|
||||
|
||||
@@ -30,4 +30,4 @@ export const listDriver = <T = R<PageResult<DriverRecord>>>(query: PageQuery) =>
|
||||
httpPost<T>(`${API_MANAGER_BASE}/driver/list`, query);
|
||||
|
||||
export const listDriverStatus = (query: Record<string, unknown>) =>
|
||||
httpPost(`${API_DATA_BASE}/driver/status/list`, query);
|
||||
httpPost<R<Record<string, string>>>(`${API_DATA_BASE}/driver/status/list`, query);
|
||||
|
||||
+16
-16
@@ -20,57 +20,57 @@ import {API_MANAGER_BASE} from '@/config/constant/api';
|
||||
import type {CommandInfoForm, DriverInfoForm, EventInfoForm, PointInfoForm} from '@/config/types/manager';
|
||||
|
||||
export const addDriverInfo = (driverInfo: DriverInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/driver_attribute_config/add`, driverInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/driver_attribute_config/add`, driverInfo);
|
||||
|
||||
export const updateDriverInfo = (driverInfo: DriverInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/driver_attribute_config/update`, driverInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/driver_attribute_config/update`, driverInfo);
|
||||
|
||||
export const getDriverInfoByDeviceIdAndAttributeId = (deviceId: string, attributeId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/driver_attribute_config/get_by_device_id_and_attribute_id`, {
|
||||
httpGet<R<DriverInfoForm>>(`${API_MANAGER_BASE}/driver_attribute_config/get_by_device_id_and_attribute_id`, {
|
||||
params: {device_id: deviceId, attribute_id: attributeId},
|
||||
});
|
||||
|
||||
export const listDriverInfoByDeviceId = (deviceId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/driver_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
httpGet<R<DriverInfoForm[]>>(`${API_MANAGER_BASE}/driver_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
|
||||
export const addPointInfo = (pointInfo: PointInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/point_attribute_config/add`, pointInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/point_attribute_config/add`, pointInfo);
|
||||
|
||||
export const updatePointInfo = (pointInfo: PointInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/point_attribute_config/update`, pointInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/point_attribute_config/update`, pointInfo);
|
||||
|
||||
export const listPointInfoByDeviceIdAndPointId = (deviceId: string, pointId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/point_attribute_config/list_by_device_id_and_point_id`, {
|
||||
httpGet<R<PointInfoForm[]>>(`${API_MANAGER_BASE}/point_attribute_config/list_by_device_id_and_point_id`, {
|
||||
params: {device_id: deviceId, point_id: pointId},
|
||||
});
|
||||
|
||||
export const listPointInfoByDeviceId = (deviceId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/point_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
httpGet<R<PointInfoForm[]>>(`${API_MANAGER_BASE}/point_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
|
||||
export const addCommandInfo = (commandInfo: CommandInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/command_attribute_config/add`, commandInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/command_attribute_config/add`, commandInfo);
|
||||
|
||||
export const updateCommandInfo = (commandInfo: CommandInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/command_attribute_config/update`, commandInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/command_attribute_config/update`, commandInfo);
|
||||
|
||||
export const listCommandInfoByDeviceIdAndCommandId = (deviceId: string, commandId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/command_attribute_config/list_by_device_id_and_command_id`, {
|
||||
httpGet<R<CommandInfoForm[]>>(`${API_MANAGER_BASE}/command_attribute_config/list_by_device_id_and_command_id`, {
|
||||
params: {device_id: deviceId, command_id: commandId},
|
||||
});
|
||||
|
||||
export const listCommandInfoByDeviceId = (deviceId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/command_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
httpGet<R<CommandInfoForm[]>>(`${API_MANAGER_BASE}/command_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
|
||||
export const addEventInfo = (eventInfo: EventInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/event_attribute_config/add`, eventInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/event_attribute_config/add`, eventInfo);
|
||||
|
||||
export const updateEventInfo = (eventInfo: EventInfoForm) =>
|
||||
httpPost(`${API_MANAGER_BASE}/event_attribute_config/update`, eventInfo);
|
||||
httpPost<R<string>>(`${API_MANAGER_BASE}/event_attribute_config/update`, eventInfo);
|
||||
|
||||
export const listEventInfoByDeviceIdAndEventId = (deviceId: string, eventId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/event_attribute_config/list_by_device_id_and_event_id`, {
|
||||
httpGet<R<EventInfoForm[]>>(`${API_MANAGER_BASE}/event_attribute_config/list_by_device_id_and_event_id`, {
|
||||
params: {device_id: deviceId, event_id: eventId},
|
||||
});
|
||||
|
||||
export const listEventInfoByDeviceId = (deviceId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/event_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
httpGet<R<EventInfoForm[]>>(`${API_MANAGER_BASE}/event_attribute_config/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
|
||||
@@ -27,7 +27,7 @@ export const addLocalCredential = crud.add;
|
||||
export const deleteLocalCredential = crud.delete;
|
||||
|
||||
export const resetLocalCredentialPassword = (id: string, password: string) =>
|
||||
httpPost(`${API_LOCAL_CREDENTIAL_BASE}/reset_password`, undefined, {params: {id, password}});
|
||||
httpPost<R<string>>(`${API_LOCAL_CREDENTIAL_BASE}/reset_password`, undefined, {params: {id, password}});
|
||||
|
||||
export const checkLoginNameAvailable = (name: string) =>
|
||||
httpGet<R<boolean>>(`${API_LOCAL_CREDENTIAL_BASE}/check`, {params: {name}});
|
||||
|
||||
@@ -33,4 +33,5 @@ export const getMenuById = crud.getById;
|
||||
|
||||
export const listMenu = crud.list;
|
||||
|
||||
export const listMenuTree = (query: PageQuery = {}) => httpPost(`${API_AUTH_BASE}/menu/list_tree`, query);
|
||||
export const listMenuTree = (query: PageQuery = {}) =>
|
||||
httpPost<R<MenuRecord[]>>(`${API_AUTH_BASE}/menu/list_tree`, query);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import {httpGet, httpPost} from '@/api/common';
|
||||
import {createCrudApi} from '@/api/factory';
|
||||
import {API_DATA_BASE, API_MANAGER_BASE} from '@/config/constant/api';
|
||||
import type {PageResult} from '@/config/types';
|
||||
import type {PointForm, PointRecord} from '@/config/types/manager';
|
||||
|
||||
const crud = createCrudApi<PointForm, PointRecord>({base: API_MANAGER_BASE, entity: 'point'});
|
||||
@@ -35,27 +36,28 @@ export const listPoint = crud.list;
|
||||
export const listPointByIds = (pointIds: string[]) =>
|
||||
httpPost<R<Record<string, PointRecord>>>(`${API_MANAGER_BASE}/point/list_by_ids`, pointIds);
|
||||
|
||||
export const listPointUnit = (pointIds: string[]) => httpPost(`${API_MANAGER_BASE}/point/unit`, pointIds);
|
||||
export const listPointUnit = (pointIds: string[]) =>
|
||||
httpPost<R<Record<string, string>>>(`${API_MANAGER_BASE}/point/unit`, pointIds);
|
||||
|
||||
export const listPointByProfileId = (profileId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/point/list_by_profile_id`, {params: {profile_id: profileId}});
|
||||
httpGet<R<PointRecord[]>>(`${API_MANAGER_BASE}/point/list_by_profile_id`, {params: {profile_id: profileId}});
|
||||
|
||||
export const listPointByDeviceId = (deviceId: string) =>
|
||||
httpGet(`${API_MANAGER_BASE}/point/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
httpGet<R<PointRecord[]>>(`${API_MANAGER_BASE}/point/list_by_device_id`, {params: {device_id: deviceId}});
|
||||
|
||||
export const getPointValueLatest = (pointValue: Record<string, unknown>) =>
|
||||
httpPost(`${API_DATA_BASE}/point_value/latest`, pointValue);
|
||||
httpPost<R<PageResult<Record<string, unknown>>>>(`${API_DATA_BASE}/point_value/latest`, pointValue);
|
||||
|
||||
export const listPointValue = (pointValue: Record<string, unknown>) =>
|
||||
httpPost(`${API_DATA_BASE}/point_value/list`, pointValue);
|
||||
httpPost<R<PageResult<Record<string, unknown>>>>(`${API_DATA_BASE}/point_value/list`, pointValue);
|
||||
|
||||
export const listPointValueHistory = (deviceId: string, pointId: string, count = 100) =>
|
||||
httpGet(`${API_DATA_BASE}/point_value/list_history_by_device_id_and_point_id`, {
|
||||
httpGet<R<string[]>>(`${API_DATA_BASE}/point_value/list_history_by_device_id_and_point_id`, {
|
||||
params: {device_id: deviceId, point_id: pointId, count}
|
||||
});
|
||||
|
||||
export const readPointValue = (pointValueReadVO: Record<string, unknown>) =>
|
||||
httpPost(`${API_DATA_BASE}/point_command/read`, pointValueReadVO);
|
||||
httpPost<R<string>>(`${API_DATA_BASE}/point_command/read`, pointValueReadVO);
|
||||
|
||||
export const writePointValue = (pointValueWriteVO: Record<string, unknown>) =>
|
||||
httpPost(`${API_DATA_BASE}/point_command/write`, pointValueWriteVO);
|
||||
httpPost<R<string>>(`${API_DATA_BASE}/point_command/write`, pointValueWriteVO);
|
||||
|
||||
@@ -29,10 +29,10 @@ export const deleteServiceAccount = crud.delete;
|
||||
export const updateServiceAccount = crud.update;
|
||||
|
||||
export const enableServiceAccount = (id: string) =>
|
||||
httpPost(`${API_SERVICE_ACCOUNT_BASE}/enable`, undefined, {params: {id}});
|
||||
httpPost<R<string>>(`${API_SERVICE_ACCOUNT_BASE}/enable`, undefined, {params: {id}});
|
||||
|
||||
export const disableServiceAccount = (id: string) =>
|
||||
httpPost(`${API_SERVICE_ACCOUNT_BASE}/disable`, undefined, {params: {id}});
|
||||
httpPost<R<string>>(`${API_SERVICE_ACCOUNT_BASE}/disable`, undefined, {params: {id}});
|
||||
|
||||
export const getServiceAccountById = crud.getById;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {httpPost} from '@/api/common';
|
||||
import {API_AUTH_BASE} from '@/config/constant/api';
|
||||
import type {Login} from '@/config/types';
|
||||
|
||||
export const generateSalt = (login: Login) => httpPost(`${API_AUTH_BASE}/token/salt`, login);
|
||||
export const generateSalt = (login: Login) => httpPost<R<string>>(`${API_AUTH_BASE}/token/salt`, login);
|
||||
|
||||
export const generateToken = (login: Login) => httpPost(`${API_AUTH_BASE}/token/generate`, login);
|
||||
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
import type {PropType} from 'vue';
|
||||
import {computed} from 'vue';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
|
||||
/**
|
||||
* Presets the frontend sends as {@code rangeKey} — kept in sync with
|
||||
* backend {@link TimeRangeKeyEnum}. The empty-string sentinel is the
|
||||
* "no filter" choice, rendered only when {@code includeAll} is true.
|
||||
*/
|
||||
export type RangeKey = '' | 'today' | '24h' | '7d' | '30d';
|
||||
export type {RangeKey};
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
||||
@@ -22,8 +22,10 @@ import {useRouter} from 'vue-router';
|
||||
|
||||
import type {Order, PageQuery} from '@/config/types';
|
||||
import type {EntityColumnConfig, EntityListConfig, EntityOption} from '@/config/types/entityList';
|
||||
import {ENUM_TAG_TYPE_MAP} from '@/config/constant/enums';
|
||||
import {timestampLabel} from '@/utils/dateUtil';
|
||||
import {prettyJson} from '@/utils/jsonUtil';
|
||||
import {logger} from '@/utils/log';
|
||||
import {successMessage} from '@/utils/notificationUtil';
|
||||
import {cleanSearchParams, resetSearchForm} from '@/utils/searchParamUtil';
|
||||
|
||||
@@ -160,7 +162,9 @@ export const useEntityListPage = (rawConfig: EntityListConfig) => {
|
||||
if (config.value.mode === 'tree') {
|
||||
state.rows = (res.data as Record<string, any>[]) || [];
|
||||
} else {
|
||||
const page = res.data || {};
|
||||
// Config-driven boundary: the envelope's data shape depends on the
|
||||
// concrete config.list implementation, so narrow it once here.
|
||||
const page = (res.data ?? {}) as { records?: Record<string, any>[]; total?: number };
|
||||
state.rows = page.records || [];
|
||||
state.page.total = Number(page.total || 0);
|
||||
}
|
||||
@@ -269,7 +273,10 @@ export const useEntityListPage = (rawConfig: EntityListConfig) => {
|
||||
const submit = () => {
|
||||
const addRequest = config.value.add;
|
||||
const updateRequest = config.value.update;
|
||||
if (!addRequest || !updateRequest) return;
|
||||
if (!addRequest || !updateRequest) {
|
||||
logger.warn('Entity list action not configured', {add: Boolean(addRequest), update: Boolean(updateRequest)});
|
||||
return;
|
||||
}
|
||||
formRef.value?.validate((valid) => {
|
||||
if (!valid) return;
|
||||
let data: Record<string, unknown>;
|
||||
@@ -317,28 +324,7 @@ export const useEntityListPage = (rawConfig: EntityListConfig) => {
|
||||
|
||||
const tagType = (value: unknown) => {
|
||||
const text = String(value ?? '');
|
||||
if (
|
||||
text === 'ENABLE' ||
|
||||
text === 'SUCCESS' ||
|
||||
text === 'NORMAL' ||
|
||||
text === 'AUTO' ||
|
||||
text === 'LOW' ||
|
||||
text === 'ACTIVE'
|
||||
)
|
||||
return 'success';
|
||||
if (
|
||||
text === 'DISABLE' ||
|
||||
text === 'FAILED' ||
|
||||
text === 'FAILURE' ||
|
||||
text === 'ERROR' ||
|
||||
text === 'DENIED' ||
|
||||
text === 'FIRING' ||
|
||||
text === 'HIGH'
|
||||
)
|
||||
return 'danger';
|
||||
if (text === 'PENDING' || text === 'RETRYING' || text === 'RECOVERED' || text === 'MEDIUM' || text === 'SUSPENDED')
|
||||
return 'warning';
|
||||
return 'info';
|
||||
return ENUM_TAG_TYPE_MAP[text] || 'info';
|
||||
};
|
||||
|
||||
const formatCell = (row: Record<string, any>, column: EntityColumnConfig) => {
|
||||
|
||||
Vendored
+1
-1
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Standard response envelope.
|
||||
*/
|
||||
declare type R<T = any> = {
|
||||
declare type R<T = unknown> = {
|
||||
ok: boolean;
|
||||
code: string;
|
||||
message: string;
|
||||
|
||||
@@ -217,3 +217,34 @@ export const MCP_RISK_LEVEL_OPTIONS: EnumOption[] = [
|
||||
{label: MCP_RISK_LEVELS.MEDIUM, value: MCP_RISK_LEVELS.MEDIUM},
|
||||
{label: MCP_RISK_LEVELS.HIGH, value: MCP_RISK_LEVELS.HIGH},
|
||||
];
|
||||
|
||||
// Backend: PrincipalTypeEnum
|
||||
export const PRINCIPAL_TYPE_OPTIONS: EnumOption[] = [
|
||||
{label: 'USER', value: 'USER'},
|
||||
{label: 'SERVICE_ACCOUNT', value: 'SERVICE_ACCOUNT'},
|
||||
{label: 'SYSTEM', value: 'SYSTEM'},
|
||||
];
|
||||
|
||||
/**
|
||||
* Enum name → el-tag type mapping (kept in sync with backend enums).
|
||||
*/
|
||||
export const ENUM_TAG_TYPE_MAP: Record<string, 'primary' | 'success' | 'info' | 'warning' | 'danger'> = {
|
||||
ENABLE: 'success',
|
||||
SUCCESS: 'success',
|
||||
NORMAL: 'success',
|
||||
AUTO: 'success',
|
||||
LOW: 'success',
|
||||
ACTIVE: 'success',
|
||||
DISABLE: 'danger',
|
||||
FAILED: 'danger',
|
||||
FAILURE: 'danger',
|
||||
ERROR: 'danger',
|
||||
DENIED: 'danger',
|
||||
FIRING: 'danger',
|
||||
HIGH: 'danger',
|
||||
PENDING: 'warning',
|
||||
RETRYING: 'warning',
|
||||
RECOVERED: 'warning',
|
||||
MEDIUM: 'warning',
|
||||
SUSPENDED: 'warning',
|
||||
};
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
* Alarm / notification data-domain types.
|
||||
*/
|
||||
|
||||
export type AlarmTargetTypeFlag = 'POINT' | 'DEVICE' | 'DRIVER' | string;
|
||||
export type NotifyChannelTypeFlag = 'FEISHU_BOT' | 'WEBHOOK' | 'EMAIL' | string;
|
||||
export type RuleStateFlag = 'NORMAL' | 'FIRING' | 'RECOVERED' | string;
|
||||
export type NotifyHistoryStatusFlag = 'PENDING' | 'SUCCESS' | 'FAILED' | 'RETRYING' | 'SKIPPED' | string;
|
||||
export type AutoConfirmFlag = 'AUTO' | 'MANUAL' | string;
|
||||
export type EnableFlag = 'ENABLE' | 'DISABLE' | string;
|
||||
export type AlarmTargetTypeFlag = 'POINT' | 'DEVICE' | 'DRIVER';
|
||||
export type NotifyChannelTypeFlag = 'FEISHU_BOT' | 'WEBHOOK' | 'EMAIL';
|
||||
export type RuleStateFlag = 'NORMAL' | 'FIRING' | 'RECOVERED';
|
||||
export type NotifyHistoryStatusFlag = 'PENDING' | 'SUCCESS' | 'FAILED' | 'RETRYING' | 'SKIPPED';
|
||||
export type AutoConfirmFlag = 'AUTO' | 'MANUAL';
|
||||
export type EnableFlag = 'ENABLE' | 'DISABLE';
|
||||
|
||||
export interface StructuredExt<T = Record<string, unknown>> {
|
||||
type?: string;
|
||||
|
||||
@@ -31,7 +31,7 @@ export interface Attribute {
|
||||
name: string;
|
||||
attributeName: string;
|
||||
attributeCode: string;
|
||||
attributeTypeFlag?: 'STRING' | 'BYTE' | 'SHORT' | 'INT' | 'LONG' | 'FLOAT' | 'DOUBLE' | 'BOOLEAN' | string;
|
||||
attributeTypeFlag?: 'STRING' | 'BYTE' | 'SHORT' | 'INT' | 'LONG' | 'FLOAT' | 'DOUBLE' | 'BOOLEAN';
|
||||
defaultValue?: string;
|
||||
remark?: string;
|
||||
attributeExt?: Record<string, unknown>;
|
||||
|
||||
@@ -16,14 +16,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dashboard / event-overview payload shapes. Keeping these here (not in
|
||||
* api/dashboard.ts alongside the fetch functions) follows the project-wide
|
||||
* convention described in CLAUDE.md: "config/entity/" is the interface-only
|
||||
* module, always imported with `import type` under verbatimModuleSyntax.
|
||||
*
|
||||
* <p>Shared primitives (AlertSource, RangeKey) live here too so every
|
||||
* card / API wrapper points at the same union instead of re-declaring
|
||||
* `'device' | 'driver'` inline.</p>
|
||||
* Dashboard / event-overview payload shapes. Kept here (not inside the API
|
||||
* wrappers) so every card and `api/dashboard/*` module points at one source
|
||||
* of truth, always imported with `import type` under verbatimModuleSyntax.
|
||||
*/
|
||||
|
||||
/** Three canonical alarm sources — point-level, device-level, driver-level. */
|
||||
@@ -215,3 +210,73 @@ export interface DailyGrowthSummary {
|
||||
pointDailyCounts: number[];
|
||||
profileDailyCounts: number[];
|
||||
}
|
||||
|
||||
// ---- Alert overview cards (previously declared inline in components) ----
|
||||
|
||||
export interface AlertEventRow {
|
||||
id: string;
|
||||
source: AlertSource;
|
||||
sourceId: string;
|
||||
createTime: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface AlertStormRow {
|
||||
source: AlertSource;
|
||||
sourceId: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AlertTypeRow {
|
||||
type: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AlertActivityRow {
|
||||
/** 0..6 = Sun..Sat, matching Postgres EXTRACT(DOW). */
|
||||
dow: number;
|
||||
hour: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AlertTrendRow {
|
||||
date: string;
|
||||
source: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AlertTopSourceRow {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/** Bucket shape shared by statsTop / latency / enable-breakdown endpoints. */
|
||||
export interface StatsCountBucket {
|
||||
entityId?: number;
|
||||
key?: string;
|
||||
bin?: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface StreamRow {
|
||||
deviceId: string;
|
||||
pointId: string;
|
||||
driverId?: string;
|
||||
// driverName / deviceName / pointName are populated server-side via metadata
|
||||
// facades, so the feed renders the full tuple without extra lookups.
|
||||
driverName?: string;
|
||||
deviceName?: string;
|
||||
pointName?: string;
|
||||
}
|
||||
|
||||
export interface DriverStats {
|
||||
byEnable: { key: string; count: number }[];
|
||||
byType: { key: string; count: number }[];
|
||||
byService: { key: string; count: number }[];
|
||||
}
|
||||
|
||||
export interface DeviceStats {
|
||||
byEnable: { key: string; count: number }[];
|
||||
byProfile: { key: string; count: number }[];
|
||||
byDriver: { key: string; count: number }[];
|
||||
}
|
||||
|
||||
@@ -88,6 +88,8 @@ export interface EntityRowAction {
|
||||
key: string;
|
||||
label: string;
|
||||
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
/** When set, the action renders inside el-popconfirm; onClick runs only after confirmation. */
|
||||
popconfirmTitle?: string;
|
||||
onClick: (row: Record<string, any>) => void;
|
||||
}
|
||||
|
||||
@@ -127,10 +129,10 @@ export interface EntityListConfig {
|
||||
/** Builds the submitted payload instead of using the default field assembly. */
|
||||
toPayload?: (form: Record<string, any>) => Record<string, unknown>;
|
||||
|
||||
list: (query: PageQuery) => Promise<R>;
|
||||
add?: (payload: Record<string, unknown>) => Promise<R>;
|
||||
update?: (payload: Record<string, unknown>) => Promise<R>;
|
||||
remove?: (id: string) => Promise<R>;
|
||||
list: (query: PageQuery) => Promise<R<unknown>>;
|
||||
add?: (payload: Record<string, unknown>) => Promise<R<unknown>>;
|
||||
update?: (payload: Record<string, unknown>) => Promise<R<unknown>>;
|
||||
remove?: (id: string) => Promise<R<unknown>>;
|
||||
|
||||
detail?: { routeName: string }; // Detail route; omit to hide the detail action.
|
||||
extraActions?: EntityRowAction[];
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
MessageRecord,
|
||||
NotifyChannelBindRecord,
|
||||
NotifyChannelRecord,
|
||||
NotifyChannelTypeFlag,
|
||||
NotifyHistoryRecord,
|
||||
NotifyRecord,
|
||||
RuleRecord,
|
||||
@@ -40,7 +41,7 @@ const ext = (type: string, content: Record<string, unknown>, version = 1) => ({
|
||||
interface ChannelDef {
|
||||
name: string;
|
||||
code: string;
|
||||
type: string;
|
||||
type: NotifyChannelTypeFlag;
|
||||
credential: string;
|
||||
content: Record<string, unknown>;
|
||||
}
|
||||
@@ -62,7 +63,7 @@ const channelDefs: ChannelDef[] = [
|
||||
{
|
||||
name: '钉钉运维群机器人',
|
||||
code: 'dingtalk-ops',
|
||||
type: 'DINGTALK_BOT',
|
||||
type: 'WEBHOOK',
|
||||
credential: 'dingtalk-ops-bot',
|
||||
content: {
|
||||
signEnabled: true,
|
||||
@@ -193,7 +194,7 @@ const messageDefs: MessageDef[] = [
|
||||
variables: ['severity', 'device', 'point', 'value', 'unit', 'threshold', 'triggerTime'],
|
||||
templates: [
|
||||
{
|
||||
channelType: 'DINGTALK_BOT',
|
||||
channelType: 'WEBHOOK',
|
||||
payloadType: 'MARKDOWN',
|
||||
template: {
|
||||
title: '${severity} ${device} 告警',
|
||||
@@ -335,7 +336,7 @@ const ruleDefs: RuleDef[] = [
|
||||
interface StateDef {
|
||||
ruleIdx: number;
|
||||
entity: string;
|
||||
state: 'FIRING' | 'PENDING' | 'RECOVERED';
|
||||
state: 'FIRING' | 'NORMAL' | 'RECOVERED';
|
||||
fingerprint: string;
|
||||
triggerCount: number;
|
||||
first: string;
|
||||
@@ -376,7 +377,7 @@ const stateDefs: StateDef[] = [
|
||||
{
|
||||
ruleIdx: 2,
|
||||
entity: '5011',
|
||||
state: 'PENDING',
|
||||
state: 'NORMAL',
|
||||
fingerprint: 'fp-0a7b3e5d9c218f64',
|
||||
triggerCount: 0,
|
||||
first: '',
|
||||
@@ -474,7 +475,7 @@ const historyDefs: HistoryDef[] = [
|
||||
retry: 2,
|
||||
time: '2026-08-05T09:30:00',
|
||||
request: {
|
||||
channelType: 'DINGTALK_BOT',
|
||||
channelType: 'WEBHOOK',
|
||||
target: 'https://oapi.dingtalk.com/robot/send?access_token=****',
|
||||
payloadType: 'MARKDOWN'
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import {computed, defineComponent, onBeforeUnmount, onMounted, reactive, watch} from 'vue';
|
||||
import {ElMessageBox} from 'element-plus';
|
||||
import type {FormItemRule, FormRules} from 'element-plus';
|
||||
import {Search} from '@element-plus/icons-vue';
|
||||
|
||||
@@ -49,6 +50,7 @@ import {
|
||||
import type {
|
||||
Attribute,
|
||||
CommandInfoForm,
|
||||
DriverInfoForm,
|
||||
CommandRecord,
|
||||
DeviceRecord,
|
||||
Dictionary,
|
||||
@@ -718,8 +720,8 @@ export default defineComponent({
|
||||
listDriverInfoByDeviceId(reactiveData.id)
|
||||
.then((res) => {
|
||||
const formData: AttributeFormData = reactiveData.driverFormData;
|
||||
res.data.forEach((info: { attributeId: string; id: any; configValue: any }) => {
|
||||
const attributeCode = reactiveData.driverAttributeTable[info.attributeId];
|
||||
res.data.forEach((info: DriverInfoForm) => {
|
||||
const attributeCode = reactiveData.driverAttributeTable[info.attributeId ?? ''];
|
||||
const attribute = reactiveData.driverAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
if (attribute) {
|
||||
formData[attributeCode] = createAttributeFormItem(attribute, info.id, info.configValue);
|
||||
@@ -775,16 +777,14 @@ export default defineComponent({
|
||||
|
||||
return listPointInfoByDeviceId(reactiveData.id)
|
||||
.then((infoRes) => {
|
||||
(infoRes.data || []).forEach(
|
||||
(info: { pointId: string; attributeId: string; id: string; configValue: unknown }) => {
|
||||
const attributeCode = reactiveData.pointAttributeTable[info.attributeId];
|
||||
const attribute = reactiveData.pointAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[info.pointId];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id, info.configValue);
|
||||
}
|
||||
(infoRes.data || []).forEach((info: PointInfoForm) => {
|
||||
const attributeCode = reactiveData.pointAttributeTable[info.attributeId ?? ''];
|
||||
const attribute = reactiveData.pointAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[String(info.pointId ?? '')];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id ?? '', info.configValue);
|
||||
}
|
||||
);
|
||||
});
|
||||
reactiveData.pointInfoData = rows;
|
||||
reactiveData.oldPointInfoData = clone(rows);
|
||||
})
|
||||
@@ -839,16 +839,14 @@ export default defineComponent({
|
||||
|
||||
return listCommandInfoByDeviceId(reactiveData.id)
|
||||
.then((infoRes) => {
|
||||
(infoRes.data || []).forEach(
|
||||
(info: { commandId: string; attributeId: string; id: string; configValue: unknown }) => {
|
||||
const attributeCode = reactiveData.commandAttributeTable[info.attributeId];
|
||||
const attribute = reactiveData.commandAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[String(info.commandId)];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id, info.configValue);
|
||||
}
|
||||
(infoRes.data || []).forEach((info: CommandInfoForm) => {
|
||||
const attributeCode = reactiveData.commandAttributeTable[info.attributeId ?? ''];
|
||||
const attribute = reactiveData.commandAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[String(info.commandId ?? '')];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id ?? '', info.configValue);
|
||||
}
|
||||
);
|
||||
});
|
||||
reactiveData.commandInfoData = rows;
|
||||
reactiveData.oldCommandInfoData = clone(rows);
|
||||
})
|
||||
@@ -900,16 +898,14 @@ export default defineComponent({
|
||||
|
||||
return listEventInfoByDeviceId(reactiveData.id)
|
||||
.then((infoRes) => {
|
||||
(infoRes.data || []).forEach(
|
||||
(info: { eventId: string; attributeId: string; id: string; configValue: unknown }) => {
|
||||
const attributeCode = reactiveData.eventAttributeTable[info.attributeId];
|
||||
const attribute = reactiveData.eventAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[String(info.eventId)];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id, info.configValue);
|
||||
}
|
||||
(infoRes.data || []).forEach((info: EventInfoForm) => {
|
||||
const attributeCode = reactiveData.eventAttributeTable[info.attributeId ?? ''];
|
||||
const attribute = reactiveData.eventAttributes.find((item) => item.attributeCode === attributeCode);
|
||||
const row = rowTable[String(info.eventId ?? '')];
|
||||
if (row && attribute) {
|
||||
row.attributes[attributeCode] = createPointAttributeCell(attribute, info.id ?? '', info.configValue);
|
||||
}
|
||||
);
|
||||
});
|
||||
reactiveData.eventInfoData = rows;
|
||||
reactiveData.oldEventInfoData = clone(rows);
|
||||
})
|
||||
@@ -1095,8 +1091,13 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
try {
|
||||
const res = cell.id ? await updatePointInfo(payload) : await addPointInfo(payload);
|
||||
cell.id = String(res?.data?.id || cell.id || '');
|
||||
// Backend add/update return R<String> (success code) without the new
|
||||
// id, so there is nothing to read back from res.data.
|
||||
if (cell.id) {
|
||||
await updatePointInfo(payload);
|
||||
} else {
|
||||
await addPointInfo(payload);
|
||||
}
|
||||
cell.originalValue = cell.configValue;
|
||||
cell.dirty = false;
|
||||
} catch (error) {
|
||||
@@ -1212,8 +1213,11 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
try {
|
||||
const res = cell.id ? await updateCommandInfo(payload) : await addCommandInfo(payload);
|
||||
cell.id = String(res?.data?.id || cell.id || '');
|
||||
if (cell.id) {
|
||||
await updateCommandInfo(payload);
|
||||
} else {
|
||||
await addCommandInfo(payload);
|
||||
}
|
||||
cell.originalValue = cell.configValue;
|
||||
cell.dirty = false;
|
||||
} catch (error) {
|
||||
@@ -1327,8 +1331,11 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
try {
|
||||
const res = cell.id ? await updateEventInfo(payload) : await addEventInfo(payload);
|
||||
cell.id = String(res?.data?.id || cell.id || '');
|
||||
if (cell.id) {
|
||||
await updateEventInfo(payload);
|
||||
} else {
|
||||
await addEventInfo(payload);
|
||||
}
|
||||
cell.originalValue = cell.configValue;
|
||||
cell.dirty = false;
|
||||
} catch (error) {
|
||||
@@ -1473,10 +1480,15 @@ export default defineComponent({
|
||||
window.removeEventListener('beforeunload', warnBeforeUnload);
|
||||
});
|
||||
|
||||
onBeforeRouteLeave((_to, _from, next) => {
|
||||
onBeforeRouteLeave(async (_to, _from, next) => {
|
||||
if (totalDirtyCount.value > 0) {
|
||||
const leave = window.confirm('You have unsaved changes. Are you sure you want to leave?');
|
||||
if (!leave) {
|
||||
try {
|
||||
await ElMessageBox.confirm(t('device.edit.unsavedConfirm'), t('common.confirm'), {
|
||||
type: 'warning',
|
||||
confirmButtonText: t('common.confirm'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
});
|
||||
} catch {
|
||||
next(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import {Chart} from '@antv/g2';
|
||||
|
||||
import {statsActivity} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
@@ -57,7 +57,7 @@ import {listDriverByIds} from '@/api/driver';
|
||||
import {listPointByIds} from '@/api/point';
|
||||
import {listProfileByIds} from '@/api/profile';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
|
||||
|
||||
type TabKey = 'deviceStatus' | 'protocol' | 'profile' | 'topDevice' | 'topPoint' | 'topDriver';
|
||||
|
||||
@@ -31,7 +31,7 @@ import {Chart} from '@antv/g2';
|
||||
|
||||
import {statsLatency} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
@@ -96,7 +96,7 @@ import type {
|
||||
TopologyStats,
|
||||
} from '@/config/types/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
|
||||
|
||||
const {t, locale} = useI18n();
|
||||
|
||||
@@ -30,7 +30,7 @@ import {Chart} from '@antv/g2';
|
||||
|
||||
import {statsTimeseries} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
|
||||
import type {RangeKey} from '@/config/types/dashboard';
|
||||
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
|
||||
|
||||
const rangeKey = ref<RangeKey>('24h');
|
||||
|
||||
@@ -92,10 +92,10 @@ export interface AlarmEntityConfig {
|
||||
columns: AlarmColumnConfig[];
|
||||
fields: AlarmFieldConfig[];
|
||||
defaultForm: () => Record<string, unknown>;
|
||||
list: (query: PageQuery) => Promise<R>;
|
||||
add?: (payload: Record<string, unknown>) => Promise<R>;
|
||||
update?: (payload: Record<string, unknown>) => Promise<R>;
|
||||
remove?: (id: string) => Promise<R>;
|
||||
list: (query: PageQuery) => Promise<R<unknown>>;
|
||||
add?: (payload: Record<string, unknown>) => Promise<R<unknown>>;
|
||||
update?: (payload: Record<string, unknown>) => Promise<R<unknown>>;
|
||||
remove?: (id: string) => Promise<R<unknown>>;
|
||||
}
|
||||
|
||||
export const ALARM_DETAIL_ROUTE_MAP: Record<AlarmTabKey, string> = {
|
||||
|
||||
@@ -57,13 +57,7 @@ import {alertStormSources} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import {useEntityNames} from '@/composables/useEntityNames';
|
||||
import {jumpToSourceEvents} from '@/utils/jumpUtil';
|
||||
import type {AlertSource} from '@/config/types/dashboard';
|
||||
|
||||
interface StormRow {
|
||||
source: AlertSource;
|
||||
sourceId: string;
|
||||
count: number;
|
||||
}
|
||||
import type {AlertSource, AlertStormRow} from '@/config/types/dashboard';
|
||||
|
||||
const props = defineProps({
|
||||
limit: {type: Number, default: 10},
|
||||
@@ -94,13 +88,13 @@ const windowKey = ref<WindowKey>('24h');
|
||||
const window = computed(() => WINDOW_SPECS[windowKey.value]);
|
||||
|
||||
const loading = ref(false);
|
||||
const rows = ref<StormRow[]>([]);
|
||||
const rows = ref<AlertStormRow[]>([]);
|
||||
|
||||
const load = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const {hours, minCount} = window.value;
|
||||
const res: { data?: StormRow[] } = await alertStormSources(hours, minCount, props.limit);
|
||||
const res: { data?: AlertStormRow[] } = await alertStormSources(hours, minCount, props.limit);
|
||||
rows.value = res?.data ?? [];
|
||||
await resolveBySource(rows.value);
|
||||
} catch {
|
||||
@@ -113,9 +107,9 @@ const load = async () => {
|
||||
watch(windowKey, load);
|
||||
watch(locale, load);
|
||||
|
||||
const nameFor = (r: StormRow) => nameBySource(r.source, r.sourceId);
|
||||
const nameFor = (r: AlertStormRow) => nameBySource(r.source, r.sourceId);
|
||||
|
||||
const onDrillIn = (row: StormRow) => jumpToSourceEvents(router, row.source, row.sourceId);
|
||||
const onDrillIn = (row: AlertStormRow) => jumpToSourceEvents(router, row.source, row.sourceId);
|
||||
|
||||
const sourceTagType = (s: AlertSource) => (s === 'device' ? 'primary' : s === 'driver' ? 'warning' : 'success');
|
||||
const sourceLabel = (s: AlertSource) => {
|
||||
|
||||
@@ -36,16 +36,12 @@ import {Chart} from '@antv/g2';
|
||||
|
||||
import {alertTypeDistribution} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import type {AlertTypeRow} from '@/config/types/dashboard';
|
||||
|
||||
const {t, locale} = useI18n();
|
||||
|
||||
interface TypeRow {
|
||||
type: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
const rows = ref<TypeRow[]>([]);
|
||||
const rows = ref<AlertTypeRow[]>([]);
|
||||
const chartRef = ref<HTMLElement>();
|
||||
let chart: Chart | undefined;
|
||||
|
||||
@@ -58,7 +54,7 @@ const labelFor = (type: string) => {
|
||||
return translated && translated !== key ? translated : type;
|
||||
};
|
||||
|
||||
const render = (data: TypeRow[]) => {
|
||||
const render = (data: AlertTypeRow[]) => {
|
||||
if (!chartRef.value) return;
|
||||
chart?.destroy();
|
||||
chart = new Chart({container: chartRef.value, autoFit: true});
|
||||
@@ -78,7 +74,7 @@ const render = (data: TypeRow[]) => {
|
||||
const load = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res: { data?: TypeRow[] } = await alertTypeDistribution(30);
|
||||
const res: { data?: AlertTypeRow[] } = await alertTypeDistribution(30);
|
||||
rows.value = res?.data ?? [];
|
||||
await nextTick();
|
||||
if (rows.value.length > 0) render(rows.value);
|
||||
|
||||
@@ -57,26 +57,18 @@ import {useI18n} from 'vue-i18n';
|
||||
import {alertPage} from '@/api/dashboard';
|
||||
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
|
||||
import {useEntityNames} from '@/composables/useEntityNames';
|
||||
import type {AlertSource} from '@/config/types/dashboard';
|
||||
|
||||
interface Row {
|
||||
id: string;
|
||||
source: AlertSource;
|
||||
sourceId: string;
|
||||
createTime: string;
|
||||
message?: string;
|
||||
}
|
||||
import type {AlertEventRow, AlertSource} from '@/config/types/dashboard';
|
||||
|
||||
const {t, locale} = useI18n();
|
||||
const loading = ref(false);
|
||||
const rows = ref<Row[]>([]);
|
||||
const rows = ref<AlertEventRow[]>([]);
|
||||
const {resolveBySource, nameBySource} = useEntityNames();
|
||||
|
||||
const load = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res: { data?: { records?: Row[] } } = await alertPage({confirmFlag: 0, current: 1, size: 5});
|
||||
const data: Row[] = res?.data?.records ?? [];
|
||||
const res: { data?: { records?: AlertEventRow[] } } = await alertPage({confirmFlag: 0, current: 1, size: 5});
|
||||
const data: AlertEventRow[] = res?.data?.records ?? [];
|
||||
rows.value = data;
|
||||
await resolveBySource(data);
|
||||
} catch {
|
||||
@@ -86,7 +78,7 @@ const load = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const nameFor = (r: Row) => nameBySource(r.source, r.sourceId);
|
||||
const nameFor = (r: AlertEventRow) => nameBySource(r.source, r.sourceId);
|
||||
|
||||
const sourceTagType = (s: AlertSource) => (s === 'device' ? 'primary' : s === 'driver' ? 'warning' : 'success');
|
||||
const sourceColor = (s: AlertSource) => (s === 'device' ? '#409eff' : s === 'driver' ? '#e6a23c' : '#67c23a');
|
||||
|
||||
@@ -133,7 +133,7 @@ export const useAlarmEntityPage = (props: AlarmEntityPageProps) => {
|
||||
activeConfig.value
|
||||
.list(query())
|
||||
.then((res: R) => {
|
||||
const page = res.data || {};
|
||||
const page = (res.data ?? {}) as { records?: AlarmEntity[]; total?: number };
|
||||
state.rows = page.records || [];
|
||||
state.page.total = Number(page.total || 0);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user