fix: polish pages and bugs fix (#36)

This commit is contained in:
Fine0830 2022-03-24 21:35:22 +08:00 committed by GitHub
parent 33365f2a14
commit 4380a874de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 314 additions and 134 deletions

View File

@ -0,0 +1,17 @@
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M12.984 14.859q1.266-0.375 1.875-1.875h7.125q-0.375 3.609-2.836 6.141t-6.164 2.859v-7.125zM14.859 11.016q-0.563-1.5-1.875-1.875v-7.125q3.703 0.328 6.164 2.859t2.836 6.141h-7.125zM11.016 9.141q-0.797 0.328-1.406 1.125t-0.609 1.734 0.609 1.734 1.406 1.125v7.125q-3.797-0.375-6.398-3.234t-2.602-6.75 2.602-6.75 6.398-3.234v7.125z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -38,7 +38,7 @@ const props = defineProps({
onMounted(async () => { onMounted(async () => {
await setOptions(props.option); await setOptions(props.option);
addResizeListener(unref(chartRef), resize); chartRef.value && addResizeListener(unref(chartRef), resize);
setTimeout(() => { setTimeout(() => {
const instance = getInstance(); const instance = getInstance();

View File

@ -22,6 +22,7 @@ limitations under the License. -->
:multiple="multiple" :multiple="multiple"
:disabled="disabled" :disabled="disabled"
:style="{ borderRadius }" :style="{ borderRadius }"
:clearable="clearable"
> >
<el-option <el-option
v-for="item in options" v-for="item in options"
@ -60,6 +61,7 @@ const props = defineProps({
borderRadius: { type: Number, default: 3 }, borderRadius: { type: Number, default: 3 },
multiple: { type: Boolean, default: false }, multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }, disabled: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
}); });
const selected = ref<string[] | string>(props.value); const selected = ref<string[] | string>(props.value);

View File

@ -101,6 +101,18 @@ limitations under the License. -->
@click="controlMenu" @click="controlMenu"
/> />
</div> </div>
<div class="version">
<el-popover
trigger="hover"
width="250"
placement="right"
:content="appStore.version"
>
<template #reference>
{{ t("version") }}
</template>
</el-popover>
</div>
</div> </div>
</template> </template>
@ -108,7 +120,10 @@ limitations under the License. -->
import { ref } from "vue"; import { ref } from "vue";
import { useRouter, RouteRecordRaw } from "vue-router"; import { useRouter, RouteRecordRaw } from "vue-router";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useAppStoreWithOut } from "@/store/modules/app";
import { ElMessage } from "element-plus";
const appStore = useAppStoreWithOut();
const { t } = useI18n(); const { t } = useI18n();
const name = ref<any>(String(useRouter().currentRoute.value.name)); const name = ref<any>(String(useRouter().currentRoute.value.name));
const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "") const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "")
@ -119,6 +134,7 @@ const isCollapse = ref(false);
const controlMenu = () => { const controlMenu = () => {
isCollapse.value = !isCollapse.value; isCollapse.value = !isCollapse.value;
}; };
getVersion();
const changePage = (menu: RouteRecordRaw) => { const changePage = (menu: RouteRecordRaw) => {
theme.value = ["VirtualMachine", "Kubernetes"].includes(String(menu.name)) theme.value = ["VirtualMachine", "Kubernetes"].includes(String(menu.name))
? "light" ? "light"
@ -127,6 +143,12 @@ const changePage = (menu: RouteRecordRaw) => {
const filterMenus = (menus: any[]) => { const filterMenus = (menus: any[]) => {
return menus.filter((d) => d.meta && !d.meta.notShow); return menus.filter((d) => d.meta && !d.meta.notShow);
}; };
async function getVersion() {
const res = await appStore.fetchVersion();
if (res.errors) {
ElMessage.error(res.errors);
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -193,4 +215,13 @@ span.collapse {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
} }
.version {
color: #eee;
position: fixed;
bottom: 20px;
left: 20px;
font-size: 12px;
cursor: pointer;
}
</style> </style>

View File

@ -23,7 +23,6 @@ const msg = {
serviceMesh: "Service Mesh", serviceMesh: "Service Mesh",
infrastructure: "Infrastructure", infrastructure: "Infrastructure",
virtualMachine: "Virtual Machine", virtualMachine: "Virtual Machine",
kubernetes: "Kubernetes",
dashboardNew: "New Dashboard", dashboardNew: "New Dashboard",
dashboardList: "Dashboard List", dashboardList: "Dashboard List",
logs: "Logs", logs: "Logs",
@ -123,6 +122,9 @@ const msg = {
viewWarning: "You are entering view mode", viewWarning: "You are entering view mode",
virtualDatabase: "Virtual Database", virtualDatabase: "Virtual Database",
reloadDashboards: "Reload dashboards", reloadDashboards: "Reload dashboards",
kubernetesService: "Service",
kubernetesCluster: "Cluster",
kubernetes: "Kubernetes",
hourTip: "Select Hour", hourTip: "Select Hour",
minuteTip: "Select Minute", minuteTip: "Select Minute",
secondTip: "Select Second", secondTip: "Select Second",
@ -304,7 +306,7 @@ const msg = {
keywordsOfContentLogTips: keywordsOfContentLogTips:
"Current storage of SkyWalking OAP server does not support this.", "Current storage of SkyWalking OAP server does not support this.",
setEvent: "Set Event", setEvent: "Set Event",
instanceAttributes: "Instance Attributes", viewAttributes: "View Attributes",
serviceEvents: "Service Events", serviceEvents: "Service Events",
select: "Select", select: "Select",
eventID: "Event ID", eventID: "Event ID",

View File

@ -22,7 +22,6 @@ const msg = {
serviceMesh: "服务网格", serviceMesh: "服务网格",
infrastructure: "基础结构", infrastructure: "基础结构",
virtualMachine: "虚拟机", virtualMachine: "虚拟机",
kubernetes: "Kubernetes",
dashboardNew: "新建仪表板", dashboardNew: "新建仪表板",
dashboardHome: "仪表盘首页", dashboardHome: "仪表盘首页",
dashboardList: "仪表盘列表", dashboardList: "仪表盘列表",
@ -123,6 +122,9 @@ const msg = {
viewWarning: "你正在进入预览模式", viewWarning: "你正在进入预览模式",
virtualDatabase: "虚拟数据库", virtualDatabase: "虚拟数据库",
reloadDashboards: "重新加载仪表盘", reloadDashboards: "重新加载仪表盘",
kubernetesService: "服务",
kubernetesCluster: "集群",
kubernetes: "Kubernetes",
hourTip: "选择小时", hourTip: "选择小时",
minuteTip: "选择分钟", minuteTip: "选择分钟",
secondTip: "选择秒数", secondTip: "选择秒数",
@ -303,7 +305,7 @@ const msg = {
"只有core/default/searchableLogsTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。", "只有core/default/searchableLogsTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
keywordsOfContentLogTips: "SkyWalking OAP服务器的当前存储不支持此操作", keywordsOfContentLogTips: "SkyWalking OAP服务器的当前存储不支持此操作",
setEvent: "设置事件", setEvent: "设置事件",
instanceAttributes: "实例属性", viewAttributes: "实例属性",
serviceEvents: "服务事件", serviceEvents: "服务事件",
select: "选择", select: "选择",
eventID: "事件ID", eventID: "事件ID",

View File

@ -26,11 +26,13 @@ import { routesAlarm } from "./alarm";
import { routesSelf } from "./selfObservability"; import { routesSelf } from "./selfObservability";
import { routesFunctions } from "./functions"; import { routesFunctions } from "./functions";
import { routesBrowser } from "./browser"; import { routesBrowser } from "./browser";
import { routesK8s } from "./k8s";
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
...routesGen, ...routesGen,
...routesMesh, ...routesMesh,
...routesFunctions, ...routesFunctions,
...routesK8s,
...routesInfra, ...routesInfra,
...routesBrowser, ...routesBrowser,
...routesDatabase, ...routesDatabase,

View File

@ -38,14 +38,6 @@ export const routesInfra: Array<RouteRecordRaw> = [
}, },
component: () => import("@/views/Layer.vue"), component: () => import("@/views/Layer.vue"),
}, },
{
path: "/kubernetes",
name: "Kubernetes",
meta: {
title: "kubernetes",
},
component: () => import("@/views/Layer.vue"),
},
// { // {
// path: "/infrastructure/vm", // path: "/infrastructure/vm",
// name: "VirtualMachine", // name: "VirtualMachine",

50
src/router/k8s.ts Normal file
View File

@ -0,0 +1,50 @@
/**
* 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.
*/
import { RouteRecordRaw } from "vue-router";
import Layout from "@/layout/Index.vue";
export const routesK8s: Array<RouteRecordRaw> = [
{
path: "",
name: "Kubernetes",
meta: {
title: "kubernetes",
icon: "donut_small",
hasGroup: true,
},
redirect: "/kubernetes/cluster",
component: Layout,
children: [
{
path: "/kubernetes/cluster",
name: "KubernetesCluster",
meta: {
title: "kubernetesCluster",
},
component: () => import("@/views/Layer.vue"),
},
{
path: "/kubernetes/service",
name: "KubernetesService",
meta: {
title: "kubernetesService",
},
component: () => import("@/views/Layer.vue"),
},
],
},
];

View File

@ -32,6 +32,7 @@ interface AppState {
timer: Nullable<any>; timer: Nullable<any>;
autoRefresh: boolean; autoRefresh: boolean;
pageTitle: string; pageTitle: string;
version: string;
} }
export const appStore = defineStore({ export const appStore = defineStore({
@ -49,6 +50,7 @@ export const appStore = defineStore({
timer: null, timer: null,
autoRefresh: false, autoRefresh: false,
pageTitle: "", pageTitle: "",
version: "",
}), }),
getters: { getters: {
duration(): Duration { duration(): Duration {
@ -155,6 +157,16 @@ export const appStore = defineStore({
return res.data; return res.data;
}, },
async fetchVersion(): Promise<void> {
const res: AxiosResponse = await graphql
.query("queryOAPVersion")
.params({});
if (res.data.errors) {
return res.data;
}
this.version = res.data.data.version;
return res.data;
},
}, },
}); });
export function useAppStoreWithOut(): any { export function useAppStoreWithOut(): any {

View File

@ -65,15 +65,15 @@ export const logStore = defineStore({
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
this.services = [ this.services = res.data.data.services;
{ value: "0", label: "All" },
...res.data.data.services,
] || [{ value: "0", label: "All" }];
return res.data; return res.data;
}, },
async getInstances() { async getInstances(id: string) {
const serviceId = this.selectorStore.currentService
? this.selectorStore.currentService.id
: id;
const res: AxiosResponse = await graphql.query("queryInstances").params({ const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId: this.selectorStore.currentService.id, serviceId,
duration: this.durationTime, duration: this.durationTime,
}); });
@ -86,9 +86,12 @@ export const logStore = defineStore({
] || [{ value: " 0", label: "All" }]; ] || [{ value: " 0", label: "All" }];
return res.data; return res.data;
}, },
async getEndpoints() { async getEndpoints(id: string) {
const serviceId = this.selectorStore.currentService
? this.selectorStore.currentService.id
: id;
const res: AxiosResponse = await graphql.query("queryEndpoints").params({ const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId: this.selectorStore.currentService.id, serviceId,
duration: this.durationTime, duration: this.durationTime,
keyword: "", keyword: "",
}); });

View File

@ -82,10 +82,7 @@ export const profileStore = defineStore({
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
this.services = [ this.services = res.data.data.services;
{ value: "0", label: "All" },
...res.data.data.services,
] || [{ value: "0", label: "All" }];
return res.data; return res.data;
}, },
async getTaskList() { async getTaskList() {

View File

@ -80,15 +80,15 @@ export const traceStore = defineStore({
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
this.services = [ this.services = res.data.data.services;
{ value: "0", label: "All" },
...res.data.data.services,
] || [{ value: "0", label: "All" }];
return res.data; return res.data;
}, },
async getInstances() { async getInstances(id: string) {
const serviceId = this.selectorStore.currentService
? this.selectorStore.currentService.id
: id;
const res: AxiosResponse = await graphql.query("queryInstances").params({ const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId: this.selectorStore.currentService.id, serviceId: serviceId,
duration: this.durationTime, duration: this.durationTime,
}); });
@ -101,9 +101,12 @@ export const traceStore = defineStore({
] || [{ value: " 0", label: "All" }]; ] || [{ value: " 0", label: "All" }];
return res.data; return res.data;
}, },
async getEndpoints() { async getEndpoints(id: string) {
const serviceId = this.selectorStore.currentService
? this.selectorStore.currentService.id
: id;
const res: AxiosResponse = await graphql.query("queryEndpoints").params({ const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId: this.selectorStore.currentService.id, serviceId,
duration: this.durationTime, duration: this.durationTime,
keyword: "", keyword: "",
}); });

View File

@ -35,7 +35,7 @@ const dashboardStore = useDashboardStore();
const routesMap: { [key: string]: string } = { const routesMap: { [key: string]: string } = {
GeneralServices: "GENERAL", GeneralServices: "GENERAL",
Database: "VIRTUAL_DATABASE", Database: "VIRTUAL_DATABASE",
MESH: "MESH", MeshServices: "MESH",
ControlPanel: "MESH_CP", ControlPanel: "MESH_CP",
DataPanel: "MESH_DP", DataPanel: "MESH_DP",
Linux: "OS_LINUX", Linux: "OS_LINUX",
@ -43,7 +43,8 @@ const routesMap: { [key: string]: string } = {
Satellite: "SO11Y_SATELLITE", Satellite: "SO11Y_SATELLITE",
Functions: "FAAS", Functions: "FAAS",
Browser: "BROWSER", Browser: "BROWSER",
Kubernetes: "K8S", KubernetesCluster: "K8S",
KubernetesService: "K8S_SERVICE",
}; };
const layer = ref<string>("GENERAL"); const layer = ref<string>("GENERAL");
@ -51,6 +52,7 @@ getDashboard();
async function getDashboard() { async function getDashboard() {
layer.value = routesMap[String(route.name)]; layer.value = routesMap[String(route.name)];
console.log(layer.value);
dashboardStore.setLayer(layer.value); dashboardStore.setLayer(layer.value);
dashboardStore.setEntity(EntityType[1].value); dashboardStore.setEntity(EntityType[1].value);
dashboardStore.setMode(false); dashboardStore.setMode(false);

View File

@ -203,6 +203,9 @@ async function importTemplates(event: any) {
dashboardFile.value = null; dashboardFile.value = null;
} }
function exportTemplates() { function exportTemplates() {
if (!multipleSelection.value.length) {
return;
}
const arr = multipleSelection.value.sort( const arr = multipleSelection.value.sort(
(a: DashboardItem, b: DashboardItem) => { (a: DashboardItem, b: DashboardItem) => {
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);

View File

@ -37,8 +37,9 @@ limitations under the License. -->
metrics: dashboardStore.selectedGrid.metrics, metrics: dashboardStore.selectedGrid.metrics,
metricTypes: dashboardStore.selectedGrid.metricTypes, metricTypes: dashboardStore.selectedGrid.metricTypes,
standard: dashboardStore.selectedGrid.standard, standard: dashboardStore.selectedGrid.standard,
isEdit: true,
}" }"
:isEdit="isEdit"
@changeOpt="setStatus"
/> />
<div v-show="!dashboardStore.selectedGrid.graph.type" class="no-data"> <div v-show="!dashboardStore.selectedGrid.graph.type" class="no-data">
{{ t("noData") }} {{ t("noData") }}
@ -51,7 +52,11 @@ limitations under the License. -->
:style="{ '--el-collapse-header-font-size': '15px' }" :style="{ '--el-collapse-header-font-size': '15px' }"
> >
<el-collapse-item :title="t('selectVisualization')" name="1"> <el-collapse-item :title="t('selectVisualization')" name="1">
<MetricOptions @update="getSource" @loading="setLoading" /> <MetricOptions
@update="getSource"
@changeOpt="setStatus"
@loading="setLoading"
/>
</el-collapse-item> </el-collapse-item>
<el-collapse-item :title="t('graphStyles')" name="2"> <el-collapse-item :title="t('graphStyles')" name="2">
<component :is="`${dashboardStore.selectedGrid.graph.type}Config`" /> <component :is="`${dashboardStore.selectedGrid.graph.type}Config`" />
@ -85,6 +90,7 @@ import configs from "./widget/graph-styles";
import WidgetOptions from "./widget/WidgetOptions.vue"; import WidgetOptions from "./widget/WidgetOptions.vue";
import StandardOptions from "./widget/StandardOptions.vue"; import StandardOptions from "./widget/StandardOptions.vue";
import MetricOptions from "./widget/MetricOptions.vue"; import MetricOptions from "./widget/MetricOptions.vue";
import { ListChartTypes } from "../data";
export default defineComponent({ export default defineComponent({
name: "ConfigEdit", name: "ConfigEdit",
@ -101,6 +107,7 @@ export default defineComponent({
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const appStoreWithOut = useAppStoreWithOut(); const appStoreWithOut = useAppStoreWithOut();
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const isEdit = ref<boolean>(false);
const states = reactive<{ const states = reactive<{
activeNames: string; activeNames: string;
source: unknown; source: unknown;
@ -123,8 +130,13 @@ export default defineComponent({
} }
function applyConfig() { function applyConfig() {
dashboardStore.setConfigs(dashboardStore.selectedGrid);
dashboardStore.setConfigPanel(false); dashboardStore.setConfigPanel(false);
setStatus();
dashboardStore.setConfigs(dashboardStore.selectedGrid);
}
function setStatus() {
isEdit.value = true;
} }
function cancelConfig() { function cancelConfig() {
@ -143,6 +155,8 @@ export default defineComponent({
cancelConfig, cancelConfig,
getSource, getSource,
setLoading, setLoading,
setStatus,
isEdit,
}; };
}, },
}); });

View File

@ -13,15 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div v-if="states.isList && states.dashboardList.length" class="ds-name"> <div v-if="states.isList" class="ds-name">
<div>{{ t("dashboards") }}</div> <div>{{ t("dashboards") }}</div>
<Selector <Selector
:value="states.dashboardName" :value="states.dashboardName || ''"
:options="states.dashboardList" :options="states.dashboardList"
size="small" size="small"
placeholder="Please select a dashboard name" placeholder="Please select a dashboard name"
@change="changeDashboard" @change="changeDashboard"
class="selectors" class="selectors"
:clearable="true"
/> />
</div> </div>
<div>{{ t("metrics") }}</div> <div>{{ t("metrics") }}</div>
@ -63,7 +64,6 @@ limitations under the License. -->
/> />
<Icon <Icon
class="cp" class="cp"
v-show="states.metrics.length > 1"
iconName="remove_circle_outline" iconName="remove_circle_outline"
size="middle" size="middle"
@click="deleteMetric(index)" @click="deleteMetric(index)"
@ -105,7 +105,7 @@ import { DashboardItem } from "@/types/dashboard";
/*global defineEmits */ /*global defineEmits */
const { t } = useI18n(); const { t } = useI18n();
const emit = defineEmits(["update", "loading"]); const emit = defineEmits(["update", "loading", "changeOpt"]);
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const { metrics, metricTypes, graph } = dashboardStore.selectedGrid; const { metrics, metricTypes, graph } = dashboardStore.selectedGrid;
const states = reactive<{ const states = reactive<{
@ -116,7 +116,7 @@ const states = reactive<{
isList: boolean; isList: boolean;
metricList: (Option & { type: string })[]; metricList: (Option & { type: string })[];
dashboardName: string; dashboardName: string;
dashboardList: (DashboardItem & { label: string; value: string })[]; dashboardList: ((DashboardItem & { label: string; value: string }) | any)[];
}>({ }>({
metrics: metrics && metrics.length ? metrics : [""], metrics: metrics && metrics.length ? metrics : [""],
metricTypes: metricTypes && metricTypes.length ? metricTypes : [""], metricTypes: metricTypes && metricTypes.length ? metricTypes : [""],
@ -125,11 +125,11 @@ const states = reactive<{
isList: false, isList: false,
metricList: [], metricList: [],
dashboardName: graph.dashboardName, dashboardName: graph.dashboardName,
dashboardList: [], dashboardList: [{ label: "", value: "" }],
}); });
states.isList = ListChartTypes.includes(graph.type); states.isList = ListChartTypes.includes(graph.type);
const defaultLen = ref<number>(states.isList ? 5 : 10); const defaultLen = ref<number>(states.isList ? 5 : 20);
states.visTypes = setVisTypes(); states.visTypes = setVisTypes();
setDashboards(); setDashboards();
@ -197,7 +197,7 @@ async function setMetricType() {
function setDashboards() { function setDashboards() {
const { graph } = dashboardStore.selectedGrid; const { graph } = dashboardStore.selectedGrid;
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
states.dashboardList = list.reduce( const arr = list.reduce(
( (
prev: (DashboardItem & { label: string; value: string })[], prev: (DashboardItem & { label: string; value: string })[],
d: DashboardItem d: DashboardItem
@ -219,6 +219,8 @@ function setDashboards() {
}, },
[] []
); );
states.dashboardList = arr.length ? arr : [{ label: "", value: "" }];
} }
function setVisTypes() { function setVisTypes() {
@ -281,6 +283,7 @@ function changeMetrics(
...{ metricTypes: states.metricTypes, metrics: states.metrics }, ...{ metricTypes: states.metricTypes, metrics: states.metrics },
}); });
if (states.isList) { if (states.isList) {
emit("changeOpt");
return; return;
} }
queryMetrics(); queryMetrics();
@ -311,6 +314,7 @@ function changeMetricType(index: number, opt: Option[] | any) {
...{ metricTypes: states.metricTypes }, ...{ metricTypes: states.metricTypes },
}); });
if (states.isList) { if (states.isList) {
emit("changeOpt");
return; return;
} }
queryMetrics(); queryMetrics();
@ -338,7 +342,11 @@ async function queryMetrics() {
} }
function changeDashboard(opt: any) { function changeDashboard(opt: any) {
states.dashboardName = opt[0].value; if (!opt[0]) {
states.dashboardName = "";
} else {
states.dashboardName = opt[0].value;
}
const graph = { const graph = {
...dashboardStore.selectedGrid.graph, ...dashboardStore.selectedGrid.graph,
dashboardName: states.dashboardName, dashboardName: states.dashboardName,
@ -358,6 +366,15 @@ function addMetric() {
states.metricTypes.push(""); states.metricTypes.push("");
} }
function deleteMetric(index: number) { function deleteMetric(index: number) {
if (states.metrics.length === 1) {
states.metrics = [""];
states.metricTypes = [""];
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
});
return;
}
states.metrics.splice(index, 1); states.metrics.splice(index, 1);
states.metricTypes.splice(index, 1); states.metricTypes.splice(index, 1);
} }

View File

@ -104,26 +104,24 @@ const props = defineProps({
i: string; i: string;
metrics: string[]; metrics: string[];
metricTypes: string[]; metricTypes: string[];
isEdit: boolean;
} }
>, >,
default: () => ({ dashboardName: "", fontSize: 12, i: "" }), default: () => ({ dashboardName: "", fontSize: 12, i: "" }),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
needQuery: { type: Boolean, default: false }, isEdit: { type: Boolean, default: false },
}); });
// const emit = defineEmits(["changeOpt"]);
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const chartLoading = ref<boolean>(false); const chartLoading = ref<boolean>(false);
const endpoints = ref<Endpoint[]>([]); const endpoints = ref<Endpoint[]>([]);
const searchEndpoints = ref<Endpoint[]>([]);
const pageSize = 5; const pageSize = 5;
const total = 10; const total = 10;
const searchText = ref<string>(""); const searchText = ref<string>("");
if (props.needQuery) { queryEndpoints(total);
queryEndpoints(total);
}
async function queryEndpoints(limit?: number) { async function queryEndpoints(limit?: number) {
chartLoading.value = true; chartLoading.value = true;
const resp = await selectorStore.getEndpoints({ const resp = await selectorStore.getEndpoints({
@ -136,15 +134,14 @@ async function queryEndpoints(limit?: number) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
} }
searchEndpoints.value = selectorStore.pods;
endpoints.value = selectorStore.pods.splice(0, pageSize); endpoints.value = selectorStore.pods.splice(0, pageSize);
if (props.config.isEdit || !endpoints.value.length) { await queryEndpointMetrics(endpoints.value);
return;
}
queryEndpointMetrics(endpoints.value);
} }
async function queryEndpointMetrics(currentPods: Endpoint[]) { async function queryEndpointMetrics(currentPods: Endpoint[]) {
const { metrics } = props.config; if (!currentPods.length) {
return;
}
const metrics = props.config.metrics.filter((d: string) => d);
if (metrics.length && metrics[0]) { if (metrics.length && metrics[0]) {
const params = await useQueryPodsMetrics( const params = await useQueryPodsMetrics(
@ -178,7 +175,7 @@ function clickEndpoint(scope: any) {
); );
} }
function changePage(pageIndex: number) { function changePage(pageIndex: number) {
endpoints.value = searchEndpoints.value.splice( endpoints.value = selectorStore.pods.splice(
(pageIndex - 1 || 0) * pageSize, (pageIndex - 1 || 0) * pageSize,
pageSize * (pageIndex || 1) pageSize * (pageIndex || 1)
); );
@ -189,10 +186,11 @@ async function searchList() {
} }
watch( watch(
() => [props.config.metricTypes, props.config.metrics], () => [props.config.metricTypes, props.config.metrics],
() => { async () => {
if (dashboardStore.showConfig) { if (props.isEdit) {
queryEndpointMetrics(endpoints.value); queryEndpointMetrics(endpoints.value);
} }
// emit("changeOpt", false);
} }
); );
watch( watch(

View File

@ -42,20 +42,6 @@ limitations under the License. -->
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Service Instances">
<template #default="scope">
<div class="attributes" v-if="scope.row.attributes.length">
<div
v-for="(attr, index) in scope.row.attributes"
:key="attr.name + index"
:style="{ fontSize: `${config.fontSize}px` }"
>
{{ attr.name }}: {{ attr.value || null }}
</div>
</div>
<div v-else>No Data</div>
</template>
</el-table-column>
<el-table-column <el-table-column
v-for="(metric, index) in config.metrics" v-for="(metric, index) in config.metrics"
:label="metric" :label="metric"
@ -77,6 +63,25 @@ limitations under the License. -->
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Attributes" :width="100">
<template #default="scope">
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<span class="link">{{ t("viewAttributes") }}</span>
</template>
<div class="attributes" v-if="scope.row.attributes.length">
<div
v-for="(attr, index) in scope.row.attributes"
:key="attr.name + index"
:style="{ fontSize: `${config.fontSize}px` }"
class="mt-5"
>
{{ attr.name }}: {{ attr.value || null }}
</div>
</div>
</el-popover>
</template>
</el-table-column>
</el-table> </el-table>
</div> </div>
<el-pagination <el-pagination
@ -94,6 +99,7 @@ limitations under the License. -->
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import type { PropType } from "vue"; import type { PropType } from "vue";
import Line from "./Line.vue"; import Line from "./Line.vue";
@ -128,7 +134,9 @@ const props = defineProps({
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
needQuery: { type: Boolean, default: false }, needQuery: { type: Boolean, default: false },
isEdit: { type: Boolean, default: false },
}); });
const { t } = useI18n();
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const chartLoading = ref<boolean>(false); const chartLoading = ref<boolean>(false);
@ -137,9 +145,7 @@ const searchInstances = ref<Instance[]>([]); // all instances
const pageSize = 5; const pageSize = 5;
const searchText = ref<string>(""); const searchText = ref<string>("");
if (props.needQuery) { queryInstance();
queryInstance();
}
async function queryInstance() { async function queryInstance() {
chartLoading.value = true; chartLoading.value = true;
const resp = await selectorStore.getServiceInstances(); const resp = await selectorStore.getServiceInstances();
@ -153,13 +159,13 @@ async function queryInstance() {
} }
searchInstances.value = selectorStore.pods; searchInstances.value = selectorStore.pods;
instances.value = searchInstances.value.splice(0, pageSize); instances.value = searchInstances.value.splice(0, pageSize);
if (props.config.isEdit) {
return;
}
queryInstanceMetrics(instances.value); queryInstanceMetrics(instances.value);
} }
async function queryInstanceMetrics(currentInstances: Instance[]) { async function queryInstanceMetrics(currentInstances: Instance[]) {
if (!currentInstances.length) {
return;
}
const { metrics } = props.config; const { metrics } = props.config;
if (metrics.length && metrics[0]) { if (metrics.length && metrics[0]) {
@ -186,6 +192,10 @@ function clickInstance(scope: any) {
layer: dashboardStore.layerId, layer: dashboardStore.layerId,
entity: EntityType[3].value, entity: EntityType[3].value,
}); });
if (!d) {
ElMessage.error("No this dashboard");
return;
}
router.push( router.push(
`/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${ `/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${
scope.row.id scope.row.id
@ -207,9 +217,7 @@ function searchList() {
watch( watch(
() => [props.config.metricTypes, props.config.metrics], () => [props.config.metricTypes, props.config.metrics],
() => { () => {
if (dashboardStore.showConfig) { queryInstanceMetrics(instances.value);
queryInstanceMetrics(instances.value);
}
} }
); );
watch( watch(
@ -231,7 +239,7 @@ watch(
} }
.attributes { .attributes {
max-height: 80px; max-height: 400px;
overflow: auto; overflow: auto;
} }
</style> </style>

View File

@ -122,15 +122,16 @@ const props = defineProps({
default: () => ({ dashboardName: "", fontSize: 12 }), default: () => ({ dashboardName: "", fontSize: 12 }),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
isEdit: { type: Boolean, default: false },
}); });
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const chartLoading = ref<boolean>(false); const chartLoading = ref<boolean>(false);
const pageSize = 5; const pageSize = 5;
const services = ref<Service[]>([]); const services = ref<Service[]>([]);
const searchServices = ref<Service[]>([]);
const searchText = ref<string>(""); const searchText = ref<string>("");
const groups = ref<any>({}); const groups = ref<any>({});
const sortServices = ref<(Service & { merge: boolean })[]>([]);
queryServices(); queryServices();
@ -142,7 +143,13 @@ async function queryServices() {
if (resp.errors) { if (resp.errors) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
} }
const map: { [key: string]: any[] } = selectorStore.services.reduce( setServices(selectorStore.services);
queryServiceMetrics(services.value);
}
function setServices(arr: (Service & { merge: boolean })[]) {
groups.value = {};
const map: { [key: string]: any[] } = arr.reduce(
(result: { [key: string]: any[] }, item: any) => { (result: { [key: string]: any[] }, item: any) => {
item.group = item.group || ""; item.group = item.group || "";
if (result[item.group]) { if (result[item.group]) {
@ -156,21 +163,23 @@ async function queryServices() {
}, },
{} {}
); );
services.value = Object.values(map).flat(1).splice(0, pageSize); sortServices.value = Object.values(map).flat(1);
const obj = {} as any; const obj = {} as any;
for (const s of services.value) { for (const s of sortServices.value) {
s.group = s.group || ""; s.group = s.group || "";
if (!obj[s.group]) { if (!obj[s.group]) {
obj[s.group] = 1; obj[s.group] = 1;
} else { } else {
if (obj[s.group] % 5 === 0) {
s.merge = false;
}
obj[s.group]++; obj[s.group]++;
} }
groups.value[s.group] = obj[s.group]; groups.value[s.group] = obj[s.group];
} }
if (props.config.isEdit) { services.value = sortServices.value.filter(
return; (d: Service, index: number) => index < pageSize
} );
queryServiceMetrics(services.value);
} }
function clickService(scope: any) { function clickService(scope: any) {
@ -188,6 +197,9 @@ function clickService(scope: any) {
router.push(path); router.push(path);
} }
async function queryServiceMetrics(currentServices: Service[]) { async function queryServiceMetrics(currentServices: Service[]) {
if (!currentServices.length) {
return;
}
const { metrics } = props.config; const { metrics } = props.config;
if (metrics.length && metrics[0]) { if (metrics.length && metrics[0]) {
@ -219,28 +231,29 @@ function objectSpanMethod(param: any): any {
rowspan: 0, rowspan: 0,
colspan: 0, colspan: 0,
}; };
} else {
return { rowspan: groups.value[param.row.group], colspan: 1 };
} }
return { rowspan: groups.value[param.row.group], colspan: 1 };
} }
function changePage(pageIndex: number) { function changePage(pageIndex: number) {
services.value = services.value.splice( services.value = sortServices.value.filter((d: Service, index: number) => {
(pageIndex - 1 || 0) * pageSize, if (
pageSize * (pageIndex || 1) index >= (pageIndex - 1 || 0) * pageSize &&
); index < pageSize * (pageIndex || 1)
) {
return d;
}
});
} }
function searchList() { function searchList() {
searchServices.value = selectorStore.services.filter((d: { label: string }) => const searchServices = sortServices.value.filter((d: { label: string }) =>
d.label.includes(searchText.value) d.label.includes(searchText.value)
); );
services.value = searchServices.value.splice(0, pageSize); services.value = searchServices.splice(0, pageSize);
} }
watch( watch(
() => [props.config.metricTypes, props.config.metrics], () => [props.config.metricTypes, props.config.metrics],
() => { () => {
if (dashboardStore.showConfig) { queryServiceMetrics(services.value);
queryServiceMetrics(services.value);
}
} }
); );
</script> </script>

View File

@ -127,6 +127,7 @@ const params = useRoute().params;
const toolIcons = ref<{ name: string; content: string; id: string }[]>( const toolIcons = ref<{ name: string; content: string; id: string }[]>(
EndpointRelationTools EndpointRelationTools
); );
const limit = ref<number>(10);
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const states = reactive<{ const states = reactive<{
destService: string; destService: string;
@ -401,7 +402,7 @@ async function fetchPods(type: string, serviceId: string, setPod: boolean) {
let resp; let resp;
switch (type) { switch (type) {
case EntityType[2].value: case EntityType[2].value:
resp = await selectorStore.getEndpoints({ serviceId }); resp = await selectorStore.getEndpoints({ serviceId, limit });
if (setPod) { if (setPod) {
selectorStore.setCurrentPod( selectorStore.setCurrentPod(
selectorStore.pods.length ? selectorStore.pods[0] : null selectorStore.pods.length ? selectorStore.pods[0] : null
@ -419,7 +420,11 @@ async function fetchPods(type: string, serviceId: string, setPod: boolean) {
} }
break; break;
case EntityType[6].value: case EntityType[6].value:
resp = await selectorStore.getEndpoints({ serviceId, isRelation: true }); resp = await selectorStore.getEndpoints({
serviceId,
isRelation: true,
limit,
});
if (setPod) { if (setPod) {
selectorStore.setCurrentDestPod( selectorStore.setCurrentDestPod(
selectorStore.destPods.length ? selectorStore.destPods[0] : null selectorStore.destPods.length ? selectorStore.destPods[0] : null

View File

@ -114,6 +114,7 @@ function setCurrentLog(log: any) {
} }
.serviceInstanceName, .serviceInstanceName,
.endpointName,
.serviceName { .serviceName {
width: 200px; width: 200px;
} }

View File

@ -82,6 +82,7 @@ function showSelectSpan() {
} }
.serviceInstanceName, .serviceInstanceName,
.endpointName,
.serviceName { .serviceName {
width: 200px; width: 200px;
} }
@ -98,6 +99,7 @@ function showSelectSpan() {
border: 1px solid transparent; border: 1px solid transparent;
border-right: 1px dotted silver; border-right: 1px dotted silver;
overflow: hidden; overflow: hidden;
height: 30px;
line-height: 30px; line-height: 30px;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;

View File

@ -24,6 +24,10 @@ export const ServiceLogConstants = [
label: "serviceInstanceName", label: "serviceInstanceName",
value: "instance", value: "instance",
}, },
{
label: "endpointName",
value: "endpoint",
},
{ {
label: "timestamp", label: "timestamp",
value: "time", value: "time",

View File

@ -143,7 +143,7 @@ const isBrowser = ref<boolean>(dashboardStore.layerId === "BROWSER");
const state = reactive<any>({ const state = reactive<any>({
instance: { value: "0", label: "All" }, instance: { value: "0", label: "All" },
endpoint: { value: "0", label: "All" }, endpoint: { value: "0", label: "All" },
service: { value: "0", label: "All" }, service: { value: "", label: "" },
}); });
init(); init();
@ -154,11 +154,10 @@ async function init() {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
} }
await fetchSelectors();
await searchLogs();
state.instance = { value: "0", label: "All" }; state.instance = { value: "0", label: "All" };
state.endpoint = { value: "0", label: "All" }; state.endpoint = { value: "0", label: "All" };
state.service = { value: "0", label: "All" };
searchLogs();
fetchSelectors();
} }
function fetchSelectors() { function fetchSelectors() {
@ -187,18 +186,20 @@ async function getServices() {
return; return;
} }
state.service = logStore.services[0]; state.service = logStore.services[0];
getInstances(state.service.id);
getEndpoints(state.service.id);
} }
async function getEndpoints() { async function getEndpoints(id?: string) {
const resp = await logStore.getEndpoints(); const resp = await logStore.getEndpoints(id);
if (resp.errors) { if (resp.errors) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
} }
state.endpoint = logStore.endpoints[0]; state.endpoint = logStore.endpoints[0];
} }
async function getInstances() { async function getInstances(id?: string) {
const resp = await logStore.getInstances(); const resp = await logStore.getInstances(id);
if (resp.errors) { if (resp.errors) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
@ -238,8 +239,8 @@ async function queryLogs() {
function changeField(type: string, opt: any) { function changeField(type: string, opt: any) {
state[type] = opt[0]; state[type] = opt[0];
if (type === "service") { if (type === "service") {
getEndpoints(); getEndpoints(state.service.id);
getInstances(); getInstances(state.service.id);
} }
} }
function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) { function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) {

View File

@ -113,7 +113,7 @@ const state = reactive<any>({
status: { label: "All", value: "ALL" }, status: { label: "All", value: "ALL" },
instance: { value: "0", label: "All" }, instance: { value: "0", label: "All" },
endpoint: { value: "0", label: "All" }, endpoint: { value: "0", label: "All" },
service: { value: "0", label: "All" }, service: { value: "", label: "" },
}); });
// const dateTime = computed(() => [ // const dateTime = computed(() => [
@ -121,24 +121,21 @@ const state = reactive<any>({
// appStore.durationRow.end, // appStore.durationRow.end,
// ]); // ]);
init(); init();
function init() { async function init() {
searchTraces();
if (dashboardStore.entity === EntityType[1].value) { if (dashboardStore.entity === EntityType[1].value) {
getServices(); await getServices();
return;
} }
if (dashboardStore.entity === EntityType[2].value) { if (dashboardStore.entity === EntityType[2].value) {
getInstances(); await getInstances();
return;
} }
if (dashboardStore.entity === EntityType[3].value) { if (dashboardStore.entity === EntityType[3].value) {
getEndpoints(); await getEndpoints();
return;
} }
if (dashboardStore.entity === EntityType[0].value) { if (dashboardStore.entity === EntityType[0].value) {
getInstances(); await getInstances();
getEndpoints(); await getEndpoints();
} }
await searchTraces();
} }
async function getServices() { async function getServices() {
@ -148,18 +145,20 @@ async function getServices() {
return; return;
} }
state.service = traceStore.services[0]; state.service = traceStore.services[0];
getEndpoints(state.service.id);
getInstances(state.service.id);
} }
async function getEndpoints() { async function getEndpoints(id?: string) {
const resp = await traceStore.getEndpoints(); const resp = await traceStore.getEndpoints(id);
if (resp.errors) { if (resp.errors) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
} }
state.endpoint = traceStore.endpoints[0]; state.endpoint = traceStore.endpoints[0];
} }
async function getInstances() { async function getInstances(id?: string) {
const resp = await traceStore.getInstances(); const resp = await traceStore.getInstances(id);
if (resp.errors) { if (resp.errors) {
ElMessage.error(resp.errors); ElMessage.error(resp.errors);
return; return;
@ -201,8 +200,8 @@ async function queryTraces() {
function changeField(type: string, opt: any) { function changeField(type: string, opt: any) {
state[type] = opt[0]; state[type] = opt[0];
if (type === "service") { if (type === "service") {
getEndpoints(); getEndpoints(state.service.id);
getInstances(); getInstances(state.service.id);
} }
} }
function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) { function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) {