mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-01 23:35:12 +00:00
feat: Implement an association between widgets(line, bar, area graphs) with time (#115)
This commit is contained in:
parent
3ff3d5d1cd
commit
7fbd6170de
@ -44,6 +44,13 @@ const props = defineProps({
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<{
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
}>,
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
@ -61,9 +68,25 @@ onMounted(async () => {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: any) => {
|
||||
instance.on("click", (params: unknown) => {
|
||||
emits("select", params);
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
@ -79,6 +102,22 @@ watch(
|
||||
setOptions(newVal);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
const instance = getInstance();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
if (props.filters) {
|
||||
instance.dispatchAction({
|
||||
type: "showTip",
|
||||
dataIndex: props.filters.dataIndex,
|
||||
seriesIndex: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeResizeListener(unref(chartRef), resize);
|
||||
|
@ -14,12 +14,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
export default function getDashboard(param: {
|
||||
name: string;
|
||||
layer: string;
|
||||
entity: string;
|
||||
}) {
|
||||
const dashboardStore = useDashboardStore();
|
||||
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||
const dashboard = list.find(
|
||||
(d: { name: string; layer: string; entity: string }) =>
|
||||
@ -27,6 +28,20 @@ export default function getDashboard(param: {
|
||||
d.entity === param.entity &&
|
||||
d.layer === param.layer
|
||||
);
|
||||
|
||||
return dashboard;
|
||||
const all = dashboardStore.layout;
|
||||
const widgets = [];
|
||||
for (const item of all) {
|
||||
if (item.type === "Tab") {
|
||||
if (item.children && item.children.length) {
|
||||
for (const child of item.children) {
|
||||
if (child.children && child.children.length) {
|
||||
widgets.push(...child.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
widgets.push(item);
|
||||
}
|
||||
}
|
||||
return { dashboard, widgets };
|
||||
}
|
||||
|
@ -142,6 +142,10 @@ export function useSourceProcessor(
|
||||
ElMessage.error(resp.errors);
|
||||
return {};
|
||||
}
|
||||
if (!resp.data) {
|
||||
ElMessage.error("The query is wrong");
|
||||
return {};
|
||||
}
|
||||
const source: { [key: string]: unknown } = {};
|
||||
const keys = Object.keys(resp.data);
|
||||
|
||||
|
@ -142,6 +142,11 @@ const msg = {
|
||||
interval: "Refresh Interval",
|
||||
pause: "Pause",
|
||||
begin: "Start",
|
||||
associateOptions: "Association Options",
|
||||
widget: "Widget",
|
||||
nameTip:
|
||||
"The name only supports Chinese and English, horizontal lines and underscores",
|
||||
duplicateName: "Duplicate name",
|
||||
seconds: "Seconds",
|
||||
hourTip: "Select Hour",
|
||||
minuteTip: "Select Minute",
|
||||
|
@ -142,6 +142,11 @@ const msg = {
|
||||
interval: "Intervalo de actualización",
|
||||
pause: "Pausa",
|
||||
begin: "Inicio",
|
||||
associateOptions: "Opciones de asociación",
|
||||
widget: "Dispositivo pequeño",
|
||||
duplicateName: "Nombre duplicado",
|
||||
nameTip:
|
||||
"Este nombre sólo admite chino e inglés, líneas cruzadas y subrayado",
|
||||
seconds: "Segundos",
|
||||
hourTip: "Seleccione Hora",
|
||||
minuteTip: "Seleccione Minuto",
|
||||
|
@ -140,6 +140,10 @@ const msg = {
|
||||
interval: "刷新间隔时间",
|
||||
pause: "暂停",
|
||||
begin: "开始",
|
||||
associateOptions: "关联选项",
|
||||
widget: "部件",
|
||||
nameTip: "该名称仅支持中文和英文、横线和下划线",
|
||||
duplicateName: "重复的名称",
|
||||
seconds: "秒",
|
||||
hourTip: "选择小时",
|
||||
minuteTip: "选择分钟",
|
||||
|
@ -80,6 +80,7 @@ export const dashboardStore = defineStore({
|
||||
const newItem: LayoutConfig = {
|
||||
...NewControl,
|
||||
i: index,
|
||||
id: index,
|
||||
type,
|
||||
metricTypes: [""],
|
||||
metrics: [""],
|
||||
@ -152,9 +153,11 @@ export const dashboardStore = defineStore({
|
||||
if (!children.length) {
|
||||
index = "0";
|
||||
}
|
||||
const id = `${activedGridItem}-${tabIndex}-${index}`;
|
||||
const newItem: LayoutConfig = {
|
||||
...NewControl,
|
||||
i: index,
|
||||
id,
|
||||
type,
|
||||
metricTypes: [""],
|
||||
metrics: [""],
|
||||
@ -275,6 +278,28 @@ export const dashboardStore = defineStore({
|
||||
};
|
||||
this.selectedGrid = this.layout[index];
|
||||
},
|
||||
setWidget(param: LayoutConfig) {
|
||||
for (let i = 0; i < this.layout.length; i++) {
|
||||
if (this.layout[i].type === "Tab") {
|
||||
if (this.layout[i].children && this.layout[i].children.length) {
|
||||
for (const child of this.layout[i].children) {
|
||||
if (child.children && child.children.length) {
|
||||
for (let c = 0; c < child.children.length; c++) {
|
||||
if (child.children[c].id === param.id) {
|
||||
child.children.splice(c, 1, param);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.layout[i].id === param.id) {
|
||||
this.layout.splice(i, 1, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async fetchMetricType(item: string) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryTypeOfMetrics")
|
||||
|
16
src/types/dashboard.d.ts
vendored
16
src/types/dashboard.d.ts
vendored
@ -36,6 +36,9 @@ export interface LayoutConfig {
|
||||
children?: { name: string; children: LayoutConfig[] }[];
|
||||
activedTabIndex?: number;
|
||||
metricConfig?: MetricConfigOpt[];
|
||||
id?: string;
|
||||
associate?: { widgetId: string }[];
|
||||
filters?: { dataIndex: number; sourceId: string };
|
||||
}
|
||||
|
||||
export type MetricConfigOpt = {
|
||||
@ -48,6 +51,7 @@ export type MetricConfigOpt = {
|
||||
};
|
||||
|
||||
export interface WidgetConfig {
|
||||
name?: string;
|
||||
title?: string;
|
||||
tips?: string;
|
||||
}
|
||||
@ -137,3 +141,15 @@ export interface TopologyConfig {
|
||||
depth?: number;
|
||||
showDepth?: boolean;
|
||||
}
|
||||
export type EventParams = {
|
||||
componentType: string;
|
||||
seriesType: string;
|
||||
seriesIndex: number;
|
||||
seriesName: string;
|
||||
name: string;
|
||||
dataIndex: number;
|
||||
data: Record<string, unknown>;
|
||||
dataType: string;
|
||||
value: number | number[];
|
||||
color: string;
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ import Tool from "./panel/Tool.vue";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import Configuration from "./configuration";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Dashboard",
|
||||
@ -66,7 +67,8 @@ export default defineComponent({
|
||||
sessionStorage.getItem(layoutKey.value) || "{}"
|
||||
);
|
||||
const layout: any = c.configuration || {};
|
||||
dashboardStore.setLayout(layout.children || []);
|
||||
|
||||
dashboardStore.setLayout(setWidgetsID(layout.children || []));
|
||||
appStore.setPageTitle(layout.name);
|
||||
if (p.entity) {
|
||||
dashboardStore.setCurrentDashboard({
|
||||
@ -78,6 +80,24 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
}
|
||||
function setWidgetsID(widgets: LayoutConfig[]) {
|
||||
for (const item of widgets) {
|
||||
item.id = item.i;
|
||||
if (item.type === "Tab") {
|
||||
if (item.children && item.children.length) {
|
||||
for (const [index, child] of item.children.entries()) {
|
||||
if (child.children && child.children.length) {
|
||||
child.children.map((d: { i: string; index: string } | any) => {
|
||||
d.id = `${item.i}-${index}-${d.i}`;
|
||||
return d;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return widgets;
|
||||
}
|
||||
function handleClick(e: any) {
|
||||
e.stopPropagation();
|
||||
if (e.target.className === "ds-main") {
|
||||
|
@ -232,12 +232,16 @@ function exportTemplates() {
|
||||
}, 2000);
|
||||
}
|
||||
function optimizeTemplate(
|
||||
children: (LayoutConfig & { moved?: boolean; standard?: unknown })[]
|
||||
children: (LayoutConfig & {
|
||||
moved?: boolean;
|
||||
standard?: unknown;
|
||||
})[]
|
||||
) {
|
||||
for (const child of children || []) {
|
||||
delete child.moved;
|
||||
delete child.activedTabIndex;
|
||||
delete child.standard;
|
||||
delete child.id;
|
||||
if (isEmptyObject(child.graph)) {
|
||||
delete child.graph;
|
||||
}
|
||||
@ -402,6 +406,7 @@ async function updateName(d: DashboardItem, value: string) {
|
||||
name: value,
|
||||
};
|
||||
delete c.id;
|
||||
delete c.filters;
|
||||
const setting = {
|
||||
id: d.id,
|
||||
configuration: JSON.stringify(c),
|
||||
|
@ -58,6 +58,13 @@ limitations under the License. -->
|
||||
<el-collapse-item :title="t('widgetOptions')" name="3">
|
||||
<WidgetOptions />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
:title="t('associateOptions')"
|
||||
name="4"
|
||||
v-if="hasAssociate"
|
||||
>
|
||||
<AssociateOptions />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
<div class="footer">
|
||||
@ -77,17 +84,13 @@ import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { Option } from "@/types/app";
|
||||
import graphs from "../graphs";
|
||||
import configs from "./widget/graph-styles";
|
||||
import WidgetOptions from "./widget/WidgetOptions.vue";
|
||||
import MetricOptions from "./widget/metric/Index.vue";
|
||||
import CustomOptions from "./widget/index";
|
||||
|
||||
export default defineComponent({
|
||||
name: "WidgetEdit",
|
||||
components: {
|
||||
...graphs,
|
||||
...configs,
|
||||
WidgetOptions,
|
||||
MetricOptions,
|
||||
...CustomOptions,
|
||||
},
|
||||
setup() {
|
||||
const configHeight = document.documentElement.clientHeight - 520;
|
||||
@ -111,6 +114,12 @@ export default defineComponent({
|
||||
const graph = computed(() => dashboardStore.selectedGrid.graph || {});
|
||||
const title = computed(() => encodeURIComponent(widget.value.title || ""));
|
||||
const tips = computed(() => encodeURIComponent(widget.value.tips || ""));
|
||||
const hasAssociate = computed(() =>
|
||||
["Bar", "Line", "Area"].includes(
|
||||
dashboardStore.selectedGrid.graph &&
|
||||
dashboardStore.selectedGrid.graph.type
|
||||
)
|
||||
);
|
||||
|
||||
function getSource(source: unknown) {
|
||||
states.source = source;
|
||||
@ -145,6 +154,7 @@ export default defineComponent({
|
||||
graph,
|
||||
title,
|
||||
tips,
|
||||
hasAssociate,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
125
src/views/dashboard/configuration/widget/AssociateOptions.vue
Normal file
125
src/views/dashboard/configuration/widget/AssociateOptions.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("widget") }}</span>
|
||||
<Selector
|
||||
:multiple="true"
|
||||
:value="widgetIds"
|
||||
:options="widgets"
|
||||
size="small"
|
||||
placeholder="Select a widget"
|
||||
class="selectors"
|
||||
@change="updateWidgetConfig"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import { Option } from "@/types/app";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const associate = dashboardStore.selectedGrid.associate || [];
|
||||
const widgetIds = ref<string[]>(
|
||||
associate.map((d: { widgetId: string }) => d.widgetId)
|
||||
);
|
||||
const widgets = computed(() => {
|
||||
const isLinear = ["Bar", "Line", "Area"].includes(
|
||||
dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type
|
||||
);
|
||||
// const isRank = ["TopList"].includes(
|
||||
// dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type
|
||||
// );
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const items = widgets.filter(
|
||||
(d: { value: string; label: string } & LayoutConfig) => {
|
||||
if (dashboardStore.selectedGrid.id !== d.id) {
|
||||
if (isLinear && d.type === "Widget" && d.widget && d.id) {
|
||||
d.value = d.id;
|
||||
d.label = d.widget.name || d.id;
|
||||
return d;
|
||||
}
|
||||
// if (isRank && d.type !== "Widget" && d.widget && d.id) {
|
||||
// d.value = d.id;
|
||||
// d.label = d.widget.name || d.id;
|
||||
// return d;
|
||||
// }
|
||||
}
|
||||
}
|
||||
);
|
||||
return items;
|
||||
});
|
||||
function updateWidgetConfig(options: Option[]) {
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const opt = options.map((d: Option) => {
|
||||
return { widgetId: d.value };
|
||||
});
|
||||
const newVal = options.map((d: Option) => d.value);
|
||||
// add association options in the source widget
|
||||
const widget = {
|
||||
...dashboardStore.selectedGrid,
|
||||
associate: opt,
|
||||
};
|
||||
dashboardStore.selectWidget({ ...widget });
|
||||
// remove unuse association widget option
|
||||
for (const id of widgetIds.value) {
|
||||
if (!newVal.includes(id)) {
|
||||
const w = widgets.find((d: { id: string }) => d.id === id);
|
||||
const config = {
|
||||
...w,
|
||||
associate: [],
|
||||
};
|
||||
dashboardStore.setWidget(config);
|
||||
}
|
||||
}
|
||||
// add association options in target widgets
|
||||
for (let i = 0; i < opt.length; i++) {
|
||||
const item = JSON.parse(JSON.stringify(opt));
|
||||
item[i] = { widgetId: dashboardStore.selectedGrid.id };
|
||||
const w = widgets.find((d: { id: string }) => d.id === opt[i].widgetId);
|
||||
const config = {
|
||||
...w,
|
||||
associate: item,
|
||||
};
|
||||
dashboardStore.setWidget(config);
|
||||
}
|
||||
widgetIds.value = newVal;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.selectors {
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
@ -13,6 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("name") }}</span>
|
||||
<el-input
|
||||
class="input"
|
||||
v-model="name"
|
||||
size="small"
|
||||
placeholder="Please input name"
|
||||
@change="updateWidgetName({ name: encodeURIComponent(name) })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("title") }}</span>
|
||||
<el-input
|
||||
@ -37,13 +47,17 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const widget = dashboardStore.selectedGrid.widget || {};
|
||||
const title = ref<string>(widget.title || "");
|
||||
const tips = ref<string>(widget.tips || "");
|
||||
const name = ref<string>(widget.name || "");
|
||||
|
||||
function updateWidgetConfig(param: { [key: string]: string }) {
|
||||
const key = Object.keys(param)[0];
|
||||
@ -57,6 +71,24 @@ function updateWidgetConfig(param: { [key: string]: string }) {
|
||||
};
|
||||
dashboardStore.selectWidget({ ...selectedGrid, widget });
|
||||
}
|
||||
function updateWidgetName(param: { [key: string]: string }) {
|
||||
const key = Object.keys(param)[0];
|
||||
const n = decodeURIComponent(param[key]);
|
||||
const pattern = /^[A-Za-z0-9-_\u4e00-\u9fa5]{1,30}$/;
|
||||
if (!pattern.test(n)) {
|
||||
ElMessage.error(t("nameTip"));
|
||||
return;
|
||||
}
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const item = widgets.find(
|
||||
(d: LayoutConfig) => d.widget && d.widget.name === n
|
||||
);
|
||||
if (item) {
|
||||
ElMessage.error(t("duplicateName"));
|
||||
return;
|
||||
}
|
||||
updateWidgetConfig(param);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
|
28
src/views/dashboard/configuration/widget/index.ts
Normal file
28
src/views/dashboard/configuration/widget/index.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import StyleOptions from "./graph-styles";
|
||||
import WidgetOptions from "./WidgetOptions.vue";
|
||||
import MetricOptions from "./metric/Index.vue";
|
||||
import AssociateOptions from "./AssociateOptions.vue";
|
||||
|
||||
export default {
|
||||
...StyleOptions,
|
||||
WidgetOptions,
|
||||
MetricOptions,
|
||||
AssociateOptions,
|
||||
};
|
@ -22,7 +22,9 @@ limitations under the License. -->
|
||||
size="small"
|
||||
placeholder="Please input unit"
|
||||
@change="
|
||||
updateConfig(index, { unit: encodeURIComponent(currentMetric.unit) })
|
||||
updateConfig(index, {
|
||||
unit: encodeURIComponent(currentMetric.unit || ''),
|
||||
})
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@ -35,7 +37,7 @@ limitations under the License. -->
|
||||
placeholder="Please input a name"
|
||||
@change="
|
||||
updateConfig(index, {
|
||||
label: encodeURIComponent(currentMetric.label),
|
||||
label: encodeURIComponent(currentMetric.label || ''),
|
||||
})
|
||||
"
|
||||
/>
|
||||
|
@ -61,9 +61,12 @@ limitations under the License. -->
|
||||
metrics: data.metrics || [''],
|
||||
metricTypes: data.metricTypes || [''],
|
||||
i: data.i,
|
||||
id: data.id,
|
||||
metricConfig: data.metricConfig,
|
||||
filters: data.filters || {},
|
||||
}"
|
||||
:needQuery="needQuery"
|
||||
@click="clickHandle"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="no-data">{{ t("noGraph") }}</div>
|
||||
@ -84,6 +87,8 @@ import {
|
||||
useGetMetricEntity,
|
||||
} from "@/hooks/useProcessor";
|
||||
import { EntityType, ListChartTypes } from "../data";
|
||||
import { EventParams } from "@/types/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
|
||||
const props = {
|
||||
data: {
|
||||
@ -93,6 +98,7 @@ const props = {
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: false },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "Widget",
|
||||
components: { ...graphs },
|
||||
@ -156,6 +162,24 @@ export default defineComponent({
|
||||
dashboardStore.activeGridItem(props.data.i);
|
||||
}
|
||||
}
|
||||
function clickHandle(params: EventParams | any) {
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const associate = (props.data.associate && props.data.associate) || [];
|
||||
|
||||
for (const item of associate) {
|
||||
const widget = widgets.find(
|
||||
(d: { id: string }) => d.id === item.widgetId
|
||||
);
|
||||
if (widget) {
|
||||
widget.filters = {
|
||||
dataIndex: params.dataIndex,
|
||||
sourceId: props.data.id || "",
|
||||
};
|
||||
|
||||
dashboardStore.setWidget(widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => [props.data.metricTypes, props.data.metrics],
|
||||
() => {
|
||||
@ -221,6 +245,7 @@ export default defineComponent({
|
||||
t,
|
||||
graph,
|
||||
widget,
|
||||
clickHandle,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -14,14 +14,20 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<Line :data="data" :intervalTime="intervalTime" :config="config" />
|
||||
<Line
|
||||
:data="data"
|
||||
:intervalTime="intervalTime"
|
||||
:config="config"
|
||||
@click="clickEvent"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import Line from "./Line.vue";
|
||||
import { AreaConfig } from "@/types/dashboard";
|
||||
import { AreaConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@ -29,8 +35,19 @@ defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<AreaConfig>,
|
||||
type: Object as PropType<
|
||||
AreaConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@ -13,15 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { BarConfig } from "@/types/dashboard";
|
||||
import { BarConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@ -31,7 +32,15 @@ const props = defineProps({
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<BarConfig>,
|
||||
type: Object as PropType<
|
||||
BarConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
@ -139,11 +148,10 @@ function getOption() {
|
||||
trigger: "axis",
|
||||
zlevel: 1000,
|
||||
z: 60,
|
||||
backgroundColor: "rgb(50,50,50)",
|
||||
confine: true,
|
||||
textStyle: {
|
||||
fontSize: 13,
|
||||
color: "#ccc",
|
||||
color: "#333",
|
||||
},
|
||||
enterable: true,
|
||||
extraCssText: "max-height: 300px; overflow: auto; border: none",
|
||||
@ -186,4 +194,7 @@ function getOption() {
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@ -147,17 +147,17 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
||||
endpoints.value = currentPods;
|
||||
}
|
||||
function clickEndpoint(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[2].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${scope.row.id}/${d.name}`
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${selectorStore.currentService.id}/${scope.row.id}/${dashboard.name}`
|
||||
);
|
||||
}
|
||||
async function searchList() {
|
||||
|
@ -180,19 +180,19 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
||||
}
|
||||
|
||||
function clickInstance(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[3].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${
|
||||
scope.row.id
|
||||
}/${d.name.split(" ").join("-")}`
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${
|
||||
selectorStore.currentService.id
|
||||
}/${scope.row.id}/${dashboard.name.split(" ").join("-")}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -13,15 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { LineConfig } from "@/types/dashboard";
|
||||
import { LineConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@ -31,7 +32,15 @@ const props = defineProps({
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<LineConfig>,
|
||||
type: Object as PropType<
|
||||
LineConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({
|
||||
step: false,
|
||||
smooth: false,
|
||||
@ -81,7 +90,7 @@ function getOption() {
|
||||
name: i,
|
||||
type: "line",
|
||||
symbol: "circle",
|
||||
symbolSize: 6,
|
||||
symbolSize: 8,
|
||||
showSymbol: props.config.showSymbol,
|
||||
step: props.config.step,
|
||||
smooth: props.config.smooth,
|
||||
@ -145,10 +154,9 @@ function getOption() {
|
||||
}
|
||||
const tooltip = {
|
||||
trigger: "axis",
|
||||
backgroundColor: "rgb(50,50,50)",
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: "#ccc",
|
||||
color: "#333",
|
||||
},
|
||||
enterable: true,
|
||||
confine: true,
|
||||
@ -215,4 +223,8 @@ function getOption() {
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@ -1,66 +0,0 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array as PropType<{ name: string; value: number }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{ sortOrder: string }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const option = computed(() => getOption());
|
||||
function getOption() {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
},
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: props.data.length === 1 ? false : true,
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: "#333",
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "pie",
|
||||
radius: "50%",
|
||||
data: props.data,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
</script>
|
@ -179,16 +179,16 @@ function setServices(arr: (Service & { merge: boolean })[]) {
|
||||
}
|
||||
|
||||
function clickService(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[0].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
const path = `/dashboard/${d.layer}/${d.entity}/${scope.row.id}/${d.name}`;
|
||||
const path = `/dashboard/${dashboard.layer}/${dashboard.entity}/${scope.row.id}/${dashboard.name}`;
|
||||
|
||||
router.push(path);
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import Bar from "./Bar.vue";
|
||||
import HeatMap from "./HeatMap.vue";
|
||||
import TopList from "./TopList.vue";
|
||||
import Table from "./Table.vue";
|
||||
import Pie from "./Pie.vue";
|
||||
import Card from "./Card.vue";
|
||||
import InstanceList from "./InstanceList.vue";
|
||||
import EndpointList from "./EndpointList.vue";
|
||||
@ -34,7 +33,6 @@ export default {
|
||||
TopList,
|
||||
Area,
|
||||
Table,
|
||||
Pie,
|
||||
Card,
|
||||
EndpointList,
|
||||
InstanceList,
|
||||
|
@ -272,19 +272,19 @@ function handleLinkClick(event: any, d: Call) {
|
||||
dashboardStore.entity === EntityType[1].value
|
||||
? EntityType[0].value
|
||||
: dashboardStore.entity;
|
||||
const p = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: settings.value.linkDashboard,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: `${e}Relation`,
|
||||
});
|
||||
if (!p) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error(
|
||||
`The dashboard named ${settings.value.linkDashboard} doesn't exist`
|
||||
);
|
||||
return;
|
||||
}
|
||||
dashboardStore.setEntity(p.entity);
|
||||
const path = `/dashboard/related/${p.layer}/${e}Relation/${d.source.id}/${d.target.id}/${p.name}`;
|
||||
dashboardStore.setEntity(dashboard.entity);
|
||||
const path = `/dashboard/related/${dashboard.layer}/${e}Relation/${d.source.id}/${d.target.id}/${dashboard.name}`;
|
||||
const routeUrl = router.resolve({ path });
|
||||
window.open(routeUrl.href, "_blank");
|
||||
dashboardStore.setEntity(origin);
|
||||
|
@ -173,18 +173,18 @@ function goDashboard() {
|
||||
dashboardStore.entity === EntityType[2].value
|
||||
? EntityType[2].value
|
||||
: EntityType[3].value;
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: settings.value.nodeDashboard,
|
||||
layer: dashboardStore.layerId,
|
||||
entity,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error(
|
||||
`The dashboard named ${settings.value.nodeDashboard} doesn't exist`
|
||||
);
|
||||
return;
|
||||
}
|
||||
const path = `/dashboard/${d.layer}/${entity}/${topologyStore.node.serviceId}/${topologyStore.node.id}/${d.name}`;
|
||||
const path = `/dashboard/${dashboard.layer}/${entity}/${topologyStore.node.serviceId}/${topologyStore.node.id}/${dashboard.name}`;
|
||||
const routeUrl = router.resolve({ path });
|
||||
window.open(routeUrl.href, "_blank");
|
||||
topologyStore.setNode(null);
|
||||
@ -217,18 +217,18 @@ function selectNodeLink(d: any) {
|
||||
dashboardStore.entity === EntityType[2].value
|
||||
? EntityType[6].value
|
||||
: EntityType[5].value;
|
||||
const p = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: settings.value.linkDashboard,
|
||||
layer: dashboardStore.layerId,
|
||||
entity,
|
||||
});
|
||||
if (!p) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error(
|
||||
`The dashboard named ${settings.value.linkDashboard} doesn't exist`
|
||||
);
|
||||
return;
|
||||
}
|
||||
const path = `/dashboard/${p.layer}/${entity}/${sourceObj.serviceId}/${sourceObj.id}/${targetObj.serviceId}/${targetObj.id}/${p.name}`;
|
||||
const path = `/dashboard/${dashboard.layer}/${entity}/${sourceObj.serviceId}/${sourceObj.id}/${targetObj.serviceId}/${targetObj.id}/${dashboard.name}`;
|
||||
const routeUrl = router.resolve({ path });
|
||||
window.open(routeUrl.href, "_blank");
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user