feat: Implement templates for dashboards (#28)

This commit is contained in:
Fine0830
2022-03-19 12:11:35 +08:00
committed by GitHub
parent 1cf3887675
commit 597e98e291
61 changed files with 1583 additions and 1193 deletions

View File

@@ -16,6 +16,7 @@ limitations under the License. -->
<template>
<div
class="chart-card"
:class="{ center: config.textAlign === 'center' }"
:style="{ fontSize: `${config.fontSize}px`, textAlign: config.textAlign }"
>
{{
@@ -52,12 +53,15 @@ const singleVal = computed(() => props.data[key.value]);
</script>
<style lang="scss" scoped>
.chart-card {
box-sizing: border-box;
color: #333;
height: 100%;
}
.center {
box-sizing: border-box;
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: center;
height: 100%;
}
</style>

View File

@@ -18,54 +18,57 @@ limitations under the License. -->
<el-input
v-model="searchText"
placeholder="Please input endpoint name"
class="input-with-search"
size="small"
@change="searchList"
class="inputs"
>
<template #append>
<el-button size="small" @click="searchList">
<Icon size="lg" iconName="search" />
<Icon size="sm" iconName="search" />
</el-button>
</template>
</el-input>
</div>
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
<el-table-column label="Endpoints">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${EntityType[2].value}/${selectorStore.currentService.id}/${scope.row.id}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
<div class="list">
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
<el-table-column label="Endpoints">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${EntityType[2].value}/${selectorStore.currentService.id}/${scope.row.id}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination
class="pagination"
background
small
layout="prev, pager, next"
:page-size="pageSize"
:total="selectorStore.pods.length"
@@ -99,6 +102,7 @@ const props = defineProps({
i: string;
metrics: string[];
metricTypes: string[];
isEdit: boolean;
}
>,
default: () => ({ dashboardName: "", fontSize: 12, i: "" }),
@@ -126,6 +130,9 @@ async function queryEndpoints() {
}
searchEndpoints.value = selectorStore.pods;
endpoints.value = selectorStore.pods.splice(0, pageSize);
if (props.config.isEdit) {
return;
}
queryEndpointMetrics(endpoints.value);
}
async function queryEndpointMetrics(currentPods: Endpoint[]) {
@@ -134,7 +141,7 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
if (metrics.length && metrics[0]) {
const params = await useQueryPodsMetrics(
currentPods,
dashboardStore.selectedGrid,
props.config,
EntityType[2].value
);
const json = await dashboardStore.fetchMetricValue(params);
@@ -143,11 +150,7 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
ElMessage.error(json.errors);
return;
}
endpoints.value = usePodsSource(
currentPods,
json,
dashboardStore.selectedGrid
);
endpoints.value = usePodsSource(currentPods, json, props.config);
return;
}
endpoints.value = currentPods;
@@ -177,4 +180,8 @@ watch(
.chart {
height: 39px;
}
.inputs {
width: 300px;
}
</style>

View File

@@ -18,54 +18,57 @@ limitations under the License. -->
<el-input
v-model="searchText"
placeholder="Please input instance name"
class="input-with-search"
size="small"
@change="searchList"
class="inputs"
>
<template #append>
<el-button size="small" @click="searchList">
<Icon size="lg" iconName="search" />
<Icon size="sm" iconName="search" />
</el-button>
</template>
</el-input>
</div>
<el-table v-loading="chartLoading" :data="instances" style="width: 100%">
<el-table-column label="Service Instances">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${EntityType[3].value}/${selectorStore.currentService.id}/${scope.row.id}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="metric ? { [metric]: scope.row[metric] } : {}"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
<div class="list">
<el-table v-loading="chartLoading" :data="instances" style="width: 100%">
<el-table-column label="Service Instances">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${EntityType[3].value}/${selectorStore.currentService.id}/${scope.row.id}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="metric ? { [metric]: scope.row[metric] } : {}"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination
class="pagination"
background
small
layout="prev, pager, next"
:page-size="pageSize"
:total="searchInstances.length"
@@ -96,6 +99,7 @@ const props = defineProps({
i: string;
metrics: string[];
metricTypes: string[];
isEdit: boolean;
}
>,
default: () => ({
@@ -129,6 +133,9 @@ async function queryInstance() {
}
searchInstances.value = selectorStore.pods;
instances.value = searchInstances.value.splice(0, pageSize);
if (props.config.isEdit) {
return;
}
queryInstanceMetrics(instances.value);
}
@@ -138,7 +145,7 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
if (metrics.length && metrics[0]) {
const params = await useQueryPodsMetrics(
currentInstances,
dashboardStore.selectedGrid,
props.config,
EntityType[3].value
);
const json = await dashboardStore.fetchMetricValue(params);
@@ -147,11 +154,7 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
ElMessage.error(json.errors);
return;
}
instances.value = usePodsSource(
currentInstances,
json,
dashboardStore.selectedGrid
);
instances.value = usePodsSource(currentInstances, json, props.config);
return;
}
instances.value = currentInstances;
@@ -182,4 +185,8 @@ watch(
.chart {
height: 40px;
}
.inputs {
width: 300px;
}
</style>

View File

@@ -78,11 +78,11 @@ function getOption() {
]),
name: i,
type: "line",
symbol: "none",
barMaxWidth: 10,
symbol: "circle",
symbolSize: 8,
showSymbol: props.config.showSymbol,
step: props.config.step,
smooth: props.config.smooth,
showSymbol: true,
lineStyle: {
width: 1.5,
type: "solid",

View File

@@ -18,55 +18,72 @@ limitations under the License. -->
<el-input
v-model="searchText"
placeholder="Please input service name"
class="input-with-search"
size="small"
@change="searchList"
class="inputs mt-5"
>
<template #append>
<el-button size="small" @click="searchList">
<Icon size="lg" iconName="search" />
<Icon size="sm" iconName="search" />
</el-button>
</template>
</el-input>
</div>
<el-table v-loading="chartLoading" :data="services" style="width: 100%">
<el-table-column label="Services">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${scope.row.id}/${config.dashboardName}`"
:key="1"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
<div class="list">
<el-table
v-loading="chartLoading"
:data="services"
style="width: 100%"
:span-method="objectSpanMethod"
:border="true"
:style="{ fontSize: '14px' }"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
<el-table-column label="Service Groups" v-if="config.showGroup">
<template #default="scope">
{{ scope.row.group }}
</template>
</el-table-column>
<el-table-column label="Service Names">
<template #default="scope">
<router-link
class="link"
:to="`/dashboard/${dashboardStore.layerId}/${
EntityType[0].value
}/${scope.row.id}/${config.dashboardName.split(' ').join('-')}`"
:key="1"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination
class="pagination"
background
small
layout="prev, pager, next"
:page-size="pageSize"
:total="selectorStore.services.length"
@@ -100,6 +117,7 @@ const props = defineProps({
i: string;
metrics: string[];
metricTypes: string[];
isEdit: boolean;
}
>,
default: () => ({ dashboardName: "", fontSize: 12 }),
@@ -113,6 +131,7 @@ const pageSize = 5;
const services = ref<Service[]>([]);
const searchServices = ref<Service[]>([]);
const searchText = ref<string>("");
const groups = ref<any>({});
queryServices();
@@ -124,7 +143,34 @@ async function queryServices() {
if (resp.errors) {
ElMessage.error(resp.errors);
}
services.value = selectorStore.services.splice(0, pageSize);
const map: { [key: string]: any[] } = selectorStore.services.reduce(
(result: { [key: string]: any[] }, item: any) => {
item.group = item.group || "";
if (result[item.group]) {
item.merge = true;
} else {
item.merge = false;
result[item.group] = [];
}
result[item.group].push(item);
return result;
},
{}
);
services.value = Object.values(map).flat(1).splice(0, pageSize);
const obj = {} as any;
for (const s of services.value) {
s.group = s.group || "";
if (!obj[s.group]) {
obj[s.group] = 1;
} else {
obj[s.group]++;
}
groups.value[s.group] = obj[s.group];
}
if (props.config.isEdit) {
return;
}
queryServiceMetrics(services.value);
}
async function queryServiceMetrics(currentServices: Service[]) {
@@ -133,7 +179,7 @@ async function queryServiceMetrics(currentServices: Service[]) {
if (metrics.length && metrics[0]) {
const params = await useQueryPodsMetrics(
currentServices,
dashboardStore.selectedGrid,
props.config,
EntityType[0].value
);
const json = await dashboardStore.fetchMetricValue(params);
@@ -142,15 +188,27 @@ async function queryServiceMetrics(currentServices: Service[]) {
ElMessage.error(json.errors);
return;
}
services.value = usePodsSource(
currentServices,
json,
dashboardStore.selectedGrid
);
services.value = usePodsSource(currentServices, json, props.config);
return;
}
services.value = currentServices;
}
function objectSpanMethod(param: any): any {
if (!props.config.showGroup) {
return;
}
if (param.columnIndex !== 0) {
return;
}
if (param.row.merge) {
return {
rowspan: 0,
colspan: 0,
};
} else {
return { rowspan: groups.value[param.row.group], colspan: 1 };
}
}
function changePage(pageIndex: number) {
services.value = selectorStore.services.splice(pageIndex - 1, pageSize);
}
@@ -175,4 +233,8 @@ watch(
.chart {
height: 39px;
}
.inputs {
width: 300px;
}
</style>

View File

@@ -21,13 +21,13 @@ limitations under the License. -->
:style="`width: ${nameWidth + initWidth}px`"
>
<div class="name" :style="`width: ${nameWidth}px`">
{{ config.tableHeaderCol1 || $t("name") }}
{{ config.graph.tableHeaderCol1 || t("name") }}
<i class="r cp" ref="draggerName">
<Icon iconName="settings_ethernet" size="middle" />
</i>
</div>
<div class="value-col" v-if="config.showTableValues">
{{ config.tableHeaderCol2 || $t("value") }}
<div class="value-col" v-if="showTableValues">
{{ config.graph.tableHeaderCol2 || t("value") }}
</div>
</div>
<div
@@ -37,8 +37,12 @@ limitations under the License. -->
:style="`width: ${nameWidth + initWidth}px`"
>
<div :style="`width: ${nameWidth}px`">{{ key }}</div>
<div class="value-col" v-if="config.showTableValues">
{{ data[key][data[key].length - 1 || 0] }}
<div class="value-col" v-if="showTableValues">
{{
config.metricTypes[0] === "readMetricsValue"
? data[key]
: data[key][data[key].length - 1 || 0]
}}
</div>
</div>
</div>
@@ -47,6 +51,7 @@ limitations under the License. -->
<script lang="ts" setup>
import { computed, ref, onMounted } from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
/*global defineProps */
const props = defineProps({
data: {
@@ -55,26 +60,32 @@ const props = defineProps({
},
config: {
type: Object as PropType<{
showTableValues: boolean;
tableHeaderCol2: string;
tableHeaderCol1: string;
graph: {
showTableValues: boolean;
tableHeaderCol2: string;
tableHeaderCol1: string;
};
metricTypes: string[];
}>,
default: () => ({}),
default: () => ({ showTableValues: true }),
},
});
/*global Nullable*/
const { t } = useI18n();
const chartTable = ref<Nullable<HTMLElement>>(null);
const initWidth = ref<number>(0);
const nameWidth = ref<number>(0);
const draggerName = ref<Nullable<HTMLElement>>(null);
const showTableValues = ref<boolean>(props.config.graph.showTableValues);
onMounted(() => {
if (!chartTable.value) {
return;
}
const width = props.config.showTableValues
const width = props.config.graph.showTableValues
? chartTable.value.offsetWidth / 2
: chartTable.value.offsetWidth;
initWidth.value = props.config.showTableValues
initWidth.value = props.config.graph.showTableValues
? chartTable.value.offsetWidth / 2
: 0;
nameWidth.value = width - 5;
@@ -95,8 +106,12 @@ onMounted(() => {
};
});
const dataKeys = computed(() => {
if (props.config.metricTypes[0] === "readMetricsValue") {
const keys = Object.keys(props.data || {});
return keys;
}
const keys = Object.keys(props.data || {}).filter(
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
(i: string) => Array.isArray(props.data[i]) && props.data[i].length
);
return keys;
});

View File

@@ -20,6 +20,11 @@
padding: 0 10px 5px 0;
}
.list {
margin-top: 10px;
margin-bottom: 10px;
}
.pagination {
width: 100%;
text-align: center;