feat: get default dashboard

This commit is contained in:
Fine 2024-01-11 13:59:42 +08:00
commit 6c4144a5ef
8 changed files with 136 additions and 17 deletions

View File

@ -17,15 +17,25 @@
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import type { LayoutConfig } from "@/types/dashboard"; import type { LayoutConfig } from "@/types/dashboard";
import { ConfigFieldTypes } from "@/views/dashboard/data";
export default function getDashboard(param?: { name: string; layer: string; entity: string }) { export default function getDashboard(param?: { name?: string; layer: string; entity: string }, t?: string) {
const type = t || ConfigFieldTypes.NAME; // "NAME" or "ISDEFAULT"
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const opt = param || dashboardStore.currentDashboard; const opt = param || dashboardStore.currentDashboard;
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
const dashboard = list.find( let dashboard;
(d: { name: string; layer: string; entity: string }) => if (type === ConfigFieldTypes.NAME) {
d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer, dashboard = list.find(
); (d: { name: string; layer: string; entity: string }) =>
d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer,
);
} else {
dashboard = list.find(
(d: { name: string; layer: string; entity: string; isDefault: boolean }) =>
d.isDefault && d.entity === opt.entity && d.layer === opt.layer,
);
}
const all = dashboardStore.layout; const all = dashboardStore.layout;
const widgets: LayoutConfig[] = []; const widgets: LayoutConfig[] = [];
for (const item of all) { for (const item of all) {

View File

@ -386,5 +386,6 @@ const msg = {
"The trace segment serves as a representation of a trace portion executed within one single OS process, such as a JVM. It comprises a collection of spans, typically associated with and collected from a single request or execution context.", "The trace segment serves as a representation of a trace portion executed within one single OS process, such as a JVM. It comprises a collection of spans, typically associated with and collected from a single request or execution context.",
tabExpressions: "Tab Expressions", tabExpressions: "Tab Expressions",
hierarchyServicesSettings: "Service Hierarchy Topology Settings", hierarchyServicesSettings: "Service Hierarchy Topology Settings",
hierarchyNodeMetrics: "Metrics related with hierarchy topology nodes",
}; };
export default msg; export default msg;

View File

@ -386,5 +386,6 @@ const msg = {
"The trace segment serves as a representation of a trace portion executed within one single OS process, such as a JVM. It comprises a collection of spans, typically associated with and collected from a single request or execution context.", "The trace segment serves as a representation of a trace portion executed within one single OS process, such as a JVM. It comprises a collection of spans, typically associated with and collected from a single request or execution context.",
tabExpressions: "Tab Expressions", tabExpressions: "Tab Expressions",
hierarchyServicesSettings: "Service Hierarchy Topology Settings", hierarchyServicesSettings: "Service Hierarchy Topology Settings",
hierarchyNodeMetrics: "Metrics related with hierarchy topology nodes",
}; };
export default msg; export default msg;

View File

@ -384,5 +384,6 @@ const msg = {
"Trace Segment代表在单一操作系统进程例如JVM中执行的追踪部分。它包含了一组跨度spans这些跨度通常与单一请求或执行上下文关联。", "Trace Segment代表在单一操作系统进程例如JVM中执行的追踪部分。它包含了一组跨度spans这些跨度通常与单一请求或执行上下文关联。",
tabExpressions: "Tab表达式", tabExpressions: "Tab表达式",
hierarchyServicesSettings: "层次结构服务拓扑设置", hierarchyServicesSettings: "层次结构服务拓扑设置",
hierarchyNodeMetrics: "层级拓扑节点关联的指标",
}; };
export default msg; export default msg;

View File

@ -22,6 +22,7 @@ export type DashboardItem = {
isRoot: boolean; isRoot: boolean;
name: string; name: string;
isDefault: boolean; isDefault: boolean;
expressions?: string[];
}; };
export interface LayoutConfig { export interface LayoutConfig {
x: number; x: number;

View File

@ -78,7 +78,7 @@ limitations under the License. -->
<el-popconfirm <el-popconfirm
:title="t('rootTitle')" :title="t('rootTitle')"
@confirm="handleTopLevel(scope.row)" @confirm="handleTopLevel(scope.row)"
v-if="[EntityType[0].value].includes(scope.row.entity)" v-if="[EntityType[0].value, EntityType[3].value].includes(scope.row.entity)"
> >
<template #reference> <template #reference>
<el-button size="small" style="width: 80px"> <el-button size="small" style="width: 80px">
@ -97,6 +97,13 @@ limitations under the License. -->
<el-button size="small" @click="handleRename(scope.row)"> <el-button size="small" @click="handleRename(scope.row)">
{{ t("rename") }} {{ t("rename") }}
</el-button> </el-button>
<el-button
:disabled="![EntityType[0].value, EntityType[3].value].includes(scope.row.entity)"
size="small"
@click="handleEditMQE(scope.row)"
>
MQE
</el-button>
<el-popconfirm :title="t('deleteTitle')" @confirm="handleDelete(scope.row)"> <el-popconfirm :title="t('deleteTitle')" @confirm="handleDelete(scope.row)">
<template #reference> <template #reference>
<el-button size="small" type="danger"> <el-button size="small" type="danger">
@ -139,6 +146,23 @@ limitations under the License. -->
@next-click="changePage" @next-click="changePage"
/> />
</div> </div>
<el-dialog v-model="MQEVisible" title="Edit MQE" width="400px">
<div>{{ t("hierarchyNodeMetrics") }}</div>
<div class="mt-10 expressions">
<Tags
:tags="currentRow.expressions || []"
:vertical="true"
:text="t('addExpressions')"
@change="(param) => changeExpressions(param)"
/>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="MQEVisible = false">Cancel</el-button>
<el-button type="primary" @click="saveMQE"> Confirm </el-button>
</span>
</template>
</el-dialog>
</div> </div>
</div> </div>
</template> </template>
@ -167,6 +191,8 @@ limitations under the License. -->
const multipleTableRef = ref<InstanceType<typeof ElTable>>(); const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const multipleSelection = ref<DashboardItem[]>([]); const multipleSelection = ref<DashboardItem[]>([]);
const dashboardFile = ref<Nullable<HTMLDivElement>>(null); const dashboardFile = ref<Nullable<HTMLDivElement>>(null);
const MQEVisible = ref<boolean>(false);
const currentRow = ref<any>({});
const handleSelectionChange = (val: DashboardItem[]) => { const handleSelectionChange = (val: DashboardItem[]) => {
multipleSelection.value = val; multipleSelection.value = val;
@ -176,6 +202,55 @@ limitations under the License. -->
await dashboardStore.setDashboards(); await dashboardStore.setDashboards();
searchDashboards(1); searchDashboards(1);
} }
function changeExpressions(params: string[]) {
currentRow.value.expressions = params;
}
function handleEditMQE(row: DashboardItem) {
MQEVisible.value = !MQEVisible.value;
currentRow.value = row;
}
async function saveMQE() {
const items: DashboardItem[] = [];
loading.value = true;
for (const d of dashboardStore.dashboards) {
if (d.id === currentRow.value.id) {
d.expressions = currentRow.value.expressions;
const key = [d.layer, d.entity, d.name].join("_");
const layout = sessionStorage.getItem(key) || "{}";
const c = {
...JSON.parse(layout).configuration,
...d,
};
delete c.id;
const setting = {
id: d.id,
configuration: JSON.stringify(c),
};
const res = await dashboardStore.updateDashboard(setting);
if (res.data.changeTemplate.status) {
sessionStorage.setItem(
key,
JSON.stringify({
id: d.id,
configuration: c,
}),
);
} else {
loading.value = false;
return;
}
}
items.push(d);
}
dashboardStore.resetDashboards(items);
searchDashboards(1);
loading.value = false;
}
async function importTemplates(event: any) { async function importTemplates(event: any) {
const arr: any = await readFile(event); const arr: any = await readFile(event);
for (const item of arr) { for (const item of arr) {
@ -352,6 +427,9 @@ limitations under the License. -->
configuration: c, configuration: c,
}), }),
); );
} else {
loading.value = false;
return;
} }
} else { } else {
if ( if (
@ -380,6 +458,9 @@ limitations under the License. -->
configuration: c, configuration: c,
}), }),
); );
} else {
loading.value = false;
return;
} }
} }
} }
@ -416,6 +497,9 @@ limitations under the License. -->
configuration: c, configuration: c,
}), }),
); );
} else {
loading.value = false;
return;
} }
} else { } else {
if (d.layer === row.layer && [EntityType[0].value].includes(d.entity) && !row.isDefault && d.isDefault) { if (d.layer === row.layer && [EntityType[0].value].includes(d.entity) && !row.isDefault && d.isDefault) {
@ -439,6 +523,9 @@ limitations under the License. -->
configuration: c, configuration: c,
}), }),
); );
} else {
loading.value = false;
return;
} }
} }
} }
@ -607,4 +694,8 @@ limitations under the License. -->
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
} }
.expressions {
height: 300px;
}
</style> </style>

View File

@ -366,3 +366,7 @@ export enum CallTypes {
Server = "SERVER", Server = "SERVER",
Client = "CLIENT", Client = "CLIENT",
} }
export enum ConfigFieldTypes {
ISDEFAULT = "ISDEFAULT",
NAME = "NAME",
}

View File

@ -94,7 +94,7 @@ limitations under the License. -->
import type { Node } from "@/types/topology"; import type { Node } from "@/types/topology";
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { EntityType } from "@/views/dashboard/data"; import { EntityType, ConfigFieldTypes } from "@/views/dashboard/data";
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
@ -105,6 +105,7 @@ limitations under the License. -->
import zoom from "@/views/dashboard/related/components/utils/zoom"; import zoom from "@/views/dashboard/related/components/utils/zoom";
import HierarchySettings from "../config/HierarchySettings.vue"; import HierarchySettings from "../config/HierarchySettings.vue";
import type { HierarchyServicesConfig } from "@/types/dashboard"; import type { HierarchyServicesConfig } from "@/types/dashboard";
import getDashboard from "@/hooks/useDashboardsSession";
/*global Nullable, defineProps */ /*global Nullable, defineProps */
const props = defineProps({ const props = defineProps({
@ -208,11 +209,16 @@ limitations under the License. -->
return Number(d[item.legendMQE]) && d.isReal ? icons.CUBEERROR : icons.CUBE; return Number(d[item.legendMQE]) && d.isReal ? icons.CUBEERROR : icons.CUBE;
} }
function showNodeTip(event: MouseEvent, data: Node) { function showNodeTip(event: MouseEvent, data: Node) {
const config: HierarchyServicesConfig = const { dashboard } = getDashboard(
(settings.value.hierarchyServicesConfig || []).find((d: HierarchyServicesConfig) => d.layer === data.layer) || {}; {
const exprssions = config.nodeExpressions || []; layer: data.layer || "",
const nodeMetricConfig = config.expressionsConfig || []; entity: EntityType[0].value,
const html = exprssions.map((m, index) => { },
ConfigFieldTypes.ISDEFAULT,
);
const exprssions = dashboard.nodeExpressions || [];
const nodeMetricConfig = dashboard.nodeExpressionsConfig || [];
const html = exprssions.map((m: string, index: number) => {
const metric = const metric =
topologyStore.hierarchyNodeMetrics[data.layer || ""][m].values.find( topologyStore.hierarchyNodeMetrics[data.layer || ""][m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id, (val: { id: string; value: unknown }) => val.id === data.id,
@ -240,11 +246,15 @@ limitations under the License. -->
function handleNodeClick(event: MouseEvent, d: Node & { x: number; y: number }) { function handleNodeClick(event: MouseEvent, d: Node & { x: number; y: number }) {
event.stopPropagation(); event.stopPropagation();
hideTip(); hideTip();
topologyStore.setHierarchyServiceNode(d); // topologyStore.setHierarchyServiceNode(d);
handleGoDashboard(); const { dashboard } = getDashboard(
} {
function handleGoDashboard() { layer: d.layer || "",
const name = ""; // todo entity: EntityType[0].value,
},
ConfigFieldTypes.ISDEFAULT,
);
const name = dashboard.name;
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${name}`; const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${name}`;
const routeUrl = router.resolve({ path }); const routeUrl = router.resolve({ path });