feat: add coldStage to the Duration (#521)

This commit is contained in:
Fine0830
2026-02-10 12:44:14 +08:00
committed by GitHub
parent 4e3b1bdeae
commit 3c907950e7
12 changed files with 1056 additions and 1774 deletions

2660
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -49,11 +49,11 @@
"@types/d3-tip": "^3.5.5",
"@types/echarts": "^4.9.12",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.11.12",
"@types/node": "^22.19.10",
"@types/three": "^0.131.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"@vitest/coverage-v8": "^3.0.6",
"@vitest/coverage-v8": "^4.0.18",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.2.6",
@@ -84,7 +84,7 @@
"vite": "^6.4.1",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svg-icons": "^2.0.1",
"vitest": "^3.0.5",
"vitest": "^4.0.18",
"vue-tsc": "^2.2.2"
},
"browserslist": [

View File

@@ -28,18 +28,22 @@ export function useDuration() {
start: getLocalTime(appStore.utc, durationRow.start),
end: getLocalTime(appStore.utc, durationRow.end),
step: durationRow.step,
coldStage: appStore.coldStageMode,
};
}
function getDurationTime(): DurationTime {
const appStore = useAppStoreWithOut();
const { start, step, end } = getDuration();
return {
start: dateFormatStep(start, step, true),
end: dateFormatStep(end, step, true),
step: step,
step,
coldStage: appStore.coldStageMode,
};
}
function setDurationRow(data: Duration) {
durationRow = data;
const appStore = useAppStoreWithOut();
durationRow = { ...data, coldStage: appStore.coldStageMode };
}
function getMaxRange(day: number) {
if (day === undefined || day === null) {

View File

@@ -54,10 +54,10 @@ limitations under the License. -->
<el-switch
v-model="coldStage"
inline-prompt
active-text="Active Data"
inactive-text="Cold Data"
active-text="Cold Excluded"
inactive-text="Cold Only"
@change="changeDataMode"
width="90px"
width="105px"
/>
</span>
<span class="ml-5" ref="themeSwitchRef">
@@ -174,7 +174,20 @@ limitations under the License. -->
async function setTTL() {
await getMetricsTTL();
await getRecordsTTL();
changeDataMode();
// Initialize TTL handling without triggering duration update
if (coldStage.value) {
handleMetricsTTL({
minute: appStore.metricsTTL?.coldMinute ?? NaN,
hour: appStore.metricsTTL?.coldHour ?? NaN,
day: appStore.metricsTTL?.coldDay ?? NaN,
});
} else {
handleMetricsTTL({
minute: appStore.metricsTTL?.minute ?? NaN,
hour: appStore.metricsTTL?.hour ?? NaN,
day: appStore.metricsTTL?.day ?? NaN,
});
}
}
async function getRecordsTTL() {
const resp = await appStore.queryRecordsTTL();

View File

@@ -82,6 +82,7 @@ describe("App Store", () => {
expect(store.durationRow.start).toBeInstanceOf(Date);
expect(store.durationRow.end).toBeInstanceOf(Date);
expect(store.durationRow.step).toBe(TimeType.MINUTE_TIME);
expect(store.durationRow.coldStage).toBe(false);
});
});
@@ -94,6 +95,7 @@ describe("App Store", () => {
expect(duration.start).toBeInstanceOf(Date);
expect(duration.end).toBeInstanceOf(Date);
expect(duration.step).toBe(TimeType.MINUTE_TIME);
expect(duration.coldStage).toBe(false);
});
it("should return correct duration time", () => {
@@ -104,6 +106,7 @@ describe("App Store", () => {
expect(durationTime.start).toBe("2023-01-01 12:00");
expect(durationTime.end).toBe("2023-01-01 12:00");
expect(durationTime.step).toBe(TimeType.MINUTE_TIME);
expect(durationTime.coldStage).toBe(false);
});
it("should calculate interval unix correctly for MINUTE", () => {
@@ -156,7 +159,7 @@ describe("App Store", () => {
store.setDuration(newDuration);
expect(store.durationRow).toEqual(newDuration);
expect(store.durationRow).toEqual({ ...newDuration, coldStage: false });
});
it("should update duration row correctly", () => {
@@ -169,7 +172,7 @@ describe("App Store", () => {
store.updateDurationRow(newDuration);
expect(store.durationRow).toEqual(newDuration);
expect(store.durationRow).toEqual({ ...newDuration, coldStage: false });
});
it("should set max range correctly", () => {
@@ -231,6 +234,55 @@ describe("App Store", () => {
expect(store.coldStageMode).toBe(true);
});
it("should set duration with coldStage when coldStageMode is enabled", () => {
const store = appStore();
store.setColdStageMode(true);
const newDuration = {
start: new Date("2023-01-01"),
end: new Date("2023-01-02"),
step: "HOUR",
};
store.setDuration(newDuration);
expect(store.durationRow).toEqual({ ...newDuration, coldStage: true });
expect(store.duration.coldStage).toBe(true);
});
it("should update duration row with coldStage when coldStageMode is enabled", () => {
const store = appStore();
store.setColdStageMode(true);
const newDuration = {
start: new Date("2023-02-01"),
end: new Date("2023-02-02"),
step: "DAY",
};
store.updateDurationRow(newDuration);
expect(store.durationRow).toEqual({ ...newDuration, coldStage: true });
expect(store.duration.coldStage).toBe(true);
});
it("should return correct duration time with coldStage when coldStageMode is enabled", () => {
const store = appStore();
store.setColdStageMode(true);
// Need to update duration row after setting cold stage mode
const newDuration = {
start: new Date("2023-01-01"),
end: new Date("2023-01-02"),
step: "HOUR",
};
store.setDuration(newDuration);
const durationTime = store.durationTime;
expect(durationTime.coldStage).toBe(true);
});
it("should set reload timer correctly", () => {
const store = appStore();
const mockTimer = setInterval(() => {

View File

@@ -18,7 +18,9 @@ import { defineStore } from "pinia";
import { store } from "@/store";
import graphql from "@/graphql";
import type { Alarm } from "@/types/alarm";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useDuration } from "@/hooks/useDuration";
const { getDurationTime } = useDuration();
interface AlarmState {
loading: boolean;
@@ -48,10 +50,14 @@ export const alarmStore = defineStore({
return res.data;
},
async getAlarmTagKeys() {
return await graphql.query("queryAlarmTagKeys").params({ duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryAlarmTagKeys")
.params({ duration: { ...getDurationTime(), coldStage: undefined } });
},
async getAlarmTagValues(tagKey: string) {
return await graphql.query("queryAlarmTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryAlarmTagValues")
.params({ tagKey, duration: { ...getDurationTime(), coldStage: undefined } });
},
},
});

View File

@@ -46,6 +46,7 @@ export const InitializationDurationRow = {
start: new Date(new Date().getTime() - 1800000),
end: new Date(),
step: TimeType.MINUTE_TIME,
coldStage: false,
};
export const appStore = defineStore({
@@ -62,7 +63,7 @@ export const appStore = defineStore({
reloadTimer: null,
allMenus: [],
theme: Themes.Dark,
coldStageMode: false,
coldStageMode: InitializationDurationRow.coldStage || false,
maxRange: [],
metricsTTL: null,
recordsTTL: null,
@@ -73,6 +74,7 @@ export const appStore = defineStore({
start: getLocalTime(this.utc, this.durationRow.start),
end: getLocalTime(this.utc, this.durationRow.end),
step: this.durationRow.step,
coldStage: this.durationRow.coldStage,
};
},
durationTime(): DurationTime {
@@ -80,6 +82,7 @@ export const appStore = defineStore({
start: dateFormatStep(this.duration.start, this.duration.step, true),
end: dateFormatStep(this.duration.end, this.duration.step, true),
step: this.duration.step,
coldStage: this.duration.coldStage,
};
},
intervalUnix(): number[] {
@@ -124,10 +127,10 @@ export const appStore = defineStore({
},
actions: {
setDuration(data: Duration): void {
this.durationRow = data;
this.durationRow = { ...data, coldStage: this.coldStageMode };
},
updateDurationRow(data: Duration) {
this.durationRow = data;
this.durationRow = { ...data, coldStage: this.coldStageMode };
},
setMaxRange(times: Date[]) {
this.maxRange = times;

View File

@@ -152,10 +152,14 @@ export const logStore = defineStore({
return response;
},
async getLogTagKeys() {
return await graphql.query("queryLogTagKeys").params({ duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryLogTagKeys")
.params({ duration: { ...getDurationTime(), coldStage: undefined } });
},
async getLogTagValues(tagKey: string) {
return await graphql.query("queryLogTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryLogTagValues")
.params({ tagKey, duration: { ...getDurationTime(), coldStage: undefined } });
},
},
});

View File

@@ -237,10 +237,14 @@ export const traceStore = defineStore({
return response;
},
async getTagKeys() {
return await graphql.query("queryTraceTagKeys").params({ duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryTraceTagKeys")
.params({ duration: { ...getDurationTime(), coldStage: undefined } });
},
async getTagValues(tagKey: string) {
return await graphql.query("queryTraceTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
return await graphql
.query("queryTraceTagValues")
.params({ tagKey, duration: { ...getDurationTime(), coldStage: undefined } });
},
async getHasQueryTracesV2Support() {
const response = await graphql.query("queryHasQueryTracesV2Support").params({});

View File

@@ -36,25 +36,29 @@ Object.defineProperty(window, "matchMedia", {
});
// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
globalThis.ResizeObserver = class ResizeObserver {
observe = vi.fn();
unobserve = vi.fn();
disconnect = vi.fn();
};
// Mock IntersectionObserver
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
globalThis.IntersectionObserver = class IntersectionObserver {
root = null;
rootMargin = "";
thresholds = [];
observe = vi.fn();
unobserve = vi.fn();
disconnect = vi.fn();
takeRecords = vi.fn(() => []);
} as any;
// Mock requestAnimationFrame
global.requestAnimationFrame = vi.fn((cb: FrameRequestCallback) => {
globalThis.requestAnimationFrame = vi.fn((cb: FrameRequestCallback) => {
const id = setTimeout(cb, 0);
return id as unknown as number;
});
global.cancelAnimationFrame = vi.fn();
globalThis.cancelAnimationFrame = vi.fn();
// Configure Vue Test Utils
config.global.plugins = [ElementPlus];

View File

@@ -22,11 +22,13 @@ export interface Duration {
start: Date;
end: Date;
step: string;
coldStage?: boolean;
}
export interface DurationTime {
start: string;
end: string;
step: string;
coldStage?: boolean;
}
export type Paging = {
pageNum: number;

View File

@@ -539,8 +539,8 @@ limitations under the License. -->
cancelButtonText: "Cancel",
inputValue: row.name,
})
.then(({ value }) => {
updateName(row, value);
.then(() => {
updateName(row);
})
.catch(() => {
ElMessage({
@@ -549,8 +549,8 @@ limitations under the License. -->
});
});
}
async function updateName(d: DashboardItem, value: string) {
if (new RegExp(/\s/).test(value)) {
async function updateName(d: DashboardItem) {
if (new RegExp(/\s/).test(d.name)) {
ElMessage.error("The name cannot contain spaces, carriage returns, etc");
return;
}
@@ -559,7 +559,7 @@ limitations under the License. -->
const c = {
...JSON.parse(layout).configuration,
...d,
name: value,
name: d.name,
};
delete c.id;
delete c.filters;
@@ -575,7 +575,7 @@ limitations under the License. -->
}
dashboardStore.setCurrentDashboard({
...d,
name: value,
name: d.name,
});
dashboards.value = dashboardStore.dashboards.map((item: DashboardItem) => {
if (dashboardStore.currentDashboard?.id === item.id) {