remove widget

This commit is contained in:
Fine 2022-08-11 20:37:27 +08:00
parent 12dd47affe
commit 260e0f2a68
10 changed files with 0 additions and 949 deletions

View File

@ -1,154 +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.
*/
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";
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;
}
export const networkProfilingStore = defineStore({
id: "networkProfiling",
state: (): NetworkProfilingState => ({
networkTasks: [],
networkTip: "",
selectedNetworkTask: {},
nodes: [],
calls: [],
node: null,
call: null,
metricsLayout: [],
selectedMetric: null,
activeMetricIndex: "",
}),
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;
const 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;
}, []);
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;
}
this.getTaskList({
...param,
targets: ["NETWORK"],
});
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 getProcessTopology(params: {
duration: any;
serviceInstanceId: string;
}) {
const res: AxiosResponse = await graphql
.query("getProcessTopology")
.params(params);
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);
}

View File

@ -1,97 +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>
<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 />
</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>

View File

@ -24,7 +24,6 @@ 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";
export default {
Tab,
@ -37,5 +36,4 @@ export default {
Ebpf,
DemandLog,
Event,
NetworkProfiling,
};

View File

@ -23,7 +23,6 @@ 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";
export default {
Widget,
@ -35,5 +34,4 @@ export default {
Ebpf,
DemandLog,
Event,
NetworkProfiling,
};

View File

@ -198,11 +198,6 @@ export const InstanceTools = [
{ 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" },

View File

@ -548,9 +548,6 @@ function setTabControls(id: string) {
case "addEvent":
dashboardStore.addTabControls("Event");
break;
case "addNetworkProfiling":
dashboardStore.addTabControls("NetworkProfiling");
break;
default:
ElMessage.info("Don't support this control");
break;
@ -589,9 +586,6 @@ function setControls(id: string) {
case "addEvent":
dashboardStore.addControl("Event");
break;
case "addNetworkProfiling":
dashboardStore.addControl("NetworkProfiling");
break;
default:
dashboardStore.addControl("Widget");
}

View File

@ -1,60 +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>
<div class="flex-h content">
<Tasks />
<div class="vis-graph ml-5">
<div class="schedules">
<Schedules />
</div>
<div class="item" v-show="networkProfilingStore.nodes.length">
<process-topology />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import Tasks from "./components/Tasks.vue";
import Schedules from "./components/Schedules.vue";
import ProcessTopology from "./components/ProcessTopology.vue";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
const networkProfilingStore = useNetworkProfilingStore();
</script>
<style lang="scss" scoped>
.content {
height: calc(100% - 30px);
width: 100%;
}
.vis-graph {
height: 100%;
flex-grow: 2;
min-width: 700px;
overflow: auto;
}
.item {
width: 100%;
height: calc(100% - 210px);
background-color: #333840;
}
.schedules {
height: 200px;
border-bottom: 1px solid #ccc;
padding-right: 10px;
}
</style>

View File

@ -1,272 +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>
<div ref="chart" class="micro-topo-chart"></div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { ref, onMounted, watch } from "vue";
import * as d3 from "d3";
import { useI18n } from "vue-i18n";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useDashboardStore } from "@/store/modules/dashboard";
import d3tip from "d3-tip";
import {
simulationInit,
simulationSkip,
} from "../../components/D3Graph/simulation";
import {
linkElement,
anchorElement,
arrowMarker,
} from "../../components/D3Graph/linkElement";
import nodeElement from "../../components/D3Graph/nodeElement";
import { Call } from "@/types/topology";
// import zoom from "../../components/D3Graph/zoom";
import { ProcessNode } from "@/types/ebpf";
import { useThrottleFn } from "@vueuse/core";
/*global Nullable, defineProps */
const props = defineProps({
config: {
type: Object as PropType<any>,
default: () => ({ graph: {} }),
},
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const networkProfilingStore = useNetworkProfilingStore();
const height = ref<number>(100);
const width = ref<number>(100);
const simulation = ref<any>(null);
const svg = ref<Nullable<any>>(null);
const chart = ref<Nullable<HTMLDivElement>>(null);
const tip = ref<Nullable<HTMLDivElement>>(null);
const graph = ref<any>(null);
const node = ref<any>(null);
const link = ref<any>(null);
const anchor = ref<any>(null);
const arrow = ref<any>(null);
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
onMounted(() => {
init();
oldVal.value = (chart.value && chart.value.getBoundingClientRect()) || {
width: 0,
height: 0,
};
});
async function init() {
svg.value = d3.select(chart.value).append("svg").attr("class", "process-svg");
if (!networkProfilingStore.nodes.length) {
return;
}
drawGraph();
update();
}
function drawGraph() {
const dom = chart.value?.getBoundingClientRect() || {
height: 20,
width: 0,
};
height.value = dom.height - 20;
width.value = dom.width;
svg.value.attr("height", height.value).attr("width", width.value);
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
graph.value = svg.value
.append("g")
.attr("class", "svg-graph")
.attr("transform", `translate(-250, -220)`);
graph.value.call(tip.value);
simulation.value = simulationInit(
d3,
networkProfilingStore.nodes,
networkProfilingStore.calls,
ticked
);
node.value = graph.value.append("g").selectAll(".topo-node");
link.value = graph.value.append("g").selectAll(".topo-line");
anchor.value = graph.value.append("g").selectAll(".topo-line-anchor");
arrow.value = graph.value.append("g").selectAll(".topo-line-arrow");
// svg.value.call(zoom(d3, graph.value));
svg.value.on("click", (event: any) => {
event.stopPropagation();
event.preventDefault();
networkProfilingStore.setNode(null);
networkProfilingStore.setLink(null);
dashboardStore.selectWidget(props.config);
});
useThrottleFn(resize, 500)();
}
function update() {
// node element
if (!node.value || !link.value) {
return;
}
node.value = node.value.data(
networkProfilingStore.nodes,
(d: ProcessNode) => d.id
);
node.value.exit().remove();
node.value = nodeElement(
d3,
node.value.enter(),
{
tipHtml: (data: ProcessNode) => {
return ` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`;
},
},
tip.value
).merge(node.value);
// line element
link.value = link.value.data(networkProfilingStore.calls, (d: Call) => d.id);
link.value.exit().remove();
link.value = linkElement(link.value.enter()).merge(link.value);
// anchorElement
anchor.value = anchor.value.data(
networkProfilingStore.calls,
(d: Call) => d.id
);
anchor.value.exit().remove();
anchor.value = anchorElement(
anchor.value.enter(),
{
handleLinkClick: handleLinkClick,
tipHtml: (data: Call) => {
const html = `<div><span class="grey">${t(
"detectPoint"
)}:</span>${data.detectPoints.join(" | ")}</div>`;
return html;
},
},
tip.value
).merge(anchor.value);
// arrow marker
arrow.value = arrow.value.data(
networkProfilingStore.calls,
(d: Call) => d.id
);
arrow.value.exit().remove();
arrow.value = arrowMarker(arrow.value.enter()).merge(arrow.value);
// force element
simulation.value.nodes(networkProfilingStore.nodes);
simulation.value
.force("link")
.links(networkProfilingStore.calls)
.id((d: Call) => d.id);
simulationSkip(d3, simulation.value, ticked);
const loopMap: any = {};
for (let i = 0; i < networkProfilingStore.calls.length; i++) {
const link: any = networkProfilingStore.calls[i];
link.loopFactor = 1;
for (let j = 0; j < networkProfilingStore.calls.length; j++) {
if (i === j || loopMap[i]) {
continue;
}
const otherLink = networkProfilingStore.calls[j];
if (
link.source.id === otherLink.target.id &&
link.target.id === otherLink.source.id
) {
link.loopFactor = -1;
loopMap[j] = 1;
break;
}
}
}
}
function handleLinkClick(event: any, d: Call) {
if (
d.source.layer !== dashboardStore.layerId ||
d.target.layer !== dashboardStore.layerId
) {
return;
}
event.stopPropagation();
networkProfilingStore.setNode(null);
networkProfilingStore.setLink(d);
}
function ticked() {
link.value.attr(
"d",
(d: Call | any) =>
`M${d.source.x} ${d.source.y} Q ${(d.source.x + d.target.x) / 2} ${
(d.target.y + d.source.y) / 2 - d.loopFactor * 90
} ${d.target.x} ${d.target.y}`
);
anchor.value.attr(
"transform",
(d: Call | any) =>
`translate(${(d.source.x + d.target.x) / 2}, ${
(d.target.y + d.source.y) / 2 - d.loopFactor * 45
})`
);
node.value.attr(
"transform",
(d: Node | any) => `translate(${d.x - 22},${d.y - 22})`
);
}
function resize() {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
const cr = entry.contentRect;
if (
Math.abs(cr.width - oldVal.value.width) < 5 &&
Math.abs(cr.height - oldVal.value.height) < 5
) {
return;
}
freshNodes();
oldVal.value = { width: cr.width, height: cr.height };
});
if (chart.value) {
observer.observe(chart.value);
}
}
async function freshNodes() {
svg.value.selectAll(".svg-graph").remove();
if (!networkProfilingStore.nodes.length) {
return;
}
drawGraph();
update();
}
watch(
() => networkProfilingStore.nodes,
() => {
freshNodes();
}
);
</script>
<style lang="scss" scoped>
.micro-topo-chart {
width: calc(100% - 10px);
margin: 0 5px 5px 0;
height: 100%;
min-height: 150px;
}
.process-svg {
width: 100%;
height: calc(100% - 10px);
cursor: move;
}
</style>

View File

@ -1,113 +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>
<el-button type="primary" size="small">
{{ t("start") }}
</el-button>
<div ref="timeRange" class="time-ranges"></div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { DataSet, Timeline } from "vis-timeline/standalone";
import "vis-timeline/styles/vis-timeline-graph2d.css";
import { useThrottleFn } from "@vueuse/core";
/*global Nullable */
const { t } = useI18n();
const networkProfilingStore = useNetworkProfilingStore();
const timeRange = ref<Nullable<HTMLDivElement>>(null);
const visGraph = ref<Nullable<any>>(null);
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
onMounted(() => {
oldVal.value = (timeRange.value &&
timeRange.value.getBoundingClientRect()) || {
width: 0,
height: 0,
};
useThrottleFn(resize, 500)();
});
function visTimeline() {
if (!timeRange.value) {
return;
}
if (visGraph.value) {
visGraph.value.destroy();
}
if (!networkProfilingStore.selectedNetworkTask.taskId) {
return;
}
const h = timeRange.value.getBoundingClientRect().height;
const task = [
{
id: 1,
content: networkProfilingStore.selectedNetworkTask.name,
start: new Date(
Number(networkProfilingStore.selectedNetworkTask.taskStartTime)
),
end: new Date(
Number(
networkProfilingStore.selectedNetworkTask.taskStartTime +
networkProfilingStore.selectedNetworkTask.fixedTriggerDuration
)
),
data: networkProfilingStore.selectedNetworkTask,
className: networkProfilingStore.selectedNetworkTask.type,
},
];
const items = new DataSet(task);
const options: any = {
height: h,
width: "100%",
locale: "en",
groupHeightMode: "fitItems",
autoResize: false,
};
visGraph.value = new Timeline(timeRange.value, items, options);
}
function resize() {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
const cr = entry.contentRect;
if (
Math.abs(cr.width - oldVal.value.width) < 3 &&
Math.abs(cr.height - oldVal.value.height) < 3
) {
return;
}
visTimeline();
oldVal.value = { width: cr.width, height: cr.height };
});
if (timeRange.value) {
observer.observe(timeRange.value);
}
}
watch(
() => networkProfilingStore.selectedNetworkTask,
() => {
visTimeline();
}
);
</script>
<style lang="scss" scoped>
.time-ranges {
width: calc(100% - 5px);
margin: 0 5px 5px 0;
height: 150px;
}
</style>

View File

@ -1,238 +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>
<div class="profile-task-list flex-v">
<div class="profile-task-wrapper flex-v">
<div class="profile-t-tool">
<span>{{ t("taskList") }}</span>
<el-popconfirm
title="Are you sure to create a task?"
@confirm="createTask"
>
<template #reference>
<span class="new-task cp">
<Icon iconName="library_add" size="middle" />
</span>
</template>
</el-popconfirm>
</div>
<div class="profile-t-wrapper">
<div
class="no-data"
v-show="!networkProfilingStore.networkTasks.length"
>
{{ t("noData") }}
</div>
<table class="profile-t">
<tr
class="profile-tr cp"
v-for="(i, index) in networkProfilingStore.networkTasks"
@click="changeTask(i)"
:key="index"
>
<td
class="profile-td"
:class="{
selected:
networkProfilingStore.selectedNetworkTask.taskId === i.taskId,
}"
>
<div class="ell">
<span>
{{ i.targetType }}
</span>
<a class="profile-btn r" @click="viewDetail = true">
<Icon iconName="view" size="middle" />
</a>
</div>
<div class="grey ell sm">
<span class="mr-10 sm">{{ dateFormat(i.taskStartTime) }}</span>
<span class="mr-10 sm">
{{
dateFormat(i.taskStartTime + i.fixedTriggerDuration * 1000)
}}
</span>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<el-dialog
v-model="viewDetail"
:destroy-on-close="true"
fullscreen
@closed="viewDetail = false"
>
<TaskDetails :details="networkProfilingStore.selectedNetworkTask" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useSelectorStore } from "@/store/modules/selectors";
import { EBPFTaskList } from "@/types/ebpf";
import { ElMessage } from "element-plus";
import TaskDetails from "../../components/TaskDetails.vue";
import dateFormatStep from "@/utils/dateFormat";
import getLocalTime from "@/utils/localtime";
import { useAppStoreWithOut } from "@/store/modules/app";
const { t } = useI18n();
const selectorStore = useSelectorStore();
const networkProfilingStore = useNetworkProfilingStore();
const appStore = useAppStoreWithOut();
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
const viewDetail = ref<boolean>(false);
fetchTasks();
async function changeTask(item: EBPFTaskList) {
networkProfilingStore.setSelectedNetworkTask(item);
getTopology();
}
async function getTopology() {
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
const resp = await networkProfilingStore.getProcessTopology({
serviceInstanceId,
duration: {
start: dateFormatStep(
getLocalTime(
appStore.utc,
new Date(networkProfilingStore.selectedNetworkTask.taskStartTime)
),
appStore.duration.step,
true
),
end: dateFormatStep(
getLocalTime(
appStore.utc,
new Date(
networkProfilingStore.selectedNetworkTask.taskStartTime +
networkProfilingStore.selectedNetworkTask.fixedTriggerDuration *
1000
)
),
appStore.duration.step,
true
),
step: appStore.duration.step,
},
});
return resp;
}
async function createTask() {
const serviceId =
(selectorStore.currentService && selectorStore.currentService.id) || "";
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
if (!serviceId) {
return;
}
if (!serviceInstanceId) {
return;
}
networkProfilingStore.createNetworkTask({
serviceId,
serviceInstanceId,
});
}
async function fetchTasks() {
const serviceId =
(selectorStore.currentService && selectorStore.currentService.id) || "";
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
const res = await networkProfilingStore.getTaskList({
serviceId,
serviceInstanceId,
targets: ["NETWORK"],
});
if (res.errors) {
return ElMessage.error(res.errors);
}
getTopology();
}
</script>
<style lang="scss" scoped>
.profile-task-list {
width: 300px;
height: calc(100% - 10px);
overflow: auto;
border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.item span {
height: 21px;
}
.profile-td {
padding: 5px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
&.selected {
background-color: #ededed;
}
}
.no-data {
text-align: center;
margin-top: 10px;
}
.profile-t-wrapper {
overflow: auto;
flex-grow: 1;
}
.profile-t {
width: 100%;
border-spacing: 0;
table-layout: fixed;
flex-grow: 1;
position: relative;
border: none;
}
.profile-tr {
&:hover {
background-color: rgba(0, 0, 0, 0.04);
}
}
.profile-t-tool {
padding: 5px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
background: #f3f4f9;
width: 100%;
}
.profile-btn {
color: #3d444f;
padding: 1px 3px;
border-radius: 2px;
font-size: 12px;
float: right;
}
.new-task {
float: right;
}
</style>