feat: add custom config for graphs

This commit is contained in:
Qiuxia Fan 2022-01-12 23:04:31 +08:00
parent 8435beda35
commit 8d0acfa1e0
16 changed files with 303 additions and 77 deletions

View File

@ -47,7 +47,7 @@ export interface StandardConfig {
seconds?: string; seconds?: string;
} }
export type GraphConfig = BarConfig | LineConfig; export type GraphConfig = BarConfig | LineConfig | CardConfig | TableConfig;
export interface BarConfig { export interface BarConfig {
type?: string; type?: string;
showBackground?: boolean; showBackground?: boolean;
@ -63,3 +63,21 @@ export interface AreaConfig {
type?: string; type?: string;
opacity?: number; opacity?: number;
} }
export interface CardConfig {
type?: string;
fontSize?: number;
showUint: boolean;
}
export interface TableConfig {
type?: string;
showTableValues: boolean;
tableHeaderCol1: string;
tableHeaderCol2: string;
}
export interface TopListConfig {
type?: string;
topN: number;
}

View File

@ -48,8 +48,8 @@ const props = defineProps({
}); });
const emits = defineEmits(["update"]); const emits = defineEmits(["update"]);
const { t } = useI18n(); const { t } = useI18n();
const title = ref<string>(props.config.title); const title = ref<string>(props.config.title || "");
const tips = ref<string>(props.config.tips); const tips = ref<string>(props.config.tips || "");
function updateWidgetConfig(param: { [key: string]: unknown }) { function updateWidgetConfig(param: { [key: string]: unknown }) {
emits("update", param); emits("update", param);

View File

@ -0,0 +1,60 @@
<!-- 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">Font Size</span>
<el-slider
class="slider"
v-model="fontSize"
show-input
input-size="small"
:min="0.1"
:max="1"
:step="0.1"
@change="updateConfig({ fontSize })"
/>
</div>
</template>
<script lang="ts" setup>
import { defineProps, ref, defineEmits } from "vue";
import type { PropType } from "vue";
import { CardConfig } from "@/types/dashboard";
const props = defineProps({
config: {
type: Object as PropType<CardConfig>,
default: () => ({ fontSize: 12 }),
},
});
const emits = defineEmits(["update"]);
const fontSize = ref(props.config.fontSize);
function updateConfig(param: { [key: string]: unknown }) {
emits("update", param);
}
</script>
<style lang="scss" scoped>
.slider {
width: 500px;
margin-top: -13px;
}
.label {
font-size: 13px;
font-weight: 500;
display: block;
margin-bottom: 5px;
}
</style>

View File

@ -62,11 +62,6 @@ function updateConfig(param: { [key: string]: unknown }) {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bar-width {
width: 500px;
margin-top: -13px;
}
.label { .label {
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;

View File

@ -0,0 +1,84 @@
<!-- 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">Show Values</span>
<el-switch
v-model="showTableValues"
active-text="Yes"
inactive-text="No"
@change="updateConfig({ showTableValues })"
/>
</div>
<div class="item">
<span class="label">{{ t("tableHeaderCol1") }}</span>
<el-input
class="input"
v-model="tableHeaderCol1"
size="mini"
placeholder="none"
@change="updateConfig({ tableHeaderCol1 })"
/>
</div>
<div class="item">
<span class="label">{{ t("tableHeaderCol2") }}</span>
<el-input
class="input"
v-model="tableHeaderCol2"
size="mini"
placeholder="none"
@change="updateConfig({ tableHeaderCol2 })"
/>
</div>
</template>
<script lang="ts" setup>
import { defineProps, ref, defineEmits } from "vue";
import type { PropType } from "vue";
import { TableConfig } from "@/types/dashboard";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps({
config: {
type: Object as PropType<TableConfig>,
default: () => ({
showTableValues: true,
tableHeaderCol1: "",
tableHeaderCol2: "",
}),
},
});
const emits = defineEmits(["update"]);
const showTableValues = ref(props.config.showTableValues);
const tableHeaderCol1 = ref(props.config.tableHeaderCol1);
const tableHeaderCol2 = ref(props.config.tableHeaderCol2);
function updateConfig(param: { [key: string]: unknown }) {
emits("update", param);
}
</script>
<style lang="scss" scoped>
.slider {
width: 500px;
margin-top: -13px;
}
.label {
font-size: 13px;
font-weight: 500;
display: block;
margin-bottom: 5px;
}
</style>

View File

@ -0,0 +1,59 @@
<!-- 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("maxItemNum") }}</span>
<el-input
class="input"
v-model="topN"
size="mini"
placeholder="none"
type="number"
:min="1"
:max="100"
@change="updateConfig({ topN })"
/>
</div>
</template>
<script lang="ts" setup>
import { defineProps, ref, defineEmits } from "vue";
import type { PropType } from "vue";
import { TopListConfig } from "@/types/dashboard";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps({
config: {
type: Object as PropType<TopListConfig>,
default: () => ({
topN: 10,
}),
},
});
const emits = defineEmits(["update"]);
const topN = ref(props.config.topN);
function updateConfig(param: { [key: string]: unknown }) {
emits("update", param);
}
</script>
<style lang="scss" scoped>
.label {
font-size: 13px;
font-weight: 500;
display: block;
margin-bottom: 5px;
}
</style>

View File

@ -48,6 +48,7 @@ limitations under the License. -->
:intervalTime="appStoreWithOut.intervalTime" :intervalTime="appStoreWithOut.intervalTime"
:data="state.source" :data="state.source"
:config="data.graph" :config="data.graph"
:standard="data.standard"
/> />
</div> </div>
<div v-else class="no-data">{{ t("noData") }}</div> <div v-else class="no-data">{{ t("noData") }}</div>

View File

@ -41,6 +41,21 @@ export const DefaultGraphConfig: { [key: string]: any } = {
type: "Area", type: "Area",
opacity: 0.4, opacity: 0.4,
}, },
Card: {
type: "Card",
fontSize: 14,
showUint: true,
},
Table: {
type: "Card",
showTableValues: true,
tableHeaderCol1: "",
tableHeaderCol2: "",
},
TopList: {
type: "TopList",
topN: 10,
},
}; };
export enum MetricQueryTypes { export enum MetricQueryTypes {

View File

@ -14,44 +14,41 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div class="chart-card"> <div class="chart-card" :style="{ fontSize: `${config.fontSize}px` }">
<div v-for="(item, index) in data" :key="index" class="card-detail"> {{
<span class="b" :style="`color: ${getColors}`">{{ typeof data[key] === "string"
typeof item.avgNum === "string" ? data[key]
? item.avgNum : isNaN(data[key])
: isNaN(item.avgNum) ? null
? null : data[key].toFixed(2)
: item.avgNum.toFixed(2) }}
}}</span> <span v-show="config.showUint">{{ standard.unit }}</span>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from "vue"; import { computed, PropType } from "vue";
import { defineProps, computed } from "vue"; import { defineProps } from "vue";
import { CardConfig, StandardConfig } from "@/types/dashboard";
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object as PropType<{ [key: string]: number[] }>, type: Object as PropType<{ [key: string]: number }>,
default: () => ({}), default: () => ({}),
}, },
theme: { type: String, default: "" }, config: {
}); type: Object as PropType<CardConfig>,
default: () => ({ fontSize: 12, showUint: true }),
const getColors = computed(() => { },
return props.theme === "dark" ? "#eee" : "#333"; standard: {
type: Object as PropType<StandardConfig>,
default: () => ({ unit: "" }),
},
}); });
const key = computed(() => Object.keys(props.data)[0]);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.chart-card { .chart-card {
font-size: 14px; box-sizing: border-box;
color: #333; color: #333;
} }
.card-detail {
span:first-child {
padding-right: 8px;
box-sizing: border-box;
color: #666;
}
}
</style> </style>

View File

@ -18,7 +18,6 @@ limitations under the License. -->
</el-table> </el-table>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElTable, ElTableColumn } from "element-plus";
import { defineProps } from "vue"; import { defineProps } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
@ -27,6 +26,9 @@ defineProps({
type: Array as PropType<{ label: string; value: string }[]>, type: Array as PropType<{ label: string; value: string }[]>,
default: () => [], default: () => [],
}, },
theme: { type: String, default: "" }, config: {
type: Object,
default: () => ({}),
},
}); });
</script> </script>

View File

@ -16,9 +16,9 @@ limitations under the License. -->
<Graph :option="option" /> <Graph :option="option" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineProps, ref, computed } from "vue"; import { defineProps, computed } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
import { Event } from "@/types/events"; import { StandardConfig } from "@/types/dashboard";
const props = defineProps({ const props = defineProps({
data: { data: {
@ -26,15 +26,15 @@ const props = defineProps({
default: () => ({}), default: () => ({}),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
theme: { type: String, default: "dark" }, config: {
itemEvents: { type: Array as PropType<Event[]>, default: () => [] }, type: Object as PropType<any>,
itemConfig: { default: () => ({}),
type: Object as PropType<{ unit: string }>, },
standard: {
type: Object as PropType<StandardConfig>,
default: () => ({}), default: () => ({}),
}, },
}); });
/*global Nullable */
const chart = ref<Nullable<HTMLElement>>(null);
const option = computed(() => getOption()); const option = computed(() => getOption());
function getOption() { function getOption() {
const source = (props.data.nodes || []).map((d: number[]) => d[2]); const source = (props.data.nodes || []).map((d: number[]) => d[2]);
@ -66,7 +66,7 @@ function getOption() {
tooltip: { tooltip: {
position: "top", position: "top",
formatter: (a: any) => formatter: (a: any) =>
`${a.data[1] * 100}${props.itemConfig.unit} [ ${a.data[2]} ]`, `${a.data[1] * 100}${props.standard.unit} [ ${a.data[2]} ]`,
textStyle: { textStyle: {
fontSize: 13, fontSize: 13,
color: "#ccc", color: "#ccc",

View File

@ -18,7 +18,6 @@ limitations under the License. -->
</el-table> </el-table>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElTable, ElTableColumn } from "element-plus";
import { defineProps } from "vue"; import { defineProps } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
@ -27,6 +26,9 @@ defineProps({
type: Array as PropType<{ label: string; value: string }[]>, type: Array as PropType<{ label: string; value: string }[]>,
default: () => [], default: () => [],
}, },
theme: { type: String, default: "" }, config: {
type: Object,
default: () => ({}),
},
}); });
</script> </script>

View File

@ -24,7 +24,10 @@ const props = defineProps({
type: Array as PropType<{ name: string; value: number }[]>, type: Array as PropType<{ name: string; value: number }[]>,
default: () => [], default: () => [],
}, },
theme: { type: String, default: "dark" }, config: {
type: Object as PropType<{ sortOrder: string }>,
default: () => ({}),
},
}); });
const option = computed(() => getOption()); const option = computed(() => getOption());
function getOption() { function getOption() {
@ -40,7 +43,7 @@ function getOption() {
left: 0, left: 0,
itemWidth: 12, itemWidth: 12,
textStyle: { textStyle: {
color: props.theme === "dark" ? "#fff" : "#333", color: "#333",
}, },
}, },
series: [ series: [

View File

@ -19,28 +19,26 @@ limitations under the License. -->
<div <div
class="row header flex-h" class="row header flex-h"
:style="`width: ${nameWidth + initWidth}px`" :style="`width: ${nameWidth + initWidth}px`"
:class="{ dark: theme === 'dark' }"
> >
<div class="name" :style="`width: ${nameWidth}px`"> <div class="name" :style="`width: ${nameWidth}px`">
{{ item.tableHeaderCol1 || $t("name") }} {{ config.tableHeaderCol1 || $t("name") }}
<i class="r cp" ref="draggerName" <i class="r cp" ref="draggerName"
><rk-icon icon="settings_ethernet" ><rk-icon icon="settings_ethernet"
/></i> /></i>
</div> </div>
<div class="value-col" v-if="showTableValues"> <div class="value-col" v-if="config.showTableValues">
{{ item.tableHeaderCol2 || $t("value") }} {{ config.tableHeaderCol2 || $t("value") }}
</div> </div>
</div> </div>
<div <div
class="row flex-h" class="row flex-h"
:class="{ dark: theme === 'dark' }"
v-for="key in dataKeys" v-for="key in dataKeys"
:key="key" :key="key"
:style="`width: ${nameWidth + initWidth}px`" :style="`width: ${nameWidth + initWidth}px`"
> >
<div :style="`width: ${nameWidth}px`">{{ key }}</div> <div :style="`width: ${nameWidth}px`">{{ key }}</div>
<div class="value-col" v-if="showTableValues"> <div class="value-col" v-if="config.showTableValues">
{{ data[key][dataLength(data[key])] }} {{ data[key][data[key].length - 1 || 0] }}
</div> </div>
</div> </div>
</div> </div>
@ -54,9 +52,12 @@ const props = defineProps({
type: Object as PropType<{ [key: string]: number[][] }>, type: Object as PropType<{ [key: string]: number[][] }>,
default: () => ({}), default: () => ({}),
}, },
theme: { type: String, default: "dark" }, config: {
itemConfig: { type: Object as PropType<{
type: Object as PropType<{ showTableValues: string | boolean }>, showTableValues: boolean;
tableHeaderCol2: string;
tableHeaderCol1: string;
}>,
default: () => ({}), default: () => ({}),
}, },
}); });
@ -69,10 +70,10 @@ onMounted(() => {
if (!chartTable.value) { if (!chartTable.value) {
return; return;
} }
const width = showTableValues.value const width = props.config.showTableValues
? chartTable.value.offsetWidth / 2 ? chartTable.value.offsetWidth / 2
: chartTable.value.offsetWidth; : chartTable.value.offsetWidth;
initWidth.value = showTableValues.value initWidth.value = props.config.showTableValues
? chartTable.value.offsetWidth / 2 ? chartTable.value.offsetWidth / 2
: 0; : 0;
nameWidth.value = width - 5; nameWidth.value = width - 5;
@ -98,15 +99,6 @@ const dataKeys = computed(() => {
); );
return keys; return keys;
}); });
const showTableValues = computed(() => {
return props.itemConfig.showTableValues === "true" ||
props.itemConfig.showTableValues === true
? true
: false;
});
const dataLength = computed((param: number[]) => {
return param.length - 1 || 0;
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.chart-table { .chart-table {

View File

@ -36,7 +36,6 @@ limitations under the License. -->
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from "vue"; import type { PropType } from "vue";
import { defineProps, computed } from "vue"; import { defineProps, computed } from "vue";
import { ElProgress } from "element-plus";
import copy from "@/utils/copy"; import copy from "@/utils/copy";
const props = defineProps({ const props = defineProps({
data: { data: {
@ -45,8 +44,7 @@ const props = defineProps({
>, >,
default: () => [], default: () => [],
}, },
theme: { type: String, default: "" }, config: {
itemConfig: {
type: Object as PropType<{ sortOrder: string }>, type: Object as PropType<{ sortOrder: string }>,
default: () => ({}), default: () => ({}),
}, },
@ -65,7 +63,7 @@ const datas: any = () => {
if (!props.data.length) { if (!props.data.length) {
return []; return [];
} }
const { sortOrder } = props.itemConfig; const { sortOrder } = props.config;
const val: any = props.data; const val: any = props.data;
switch (sortOrder) { switch (sortOrder) {

View File

@ -18,7 +18,7 @@
import Area from "./Area.vue"; import Area from "./Area.vue";
import Line from "./Line.vue"; import Line from "./Line.vue";
import Bar from "./Bar.vue"; import Bar from "./Bar.vue";
// import Heatmap from "./Heatmap.vue"; import Heatmap from "./Heatmap.vue";
import TopList from "./TopList.vue"; import TopList from "./TopList.vue";
import Table from "./Table.vue"; import Table from "./Table.vue";
import Pie from "./Pie.vue"; import Pie from "./Pie.vue";
@ -27,7 +27,7 @@ import Card from "./Card.vue";
export default { export default {
Line, Line,
Bar, Bar,
// Heatmap, Heatmap,
TopList, TopList,
Area, Area,
Table, Table,