refactor!(web): parse identifiers as strings and drop json-bigint

BREAKING CHANGE: frontend identifier types converge to string and the json-bigint dependency is removed; the backend now emits string ids.
This commit is contained in:
pnoker
2026-08-17 21:37:49 +08:00
parent c87f31be9a
commit b02d8f81fc
30 changed files with 97 additions and 147 deletions
+2 -1
View File
@@ -54,6 +54,7 @@ iot-dc3/
├── dc3-coverage/ aggregate JaCoCo report and absolute coverage gate
├── dc3-e2e/ Testcontainers-backed end-to-end tests
├── dc3/ compose files, environment files, scripts, and generated release notes
├── docs/design/ design documents (e.g. `mq-abstraction.md` — proposed pluggable broker port)
├── Makefile preferred backend/container command entrypoint
└── .mvn/settings.xml local Maven mirror configuration
```
@@ -213,7 +214,7 @@ Key rules:
- `verbatimModuleSyntax` is enabled. Use `import type` for every type-only import; Vue components, functions, and icons
remain normal value imports.
- Use `<Entity>Form` for create/update payloads and `<Entity>Record` for read responses.
- Represent Java 64-bit IDs as strings and preserve the existing JSONBigInt handling.
- Represent Java 64-bit IDs as strings. The backend emits identifiers as JSON strings on the HTTP contract, so standard JSON parsing (no JSONBigInt) is sufficient.
- API wrappers mirror backend cardinality: `getXxx` for one value, `listXxx` for collections/maps/pages, and
`addXxx`/`updateXxx`/`deleteXxx` for mutations.
- Reuse CRUD helpers from `src/api/common.ts` and API bases from `src/config/constant/api.ts`; keep API wrappers thin.
-2
View File
@@ -64,7 +64,6 @@
"highlight.js": "^11.11.1",
"js-base64": "^3.9.1",
"js-cookie": "^3.0.8",
"json-bigint": "^1.0.0",
"marked": "^18.0.6",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
@@ -78,7 +77,6 @@
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.62.0",
"@types/js-cookie": "^3.0.6",
"@types/json-bigint": "^1.0.4",
"@types/node": "^26.1.1",
"@types/nprogress": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^8.64.0",
-23
View File
@@ -45,9 +45,6 @@ importers:
js-cookie:
specifier: ^3.0.8
version: 3.0.8
json-bigint:
specifier: ^1.0.0
version: 1.0.0
marked:
specifier: ^18.0.6
version: 18.0.6
@@ -82,9 +79,6 @@ importers:
'@types/js-cookie':
specifier: ^3.0.6
version: 3.0.6
'@types/json-bigint':
specifier: ^1.0.4
version: 1.0.4
'@types/node':
specifier: ^26.1.1
version: 26.1.1
@@ -774,9 +768,6 @@ packages:
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
'@types/json-bigint@1.0.4':
resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -1185,9 +1176,6 @@ packages:
bidi-js@1.0.3:
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
bignumber.js@9.0.0:
resolution: {integrity: sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==}
birpc@2.6.1:
resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==}
@@ -1841,9 +1829,6 @@ packages:
engines: {node: '>=6'}
hasBin: true
json-bigint@1.0.0:
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -3418,8 +3403,6 @@ snapshots:
'@types/jsesc@2.5.1': {}
'@types/json-bigint@1.0.4': {}
'@types/json-schema@7.0.15': {}
'@types/lodash-es@4.17.12':
@@ -3960,8 +3943,6 @@ snapshots:
dependencies:
require-from-string: 2.0.2
bignumber.js@9.0.0: {}
birpc@2.6.1: {}
boolbase@1.0.0: {}
@@ -4610,10 +4591,6 @@ snapshots:
jsesc@3.1.0: {}
json-bigint@1.0.0:
dependencies:
bignumber.js: 9.0.0
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
+3 -3
View File
@@ -21,13 +21,13 @@ import type {AlertPageQuery, AlertSource} from '@/config/types/dashboard';
export const alertPage = (body: AlertPageQuery = {}) => httpPost(`${API_DATA_BASE}/dashboard/alert/page`, body);
export const alertConfirm = (source: AlertSource, id: string | number) =>
export const alertConfirm = (source: AlertSource, id: string) =>
httpPost(`${API_DATA_BASE}/dashboard/alert/confirm`, undefined, {params: {source, id}});
export const alertUnconfirm = (source: AlertSource, id: string | number) =>
export const alertUnconfirm = (source: AlertSource, id: string) =>
httpPost(`${API_DATA_BASE}/dashboard/alert/unconfirm`, undefined, {params: {source, id}});
export const alertBulkConfirm = (items: Array<{ source: AlertSource; id: string | number }>, confirm: boolean) =>
export const alertBulkConfirm = (items: Array<{ source: AlertSource; id: string }>, confirm: boolean) =>
httpPost(`${API_DATA_BASE}/dashboard/alert/bulk_confirm`, {items, confirm});
export const alertTrend = (days = 30) => httpGet(`${API_DATA_BASE}/dashboard/alert/trend`, {params: {days}});
@@ -801,7 +801,7 @@ const handlePromptClick = (item: AssistantPromptItem) => {
draft.value = `${item.label || ''}${description}`.trim();
};
const handleModelChange = (model: string | number) => {
const handleModelChange = (model: string) => {
void agenticStore.setSelectedModel(model);
};
+15 -15
View File
@@ -86,7 +86,7 @@ const nameField: Record<EntityKind, string> = {
point: 'pointName',
};
async function fetchMissing(kind: EntityKind, rawIds: Array<string | number>): Promise<void> {
async function fetchMissing(kind: EntityKind, rawIds: Array<string>): Promise<void> {
if (!rawIds || rawIds.length === 0) return;
const locale = String(i18n.global.locale.value);
const cacheKey = (id: string) => `${locale}:${id}`;
@@ -150,33 +150,33 @@ async function fetchMissing(kind: EntityKind, rawIds: Array<string | number>): P
export type AlertSourceKind = 'point' | 'device' | 'driver';
export const useEntityNames = () => {
const cachedName = (kind: EntityKind, id: string | number): string =>
const cachedName = (kind: EntityKind, id: string): string =>
cache[kind][`${String(i18n.global.locale.value)}:${String(id)}`] ?? String(id);
const resolveDevices = (ids: Array<string | number>) => fetchMissing('device', ids);
const resolveDrivers = (ids: Array<string | number>) => fetchMissing('driver', ids);
const resolveProfiles = (ids: Array<string | number>) => fetchMissing('profile', ids);
const resolvePoints = (ids: Array<string | number>) => fetchMissing('point', ids);
const resolveDevices = (ids: Array<string>) => fetchMissing('device', ids);
const resolveDrivers = (ids: Array<string>) => fetchMissing('driver', ids);
const resolveProfiles = (ids: Array<string>) => fetchMissing('profile', ids);
const resolvePoints = (ids: Array<string>) => fetchMissing('point', ids);
const deviceName = (id: string | number | undefined | null): string =>
const deviceName = (id: string | undefined | null): string =>
id == null ? '' : cachedName('device', id);
const driverName = (id: string | number | undefined | null): string =>
const driverName = (id: string | undefined | null): string =>
id == null ? '' : cachedName('driver', id);
const profileName = (id: string | number | undefined | null): string =>
const profileName = (id: string | undefined | null): string =>
id == null ? '' : cachedName('profile', id);
const pointName = (id: string | number | undefined | null): string =>
const pointName = (id: string | undefined | null): string =>
id == null ? '' : cachedName('point', id);
/** Resolve a mixed batch of (source, id) rows in one call. */
const resolveBySource = async (
rows: Array<{
source: AlertSourceKind;
sourceId: string | number;
sourceId: string;
}>
): Promise<void> => {
const ptIds: Array<string | number> = [];
const devIds: Array<string | number> = [];
const drvIds: Array<string | number> = [];
const ptIds: Array<string> = [];
const devIds: Array<string> = [];
const drvIds: Array<string> = [];
for (const r of rows) {
if (r.source === 'point') ptIds.push(r.sourceId);
else if (r.source === 'device') devIds.push(r.sourceId);
@@ -185,7 +185,7 @@ export const useEntityNames = () => {
await Promise.all([resolvePoints(ptIds), resolveDevices(devIds), resolveDrivers(drvIds)]);
};
const nameBySource = (source: AlertSourceKind, id: string | number): string => {
const nameBySource = (source: AlertSourceKind, id: string): string => {
if (source === 'point') return pointName(id);
if (source === 'driver') return driverName(id);
return deviceName(id);
+1 -25
View File
@@ -25,40 +25,16 @@ import {failMessage, warnMessage} from '@/utils/notificationUtil';
import {getStorage, removeStorage} from '@/utils/storageUtil';
import {isNull} from '@/utils/validationUtil';
import router from '@/config/router';
import JSONBigInt from 'json-bigint';
/**
* JSONBigInt parser instance with storeAsString option
*/
const JSONBigIntStr = JSONBigInt({storeAsString: true});
/**
* Transform response data using JSONBigInt to handle large integers
*
* @param data Raw response data
* @returns Parsed data
*/
function transformResponse(data: any): any {
if (typeof data !== 'string' || data === '') {
return data;
}
try {
return JSONBigIntStr.parse(data);
} catch {
return data;
}
}
/**
* Custom Axios instance with default configuration
* Handles large integers via JSONBigInt and includes authentication headers
* Includes authentication headers
*/
const request: AxiosInstance = axios.create({
timeout: AXIOS_CONFIG.TIMEOUT,
withCredentials: true,
headers: {Accept: AXIOS_CONFIG.HEADERS.ACCEPT, 'Content-Type': AXIOS_CONFIG.HEADERS.CONTENT_TYPE},
validateStatus: (status) => status >= AXIOS_CONFIG.MIN_STATUS && status <= AXIOS_CONFIG.MAX_STATUS,
transformResponse: [transformResponse],
});
/**
+3 -3
View File
@@ -111,7 +111,7 @@ export interface AgenticMessage {
export interface AgenticMessageContent {
text?: string;
format?: string;
attachments?: number[];
attachments?: string[];
tools?: string[];
traces?: AgenticTraceEvent[];
charts?: AgenticVisualizationSpec[];
@@ -165,7 +165,7 @@ export interface AgenticMessageTokens {
}
export interface AgenticAttachment {
id: number;
id: string;
conversationId: string;
fileName: string;
contentType: string;
@@ -209,7 +209,7 @@ export interface AgenticChatCompletionRequest {
* Leaving this empty is rejected by the backend with HTTP 400.
*/
conversationId: string;
attachments?: number[];
attachments?: string[];
reasoning?: boolean;
}
+4 -4
View File
@@ -43,7 +43,7 @@ export interface UserRecord extends UserForm {
export interface RoleForm {
id?: string;
parentRoleId?: number | string;
parentRoleId?: string;
roleName?: string;
roleCode?: string;
enableFlag?: string;
@@ -63,7 +63,7 @@ export interface RoleRecord extends RoleForm {
export interface MenuForm {
id?: string;
parentMenuId?: number | string;
parentMenuId?: string;
menuName?: string;
menuCode?: string;
menuTypeFlag?: string;
@@ -93,13 +93,13 @@ export interface MenuRecord extends MenuForm {
export interface ResourceForm {
id?: string;
parentResourceId?: number | string;
parentResourceId?: string;
resourceName?: string;
resourceCode?: string;
serviceName?: string;
resourceTypeFlag?: string;
resourceScopeFlag?: string;
entityId?: string | number;
entityId?: string;
resourceExt?: Record<string, unknown>;
enableFlag?: string;
remark?: string;
+10 -10
View File
@@ -112,24 +112,24 @@ export interface TopologyResponse {
export interface FlappingSource {
source: AlertSource;
sourceId: number | string;
sourceId: string;
eventTypeFlag: number;
count: number;
}
export interface CorrelationPair {
aSource: AlertSource;
aSourceId: number | string;
aSourceId: string;
aEventType: number;
bSource: AlertSource;
bSourceId: number | string;
bSourceId: string;
bEventType: number;
coCount: number;
}
export interface PeerDeviation {
profileId: number | string;
deviceId: number | string;
profileId: string;
deviceId: string;
alarmCount: number;
peerMedian: number;
ratio: number;
@@ -159,20 +159,20 @@ export interface ProtocolHealth {
export interface ChangeImpact {
kind: ChangeKind;
entityId: number | string;
entityId: string;
operateTime: string;
}
export interface SilentSource {
deviceId: number | string;
pointId: number | string;
deviceId: string;
pointId: string;
lastSeen: string;
silentSeconds: number;
}
export interface CoverageGapItem {
pointId: number | string;
profileId: number | string;
pointId: string;
profileId: string;
}
export interface CoverageGap {
+1 -1
View File
@@ -172,7 +172,7 @@ export interface EventInfoForm {
export interface GroupForm {
id?: string;
parentGroupId?: number | string | null;
parentGroupId?: string | null;
groupTypeFlag?: string;
groupName?: string;
groupCode?: string;
@@ -295,7 +295,7 @@ export const agenticActions: LocalizedAction[] = [
];
export const agenticAttachments: AgenticAttachment[] = [
{id: 91001, conversationId: sessionDefs[3]!.id, fileName: 'opcua-handshake-20260811.log', contentType: 'text/plain', size: 2516582, createTime: at(1, 10, 58)},
{id: 91002, conversationId: sessionDefs[5]!.id, fileName: 'M-104-vibration-spectrum.csv', contentType: 'text/csv', size: 486210, createTime: at(3, 9, 54)},
{id: 91003, conversationId: sessionDefs[6]!.id, fileName: 'canary-rollout-metrics.json', contentType: 'application/json', size: 128044, createTime: at(4, 16, 50)},
{id: '91001', conversationId: sessionDefs[3]!.id, fileName: 'opcua-handshake-20260811.log', contentType: 'text/plain', size: 2516582, createTime: at(1, 10, 58)},
{id: '91002', conversationId: sessionDefs[5]!.id, fileName: 'M-104-vibration-spectrum.csv', contentType: 'text/csv', size: 486210, createTime: at(3, 9, 54)},
{id: '91003', conversationId: sessionDefs[6]!.id, fileName: 'canary-rollout-metrics.json', contentType: 'application/json', size: 128044, createTime: at(4, 16, 50)},
];
+6 -6
View File
@@ -115,7 +115,7 @@ export const users: UserRecord[] = userDefs.map((u, i) => ({
// nested under ROLE_OPERATOR / ROLE_ADMIN to exercise the role tree.
interface RoleDef {
parentRoleId: number;
parentRoleId: string;
roleName: string;
roleCode: string;
remark: string;
@@ -123,11 +123,11 @@ interface RoleDef {
}
const roleDefs: RoleDef[] = [
{parentRoleId: 0, roleName: '系统管理员', roleCode: 'ROLE_ADMIN', remark: '内置超级管理员,拥有全部权限'},
{parentRoleId: 0, roleName: '运维人员', roleCode: 'ROLE_OPERATOR', remark: '负责设备与驱动的日常运维'},
{parentRoleId: 0, roleName: '只读访客', roleCode: 'ROLE_VIEWER', remark: '仅可查看监控数据'},
{parentRoleId: 2, roleName: '运维组长', roleCode: 'ROLE_OPERATOR_LEAD', remark: '运维组长,可管理运维人员'},
{parentRoleId: 1, roleName: '审计员', roleCode: 'ROLE_AUDITOR', remark: '审计员,查看审计与日志', enabled: false},
{parentRoleId: '0', roleName: '系统管理员', roleCode: 'ROLE_ADMIN', remark: '内置超级管理员,拥有全部权限'},
{parentRoleId: '0', roleName: '运维人员', roleCode: 'ROLE_OPERATOR', remark: '负责设备与驱动的日常运维'},
{parentRoleId: '0', roleName: '只读访客', roleCode: 'ROLE_VIEWER', remark: '仅可查看监控数据'},
{parentRoleId: '2', roleName: '运维组长', roleCode: 'ROLE_OPERATOR_LEAD', remark: '运维组长,可管理运维人员'},
{parentRoleId: '1', roleName: '审计员', roleCode: 'ROLE_AUDITOR', remark: '审计员,查看审计与日志', enabled: false},
];
export const roles: RoleRecord[] = roleDefs.map((r, i) => ({
+10 -10
View File
@@ -36,7 +36,7 @@ const KEEP = {content: {keep: ''}};
export const groups: GroupRecord[] = [
{
id: '1',
parentGroupId: 0,
parentGroupId: '0',
groupTypeFlag: 'DRIVER',
groupName: '驱动分组',
groupCode: 'DRIVER-GROUP',
@@ -49,7 +49,7 @@ export const groups: GroupRecord[] = [
},
{
id: '2',
parentGroupId: 1,
parentGroupId: '1',
groupTypeFlag: 'DRIVER',
groupName: 'Modbus 驱动组',
groupCode: 'DRIVER-MODBUS',
@@ -62,7 +62,7 @@ export const groups: GroupRecord[] = [
},
{
id: '3',
parentGroupId: 0,
parentGroupId: '0',
groupTypeFlag: 'DEVICE',
groupName: '设备分组',
groupCode: 'DEVICE-GROUP',
@@ -75,7 +75,7 @@ export const groups: GroupRecord[] = [
},
{
id: '4',
parentGroupId: 3,
parentGroupId: '3',
groupTypeFlag: 'DEVICE',
groupName: '温湿度设备组',
groupCode: 'DEVICE-TH',
@@ -88,7 +88,7 @@ export const groups: GroupRecord[] = [
},
{
id: '5',
parentGroupId: 3,
parentGroupId: '3',
groupTypeFlag: 'DEVICE',
groupName: '电力设备组',
groupCode: 'DEVICE-POWER',
@@ -238,7 +238,7 @@ export const apis: ApiRecord[] = [
export const resources: ResourceRecord[] = [
{
id: '5001',
parentResourceId: 0,
parentResourceId: '0',
resourceName: '根资源',
resourceCode: 'ROOT',
serviceName: 'dc3-manager',
@@ -253,7 +253,7 @@ export const resources: ResourceRecord[] = [
},
{
id: '5002',
parentResourceId: 5001,
parentResourceId: '5001',
resourceName: '驱动新增',
resourceCode: 'driver_add',
serviceName: 'dc3-manager',
@@ -268,7 +268,7 @@ export const resources: ResourceRecord[] = [
},
{
id: '5003',
parentResourceId: 5001,
parentResourceId: '5001',
resourceName: '设备查询',
resourceCode: 'device_list',
serviceName: 'dc3-manager',
@@ -283,7 +283,7 @@ export const resources: ResourceRecord[] = [
},
{
id: '5004',
parentResourceId: 5001,
parentResourceId: '5001',
resourceName: '位号更新',
resourceCode: 'point_update',
serviceName: 'dc3-data',
@@ -298,7 +298,7 @@ export const resources: ResourceRecord[] = [
},
{
id: '5005',
parentResourceId: 5001,
parentResourceId: '5001',
resourceName: '模板删除',
resourceCode: 'profile_delete',
serviceName: 'dc3-manager',
+2 -2
View File
@@ -34,8 +34,8 @@ interface NodeOpts {
* static demo matches what the backend serves; resolveMenuTitle prefers content.titles.
*/
const mk = (menuCode: string, opts: NodeOpts = {}): MenuNode => ({
id: ++seq,
parentMenuId: 0,
id: String(++seq),
parentMenuId: '0',
menuName: opts.menuName ?? menuCode,
menuCode,
menuIndex: opts.menuIndex,
+4 -4
View File
@@ -71,7 +71,7 @@ export const useAgenticStore = defineStore('agentic', () => {
const currentAbortController = ref<AbortController>();
const messagesByConversation = ref<Record<string, AgenticMessage[]>>(readCachedMessages());
const attachmentsByConversation = ref<Record<string, AgenticAttachment[]>>({});
const pendingAttachmentIdsByConversation = ref<Record<string, number[]>>({});
const pendingAttachmentIdsByConversation = ref<Record<string, string[]>>({});
const pendingActionsByConversation = ref<Record<string, AgenticAction[]>>({});
const traceEventsByConversation = ref<Record<string, AgenticTraceEvent[]>>({});
@@ -206,7 +206,7 @@ export const useAgenticStore = defineStore('agentic', () => {
persistMessages();
};
const selectSession = async (conversationId?: string | number) => {
const selectSession = async (conversationId?: string) => {
if (!conversationId) {
return;
}
@@ -405,7 +405,7 @@ export const useAgenticStore = defineStore('agentic', () => {
];
};
const removeLocalAttachment = (attachmentId: number) => {
const removeLocalAttachment = (attachmentId: string) => {
const conversationId = activeConversationId.value;
pendingAttachmentIdsByConversation.value[conversationId] = (
pendingAttachmentIdsByConversation.value[conversationId] || []
@@ -472,7 +472,7 @@ export const useAgenticStore = defineStore('agentic', () => {
}
};
const setSelectedModel = async (model: string | number) => {
const setSelectedModel = async (model: string) => {
const nextModel = resolveModelName(String(model || ''));
selectedModel.value = nextModel;
applyModelCapabilities();
+3 -3
View File
@@ -20,8 +20,8 @@ import {defineStore} from 'pinia';
import {listMenuTree} from '@/api/menu';
export interface MenuNode {
id: number | string;
parentMenuId: number | string;
id: string;
parentMenuId: string;
menuName: string;
menuCode: string;
menuTypeFlag?: string;
@@ -86,7 +86,7 @@ export const useMenuStore = defineStore('menu', {
*/
findById:
(state) =>
(id: number | string): MenuNode | undefined => {
(id: string): MenuNode | undefined => {
const key = String(id);
const walk = (nodes: MenuNode[]): MenuNode | undefined => {
for (const n of nodes) {
+2 -2
View File
@@ -40,7 +40,7 @@ export type AlertSourceKind = 'point' | 'device' | 'driver';
* kind. Router errors (e.g. aborted nav during transitions) are swallowed
* so callers don't have to .catch() themselves.
*/
export const jumpToEntity = (router: Router, kind: JumpKind, id: string | number): void => {
export const jumpToEntity = (router: Router, kind: JumpKind, id: string): void => {
const idStr = String(id);
if (!idStr) return;
switch (kind) {
@@ -66,7 +66,7 @@ export const jumpToEntity = (router: Router, kind: JumpKind, id: string | number
/**
* Jump to the per-source event list with the source id pre-filtered.
*/
export const jumpToSourceEvents = (router: Router, source: AlertSourceKind, sourceId: string | number): void => {
export const jumpToSourceEvents = (router: Router, source: AlertSourceKind, sourceId: string): void => {
let name: string;
if (source === 'point') name = 'settingsPointAlarm';
else if (source === 'driver') name = 'settingsDriverAlarm';
+4 -4
View File
@@ -720,7 +720,7 @@ export default defineComponent({
listDriverInfoByDeviceId(reactiveData.id)
.then((res) => {
const formData: AttributeFormData = reactiveData.driverFormData;
res.data.forEach((info: { attributeId: string | number; id: any; configValue: any }) => {
res.data.forEach((info: { attributeId: string; id: any; configValue: any }) => {
const attributeCode = reactiveData.driverAttributeTable[info.attributeId];
const attribute = reactiveData.driverAttributes.find((item) => item.attributeCode === attributeCode);
if (attribute) {
@@ -778,7 +778,7 @@ export default defineComponent({
return listPointInfoByDeviceId(reactiveData.id)
.then((infoRes) => {
(infoRes.data || []).forEach(
(info: { pointId: string; attributeId: string | number; id: string; configValue: unknown }) => {
(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];
@@ -842,7 +842,7 @@ export default defineComponent({
return listCommandInfoByDeviceId(reactiveData.id)
.then((infoRes) => {
(infoRes.data || []).forEach(
(info: { commandId: string; attributeId: string | number; id: string; configValue: unknown }) => {
(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)];
@@ -903,7 +903,7 @@ export default defineComponent({
return listEventInfoByDeviceId(reactiveData.id)
.then((infoRes) => {
(infoRes.data || []).forEach(
(info: { eventId: string; attributeId: string | number; id: string; configValue: unknown }) => {
(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)];
@@ -69,10 +69,10 @@ import {useEntityNames} from '@/composables/useEntityNames';
import type {AlertSource} from '@/config/types/dashboard';
interface AlertRow {
id: number | string;
id: string;
source: AlertSource;
sourceId: number | string;
pointId: number | string;
sourceId: string;
pointId: string;
eventTypeFlag: number;
confirmFlag: string;
createTime: string;
@@ -70,9 +70,9 @@ import {streamLatest} from '@/api/dashboard';
import DashboardCard from '@/components/card/dashboard/DashboardCard.vue';
interface Row {
deviceId: number | string;
pointId: number | string;
driverId?: number | string;
deviceId: string;
pointId: string;
driverId?: string;
// driverName / deviceName / pointName are populated server-side by
// DashboardServiceImpl.latestStream via the metadata facades, so the
// feed can render the full tuple without a separate lookup round-trip.
+3 -3
View File
@@ -132,7 +132,7 @@ const addThing = (form: unknown, done: () => void) => {
});
};
const disableThing = (id: number | string, done: () => void) => {
const disableThing = (id: string, done: () => void) => {
updateProfile({id: String(id), enableFlag: 'DISABLE'})
.then(() => {
successMessage();
@@ -145,7 +145,7 @@ const disableThing = (id: number | string, done: () => void) => {
});
};
const enableThing = (id: number | string, done: () => void) => {
const enableThing = (id: string, done: () => void) => {
updateProfile({id: String(id), enableFlag: 'ENABLE'})
.then(() => {
successMessage();
@@ -158,7 +158,7 @@ const enableThing = (id: number | string, done: () => void) => {
});
};
const deleteThing = (id: number | string, done: () => void) => {
const deleteThing = (id: string, done: () => void) => {
deleteProfile(String(id))
.then(() => {
successMessage();
@@ -118,7 +118,7 @@ const providers = ref<AgenticProvider[]>([]);
interface ModelConfigQuery {
model?: string;
providerId?: string | number;
providerId?: string;
enableFlag?: string;
}
@@ -87,7 +87,7 @@ watch(locale, load);
onMounted(load);
onUnmounted(() => graph?.destroy());
const nodeId = (source: string, id: number | string) => `${source}:${id}`;
const nodeId = (source: string, id: string) => `${source}:${id}`;
const renderGraph = () => {
const container = graphRef.value;
@@ -95,7 +95,7 @@ const renderGraph = () => {
graph?.destroy();
// Convert pairs → unique nodes + weighted edges.
const nodeMap = new Map<string, { id: string; data: { source: string; sid: number | string; name: string } }>();
const nodeMap = new Map<string, { id: string; data: { source: string; sid: string; name: string } }>();
const edges: Array<{ source: string; target: string; data: { weight: number } }> = [];
for (const p of pairs.value) {
const aId = nodeId(p.aSource, p.aSourceId);
@@ -61,7 +61,7 @@ import type {AlertSource} from '@/config/types/dashboard';
interface StormRow {
source: AlertSource;
sourceId: number | string;
sourceId: string;
count: number;
}
@@ -191,10 +191,10 @@ import type {RangeKey} from '@/components/segmented/RangeSegmented.vue';
import RangeSegmented from '@/components/segmented/RangeSegmented.vue';
interface Row {
id: number | string;
id: string;
source: 'point' | 'device' | 'driver';
sourceId: number | string;
pointId: number | string;
sourceId: string;
pointId: string;
eventTypeFlag: number;
alarmLevelFlag?: number;
confirmFlag: string;
@@ -60,9 +60,9 @@ import {useEntityNames} from '@/composables/useEntityNames';
import type {AlertSource} from '@/config/types/dashboard';
interface Row {
id: number | string;
id: string;
source: AlertSource;
sourceId: number | string;
sourceId: string;
createTime: string;
message?: string;
}
@@ -101,7 +101,7 @@ import {listResourceByRoleId, listRoleResourceBind} from '@/api/roleResourceBind
interface ResourceNode {
id: string;
parentResourceId?: string | number;
parentResourceId?: string;
resourceName: string;
resourceCode: string;
resourceTypeFlag: string;
+2 -4
View File
@@ -16,7 +16,6 @@
*/
import {expect, type Locator, type Page} from '@playwright/test';
import JSONBigInt from 'json-bigint';
import type {RouteIds} from './routes';
export interface PageHealth {
@@ -60,7 +59,6 @@ export interface E2eDataContext {
cleanup: () => Promise<void>;
}
const JSONBigIntStr = JSONBigInt({storeAsString: true});
const E2E_CREDENTIALS = {
tenant: process.env.E2E_TENANT || 'default',
name: process.env.E2E_USERNAME || 'dc3',
@@ -194,7 +192,7 @@ export async function apiPost<T = unknown>(
let data: unknown;
try {
data = JSONBigIntStr.parse(response.text);
data = JSON.parse(response.text);
} catch {
data = response.text;
}
@@ -244,7 +242,7 @@ export async function apiGet<T = unknown>(
let data: unknown;
try {
data = JSONBigIntStr.parse(response.text);
data = JSON.parse(response.text);
} catch {
data = response.text;
}
+2 -2
View File
@@ -52,7 +52,7 @@ describe('axios request instance', () => {
notificationSpies.warnMessage.mockClear();
});
it('injects auth headers and parses large integer JSON responses as strings', async () => {
it('injects auth headers and preserves string identifiers in JSON responses', async () => {
setStorage(AUTH_HEADERS.TENANT, 'default');
setStorage(AUTH_HEADERS.LOGIN, 'dc3');
@@ -62,7 +62,7 @@ describe('axios request instance', () => {
// Token is NOT injected — it travels in an httpOnly cookie.
expect(config.headers.get(AUTH_HEADERS.TOKEN)).toBeUndefined();
return responseOf(config, 200, '{"ok":true,"code":0,"message":"success","data":{"id":9007199254740993}}');
return responseOf(config, 200, '{"ok":true,"code":0,"message":"success","data":{"id":"9007199254740993"}}');
});
const response = await request({