mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-01 19:03:40 +00:00
feat: Enhance associations for the Event widget (#120)
This commit is contained in:
parent
e144b43267
commit
42ead4a572
22
package-lock.json
generated
22
package-lock.json
generated
@ -22,8 +22,7 @@
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-types": "^4.1.1",
|
||||
"vuex": "^4.0.0-0"
|
||||
"vue-types": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^7.1.0",
|
||||
@ -27338,17 +27337,6 @@
|
||||
"vue": "^2.0.0 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vuex": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
|
||||
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-hr-time": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
@ -50552,14 +50540,6 @@
|
||||
"is-plain-object": "5.0.0"
|
||||
}
|
||||
},
|
||||
"vuex": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
|
||||
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
|
||||
"requires": {
|
||||
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||
}
|
||||
},
|
||||
"w3c-hr-time": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
|
@ -24,8 +24,7 @@
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-types": "^4.1.1",
|
||||
"vuex": "^4.0.0-0"
|
||||
"vue-types": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^7.1.0",
|
||||
|
@ -46,8 +46,12 @@ const props = defineProps({
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<{
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
sourceId: string;
|
||||
}>,
|
||||
},
|
||||
@ -73,7 +77,7 @@ onMounted(async () => {
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
(event: Event) => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
@ -84,6 +88,27 @@ onMounted(async () => {
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
if (
|
||||
["vis-item-overflow", "vis-item-content"].includes(
|
||||
(event.target as HTMLDivElement).className
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const series = (window as any).structuredClone(props.option.series);
|
||||
for (const temp of series) {
|
||||
if (temp.markArea) {
|
||||
delete temp.markArea;
|
||||
}
|
||||
}
|
||||
const options = {
|
||||
...props.option,
|
||||
series,
|
||||
};
|
||||
if (JSON.stringify(options) === JSON.stringify(props.option)) {
|
||||
return;
|
||||
}
|
||||
setOptions(options);
|
||||
},
|
||||
true
|
||||
);
|
||||
@ -110,6 +135,39 @@ watch(
|
||||
return;
|
||||
}
|
||||
if (props.filters) {
|
||||
if (props.filters.isRange) {
|
||||
const markArea = {
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
opacity: 0.3,
|
||||
},
|
||||
data: [
|
||||
[
|
||||
{
|
||||
xAxis: props.filters.duration.startTime,
|
||||
},
|
||||
{
|
||||
xAxis: props.filters.duration.endTime,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
const series = (window as any).structuredClone(props.option.series);
|
||||
for (const [key, temp] of series.entries()) {
|
||||
if (key === 0) {
|
||||
temp.markArea = markArea;
|
||||
}
|
||||
}
|
||||
const options = {
|
||||
...props.option,
|
||||
series,
|
||||
};
|
||||
if (JSON.stringify(options) === JSON.stringify(props.option)) {
|
||||
return;
|
||||
}
|
||||
setOptions(options);
|
||||
return;
|
||||
}
|
||||
instance.dispatchAction({
|
||||
type: "showTip",
|
||||
dataIndex: props.filters.dataIndex,
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
BarSeriesOption,
|
||||
LineSeriesOption,
|
||||
HeatmapSeriesOption,
|
||||
PieSeriesOption,
|
||||
SankeySeriesOption,
|
||||
} from "echarts/charts";
|
||||
import {
|
||||
@ -46,7 +45,6 @@ export type ECOption = echarts.ComposeOption<
|
||||
| DatasetComponentOption
|
||||
| LegendComponentOption
|
||||
| HeatmapSeriesOption
|
||||
| PieSeriesOption
|
||||
| SankeySeriesOption
|
||||
>;
|
||||
|
||||
|
@ -147,6 +147,7 @@ const msg = {
|
||||
nameTip:
|
||||
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
||||
duplicateName: "Duplicate name",
|
||||
enableAssociate: "Enable association",
|
||||
seconds: "Seconds",
|
||||
hourTip: "Select Hour",
|
||||
minuteTip: "Select Minute",
|
||||
|
@ -147,6 +147,7 @@ const msg = {
|
||||
duplicateName: "Nombre duplicado",
|
||||
nameTip:
|
||||
"El nombre sólo admite chino e inglés, líneas horizontales y subrayado, y la longitud del nombre no excederá de 300 caracteres",
|
||||
enableAssociate: "Activar asociación",
|
||||
seconds: "Segundos",
|
||||
hourTip: "Seleccione Hora",
|
||||
minuteTip: "Seleccione Minuto",
|
||||
|
@ -142,6 +142,7 @@ const msg = {
|
||||
begin: "开始",
|
||||
associateOptions: "关联选项",
|
||||
widget: "部件",
|
||||
enableAssociate: "启用关联",
|
||||
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
||||
duplicateName: "重复的名称",
|
||||
seconds: "秒",
|
||||
|
@ -104,6 +104,9 @@ export const eventStore = defineStore({
|
||||
scope = "Endpoint";
|
||||
}
|
||||
item.scope = scope;
|
||||
if (!item.endTime || item.endTime === item.startTime) {
|
||||
item.endTime = Number(item.startTime) + 60000;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
);
|
||||
|
11
src/types/dashboard.d.ts
vendored
11
src/types/dashboard.d.ts
vendored
@ -38,7 +38,16 @@ export interface LayoutConfig {
|
||||
metricConfig?: MetricConfigOpt[];
|
||||
id?: string;
|
||||
associate?: { widgetId: string }[];
|
||||
filters?: { dataIndex: number; sourceId: string };
|
||||
eventAssociate?: boolean;
|
||||
filters?: {
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
isRange?: boolean;
|
||||
duration?: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type MetricConfigOpt = {
|
||||
|
@ -16,13 +16,7 @@
|
||||
*/
|
||||
import * as echarts from "echarts/core";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
HeatmapChart,
|
||||
SankeyChart,
|
||||
} from "echarts/charts";
|
||||
import { BarChart, LineChart, HeatmapChart, SankeyChart } from "echarts/charts";
|
||||
|
||||
import {
|
||||
TitleComponent,
|
||||
@ -32,6 +26,7 @@ import {
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
MarkAreaComponent,
|
||||
} from "echarts/components";
|
||||
|
||||
import { SVGRenderer } from "echarts/renderers";
|
||||
@ -43,13 +38,13 @@ echarts.use([
|
||||
GridComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
HeatmapChart,
|
||||
SankeyChart,
|
||||
SVGRenderer,
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
MarkAreaComponent,
|
||||
]);
|
||||
|
||||
export default echarts;
|
||||
|
82
src/views/dashboard/configuration/Event.vue
Normal file
82
src/views/dashboard/configuration/Event.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<!-- 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>
|
||||
<span class="label">{{ t("enableAssociate") }}</span>
|
||||
<el-switch
|
||||
v-model="eventAssociate"
|
||||
active-text="Yes"
|
||||
inactive-text="No"
|
||||
@change="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<el-button size="small" @click="cancelConfig">
|
||||
{{ t("cancel") }}
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" @click="applyConfig">
|
||||
{{ t("apply") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref } from "vue";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const originConfig = dashboardStore.selectedGrid;
|
||||
const eventAssociate = ref(dashboardStore.selectedGrid.eventAssociate || false);
|
||||
|
||||
function updateConfig() {
|
||||
dashboardStore.selectedGrid = {
|
||||
...dashboardStore.selectedGrid,
|
||||
eventAssociate,
|
||||
};
|
||||
dashboardStore.selectWidget(dashboardStore.selectedGrid);
|
||||
}
|
||||
|
||||
function applyConfig() {
|
||||
dashboardStore.setConfigPanel(false);
|
||||
dashboardStore.setConfigs(dashboardStore.selectedGrid);
|
||||
}
|
||||
|
||||
function cancelConfig() {
|
||||
dashboardStore.selectWidget(originConfig);
|
||||
dashboardStore.setConfigPanel(false);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-top: 1px solid #eee;
|
||||
padding: 10px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
@ -18,9 +18,11 @@
|
||||
import Text from "./Text.vue";
|
||||
import Widget from "./Widget.vue";
|
||||
import Topology from "./Topology.vue";
|
||||
import Event from "./Event.vue";
|
||||
|
||||
export default {
|
||||
Text,
|
||||
Widget,
|
||||
Topology,
|
||||
Event,
|
||||
};
|
||||
|
@ -41,32 +41,16 @@ 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(
|
||||
const all = getDashboard(dashboardStore.currentDashboard).widgets;
|
||||
const items = all.filter(
|
||||
(d: { value: string; label: string } & LayoutConfig) => {
|
||||
if (dashboardStore.selectedGrid.id !== d.id) {
|
||||
if (
|
||||
isLinear &&
|
||||
d.type === "Widget" &&
|
||||
d.widget &&
|
||||
d.widget.name &&
|
||||
d.id
|
||||
) {
|
||||
d.value = d.id;
|
||||
d.label = d.widget.name;
|
||||
return d;
|
||||
}
|
||||
// if (isRank && d.type !== "Widget" && d.widget && d.id) {
|
||||
// d.value = d.id;
|
||||
// d.label = d.widget.name || d.id;
|
||||
// return d;
|
||||
// }
|
||||
const isLinear = ["Bar", "Line", "Area"].includes(
|
||||
(d.graph && d.graph.type) || ""
|
||||
);
|
||||
if (isLinear && d.id && dashboardStore.selectedGrid.id !== d.id) {
|
||||
d.value = d.id;
|
||||
d.label = (d.widget && d.widget.name) || d.id;
|
||||
return d;
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -84,6 +68,7 @@ function updateWidgetConfig(options: Option[]) {
|
||||
associate: opt,
|
||||
};
|
||||
dashboardStore.selectWidget({ ...widget });
|
||||
|
||||
// remove unuse association widget option
|
||||
for (const id of widgetIds.value) {
|
||||
if (!newVal.includes(id)) {
|
||||
|
@ -25,6 +25,9 @@ limitations under the License. -->
|
||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="tools" @click="editConfig">
|
||||
<span>{{ t("edit") }}</span>
|
||||
</div>
|
||||
<div class="tools" @click="removeWidget">
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
@ -58,6 +61,10 @@ const dashboardStore = useDashboardStore();
|
||||
function removeWidget() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
function editConfig() {
|
||||
dashboardStore.setConfigPanel(true);
|
||||
dashboardStore.selectWidget(props.data);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.event-wrapper {
|
||||
|
@ -38,9 +38,13 @@ defineProps({
|
||||
type: Object as PropType<
|
||||
AreaConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
|
@ -18,7 +18,6 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { BarConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
@ -30,14 +29,17 @@ const props = defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
BarConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
@ -50,30 +52,7 @@ function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const startP = keys.length > 1 ? 50 : 15;
|
||||
const diff = 15;
|
||||
const markAreas = (props.itemEvents || []).map(
|
||||
(event: Event, index: number) => {
|
||||
return [
|
||||
{
|
||||
name: `${event.name}:${event.type}`,
|
||||
xAxis: event.startTime,
|
||||
y: startP + diff * index,
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
borderColor: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
color: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: event.message,
|
||||
xAxis: event.endTime,
|
||||
y: startP + diff * (index + 1),
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
const temp = keys.map((i: string, index: number) => {
|
||||
const temp = keys.map((i: string) => {
|
||||
if (!props.intervalTime) {
|
||||
return;
|
||||
}
|
||||
@ -94,23 +73,6 @@ function getOption() {
|
||||
backgroundStyle: {
|
||||
color: "rgba(180, 180, 180, 0.1)",
|
||||
},
|
||||
markArea:
|
||||
index === 0
|
||||
? {
|
||||
silent: false,
|
||||
data: markAreas,
|
||||
label: {
|
||||
show: false,
|
||||
width: 60,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
position: "bottom",
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
let color: string[] = [];
|
||||
|
@ -18,7 +18,6 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { LineConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
@ -30,14 +29,17 @@ const props = defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
LineConfig & {
|
||||
filters: {
|
||||
value: number | string;
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
@ -58,30 +60,7 @@ function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const startP = keys.length > 1 ? 50 : 15;
|
||||
const diff = 10;
|
||||
const markAreas = (props.itemEvents || []).map(
|
||||
(event: Event, index: number) => {
|
||||
return [
|
||||
{
|
||||
name: `${event.name}:${event.type}`,
|
||||
xAxis: event.startTime,
|
||||
y: startP + diff * index,
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
borderColor: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
color: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: event.message,
|
||||
xAxis: event.endTime,
|
||||
y: startP + diff * (index + 1),
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
const temp = keys.map((i: any, index: number) => {
|
||||
const temp = keys.map((i: any) => {
|
||||
const serie: any = {
|
||||
data: props.data[i].map((item: any, itemIndex: number) => [
|
||||
props.intervalTime[itemIndex],
|
||||
@ -98,23 +77,6 @@ function getOption() {
|
||||
width: 1.5,
|
||||
type: "solid",
|
||||
},
|
||||
markArea:
|
||||
index === 0
|
||||
? {
|
||||
silent: false,
|
||||
data: markAreas,
|
||||
label: {
|
||||
show: false,
|
||||
width: 60,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
position: "bottom",
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
if (props.config.type === "Area") {
|
||||
serie.areaStyle = {
|
||||
|
@ -19,15 +19,23 @@ limitations under the License. -->
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useThrottleFn } from "@vueuse/core";
|
||||
import { Event } from "@/types/events";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import { useEventStore } from "@/store/modules/event";
|
||||
import { DataSet, Timeline } from "vis-timeline/standalone";
|
||||
import "vis-timeline/styles/vis-timeline-graph2d.css";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { dateFormatTime } from "@/utils/dateFormat";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
|
||||
const eventStore = useEventStore();
|
||||
/*global Nullable */
|
||||
const timeline = ref<Nullable<HTMLDivElement>>(null);
|
||||
const visGraph = ref<Nullable<any>>(null);
|
||||
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
|
||||
const dashboardStore = useDashboardStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
new Date(dayjs(date).format(pattern));
|
||||
const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
@ -49,12 +57,12 @@ function visTimeline() {
|
||||
visGraph.value.destroy();
|
||||
}
|
||||
const h = timeline.value.getBoundingClientRect().height;
|
||||
const events = eventStore.events.map((d, index) => {
|
||||
const events = eventStore.events.map((d: Event, index: number) => {
|
||||
return {
|
||||
id: index + 1,
|
||||
content: d.name,
|
||||
start: dateFormat(d.startTime),
|
||||
end: dateFormat(d.endTime),
|
||||
start: dateFormat(Number(d.startTime)),
|
||||
end: dateFormat(Number(d.endTime)),
|
||||
data: d,
|
||||
className: d.type,
|
||||
};
|
||||
@ -68,7 +76,7 @@ function visTimeline() {
|
||||
autoResize: false,
|
||||
tooltip: {
|
||||
overflowMethod: "cap",
|
||||
template(item) {
|
||||
template(item: Event | any) {
|
||||
const data = item.data || {};
|
||||
let tmp = `<div>ID: ${data.uuid || ""}</div>
|
||||
<div>Name: ${data.name || ""}</div>
|
||||
@ -88,6 +96,38 @@ function visTimeline() {
|
||||
},
|
||||
};
|
||||
visGraph.value = new Timeline(timeline.value, items, options);
|
||||
visGraph.value.on("select", (properties: { items: number[] }) => {
|
||||
if (!dashboardStore.selectedGrid.eventAssociate) {
|
||||
return;
|
||||
}
|
||||
const all = getDashboard(dashboardStore.currentDashboard).widgets;
|
||||
const widgets = all.filter(
|
||||
(d: { value: string; label: string } & LayoutConfig) => {
|
||||
const isLinear = ["Bar", "Line", "Area"].includes(
|
||||
(d.graph && d.graph.type) || ""
|
||||
);
|
||||
if (isLinear) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
);
|
||||
const index = properties.items[0];
|
||||
const i = events[index - 1 || 0];
|
||||
|
||||
for (const widget of widgets) {
|
||||
const startTime = dateFormatTime(i.start, appStore.duration.step);
|
||||
const endTime = dateFormatTime(i.end, appStore.duration.step);
|
||||
widget.filters = {
|
||||
sourceId: dashboardStore.selectedGrid.id || "",
|
||||
isRange: true,
|
||||
duration: {
|
||||
startTime,
|
||||
endTime,
|
||||
},
|
||||
};
|
||||
dashboardStore.setWidget(widget);
|
||||
}
|
||||
});
|
||||
}
|
||||
function resize() {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
|
Loading…
Reference in New Issue
Block a user