mirror of
https://github.com/ntop/ntopng.git
synced 2026-07-10 00:14:24 +00:00
Fixes pie chart not correctly rendered
This commit is contained in:
parent
648e668d23
commit
2e1f18915d
7 changed files with 58 additions and 46 deletions
|
|
@ -3,11 +3,12 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div class="donut-charts-wrapper" :style="gridStyle">
|
||||
<div :style="gridStyle">
|
||||
<PieChart
|
||||
v-for="chart in charts"
|
||||
:key="chart.name"
|
||||
:chart="chart"
|
||||
:style="style"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -22,6 +23,8 @@ const props = defineProps({
|
|||
charts_per_row: { type: Number, default: null },
|
||||
});
|
||||
|
||||
const style = "max-height:400px"
|
||||
|
||||
const charts = computed(() => {
|
||||
if (props.context?.charts) return props.context.charts;
|
||||
if (props.chart) return [props.chart];
|
||||
|
|
@ -33,8 +36,7 @@ const gridStyle = computed(() => {
|
|||
return {
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${perRow}, 1fr)`,
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
gap: "10px"
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
(C) 2026 - ntop.org
|
||||
-->
|
||||
<template>
|
||||
<div ref="container" class="pie-container" :class="{ 'layout-column': legend_below }">
|
||||
<div ref="container" class="pie-container" :class="{ 'layout-column': legend_below }" :style="props.style">
|
||||
<!-- Title -->
|
||||
<div v-if="chart.title" class="pie-title"><strong>{{ chart.title }}</strong></div>
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<!-- Legend -->
|
||||
<div v-if="!loading && items.length && !no_data" class="pie-legend-wrap">
|
||||
<div class="pie-legend">
|
||||
<div class="pie-legend justify-content-center">
|
||||
<div v-for="(it, i) in items" :key="i" class="legend-item" :class="{ clickable: !!it.url }"
|
||||
@click="it.url && (window.location.href = it.url)">
|
||||
<span class="legend-dot" :style="{ background: it.color }"></span>
|
||||
|
|
@ -51,7 +51,7 @@ import NoData from '../components/no-data.vue'
|
|||
const d3 = d3v7;
|
||||
const _i18n = (t) => (typeof i18n === "function" ? i18n(t) : t);
|
||||
|
||||
const props = defineProps({ chart: { type: Object, required: true }, hideLoading: Boolean });
|
||||
const props = defineProps({ chart: { type: Object, required: true }, hideLoading: Boolean, style: String });
|
||||
const { name, update_url, url_params, refresh, unit, label, custom_fetch } = props.chart;
|
||||
const formatted_label = label ? (i18n(label) || label) : null;
|
||||
const container = ref(null);
|
||||
|
|
@ -133,7 +133,7 @@ async function load() {
|
|||
if (!has_loaded.value) loading.value = true;
|
||||
const { update_url, url_params, custom_fetch } = props.chart;
|
||||
emit("update-requested");
|
||||
|
||||
|
||||
try {
|
||||
let data;
|
||||
|
||||
|
|
@ -315,9 +315,11 @@ watch(() => props.chart.url_params, () => {
|
|||
}
|
||||
|
||||
.pie-wrapper {
|
||||
flex: 0 0 auto;
|
||||
/* width drives height via aspect-ratio — no height: 100% which caused circular sizing */
|
||||
width: clamp(100px, 45%, 200px);
|
||||
flex: 0 0 auto;
|
||||
/* prende il minore tra 45% della larghezza e il 100% dell'altezza disponibile */
|
||||
width: min(clamp(100px, 45%, 100%), 100%);
|
||||
height: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
@select_option="changeCriteria">
|
||||
</SelectSearch>
|
||||
</div>
|
||||
<div v-if="props.context.isEnterpriseXL && props.context.hasClickHouseSupport" class="w-100 d-flex align-items-center button-group">
|
||||
<div v-if="props.context.isEnterpriseXL && props.context.hasClickHouseSupport"
|
||||
class="w-100 d-flex align-items-center button-group">
|
||||
<!--
|
||||
<CustomSwitch v-model:value="toggle_slider" :change_label_side="true" :label="toggle_slider_label" style=""
|
||||
class="me-1" icon="fa-calendar-days" :title="toggle_slider_label" @change_value="saveSwitch">
|
||||
|
|
@ -16,8 +17,8 @@
|
|||
-->
|
||||
<div class="w-100 position-relative">
|
||||
<Transition name="add-effect" mode="out-in">
|
||||
<DateTimeRangePicker class="dontprint" id="as-date-time-picker"
|
||||
:round_time="true" :custom_time_interval_list="time_preset_list" min_time_interval_id="live"
|
||||
<DateTimeRangePicker class="dontprint" id="as-date-time-picker" :round_time="true"
|
||||
:custom_time_interval_list="time_preset_list" min_time_interval_id="live"
|
||||
:custom_change_select_time="changeTime" @epoch_change="setTimeInterval">
|
||||
</DateTimeRangePicker>
|
||||
</Transition>
|
||||
|
|
@ -34,20 +35,18 @@
|
|||
<div class="m-2 mb-3">
|
||||
<Transition name="add-effect" mode="out-in">
|
||||
<div class="position-relative">
|
||||
<div class="mb-4 d-flex flex-column" style="height: 60vh;">
|
||||
<div class="mb-4 d-flex flex-column" :style="{ height: show_pie ? '360px' : '60vh' }">
|
||||
<Loading :isLoading="loading"></Loading>
|
||||
<Sankey v-if="show_sankey" ref="sankey_chart" :no_data_message="no_data_message"
|
||||
:sankey_data="sankey_data" @node_click="onNodeClick" @autorefresh_toggle="onAutoRefreshToggle">
|
||||
</Sankey>
|
||||
<Pie v-if="show_pie" :key="reRenderPie" ref="pie_chart" :no_data_message="no_data_message"
|
||||
:pie_data="pie_data" @autorefresh_toggle="onAutoRefreshToggle">>
|
||||
</Pie>
|
||||
<PieChart v-if="show_pie" ref="pie_chart" :key="reRenderPie" :chart="pieChartInfo" />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition name="add-effect" v-if="props.context.isEnterpriseXL && props.context.hasClickHouseSupport" mode="out-in">
|
||||
<div class="position-relative" :key="reRenderTable"
|
||||
style="min-height: 614px;">
|
||||
<Transition name="add-effect" v-if="props.context.isEnterpriseXL && props.context.hasClickHouseSupport"
|
||||
mode="out-in">
|
||||
<div class="position-relative" :key="reRenderTable" style="min-height: 614px;">
|
||||
<TableWithConfig ref="table_as_stats" :table_id="table_id" :csrf="props.context.csrf"
|
||||
:showLoading="true" :f_map_columns="mapTableColumns" :f_sort_rows="columnsSorting"
|
||||
:get_extra_params_obj="getExtraParameters" @custom_event="onTableCustomEvent">
|
||||
|
|
@ -65,7 +64,7 @@ import { ref, onMounted, onBeforeMount, computed } from "vue";
|
|||
import { default as NoteList } from "./note-list.vue";
|
||||
import { default as Loading } from "./loading.vue"
|
||||
import { default as Sankey } from "./sankey.vue";
|
||||
import { default as Pie } from "./pie-test.vue";
|
||||
import { default as PieChart } from "./charts/pie-chart.vue";
|
||||
import { default as sortingFunctions } from "../utilities/sorting-utils.js";
|
||||
import { default as TableWithConfig } from "./table-with-config.vue";
|
||||
import { default as SelectSearch } from "./select-search.vue";
|
||||
|
|
@ -126,9 +125,23 @@ const note_list = [
|
|||
|
||||
/* ************************************** */
|
||||
|
||||
const updatePieData = async () => {
|
||||
loading.value = true;
|
||||
const data = await getPieData();
|
||||
loading.value = false;
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const pieChartInfo = {
|
||||
custom_fetch: updatePieData,
|
||||
unit: "bytes"
|
||||
}
|
||||
|
||||
/* ************************************** */
|
||||
|
||||
onMounted(() => {
|
||||
updateSankeyData();
|
||||
updatePieData();
|
||||
})
|
||||
|
||||
/* ************************************** */
|
||||
|
|
@ -184,7 +197,7 @@ const onAutoRefreshToggle = (enabled) => {
|
|||
if (enabled) {
|
||||
intervalId = setInterval(() => {
|
||||
updateSankeyData()
|
||||
updatePieData()
|
||||
pie_chart.value?.update();
|
||||
}, 10000 /* 10 sec refresh */)
|
||||
} else {
|
||||
clearInterval(intervalId);
|
||||
|
|
@ -212,7 +225,7 @@ const changeCriteria = async (opt) => {
|
|||
checkComponentsToShow();
|
||||
ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
|
||||
updateSankeyData();
|
||||
updatePieData();
|
||||
pie_chart.value?.update();
|
||||
}
|
||||
|
||||
/* ************************************** */
|
||||
|
|
@ -237,7 +250,7 @@ function setTimeInterval(epoch_interval) {
|
|||
}
|
||||
main_epoch_interval.value = epoch_interval;
|
||||
updateSankeyData();
|
||||
updatePieData();
|
||||
pie_chart.value?.update();
|
||||
reloadTable()
|
||||
}
|
||||
|
||||
|
|
@ -249,18 +262,6 @@ function saveSwitch() {
|
|||
|
||||
/* ************************************** */
|
||||
|
||||
const updatePieData = async () => {
|
||||
if (show_pie.value) {
|
||||
reRenderPie.value = !reRenderPie.value
|
||||
loading.value = true;
|
||||
const data = await getPieData();
|
||||
pie_data.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************** */
|
||||
|
||||
const getPieData = async () => {
|
||||
const url_request = getPieUrl();
|
||||
let graph = await ntopng_utility.http_request(url_request);
|
||||
|
|
@ -388,7 +389,7 @@ function columnsSorting(col, r0, r1) {
|
|||
} else if (col.id == "customer") {
|
||||
return sortingFunctions.sortByName(r0.customer.name, r1.customer.name, col.sort);
|
||||
} else if (col.id == "as") {
|
||||
return sortingFunctions.sortByName(r0.as.name, r1.as.name, col.sort);
|
||||
return sortingFunctions.sortByNumber(r0.asn.name, r1.asn.name, col.sort);
|
||||
} else if (col.id == "dst_as") {
|
||||
return sortingFunctions.sortByName(r0.dst_as.name, r1.dst_as.name, col.sort);
|
||||
} else if (col.id == "src_as") {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit a565a45fb166f8fe946c05ecaaf22ad38b8fe22d
|
||||
Subproject commit 3450d2fe25d5e29a37b44ebeac9516ae72c31a7c
|
||||
|
|
@ -805,7 +805,7 @@ if (ifstats.type ~= "zmq") then
|
|||
end
|
||||
|
||||
print [[ <tr>]]
|
||||
print [[<td colspan=6>]]
|
||||
print [[<td colspan=6><div class="row"><div class="row-4">]]
|
||||
|
||||
template.render("pages/vue_page.template", {
|
||||
vue_page_name = "MultiPieChart",
|
||||
|
|
@ -814,7 +814,7 @@ template.render("pages/vue_page.template", {
|
|||
}),
|
||||
})
|
||||
|
||||
print [[</td></tr>]]
|
||||
print [[</div></div></td></tr>]]
|
||||
if (ifstats.zmqRecvStats ~= nil and table.len(ifstats.zmqRecvStats) > 0) then
|
||||
print("<tr><th colspan=7 nowrap>" .. i18n("if_stats_overview.zmq_rx_statistics") .. "</th></tr>\n")
|
||||
local tot_flows = (ifstats.zmqRecvStats.flows or 0) + (ifstats.zmqRecvStats.dropped_flows or 0)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,12 @@ local values_table = {}
|
|||
-- ##########################################
|
||||
|
||||
local function update_section(sections, section_ref, value, formatter)
|
||||
local section_label = formatter(section_ref)
|
||||
local section_label
|
||||
if section_ref == "others" then
|
||||
section_label = i18n(section_ref)
|
||||
else
|
||||
section_label = formatter(section_ref)
|
||||
end
|
||||
local index = values_table[section_ref].index
|
||||
local previous_value = values_table[section_ref].value
|
||||
local new_value = previous_value + value
|
||||
|
|
@ -27,16 +32,18 @@ end
|
|||
-- ##########################################
|
||||
|
||||
local function create_section(sections, section_ref, value, formatter)
|
||||
local section_label = formatter(section_ref)
|
||||
local section_label
|
||||
if section_ref == "others" then
|
||||
section_label = i18n(section_ref)
|
||||
else
|
||||
section_label = formatter(section_ref)
|
||||
end
|
||||
local index = #sections + 1
|
||||
sections[index] = {
|
||||
label = section_label .. " (" .. bytesToSize(value) .. ")",
|
||||
value = value,
|
||||
url = "#",
|
||||
}
|
||||
if sections_ref == nil then
|
||||
return
|
||||
end
|
||||
values_table[section_ref] = {
|
||||
index = index,
|
||||
value = value,
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ if criteria_as == "user_traffic_breakdown" then
|
|||
}
|
||||
end
|
||||
|
||||
local sections = flow_pie.generatePie(queries, 10000, not isEmptyString(epoch_begin))
|
||||
local sections = flow_pie.generatePie(queries, 8, not isEmptyString(epoch_begin))
|
||||
|
||||
table.sort(sections, function(a, b)
|
||||
return a.value > b.value
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue