mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-10-15 04:09:14 +00:00
build: migrate the build tool from vue-cli to vite4 (#208)
This commit is contained in:
@@ -14,44 +14,34 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<Line
|
||||
:data="data"
|
||||
:intervalTime="intervalTime"
|
||||
:config="config"
|
||||
@click="clickEvent"
|
||||
/>
|
||||
<Line :data="data" :intervalTime="intervalTime" :config="config" @click="clickEvent" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import Line from "./Line.vue";
|
||||
import {
|
||||
AreaConfig,
|
||||
EventParams,
|
||||
RelatedTrace,
|
||||
Filters,
|
||||
} from "@/types/dashboard";
|
||||
import type { PropType } from "vue";
|
||||
import Line from "./Line.vue";
|
||||
import type { AreaConfig, EventParams, RelatedTrace, Filters } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
AreaConfig & {
|
||||
filters: Filters;
|
||||
relatedTrace: RelatedTrace;
|
||||
id: string;
|
||||
associate: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
AreaConfig & {
|
||||
filters: Filters;
|
||||
relatedTrace: RelatedTrace;
|
||||
id: string;
|
||||
associate: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@@ -14,141 +14,124 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="graph" :class="isRight ? 'flex-h' : 'flex-v'">
|
||||
<Graph
|
||||
:option="option"
|
||||
@select="clickEvent"
|
||||
:filters="config.filters"
|
||||
:associate="config.associate || []"
|
||||
/>
|
||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" :associate="config.associate || []" />
|
||||
<Legend :config="config.legend" :data="data" :intervalTime="intervalTime" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import {
|
||||
BarConfig,
|
||||
EventParams,
|
||||
RelatedTrace,
|
||||
Filters,
|
||||
} from "@/types/dashboard";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import type { BarConfig, EventParams, RelatedTrace, Filters } from "@/types/dashboard";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
BarConfig & {
|
||||
filters: Filters;
|
||||
relatedTrace: RelatedTrace;
|
||||
id: string;
|
||||
associate: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
|
||||
props.config.legend
|
||||
);
|
||||
const option = computed(() => getOption());
|
||||
|
||||
function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const temp = keys.map((i: string) => {
|
||||
if (!props.intervalTime) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
data: props.data[i].map((item: number, itemIndex: number) => [
|
||||
props.intervalTime[itemIndex],
|
||||
item,
|
||||
]),
|
||||
name: i,
|
||||
type: "bar",
|
||||
symbol: "none",
|
||||
stack: "sum",
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
type: "dotted",
|
||||
},
|
||||
showBackground: props.config.showBackground,
|
||||
backgroundStyle: {
|
||||
color: "rgba(180, 180, 180, 0.1)",
|
||||
},
|
||||
};
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
BarConfig & {
|
||||
filters: Filters;
|
||||
relatedTrace: RelatedTrace;
|
||||
id: string;
|
||||
associate: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const color: string[] = chartColors(keys);
|
||||
return {
|
||||
color,
|
||||
tooltip: {
|
||||
trigger: "none",
|
||||
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(props.config.legend);
|
||||
const option = computed(() => getOption());
|
||||
|
||||
function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter((i: any) => Array.isArray(props.data[i]) && props.data[i].length);
|
||||
const temp = keys.map((i: string) => {
|
||||
if (!props.intervalTime) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
data: props.data[i].map((item: number, itemIndex: number) => [props.intervalTime[itemIndex], item]),
|
||||
name: i,
|
||||
type: "bar",
|
||||
symbol: "none",
|
||||
stack: "sum",
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
type: "dotted",
|
||||
},
|
||||
showBackground: props.config.showBackground,
|
||||
backgroundStyle: {
|
||||
color: "rgba(180, 180, 180, 0.1)",
|
||||
},
|
||||
};
|
||||
});
|
||||
const color: string[] = chartColors(keys);
|
||||
return {
|
||||
color,
|
||||
tooltip: {
|
||||
trigger: "none",
|
||||
axisPointer: {
|
||||
type: "cross",
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: showEchartsLegend(keys),
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: "#333",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: keys.length === 1 ? 15 : 40,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel: true,
|
||||
},
|
||||
axisPointer: {
|
||||
type: "cross",
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
label: {
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: showEchartsLegend(keys),
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: "#333",
|
||||
xAxis: {
|
||||
type: "category",
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca41" },
|
||||
alignWithLabel: true,
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: keys.length === 1 ? 15 : 40,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel: true,
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
yAxis: {
|
||||
type: "value",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca41" },
|
||||
alignWithLabel: true,
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@@ -30,52 +30,49 @@ limitations under the License. -->
|
||||
<div class="center no-data" v-else>{{ t("noData") }}</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { CardConfig, MetricConfigOpt } from "@/types/dashboard";
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { CardConfig, MetricConfigOpt } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<CardConfig & { metricConfig?: MetricConfigOpt[] }>,
|
||||
default: () => ({
|
||||
fontSize: 12,
|
||||
showUnit: true,
|
||||
textAlign: "center",
|
||||
metricConfig: [],
|
||||
}),
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const metricConfig = computed(() => props.config.metricConfig || []);
|
||||
const key = computed(() => Object.keys(props.data)[0]);
|
||||
const singleVal = computed(() => Number(props.data[key.value]));
|
||||
const unit = computed(
|
||||
() =>
|
||||
metricConfig.value[0] &&
|
||||
encodeURIComponent(metricConfig.value[0].unit || "")
|
||||
);
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<CardConfig & { metricConfig?: MetricConfigOpt[] }>,
|
||||
default: () => ({
|
||||
fontSize: 12,
|
||||
showUnit: true,
|
||||
textAlign: "center",
|
||||
metricConfig: [],
|
||||
}),
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const metricConfig = computed(() => props.config.metricConfig || []);
|
||||
const key = computed(() => Object.keys(props.data)[0]);
|
||||
const singleVal = computed(() => Number(props.data[key.value]));
|
||||
const unit = computed(() => metricConfig.value[0] && encodeURIComponent(metricConfig.value[0].unit || ""));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chart-card {
|
||||
color: #333;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.chart-card {
|
||||
color: #333;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
height: 100%;
|
||||
color: #666;
|
||||
}
|
||||
.no-data {
|
||||
height: 100%;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.unit {
|
||||
display: inline-block;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.unit {
|
||||
display: inline-block;
|
||||
margin-left: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -15,12 +15,7 @@ limitations under the License. -->
|
||||
<template>
|
||||
<div class="table">
|
||||
<div class="search">
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
placeholder="Search for more endpoints"
|
||||
@change="searchList"
|
||||
class="inputs"
|
||||
>
|
||||
<el-input v-model="searchText" placeholder="Search for more endpoints" @change="searchList" class="inputs">
|
||||
<template #append>
|
||||
<el-button @click="searchList" class="btn">
|
||||
<Icon size="middle" iconName="search" />
|
||||
@@ -33,11 +28,7 @@ limitations under the License. -->
|
||||
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
|
||||
<el-table-column label="Endpoints" fixed min-width="220">
|
||||
<template #default="scope">
|
||||
<span
|
||||
class="link"
|
||||
@click="clickEndpoint(scope)"
|
||||
:style="{ fontSize: `${config.fontSize}px` }"
|
||||
>
|
||||
<span class="link" @click="clickEndpoint(scope)" :style="{ fontSize: `${config.fontSize}px` }">
|
||||
{{ scope.row.label }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -58,152 +49,137 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { PropType } from "vue";
|
||||
import { EndpointListConfig } from "@/types/dashboard";
|
||||
import { Endpoint } from "@/types/selector";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import {
|
||||
useQueryPodsMetrics,
|
||||
usePodsSource,
|
||||
} from "@/hooks/useMetricsProcessor";
|
||||
import { EntityType } from "../data";
|
||||
import router from "@/router";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { MetricConfigOpt } from "@/types/dashboard";
|
||||
import ColumnGraph from "./components/ColumnGraph.vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { PropType } from "vue";
|
||||
import type { EndpointListConfig } from "@/types/dashboard";
|
||||
import type { Endpoint } from "@/types/selector";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor";
|
||||
import { EntityType } 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<
|
||||
EndpointListConfig & {
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
} & { metricConfig: MetricConfigOpt[] }
|
||||
>,
|
||||
default: () => ({
|
||||
metrics: [],
|
||||
metricTypes: [],
|
||||
dashboardName: "",
|
||||
fontSize: 12,
|
||||
i: "",
|
||||
}),
|
||||
},
|
||||
needQuery: { type: Boolean, default: false },
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const endpoints = ref<Endpoint[]>([]);
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = ref<string[]>([]);
|
||||
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||
|
||||
if (props.needQuery) {
|
||||
queryEndpoints();
|
||||
}
|
||||
async function queryEndpoints() {
|
||||
chartLoading.value = true;
|
||||
const resp = await selectorStore.getEndpoints({
|
||||
keyword: searchText.value,
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
EndpointListConfig & {
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
} & { metricConfig: MetricConfigOpt[] }
|
||||
>,
|
||||
default: () => ({
|
||||
metrics: [],
|
||||
metricTypes: [],
|
||||
dashboardName: "",
|
||||
fontSize: 12,
|
||||
i: "",
|
||||
}),
|
||||
},
|
||||
needQuery: { type: Boolean, default: false },
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
|
||||
chartLoading.value = false;
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return;
|
||||
}
|
||||
endpoints.value = selectorStore.pods;
|
||||
queryEndpointMetrics(endpoints.value);
|
||||
}
|
||||
async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
||||
if (!currentPods.length) {
|
||||
return;
|
||||
}
|
||||
const metrics = props.config.metrics || [];
|
||||
const types = props.config.metricTypes || [];
|
||||
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||
const params = await useQueryPodsMetrics(
|
||||
currentPods,
|
||||
props.config,
|
||||
EntityType[2].value
|
||||
);
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
const { t } = useI18n();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const endpoints = ref<Endpoint[]>([]);
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = ref<string[]>([]);
|
||||
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
|
||||
currentPods,
|
||||
json,
|
||||
{
|
||||
...props.config,
|
||||
metricConfig: metricConfig.value,
|
||||
}
|
||||
);
|
||||
endpoints.value = data;
|
||||
colMetrics.value = names;
|
||||
metricTypes.value = metricTypesArr;
|
||||
metricConfig.value = metricConfigArr;
|
||||
return;
|
||||
}
|
||||
endpoints.value = currentPods;
|
||||
}
|
||||
function clickEndpoint(scope: any) {
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[2].value,
|
||||
});
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${selectorStore.currentService.id}/${scope.row.id}/${dashboard.name}`
|
||||
);
|
||||
}
|
||||
async function searchList() {
|
||||
await queryEndpoints();
|
||||
}
|
||||
watch(
|
||||
() => [
|
||||
...(props.config.metricTypes || []),
|
||||
...(props.config.metrics || []),
|
||||
...(props.config.metricConfig || []),
|
||||
],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
queryEndpointMetrics(endpoints.value);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
() => {
|
||||
if (props.needQuery) {
|
||||
queryEndpoints();
|
||||
}
|
||||
);
|
||||
async function queryEndpoints() {
|
||||
chartLoading.value = true;
|
||||
const resp = await selectorStore.getEndpoints({
|
||||
keyword: searchText.value,
|
||||
});
|
||||
|
||||
chartLoading.value = false;
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return;
|
||||
}
|
||||
endpoints.value = selectorStore.pods;
|
||||
queryEndpointMetrics(endpoints.value);
|
||||
}
|
||||
async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
||||
if (!currentPods.length) {
|
||||
return;
|
||||
}
|
||||
const metrics = props.config.metrics || [];
|
||||
const types = props.config.metricTypes || [];
|
||||
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||
const params = await useQueryPodsMetrics(currentPods, props.config, EntityType[2].value);
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(currentPods, json, {
|
||||
...props.config,
|
||||
metricConfig: metricConfig.value,
|
||||
});
|
||||
endpoints.value = data;
|
||||
colMetrics.value = names;
|
||||
metricTypes.value = metricTypesArr;
|
||||
metricConfig.value = metricConfigArr;
|
||||
return;
|
||||
}
|
||||
endpoints.value = currentPods;
|
||||
}
|
||||
function clickEndpoint(scope: any) {
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[2].value,
|
||||
});
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${selectorStore.currentService.id}/${scope.row.id}/${dashboard.name}`,
|
||||
);
|
||||
}
|
||||
async function searchList() {
|
||||
await queryEndpoints();
|
||||
}
|
||||
watch(
|
||||
() => [...(props.config.metricTypes || []), ...(props.config.metrics || []), ...(props.config.metricConfig || [])],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
queryEndpointMetrics(endpoints.value);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
() => {
|
||||
queryEndpoints();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
@import "./style.scss";
|
||||
|
||||
.tips {
|
||||
color: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
.tips {
|
||||
color: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
@@ -16,147 +16,147 @@ limitations under the License. -->
|
||||
<Graph :option="option" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{
|
||||
[key: string]: { nodes: number[][]; buckets: number[][] };
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({ metrics: [] }),
|
||||
},
|
||||
});
|
||||
const option = computed(() => getOption());
|
||||
|
||||
function getOption() {
|
||||
const metric = props.config.metrics && props.config.metrics[0];
|
||||
let nodes: any = [];
|
||||
if (metric) {
|
||||
nodes = (props.data[metric] && props.data[metric].nodes) || [];
|
||||
}
|
||||
const source = (nodes || []).map((d: number[]) => d[2]);
|
||||
const maxItem = Math.max(...source);
|
||||
const minItem = Math.min(...source);
|
||||
const colorBox = [
|
||||
"#fff",
|
||||
"#FDF0F0",
|
||||
"#FAE2E2",
|
||||
"#F8D3D3",
|
||||
"#F6C4C4",
|
||||
"#F4B5B5",
|
||||
"#F1A7A7",
|
||||
"#EF9898",
|
||||
"#E86C6C",
|
||||
"#E44E4E",
|
||||
"#E23F3F",
|
||||
"#DF3131",
|
||||
"#DD2222",
|
||||
"#CE2020",
|
||||
"#C01D1D",
|
||||
"#B11B1B",
|
||||
"#A21919",
|
||||
"#851414",
|
||||
"#761212",
|
||||
"#671010",
|
||||
];
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
position: "top",
|
||||
// formatter: (a: any) =>
|
||||
// `${a.data[1] * 100}${props.standard.unit} [ ${a.data[2]} ]`,
|
||||
// textStyle: {
|
||||
// fontSize: 13,
|
||||
// color: "#ccc",
|
||||
// },
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{
|
||||
[key: string]: { nodes: number[][]; buckets: number[][] };
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel: true,
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({ metrics: [] }),
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: props.intervalTime,
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca" },
|
||||
alignWithLabel: true,
|
||||
});
|
||||
const option = computed(() => getOption());
|
||||
|
||||
function getOption() {
|
||||
const metric = props.config.metrics && props.config.metrics[0];
|
||||
let nodes: any = [];
|
||||
if (metric) {
|
||||
nodes = (props.data[metric] && props.data[metric].nodes) || [];
|
||||
}
|
||||
const source = (nodes || []).map((d: number[]) => d[2]);
|
||||
const maxItem = Math.max(...source);
|
||||
const minItem = Math.min(...source);
|
||||
const colorBox = [
|
||||
"#fff",
|
||||
"#FDF0F0",
|
||||
"#FAE2E2",
|
||||
"#F8D3D3",
|
||||
"#F6C4C4",
|
||||
"#F4B5B5",
|
||||
"#F1A7A7",
|
||||
"#EF9898",
|
||||
"#E86C6C",
|
||||
"#E44E4E",
|
||||
"#E23F3F",
|
||||
"#DF3131",
|
||||
"#DD2222",
|
||||
"#CE2020",
|
||||
"#C01D1D",
|
||||
"#B11B1B",
|
||||
"#A21919",
|
||||
"#851414",
|
||||
"#761212",
|
||||
"#671010",
|
||||
];
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
position: "top",
|
||||
// formatter: (a: any) =>
|
||||
// `${a.data[1] * 100}${props.standard.unit} [ ${a.data[2]} ]`,
|
||||
// textStyle: {
|
||||
// fontSize: 13,
|
||||
// color: "#ccc",
|
||||
// },
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
visualMap: [
|
||||
{
|
||||
min: minItem,
|
||||
max: maxItem,
|
||||
show: false,
|
||||
type: "piecewise",
|
||||
calculable: true,
|
||||
pieces: generatePieces(maxItem, colorBox, minItem),
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel: true,
|
||||
},
|
||||
],
|
||||
yAxis: {
|
||||
type: "category",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca", type: "dashed" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
data: props.data.buckets,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "heatmap",
|
||||
data: nodes || [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: props.intervalTime,
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca" },
|
||||
alignWithLabel: true,
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
visualMap: [
|
||||
{
|
||||
min: minItem,
|
||||
max: maxItem,
|
||||
show: false,
|
||||
type: "piecewise",
|
||||
calculable: true,
|
||||
pieces: generatePieces(maxItem, colorBox, minItem),
|
||||
},
|
||||
],
|
||||
yAxis: {
|
||||
type: "category",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca", type: "dashed" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
data: props.data.buckets,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "heatmap",
|
||||
data: nodes || [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
function generatePieces(maxValue: number, colorBox: string[], minItem: number) {
|
||||
if (maxValue < minItem) {
|
||||
return [];
|
||||
],
|
||||
};
|
||||
}
|
||||
const pieces = [];
|
||||
let quotient = 1;
|
||||
let temp = {} as { min: number; max: number; color: string };
|
||||
temp.max = minItem;
|
||||
temp.min = minItem;
|
||||
temp.color = colorBox[0];
|
||||
pieces.push(temp);
|
||||
if (maxValue && maxValue >= 19) {
|
||||
quotient = Math.floor(maxValue / 19);
|
||||
for (let i = 1; i < 20; i++) {
|
||||
temp = {} as any;
|
||||
if (i === 1) {
|
||||
temp.min = minItem;
|
||||
} else {
|
||||
temp.min = quotient * (i - 1);
|
||||
}
|
||||
temp.max = quotient * i;
|
||||
temp.color = colorBox[i];
|
||||
pieces.push(temp);
|
||||
function generatePieces(maxValue: number, colorBox: string[], minItem: number) {
|
||||
if (maxValue < minItem) {
|
||||
return [];
|
||||
}
|
||||
const pieces = [];
|
||||
let quotient = 1;
|
||||
let temp = {} as { min: number; max: number; color: string };
|
||||
temp.max = minItem;
|
||||
temp.min = minItem;
|
||||
temp.color = colorBox[0];
|
||||
pieces.push(temp);
|
||||
if (maxValue && maxValue >= 19) {
|
||||
quotient = Math.floor(maxValue / 19);
|
||||
for (let i = 1; i < 20; i++) {
|
||||
temp = {} as any;
|
||||
if (i === 1) {
|
||||
temp.min = minItem;
|
||||
} else {
|
||||
temp.min = quotient * (i - 1);
|
||||
}
|
||||
temp.max = quotient * i;
|
||||
temp.color = colorBox[i];
|
||||
pieces.push(temp);
|
||||
}
|
||||
}
|
||||
const length = pieces.length;
|
||||
if (length) {
|
||||
const item = pieces[length - 1];
|
||||
item.max = maxValue;
|
||||
}
|
||||
return pieces;
|
||||
}
|
||||
const length = pieces.length;
|
||||
if (length) {
|
||||
const item = pieces[length - 1];
|
||||
item.max = maxValue;
|
||||
}
|
||||
return pieces;
|
||||
}
|
||||
</script>
|
||||
|
@@ -15,12 +15,7 @@ limitations under the License. -->
|
||||
<template>
|
||||
<div class="table">
|
||||
<div class="search">
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
placeholder="Please input instance name"
|
||||
@change="searchList"
|
||||
class="inputs"
|
||||
>
|
||||
<el-input v-model="searchText" placeholder="Please input instance name" @change="searchList" class="inputs">
|
||||
<template #append>
|
||||
<el-button class="btn" @click="searchList">
|
||||
<Icon size="sm" iconName="search" />
|
||||
@@ -32,11 +27,7 @@ limitations under the License. -->
|
||||
<el-table v-loading="chartLoading" :data="instances" style="width: 100%">
|
||||
<el-table-column label="Service Instances" fixed min-width="320">
|
||||
<template #default="scope">
|
||||
<span
|
||||
class="link"
|
||||
@click="clickInstance(scope)"
|
||||
:style="{ fontSize: `${config.fontSize}px` }"
|
||||
>
|
||||
<span class="link" @click="clickInstance(scope)" :style="{ fontSize: `${config.fontSize}px` }">
|
||||
{{ scope.row.label }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -87,174 +78,153 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ElMessage } from "element-plus";
|
||||
import type { PropType } from "vue";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { InstanceListConfig } from "@/types/dashboard";
|
||||
import { Instance } from "@/types/selector";
|
||||
import {
|
||||
useQueryPodsMetrics,
|
||||
usePodsSource,
|
||||
} from "@/hooks/useMetricsProcessor";
|
||||
import { EntityType } from "../data";
|
||||
import router from "@/router";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { MetricConfigOpt } from "@/types/dashboard";
|
||||
import ColumnGraph from "./components/ColumnGraph.vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ElMessage } from "element-plus";
|
||||
import type { PropType } from "vue";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import type { InstanceListConfig } from "@/types/dashboard";
|
||||
import type { Instance } from "@/types/selector";
|
||||
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor";
|
||||
import { EntityType } 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({
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
InstanceListConfig & {
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
isEdit: boolean;
|
||||
} & { metricConfig: MetricConfigOpt[] }
|
||||
>,
|
||||
default: () => ({
|
||||
dashboardName: "",
|
||||
fontSize: 12,
|
||||
i: "",
|
||||
metrics: [],
|
||||
metricTypes: [],
|
||||
}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
needQuery: { type: Boolean, default: false },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const instances = ref<Instance[]>([]); // current instances
|
||||
const pageSize = 10;
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = ref<string[]>([]);
|
||||
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||
if (props.needQuery) {
|
||||
queryInstance();
|
||||
}
|
||||
|
||||
async function queryInstance() {
|
||||
chartLoading.value = true;
|
||||
const resp = await selectorStore.getServiceInstances();
|
||||
|
||||
chartLoading.value = false;
|
||||
if (resp && resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
instances.value = [];
|
||||
return;
|
||||
}
|
||||
instances.value = selectorStore.pods.filter(
|
||||
(d: unknown, index: number) => index < pageSize
|
||||
);
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
async function queryInstanceMetrics(currentInstances: Instance[]) {
|
||||
if (!currentInstances.length) {
|
||||
return;
|
||||
}
|
||||
const metrics = props.config.metrics || [];
|
||||
const types = props.config.metricTypes || [];
|
||||
|
||||
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||
const params = await useQueryPodsMetrics(
|
||||
currentInstances,
|
||||
props.config,
|
||||
EntityType[3].value
|
||||
);
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
|
||||
currentInstances,
|
||||
json,
|
||||
{
|
||||
...props.config,
|
||||
metricConfig: metricConfig.value,
|
||||
}
|
||||
);
|
||||
instances.value = data;
|
||||
colMetrics.value = names;
|
||||
metricTypes.value = metricTypesArr;
|
||||
metricConfig.value = metricConfigArr;
|
||||
return;
|
||||
}
|
||||
instances.value = currentInstances;
|
||||
}
|
||||
|
||||
function clickInstance(scope: any) {
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[3].value,
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
InstanceListConfig & {
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
isEdit: boolean;
|
||||
} & { metricConfig: MetricConfigOpt[] }
|
||||
>,
|
||||
default: () => ({
|
||||
dashboardName: "",
|
||||
fontSize: 12,
|
||||
i: "",
|
||||
metrics: [],
|
||||
metricTypes: [],
|
||||
}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
needQuery: { type: Boolean, default: false },
|
||||
});
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${
|
||||
selectorStore.currentService.id
|
||||
}/${scope.row.id}/${dashboard.name.split(" ").join("-")}`
|
||||
);
|
||||
}
|
||||
|
||||
function changePage(pageIndex: number) {
|
||||
instances.value = selectorStore.pods.filter((d: unknown, index: number) => {
|
||||
if (index >= (pageIndex - 1) * pageSize && index < pageIndex * pageSize) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
function searchList() {
|
||||
const searchInstances = selectorStore.pods.filter((d: { label: string }) =>
|
||||
d.label.includes(searchText.value)
|
||||
);
|
||||
instances.value = searchInstances.filter(
|
||||
(d: unknown, index: number) => index < pageSize
|
||||
);
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [
|
||||
...(props.config.metricTypes || []),
|
||||
...(props.config.metrics || []),
|
||||
...(props.config.metricConfig || []),
|
||||
],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
() => {
|
||||
const { t } = useI18n();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const instances = ref<Instance[]>([]); // current instances
|
||||
const pageSize = 10;
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = ref<string[]>([]);
|
||||
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||
if (props.needQuery) {
|
||||
queryInstance();
|
||||
}
|
||||
);
|
||||
|
||||
async function queryInstance() {
|
||||
chartLoading.value = true;
|
||||
const resp = await selectorStore.getServiceInstances();
|
||||
|
||||
chartLoading.value = false;
|
||||
if (resp && resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
instances.value = [];
|
||||
return;
|
||||
}
|
||||
instances.value = selectorStore.pods.filter((d: unknown, index: number) => index < pageSize);
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
async function queryInstanceMetrics(currentInstances: Instance[]) {
|
||||
if (!currentInstances.length) {
|
||||
return;
|
||||
}
|
||||
const metrics = props.config.metrics || [];
|
||||
const types = props.config.metricTypes || [];
|
||||
|
||||
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||
const params = await useQueryPodsMetrics(currentInstances, props.config, EntityType[3].value);
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(currentInstances, json, {
|
||||
...props.config,
|
||||
metricConfig: metricConfig.value,
|
||||
});
|
||||
instances.value = data;
|
||||
colMetrics.value = names;
|
||||
metricTypes.value = metricTypesArr;
|
||||
metricConfig.value = metricConfigArr;
|
||||
return;
|
||||
}
|
||||
instances.value = currentInstances;
|
||||
}
|
||||
|
||||
function clickInstance(scope: any) {
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[3].value,
|
||||
});
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${selectorStore.currentService.id}/${
|
||||
scope.row.id
|
||||
}/${dashboard.name.split(" ").join("-")}`,
|
||||
);
|
||||
}
|
||||
|
||||
function changePage(pageIndex: number) {
|
||||
instances.value = selectorStore.pods.filter((d: unknown, index: number) => {
|
||||
if (index >= (pageIndex - 1) * pageSize && index < pageIndex * pageSize) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
function searchList() {
|
||||
const searchInstances = selectorStore.pods.filter((d: { label: string }) => d.label.includes(searchText.value));
|
||||
instances.value = searchInstances.filter((d: unknown, index: number) => index < pageSize);
|
||||
queryInstanceMetrics(instances.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [...(props.config.metricTypes || []), ...(props.config.metrics || []), ...(props.config.metricConfig || [])],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
queryInstanceMetrics(instances.value);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
() => {
|
||||
queryInstance();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
@import "./style.scss";
|
||||
|
||||
.attributes {
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
.attributes {
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
@@ -25,174 +25,161 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import {
|
||||
LineConfig,
|
||||
EventParams,
|
||||
RelatedTrace,
|
||||
Filters,
|
||||
} from "@/types/dashboard";
|
||||
import Legend from "./components/Legend.vue";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
import { computed, ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import type { LineConfig, EventParams, RelatedTrace, Filters } from "@/types/dashboard";
|
||||
import Legend from "./components/Legend.vue";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
LineConfig & {
|
||||
filters?: Filters;
|
||||
relatedTrace?: RelatedTrace;
|
||||
id?: string;
|
||||
associate: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({
|
||||
step: false,
|
||||
smooth: false,
|
||||
showSymbol: false,
|
||||
opacity: 0.4,
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
smallTips: false,
|
||||
showlabels: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
const setRight = ref<boolean>(false);
|
||||
const option = computed(() => getOption());
|
||||
function getOption() {
|
||||
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
|
||||
props.config.legend
|
||||
);
|
||||
setRight.value = isRight;
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const temp = keys.map((i: any) => {
|
||||
const serie: any = {
|
||||
data: props.data[i].map((item: any, itemIndex: number) => [
|
||||
props.intervalTime[itemIndex],
|
||||
item,
|
||||
]),
|
||||
name: i,
|
||||
type: "line",
|
||||
symbol: "circle",
|
||||
symbolSize: 8,
|
||||
showSymbol: props.config.showSymbol,
|
||||
step: props.config.step,
|
||||
smooth: props.config.smooth,
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
type: "solid",
|
||||
},
|
||||
};
|
||||
if (props.config.type === "Area") {
|
||||
serie.areaStyle = {
|
||||
opacity: props.config.opacity || 0.4,
|
||||
};
|
||||
}
|
||||
return serie;
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
config: {
|
||||
type: Object as PropType<
|
||||
LineConfig & {
|
||||
filters?: Filters;
|
||||
relatedTrace?: RelatedTrace;
|
||||
id?: string;
|
||||
associate?: { widgetId: string }[];
|
||||
}
|
||||
>,
|
||||
default: () => ({
|
||||
step: false,
|
||||
smooth: false,
|
||||
showSymbol: false,
|
||||
opacity: 0.4,
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
smallTips: false,
|
||||
showlabels: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
const color: string[] = chartColors(keys);
|
||||
const tooltip = {
|
||||
trigger: "none",
|
||||
axisPointer: {
|
||||
type: "cross",
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
// trigger: "axis",
|
||||
// textStyle: {
|
||||
// fontSize: 12,
|
||||
// color: "#333",
|
||||
// },
|
||||
// enterable: true,
|
||||
// confine: true,
|
||||
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
||||
};
|
||||
const tips = {
|
||||
formatter(params: any) {
|
||||
return `${params[0].value[1]}`;
|
||||
},
|
||||
confine: true,
|
||||
extraCssText: `height: 20px; padding:0 2px;`,
|
||||
trigger: "axis",
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: "#333",
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
color,
|
||||
tooltip: props.config.smallTips ? tips : tooltip,
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: showEchartsLegend(keys),
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: props.theme === "dark" ? "#fff" : "#333",
|
||||
},
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
const setRight = ref<boolean>(false);
|
||||
const option = computed(() => getOption());
|
||||
function getOption() {
|
||||
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(props.config.legend);
|
||||
setRight.value = isRight;
|
||||
const keys = Object.keys(props.data || {}).filter((i: any) => Array.isArray(props.data[i]) && props.data[i].length);
|
||||
const temp = keys.map((i: any) => {
|
||||
const serie: any = {
|
||||
data: props.data[i].map((item: any, itemIndex: number) => [props.intervalTime[itemIndex], item]),
|
||||
name: i,
|
||||
type: "line",
|
||||
symbol: "circle",
|
||||
symbolSize: 8,
|
||||
showSymbol: props.config.showSymbol,
|
||||
step: props.config.step,
|
||||
smooth: props.config.smooth,
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
type: "solid",
|
||||
},
|
||||
};
|
||||
if (props.config.type === "Area") {
|
||||
serie.areaStyle = {
|
||||
opacity: props.config.opacity || 0.4,
|
||||
};
|
||||
}
|
||||
return serie;
|
||||
});
|
||||
const color: string[] = chartColors(keys);
|
||||
const tooltip = {
|
||||
trigger: "none",
|
||||
axisPointer: {
|
||||
type: "cross",
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: showEchartsLegend(keys) ? 35 : 10,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel:
|
||||
props.config.showlabels === undefined ? true : props.config.showlabels,
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
show: props.config.showXAxis,
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca41" },
|
||||
alignWithLabel: true,
|
||||
// trigger: "axis",
|
||||
// textStyle: {
|
||||
// fontSize: 12,
|
||||
// color: "#333",
|
||||
// },
|
||||
// enterable: true,
|
||||
// confine: true,
|
||||
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
||||
};
|
||||
const tips = {
|
||||
formatter(params: any) {
|
||||
return `${params[0].value[1]}`;
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
yAxis: {
|
||||
show: props.config.showYAxis,
|
||||
type: "value",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
|
||||
axisLabel: {
|
||||
color: "#9da5b2",
|
||||
fontSize: "11",
|
||||
show: props.config.showYAxis,
|
||||
confine: true,
|
||||
extraCssText: `height: 20px; padding:0 2px;`,
|
||||
trigger: "axis",
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: "#333",
|
||||
},
|
||||
},
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
return {
|
||||
color,
|
||||
tooltip: props.config.smallTips ? tips : tooltip,
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: showEchartsLegend(keys),
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: props.theme === "dark" ? "#fff" : "#333",
|
||||
},
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
color: "#333",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: showEchartsLegend(keys) ? 35 : 10,
|
||||
left: 0,
|
||||
right: 10,
|
||||
bottom: 5,
|
||||
containLabel: props.config.showlabels === undefined ? true : props.config.showlabels,
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
show: props.config.showXAxis,
|
||||
axisTick: {
|
||||
lineStyle: { color: "#c1c5ca41" },
|
||||
alignWithLabel: true,
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
|
||||
axisLabel: { color: "#9da5b2", fontSize: "11" },
|
||||
},
|
||||
yAxis: {
|
||||
show: props.config.showYAxis,
|
||||
type: "value",
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
|
||||
axisLabel: {
|
||||
color: "#9da5b2",
|
||||
fontSize: "11",
|
||||
show: props.config.showYAxis,
|
||||
},
|
||||
},
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@@ -15,12 +15,7 @@ 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"
|
||||
>
|
||||
<el-input v-model="searchText" placeholder="Please input service name" @change="searchList" class="inputs mt-5">
|
||||
<template #append>
|
||||
<el-button class="btn" @click="searchList">
|
||||
<Icon size="sm" iconName="search" />
|
||||
@@ -37,23 +32,14 @@ limitations under the License. -->
|
||||
:border="true"
|
||||
:style="{ fontSize: '14px' }"
|
||||
>
|
||||
<el-table-column
|
||||
fixed
|
||||
label="Service Groups"
|
||||
v-if="config.showGroup"
|
||||
min-width="150"
|
||||
>
|
||||
<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)"
|
||||
>
|
||||
<span class="link" :style="{ fontSize: `${config.fontSize}px` }" @click="clickService(scope)">
|
||||
{{ scope.row.label }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -85,89 +71,83 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { watch, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import type { PropType } from "vue";
|
||||
import { ServiceListConfig } from "@/types/dashboard";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { Service } from "@/types/selector";
|
||||
import {
|
||||
useQueryPodsMetrics,
|
||||
usePodsSource,
|
||||
} from "@/hooks/useMetricsProcessor";
|
||||
import { EntityType } from "../data";
|
||||
import router from "@/router";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { MetricConfigOpt } from "@/types/dashboard";
|
||||
import ColumnGraph from "./components/ColumnGraph.vue";
|
||||
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 { EntityType } 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[];
|
||||
}
|
||||
>,
|
||||
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 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 || []);
|
||||
|
||||
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;
|
||||
/*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[];
|
||||
}
|
||||
>,
|
||||
default: () => ({ dashboardName: "", fontSize: 12 }),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
isEdit: { type: Boolean, default: false },
|
||||
});
|
||||
const s = sortServices.value.filter(
|
||||
(d: Service, index: number) => index < pageSize
|
||||
);
|
||||
setServices(s);
|
||||
}
|
||||
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 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 || []);
|
||||
|
||||
function setServices(arr: (Service & { merge: boolean })[]) {
|
||||
groups.value = {};
|
||||
const map: { [key: string]: any[] } = arr.reduce(
|
||||
(result: { [key: string]: any[] }, item: any) => {
|
||||
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;
|
||||
@@ -177,132 +157,118 @@ function setServices(arr: (Service & { merge: boolean })[]) {
|
||||
}
|
||||
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(currentServices: Service[]) {
|
||||
if (!currentServices.length) {
|
||||
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 || [],
|
||||
}, {});
|
||||
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]++;
|
||||
}
|
||||
);
|
||||
services.value = data;
|
||||
colMetrics.value = names;
|
||||
metricTypes.value = metricTypesArr;
|
||||
metricConfig.value = metricConfigArr;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
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;
|
||||
groups.value[s.group] = obj[s.group];
|
||||
}
|
||||
});
|
||||
|
||||
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 || []),
|
||||
],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
services.value = list;
|
||||
queryServiceMetrics(services.value);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => {
|
||||
if (dashboardStore.entity === EntityType[1].value) {
|
||||
queryServices();
|
||||
|
||||
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(currentServices: Service[]) {
|
||||
if (!currentServices.length) {
|
||||
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;
|
||||
}
|
||||
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 || [])],
|
||||
(data, old) => {
|
||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||
return;
|
||||
}
|
||||
metricConfig.value = props.config.metricConfig;
|
||||
queryServiceMetrics(services.value);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => {
|
||||
if (dashboardStore.entity === EntityType[1].value) {
|
||||
queryServices();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
|
@@ -26,103 +26,97 @@ limitations under the License. -->
|
||||
<div class="row flex-h" v-for="key in dataKeys" :key="key">
|
||||
<div class="name" :style="`width: ${nameWidth}`">{{ key }}</div>
|
||||
<div class="value-col" v-if="config.showTableValues">
|
||||
{{
|
||||
config.metricTypes[0] === "readMetricsValue"
|
||||
? data[key]
|
||||
: data[key][data[key].length - 1 || 0]
|
||||
}}
|
||||
{{ config.metricTypes[0] === "readMetricsValue" ? data[key] : data[key][data[key].length - 1 || 0] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
showTableValues: boolean;
|
||||
tableHeaderCol2: string;
|
||||
tableHeaderCol1: string;
|
||||
metricTypes: string[];
|
||||
}>,
|
||||
default: () => ({ showTableValues: true }),
|
||||
},
|
||||
});
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
showTableValues: boolean;
|
||||
tableHeaderCol2: string;
|
||||
tableHeaderCol1: string;
|
||||
metricTypes: string[];
|
||||
}>,
|
||||
default: () => ({ showTableValues: true }),
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const nameWidth = computed(() =>
|
||||
props.config.showTableValues ? "80%" : "100%"
|
||||
);
|
||||
const dataKeys = computed(() => {
|
||||
if (props.config.metricTypes[0] === "readMetricsValue") {
|
||||
const keys = Object.keys(props.data || {});
|
||||
const { t } = useI18n();
|
||||
const nameWidth = computed(() => (props.config.showTableValues ? "80%" : "100%"));
|
||||
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: string) => Array.isArray(props.data[i]) && props.data[i].length,
|
||||
);
|
||||
return keys;
|
||||
}
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: string) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
return keys;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chart-table {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.name {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.row {
|
||||
border-left: 1px solid #ccc;
|
||||
height: 20px;
|
||||
.chart-table {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
div {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-right: 1px solid #ccc;
|
||||
text-align: center;
|
||||
.name {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.row {
|
||||
border-left: 1px solid #ccc;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
div {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-right: 1px solid #ccc;
|
||||
text-align: center;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div:nth-last-child(2) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
border-bottom: 1px solid #ccc;
|
||||
.dark {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
div:nth-last-child(2) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
.row:first-child {
|
||||
div {
|
||||
border-top: 1px solid #ccc;
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value-col {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.row:first-child {
|
||||
div {
|
||||
border-top: 1px solid #ccc;
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value-col {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -32,22 +32,14 @@ limitations under the License. -->
|
||||
<div class="operation" @click="handleClick(i.name)">
|
||||
<span>{{ t("copy") }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="operation"
|
||||
@click="viewTrace(i)"
|
||||
v-show="refIdType === RefIdTypes[0].value"
|
||||
>
|
||||
<div class="operation" @click="viewTrace(i)" v-show="refIdType === RefIdTypes[0].value">
|
||||
<span>{{ t("viewTrace") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<el-progress
|
||||
:stroke-width="6"
|
||||
:percentage="
|
||||
isNaN(Number(i.value) / maxValue)
|
||||
? 0
|
||||
: (Number(i.value) / maxValue) * 100
|
||||
"
|
||||
:percentage="isNaN(Number(i.value) / maxValue) ? 0 : (Number(i.value) / maxValue) * 100"
|
||||
:color="TextColors[config.color || 'purple']"
|
||||
:show-text="false"
|
||||
/>
|
||||
@@ -66,149 +58,142 @@ limitations under the License. -->
|
||||
<div class="center no-data" v-else>No Data</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { computed, ref } from "vue";
|
||||
import copy from "@/utils/copy";
|
||||
import { TextColors } from "@/views/dashboard/data";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import { QueryOrders, Status, RefIdTypes } from "../data";
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{
|
||||
[key: string]: { name: string; value: number; id: string }[];
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
color: string;
|
||||
metrics: string[];
|
||||
relatedTrace: any;
|
||||
}>,
|
||||
default: () => ({ color: "purple" }),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const showTrace = ref<boolean>(false);
|
||||
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||
type: "Trace",
|
||||
});
|
||||
const refIdType = computed(
|
||||
() =>
|
||||
(props.config.relatedTrace && props.config.relatedTrace.refIdType) ||
|
||||
RefIdTypes[0].value
|
||||
);
|
||||
const key = computed(() => Object.keys(props.data)[0] || "");
|
||||
const available = computed(
|
||||
() =>
|
||||
Array.isArray(props.data[key.value]) &&
|
||||
props.data[key.value][0] &&
|
||||
props.data[key.value][0].value
|
||||
);
|
||||
const maxValue = computed(() => {
|
||||
if (!(props.data[key.value] && props.data[key.value].length)) {
|
||||
return 0;
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { computed, ref } from "vue";
|
||||
import copy from "@/utils/copy";
|
||||
import { TextColors } from "@/views/dashboard/data";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import { QueryOrders, Status, RefIdTypes } from "../data";
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{
|
||||
[key: string]: { name: string; value: number; id: string }[];
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
color: string;
|
||||
metrics: string[];
|
||||
relatedTrace: any;
|
||||
}>,
|
||||
default: () => ({ color: "purple" }),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const showTrace = ref<boolean>(false);
|
||||
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||
type: "Trace",
|
||||
});
|
||||
const refIdType = computed(
|
||||
() => (props.config.relatedTrace && props.config.relatedTrace.refIdType) || RefIdTypes[0].value,
|
||||
);
|
||||
const key = computed(() => Object.keys(props.data)[0] || "");
|
||||
const available = computed(
|
||||
() => Array.isArray(props.data[key.value]) && props.data[key.value][0] && props.data[key.value][0].value,
|
||||
);
|
||||
const maxValue = computed(() => {
|
||||
if (!(props.data[key.value] && props.data[key.value].length)) {
|
||||
return 0;
|
||||
}
|
||||
const temp: number[] = props.data[key.value].map((i: any) => i.value);
|
||||
return Math.max.apply(null, temp);
|
||||
});
|
||||
function handleClick(i: string) {
|
||||
copy(i);
|
||||
}
|
||||
function viewTrace(item: { name: string; id: string; value: unknown }) {
|
||||
const filters = {
|
||||
...item,
|
||||
queryOrder: QueryOrders[1].value,
|
||||
status: Status[2].value,
|
||||
id: item.id || item.name,
|
||||
metricValue: [{ label: props.config.metrics[0], data: item.value, value: item.name }],
|
||||
};
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters,
|
||||
};
|
||||
showTrace.value = true;
|
||||
}
|
||||
const temp: number[] = props.data[key.value].map((i: any) => i.value);
|
||||
return Math.max.apply(null, temp);
|
||||
});
|
||||
function handleClick(i: string) {
|
||||
copy(i);
|
||||
}
|
||||
function viewTrace(item: { name: string; id: string; value: unknown }) {
|
||||
const filters = {
|
||||
...item,
|
||||
queryOrder: QueryOrders[1].value,
|
||||
status: Status[2].value,
|
||||
id: item.id || item.name,
|
||||
metricValue: [
|
||||
{ label: props.config.metrics[0], data: item.value, value: item.name },
|
||||
],
|
||||
};
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters,
|
||||
};
|
||||
showTrace.value = true;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.top-list {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.chart-slow-i {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.chart-slow {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.desc {
|
||||
flex-grow: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.calls {
|
||||
font-size: 12px;
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
background-color: #40454e;
|
||||
color: #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chart-slow-link {
|
||||
padding: 4px 10px 7px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
will-change: opacity, background-color;
|
||||
transition: opacity 0.3s, background-color 0.3s;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
height: 100%;
|
||||
color: #666;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
}
|
||||
|
||||
.operation-icon {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.operation {
|
||||
padding: 5px 0;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
.top-list {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.chart-slow-i {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.chart-slow {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.desc {
|
||||
flex-grow: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.calls {
|
||||
font-size: 12px;
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
background-color: #40454e;
|
||||
color: #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chart-slow-link {
|
||||
padding: 4px 10px 7px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
will-change: opacity, background-color;
|
||||
transition: opacity 0.3s, background-color 0.3s;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
height: 100%;
|
||||
color: #666;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
}
|
||||
|
||||
.operation-icon {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.operation {
|
||||
padding: 5px 0;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -16,9 +16,7 @@ limitations under the License. -->
|
||||
<template>
|
||||
<el-table-column
|
||||
v-for="(metric, index) in colMetrics"
|
||||
:label="`${decodeURIComponent(
|
||||
getLabel(metric, index)
|
||||
)} ${decodeURIComponent(getUnit(index))}`"
|
||||
:label="`${decodeURIComponent(getLabel(metric, index))} ${decodeURIComponent(getUnit(index))}`"
|
||||
:key="metric + index"
|
||||
min-width="150"
|
||||
>
|
||||
@@ -37,18 +35,11 @@ limitations under the License. -->
|
||||
showlabels: false,
|
||||
}"
|
||||
/>
|
||||
<span
|
||||
class="item flex-h"
|
||||
v-else-if="useListConfig(config, index).isAvg"
|
||||
>
|
||||
<span class="item flex-h" v-else-if="useListConfig(config, index).isAvg">
|
||||
<el-popover placement="left" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<span class="trend">
|
||||
<Icon
|
||||
iconName="timeline"
|
||||
size="middle"
|
||||
style="color: #409eff"
|
||||
/>
|
||||
<Icon iconName="timeline" size="middle" style="color: #409eff" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="view-line">
|
||||
@@ -75,99 +66,85 @@ limitations under the License. -->
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<Card
|
||||
v-else
|
||||
:data="{ [metric]: scope.row[metric] }"
|
||||
:config="{ textAlign: 'left' }"
|
||||
/>
|
||||
<Card v-else :data="{ [metric]: scope.row[metric] }" :config="{ textAlign: 'left' }" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import { MetricConfigOpt } from "@/types/dashboard";
|
||||
import { useListConfig } from "@/hooks/useListConfig";
|
||||
import Line from "../Line.vue";
|
||||
import Card from "../Card.vue";
|
||||
import { MetricQueryTypes } from "@/hooks/data";
|
||||
import type { PropType } from "vue";
|
||||
import type { MetricConfigOpt } from "@/types/dashboard";
|
||||
import { useListConfig } from "@/hooks/useListConfig";
|
||||
import Line from "../Line.vue";
|
||||
import Card from "../Card.vue";
|
||||
import { MetricQueryTypes } from "@/hooks/data";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
colMetrics: { type: Object },
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
colMetrics: { type: Object },
|
||||
config: {
|
||||
type: Object as PropType<{
|
||||
i: string;
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
}>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
|
||||
function getUnit(index: string) {
|
||||
const i = Number(index);
|
||||
const u =
|
||||
props.config.metricConfig &&
|
||||
props.config.metricConfig[i] &&
|
||||
props.config.metricConfig[i].unit;
|
||||
if (u) {
|
||||
return `(${encodeURIComponent(u)})`;
|
||||
}
|
||||
return encodeURIComponent("");
|
||||
}
|
||||
function getLabel(metric: string, index: string) {
|
||||
const i = Number(index);
|
||||
const label =
|
||||
props.config.metricConfig &&
|
||||
props.config.metricConfig[i] &&
|
||||
props.config.metricConfig[i].label;
|
||||
if (label) {
|
||||
if (
|
||||
props.config.metricTypes[i] === MetricQueryTypes.ReadLabeledMetricsValues
|
||||
) {
|
||||
const name = (label || "")
|
||||
.split(",")
|
||||
.map((item: string) => item.replace(/^\s*|\s*$/g, ""))[
|
||||
props.config.metricConfig[i].index || 0
|
||||
];
|
||||
return encodeURIComponent(name || "");
|
||||
function getUnit(index: string) {
|
||||
const i = Number(index);
|
||||
const u = props.config.metricConfig && props.config.metricConfig[i] && props.config.metricConfig[i].unit;
|
||||
if (u) {
|
||||
return `(${encodeURIComponent(u)})`;
|
||||
}
|
||||
return encodeURIComponent(label);
|
||||
return encodeURIComponent("");
|
||||
}
|
||||
function getLabel(metric: string, index: string) {
|
||||
const i = Number(index);
|
||||
const label = props.config.metricConfig && props.config.metricConfig[i] && props.config.metricConfig[i].label;
|
||||
if (label) {
|
||||
if (props.config.metricTypes[i] === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||
const name = (label || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""))[
|
||||
props.config.metricConfig[i].index || 0
|
||||
];
|
||||
return encodeURIComponent(name || "");
|
||||
}
|
||||
return encodeURIComponent(label);
|
||||
}
|
||||
return encodeURIComponent(metric);
|
||||
}
|
||||
return encodeURIComponent(metric);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chart {
|
||||
height: 40px;
|
||||
}
|
||||
.chart {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.view-line {
|
||||
width: 380px;
|
||||
height: 200px;
|
||||
}
|
||||
.view-line {
|
||||
width: 380px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.item {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.trend {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.trend {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: inline-block;
|
||||
flex-grow: 2;
|
||||
height: 100%;
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
.value {
|
||||
display: inline-block;
|
||||
flex-grow: 2;
|
||||
height: 100%;
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
</style>
|
||||
|
@@ -81,115 +81,107 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { LegendOptions } from "@/types/dashboard";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { LegendOptions } from "@/types/dashboard";
|
||||
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<LegendOptions>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const tableData: any = computed(() => {
|
||||
const { aggregations } = useLegendProcess(props.config);
|
||||
return aggregations(props.data, props.intervalTime).source;
|
||||
});
|
||||
const headerRow = computed(() => {
|
||||
const { aggregations } = useLegendProcess(props.config);
|
||||
return aggregations(props.data, props.intervalTime).headers;
|
||||
});
|
||||
const isRight = computed(() => useLegendProcess(props.config).isRight);
|
||||
const width = computed(() =>
|
||||
props.config.width
|
||||
? props.config.width + "px"
|
||||
: isRight.value
|
||||
? "150px"
|
||||
: "100%"
|
||||
);
|
||||
const colors = computed(() => {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const { chartColors } = useLegendProcess(props.config);
|
||||
return chartColors(keys);
|
||||
});
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<LegendOptions>,
|
||||
default: () => ({}),
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const tableData: any = computed(() => {
|
||||
const { aggregations } = useLegendProcess(props.config);
|
||||
return aggregations(props.data, props.intervalTime).source;
|
||||
});
|
||||
const headerRow = computed(() => {
|
||||
const { aggregations } = useLegendProcess(props.config);
|
||||
return aggregations(props.data, props.intervalTime).headers;
|
||||
});
|
||||
const isRight = computed(() => useLegendProcess(props.config).isRight);
|
||||
const width = computed(() => (props.config.width ? props.config.width + "px" : isRight.value ? "150px" : "100%"));
|
||||
const colors = computed(() => {
|
||||
const keys = Object.keys(props.data || {}).filter((i: any) => Array.isArray(props.data[i]) && props.data[i].length);
|
||||
const { chartColors } = useLegendProcess(props.config);
|
||||
return chartColors(keys);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
width: 25vw;
|
||||
background: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table thead th:first-child {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
table tbody th {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
text-align: left;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
[role="region"][aria-labelledby][tabindex] {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.value {
|
||||
span {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
width: 80px;
|
||||
table {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
table-layout: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
}
|
||||
table th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
width: 25vw;
|
||||
background: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table thead th:first-child {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
table tbody th {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
text-align: left;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
[role="region"][aria-labelledby][tabindex] {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.value {
|
||||
span {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user