feat: query metric list and add pagination for list components

This commit is contained in:
Qiuxia Fan 2022-01-19 14:21:20 +08:00
parent 2e82d52e71
commit d9cb7500c8
9 changed files with 254 additions and 115 deletions

View File

@ -0,0 +1,18 @@
<!-- 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>add_circle_outlinecontrol_point</title>
<path d="M12 20.016q3.281 0 5.648-2.367t2.367-5.648-2.367-5.648-5.648-2.367-5.648 2.367-2.367 5.648 2.367 5.648 5.648 2.367zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93zM12.984 6.984v4.031h4.031v1.969h-4.031v4.031h-1.969v-4.031h-4.031v-1.969h4.031v-4.031h1.969z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -18,6 +18,17 @@ export const TypeOfMetrics = {
variable: "$name: String!",
query: `typeOfMetrics(name: $name)`,
};
export const listMetrics = {
variable: "$regex: String",
query: `
metrics: listMetrics(regex: $regex) {
value: name
label: name
type
catalog
}
`,
};
export const queryMetricsValues = {
variable: ["$condition: MetricsCondition!, $duration: Duration!"],
query: `

View File

@ -22,6 +22,7 @@ import {
querySortMetrics,
queryMetricsValue,
queryMetricsValues,
listMetrics,
} from "../fragments/dashboard";
export const queryTypeOfMetrics = `query typeOfMetrics(${TypeOfMetrics.variable}) {${TypeOfMetrics.query}}`;
@ -38,3 +39,5 @@ export const sortMetrics = `query queryData(${querySortMetrics.variable}) {${que
export const readMetricsValue = `query queryData(${queryMetricsValue.variable}) {${queryMetricsValue.query}}`;
export const readMetricsValues = `query queryData(${queryMetricsValues.variable}) {${queryMetricsValues.query}}`;
export const queryMetrics = `query queryData(${listMetrics.variable}) {${listMetrics.query}}`;

View File

@ -165,6 +165,13 @@ export const dashboardStore = defineStore({
return res.data;
},
async fetchMetricList(regex: string) {
const res: AxiosResponse = await graph
.query("queryMetrics")
.params({ regex });
return res.data;
},
async fetchMetricValue(config: LayoutConfig) {
// if (!config.queryMetricType) {
// return;

View File

@ -38,37 +38,22 @@ limitations under the License. -->
v-model="states.activeNames"
:style="{ '--el-collapse-header-font-size': '15px' }"
>
<el-collapse-item :title="t('selectVisualization')" name="1">
<div class="chart-types">
<span
v-for="(type, index) in states.visType"
:key="index"
@click="changeChartType(type)"
:class="{ active: type.value === states.graph.type }"
>
{{ type.label }}
</span>
</div>
</el-collapse-item>
<el-collapse-item
:title="t(states.isTable ? 'dashboardName' : 'metricName')"
name="2"
>
<div v-if="states.isTable">
<el-collapse-item :title="t('metricName')" name="1">
<div v-show="states.isTable" class="ds-name">
<Selector
:value="states.graph.dashboardName"
:options="metricOpts"
:options="states.metricList"
size="mini"
placeholder="Select a dashboard"
@change="changeDashboard"
class="selectors"
/>
</div>
<div v-else>
<div>
<Selector
:value="states.metrics"
:options="metricOpts"
:multiple="true"
:options="states.metricList"
:multiple="!states.isTable"
size="mini"
placeholder="Select a metric"
@change="changeMetrics"
@ -83,6 +68,25 @@ limitations under the License. -->
class="selectors"
v-loading="loading"
/>
<Icon
iconName="add_circle_outlinecontrol_point"
size="middle"
v-show="states.isTable"
@click="addMetricName"
class="cp"
/>
</div>
</el-collapse-item>
<el-collapse-item :title="t('selectVisualization')" name="2">
<div class="chart-types">
<span
v-for="(type, index) in states.visType"
:key="index"
@click="changeChartType(type)"
:class="{ active: type.value === states.graph.type }"
>
{{ type.label }}
</span>
</div>
</el-collapse-item>
<el-collapse-item :title="t('graphStyles')" name="3">
@ -147,6 +151,7 @@ export default defineComponent({
StandardOptions,
},
setup() {
const configHeight = document.documentElement.clientHeight - 520;
const loading = ref<boolean>(false);
const { t } = useI18n();
const dashboardStore = useDashboardStore();
@ -166,6 +171,7 @@ export default defineComponent({
standard: StandardConfig;
visType: Option[];
isTable: boolean;
metricList: (Option & { type: string })[];
}>({
metrics: selectedGrid.metrics || [],
valueTypes: [],
@ -179,14 +185,11 @@ export default defineComponent({
standard: selectedGrid.standard,
visType: [],
isTable: false,
metricList: [],
});
states.isTable = TableChartTypes.includes(states.graph.type || "");
if (states.metrics[0]) {
queryMetricType(states.metrics[0]);
}
if (params.entity === "service") {
states.visType = ChartTypes.filter(
(d: Option) => d.value !== "serviceList"
@ -201,32 +204,23 @@ export default defineComponent({
);
}
async function changeMetrics(arr: Option[]) {
setMetricType(states.metrics[0]);
async function changeMetrics(arr: (Option & { type: string })[]) {
if (!arr.length) {
states.valueTypes = [];
states.valueType = "";
return;
}
states.metrics = arr.map((d: Option) => String(d.value));
if (arr[0].value) {
queryMetricType(String(arr[0].value));
states.metrics = arr.map((d: Option) => d.value);
if (arr[0]) {
const typeOfMetrics = arr[0].type;
states.valueTypes = ValuesTypes[typeOfMetrics];
states.valueType = ValuesTypes[typeOfMetrics][0].value;
queryMetrics();
}
}
async function queryMetricType(metric: string) {
loading.value = true;
const resp = await dashboardStore.fetchMetricType(metric);
loading.value = false;
if (resp.error) {
ElMessage.error(resp.data.error);
return;
}
const { typeOfMetrics } = resp.data;
states.valueTypes = ValuesTypes[typeOfMetrics];
states.valueType = ValuesTypes[typeOfMetrics][0].value;
queryMetrics();
}
function changeValueType(val: Option[]) {
states.valueType = String(val[0].value);
states.metricQueryType = (MetricQueryTypes as any)[states.valueType];
@ -244,20 +238,6 @@ export default defineComponent({
states.graph.dashboardName = item[0].value;
}
const metricOpts = [
{ value: "service_apdex", label: "service_apdex" },
{ value: "service_sla", label: "service_sla" },
{ value: "service_cpm", label: "service_cpm" },
{ value: "service_resp_time", label: "service_resp_time" },
{ value: "service_percentile", label: "service_percentile" },
{
value: "service_mq_consume_latency",
label: "service_mq_consume_latency",
},
{ value: "service_mq_consume_count", label: "service_mq_consume_count" },
];
const configHeight = document.documentElement.clientHeight - 520;
function updateWidgetOptions(param: { [key: string]: unknown }) {
states.widget = {
...states.widget,
@ -288,10 +268,11 @@ export default defineComponent({
}
if (json.errors) {
ElMessage.error(json.errors);
return;
}
const metricVal = json.data.readMetricsValues.values.values.map(
(d: { value: number }) => d.value + 1
(d: { value: number }) => d.value
);
const m = states.metrics[0];
if (!m) {
@ -301,8 +282,28 @@ export default defineComponent({
[m]: metricVal,
};
}
async function setMetricType(regex: string) {
const json = await dashboardStore.fetchMetricList(regex);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
states.metricList = json.data.metrics || [];
const name = states.metrics[0];
if (!name) {
return;
}
const typeOfMetrics: any = states.metricList.filter(
(d: { value: string }) => d.value === name
)[0];
states.valueTypes = ValuesTypes[typeOfMetrics];
states.valueType = ValuesTypes[typeOfMetrics][0].value;
queryMetrics();
}
queryMetrics();
function addMetricName() {
console.log(states);
}
function applyConfig() {
const opts = {
@ -325,13 +326,13 @@ export default defineComponent({
t,
appStoreWithOut,
ChartTypes,
metricOpts,
updateWidgetOptions,
configHeight,
updateGraphOptions,
updateStandardOptions,
applyConfig,
changeDashboard,
addMetricName,
loading,
};
},
@ -369,7 +370,7 @@ export default defineComponent({
.render-chart {
padding: 5px;
height: 350px;
height: 360px;
width: 100%;
}
@ -427,4 +428,8 @@ span.active {
margin-top: 10px;
overflow: auto;
}
.ds-name {
margin-bottom: 10px;
}
</style>

View File

@ -18,9 +18,12 @@
import AreaConfig from "./Area.vue";
import LineConfig from "./Line.vue";
import BarConfig from "./Bar.vue";
import TableConfig from "./Table.vue";
import CardConfig from "./Card.vue";
import InstanceListConfig from "./InstanceList.vue";
import EndpointListConfig from "./EndpointList.vue";
import ServiceListConfig from "./ServiceList.vue";
import TopListConfig from "./TopList.vue";
export default {
AreaConfig,
@ -29,4 +32,7 @@ export default {
InstanceListConfig,
EndpointListConfig,
ServiceListConfig,
TableConfig,
CardConfig,
TopListConfig,
};

View File

@ -13,24 +13,36 @@ 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>
<el-table
v-loading="chartLoading"
:data="selectorStore.endpoints"
style="width: 100%; height: 100%; overflow: auto"
>
<el-table-column label="Endpoints">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/endpoint/${selectorStore.currentService}/${scope.row.value}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<div class="table">
<el-table
v-loading="chartLoading"
:data="endpoints"
style="width: 100%; height: 100%; overflow: auto"
>
<el-table-column label="Endpoints">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/endpoint/${selectorStore.currentService}/${scope.row.value}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
background
layout="prev, pager, next"
:page-size="6"
:total="selectorStore.endpoints.length"
@current-change="changePage"
@prev-click="changePage"
@next-click="changePage"
/>
</div>
</template>
<script setup lang="ts">
import { defineProps, onBeforeMount, ref } from "vue";
@ -50,6 +62,8 @@ defineProps({
});
const selectorStore = useSelectorStore();
const chartLoading = ref<boolean>(false);
const endpoints = ref<{ label: string; value: string }[]>([]);
const pageSize = 7;
onBeforeMount(async () => {
chartLoading.value = true;
@ -58,10 +72,26 @@ onBeforeMount(async () => {
chartLoading.value = false;
if (resp.errors) {
ElMessage.error(resp.errors);
return;
}
endpoints.value = selectorStore.endpoints.splice(0, pageSize);
});
function changePage(pageIndex: number) {
endpoints.value = selectorStore.endpoints.splice(pageIndex - 1, pageSize);
}
</script>
<style lang="scss" scoped>
.table {
height: 100%;
}
.pagination {
width: 100%;
text-align: center;
height: 30px;
padding: 3px 0;
}
.link {
cursor: pointer;
color: #409eff;

View File

@ -13,24 +13,36 @@ 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>
<el-table
v-loading="chartLoading"
:data="selectorStore.instances"
style="width: 100%; height: 100%; overflow: auto"
>
<el-table-column label="Service Instances">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/serviceInstance/${selectorStore.currentService}/${scope.row.value}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<div class="table">
<el-table
v-loading="chartLoading"
:data="instances"
style="width: 100%; height: 320px; overflow: auto"
>
<el-table-column label="Service Instances">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/serviceInstance/${selectorStore.currentService}/${scope.row.value}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
background
layout="prev, pager, next"
:page-size="6"
:total="selectorStore.instances.length"
@current-change="changePage"
@prev-click="changePage"
@next-click="changePage"
/>
</div>
</template>
<script setup lang="ts">
import { defineProps, onBeforeMount, ref } from "vue";
@ -50,6 +62,8 @@ defineProps({
});
const selectorStore = useSelectorStore();
const chartLoading = ref<boolean>(false);
const instances = ref<any[]>([]);
const pageSize = 7;
onBeforeMount(async () => {
chartLoading.value = true;
@ -58,10 +72,26 @@ onBeforeMount(async () => {
chartLoading.value = false;
if (resp.errors) {
ElMessage.error(resp.errors);
return;
}
instances.value = selectorStore.instances.splice(0, pageSize);
});
function changePage(pageIndex: number) {
instances.value = selectorStore.instances.splice(pageIndex - 1, pageSize);
}
</script>
<style lang="scss" scoped>
.table {
height: 100%;
}
.pagination {
width: 100%;
text-align: center;
height: 30px;
padding: 3px 0;
}
.link {
cursor: pointer;
color: #409eff;

View File

@ -13,24 +13,36 @@ 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>
<el-table
v-loading="chartLoading"
:data="selectorStore.services"
style="width: 100%; height: 100%; overflow: auto"
>
<el-table-column label="Services">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/service/${selectorStore.currentService}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<div class="table">
<el-table
v-loading="chartLoading"
:data="services"
style="width: 100%; height: 100%; overflow: auto"
>
<el-table-column label="Services">
<template #default="scope">
<router-link
target="_blank"
class="link"
:to="`/dashboard/${scope.row.layer}/service/${selectorStore.currentService}/${config.dashboardName}`"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ scope.row.label }}
</router-link>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
background
layout="prev, pager, next"
:page-size="6"
:total="selectorStore.services.length"
@current-change="changePage"
@prev-click="changePage"
@next-click="changePage"
/>
</div>
</template>
<script setup lang="ts">
import { defineProps, onBeforeMount, ref } from "vue";
@ -50,6 +62,8 @@ defineProps({
});
const selectorStore = useSelectorStore();
const chartLoading = ref<boolean>(false);
const pageSize = 6;
const services = ref<{ label: string; layer: string }>([]);
onBeforeMount(async () => {
chartLoading.value = true;
@ -59,9 +73,24 @@ onBeforeMount(async () => {
if (resp.errors) {
ElMessage.error(resp.errors);
}
services.value = selectorStore.services.splice(0, pageSize);
});
function changePage(pageIndex: number) {
services.value = selectorStore.services.splice(pageIndex - 1, pageSize);
}
</script>
<style lang="scss" scoped>
.table {
height: 100%;
}
.pagination {
width: 100%;
text-align: center;
height: 30px;
padding: 3px 0;
}
.link {
cursor: pointer;
color: #409eff;