| <!-- 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="table"> |
| <div class="search"> |
| <el-input v-model="searchText" placeholder="Please input service name" @change="searchList" class="inputs mt-5"> |
| <template #append> |
| <el-button @click="searchList"> |
| <Icon size="sm" iconName="search" /> |
| </el-button> |
| </template> |
| </el-input> |
| </div> |
| <div class="list"> |
| <el-table |
| v-loading="chartLoading" |
| :data="services" |
| style="width: 100%" |
| :span-method="objectSpanMethod" |
| :border="true" |
| :style="{ fontSize: '14px' }" |
| > |
| <el-table-column fixed label="Service Groups" v-if="config.showGroup" min-width="150"> |
| <template #default="scope"> |
| {{ scope.row.group }} |
| </template> |
| </el-table-column> |
| <el-table-column fixed label="Service Names" min-width="220"> |
| <template #default="scope"> |
| <span class="link" :style="{ fontSize: `${config.fontSize}px` }" @click="clickService(scope)"> |
| {{ scope.row.label }} |
| </span> |
| </template> |
| </el-table-column> |
| <ColumnGraph |
| :intervalTime="intervalTime" |
| :colMetrics="colMetrics" |
| :colSubMetrics="colSubMetrics" |
| :config="{ |
| ...config, |
| metricConfig, |
| metricTypes, |
| metricMode, |
| }" |
| v-if="colMetrics.length" |
| /> |
| </el-table> |
| </div> |
| <el-pagination |
| class="pagination" |
| background |
| small |
| layout="prev, pager, next" |
| :page-size="pageSize" |
| :total="selectorStore.services.length" |
| @current-change="changePage" |
| @prev-click="changePage" |
| @next-click="changePage" |
| /> |
| </div> |
| </template> |
| <script setup lang="ts"> |
| import { watch, ref } from "vue"; |
| import { ElMessage } from "element-plus"; |
| import type { PropType } from "vue"; |
| import type { ServiceListConfig } from "@/types/dashboard"; |
| import { useSelectorStore } from "@/store/modules/selectors"; |
| import { useDashboardStore } from "@/store/modules/dashboard"; |
| import { useAppStoreWithOut } from "@/store/modules/app"; |
| import type { Service } from "@/types/selector"; |
| import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor"; |
| import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; |
| import { EntityType, MetricModes } from "../data"; |
| import router from "@/router"; |
| import getDashboard from "@/hooks/useDashboardsSession"; |
| import type { MetricConfigOpt } from "@/types/dashboard"; |
| import ColumnGraph from "./components/ColumnGraph.vue"; |
| |
| /*global defineProps */ |
| const props = defineProps({ |
| data: { |
| type: Object, |
| }, |
| config: { |
| type: Object as PropType< |
| ServiceListConfig & { |
| i: string; |
| metrics: string[]; |
| metricTypes: string[]; |
| isEdit: boolean; |
| names: string[]; |
| metricConfig: MetricConfigOpt[]; |
| metricMode: string; |
| expressions: string[]; |
| typesOfMQE: string[]; |
| subExpressions: string[]; |
| subTypesOfMQE: string[]; |
| } |
| >, |
| default: () => ({ dashboardName: "", fontSize: 12 }), |
| }, |
| intervalTime: { type: Array as PropType<string[]>, default: () => [] }, |
| isEdit: { type: Boolean, default: false }, |
| }); |
| const selectorStore = useSelectorStore(); |
| const dashboardStore = useDashboardStore(); |
| const appStore = useAppStoreWithOut(); |
| const chartLoading = ref<boolean>(false); |
| const pageSize = 10; |
| const services = ref<Service[]>([]); |
| const colMetrics = ref<string[]>([]); |
| const colSubMetrics = ref<string[]>([]); |
| const searchText = ref<string>(""); |
| const groups = ref<any>({}); |
| const sortServices = ref<(Service & { merge: boolean })[]>([]); |
| const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []); |
| const metricTypes = ref<string[]>(props.config.metricTypes || []); |
| const metricMode = ref<string>(props.config.metricMode); |
| |
| queryServices(); |
| |
| async function queryServices() { |
| chartLoading.value = true; |
| const resp = await selectorStore.fetchServices(dashboardStore.layerId); |
| |
| chartLoading.value = false; |
| if (resp.errors) { |
| ElMessage.error(resp.errors); |
| } |
| sortServices.value = selectorStore.services.sort((a: any, b: any) => { |
| const groupA = a.group.toUpperCase(); |
| const groupB = b.group.toUpperCase(); |
| if (groupA < groupB) { |
| return -1; |
| } |
| if (groupA > groupB) { |
| return 1; |
| } |
| return 0; |
| }); |
| const s = sortServices.value.filter((d: Service, index: number) => index < pageSize); |
| setServices(s); |
| } |
| |
| function setServices(arr: (Service & { merge: boolean })[]) { |
| groups.value = {}; |
| const map: { [key: string]: any[] } = arr.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; |
| }, {}); |
| const list = Object.values(map).flat(1); |
| const obj = {} as any; |
| for (const s of list) { |
| s.group = s.group || ""; |
| if (!obj[s.group]) { |
| obj[s.group] = 1; |
| } else { |
| obj[s.group]++; |
| } |
| groups.value[s.group] = obj[s.group]; |
| } |
| services.value = list; |
| queryServiceMetrics(services.value); |
| } |
| |
| function clickService(scope: any) { |
| const { dashboard } = getDashboard({ |
| name: props.config.dashboardName, |
| layer: dashboardStore.layerId, |
| entity: EntityType[0].value, |
| }); |
| if (!dashboard) { |
| ElMessage.error("No this dashboard"); |
| return; |
| } |
| const path = `/dashboard/${dashboard.layer}/${dashboard.entity}/${scope.row.id}/${dashboard.name}`; |
| |
| router.push(path); |
| } |
| async function queryServiceMetrics(arr: Service[]) { |
| if (!arr.length) { |
| return; |
| } |
| const currentServices = arr.map((d: Service) => { |
| return { |
| id: d.id, |
| value: d.value, |
| label: d.label, |
| layers: d.layers, |
| group: d.group, |
| normal: d.normal, |
| merge: d.merge, |
| }; |
| }); |
| if (props.config.metricMode === MetricModes.Expression) { |
| queryServiceExpressions(currentServices); |
| return; |
| } |
| const metrics = props.config.metrics || []; |
| const types = props.config.metricTypes || []; |
| |
| if (metrics.length && metrics[0] && types.length && types[0]) { |
| const params = await useQueryPodsMetrics( |
| currentServices, |
| { ...props.config, metricConfig: metricConfig.value || [] }, |
| EntityType[0].value, |
| ); |
| const json = await dashboardStore.fetchMetricValue(params); |
| |
| if (json.errors) { |
| ElMessage.error(json.errors); |
| return; |
| } |
| |
| const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(currentServices, json, { |
| ...props.config, |
| metricConfig: metricConfig.value || [], |
| }); |
| |
| services.value = data; |
| colMetrics.value = names; |
| metricTypes.value = metricTypesArr; |
| metricConfig.value = metricConfigArr; |
| |
| return; |
| } |
| services.value = currentServices; |
| colMetrics.value = []; |
| colMetrics.value = []; |
| metricTypes.value = []; |
| metricConfig.value = []; |
| } |
| async function queryServiceExpressions(currentServices: Service[]) { |
| const expressions = props.config.expressions || []; |
| const typesOfMQE = props.config.typesOfMQE || []; |
| const subExpressions = props.config.subExpressions || []; |
| |
| if (expressions.length && expressions[0] && typesOfMQE.length && typesOfMQE[0]) { |
| const params = await useExpressionsQueryPodsMetrics( |
| currentServices, |
| { ...props.config, metricConfig: metricConfig.value || [], typesOfMQE, expressions, subExpressions }, |
| EntityType[0].value, |
| ); |
| services.value = params.data; |
| colMetrics.value = params.names; |
| colSubMetrics.value = params.subNames; |
| metricTypes.value = params.metricTypesArr; |
| metricConfig.value = params.metricConfigArr; |
| return; |
| } |
| services.value = currentServices; |
| colMetrics.value = []; |
| colSubMetrics.value = []; |
| metricTypes.value = []; |
| metricConfig.value = []; |
| } |
| function objectSpanMethod(param: any): any { |
| if (!props.config.showGroup) { |
| return; |
| } |
| if (param.columnIndex !== 0) { |
| return; |
| } |
| if (param.row.merge) { |
| return { |
| rowspan: 0, |
| colspan: 0, |
| }; |
| } |
| return { rowspan: groups.value[param.row.group], colspan: 1 }; |
| } |
| function changePage(pageIndex: number) { |
| const arr = sortServices.value.filter((d: Service, index: number) => { |
| if (index >= (pageIndex - 1) * pageSize && index < pageSize * pageIndex) { |
| return d; |
| } |
| }); |
| |
| setServices(arr); |
| } |
| function searchList() { |
| const searchServices = sortServices.value.filter((d: { label: string }) => d.label.includes(searchText.value)); |
| const services = searchServices.filter((d: unknown, index: number) => index < pageSize); |
| setServices(services); |
| } |
| |
| watch( |
| () => [ |
| ...(props.config.metricTypes || []), |
| ...(props.config.metrics || []), |
| ...(props.config.metricConfig || []), |
| ...(props.config.expressions || []), |
| ...(props.config.subExpressions || []), |
| props.config.metricMode, |
| ], |
| (data, old) => { |
| if (JSON.stringify(data) === JSON.stringify(old)) { |
| return; |
| } |
| metricConfig.value = props.config.metricConfig; |
| metricMode.value = props.config.metricMode; |
| queryServiceMetrics(services.value); |
| }, |
| ); |
| |
| watch( |
| () => appStore.durationTime, |
| () => { |
| if (dashboardStore.entity === EntityType[1].value) { |
| queryServices(); |
| } |
| }, |
| ); |
| </script> |
| <style lang="scss" scoped> |
| @import url("./style.scss"); |
| </style> |