Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b88356ba46 | ||
![]() |
d8889f1787 | ||
![]() |
1a989a1434 | ||
![]() |
82b348a766 | ||
![]() |
42b20660e4 | ||
![]() |
cb3aa940b3 | ||
![]() |
87a5553e6d | ||
![]() |
e17562a766 | ||
![]() |
fdfdaab47b | ||
![]() |
a4fc5192ac | ||
![]() |
ffabc7c7a7 | ||
![]() |
2fd5fb9b1e | ||
![]() |
adb457d660 | ||
![]() |
9c0bb988e6 | ||
![]() |
973b51e9ca | ||
![]() |
f5bcd5da2e | ||
![]() |
732b834749 | ||
![]() |
4b43196bc2 | ||
![]() |
b01565b2b8 | ||
![]() |
3b3e790dd9 | ||
![]() |
dc8e4bf273 | ||
![]() |
2ba3c67d31 | ||
![]() |
673b1a41a8 | ||
![]() |
c7079ea17c | ||
![]() |
017f5bf709 | ||
![]() |
bec86e80fd | ||
![]() |
ec7a8bbfa9 | ||
![]() |
4e022ff29a | ||
![]() |
42ead4a572 | ||
![]() |
e144b43267 | ||
![]() |
b5c07e023b | ||
![]() |
04d109d0e3 | ||
![]() |
024cd1195d | ||
![]() |
7fbd6170de | ||
![]() |
3ff3d5d1cd | ||
![]() |
2230702d97 | ||
![]() |
9ad9362935 | ||
![]() |
2be0a84d48 | ||
![]() |
4c762a2458 | ||
![]() |
7813c92673 | ||
![]() |
f12d7899e4 | ||
![]() |
b697fe4713 | ||
![]() |
0828f8a7aa |
1350
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skywalking-booster-ui",
|
||||
"version": "9.1.0",
|
||||
"version": "9.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
@@ -19,12 +19,12 @@
|
||||
"lodash": "^4.17.21",
|
||||
"monaco-editor": "^0.27.0",
|
||||
"pinia": "^2.0.5",
|
||||
"vis-timeline": "^7.5.1",
|
||||
"vue": "^3.0.0",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-types": "^4.1.1",
|
||||
"vuex": "^4.0.0-0"
|
||||
"vue-types": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^7.1.0",
|
||||
@@ -36,7 +36,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-e2e-cypress": "~4.5.0",
|
||||
"@vue/cli-plugin-e2e-cypress": "~5.0.8",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||
|
15
src/assets/icons/add_fill.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- 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 t="1655799536378" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9286" width="48" height="48"><path d="M563.2 614.4v51.2c0 30.72-20.48 51.2-51.2 51.2s-51.2-20.48-51.2-51.2v-51.2H409.6c-30.72 0-51.2-20.48-51.2-51.2s20.48-51.2 51.2-51.2h51.2V460.8c0-30.72 20.48-51.2 51.2-51.2s51.2 20.48 51.2 51.2v51.2h51.2c30.72 0 51.2 20.48 51.2 51.2s-20.48 51.2-51.2 51.2h-51.2z m51.2-563.2c158.72 15.36 281.6 143.36 281.6 307.2v512c0 56.32-46.08 102.4-102.4 102.4h-563.2c-56.32 0-102.4-46.08-102.4-102.4V153.6c0-56.32 46.08-102.4 102.4-102.4H614.4z m163.84 230.4c-25.6-61.44-76.8-107.52-138.24-122.88v71.68c0 30.72 20.48 51.2 51.2 51.2h87.04zM537.6 153.6h-256c-30.72 0-51.2 20.48-51.2 51.2v614.4c0 30.72 20.48 51.2 51.2 51.2h460.8c30.72 0 51.2-20.48 51.2-51.2V384h-153.6c-56.32 0-102.4-46.08-102.4-102.4V153.6z" fill="#707070" p-id="9287"></path></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@@ -12,4 +12,4 @@ 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 t="1648717513168" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15451" width="48" height="48"><path d="M810.666667 213.333333v597.333334H213.333333V213.333333h597.333334m85.333333-85.333333H128v768h768V128z m-512 170.666667h-85.333333v85.333333h85.333333z m170.666667 0h-85.333334v85.333333h85.333334z m-170.666667 170.666666h-85.333333v85.333334h85.333333z m170.666667 0h-85.333334v85.333334h85.333334z m-170.666667 170.666667h-85.333333v85.333333h85.333333z m170.666667 0h-85.333334v85.333333h85.333334z m170.666666-341.333333h-85.333333v85.333333h85.333333z m0 170.666666h-85.333333v85.333334h85.333333z m0 170.666667h-85.333333v85.333333h85.333333z" p-id="15452" fill="#515151"></path></svg>
|
||||
<svg t="1648717513168" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15451" width="48" height="48"><path d="M810.666667 213.333333v597.333334H213.333333V213.333333h597.333334m85.333333-85.333333H128v768h768V128z m-512 170.666667h-85.333333v85.333333h85.333333z m170.666667 0h-85.333334v85.333333h85.333334z m-170.666667 170.666666h-85.333333v85.333334h85.333333z m170.666667 0h-85.333334v85.333334h85.333334z m-170.666667 170.666667h-85.333333v85.333333h85.333333z m170.666667 0h-85.333334v85.333333h85.333334z m170.666666-341.333333h-85.333333v85.333333h85.333333z m0 170.666666h-85.333333v85.333334h85.333333z m0 170.666667h-85.333333v85.333333h85.333333z" p-id="15452"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
15
src/assets/icons/event.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- 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 t="1655695739627" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2218" width="48" height="48"><path d="M173.292308 177.230769C86.646154 265.846154 39.384615 382.030769 39.384615 504.123077 39.384615 531.692308 61.046154 551.384615 86.646154 551.384615s47.261538-21.661538 47.261538-47.261538c-1.969231-96.492308 37.415385-189.046154 106.338462-257.969231s163.446154-106.338462 257.969231-106.338461c27.569231 0 47.261538-21.661538 47.261538-47.261539 0-27.569231-21.661538-47.261538-47.261538-47.261538C378.092308 43.323077 259.938462 90.584615 173.292308 177.230769z m57.107692 326.892308c0 27.569231 19.692308 47.261538 47.261538 47.261538s47.261538-21.661538 47.261539-47.261538c0-45.292308 17.723077-90.584615 51.2-122.092308 33.476923-33.476923 76.8-49.230769 122.092308-51.2 27.569231 0 47.261538-21.661538 47.261538-47.261538 0-27.569231-19.692308-47.261538-47.261538-47.261539-70.892308 0-139.815385 27.569231-191.015385 76.8-7.876923 9.846154-80.738462 82.707692-76.8 191.015385z m665.6-204.8c-17.723077-23.630769-41.353846-51.2-45.292308-55.138462-5.907692-3.938462-13.784615-7.876923-21.661538-7.876923-7.876923 0-15.753846 1.969231-19.692308 7.876923L610.461538 441.107692c-47.261538-39.384615-118.153846-37.415385-163.446153 7.876923-45.292308 45.292308-47.261538 116.184615-7.876923 163.446154l-191.015385 191.015385c-5.907692 5.907692-9.846154 13.784615-9.846154 21.661538 0 9.846154 3.938462 19.692308 11.815385 25.6l53.16923 39.384616c72.861538 57.107692 163.446154 88.615385 259.938462 88.615384 232.369231 0 421.415385-189.046154 421.415385-421.415384 0-94.523077-33.476923-185.107692-88.615385-257.969231z" p-id="2219" fill="#707070"></path></svg>
|
After Width: | Height: | Size: 2.4 KiB |
17
src/assets/icons/keyboard_arrow_down.svg
Normal 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="M7.406 8.578l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 946 B |
17
src/assets/icons/keyboard_arrow_up.svg
Normal 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="M7.406 15.422l-1.406-1.406 6-6 6 6-1.406 1.406-4.594-4.594z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 948 B |
17
src/assets/icons/library_add.svg
Normal 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="M18.984 11.016v-2.016h-3.984v-3.984h-2.016v3.984h-3.984v2.016h3.984v3.984h2.016v-3.984h3.984zM20.016 2.016q0.797 0 1.383 0.586t0.586 1.383v12q0 0.797-0.586 1.406t-1.383 0.609h-12q-0.797 0-1.406-0.609t-0.609-1.406v-12q0-0.797 0.609-1.383t1.406-0.586h12zM3.984 6v14.016h14.016v1.969h-14.016q-0.797 0-1.383-0.586t-0.586-1.383v-14.016h1.969z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
16
src/assets/icons/setting_empty.svg
Normal file
After Width: | Height: | Size: 5.7 KiB |
19
src/assets/icons/time_range.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- 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 t="1660976558460" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="813" width="48" height="48">
|
||||
<path d="M498.596 482.29H345.42v57.308h210.478V274.197h-57.301V482.29z m0 0M577.685 644.985h379.88v57.302h-379.88v-57.302z m0 0M577.685 773.765h379.88v57.307h-379.88v-57.307z m0 0M577.685 902.55h379.88v57.307h-379.88V902.55z m0 0" p-id="814"></path>
|
||||
<path d="M102.523 382.29a28.668 28.668 0 0 0 23.367 2.56l190.81-61.886c15.053-4.883 23.298-21.04 18.415-36.09-4.882-15.052-21.04-23.297-36.093-18.415l-123.346 40c15.994-26.117 35.17-50.538 57.37-72.745 73.768-73.767 171.847-114.388 276.169-114.388 104.32 0 202.395 40.622 276.161 114.388S899.77 407.56 899.77 511.882c0 26.428-2.616 52.45-7.71 77.78h58.303c4.465-25.499 6.709-51.47 6.709-77.78 0-60.45-11.846-119.102-35.205-174.336-22.56-53.335-54.85-101.227-95.969-142.35-41.122-41.122-89.017-73.408-142.348-95.968-55.233-23.361-113.89-35.207-174.334-35.207-60.45 0-119.107 11.846-174.337 35.208-53.335 22.56-101.23 54.846-142.35 95.969-23.98 23.98-44.933 50.278-62.727 78.6l-20.738-105.654c-3.043-15.528-18.105-25.642-33.632-22.6-15.528 3.048-25.643 18.105-22.6 33.637l36.103 183.932a28.666 28.666 0 0 0 13.588 19.178z m0 0M126.02 587.942H67.768c5.76 33.679 15.368 66.544 28.79 98.278 22.56 53.334 54.85 101.225 95.972 142.348 41.123 41.123 89.014 73.409 142.349 95.969 54.112 22.888 111.518 34.711 170.668 35.182v-57.324c-102.95-0.941-199.595-41.446-272.5-114.349-55.501-55.502-92.237-124.77-107.027-200.104z m0 0" p-id="815">
|
||||
</path>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/img/technologies/BOTTLE.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/img/technologies/HTTPDARK.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/img/technologies/HTTPS.png
Normal file
After Width: | Height: | Size: 895 B |
BIN
src/assets/img/technologies/MICRONAUT.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/assets/img/technologies/NATS.png
Normal file
After Width: | Height: | Size: 626 B |
@@ -552,7 +552,7 @@ const ok = (info: any) => {
|
||||
if (props.right && _time.getTime() / 100000 > start.value) {
|
||||
emit("setDates", _time, "right");
|
||||
}
|
||||
if (!(props.left && props.right)) {
|
||||
if (!(props.left || props.right)) {
|
||||
emit("setDates", _time);
|
||||
}
|
||||
emit("ok", info === "h");
|
||||
|
@@ -44,6 +44,17 @@ const props = defineProps({
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<{
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
sourceId: string;
|
||||
}>,
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
@@ -61,12 +72,93 @@ onMounted(async () => {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: any) => {
|
||||
instance.on("click", (params: unknown) => {
|
||||
emits("select", params);
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
function updateOptions() {
|
||||
const instance = getInstance();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (props.filters.isRange) {
|
||||
const options = eventAssociate();
|
||||
setOptions(options || props.option);
|
||||
} else {
|
||||
instance.dispatchAction({
|
||||
type: "showTip",
|
||||
dataIndex: props.filters.dataIndex,
|
||||
seriesIndex: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function eventAssociate() {
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (!props.filters.duration) {
|
||||
return props.option;
|
||||
}
|
||||
if (!props.option.series[0]) {
|
||||
return;
|
||||
}
|
||||
const list = props.option.series[0].data.map(
|
||||
(d: (number | string)[]) => d[0]
|
||||
);
|
||||
if (!list.includes(props.filters.duration.endTime)) {
|
||||
return;
|
||||
}
|
||||
const markArea = {
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
opacity: 0.3,
|
||||
},
|
||||
data: [
|
||||
[
|
||||
{
|
||||
xAxis: props.filters.duration.startTime,
|
||||
},
|
||||
{
|
||||
xAxis: props.filters.duration.endTime,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
const series = (window as any).structuredClone(props.option.series);
|
||||
for (const [key, temp] of series.entries()) {
|
||||
if (key === 0) {
|
||||
temp.markArea = markArea;
|
||||
}
|
||||
}
|
||||
const options = {
|
||||
...props.option,
|
||||
series,
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(newVal, oldVal) => {
|
||||
@@ -76,7 +168,17 @@ watch(
|
||||
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
|
||||
return;
|
||||
}
|
||||
setOptions(newVal);
|
||||
let options;
|
||||
if (props.filters && props.filters.isRange) {
|
||||
options = eventAssociate();
|
||||
}
|
||||
setOptions(options || props.option);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
updateOptions();
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -29,9 +29,9 @@ limitations under the License. -->
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="item.value || ''"
|
||||
:label="item.label || ''"
|
||||
:value="item.value || ''"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
@@ -28,15 +28,30 @@ export const Languages = [
|
||||
|
||||
export const RoutesMap: { [key: string]: string } = {
|
||||
GeneralServices: "GENERAL",
|
||||
Database: "VIRTUAL_DATABASE",
|
||||
GeneralServicesActiveTabIndex: "GENERAL",
|
||||
VirtualDatabase: "VIRTUAL_DATABASE",
|
||||
VirtualDatabaseActiveTabIndex: "VIRTUAL_DATABASE",
|
||||
MeshServices: "MESH",
|
||||
MeshServicesActiveTabIndex: "MESH",
|
||||
ControlPanel: "MESH_CP",
|
||||
ControlPanelActiveTabIndex: "MESH_CP",
|
||||
DataPanel: "MESH_DP",
|
||||
DataPanelActiveTabIndex: "MESH_DP",
|
||||
Linux: "OS_LINUX",
|
||||
SkyWalkingServer: "SO11Y_OAP",
|
||||
SkyWalkingServerActiveTabIndex: "SO11Y_OAP",
|
||||
SatelliteActiveTabIndex: "SO11Y_SATELLITE",
|
||||
Satellite: "SO11Y_SATELLITE",
|
||||
Functions: "FAAS",
|
||||
FunctionsActiveTabIndex: "FAAS",
|
||||
Browser: "BROWSER",
|
||||
BrowserActiveTabIndex: "BROWSER",
|
||||
KubernetesCluster: "K8S",
|
||||
KubernetesClusterActiveTabIndex: "K8S",
|
||||
KubernetesService: "K8S_SERVICE",
|
||||
KubernetesServiceActiveTabIndex: "K8S_SERVICE",
|
||||
MySQL: "MYSQL",
|
||||
MySQLActiveTabIndex: "MYSQL",
|
||||
PostgreSQL: "POSTGRESQL",
|
||||
PostgreSQLActiveTabIndex: "POSTGRESQL",
|
||||
};
|
||||
|
@@ -33,12 +33,15 @@ export const createEBPFTask = {
|
||||
}`,
|
||||
};
|
||||
export const queryEBPFTasks = {
|
||||
variable: "$serviceId: ID!",
|
||||
variable:
|
||||
"$serviceId: ID, $serviceInstanceId: ID, $targets: [EBPFProfilingTargetType!]",
|
||||
query: `
|
||||
queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId) {
|
||||
queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId, serviceInstanceId: $serviceInstanceId, targets: $targets) {
|
||||
taskId
|
||||
serviceName
|
||||
serviceId
|
||||
serviceInstanceId
|
||||
serviceInstanceName
|
||||
processLabels
|
||||
taskStartTime
|
||||
triggerType
|
||||
@@ -90,3 +93,22 @@ export const analysisEBPFResult = {
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
export const createNetworkProfiling = {
|
||||
variable: "$request: EBPFProfilingNetworkTaskRequest!",
|
||||
query: `
|
||||
createEBPFNetworkProfiling(request: $request) {
|
||||
status
|
||||
errorReason
|
||||
id
|
||||
}`,
|
||||
};
|
||||
|
||||
export const keepNetworkProfiling = {
|
||||
variable: "$taskId: ID!",
|
||||
query: `
|
||||
keepEBPFNetworkProfiling(taskId: $taskId) {
|
||||
status
|
||||
errorReason
|
||||
}`,
|
||||
};
|
||||
|
@@ -48,6 +48,29 @@ export const Instances = {
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const Processes = {
|
||||
variable: "$instanceId: ID!, $duration: Duration!",
|
||||
query: `
|
||||
processes: listProcesses(instanceId: $instanceId, duration: $duration) {
|
||||
id
|
||||
value: name
|
||||
label: name
|
||||
serviceId
|
||||
serviceName
|
||||
instanceId
|
||||
instanceName
|
||||
agentId
|
||||
detectType
|
||||
attributes {
|
||||
name
|
||||
value
|
||||
}
|
||||
labels
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const Endpoints = {
|
||||
variable: "$serviceId: ID!, $keyword: String!",
|
||||
query: `
|
||||
@@ -102,3 +125,24 @@ export const getEndpoint = {
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const getProcess = {
|
||||
variable: "$processId: ID!",
|
||||
query: `
|
||||
process: getProcess(processId: $processId) {
|
||||
id
|
||||
value: name
|
||||
label: name
|
||||
serviceId
|
||||
serviceName
|
||||
instanceId
|
||||
instanceName
|
||||
agentId
|
||||
detectType
|
||||
attributes {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
@@ -75,3 +75,28 @@ export const InstanceTopology = {
|
||||
}
|
||||
`,
|
||||
};
|
||||
export const ProcessTopology = {
|
||||
variable: "$serviceInstanceId: ID!, $duration: Duration!",
|
||||
query: `
|
||||
topology: getProcessTopology(serviceInstanceId: $serviceInstanceId,
|
||||
duration: $duration) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
isReal
|
||||
serviceName
|
||||
serviceId
|
||||
serviceInstanceId
|
||||
serviceInstanceName
|
||||
}
|
||||
calls {
|
||||
id
|
||||
source
|
||||
detectPoints
|
||||
target
|
||||
sourceComponents
|
||||
targetComponents
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
@@ -21,6 +21,8 @@ import {
|
||||
queryEBPFTasks,
|
||||
queryEBPFSchedules,
|
||||
analysisEBPFResult,
|
||||
createNetworkProfiling,
|
||||
keepNetworkProfiling,
|
||||
} from "../fragments/ebpf";
|
||||
|
||||
export const getCreateTaskData = `query queryCreateTaskData(${queryCreateTaskData.variable}) {${queryCreateTaskData.query}}`;
|
||||
@@ -32,3 +34,7 @@ export const getEBPFTasks = `query queryEBPFTasks(${queryEBPFTasks.variable}) {$
|
||||
export const getEBPFSchedules = `query queryEBPFSchedules(${queryEBPFSchedules.variable}) {${queryEBPFSchedules.query}}`;
|
||||
|
||||
export const getEBPFResult = `query analysisEBPFResult(${analysisEBPFResult.variable}) {${analysisEBPFResult.query}}`;
|
||||
|
||||
export const newNetworkProfiling = `mutation createNetworkProfiling(${createNetworkProfiling.variable}) {${createNetworkProfiling.query}}`;
|
||||
|
||||
export const aliveNetworkProfiling = `mutation keepNetworkProfiling(${keepNetworkProfiling.variable}) {${keepNetworkProfiling.query}}`;
|
||||
|
@@ -22,6 +22,8 @@ import {
|
||||
getService,
|
||||
getInstance,
|
||||
getEndpoint,
|
||||
Processes,
|
||||
getProcess,
|
||||
} from "../fragments/selector";
|
||||
|
||||
export const queryServices = `query queryServices(${Services.variable}) {${Services.query}}`;
|
||||
@@ -31,3 +33,5 @@ export const queryLayers = `query listLayer {${Layers.query}}`;
|
||||
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
|
||||
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
|
||||
export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) {${getEndpoint.query}}`;
|
||||
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
|
||||
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;
|
||||
|
@@ -18,8 +18,10 @@ import {
|
||||
InstanceTopology,
|
||||
EndpointTopology,
|
||||
ServicesTopology,
|
||||
ProcessTopology,
|
||||
} from "../fragments/topology";
|
||||
|
||||
export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
|
||||
export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
|
||||
export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;
|
||||
export const getProcessTopology = `query queryData(${ProcessTopology.variable}) {${ProcessTopology.query}}`;
|
||||
|
@@ -32,10 +32,12 @@ export enum Calculations {
|
||||
Precision = "precision",
|
||||
ConvertSeconds = "convertSeconds",
|
||||
ConvertMilliseconds = "convertMilliseconds",
|
||||
MsTos = "msTos",
|
||||
MsToS = "msTos",
|
||||
Average = "average",
|
||||
PercentageAvg = "percentageAvg",
|
||||
ApdexAvg = "apdexAvg",
|
||||
SecondToDay = "secondToDay",
|
||||
NanosecondToMillisecond = "nanosecondToMillisecond",
|
||||
}
|
||||
export enum sizeEnum {
|
||||
XS = "XS",
|
||||
|
@@ -14,19 +14,68 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
export default function getDashboard(param: {
|
||||
export default function getDashboard(param?: {
|
||||
name: string;
|
||||
layer: string;
|
||||
entity: string;
|
||||
}) {
|
||||
const dashboardStore = useDashboardStore();
|
||||
const opt = param || dashboardStore.currentDashboard;
|
||||
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||
const dashboard = list.find(
|
||||
(d: { name: string; layer: string; entity: string }) =>
|
||||
d.name === param.name &&
|
||||
d.entity === param.entity &&
|
||||
d.layer === param.layer
|
||||
d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer
|
||||
);
|
||||
|
||||
return dashboard;
|
||||
const all = dashboardStore.layout;
|
||||
const widgets: LayoutConfig[] = [];
|
||||
for (const item of all) {
|
||||
if (item.type === "Tab") {
|
||||
widgets.push(item);
|
||||
if (item.children && item.children.length) {
|
||||
for (const child of item.children) {
|
||||
if (child.children && child.children.length) {
|
||||
widgets.push(...child.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
widgets.push(item);
|
||||
}
|
||||
}
|
||||
function associationWidget(sourceId: string, filters: unknown, type: string) {
|
||||
const widget = widgets.find((d: { type: string }) => d.type === type);
|
||||
if (!widget) {
|
||||
return ElMessage.info(`There has no a ${type} widget in the dashboard`);
|
||||
}
|
||||
const item = {
|
||||
...widget,
|
||||
filters,
|
||||
};
|
||||
dashboardStore.setWidget(item);
|
||||
const targetTabIndex = (widget.id || "").split("-");
|
||||
const sourceTabindex = (sourceId || "").split("-") || [];
|
||||
let container: Nullable<Element>;
|
||||
|
||||
if (targetTabIndex[1] === undefined) {
|
||||
container = document.querySelector(".ds-main");
|
||||
} else {
|
||||
const w = widgets.find((d: any) => d.id === targetTabIndex[0]);
|
||||
container = document.querySelector(".tab-layout");
|
||||
const layout: Nullable<Element> = document.querySelector(".ds-main");
|
||||
if (w && layout) {
|
||||
layout.scrollTop = w.y * 10 + w.h * 5;
|
||||
}
|
||||
}
|
||||
if (targetTabIndex[1] && targetTabIndex[1] !== sourceTabindex[1]) {
|
||||
dashboardStore.setActiveTabIndex(Number(targetTabIndex[1]));
|
||||
}
|
||||
if (container && widget) {
|
||||
container.scrollTop = widget.y * 10 + widget.h * 5;
|
||||
}
|
||||
}
|
||||
return { dashboard, widgets, associationWidget };
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import {
|
||||
BarSeriesOption,
|
||||
LineSeriesOption,
|
||||
HeatmapSeriesOption,
|
||||
PieSeriesOption,
|
||||
SankeySeriesOption,
|
||||
} from "echarts/charts";
|
||||
import {
|
||||
@@ -46,7 +45,6 @@ export type ECOption = echarts.ComposeOption<
|
||||
| DatasetComponentOption
|
||||
| LegendComponentOption
|
||||
| HeatmapSeriesOption
|
||||
| PieSeriesOption
|
||||
| SankeySeriesOption
|
||||
>;
|
||||
|
||||
|
@@ -46,6 +46,7 @@ export function useQueryProcessor(config: any) {
|
||||
"ServiceRelation",
|
||||
"ServiceInstanceRelation",
|
||||
"EndpointRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity);
|
||||
if (isRelation && !selectorStore.currentDestService) {
|
||||
return;
|
||||
@@ -93,28 +94,40 @@ export function useQueryProcessor(config: any) {
|
||||
dashboardStore.entity === "All"
|
||||
? undefined
|
||||
: selectorStore.currentService.normal,
|
||||
serviceInstanceName: dashboardStore.entity.includes("ServiceInstance")
|
||||
serviceInstanceName: [
|
||||
"ServiceInstance",
|
||||
"ServiceInstanceRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity)
|
||||
? selectorStore.currentPod && selectorStore.currentPod.value
|
||||
: undefined,
|
||||
endpointName: dashboardStore.entity.includes("Endpoint")
|
||||
? selectorStore.currentPod && selectorStore.currentPod.value
|
||||
: undefined,
|
||||
processName: dashboardStore.entity.includes("Process")
|
||||
? selectorStore.currentProcess && selectorStore.currentProcess.value
|
||||
: undefined,
|
||||
destNormal: isRelation
|
||||
? selectorStore.currentDestService.normal
|
||||
: undefined,
|
||||
destServiceName: isRelation
|
||||
? selectorStore.currentDestService.value
|
||||
: undefined,
|
||||
destServiceInstanceName:
|
||||
dashboardStore.entity === "ServiceInstanceRelation"
|
||||
? selectorStore.currentDestPod &&
|
||||
selectorStore.currentDestPod.value
|
||||
destServiceInstanceName: [
|
||||
"ServiceInstanceRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity)
|
||||
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destEndpointName:
|
||||
dashboardStore.entity === "EndpointRelation"
|
||||
? selectorStore.currentDestPod &&
|
||||
selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destProcessName: dashboardStore.entity.includes("ProcessRelation")
|
||||
? selectorStore.currentDestProcess &&
|
||||
selectorStore.currentDestProcess.value
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -142,6 +155,10 @@ export function useSourceProcessor(
|
||||
ElMessage.error(resp.errors);
|
||||
return {};
|
||||
}
|
||||
if (!resp.data) {
|
||||
ElMessage.error("The query is wrong");
|
||||
return {};
|
||||
}
|
||||
const source: { [key: string]: unknown } = {};
|
||||
const keys = Object.keys(resp.data);
|
||||
|
||||
@@ -150,7 +167,10 @@ export function useSourceProcessor(
|
||||
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||
|
||||
if (type === MetricQueryTypes.ReadMetricsValues) {
|
||||
source[m] = calculateExp(resp.data[keys[index]].values.values, c);
|
||||
source[m] =
|
||||
(resp.data[keys[index]] &&
|
||||
calculateExp(resp.data[keys[index]].values.values, c)) ||
|
||||
[];
|
||||
}
|
||||
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||
const resVal = Object.values(resp.data)[0] || [];
|
||||
@@ -377,23 +397,26 @@ export function aggregation(
|
||||
data = (val / 1024 / 1024 / 1024).toFixed(2);
|
||||
break;
|
||||
case Calculations.Apdex:
|
||||
data = val / 10000;
|
||||
break;
|
||||
case Calculations.ApdexAvg:
|
||||
data = val / 10000;
|
||||
data = (val / 10000).toFixed(2);
|
||||
break;
|
||||
case Calculations.ConvertSeconds:
|
||||
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
|
||||
data = dayjs(val * 1000).format("YYYY-MM-DD HH:mm:ss");
|
||||
break;
|
||||
case Calculations.ConvertMilliseconds:
|
||||
data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
|
||||
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
|
||||
break;
|
||||
case Calculations.Precision:
|
||||
data = data.toFixed(2);
|
||||
break;
|
||||
case Calculations.MsTos:
|
||||
case Calculations.MsToS:
|
||||
data = (val / 1000).toFixed(2);
|
||||
break;
|
||||
case Calculations.SecondToDay:
|
||||
data = (val / 86400).toFixed(2);
|
||||
break;
|
||||
case Calculations.NanosecondToMillisecond:
|
||||
data = (val / 1000 / 1000).toFixed(2);
|
||||
break;
|
||||
default:
|
||||
data;
|
||||
break;
|
||||
|
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="nav-bar flex-h" :class="{ dark: theme === 'dark' }">
|
||||
<div class="nav-bar flex-h">
|
||||
<div class="title">{{ appStore.pageTitle || t(pageName) }}</div>
|
||||
<div class="app-config">
|
||||
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
|
||||
@@ -49,34 +49,38 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { ElMessage } from "element-plus";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const route = useRoute();
|
||||
const pageName = ref<string>("");
|
||||
const timeRange = ref<number>(0);
|
||||
const theme = ref<string>("light");
|
||||
|
||||
getVersion();
|
||||
const setConfig = (value: string) => {
|
||||
pageName.value = value || "";
|
||||
// theme.value = route.path.includes("/infrastructure/") ? "dark" : "light";
|
||||
};
|
||||
const time = computed(() => [
|
||||
const time = ref<Date[]>([
|
||||
appStore.durationRow.start,
|
||||
appStore.durationRow.end,
|
||||
]);
|
||||
|
||||
resetDuration();
|
||||
getVersion();
|
||||
const setConfig = (value: string) => {
|
||||
pageName.value = value || "";
|
||||
};
|
||||
|
||||
const handleReload = () => {
|
||||
const gap =
|
||||
appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const time: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||
appStore.setDuration(timeFormat(time));
|
||||
const dates: Date[] = [
|
||||
getLocalTime(appStore.utc, new Date(new Date().getTime() - gap)),
|
||||
getLocalTime(appStore.utc, new Date()),
|
||||
];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
};
|
||||
function changeTimeRange(val: Date[] | any) {
|
||||
timeRange.value =
|
||||
@@ -99,6 +103,20 @@ async function getVersion() {
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
}
|
||||
function resetDuration() {
|
||||
const { duration }: any = route.params;
|
||||
if (duration) {
|
||||
const d = JSON.parse(duration);
|
||||
|
||||
appStore.updateDurationRow({
|
||||
start: new Date(d.start),
|
||||
end: new Date(d.end),
|
||||
step: d.step,
|
||||
});
|
||||
appStore.updateUTC(d.utc);
|
||||
time.value = [new Date(d.start), new Date(d.end)];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.nav-bar {
|
||||
|
@@ -55,9 +55,9 @@ limitations under the License. -->
|
||||
<router-link
|
||||
class="items"
|
||||
:to="m.path"
|
||||
:exact="m.meta.exact || false"
|
||||
:exact="(m.meta && m.meta.exact) || false"
|
||||
>
|
||||
<span class="title">{{ t(m.meta.title) }}</span>
|
||||
<span class="title">{{ m.meta && t(m.meta.title) }}</span>
|
||||
</router-link>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
@@ -32,6 +32,7 @@ const msg = {
|
||||
dashboards: "Dashboards",
|
||||
profiles: "Profiles",
|
||||
database: "Database",
|
||||
mySQL: "MySQL",
|
||||
serviceName: "Service Name",
|
||||
technologies: "Technologies",
|
||||
generalServicePanel: "General Service Panel",
|
||||
@@ -142,6 +143,15 @@ const msg = {
|
||||
interval: "Refresh Interval",
|
||||
pause: "Pause",
|
||||
begin: "Start",
|
||||
associateOptions: "Association Options",
|
||||
widget: "Widget",
|
||||
nameTip:
|
||||
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
||||
duplicateName: "Duplicate name",
|
||||
enableAssociate: "Enable association",
|
||||
text: "Text",
|
||||
query: "Query",
|
||||
postgreSQL: "PostgreSQL",
|
||||
seconds: "Seconds",
|
||||
hourTip: "Select Hour",
|
||||
minuteTip: "Select Minute",
|
||||
@@ -151,8 +161,6 @@ const msg = {
|
||||
monthsHead: "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec",
|
||||
months: "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec",
|
||||
weeks: "Mon_Tue_Wed_Thu_Fri_Sat_Sun",
|
||||
hello: "Hello",
|
||||
helloMessage: "Welcome Back, Apache SkyWalking APM System !",
|
||||
username: "Username",
|
||||
password: "Password",
|
||||
title: "Title",
|
||||
@@ -161,7 +169,7 @@ const msg = {
|
||||
dashboard: "Dashboard",
|
||||
topology: "Topology",
|
||||
trace: "Trace",
|
||||
alarm: "Alarms",
|
||||
alarm: "Alerting",
|
||||
auto: "Auto",
|
||||
reload: "Reload",
|
||||
version: "Version",
|
||||
|
@@ -32,6 +32,7 @@ const msg = {
|
||||
dashboards: "Paneles",
|
||||
profiles: "Perfiles",
|
||||
database: "Base de Datos",
|
||||
mySQL: "MySQL",
|
||||
serviceName: "Nombre Servicio",
|
||||
technologies: "Tecnologías",
|
||||
generalServicePanel: "Panel Servicio General",
|
||||
@@ -142,6 +143,15 @@ const msg = {
|
||||
interval: "Intervalo de actualización",
|
||||
pause: "Pausa",
|
||||
begin: "Inicio",
|
||||
associateOptions: "Opciones de asociación",
|
||||
widget: "Dispositivo pequeño",
|
||||
text: "Texto",
|
||||
duplicateName: "Nombre duplicado",
|
||||
nameTip:
|
||||
"El nombre sólo admite chino e inglés, líneas horizontales y subrayado, y la longitud del nombre no excederá de 300 caracteres",
|
||||
enableAssociate: "Activar asociación",
|
||||
query: "Consulta",
|
||||
postgreSQL: "PostgreSQL",
|
||||
seconds: "Segundos",
|
||||
hourTip: "Seleccione Hora",
|
||||
minuteTip: "Seleccione Minuto",
|
||||
@@ -151,8 +161,6 @@ const msg = {
|
||||
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
||||
months: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
||||
weeks: "Lun_Mar_Mier_Jue_Vie_Sáb_Dom",
|
||||
hello: "Hola",
|
||||
helloMessage: "Bienvenido de vuelta, Apache SkyWalking APM System !",
|
||||
username: "Usuario",
|
||||
password: "Contraseña",
|
||||
title: "Título",
|
||||
@@ -161,7 +169,7 @@ const msg = {
|
||||
dashboard: "Panel",
|
||||
topology: "Topología",
|
||||
trace: "Traza",
|
||||
alarm: "Alarmas",
|
||||
alarm: "Recordatorio en curso",
|
||||
auto: "Auto",
|
||||
reload: "Recargar",
|
||||
version: "Versión",
|
||||
|
@@ -32,6 +32,7 @@ const msg = {
|
||||
dashboards: "仪表盘",
|
||||
profiles: "性能剖析",
|
||||
database: "数据库",
|
||||
mySQL: "MySQL",
|
||||
serviceName: "服务名称",
|
||||
technologies: "技术",
|
||||
health: "健康",
|
||||
@@ -140,6 +141,14 @@ const msg = {
|
||||
interval: "刷新间隔时间",
|
||||
pause: "暂停",
|
||||
begin: "开始",
|
||||
associateOptions: "关联选项",
|
||||
widget: "部件",
|
||||
enableAssociate: "启用关联",
|
||||
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
||||
duplicateName: "重复的名称",
|
||||
text: "文本",
|
||||
query: "查询",
|
||||
postgreSQL: "PostgreSQL",
|
||||
seconds: "秒",
|
||||
hourTip: "选择小时",
|
||||
minuteTip: "选择分钟",
|
||||
@@ -149,8 +158,6 @@ const msg = {
|
||||
monthsHead: "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月",
|
||||
months: "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月",
|
||||
weeks: "一_二_三_四_五_六_日",
|
||||
hello: "你好",
|
||||
helloMessage: "欢迎来到, Apache SkyWalking APM 系统 !",
|
||||
username: "用户名",
|
||||
password: "密码",
|
||||
title: "标题",
|
||||
|
@@ -30,13 +30,13 @@ export const routesAlarm: Array<RouteRecordRaw> = [
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: "/alarm",
|
||||
path: "/alerting",
|
||||
name: "Alarm",
|
||||
meta: {
|
||||
exact: false,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "alarms" */ "@/views/Alarm.vue"),
|
||||
import(/* webpackChunkName: "alerting" */ "@/views/Alarm.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -33,12 +33,20 @@ export const routesBrowser: Array<RouteRecordRaw> = [
|
||||
name: "Browser",
|
||||
meta: {
|
||||
title: "browser",
|
||||
headPath: "/browser",
|
||||
exact: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/browser/tab/:activeTabIndex",
|
||||
name: "BrowserActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -53,32 +53,79 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
exact: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect: "/dashboard/:layerId/:entity/:name",
|
||||
name: "Create",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "Create",
|
||||
name: "CreateChild",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "CreateActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "View",
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:name",
|
||||
meta: {
|
||||
title: "dashboardEdit",
|
||||
exact: false,
|
||||
notShow: true,
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "View",
|
||||
name: "ViewChild",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewServiceRelation",
|
||||
meta: {
|
||||
title: "dashboardEdit",
|
||||
exact: false,
|
||||
notShow: true,
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
|
||||
component: () =>
|
||||
@@ -86,12 +133,29 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewServiceRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewServiceRelationActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewPod",
|
||||
meta: {
|
||||
title: "dashboardEdit",
|
||||
exact: false,
|
||||
notShow: true,
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:name",
|
||||
component: () =>
|
||||
@@ -99,12 +163,30 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewPod",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewPodActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "PodRelation",
|
||||
meta: {
|
||||
title: "dashboardEdit",
|
||||
exact: false,
|
||||
notShow: true,
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
|
||||
component: () =>
|
||||
@@ -112,11 +194,55 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewPodRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewPodRelationActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ProcessRelation",
|
||||
meta: {
|
||||
title: "dashboardEdit",
|
||||
exact: true,
|
||||
notShow: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewProcessRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewProcessRelationActiveTabIndex",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name/duration/:duration",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
name: "ViewProcessRelationDuration",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -26,20 +26,47 @@ export const routesDatabase: Array<RouteRecordRaw> = [
|
||||
icon: "storage",
|
||||
hasGroup: true,
|
||||
},
|
||||
redirect: "/database",
|
||||
redirect: "/mySQL",
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: "/database",
|
||||
name: "Database",
|
||||
path: "/mySQL",
|
||||
name: "MySQL",
|
||||
meta: {
|
||||
title: "virtualDatabase",
|
||||
headPath: "/database",
|
||||
title: "mySQL",
|
||||
exact: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mySQL/tab/:activeTabIndex",
|
||||
name: "MySQLActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/postgreSQL",
|
||||
name: "PostgreSQL",
|
||||
meta: {
|
||||
title: "postgreSQL",
|
||||
exact: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/postgreSQL/tab/:activeTabIndex",
|
||||
name: "PostgreSQLActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -32,13 +32,17 @@ export const routesFunctions: Array<RouteRecordRaw> = [
|
||||
path: "/functions",
|
||||
name: "Functions",
|
||||
meta: {
|
||||
title: "functions",
|
||||
headPath: "/functions",
|
||||
exact: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/functions/tab/:activeTabIndex",
|
||||
name: "FunctionsActiveTabIndex",
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -24,7 +24,7 @@ export const routesGen: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: "general",
|
||||
icon: "chart",
|
||||
hasGroup: false,
|
||||
hasGroup: true,
|
||||
exact: true,
|
||||
},
|
||||
component: Layout,
|
||||
@@ -33,13 +33,41 @@ export const routesGen: Array<RouteRecordRaw> = [
|
||||
path: "/general",
|
||||
name: "GeneralServices",
|
||||
meta: {
|
||||
title: "services",
|
||||
headPath: "/general/service",
|
||||
exact: true,
|
||||
title: "services",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layers" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/general/tab/:activeTabIndex",
|
||||
name: "GeneralServicesActiveTabIndex",
|
||||
meta: {
|
||||
exact: true,
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layers" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/database",
|
||||
name: "VirtualDatabase",
|
||||
meta: {
|
||||
title: "virtualDatabase",
|
||||
exact: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/database/tab/:activeTabIndex",
|
||||
name: "VirtualDatabaseActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -20,7 +20,6 @@ import { routesMesh } from "./serviceMesh";
|
||||
import { routesDatabase } from "./database";
|
||||
import { routesInfra } from "./infrastructure";
|
||||
import { routesDashboard } from "./dashboard";
|
||||
import { routesEvent } from "./event";
|
||||
import { routesSetting } from "./setting";
|
||||
import { routesAlarm } from "./alarm";
|
||||
import { routesSelf } from "./selfObservability";
|
||||
@@ -39,7 +38,6 @@ const routes: Array<RouteRecordRaw> = [
|
||||
...routesSelf,
|
||||
...routesDashboard,
|
||||
...routesAlarm,
|
||||
...routesEvent,
|
||||
...routesSetting,
|
||||
];
|
||||
|
||||
|
@@ -39,6 +39,16 @@ export const routesInfra: Array<RouteRecordRaw> = [
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/linux/tab/:activeTabIndex",
|
||||
name: "LinuxActiveTabIndex",
|
||||
meta: {
|
||||
title: "linux",
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
// {
|
||||
// path: "/infrastructure/vm",
|
||||
// name: "VirtualMachine",
|
||||
|
@@ -33,20 +33,42 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
||||
path: "/kubernetes/cluster",
|
||||
name: "KubernetesCluster",
|
||||
meta: {
|
||||
notShow: false,
|
||||
title: "kubernetesCluster",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/kubernetes/cluster/tab/:activeTabIndex",
|
||||
name: "KubernetesClusterActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
title: "kubernetesClusterActiveTabIndex",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/kubernetes/service",
|
||||
name: "KubernetesService",
|
||||
meta: {
|
||||
notShow: false,
|
||||
title: "kubernetesService",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/kubernetes/service/tab/:activeTabIndex",
|
||||
name: "KubernetesServiceActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
title: "kubernetesServiceActiveTabIndex",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -34,7 +34,15 @@ export const routesSelf: Array<RouteRecordRaw> = [
|
||||
name: "SkyWalkingServer",
|
||||
meta: {
|
||||
title: "skyWalkingServer",
|
||||
headPath: "/mesh/services",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/self/skyWalkingServer/tab/:activeTabIndex",
|
||||
name: "SkyWalkingServerActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
@@ -44,7 +52,15 @@ export const routesSelf: Array<RouteRecordRaw> = [
|
||||
name: "Satellite",
|
||||
meta: {
|
||||
title: "satellite",
|
||||
headPath: "/mesh/controlPanel",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/self/satellite/tab/:activeTabIndex",
|
||||
name: "SatelliteActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
|
@@ -33,8 +33,17 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
||||
path: "/mesh/services",
|
||||
name: "MeshServices",
|
||||
meta: {
|
||||
notShow: false,
|
||||
title: "services",
|
||||
headPath: "/mesh/services",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mesh/services/tab/:activeTabIndex",
|
||||
name: "MeshServicesActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
@@ -43,8 +52,17 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
||||
path: "/mesh/controlPanel",
|
||||
name: "ControlPanel",
|
||||
meta: {
|
||||
notShow: false,
|
||||
title: "controlPanel",
|
||||
headPath: "/mesh/controlPanel",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mesh/controlPanel/tab/:activeTabIndex",
|
||||
name: "ControlPanelActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
@@ -53,10 +71,21 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
||||
path: "/mesh/dataPanel",
|
||||
name: "DataPanel",
|
||||
meta: {
|
||||
notShow: false,
|
||||
title: "dataPanel",
|
||||
headPath: "/mesh/dataPanel",
|
||||
},
|
||||
component: () => import("@/views/Layer.vue"),
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mesh/dataPanel/tab/:activeTabIndex",
|
||||
name: "DataPanelActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
title: "dataPanelActiveTabIndex",
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -29,3 +29,11 @@ export const TextConfig = {
|
||||
fontSize: 14,
|
||||
textAlign: "left",
|
||||
};
|
||||
|
||||
export const TimeRangeConfig = {
|
||||
fontColor: "black",
|
||||
backgroundColor: "white",
|
||||
fontSize: 14,
|
||||
textAlign: "center",
|
||||
text: "text",
|
||||
};
|
||||
|
@@ -34,6 +34,7 @@ interface AppState {
|
||||
pageTitle: string;
|
||||
version: string;
|
||||
isMobile: boolean;
|
||||
reloadTimer: Nullable<any>;
|
||||
}
|
||||
|
||||
export const appStore = defineStore({
|
||||
@@ -53,6 +54,7 @@ export const appStore = defineStore({
|
||||
pageTitle: "",
|
||||
version: "",
|
||||
isMobile: false,
|
||||
reloadTimer: null,
|
||||
}),
|
||||
getters: {
|
||||
duration(): Duration {
|
||||
@@ -117,12 +119,18 @@ export const appStore = defineStore({
|
||||
}
|
||||
this.runEventStack();
|
||||
},
|
||||
updateDurationRow(data: Duration) {
|
||||
this.durationRow = data;
|
||||
},
|
||||
setUTC(utcHour: number, utcMin: number): void {
|
||||
this.runEventStack();
|
||||
this.utcMin = utcMin;
|
||||
this.utcHour = utcHour;
|
||||
this.utc = `${utcHour}:${utcMin}`;
|
||||
},
|
||||
updateUTC(data: string) {
|
||||
this.utc = data;
|
||||
},
|
||||
setIsMobile(mode: boolean) {
|
||||
this.isMobile = mode;
|
||||
},
|
||||
@@ -153,10 +161,9 @@ export const appStore = defineStore({
|
||||
.params({});
|
||||
if (res.data.errors) {
|
||||
this.utc = -(new Date().getTimezoneOffset() / 60) + ":0";
|
||||
return res.data;
|
||||
}
|
||||
} else {
|
||||
this.utc = res.data.data.getTimeInfo.timezone / 100 + ":0";
|
||||
|
||||
}
|
||||
const utcArr = this.utc.split(":");
|
||||
this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
|
||||
this.utcMin = isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]);
|
||||
@@ -173,6 +180,9 @@ export const appStore = defineStore({
|
||||
this.version = res.data.data.version;
|
||||
return res.data;
|
||||
},
|
||||
setReloadTimer(timer: any): void {
|
||||
this.reloadTimer = timer;
|
||||
},
|
||||
},
|
||||
});
|
||||
export function useAppStoreWithOut(): any {
|
||||
|
@@ -21,7 +21,7 @@ import graphql from "@/graphql";
|
||||
import query from "@/graphql/fetch";
|
||||
import { DashboardItem } from "@/types/dashboard";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { NewControl, TextConfig } from "../data";
|
||||
import { NewControl, TextConfig, TimeRangeConfig } from "../data";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@@ -39,6 +39,7 @@ interface DashboardState {
|
||||
dashboards: DashboardItem[];
|
||||
currentDashboard: Nullable<DashboardItem>;
|
||||
editMode: boolean;
|
||||
currentTabIndex: number;
|
||||
}
|
||||
|
||||
export const dashboardStore = defineStore({
|
||||
@@ -56,6 +57,7 @@ export const dashboardStore = defineStore({
|
||||
dashboards: [],
|
||||
currentDashboard: null,
|
||||
editMode: false,
|
||||
currentTabIndex: 0,
|
||||
}),
|
||||
actions: {
|
||||
setLayout(data: LayoutConfig[]) {
|
||||
@@ -80,6 +82,7 @@ export const dashboardStore = defineStore({
|
||||
const newItem: LayoutConfig = {
|
||||
...NewControl,
|
||||
i: index,
|
||||
id: index,
|
||||
type,
|
||||
metricTypes: [""],
|
||||
metrics: [""],
|
||||
@@ -111,11 +114,14 @@ export const dashboardStore = defineStore({
|
||||
};
|
||||
}
|
||||
if (
|
||||
type === "Trace" ||
|
||||
type === "Profile" ||
|
||||
type === "Log" ||
|
||||
type === "Ebpf" ||
|
||||
type === "DemandLog"
|
||||
[
|
||||
"Trace",
|
||||
"Profile",
|
||||
"Log",
|
||||
"DemandLog",
|
||||
"Ebpf",
|
||||
"NetworkProfiling",
|
||||
].includes(type)
|
||||
) {
|
||||
newItem.h = 36;
|
||||
}
|
||||
@@ -123,6 +129,11 @@ export const dashboardStore = defineStore({
|
||||
newItem.h = 6;
|
||||
newItem.graph = TextConfig;
|
||||
}
|
||||
if (type === "TimeRange") {
|
||||
newItem.w = 8;
|
||||
newItem.h = 6;
|
||||
newItem.graph = TimeRangeConfig;
|
||||
}
|
||||
this.activedGridItem = newItem.i;
|
||||
this.selectedGrid = newItem;
|
||||
this.layout = this.layout.map((d: LayoutConfig) => {
|
||||
@@ -158,9 +169,11 @@ export const dashboardStore = defineStore({
|
||||
if (!children.length) {
|
||||
index = "0";
|
||||
}
|
||||
const id = `${activedGridItem}-${tabIndex}-${index}`;
|
||||
const newItem: LayoutConfig = {
|
||||
...NewControl,
|
||||
i: index,
|
||||
id,
|
||||
type,
|
||||
metricTypes: [""],
|
||||
metrics: [""],
|
||||
@@ -172,11 +185,14 @@ export const dashboardStore = defineStore({
|
||||
};
|
||||
}
|
||||
if (
|
||||
type === "Trace" ||
|
||||
type === "Profile" ||
|
||||
type === "Log" ||
|
||||
type === "DemandLog" ||
|
||||
type === "Ebpf"
|
||||
[
|
||||
"Trace",
|
||||
"Profile",
|
||||
"Log",
|
||||
"DemandLog",
|
||||
"Ebpf",
|
||||
"NetworkProfiling",
|
||||
].includes(type)
|
||||
) {
|
||||
newItem.h = 32;
|
||||
}
|
||||
@@ -184,6 +200,11 @@ export const dashboardStore = defineStore({
|
||||
newItem.h = 6;
|
||||
newItem.graph = TextConfig;
|
||||
}
|
||||
if (type === "TimeRange") {
|
||||
newItem.w = 8;
|
||||
newItem.h = 6;
|
||||
newItem.graph = TextConfig;
|
||||
}
|
||||
if (this.layout[idx].children) {
|
||||
const items = children.map((d: LayoutConfig) => {
|
||||
d.y = d.y + newItem.h;
|
||||
@@ -198,6 +219,7 @@ export const dashboardStore = defineStore({
|
||||
this.activedGridItem = index;
|
||||
},
|
||||
setActiveTabIndex(index: number, target?: number) {
|
||||
this.currentTabIndex = index;
|
||||
const m = target || this.activedGridItem;
|
||||
const idx = this.layout.findIndex((d: LayoutConfig) => d.i === m);
|
||||
if (idx < 0) {
|
||||
@@ -287,6 +309,28 @@ export const dashboardStore = defineStore({
|
||||
};
|
||||
this.selectedGrid = this.layout[index];
|
||||
},
|
||||
setWidget(param: LayoutConfig) {
|
||||
for (let i = 0; i < this.layout.length; i++) {
|
||||
if (this.layout[i].type === "Tab") {
|
||||
if (this.layout[i].children && this.layout[i].children.length) {
|
||||
for (const child of this.layout[i].children) {
|
||||
if (child.children && child.children.length) {
|
||||
for (let c = 0; c < child.children.length; c++) {
|
||||
if (child.children[c].id === param.id) {
|
||||
child.children.splice(c, 1, param);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.layout[i].id === param.id) {
|
||||
this.layout.splice(i, 1, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async fetchMetricType(item: string) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryTypeOfMetrics")
|
||||
|
@@ -25,8 +25,7 @@ import {
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
interface EbpfStore {
|
||||
interface EbpfState {
|
||||
taskList: EBPFTaskList[];
|
||||
eBPFSchedules: EBPFProfilingSchedule[];
|
||||
currentSchedule: EBPFProfilingSchedule | Record<string, never>;
|
||||
@@ -40,7 +39,7 @@ interface EbpfStore {
|
||||
|
||||
export const ebpfStore = defineStore({
|
||||
id: "eBPF",
|
||||
state: (): EbpfStore => ({
|
||||
state: (): EbpfState => ({
|
||||
taskList: [],
|
||||
eBPFSchedules: [],
|
||||
currentSchedule: {},
|
||||
@@ -53,7 +52,7 @@ export const ebpfStore = defineStore({
|
||||
}),
|
||||
actions: {
|
||||
setSelectedTask(task: EBPFTaskList) {
|
||||
this.selectedTask = task;
|
||||
this.selectedTask = task || {};
|
||||
},
|
||||
setCurrentSchedule(s: EBPFProfilingSchedule) {
|
||||
this.currentSchedule = s;
|
||||
@@ -84,22 +83,31 @@ export const ebpfStore = defineStore({
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.getTaskList(param.serviceId);
|
||||
this.getTaskList({
|
||||
serviceId: param.serviceId,
|
||||
targets: ["ON_CPU", "OFF_CPU"],
|
||||
});
|
||||
return res.data;
|
||||
},
|
||||
async getTaskList(serviceId: string) {
|
||||
if (!serviceId) {
|
||||
async getTaskList(params: {
|
||||
serviceId: string;
|
||||
serviceInstanceId: string;
|
||||
targets: string[];
|
||||
}) {
|
||||
if (!params.serviceId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("getEBPFTasks")
|
||||
.params({ serviceId });
|
||||
.params(params);
|
||||
|
||||
this.tip = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.taskList = res.data.data.queryEBPFTasks || [];
|
||||
this.selectedTask = this.taskList[0] || {};
|
||||
this.setSelectedTask(this.selectedTask);
|
||||
if (!this.taskList.length) {
|
||||
return res.data;
|
||||
}
|
||||
|
@@ -19,16 +19,16 @@ import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { Event, QueryEventCondition } from "@/types/events";
|
||||
import { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import { Instance, Endpoint } from "@/types/selector";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
|
||||
interface eventState {
|
||||
loading: boolean;
|
||||
events: Event[];
|
||||
services: Service[];
|
||||
instances: Instance[];
|
||||
endpoints: Endpoint[];
|
||||
condition: QueryEventCondition | any;
|
||||
condition: Nullable<QueryEventCondition>;
|
||||
}
|
||||
|
||||
export const eventStore = defineStore({
|
||||
@@ -36,32 +36,18 @@ export const eventStore = defineStore({
|
||||
state: (): eventState => ({
|
||||
loading: false,
|
||||
events: [],
|
||||
services: [{ value: "", label: "All" }],
|
||||
instances: [{ value: "", label: "All" }],
|
||||
endpoints: [{ value: "", label: "All" }],
|
||||
condition: {
|
||||
paging: { pageNum: 1, pageSize: 15 },
|
||||
},
|
||||
condition: null,
|
||||
}),
|
||||
actions: {
|
||||
setEventCondition(data: any) {
|
||||
this.condition = { ...this.condition, ...data };
|
||||
setEventCondition(data: QueryEventCondition) {
|
||||
this.condition = data;
|
||||
},
|
||||
async getServices(layer: string) {
|
||||
if (!layer) {
|
||||
this.services = [{ value: "", label: "All" }];
|
||||
return new Promise((resolve) => resolve([]));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({
|
||||
layer,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.services = res.data.data.services;
|
||||
return res.data;
|
||||
},
|
||||
async getInstances(serviceId: string) {
|
||||
async getInstances() {
|
||||
const serviceId = useSelectorStore().currentService
|
||||
? useSelectorStore().currentService.id
|
||||
: "";
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
@@ -75,7 +61,13 @@ export const eventStore = defineStore({
|
||||
];
|
||||
return res.data;
|
||||
},
|
||||
async getEndpoints(serviceId: string) {
|
||||
async getEndpoints() {
|
||||
const serviceId = useSelectorStore().currentService
|
||||
? useSelectorStore().currentService.id
|
||||
: "";
|
||||
if (!serviceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
@@ -112,6 +104,9 @@ export const eventStore = defineStore({
|
||||
scope = "Endpoint";
|
||||
}
|
||||
item.scope = scope;
|
||||
if (!item.endTime || item.endTime === item.startTime) {
|
||||
item.endTime = Number(item.startTime) + 60000;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
);
|
||||
|
@@ -53,6 +53,13 @@ export const logStore = defineStore({
|
||||
setLogCondition(data: any) {
|
||||
this.conditions = { ...this.conditions, ...data };
|
||||
},
|
||||
resetState() {
|
||||
this.logs = [];
|
||||
this.conditions = {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
paging: { pageNum: 1, pageSize: 15 },
|
||||
};
|
||||
},
|
||||
async getServices(layer: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({
|
||||
layer,
|
||||
|
183
src/store/modules/network-profiling.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* 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 { defineStore } from "pinia";
|
||||
import { EBPFTaskList, ProcessNode } from "@/types/ebpf";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { Call } from "@/types/topology";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
interface NetworkProfilingState {
|
||||
networkTasks: EBPFTaskList[];
|
||||
networkTip: string;
|
||||
selectedNetworkTask: Recordable<EBPFTaskList>;
|
||||
nodes: ProcessNode[];
|
||||
calls: Call[];
|
||||
node: Nullable<ProcessNode>;
|
||||
call: Nullable<Call>;
|
||||
metricsLayout: LayoutConfig[];
|
||||
selectedMetric: Nullable<LayoutConfig>;
|
||||
activeMetricIndex: string;
|
||||
aliveNetwork: boolean;
|
||||
loadNodes: boolean;
|
||||
}
|
||||
|
||||
export const networkProfilingStore = defineStore({
|
||||
id: "networkProfiling",
|
||||
state: (): NetworkProfilingState => ({
|
||||
networkTasks: [],
|
||||
networkTip: "",
|
||||
selectedNetworkTask: {},
|
||||
nodes: [],
|
||||
calls: [],
|
||||
node: null,
|
||||
call: null,
|
||||
metricsLayout: [],
|
||||
selectedMetric: null,
|
||||
activeMetricIndex: "",
|
||||
aliveNetwork: false,
|
||||
loadNodes: false,
|
||||
}),
|
||||
actions: {
|
||||
setSelectedNetworkTask(task: EBPFTaskList) {
|
||||
this.selectedNetworkTask = task || {};
|
||||
},
|
||||
setNode(node: Node) {
|
||||
this.node = node;
|
||||
},
|
||||
setLink(link: Call) {
|
||||
this.call = link;
|
||||
},
|
||||
setMetricsLayout(layout: LayoutConfig[]) {
|
||||
this.metricsLayout = layout;
|
||||
},
|
||||
setSelectedMetric(item: LayoutConfig) {
|
||||
this.selectedMetric = item;
|
||||
},
|
||||
setActiveItem(index: string) {
|
||||
this.activeMetricIndex = index;
|
||||
},
|
||||
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
|
||||
const obj = {} as any;
|
||||
let calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
|
||||
if (!obj[next.id]) {
|
||||
obj[next.id] = true;
|
||||
next.value = next.value || 1;
|
||||
for (const node of data.nodes) {
|
||||
if (next.source === node.id) {
|
||||
next.sourceObj = node;
|
||||
}
|
||||
if (next.target === node.id) {
|
||||
next.targetObj = node;
|
||||
}
|
||||
}
|
||||
next.value = next.value || 1;
|
||||
prev.push(next);
|
||||
}
|
||||
return prev;
|
||||
}, []);
|
||||
calls = calls.map((d: any) => {
|
||||
d.sourceId = d.source;
|
||||
d.targetId = d.target;
|
||||
d.source = d.sourceObj;
|
||||
d.target = d.targetObj;
|
||||
delete d.sourceObj;
|
||||
delete d.targetObj;
|
||||
return d;
|
||||
});
|
||||
this.calls = calls;
|
||||
this.nodes = data.nodes;
|
||||
},
|
||||
async createNetworkTask(param: {
|
||||
serviceId: string;
|
||||
serviceInstanceId: string;
|
||||
}) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("newNetworkProfiling")
|
||||
.params({ request: { instanceId: param.serviceInstanceId } });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getTaskList(params: {
|
||||
serviceId: string;
|
||||
serviceInstanceId: string;
|
||||
targets: string[];
|
||||
}) {
|
||||
if (!params.serviceId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("getEBPFTasks")
|
||||
.params(params);
|
||||
|
||||
this.networkTip = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.networkTasks = res.data.data.queryEBPFTasks || [];
|
||||
this.selectedNetworkTask = this.networkTasks[0] || {};
|
||||
this.setSelectedNetworkTask(this.selectedNetworkTask);
|
||||
return res.data;
|
||||
},
|
||||
async keepNetworkProfiling(taskId: string) {
|
||||
if (!taskId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("aliveNetworkProfiling")
|
||||
.params({ taskId });
|
||||
|
||||
this.aliveMessage = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.aliveNetwork = res.data.data.keepEBPFNetworkProfiling.status;
|
||||
if (!this.aliveNetwork) {
|
||||
ElMessage.warning(res.data.data.keepEBPFNetworkProfiling.errorReason);
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getProcessTopology(params: {
|
||||
duration: any;
|
||||
serviceInstanceId: string;
|
||||
}) {
|
||||
this.loadNodes = true;
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("getProcessTopology")
|
||||
.params(params);
|
||||
this.loadNodes = false;
|
||||
if (res.data.errors) {
|
||||
this.nodes = [];
|
||||
this.calls = [];
|
||||
return res.data;
|
||||
}
|
||||
const { topology } = res.data.data;
|
||||
|
||||
this.setTopology(topology);
|
||||
return res.data;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function useNetworkProfilingStore(): any {
|
||||
return networkProfilingStore(store);
|
||||
}
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { defineStore } from "pinia";
|
||||
import { Service, Instance, Endpoint } from "@/types/selector";
|
||||
import { Service, Instance, Endpoint, Process } from "@/types/selector";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
@@ -24,11 +24,15 @@ interface SelectorState {
|
||||
services: Service[];
|
||||
destServices: Service[];
|
||||
pods: Array<Instance | Endpoint>;
|
||||
processes: Process[];
|
||||
destProcesses: Process[];
|
||||
currentService: Nullable<Service>;
|
||||
currentPod: Nullable<Instance | Endpoint>;
|
||||
currentProcess: Nullable<Process>;
|
||||
currentDestService: Nullable<Service>;
|
||||
currentDestPod: Nullable<Instance | Endpoint>;
|
||||
destPods: Array<Instance | Endpoint>;
|
||||
currentDestProcess: Nullable<Process>;
|
||||
}
|
||||
|
||||
export const selectorStore = defineStore({
|
||||
@@ -38,10 +42,14 @@ export const selectorStore = defineStore({
|
||||
destServices: [],
|
||||
pods: [],
|
||||
destPods: [],
|
||||
processes: [],
|
||||
destProcesses: [],
|
||||
currentService: null,
|
||||
currentPod: null,
|
||||
currentProcess: null,
|
||||
currentDestService: null,
|
||||
currentDestPod: null,
|
||||
currentDestProcess: null,
|
||||
}),
|
||||
actions: {
|
||||
setCurrentService(service: Nullable<Service>) {
|
||||
@@ -56,6 +64,18 @@ export const selectorStore = defineStore({
|
||||
setCurrentDestPod(pod: Nullable<Instance | Endpoint>) {
|
||||
this.currentDestPod = pod;
|
||||
},
|
||||
setCurrentProcess(process: Nullable<Process>) {
|
||||
this.currentProcess = process;
|
||||
},
|
||||
setCurrentDestProcess(process: Nullable<Process>) {
|
||||
this.currentDestProcess = process;
|
||||
},
|
||||
setDestPods(pods: Array<Instance | Endpoint>) {
|
||||
this.destPods = pods;
|
||||
},
|
||||
setDestProcesses(processes: Array<Process>) {
|
||||
this.destProcesses = processes;
|
||||
},
|
||||
async fetchLayers(): Promise<AxiosResponse> {
|
||||
const res: AxiosResponse = await graphql.query("queryLayers").params({});
|
||||
|
||||
@@ -93,6 +113,27 @@ export const selectorStore = defineStore({
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getProcesses(param?: {
|
||||
instanceId: string;
|
||||
isRelation: boolean;
|
||||
}): Promise<Nullable<AxiosResponse>> {
|
||||
const instanceId = param ? param.instanceId : this.currentPod?.id;
|
||||
if (!instanceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryProcesses").params({
|
||||
instanceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (param && param.isRelation) {
|
||||
this.destProcesses = res.data.data.processes || [];
|
||||
return res.data;
|
||||
}
|
||||
this.processes = res.data.data.processes || [];
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getEndpoints(params: {
|
||||
keyword?: string;
|
||||
serviceId?: string;
|
||||
@@ -176,6 +217,25 @@ export const selectorStore = defineStore({
|
||||
this.pods = [res.data.data.endpoint];
|
||||
}
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async getProcess(instanceId: string, isRelation?: boolean) {
|
||||
if (!instanceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryProcess").params({
|
||||
instanceId,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (isRelation) {
|
||||
this.currentDestProcess = res.data.data.process || null;
|
||||
this.destProcesses = [res.data.data.process];
|
||||
return;
|
||||
}
|
||||
this.currentProcess = res.data.data.process || null;
|
||||
this.processes = [res.data.data.process];
|
||||
}
|
||||
|
||||
return res.data;
|
||||
},
|
||||
},
|
||||
|
@@ -63,6 +63,17 @@ export const traceStore = defineStore({
|
||||
setTraceSpans(spans: Span) {
|
||||
this.traceSpans = spans;
|
||||
},
|
||||
resetState() {
|
||||
this.traceSpans = [];
|
||||
this.traceList = [];
|
||||
this.currentTrace = {};
|
||||
this.conditions = {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
paging: { pageNum: 1, pageSize: 20 },
|
||||
traceState: "ALL",
|
||||
queryOrder: "BY_START_TIME",
|
||||
};
|
||||
},
|
||||
async getServices(layer: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({
|
||||
layer,
|
||||
|
@@ -139,8 +139,10 @@ pre {
|
||||
padding-left: 56px !important;
|
||||
}
|
||||
|
||||
.el-sub-menu__title {
|
||||
.el-icon.menu-icons {
|
||||
margin-top: -3px !important;
|
||||
margin-top: -5px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-switch__label--left {
|
||||
@@ -154,3 +156,38 @@ pre {
|
||||
.switch {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
div.vis-tooltip {
|
||||
max-width: 600px;
|
||||
overflow: hidden;
|
||||
background-color: #fff !important;
|
||||
white-space: normal !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.vis-item {
|
||||
cursor: pointer;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.vis-item.Error {
|
||||
background-color: #e66;
|
||||
opacity: 0.8;
|
||||
border-color: #e66;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.vis-item.Normal {
|
||||
background-color: #fac858;
|
||||
border-color: #fac858;
|
||||
color: #666 !important;
|
||||
}
|
||||
|
||||
.vis-item .vis-item-content {
|
||||
padding: 0 5px !important;
|
||||
}
|
||||
|
||||
.vis-item.vis-selected.Error,
|
||||
.vis-item.vis-selected.Normal {
|
||||
color: #1a1a1a !important;
|
||||
}
|
||||
|
28
src/types/dashboard.d.ts
vendored
@@ -36,6 +36,21 @@ export interface LayoutConfig {
|
||||
children?: { name: string; children: LayoutConfig[] }[];
|
||||
activedTabIndex?: number;
|
||||
metricConfig?: MetricConfigOpt[];
|
||||
id?: string;
|
||||
associate?: { widgetId: string }[];
|
||||
eventAssociate?: boolean;
|
||||
filters?: {
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
isRange?: boolean;
|
||||
duration?: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
traceId?: string;
|
||||
spanId?: string;
|
||||
segmentId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type MetricConfigOpt = {
|
||||
@@ -48,6 +63,7 @@ export type MetricConfigOpt = {
|
||||
};
|
||||
|
||||
export interface WidgetConfig {
|
||||
name?: string;
|
||||
title?: string;
|
||||
tips?: string;
|
||||
}
|
||||
@@ -137,3 +153,15 @@ export interface TopologyConfig {
|
||||
depth?: number;
|
||||
showDepth?: boolean;
|
||||
}
|
||||
export type EventParams = {
|
||||
componentType: string;
|
||||
seriesType: string;
|
||||
seriesIndex: number;
|
||||
seriesName: string;
|
||||
name: string;
|
||||
dataIndex: number;
|
||||
data: Record<string, unknown>;
|
||||
dataType: string;
|
||||
value: number | number[];
|
||||
color: string;
|
||||
};
|
||||
|
24
src/types/ebpf.d.ts
vendored
@@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Process } from "./selector";
|
||||
export interface EBPFTaskCreationRequest {
|
||||
serviceId: string;
|
||||
processLabels: string[];
|
||||
@@ -43,18 +44,7 @@ export interface EBPFProfilingSchedule {
|
||||
startTime: number;
|
||||
}
|
||||
|
||||
export type Process = {
|
||||
id: string;
|
||||
name: string;
|
||||
serviceId: string;
|
||||
serviceName: string;
|
||||
instanceId: string;
|
||||
instanceName: string;
|
||||
agentId: string;
|
||||
detectType: string;
|
||||
attributes: { name: string; value: string }[];
|
||||
labels: string[];
|
||||
};
|
||||
export type Process = Process;
|
||||
export type StackElement = {
|
||||
id: string;
|
||||
originId: string;
|
||||
@@ -75,3 +65,13 @@ export type AnalyzationTrees = {
|
||||
dumpCount: number;
|
||||
stackType: string;
|
||||
};
|
||||
export type ProcessNode = {
|
||||
id: string;
|
||||
name: string;
|
||||
serviceId: string;
|
||||
serviceName: string;
|
||||
serviceInstanceId: string;
|
||||
serviceInstanceName: string;
|
||||
name: string;
|
||||
isReal: boolean;
|
||||
};
|
||||
|
13
src/types/selector.d.ts
vendored
@@ -46,3 +46,16 @@ export type Service = {
|
||||
layers: string[];
|
||||
shortName: string;
|
||||
};
|
||||
|
||||
export type Process = {
|
||||
id: string;
|
||||
name: string;
|
||||
serviceId: string;
|
||||
serviceName: string;
|
||||
instanceId: string;
|
||||
instanceName: string;
|
||||
agentId: string;
|
||||
detectType: string;
|
||||
attributes: { name: string; value: string }[];
|
||||
labels: string[];
|
||||
};
|
||||
|
3
src/types/topology.d.ts
vendored
@@ -23,6 +23,9 @@ export interface Call {
|
||||
sourceObj?: any;
|
||||
targetObj?: any;
|
||||
value?: number;
|
||||
lowerArc?: boolean;
|
||||
sourceComponents: string[];
|
||||
targetComponents: string[];
|
||||
}
|
||||
export interface Node {
|
||||
id: string;
|
||||
|
@@ -14,6 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import dayjs from "dayjs";
|
||||
export default function dateFormatStep(
|
||||
date: Date,
|
||||
step: string,
|
||||
@@ -99,3 +100,6 @@ export const dateFormatTime = (date: Date, step: string): string => {
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
export const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
dayjs(new Date(date)).format(pattern);
|
||||
|
@@ -16,13 +16,7 @@
|
||||
*/
|
||||
import * as echarts from "echarts/core";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
HeatmapChart,
|
||||
SankeyChart,
|
||||
} from "echarts/charts";
|
||||
import { BarChart, LineChart, HeatmapChart, SankeyChart } from "echarts/charts";
|
||||
|
||||
import {
|
||||
TitleComponent,
|
||||
@@ -32,6 +26,7 @@ import {
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
MarkAreaComponent,
|
||||
} from "echarts/components";
|
||||
|
||||
import { SVGRenderer } from "echarts/renderers";
|
||||
@@ -43,13 +38,13 @@ echarts.use([
|
||||
GridComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
HeatmapChart,
|
||||
SankeyChart,
|
||||
SVGRenderer,
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
MarkAreaComponent,
|
||||
]);
|
||||
|
||||
export default echarts;
|
||||
|
@@ -24,7 +24,7 @@ import Header from "./alarm/Header.vue";
|
||||
import Content from "./alarm/Content.vue";
|
||||
|
||||
const appStore = useAppStoreWithOut();
|
||||
appStore.setPageTitle("Alarm");
|
||||
appStore.setPageTitle("Alerting");
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.alarm {
|
||||
|
@@ -69,18 +69,16 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { Languages } from "@/constants/data";
|
||||
import Selector from "@/components/Selector.vue";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const state = reactive<{ timer: ReturnType<typeof setInterval> | null }>({
|
||||
timer: null,
|
||||
});
|
||||
const lang = ref<string>(locale.value || "en");
|
||||
const autoTime = ref<number>(6);
|
||||
const auto = ref<boolean>(appStore.autoRefresh || false);
|
||||
@@ -91,8 +89,11 @@ appStore.setPageTitle("Setting");
|
||||
const handleReload = () => {
|
||||
const gap =
|
||||
appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const time: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||
appStore.setDuration(timeFormat(time));
|
||||
const dates: Date[] = [
|
||||
getLocalTime(appStore.utc, new Date(new Date().getTime() - gap)),
|
||||
getLocalTime(appStore.utc, new Date()),
|
||||
];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
};
|
||||
const handleAuto = () => {
|
||||
if (autoTime.value < 1) {
|
||||
@@ -101,10 +102,10 @@ const handleAuto = () => {
|
||||
appStore.setAutoRefresh(auto.value);
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
state.timer = setInterval(handleReload, autoTime.value * 1000);
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
} else {
|
||||
if (state.timer) {
|
||||
clearInterval(state.timer);
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -112,12 +113,12 @@ const changeAutoTime = () => {
|
||||
if (autoTime.value < 1) {
|
||||
return;
|
||||
}
|
||||
if (state.timer) {
|
||||
clearInterval(state.timer);
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
state.timer = setInterval(handleReload, autoTime.value * 1000);
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
}
|
||||
};
|
||||
const setLang = (): void => {
|
||||
|
@@ -145,16 +145,14 @@ limitations under the License. -->
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Alarm, Event } from "@/types/alarm";
|
||||
import { useAlarmStore } from "@/store/modules/alarm";
|
||||
import { EventsDetailHeaders, AlarmDetailCol, EventsDetailKeys } from "./data";
|
||||
import { dateFormat } from "@/utils/dateFormat";
|
||||
|
||||
const { t } = useI18n();
|
||||
const alarmStore = useAlarmStore();
|
||||
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
dayjs(date).format(pattern);
|
||||
const isShowDetails = ref<boolean>(false);
|
||||
const showEventDetails = ref<boolean>(false);
|
||||
const currentDetail = ref<Alarm | any>({});
|
||||
|
@@ -15,11 +15,7 @@ limitations under the License. -->
|
||||
<template>
|
||||
<div>
|
||||
<span class="grey">{{ t("tags") }}: </span>
|
||||
<span
|
||||
v-if="tagsList.length"
|
||||
class="trace-tags"
|
||||
:style="type === 'LOG' ? `min-width: 122px;` : ''"
|
||||
>
|
||||
<span v-if="tagsList.length" class="trace-tags">
|
||||
<span class="selected" v-for="(item, index) in tagsList" :key="index">
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeTags(index)">×</span>
|
||||
@@ -33,31 +29,28 @@ limitations under the License. -->
|
||||
@change="addLabels"
|
||||
:placeholder="t('addTags')"
|
||||
/>
|
||||
<span v-else>
|
||||
<el-popover v-else trigger="click" :visible="visible" width="300px">
|
||||
<template #reference>
|
||||
<el-input
|
||||
size="small"
|
||||
v-model="tags"
|
||||
class="trace-new-tag"
|
||||
@click="showClick"
|
||||
@input="searchTags"
|
||||
@blur="visible = false"
|
||||
@focus="visible = true"
|
||||
/>
|
||||
<el-dropdown
|
||||
ref="dropdownTag"
|
||||
trigger="contextmenu"
|
||||
:hide-on-click="false"
|
||||
style="margin: 20px 0 0 -130px"
|
||||
v-if="tagArr.length"
|
||||
</template>
|
||||
<div class="content">
|
||||
<span
|
||||
v-for="(item, index) in tagList"
|
||||
:key="index"
|
||||
@click="selectTag(item)"
|
||||
class="tag-item"
|
||||
>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="(item, index) in tagArr" :key="index">
|
||||
<span @click="selectTag(item)" class="tag-item">
|
||||
{{ item }}
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<span
|
||||
class="tags-tip"
|
||||
:class="type !== 'ALARM' && tagArr.length ? 'link-tips' : ''"
|
||||
@@ -68,12 +61,12 @@ limitations under the License. -->
|
||||
>
|
||||
{{ t("tagsLink") }}
|
||||
</a>
|
||||
<el-tooltip :content="t(tipsMap[type])">
|
||||
<el-tooltip :content="t((tipsMap as any)[type])">
|
||||
<span>
|
||||
<Icon class="icon-help mr-5" iconName="help" size="middle" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<b v-if="type === 'AL'">{{ t("noticeTag") }}</b>
|
||||
<b v-if="type === 'ALARM'">{{ t("noticeTag") }}</b>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -85,7 +78,7 @@ import { useLogStore } from "@/store/modules/log";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
|
||||
/*global Nullable, defineEmits, defineProps */
|
||||
/*global defineEmits, defineProps */
|
||||
const emit = defineEmits(["update"]);
|
||||
const props = defineProps({
|
||||
type: { type: String, default: "TRACE" },
|
||||
@@ -97,13 +90,14 @@ const { t } = useI18n();
|
||||
const tags = ref<string>("");
|
||||
const tagsList = ref<string[]>([]);
|
||||
const tagArr = ref<string[]>([]);
|
||||
const tagList = ref<string[]>([]);
|
||||
const tagKeys = ref<string[]>([]);
|
||||
const visible = ref<boolean>(false);
|
||||
const tipsMap = {
|
||||
LOG: "logTagsTip",
|
||||
TRACE: "traceTagsTip",
|
||||
ALARM: "alarmTagsTip",
|
||||
};
|
||||
const dropdownTag = ref<Nullable<any>>(null);
|
||||
|
||||
fetchTagKeys();
|
||||
|
||||
@@ -143,6 +137,7 @@ async function fetchTagKeys() {
|
||||
}
|
||||
tagArr.value = resp.data.tagKeys;
|
||||
tagKeys.value = resp.data.tagKeys;
|
||||
searchTags();
|
||||
}
|
||||
|
||||
async function fetchTagValues() {
|
||||
@@ -159,25 +154,36 @@ async function fetchTagValues() {
|
||||
return;
|
||||
}
|
||||
tagArr.value = resp.data.tagValues;
|
||||
searchTags();
|
||||
}
|
||||
|
||||
function selectTag(item: string) {
|
||||
if (tags.value.includes("=")) {
|
||||
tags.value += item;
|
||||
const key = tags.value.split("=")[0];
|
||||
tags.value = key + "=" + item;
|
||||
addLabels();
|
||||
tagArr.value = tagKeys.value;
|
||||
dropdownTag.value.handleClose();
|
||||
searchTags();
|
||||
return;
|
||||
}
|
||||
tags.value = item + "=";
|
||||
fetchTagValues();
|
||||
}
|
||||
|
||||
function showClick() {
|
||||
if (dropdownTag.value) {
|
||||
dropdownTag.value.handleOpen();
|
||||
function searchTags() {
|
||||
if (!tags.value) {
|
||||
tagList.value = tagArr.value;
|
||||
return;
|
||||
}
|
||||
let search = "";
|
||||
if (tags.value.includes("=")) {
|
||||
search = tags.value.split("=")[1];
|
||||
} else {
|
||||
search = tags.value;
|
||||
}
|
||||
tagList.value = tagArr.value.filter((d: string) => d.includes(search));
|
||||
}
|
||||
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => {
|
||||
@@ -221,6 +227,12 @@ watch(
|
||||
.tag-item {
|
||||
display: inline-block;
|
||||
min-width: 210px;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-tip {
|
||||
@@ -229,7 +241,6 @@ watch(
|
||||
|
||||
.link-tips {
|
||||
display: inline-block;
|
||||
margin-left: 130px;
|
||||
}
|
||||
|
||||
.light {
|
||||
@@ -247,4 +258,10 @@ watch(
|
||||
.icon-help {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 300px;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
@@ -41,6 +41,7 @@ import Tool from "./panel/Tool.vue";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import Configuration from "./configuration";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Dashboard",
|
||||
@@ -66,7 +67,8 @@ export default defineComponent({
|
||||
sessionStorage.getItem(layoutKey.value) || "{}"
|
||||
);
|
||||
const layout: any = c.configuration || {};
|
||||
dashboardStore.setLayout(layout.children || []);
|
||||
|
||||
dashboardStore.setLayout(setWidgetsID(layout.children || []));
|
||||
appStore.setPageTitle(layout.name);
|
||||
if (p.entity) {
|
||||
dashboardStore.setCurrentDashboard({
|
||||
@@ -78,6 +80,24 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
}
|
||||
function setWidgetsID(widgets: LayoutConfig[]) {
|
||||
for (const item of widgets) {
|
||||
item.id = item.i;
|
||||
if (item.type === "Tab") {
|
||||
if (item.children && item.children.length) {
|
||||
for (const [index, child] of item.children.entries()) {
|
||||
if (child.children && child.children.length) {
|
||||
child.children.map((d: { i: string; index: string } | any) => {
|
||||
d.id = `${item.i}-${index}-${d.i}`;
|
||||
return d;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return widgets;
|
||||
}
|
||||
function handleClick(e: any) {
|
||||
e.stopPropagation();
|
||||
if (e.target.className === "ds-main") {
|
||||
|
@@ -20,7 +20,7 @@ limitations under the License. -->
|
||||
placeholder="Please input name"
|
||||
class="input-with-search ml-10"
|
||||
size="small"
|
||||
@change="searchDashboards"
|
||||
@change="searchDashboards(1)"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="small">
|
||||
@@ -129,7 +129,8 @@ limitations under the License. -->
|
||||
small
|
||||
layout="prev, pager, next"
|
||||
:page-size="pageSize"
|
||||
:total="dashboardStore.dashboards.length"
|
||||
:total="total"
|
||||
v-model="currentPage"
|
||||
@current-change="changePage"
|
||||
@prev-click="changePage"
|
||||
@next-click="changePage"
|
||||
@@ -159,6 +160,8 @@ const pageSize = 18;
|
||||
const dashboards = ref<DashboardItem[]>([]);
|
||||
const searchText = ref<string>("");
|
||||
const loading = ref<boolean>(false);
|
||||
const currentPage = ref<number>(1);
|
||||
const total = ref<number>(0);
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>();
|
||||
const multipleSelection = ref<DashboardItem[]>([]);
|
||||
const dashboardFile = ref<Nullable<HTMLDivElement>>(null);
|
||||
@@ -170,7 +173,7 @@ const handleSelectionChange = (val: DashboardItem[]) => {
|
||||
setList();
|
||||
async function setList() {
|
||||
await dashboardStore.setDashboards();
|
||||
searchDashboards();
|
||||
searchDashboards(1);
|
||||
}
|
||||
async function importTemplates(event: any) {
|
||||
const arr: any = await readFile(event);
|
||||
@@ -232,12 +235,20 @@ function exportTemplates() {
|
||||
}, 2000);
|
||||
}
|
||||
function optimizeTemplate(
|
||||
children: (LayoutConfig & { moved?: boolean; standard?: unknown })[]
|
||||
children: (LayoutConfig & {
|
||||
moved?: boolean;
|
||||
standard?: unknown;
|
||||
label?: string;
|
||||
value?: string;
|
||||
})[]
|
||||
) {
|
||||
for (const child of children || []) {
|
||||
delete child.moved;
|
||||
delete child.activedTabIndex;
|
||||
delete child.standard;
|
||||
delete child.id;
|
||||
delete child.label;
|
||||
delete child.value;
|
||||
if (isEmptyObject(child.graph)) {
|
||||
delete child.graph;
|
||||
}
|
||||
@@ -370,7 +381,7 @@ async function setRoot(row: DashboardItem) {
|
||||
items.push(d);
|
||||
}
|
||||
dashboardStore.resetDashboards(items);
|
||||
searchDashboards();
|
||||
searchDashboards(1);
|
||||
loading.value = false;
|
||||
}
|
||||
function handleRename(row: DashboardItem) {
|
||||
@@ -402,6 +413,7 @@ async function updateName(d: DashboardItem, value: string) {
|
||||
name: value,
|
||||
};
|
||||
delete c.id;
|
||||
delete c.filters;
|
||||
const setting = {
|
||||
id: d.id,
|
||||
configuration: JSON.stringify(c),
|
||||
@@ -453,10 +465,13 @@ function searchDashboards(pageIndex?: any) {
|
||||
const arr = list.filter((d: { name: string }) =>
|
||||
d.name.includes(searchText.value)
|
||||
);
|
||||
dashboards.value = arr.splice(
|
||||
(pageIndex - 1 || 0) * pageSize,
|
||||
pageSize * (pageIndex || 1)
|
||||
|
||||
total.value = arr.length;
|
||||
dashboards.value = arr.filter(
|
||||
(d: { name: string }, index: number) =>
|
||||
index < pageIndex * pageSize && index >= (pageIndex - 1) * pageSize
|
||||
);
|
||||
currentPage.value = pageIndex;
|
||||
}
|
||||
|
||||
async function reloadTemplates() {
|
||||
@@ -465,6 +480,7 @@ async function reloadTemplates() {
|
||||
loading.value = false;
|
||||
}
|
||||
function changePage(pageIndex: number) {
|
||||
currentPage.value = pageIndex;
|
||||
searchDashboards(pageIndex);
|
||||
}
|
||||
</script>
|
||||
|
82
src/views/dashboard/configuration/Event.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div>
|
||||
<span class="label">{{ t("enableAssociate") }}</span>
|
||||
<el-switch
|
||||
v-model="eventAssociate"
|
||||
active-text="Yes"
|
||||
inactive-text="No"
|
||||
@change="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<el-button size="small" @click="cancelConfig">
|
||||
{{ t("cancel") }}
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" @click="applyConfig">
|
||||
{{ t("apply") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref } from "vue";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const originConfig = dashboardStore.selectedGrid;
|
||||
const eventAssociate = ref(dashboardStore.selectedGrid.eventAssociate || false);
|
||||
|
||||
function updateConfig() {
|
||||
dashboardStore.selectedGrid = {
|
||||
...dashboardStore.selectedGrid,
|
||||
eventAssociate,
|
||||
};
|
||||
dashboardStore.selectWidget(dashboardStore.selectedGrid);
|
||||
}
|
||||
|
||||
function applyConfig() {
|
||||
dashboardStore.setConfigPanel(false);
|
||||
dashboardStore.setConfigs(dashboardStore.selectedGrid);
|
||||
}
|
||||
|
||||
function cancelConfig() {
|
||||
dashboardStore.selectWidget(originConfig);
|
||||
dashboardStore.setConfigPanel(false);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-top: 1px solid #eee;
|
||||
padding: 10px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
161
src/views/dashboard/configuration/TimeRange.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("text") }}</span>
|
||||
<el-input
|
||||
class="input"
|
||||
v-model="text"
|
||||
size="small"
|
||||
@change="changeConfig({ text })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("textAlign") }}</span>
|
||||
<Selector
|
||||
:value="textAlign"
|
||||
:options="AlignStyle"
|
||||
size="small"
|
||||
placeholder="Select a color"
|
||||
class="input"
|
||||
@change="changeConfig({ textAlign: $event[0].value })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("backgroundColors") }}</span>
|
||||
<Selector
|
||||
:value="backgroundColor"
|
||||
:options="Colors"
|
||||
size="small"
|
||||
placeholder="Select a color"
|
||||
class="input"
|
||||
@change="changeConfig({ backgroundColor: $event[0].value })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("fontSize") }}</span>
|
||||
<el-slider
|
||||
class="slider"
|
||||
v-model="fontSize"
|
||||
show-input
|
||||
input-size="small"
|
||||
:min="12"
|
||||
:max="30"
|
||||
:step="1"
|
||||
@change="changeConfig({ fontSize })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("fontColors") }}</span>
|
||||
<Selector
|
||||
:value="fontColor"
|
||||
:options="Colors"
|
||||
size="small"
|
||||
placeholder="Select a color"
|
||||
class="input"
|
||||
@change="changeConfig({ fontColor: $event[0].value })"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<el-button size="small" @click="cancelConfig">
|
||||
{{ t("cancel") }}
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" @click="applyConfig">
|
||||
{{ t("apply") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref } from "vue";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const originConfig = dashboardStore.selectedGrid;
|
||||
const graph = originConfig.graph || {};
|
||||
const backgroundColor = ref(graph.backgroundColor || "green");
|
||||
const fontColor = ref(graph.fontColor || "white");
|
||||
const fontSize = ref<number>(graph.fontSize || 12);
|
||||
const textAlign = ref(graph.textAlign || "left");
|
||||
const text = ref<string>(graph.text || "");
|
||||
const Colors = [
|
||||
{
|
||||
label: "Green",
|
||||
value: "green",
|
||||
},
|
||||
{ label: "Blue", value: "blue" },
|
||||
{ label: "Red", value: "red" },
|
||||
{ label: "Grey", value: "grey" },
|
||||
{ label: "White", value: "white" },
|
||||
{ label: "Black", value: "black" },
|
||||
{ label: "Orange", value: "orange" },
|
||||
];
|
||||
const AlignStyle = [
|
||||
{
|
||||
label: "Left",
|
||||
value: "left",
|
||||
},
|
||||
{ label: "Center", value: "center" },
|
||||
{ label: "Right", value: "right" },
|
||||
];
|
||||
function changeConfig(param: { [key: string]: unknown }) {
|
||||
const { selectedGrid } = dashboardStore;
|
||||
const graph = {
|
||||
...selectedGrid.graph,
|
||||
...param,
|
||||
};
|
||||
dashboardStore.selectWidget({ ...selectedGrid, graph });
|
||||
}
|
||||
function applyConfig() {
|
||||
dashboardStore.setConfigPanel(false);
|
||||
dashboardStore.setConfigs(dashboardStore.selectedGrid);
|
||||
}
|
||||
|
||||
function cancelConfig() {
|
||||
dashboardStore.selectWidget(originConfig);
|
||||
dashboardStore.setConfigPanel(false);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.slider {
|
||||
width: 500px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-top: 1px solid #eee;
|
||||
padding: 10px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
@@ -58,6 +58,13 @@ limitations under the License. -->
|
||||
<el-collapse-item :title="t('widgetOptions')" name="3">
|
||||
<WidgetOptions />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
:title="t('associateOptions')"
|
||||
name="4"
|
||||
v-if="hasAssociate"
|
||||
>
|
||||
<AssociateOptions />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
<div class="footer">
|
||||
@@ -77,17 +84,13 @@ import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { Option } from "@/types/app";
|
||||
import graphs from "../graphs";
|
||||
import configs from "./widget/graph-styles";
|
||||
import WidgetOptions from "./widget/WidgetOptions.vue";
|
||||
import MetricOptions from "./widget/metric/Index.vue";
|
||||
import CustomOptions from "./widget/index";
|
||||
|
||||
export default defineComponent({
|
||||
name: "WidgetEdit",
|
||||
components: {
|
||||
...graphs,
|
||||
...configs,
|
||||
WidgetOptions,
|
||||
MetricOptions,
|
||||
...CustomOptions,
|
||||
},
|
||||
setup() {
|
||||
const configHeight = document.documentElement.clientHeight - 520;
|
||||
@@ -111,6 +114,12 @@ export default defineComponent({
|
||||
const graph = computed(() => dashboardStore.selectedGrid.graph || {});
|
||||
const title = computed(() => encodeURIComponent(widget.value.title || ""));
|
||||
const tips = computed(() => encodeURIComponent(widget.value.tips || ""));
|
||||
const hasAssociate = computed(() =>
|
||||
["Bar", "Line", "Area"].includes(
|
||||
dashboardStore.selectedGrid.graph &&
|
||||
dashboardStore.selectedGrid.graph.type
|
||||
)
|
||||
);
|
||||
|
||||
function getSource(source: unknown) {
|
||||
states.source = source;
|
||||
@@ -145,6 +154,7 @@ export default defineComponent({
|
||||
graph,
|
||||
title,
|
||||
tips,
|
||||
hasAssociate,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -18,9 +18,13 @@
|
||||
import Text from "./Text.vue";
|
||||
import Widget from "./Widget.vue";
|
||||
import Topology from "./Topology.vue";
|
||||
import Event from "./Event.vue";
|
||||
import TimeRange from "./TimeRange.vue";
|
||||
|
||||
export default {
|
||||
Text,
|
||||
Widget,
|
||||
Topology,
|
||||
Event,
|
||||
TimeRange,
|
||||
};
|
||||
|
112
src/views/dashboard/configuration/widget/AssociateOptions.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("widget") }}</span>
|
||||
<Selector
|
||||
:multiple="true"
|
||||
:value="widgetIds"
|
||||
:options="widgets"
|
||||
size="small"
|
||||
placeholder="Select a widget"
|
||||
class="selectors"
|
||||
@change="updateWidgetConfig"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { Option } from "@/types/app";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const associate = dashboardStore.selectedGrid.associate || [];
|
||||
const widgetIds = ref<string[]>(
|
||||
associate.map((d: { widgetId: string }) => d.widgetId)
|
||||
);
|
||||
const widgets: any = computed(() => {
|
||||
const widgetList = getDashboard(dashboardStore.currentDashboard).widgets;
|
||||
const items = [];
|
||||
for (const d of widgetList) {
|
||||
const isLinear = ["Bar", "Line", "Area"].includes(
|
||||
(d.graph && d.graph.type) || ""
|
||||
);
|
||||
if (isLinear && d.id && dashboardStore.selectedGrid.id !== d.id) {
|
||||
items.push({ value: d.id, label: (d.widget && d.widget.name) || d.id });
|
||||
}
|
||||
}
|
||||
return items;
|
||||
});
|
||||
function updateWidgetConfig(options: Option[]) {
|
||||
const arr: any = getDashboard(dashboardStore.currentDashboard).widgets;
|
||||
const opt = options.map((d: Option) => {
|
||||
return { widgetId: d.value };
|
||||
});
|
||||
const newVal = options.map((d: Option) => d.value);
|
||||
// add association options in the source widget
|
||||
const widget = {
|
||||
...dashboardStore.selectedGrid,
|
||||
associate: opt,
|
||||
};
|
||||
dashboardStore.selectWidget({ ...widget });
|
||||
|
||||
// remove unuse association widget option
|
||||
for (const id of widgetIds.value) {
|
||||
if (!newVal.includes(id)) {
|
||||
const w = arr.find((d: { id: string }) => d.id === id);
|
||||
const config = {
|
||||
...w,
|
||||
associate: [],
|
||||
};
|
||||
dashboardStore.setWidget(config);
|
||||
}
|
||||
}
|
||||
// add association options in target widgets
|
||||
for (let i = 0; i < opt.length; i++) {
|
||||
const item = JSON.parse(JSON.stringify(opt));
|
||||
item[i] = { widgetId: dashboardStore.selectedGrid.id };
|
||||
const w = arr.find((d: { id: string }) => d.id === opt[i].widgetId);
|
||||
const config = {
|
||||
...w,
|
||||
associate: item,
|
||||
};
|
||||
dashboardStore.setWidget(config);
|
||||
}
|
||||
widgetIds.value = newVal;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.selectors {
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
@@ -13,6 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("name") }}</span>
|
||||
<el-input
|
||||
class="input"
|
||||
v-model="name"
|
||||
size="small"
|
||||
placeholder="Please input name"
|
||||
@change="updateWidgetName({ name: encodeURIComponent(name) })"
|
||||
/>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">{{ t("title") }}</span>
|
||||
<el-input
|
||||
@@ -37,13 +47,17 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const widget = dashboardStore.selectedGrid.widget || {};
|
||||
const title = ref<string>(widget.title || "");
|
||||
const tips = ref<string>(widget.tips || "");
|
||||
const name = ref<string>(widget.name || "");
|
||||
|
||||
function updateWidgetConfig(param: { [key: string]: string }) {
|
||||
const key = Object.keys(param)[0];
|
||||
@@ -57,6 +71,24 @@ function updateWidgetConfig(param: { [key: string]: string }) {
|
||||
};
|
||||
dashboardStore.selectWidget({ ...selectedGrid, widget });
|
||||
}
|
||||
function updateWidgetName(param: { [key: string]: string }) {
|
||||
const key = Object.keys(param)[0];
|
||||
const n = decodeURIComponent(param[key]);
|
||||
const pattern = /^[A-Za-z0-9-_\u4e00-\u9fa5]{1,300}$/;
|
||||
if (!pattern.test(n)) {
|
||||
ElMessage.error(t("nameTip"));
|
||||
return;
|
||||
}
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const item = widgets.find(
|
||||
(d: LayoutConfig) => d.widget && d.widget.name === n
|
||||
);
|
||||
if (item) {
|
||||
ElMessage.error(t("duplicateName"));
|
||||
return;
|
||||
}
|
||||
updateWidgetConfig(param);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.label {
|
||||
|
28
src/views/dashboard/configuration/widget/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 StyleOptions from "./graph-styles";
|
||||
import WidgetOptions from "./WidgetOptions.vue";
|
||||
import MetricOptions from "./metric/Index.vue";
|
||||
import AssociateOptions from "./AssociateOptions.vue";
|
||||
|
||||
export default {
|
||||
...StyleOptions,
|
||||
WidgetOptions,
|
||||
MetricOptions,
|
||||
AssociateOptions,
|
||||
};
|
@@ -179,12 +179,16 @@ const setVisTypes = computed(() => {
|
||||
|
||||
async function setMetricType(chart?: any) {
|
||||
const g = chart || dashboardStore.selectedGrid.graph || {};
|
||||
let arr: any[] = states.metricList;
|
||||
if (!chart) {
|
||||
const json = await dashboardStore.fetchMetricList();
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
states.metricList = (json.data.metrics || []).filter(
|
||||
arr = json.data.metrics;
|
||||
}
|
||||
states.metricList = (arr || []).filter(
|
||||
(d: { catalog: string; type: string }) => {
|
||||
if (states.isList) {
|
||||
if (d.type === MetricsType.REGULAR_VALUE) {
|
||||
|
@@ -22,7 +22,9 @@ limitations under the License. -->
|
||||
size="small"
|
||||
placeholder="Please input unit"
|
||||
@change="
|
||||
updateConfig(index, { unit: encodeURIComponent(currentMetric.unit) })
|
||||
updateConfig(index, {
|
||||
unit: encodeURIComponent(currentMetric.unit || ''),
|
||||
})
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@@ -35,7 +37,7 @@ limitations under the License. -->
|
||||
placeholder="Please input a name"
|
||||
@change="
|
||||
updateConfig(index, {
|
||||
label: encodeURIComponent(currentMetric.label),
|
||||
label: encodeURIComponent(currentMetric.label || ''),
|
||||
})
|
||||
"
|
||||
/>
|
||||
|
@@ -29,7 +29,7 @@ limitations under the License. -->
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<Header />
|
||||
<Header :needQuery="needQuery" />
|
||||
<Content />
|
||||
</div>
|
||||
</template>
|
||||
@@ -47,6 +47,7 @@ const props = defineProps({
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
109
src/views/dashboard/controls/Event.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="event-wrapper flex-v">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:width="100"
|
||||
v-if="dashboardStore.editMode"
|
||||
>
|
||||
<template #reference>
|
||||
<span class="delete cp">
|
||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="tools" @click="editConfig">
|
||||
<span>{{ t("edit") }}</span>
|
||||
</div>
|
||||
<div class="tools" @click="removeWidget">
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="header">
|
||||
<Header :needQuery="needQuery" />
|
||||
</div>
|
||||
<div class="event">
|
||||
<Content :data="data" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import Header from "../related/event/Header.vue";
|
||||
import Content from "../related/event/Content.vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
||||
function removeWidget() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
function editConfig() {
|
||||
dashboardStore.setConfigPanel(true);
|
||||
dashboardStore.selectWidget(props.data);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.event-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.delete {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 3px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
min-width: 1024px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 5px 0;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.event {
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
}
|
||||
</style>
|
@@ -30,7 +30,7 @@ limitations under the License. -->
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="header">
|
||||
<Header />
|
||||
<Header :needQuery="needQuery" :data="data" />
|
||||
</div>
|
||||
<div class="log">
|
||||
<List />
|
||||
@@ -38,19 +38,24 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { provide } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import Header from "../related/log/Header.vue";
|
||||
import List from "../related/log/List.vue";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
type: Object as PropType<LayoutConfig>,
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
provide("options", props.data);
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
||||
@@ -71,6 +76,7 @@ function removeWidget() {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 3px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
97
src/views/dashboard/controls/NetworkProfiling.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="profile-wrapper flex-v">
|
||||
<div class="title">Network Profiling</div>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:width="100"
|
||||
v-if="dashboardStore.editMode"
|
||||
>
|
||||
<template #reference>
|
||||
<span class="operation cp">
|
||||
<Icon iconName="ellipsis_v" size="middle" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="tools" @click="removeWidget">
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<Content :config="props.data" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import Content from "../related/network-profiling/Content.vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
||||
function removeWidget() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.profile-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.operation {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 5px 0;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
padding: 0 10px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
</style>
|
@@ -29,7 +29,7 @@ limitations under the License. -->
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<Header />
|
||||
<Header :needQuery="needQuery" />
|
||||
<Content />
|
||||
</div>
|
||||
</template>
|
||||
@@ -47,6 +47,7 @@ const props = defineProps({
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
@@ -37,10 +37,17 @@ limitations under the License. -->
|
||||
v-if="dashboardStore.editMode && canEditTabName"
|
||||
/>
|
||||
</span>
|
||||
<span class="tab-icons">
|
||||
<el-tooltip content="Copy Link" placement="bottom">
|
||||
<i @click="copyLink">
|
||||
<Icon size="middle" iconName="review-list" class="tab-icon" />
|
||||
</i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span class="tab-icons" v-if="dashboardStore.editMode">
|
||||
<el-tooltip content="Add tab items" placement="bottom">
|
||||
<i @click="addTabItem">
|
||||
<Icon size="middle" iconName="add" />
|
||||
<Icon size="middle" iconName="add_fill" class="tab-icon" />
|
||||
</i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
@@ -80,7 +87,7 @@ limitations under the License. -->
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
:key="item.id"
|
||||
@click="clickTabGrid($event, item)"
|
||||
:class="{ active: activeTabWidget === item.i }"
|
||||
:drag-ignore-from="dragIgnoreFrom"
|
||||
@@ -99,18 +106,13 @@ limitations under the License. -->
|
||||
<script lang="ts">
|
||||
import { ref, watch, defineComponent, toRefs } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import type { PropType } from "vue";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import Topology from "./Topology.vue";
|
||||
import Widget from "./Widget.vue";
|
||||
import Trace from "./Trace.vue";
|
||||
import Profile from "./Profile.vue";
|
||||
import Log from "./Log.vue";
|
||||
import Text from "./Text.vue";
|
||||
import Ebpf from "./Ebpf.vue";
|
||||
import controls from "./tab";
|
||||
import { dragIgnoreFrom } from "../data";
|
||||
import DemandLog from "./DemandLog.vue";
|
||||
import copy from "@/utils/copy";
|
||||
|
||||
const props = {
|
||||
data: {
|
||||
@@ -121,16 +123,23 @@ const props = {
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "Tab",
|
||||
components: { Topology, Widget, Trace, Profile, Log, Text, Ebpf, DemandLog },
|
||||
components: {
|
||||
...controls,
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const activeTabIndex = ref<number>(0);
|
||||
const route = useRoute();
|
||||
const activeTabIndex = ref<number>(
|
||||
Number(route.params.activeTabIndex) || 0
|
||||
);
|
||||
const activeTabWidget = ref<string>("");
|
||||
const editTabIndex = ref<number>(NaN); // edit tab item name
|
||||
const canEditTabName = ref<boolean>(false);
|
||||
const needQuery = ref<boolean>(false);
|
||||
|
||||
dashboardStore.setActiveTabIndex(activeTabIndex);
|
||||
const l = dashboardStore.layout.findIndex(
|
||||
(d: LayoutConfig) => d.i === props.data.i
|
||||
);
|
||||
@@ -154,6 +163,11 @@ export default defineComponent({
|
||||
dashboardStore.layout[l].children[activeTabIndex.value].children
|
||||
);
|
||||
needQuery.value = true;
|
||||
if (route.params.activeTabIndex) {
|
||||
let p = location.href.split("/tab/")[0];
|
||||
p = p + "/tab/" + activeTabIndex.value;
|
||||
history.replaceState({}, "", p);
|
||||
}
|
||||
}
|
||||
function removeTab(e: Event) {
|
||||
e.stopPropagation();
|
||||
@@ -180,6 +194,7 @@ export default defineComponent({
|
||||
editTabIndex.value = index;
|
||||
}
|
||||
function handleClick(el: any) {
|
||||
needQuery.value = true;
|
||||
if (["tab-name", "edit-tab"].includes(el.target.className)) {
|
||||
return;
|
||||
}
|
||||
@@ -202,6 +217,16 @@ export default defineComponent({
|
||||
dashboardStore.layout[l].children[activeTabIndex.value].children
|
||||
);
|
||||
}
|
||||
function copyLink() {
|
||||
let path = "";
|
||||
if (route.params.activeTabIndex === undefined) {
|
||||
path = location.href + "/tab/" + activeTabIndex.value;
|
||||
} else {
|
||||
const p = location.href.split("/tab/")[0];
|
||||
path = p + "/tab/" + activeTabIndex.value;
|
||||
}
|
||||
copy(path);
|
||||
}
|
||||
document.body.addEventListener("click", handleClick, false);
|
||||
watch(
|
||||
() => dashboardStore.activedGridItem,
|
||||
@@ -218,6 +243,26 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => dashboardStore.currentTabIndex,
|
||||
() => {
|
||||
activeTabIndex.value = dashboardStore.currentTabIndex;
|
||||
dashboardStore.activeGridItem(props.data.i);
|
||||
dashboardStore.selectWidget(props.data);
|
||||
const l = dashboardStore.layout.findIndex(
|
||||
(d: LayoutConfig) => d.i === props.data.i
|
||||
);
|
||||
dashboardStore.setCurrentTabItems(
|
||||
dashboardStore.layout[l].children[activeTabIndex.value].children
|
||||
);
|
||||
needQuery.value = true;
|
||||
if (route.params.activeTabIndex) {
|
||||
let p = location.href.split("/tab/")[0];
|
||||
p = p + "/tab/" + activeTabIndex.value;
|
||||
history.replaceState({}, "", p);
|
||||
}
|
||||
}
|
||||
);
|
||||
return {
|
||||
handleClick,
|
||||
layoutUpdatedEvent,
|
||||
@@ -227,6 +272,7 @@ export default defineComponent({
|
||||
deleteTabItem,
|
||||
removeTab,
|
||||
clickTabs,
|
||||
copyLink,
|
||||
...toRefs(props),
|
||||
activeTabWidget,
|
||||
dashboardStore,
|
||||
@@ -330,6 +376,10 @@ export default defineComponent({
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.vue-grid-item.active {
|
||||
border: 1px solid #409eff;
|
||||
}
|
||||
|
187
src/views/dashboard/controls/TimeRange.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="time-range">
|
||||
<div class="header">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:width="100"
|
||||
v-if="dashboardStore.editMode"
|
||||
>
|
||||
<template #reference>
|
||||
<span>
|
||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="tools" @click="editConfig">
|
||||
<span>{{ t("edit") }}</span>
|
||||
</div>
|
||||
<div class="tools" @click="removeTopo">
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
:style="{
|
||||
backgroundColor: TextColors[graph.backgroundColor],
|
||||
justifyContent: graph.textAlign,
|
||||
color: TextColors[graph.fontColor],
|
||||
}"
|
||||
>
|
||||
<span
|
||||
class="mr-5"
|
||||
:style="{
|
||||
fontSize: graph.fontSize + 'px',
|
||||
}"
|
||||
>{{ graph.text }}
|
||||
</span>
|
||||
<Icon iconName="time_range" size="middle" />
|
||||
<span
|
||||
class="ml-5"
|
||||
:style="{
|
||||
fontSize: graph.fontSize + 'px',
|
||||
}"
|
||||
>
|
||||
{{ content }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { TextColors } from "@/views/dashboard/data";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const graph = computed(() => props.data.graph || {});
|
||||
const dashboardStore = useDashboardStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const content = computed(() => {
|
||||
const text = [appStore.durationRow.start, appStore.durationRow.end]
|
||||
.map((date: Date) => tf(date, "YYYY-MM-DD HH:mm"))
|
||||
.join(` ~ `);
|
||||
return text;
|
||||
});
|
||||
|
||||
function removeTopo() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
function editConfig() {
|
||||
dashboardStore.setConfigPanel(true);
|
||||
dashboardStore.selectWidget(props.data);
|
||||
}
|
||||
function tf(time: Date, format: any): string {
|
||||
const local = {
|
||||
dow: 1, // Monday is the first day of the week
|
||||
hourTip: t("hourTip"), // tip of select hour
|
||||
minuteTip: t("minuteTip"), // tip of select minute
|
||||
secondTip: t("secondTip"), // tip of select second
|
||||
yearSuffix: t("yearSuffix"), // format of head
|
||||
monthsHead: t("monthsHead").split("_"), // months of head
|
||||
months: t("months").split("_"), // months of panel
|
||||
weeks: t("weeks").split("_"), // weeks
|
||||
cancelTip: t("cancel"), // default text for cancel button
|
||||
submitTip: t("confirm"), // default text for submit button
|
||||
quarterHourCutTip: t("quarterHourCutTip"),
|
||||
halfHourCutTip: t("halfHourCutTip"),
|
||||
hourCutTip: t("hourCutTip"),
|
||||
dayCutTip: t("dayCutTip"),
|
||||
weekCutTip: t("weekCutTip"),
|
||||
monthCutTip: t("monthCutTip"),
|
||||
};
|
||||
const year = time.getFullYear();
|
||||
const month = time.getMonth();
|
||||
const day = time.getDate();
|
||||
const hours24 = time.getHours();
|
||||
const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
const minutes = time.getMinutes();
|
||||
const seconds = time.getSeconds();
|
||||
const milliseconds = time.getMilliseconds();
|
||||
const dd = (t: number) => `0${t}`.slice(-2);
|
||||
const map: { [key: string]: string | number } = {
|
||||
YYYY: year,
|
||||
MM: dd(month + 1),
|
||||
MMM: local.months[month],
|
||||
MMMM: local.monthsHead[month],
|
||||
M: month + 1,
|
||||
DD: dd(day),
|
||||
D: day,
|
||||
HH: dd(hours24),
|
||||
H: hours24,
|
||||
hh: dd(hours),
|
||||
h: hours,
|
||||
mm: dd(minutes),
|
||||
m: minutes,
|
||||
ss: dd(seconds),
|
||||
s: seconds,
|
||||
S: milliseconds,
|
||||
};
|
||||
return format.replace(/Y+|M+|D+|H+|h+|m+|s+|S+/g, (str: string) => map[str]);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.time-range {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.operation {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 0 20px 0 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 5px 0;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -30,7 +30,7 @@ limitations under the License. -->
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="header">
|
||||
<Filter />
|
||||
<Filter :needQuery="needQuery" :data="data" />
|
||||
</div>
|
||||
<div class="trace flex-h">
|
||||
<TraceList />
|
||||
@@ -39,6 +39,7 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { provide } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import Filter from "../related/trace/Filter.vue";
|
||||
import TraceList from "../related/trace/TraceList.vue";
|
||||
@@ -53,7 +54,9 @@ const props = defineProps({
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: true },
|
||||
});
|
||||
provide("options", props.data);
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
function removeWidget() {
|
||||
|
@@ -61,9 +61,12 @@ limitations under the License. -->
|
||||
metrics: data.metrics || [''],
|
||||
metricTypes: data.metricTypes || [''],
|
||||
i: data.i,
|
||||
id: data.id,
|
||||
metricConfig: data.metricConfig,
|
||||
filters: data.filters || {},
|
||||
}"
|
||||
:needQuery="needQuery"
|
||||
@click="clickHandle"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="no-data">{{ t("noGraph") }}</div>
|
||||
@@ -84,6 +87,8 @@ import {
|
||||
useGetMetricEntity,
|
||||
} from "@/hooks/useProcessor";
|
||||
import { EntityType, ListChartTypes } from "../data";
|
||||
import { EventParams } from "@/types/dashboard";
|
||||
import getDashboard from "@/hooks/useDashboardsSession";
|
||||
|
||||
const props = {
|
||||
data: {
|
||||
@@ -93,6 +98,7 @@ const props = {
|
||||
activeIndex: { type: String, default: "" },
|
||||
needQuery: { type: Boolean, default: false },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "Widget",
|
||||
components: { ...graphs },
|
||||
@@ -156,6 +162,24 @@ export default defineComponent({
|
||||
dashboardStore.activeGridItem(props.data.i);
|
||||
}
|
||||
}
|
||||
function clickHandle(params: EventParams | any) {
|
||||
const { widgets } = getDashboard(dashboardStore.currentDashboard);
|
||||
const associate = (props.data.associate && props.data.associate) || [];
|
||||
|
||||
for (const item of associate) {
|
||||
const widget = widgets.find(
|
||||
(d: LayoutConfig) => d.id === item.widgetId
|
||||
);
|
||||
if (widget) {
|
||||
widget.filters = {
|
||||
dataIndex: params.dataIndex,
|
||||
sourceId: props.data.id || "",
|
||||
};
|
||||
|
||||
dashboardStore.setWidget(widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => [props.data.metricTypes, props.data.metrics],
|
||||
() => {
|
||||
@@ -189,7 +213,10 @@ export default defineComponent({
|
||||
watch(
|
||||
() => [selectorStore.currentPod, selectorStore.currentDestPod],
|
||||
() => {
|
||||
if (dashboardStore.entity === EntityType[0].value) {
|
||||
if (
|
||||
dashboardStore.entity === EntityType[0].value ||
|
||||
dashboardStore.entity === EntityType[7].value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isList.value) {
|
||||
@@ -198,6 +225,14 @@ export default defineComponent({
|
||||
queryMetrics();
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => [selectorStore.currentProcess, selectorStore.currentDestProcess],
|
||||
() => {
|
||||
if (dashboardStore.entity === EntityType[7].value) {
|
||||
queryMetrics();
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => {
|
||||
@@ -221,6 +256,7 @@ export default defineComponent({
|
||||
t,
|
||||
graph,
|
||||
widget,
|
||||
clickHandle,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -23,6 +23,9 @@ import Log from "./Log.vue";
|
||||
import Text from "./Text.vue";
|
||||
import Ebpf from "./Ebpf.vue";
|
||||
import DemandLog from "./DemandLog.vue";
|
||||
import Event from "./Event.vue";
|
||||
import NetworkProfiling from "./NetworkProfiling.vue";
|
||||
import TimeRange from "./TimeRange.vue";
|
||||
|
||||
export default {
|
||||
Tab,
|
||||
@@ -34,4 +37,7 @@ export default {
|
||||
Text,
|
||||
Ebpf,
|
||||
DemandLog,
|
||||
Event,
|
||||
NetworkProfiling,
|
||||
TimeRange,
|
||||
};
|
||||
|
41
src/views/dashboard/controls/tab.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 Topology from "./Topology.vue";
|
||||
import Widget from "./Widget.vue";
|
||||
import Trace from "./Trace.vue";
|
||||
import Profile from "./Profile.vue";
|
||||
import Log from "./Log.vue";
|
||||
import Text from "./Text.vue";
|
||||
import Ebpf from "./Ebpf.vue";
|
||||
import DemandLog from "./DemandLog.vue";
|
||||
import Event from "./Event.vue";
|
||||
import NetworkProfiling from "./NetworkProfiling.vue";
|
||||
import TimeRange from "./TimeRange.vue";
|
||||
|
||||
export default {
|
||||
Widget,
|
||||
Trace,
|
||||
Topology,
|
||||
Profile,
|
||||
Log,
|
||||
Text,
|
||||
Ebpf,
|
||||
DemandLog,
|
||||
Event,
|
||||
NetworkProfiling,
|
||||
TimeRange,
|
||||
};
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export const dragIgnoreFrom =
|
||||
"svg.d3-trace-tree, .dragger, .micro-topo-chart, .schedules";
|
||||
"svg.d3-trace-tree, .dragger, .micro-topo-chart, .schedules, .vis-item, .vis-timeline, .process-svg";
|
||||
|
||||
export const PodsChartTypes = ["EndpointList", "InstanceList"];
|
||||
|
||||
@@ -159,6 +159,7 @@ export const EntityType = [
|
||||
key: 4,
|
||||
},
|
||||
{ value: "EndpointRelation", label: "Endpoint Relation", key: 4 },
|
||||
{ value: "ProcessRelation", label: "Process Relation", key: 5 },
|
||||
];
|
||||
export const ListEntity: any = {
|
||||
InstanceList: EntityType[3].value,
|
||||
@@ -178,15 +179,16 @@ export const AllTools = [
|
||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||
];
|
||||
export const ServiceTools = [
|
||||
{ name: "playlist_add", content: "Widget", id: "addWidget" },
|
||||
{ name: "all_inbox", content: "Tab", id: "addTab" },
|
||||
{ name: "library_books", content: "Text", id: "addText" },
|
||||
{ name: "device_hub", content: "Topology", id: "addTopology" },
|
||||
{ name: "merge", content: "Trace", id: "addTrace" },
|
||||
{ name: "timeline", content: "Trace Profiling", id: "addProfile" },
|
||||
{ name: "insert_chart", content: "eBPF Profiling", id: "addEbpf" },
|
||||
{ name: "assignment", content: "Log", id: "addLog" },
|
||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||
{ name: "timeline", content: "Add Trace Profiling", id: "addProfile" },
|
||||
{ name: "insert_chart", content: "Add eBPF Profiling", id: "addEbpf" },
|
||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||
{ name: "demand", content: "Add On Demand Log", id: "addDemandLog" },
|
||||
{ name: "event", content: "Add Event", id: "addEvent" },
|
||||
];
|
||||
export const InstanceTools = [
|
||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||
@@ -195,6 +197,12 @@ export const InstanceTools = [
|
||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||
{ name: "demand", content: "Add On Demand Log", id: "addDemandLog" },
|
||||
{ name: "event", content: "Add Event", id: "addEvent" },
|
||||
{
|
||||
name: "timeline",
|
||||
content: "Add Network Profiling",
|
||||
id: "addNetworkProfiling",
|
||||
},
|
||||
];
|
||||
export const EndpointTools = [
|
||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||
@@ -203,6 +211,13 @@ export const EndpointTools = [
|
||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||
{ name: "event", content: "Add Event", id: "addEvent" },
|
||||
];
|
||||
export const ProcessTools = [
|
||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||
{ name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
|
||||
];
|
||||
export const ServiceRelationTools = [
|
||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||
@@ -281,4 +296,6 @@ export const CalculationOpts = [
|
||||
{ label: "Seconds to YYYY-MM-DD HH:mm:ss", value: "convertSeconds" },
|
||||
{ label: "Precision is 2", value: "precision" },
|
||||
{ label: "Milliseconds to seconds", value: "msTos" },
|
||||
{ label: "Seconds to days", value: "secondToDay" },
|
||||
{ label: "Nanoseconds to milliseconds", value: "nanosecondToMillisecond" },
|
||||
];
|
||||
|
@@ -14,14 +14,20 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<Line :data="data" :intervalTime="intervalTime" :config="config" />
|
||||
<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 } from "@/types/dashboard";
|
||||
import { AreaConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@@ -29,8 +35,23 @@ defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<AreaConfig>,
|
||||
type: Object as PropType<
|
||||
AreaConfig & {
|
||||
filters: {
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@@ -13,15 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { BarConfig } from "@/types/dashboard";
|
||||
import { BarConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@@ -29,9 +29,20 @@ const props = defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<BarConfig>,
|
||||
type: Object as PropType<
|
||||
BarConfig & {
|
||||
filters: {
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
@@ -41,30 +52,7 @@ function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const startP = keys.length > 1 ? 50 : 15;
|
||||
const diff = 15;
|
||||
const markAreas = (props.itemEvents || []).map(
|
||||
(event: Event, index: number) => {
|
||||
return [
|
||||
{
|
||||
name: `${event.name}:${event.type}`,
|
||||
xAxis: event.startTime,
|
||||
y: startP + diff * index,
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
borderColor: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
color: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: event.message,
|
||||
xAxis: event.endTime,
|
||||
y: startP + diff * (index + 1),
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
const temp = keys.map((i: string, index: number) => {
|
||||
const temp = keys.map((i: string) => {
|
||||
if (!props.intervalTime) {
|
||||
return;
|
||||
}
|
||||
@@ -85,23 +73,6 @@ function getOption() {
|
||||
backgroundStyle: {
|
||||
color: "rgba(180, 180, 180, 0.1)",
|
||||
},
|
||||
markArea:
|
||||
index === 0
|
||||
? {
|
||||
silent: false,
|
||||
data: markAreas,
|
||||
label: {
|
||||
show: false,
|
||||
width: 60,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
position: "bottom",
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
let color: string[] = [];
|
||||
@@ -139,11 +110,10 @@ function getOption() {
|
||||
trigger: "axis",
|
||||
zlevel: 1000,
|
||||
z: 60,
|
||||
backgroundColor: "rgb(50,50,50)",
|
||||
confine: true,
|
||||
textStyle: {
|
||||
fontSize: 13,
|
||||
color: "#ccc",
|
||||
color: "#333",
|
||||
},
|
||||
enterable: true,
|
||||
extraCssText: "max-height: 300px; overflow: auto; border: none",
|
||||
@@ -186,4 +156,7 @@ function getOption() {
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@@ -46,6 +46,7 @@ limitations under the License. -->
|
||||
:intervalTime="intervalTime"
|
||||
:colMetrics="colMetrics"
|
||||
:config="config"
|
||||
v-if="colMetrics.length"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
@@ -96,7 +97,9 @@ const dashboardStore = useDashboardStore();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const endpoints = ref<Endpoint[]>([]);
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = computed(() => props.config.metrics.map((d: string) => d));
|
||||
const colMetrics = computed(() =>
|
||||
(props.config.metrics || []).filter((d: string) => d)
|
||||
);
|
||||
|
||||
if (props.needQuery) {
|
||||
queryEndpoints();
|
||||
@@ -119,7 +122,7 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
||||
if (!currentPods.length) {
|
||||
return;
|
||||
}
|
||||
const metrics = (props.config.metrics || []).filter((d: string) => d);
|
||||
const metrics = props.config.metrics || [];
|
||||
const metricTypes = props.config.metricTypes || [];
|
||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
||||
const params = await useQueryPodsMetrics(
|
||||
@@ -144,17 +147,17 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
||||
endpoints.value = currentPods;
|
||||
}
|
||||
function clickEndpoint(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[2].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${scope.row.id}/${d.name}`
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${selectorStore.currentService.id}/${scope.row.id}/${dashboard.name}`
|
||||
);
|
||||
}
|
||||
async function searchList() {
|
||||
|
@@ -43,6 +43,7 @@ limitations under the License. -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
<ColumnGraph
|
||||
v-if="colMetrics.length"
|
||||
:intervalTime="intervalTime"
|
||||
:colMetrics="colMetrics"
|
||||
:config="config"
|
||||
@@ -126,7 +127,9 @@ const chartLoading = ref<boolean>(false);
|
||||
const instances = ref<Instance[]>([]); // current instances
|
||||
const pageSize = 10;
|
||||
const searchText = ref<string>("");
|
||||
const colMetrics = computed(() => props.config.metrics.map((d: string) => d));
|
||||
const colMetrics = computed(() =>
|
||||
(props.config.metrics || []).filter((d: string) => d)
|
||||
);
|
||||
if (props.needQuery) {
|
||||
queryInstance();
|
||||
}
|
||||
@@ -151,7 +154,8 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
||||
if (!currentInstances.length) {
|
||||
return;
|
||||
}
|
||||
const { metrics, metricTypes } = props.config;
|
||||
const metrics = props.config.metrics || [];
|
||||
const metricTypes = props.config.metricTypes || [];
|
||||
|
||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
||||
const params = await useQueryPodsMetrics(
|
||||
@@ -176,19 +180,19 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
||||
}
|
||||
|
||||
function clickInstance(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[3].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
router.push(
|
||||
`/dashboard/${d.layer}/${d.entity}/${selectorStore.currentService.id}/${
|
||||
scope.row.id
|
||||
}/${d.name.split(" ").join("-")}`
|
||||
`/dashboard/${dashboard.layer}/${dashboard.entity}/${
|
||||
selectorStore.currentService.id
|
||||
}/${scope.row.id}/${dashboard.name.split(" ").join("-")}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -13,15 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Event } from "@/types/events";
|
||||
import { LineConfig } from "@/types/dashboard";
|
||||
import { LineConfig, EventParams } from "@/types/dashboard";
|
||||
|
||||
/*global defineProps */
|
||||
/*global defineProps, defineEmits */
|
||||
const emits = defineEmits(["click"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<{ [key: string]: number[] }>,
|
||||
@@ -29,9 +29,20 @@ const props = defineProps({
|
||||
},
|
||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||
theme: { type: String, default: "light" },
|
||||
itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
|
||||
config: {
|
||||
type: Object as PropType<LineConfig>,
|
||||
type: Object as PropType<
|
||||
LineConfig & {
|
||||
filters: {
|
||||
sourceId: string;
|
||||
duration: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
isRange: boolean;
|
||||
dataIndex?: number;
|
||||
};
|
||||
} & { id: string }
|
||||
>,
|
||||
default: () => ({
|
||||
step: false,
|
||||
smooth: false,
|
||||
@@ -49,30 +60,7 @@ function getOption() {
|
||||
const keys = Object.keys(props.data || {}).filter(
|
||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||
);
|
||||
const startP = keys.length > 1 ? 50 : 15;
|
||||
const diff = 10;
|
||||
const markAreas = (props.itemEvents || []).map(
|
||||
(event: Event, index: number) => {
|
||||
return [
|
||||
{
|
||||
name: `${event.name}:${event.type}`,
|
||||
xAxis: event.startTime,
|
||||
y: startP + diff * index,
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
borderColor: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
color: event.type === "Normal" ? "#5dc859" : "#FF0087",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: event.message,
|
||||
xAxis: event.endTime,
|
||||
y: startP + diff * (index + 1),
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
const temp = keys.map((i: any, index: number) => {
|
||||
const temp = keys.map((i: any) => {
|
||||
const serie: any = {
|
||||
data: props.data[i].map((item: any, itemIndex: number) => [
|
||||
props.intervalTime[itemIndex],
|
||||
@@ -81,7 +69,7 @@ function getOption() {
|
||||
name: i,
|
||||
type: "line",
|
||||
symbol: "circle",
|
||||
symbolSize: 6,
|
||||
symbolSize: 8,
|
||||
showSymbol: props.config.showSymbol,
|
||||
step: props.config.step,
|
||||
smooth: props.config.smooth,
|
||||
@@ -89,23 +77,6 @@ function getOption() {
|
||||
width: 1.5,
|
||||
type: "solid",
|
||||
},
|
||||
markArea:
|
||||
index === 0
|
||||
? {
|
||||
silent: false,
|
||||
data: markAreas,
|
||||
label: {
|
||||
show: false,
|
||||
width: 60,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
position: "bottom",
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
if (props.config.type === "Area") {
|
||||
serie.areaStyle = {
|
||||
@@ -145,10 +116,9 @@ function getOption() {
|
||||
}
|
||||
const tooltip = {
|
||||
trigger: "axis",
|
||||
backgroundColor: "rgb(50,50,50)",
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: "#ccc",
|
||||
color: "#333",
|
||||
},
|
||||
enterable: true,
|
||||
confine: true,
|
||||
@@ -215,4 +185,8 @@ function getOption() {
|
||||
series: temp,
|
||||
};
|
||||
}
|
||||
|
||||
function clickEvent(params: EventParams) {
|
||||
emits("click", params);
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,66 +0,0 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<Graph :option="option" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array as PropType<{ name: string; value: number }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<{ sortOrder: string }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const option = computed(() => getOption());
|
||||
function getOption() {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
},
|
||||
legend: {
|
||||
type: "scroll",
|
||||
show: props.data.length === 1 ? false : true,
|
||||
icon: "circle",
|
||||
top: 0,
|
||||
left: 0,
|
||||
itemWidth: 12,
|
||||
textStyle: {
|
||||
color: "#333",
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "pie",
|
||||
radius: "50%",
|
||||
data: props.data,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
</script>
|
@@ -58,6 +58,7 @@ limitations under the License. -->
|
||||
:intervalTime="intervalTime"
|
||||
:colMetrics="colMetrics"
|
||||
:config="config"
|
||||
v-if="colMetrics.length"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
@@ -81,6 +82,7 @@ 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/useProcessor";
|
||||
import { EntityType } from "../data";
|
||||
@@ -110,6 +112,7 @@ const props = defineProps({
|
||||
});
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const chartLoading = ref<boolean>(false);
|
||||
const pageSize = 10;
|
||||
const services = ref<Service[]>([]);
|
||||
@@ -117,7 +120,7 @@ const searchText = ref<string>("");
|
||||
const groups = ref<any>({});
|
||||
const sortServices = ref<(Service & { merge: boolean })[]>([]);
|
||||
const colMetrics = computed(() =>
|
||||
props.config.metrics.filter((d: string) => d)
|
||||
(props.config.metrics || []).filter((d: string) => d)
|
||||
);
|
||||
queryServices();
|
||||
|
||||
@@ -178,16 +181,16 @@ function setServices(arr: (Service & { merge: boolean })[]) {
|
||||
}
|
||||
|
||||
function clickService(scope: any) {
|
||||
const d = getDashboard({
|
||||
const { dashboard } = getDashboard({
|
||||
name: props.config.dashboardName,
|
||||
layer: dashboardStore.layerId,
|
||||
entity: EntityType[0].value,
|
||||
});
|
||||
if (!d) {
|
||||
if (!dashboard) {
|
||||
ElMessage.error("No this dashboard");
|
||||
return;
|
||||
}
|
||||
const path = `/dashboard/${d.layer}/${d.entity}/${scope.row.id}/${d.name}`;
|
||||
const path = `/dashboard/${dashboard.layer}/${dashboard.entity}/${scope.row.id}/${dashboard.name}`;
|
||||
|
||||
router.push(path);
|
||||
}
|
||||
@@ -195,7 +198,8 @@ async function queryServiceMetrics(currentServices: Service[]) {
|
||||
if (!currentServices.length) {
|
||||
return;
|
||||
}
|
||||
const { metrics, metricTypes } = props.config;
|
||||
const metrics = props.config.metrics || [];
|
||||
const metricTypes = props.config.metricTypes || [];
|
||||
|
||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
||||
const params = await useQueryPodsMetrics(
|
||||
@@ -271,6 +275,14 @@ watch(
|
||||
queryServiceMetrics(services.value);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => {
|
||||
if (dashboardStore.entity === EntityType[1].value) {
|
||||
queryServices();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
|
@@ -21,7 +21,6 @@ import Bar from "./Bar.vue";
|
||||
import HeatMap from "./HeatMap.vue";
|
||||
import TopList from "./TopList.vue";
|
||||
import Table from "./Table.vue";
|
||||
import Pie from "./Pie.vue";
|
||||
import Card from "./Card.vue";
|
||||
import InstanceList from "./InstanceList.vue";
|
||||
import EndpointList from "./EndpointList.vue";
|
||||
@@ -34,7 +33,6 @@ export default {
|
||||
TopList,
|
||||
Area,
|
||||
Table,
|
||||
Pie,
|
||||
Card,
|
||||
EndpointList,
|
||||
InstanceList,
|
||||
|
32
src/views/dashboard/graphs/topology.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 Area from "./Area.vue";
|
||||
import Line from "./Line.vue";
|
||||
import Bar from "./Bar.vue";
|
||||
import TopList from "./TopList.vue";
|
||||
import Table from "./Table.vue";
|
||||
import Card from "./Card.vue";
|
||||
|
||||
export default {
|
||||
Line,
|
||||
Bar,
|
||||
TopList,
|
||||
Area,
|
||||
Table,
|
||||
Card,
|
||||
};
|
@@ -28,7 +28,7 @@ limitations under the License. -->
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
:key="item.id"
|
||||
@click="clickGrid(item, $event)"
|
||||
:class="{ active: dashboardStore.activedGridItem === item.i }"
|
||||
:drag-ignore-from="dragIgnoreFrom"
|
||||
|
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="dashboard-tool flex-h">
|
||||
<div :class="isRelation ? 'flex-v' : 'flex-h'">
|
||||
<div class="flex-h">
|
||||
<div class="selectors-item" v-if="key !== 10">
|
||||
<span class="label">$Service</span>
|
||||
@@ -26,7 +27,7 @@ limitations under the License. -->
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 3 || key === 4">
|
||||
<div class="selectors-item" v-if="key === 3 || key === 4 || key === 5">
|
||||
<span class="label">
|
||||
{{
|
||||
["EndpointRelation", "Endpoint"].includes(dashboardStore.entity)
|
||||
@@ -47,7 +48,20 @@ limitations under the License. -->
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 2 || key === 4">
|
||||
<div class="selectors-item" v-if="key === 5">
|
||||
<span class="label"> $Process </span>
|
||||
<Selector
|
||||
v-model="states.currentProcess"
|
||||
:options="selectorStore.processes"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changeProcess"
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h" :class="isRelation ? 'relation' : ''">
|
||||
<div class="selectors-item" v-if="key === 2 || key === 4 || key === 5">
|
||||
<span class="label">$DestinationService</span>
|
||||
<Selector
|
||||
v-model="states.currentDestService"
|
||||
@@ -58,7 +72,7 @@ limitations under the License. -->
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 4">
|
||||
<div class="selectors-item" v-if="key === 4 || key === 5">
|
||||
<span class="label">
|
||||
{{
|
||||
dashboardStore.entity === "EndpointRelation"
|
||||
@@ -77,9 +91,21 @@ limitations under the License. -->
|
||||
:isRemote="dashboardStore.entity === 'EndpointRelation'"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 5">
|
||||
<span class="label"> $DestinationProcess </span>
|
||||
<Selector
|
||||
v-model="states.currentDestProcess"
|
||||
:options="selectorStore.destProcesses"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changeDestProcess"
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile">
|
||||
<div class="tool-icons flex-h" v-if="dashboardStore.editMode">
|
||||
<div class="tool-icons" v-if="dashboardStore.editMode">
|
||||
<el-dropdown content="Controls" placement="bottom" :persistent="false">
|
||||
<i>
|
||||
<Icon class="icon-btn" size="sm" iconName="control" />
|
||||
@@ -133,6 +159,7 @@ import {
|
||||
EndpointRelationTools,
|
||||
InstanceRelationTools,
|
||||
ServiceRelationTools,
|
||||
ProcessTools,
|
||||
} from "../data";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { ElMessage } from "element-plus";
|
||||
@@ -144,9 +171,8 @@ const dashboardStore = useDashboardStore();
|
||||
const selectorStore = useSelectorStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const params = useRoute().params;
|
||||
const toolIcons = ref<{ name: string; content: string; id: string }[]>(
|
||||
EndpointRelationTools
|
||||
);
|
||||
const toolIcons =
|
||||
ref<{ name: string; content: string; id: string }[]>(AllTools);
|
||||
const loading = ref<boolean>(false);
|
||||
const states = reactive<{
|
||||
destService: string;
|
||||
@@ -154,16 +180,20 @@ const states = reactive<{
|
||||
key: number;
|
||||
currentService: string;
|
||||
currentPod: string;
|
||||
currentProcess: string;
|
||||
currentDestService: string;
|
||||
currentDestPod: string;
|
||||
currentDestProcess: string;
|
||||
}>({
|
||||
destService: "",
|
||||
destPod: "",
|
||||
key: 0,
|
||||
currentService: "",
|
||||
currentPod: "",
|
||||
currentProcess: "",
|
||||
currentDestService: "",
|
||||
currentDestPod: "",
|
||||
currentDestProcess: "",
|
||||
});
|
||||
const key = computed(() => {
|
||||
const type = EntityType.find(
|
||||
@@ -172,6 +202,14 @@ const key = computed(() => {
|
||||
return (type && type.key) || 0;
|
||||
});
|
||||
|
||||
const isRelation = computed(() => {
|
||||
return [
|
||||
EntityType[7].value,
|
||||
EntityType[6].value,
|
||||
EntityType[5].value,
|
||||
].includes(dashboardStore.entity);
|
||||
});
|
||||
|
||||
setCurrentDashboard();
|
||||
appStore.setEventStack([initSelector]);
|
||||
initSelector();
|
||||
@@ -199,6 +237,7 @@ async function setSelector() {
|
||||
EntityType[3].value,
|
||||
EntityType[5].value,
|
||||
EntityType[6].value,
|
||||
EntityType[7].value,
|
||||
].includes(String(params.entity))
|
||||
) {
|
||||
setSourceSelector();
|
||||
@@ -248,25 +287,7 @@ async function setSourceSelector() {
|
||||
await selectorStore.getService(String(params.serviceId));
|
||||
states.currentService = selectorStore.currentService.value;
|
||||
const e = String(params.entity).split("Relation")[0];
|
||||
await fetchPods(e, selectorStore.currentService.id, false);
|
||||
if (!(selectorStore.pods.length && selectorStore.pods[0])) {
|
||||
selectorStore.setCurrentPod(null);
|
||||
states.currentPod = "";
|
||||
return;
|
||||
}
|
||||
const pod = params.podId || selectorStore.pods[0].id;
|
||||
let currentPod;
|
||||
if (states.currentPod) {
|
||||
currentPod = selectorStore.pods.find(
|
||||
(d: { label: string }) => d.label === states.currentPod
|
||||
);
|
||||
} else {
|
||||
currentPod = selectorStore.pods.find((d: { id: string }) => d.id === pod);
|
||||
}
|
||||
if (currentPod) {
|
||||
selectorStore.setCurrentPod(currentPod);
|
||||
states.currentPod = currentPod.label;
|
||||
}
|
||||
await fetchPods(e, selectorStore.currentService.id, true);
|
||||
}
|
||||
|
||||
async function setDestSelector() {
|
||||
@@ -275,28 +296,8 @@ async function setDestSelector() {
|
||||
await fetchPods(
|
||||
String(params.entity),
|
||||
selectorStore.currentDestService.id,
|
||||
false
|
||||
true
|
||||
);
|
||||
if (!(selectorStore.destPods.length && selectorStore.destPods[0])) {
|
||||
selectorStore.setCurrentDestPod(null);
|
||||
states.currentDestPod = "";
|
||||
return;
|
||||
}
|
||||
const destPod = params.destPodId || selectorStore.destPods[0].id;
|
||||
let currentDestPod = { label: "" };
|
||||
if (states.currentDestPod) {
|
||||
currentDestPod = selectorStore.pods.find(
|
||||
(d: { label: string }) => d.label === states.currentDestPod
|
||||
);
|
||||
} else {
|
||||
currentDestPod = selectorStore.destPods.find(
|
||||
(d: { id: string }) => d.id === destPod
|
||||
);
|
||||
}
|
||||
if (currentDestPod) {
|
||||
selectorStore.setCurrentDestPod(currentDestPod);
|
||||
states.currentDestPod = currentDestPod.label;
|
||||
}
|
||||
}
|
||||
|
||||
async function getServices() {
|
||||
@@ -325,16 +326,21 @@ async function getServices() {
|
||||
);
|
||||
}
|
||||
selectorStore.setCurrentService(s || null);
|
||||
let d;
|
||||
let d,
|
||||
val = 1;
|
||||
if (key.value === 5) {
|
||||
val = 0;
|
||||
}
|
||||
if (states.currentService) {
|
||||
d = (selectorStore.services || []).find(
|
||||
(d: { label: string }) => d.label === states.currentDestService
|
||||
);
|
||||
} else {
|
||||
d = (selectorStore.services || []).find(
|
||||
(d: unknown, index: number) => index === 1
|
||||
(d: unknown, index: number) => index === val
|
||||
);
|
||||
}
|
||||
|
||||
selectorStore.setCurrentDestService(d || null);
|
||||
if (!selectorStore.currentService) {
|
||||
return;
|
||||
@@ -347,62 +353,86 @@ async function getServices() {
|
||||
EntityType[3].value,
|
||||
EntityType[5].value,
|
||||
EntityType[6].value,
|
||||
EntityType[7].value,
|
||||
].includes(dashboardStore.entity)
|
||||
) {
|
||||
fetchPods(e, selectorStore.currentService.id, true);
|
||||
await fetchPods(e, selectorStore.currentService.id, true);
|
||||
}
|
||||
if (!selectorStore.currentDestService) {
|
||||
return;
|
||||
}
|
||||
states.currentDestService = selectorStore.currentDestService.value;
|
||||
if (
|
||||
[EntityType[5].value, EntityType[6].value].includes(dashboardStore.entity)
|
||||
[EntityType[5].value, EntityType[6].value, EntityType[7].value].includes(
|
||||
dashboardStore.entity
|
||||
)
|
||||
) {
|
||||
fetchPods(dashboardStore.entity, selectorStore.currentDestService.id, true);
|
||||
await fetchPods(
|
||||
dashboardStore.entity,
|
||||
selectorStore.currentDestService.id,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeService(service: any) {
|
||||
async function changeService(service: Option[]) {
|
||||
if (service[0]) {
|
||||
states.currentService = service[0].value;
|
||||
selectorStore.setCurrentService(service[0]);
|
||||
const e = dashboardStore.entity.split("Relation")[0];
|
||||
selectorStore.setCurrentPod(null);
|
||||
states.currentPod = "";
|
||||
fetchPods(e, selectorStore.currentService.id, true);
|
||||
states.currentProcess = "";
|
||||
if (dashboardStore.entity === EntityType[7].value) {
|
||||
fetchPods("Process", selectorStore.currentService.id, true);
|
||||
} else {
|
||||
fetchPods(dashboardStore.entity, selectorStore.currentService.id, true);
|
||||
}
|
||||
} else {
|
||||
selectorStore.setCurrentService(null);
|
||||
}
|
||||
}
|
||||
|
||||
function changeDestService(service: any) {
|
||||
function changeDestService(service: Option[]) {
|
||||
if (service[0]) {
|
||||
states.currentDestService = service[0].value;
|
||||
selectorStore.setCurrentDestService(service[0]);
|
||||
selectorStore.setCurrentDestPod(null);
|
||||
states.currentDestPod = "";
|
||||
states.currentDestProcess = "";
|
||||
fetchPods(dashboardStore.entity, selectorStore.currentDestService.id, true);
|
||||
} else {
|
||||
selectorStore.setCurrentDestService(null);
|
||||
}
|
||||
}
|
||||
|
||||
function changePods(pod: any) {
|
||||
if (pod[0]) {
|
||||
selectorStore.setCurrentPod(pod[0]);
|
||||
} else {
|
||||
selectorStore.setCurrentPod("");
|
||||
async function changePods(pod: Option[]) {
|
||||
selectorStore.setCurrentPod(pod[0] || null);
|
||||
if (dashboardStore.entity === EntityType[7].value) {
|
||||
selectorStore.setCurrentProcess(null);
|
||||
states.currentProcess = "";
|
||||
fetchProcess(true);
|
||||
}
|
||||
}
|
||||
|
||||
function changeDestPods(pod: any) {
|
||||
if (pod[0]) {
|
||||
selectorStore.setCurrentDestPod(pod[0]);
|
||||
} else {
|
||||
selectorStore.setCurrentDestPod(null);
|
||||
function changeDestPods(pod: Option[]) {
|
||||
selectorStore.setCurrentDestPod(pod[0] || null);
|
||||
if (dashboardStore.entity === EntityType[7].value) {
|
||||
selectorStore.setCurrentDestProcess(null);
|
||||
states.currentDestProcess = "";
|
||||
fetchDestProcess(true);
|
||||
}
|
||||
}
|
||||
|
||||
function changeDestProcess(pod: Option[]) {
|
||||
selectorStore.setCurrentDestProcess(pod[0] || null);
|
||||
states.currentDestProcess = pod[0].label || "";
|
||||
}
|
||||
|
||||
function changeProcess(pod: Option[]) {
|
||||
selectorStore.setCurrentProcess(pod[0] || null);
|
||||
states.currentProcess = pod[0].label || "";
|
||||
}
|
||||
|
||||
function changeMode() {
|
||||
if (dashboardStore.editMode) {
|
||||
ElMessage.warning(t("editWarning"));
|
||||
@@ -458,6 +488,15 @@ function setTabControls(id: string) {
|
||||
case "addDemandLog":
|
||||
dashboardStore.addTabControls("DemandLog");
|
||||
break;
|
||||
case "addEvent":
|
||||
dashboardStore.addTabControls("Event");
|
||||
break;
|
||||
case "addNetworkProfiling":
|
||||
dashboardStore.addTabControls("NetworkProfiling");
|
||||
break;
|
||||
case "addTimeRange":
|
||||
dashboardStore.addTabControls("TimeRange");
|
||||
break;
|
||||
default:
|
||||
ElMessage.info("Don't support this control");
|
||||
break;
|
||||
@@ -493,6 +532,15 @@ function setControls(id: string) {
|
||||
case "addDemandLog":
|
||||
dashboardStore.addControl("DemandLog");
|
||||
break;
|
||||
case "addEvent":
|
||||
dashboardStore.addControl("Event");
|
||||
break;
|
||||
case "addNetworkProfiling":
|
||||
dashboardStore.addControl("NetworkProfiling");
|
||||
break;
|
||||
case "addTimeRange":
|
||||
dashboardStore.addControl("TimeRange");
|
||||
break;
|
||||
default:
|
||||
dashboardStore.addControl("Widget");
|
||||
}
|
||||
@@ -509,35 +557,13 @@ async function fetchPods(
|
||||
case EntityType[2].value:
|
||||
resp = await selectorStore.getEndpoints({ serviceId, ...param });
|
||||
if (setPod) {
|
||||
let p;
|
||||
if (states.currentPod) {
|
||||
p = selectorStore.pods.find(
|
||||
(d: { label: unknown }) => d.label === states.currentPod
|
||||
);
|
||||
} else {
|
||||
p = selectorStore.pods.find(
|
||||
(d: unknown, index: number) => index === 0
|
||||
);
|
||||
}
|
||||
selectorStore.setCurrentPod(p || null);
|
||||
states.currentPod = selectorStore.currentPod.label;
|
||||
updateCurrentPod();
|
||||
}
|
||||
break;
|
||||
case EntityType[3].value:
|
||||
resp = await selectorStore.getServiceInstances({ serviceId });
|
||||
if (setPod) {
|
||||
let p;
|
||||
if (states.currentPod) {
|
||||
p = selectorStore.pods.find(
|
||||
(d: { label: string }) => d.label === states.currentPod
|
||||
);
|
||||
} else {
|
||||
p = selectorStore.pods.find(
|
||||
(d: { label: string }, index: number) => index === 0
|
||||
);
|
||||
}
|
||||
selectorStore.setCurrentPod(p || null);
|
||||
states.currentPod = selectorStore.currentPod.label;
|
||||
updateCurrentPod();
|
||||
}
|
||||
break;
|
||||
case EntityType[6].value:
|
||||
@@ -547,18 +573,7 @@ async function fetchPods(
|
||||
...param,
|
||||
});
|
||||
if (setPod) {
|
||||
let p;
|
||||
if (states.currentDestPod) {
|
||||
p = selectorStore.destPods.find(
|
||||
(d: { label: string }) => d.label === states.currentDestPod
|
||||
);
|
||||
} else {
|
||||
p = selectorStore.destPods.find(
|
||||
(d: { label: string }, index: number) => index === 0
|
||||
);
|
||||
}
|
||||
selectorStore.setCurrentDestPod(p || null);
|
||||
states.currentDestPod = selectorStore.currentDestPod.label;
|
||||
updateCurrentDestPod();
|
||||
}
|
||||
break;
|
||||
case EntityType[5].value:
|
||||
@@ -567,20 +582,17 @@ async function fetchPods(
|
||||
isRelation: true,
|
||||
});
|
||||
if (setPod) {
|
||||
let p;
|
||||
if (states.currentDestPod) {
|
||||
p = selectorStore.destPods.find(
|
||||
(d: { label: string }) => d.label === states.currentDestPod
|
||||
);
|
||||
} else {
|
||||
p = selectorStore.destPods.find(
|
||||
(d: { label: string }, index: number) => index === 0
|
||||
);
|
||||
}
|
||||
selectorStore.setCurrentDestPod(p || null);
|
||||
states.currentDestPod = selectorStore.currentDestPod.label;
|
||||
updateCurrentDestPod();
|
||||
}
|
||||
break;
|
||||
case EntityType[7].value:
|
||||
await fetchPods(EntityType[5].value, serviceId, setPod, param);
|
||||
resp = await fetchDestProcess(setPod);
|
||||
break;
|
||||
case "Process":
|
||||
await fetchPods(EntityType[3].value, serviceId, setPod, param);
|
||||
resp = await fetchProcess(setPod);
|
||||
break;
|
||||
default:
|
||||
resp = {};
|
||||
}
|
||||
@@ -588,6 +600,81 @@ async function fetchPods(
|
||||
ElMessage.error(resp.errors);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchProcess(setPod: boolean) {
|
||||
const resp = await selectorStore.getProcesses({
|
||||
instanceId: selectorStore.currentPod.id,
|
||||
});
|
||||
if (setPod) {
|
||||
const process = params.processId || selectorStore.processes[0].id;
|
||||
const currentProcess = selectorStore.processes.find(
|
||||
(d: { id: string }) => d.id === process
|
||||
);
|
||||
if (currentProcess) {
|
||||
selectorStore.setCurrentProcess(currentProcess);
|
||||
states.currentProcess = currentProcess.label;
|
||||
}
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
async function fetchDestProcess(setPod: boolean) {
|
||||
const resp = await selectorStore.getProcesses({
|
||||
instanceId: selectorStore.currentDestPod.id,
|
||||
isRelation: true,
|
||||
});
|
||||
if (setPod) {
|
||||
const destProcess =
|
||||
params.destProcessId || selectorStore.destProcesses[0].id;
|
||||
const currentDestProcess = selectorStore.destProcesses.find(
|
||||
(d: { id: string }) => d.id === destProcess
|
||||
);
|
||||
if (!currentDestProcess) {
|
||||
states.currentDestProcess = "";
|
||||
selectorStore.setCurrentDestProcess(null);
|
||||
return;
|
||||
}
|
||||
selectorStore.setCurrentDestProcess(currentDestProcess);
|
||||
states.currentDestProcess = currentDestProcess.label;
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
function updateCurrentDestPod() {
|
||||
if (!(selectorStore.destPods.length && selectorStore.destPods[0])) {
|
||||
selectorStore.setCurrentDestPod(null);
|
||||
states.currentDestPod = "";
|
||||
return;
|
||||
}
|
||||
const destPod = params.destPodId || selectorStore.destPods[0].id;
|
||||
const currentDestPod = selectorStore.destPods.find(
|
||||
(d: { id: string }) => d.id === destPod
|
||||
);
|
||||
if (!currentDestPod) {
|
||||
states.currentDestPod = "";
|
||||
selectorStore.setCurrentDestPod(null);
|
||||
return;
|
||||
}
|
||||
selectorStore.setCurrentDestPod(currentDestPod);
|
||||
states.currentDestPod = currentDestPod.label;
|
||||
}
|
||||
function updateCurrentPod() {
|
||||
if (!(selectorStore.pods.length && selectorStore.pods[0])) {
|
||||
selectorStore.setCurrentPod(null);
|
||||
states.currentPod = "";
|
||||
return;
|
||||
}
|
||||
const pod = params.podId || selectorStore.pods[0].id;
|
||||
const currentPod = selectorStore.pods.find(
|
||||
(d: { id: string }) => d.id === pod
|
||||
);
|
||||
if (!currentPod) {
|
||||
selectorStore.setCurrentPod(null);
|
||||
states.currentPod = "";
|
||||
return;
|
||||
}
|
||||
selectorStore.setCurrentPod(currentPod);
|
||||
states.currentPod = currentPod.label;
|
||||
}
|
||||
function getTools() {
|
||||
switch (params.entity) {
|
||||
case EntityType[1].value:
|
||||
@@ -611,8 +698,11 @@ function getTools() {
|
||||
case EntityType[6].value:
|
||||
toolIcons.value = EndpointRelationTools;
|
||||
break;
|
||||
case EntityType[7].value:
|
||||
toolIcons.value = ProcessTools;
|
||||
break;
|
||||
default:
|
||||
toolIcons.value = EndpointRelationTools;
|
||||
toolIcons.value = AllTools;
|
||||
}
|
||||
}
|
||||
function searchPods(query: string) {
|
||||
@@ -695,4 +785,8 @@ watch(
|
||||
.selectorPod {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
|
81
src/views/dashboard/related/components/TaskDetails.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="profile-detail flex-v">
|
||||
<div>
|
||||
<h5 class="mb-10">{{ t("task") }}.</h5>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("taskId") }}:</span>
|
||||
<span class="g-sm-8 wba">
|
||||
{{ details.taskId }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("service") }}:</span>
|
||||
<span class="g-sm-8 wba">
|
||||
{{ details.serviceName }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("labels") }}:</span>
|
||||
<span class="g-sm-8 wba">
|
||||
{{ details.processLabels.join(";") }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("monitorTime") }}:</span>
|
||||
<span class="g-sm-8 wba">
|
||||
{{ dateFormat(details.taskStartTime) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span>
|
||||
<span class="g-sm-8 wba">
|
||||
{{ details.fixedTriggerDuration / 60 }} min
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("triggerType") }}:</span>
|
||||
<span class="g-sm-8 wba">{{ details.triggerType }}</span>
|
||||
</div>
|
||||
<div class="mb-10 clear item">
|
||||
<span class="g-sm-4 grey">{{ t("targetType") }}:</span>
|
||||
<span class="g-sm-8 wba">{{ details.targetType }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { EBPFTaskList } from "@/types/ebpf";
|
||||
|
||||
/*global defineProps */
|
||||
defineProps({
|
||||
details: {
|
||||
type: Object as PropType<EBPFTaskList>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
dayjs(date).format(pattern);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.item span {
|
||||
height: 21px;
|
||||
}
|
||||
</style>
|