ntopng/http_src/vue/table.vue
2023-04-12 16:19:54 +02:00

262 lines
6.7 KiB
Vue

<!-- (C) 2022 - ntop.org -->
<template>
<div>
<div class="button-group mb-2"> <!-- TableHeader -->
<div style="float:left;">
<label>
Show
<select v-model="per_page" @change="change_per_page">
<option v-for="pp in per_page_options" :value="pp">{{pp}}</option>
</select>
Entries
</label>
</div>
<div style="text-align:right;">
<button class="btn btn-link me-1" type="button" @click="reset_column_size">
<i class="fas fa-columns"></i>
</button>
<button class="btn btn-link me-1" type="button" @click="refresh_table">
<i class="fas fa-refresh"></i>
</button>
<Dropdown :id="id" ref="dropdown"> <!-- Dropdown columns -->
<template v-slot:title>
<i class="fas fa-eye"></i>
</template>
<template v-slot:menu>
<div v-for="col in columns_wrap" class="form-check form-switch ms-1">
<input class="form-check-input" v-model="col.visible" @click="change_columns_visibility(col)" checked="" type="checkbox" id="toggle-Begin">
<label class="form-check-label" for="toggle-Begin" v-html="print_column_name(col.data)">
</label>
</div>
</template>
</Dropdown> <!-- Dropdown columns -->
</div>
</div> <!-- TableHeader -->
<div :key="table_key" class="" style="overflow:auto;width:100%;"> <!-- Table -->
<table ref="table" class="table table-striped table-bordered ml-0 mr-0 mb-0 " style="table-layout: auto; white-space: nowrap;" data-resizable="true" :data-resizable-columns-id="id"> <!-- Table -->
<thead>
<tr>
<template v-for="(col, col_index) in columns_wrap">
<th v-if="col.visible" scope="col" style="cursor:pointer;white-space: nowrap;" @click="change_column_sort(col, col_index)" :data-resizable-column-id="get_column_id(col.data)">
<div style="display:flex;">
<span v-html="print_column_name(col.data)" class="wrap-column"></span>
<!-- <i v-show="col.sort == 0" class="fa fa-fw fa-sort"></i> -->
<i v-show="col.sort == 1" class="fa fa-fw fa-sort-up"></i>
<i v-show="col.sort == 2" class="fa fa-fw fa-sort-down"></i>
</div>
</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="row in active_rows">
<template v-for="col in columns_wrap">
<td v-if="col.visible" scope="col"><div class="wrap-column" v-html="print_html_row(col.data, row)"></div></td>
</template>
</tr>
</tbody>
</table> <!-- Table -->
</div> <!-- Table div-->
<div>
<SelectTablePage :key="select_pages_key"
:total_rows="total_rows"
:per_page="per_page"
@change_active_page="change_active_page">
</SelectTablePage>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed, watch, onBeforeUnmount, nextTick } from "vue";
import { default as Dropdown } from "./dropdown.vue";
import { default as SelectTablePage } from "./select_table_page.vue";
const props = defineProps({
id: String,
columns: Array,
get_rows: Function, // async (active_page: number, per_page: number, columns_wrap: any[]) => { total_rows: number, rows: any[] }
get_column_id: Function,
print_column_name: Function,
print_html_row: Function,
f_sort_rows: Function,
paging: Boolean,
});
const show_table = ref(true);
const table = ref(null);
const dropdown = ref(null);
let active_page = 0;
let rows = [];
const columns_wrap = ref([]);
const active_rows = ref([]);
const total_rows = ref(0);
const per_page_options = [10, 20, 40, 50, 80, 100];
const per_page = ref(10);
const store = window.store;
onMounted(async () => {
if (props.columns != null) {
load_table();
}
});
watch(() => props.columns, (cur_value, old_value) => {
load_table();
}, { flush: 'pre'});
async function load_table() {
set_columns_wrap();
await set_rows();
set_columns_resizable();
await nextTick();
dropdown.value.load_menu();
}
async function change_columns_visibility(col) {
if (props.paging) {
await set_rows();
}
redraw_table();
await redraw_table_resizable();
set_columns_resizable();
}
async function redraw_table_resizable() {
redraw_table();
await nextTick();
set_columns_resizable();
}
const table_key = ref(0);
function redraw_table() {
table_key.value += 1;
}
function set_columns_resizable() {
let options = {
// selector: table.value,
// padding: 0,
store: store,
minWidth: 32,
// padding: -50,
// maxWidth: 150,
};
$(table.value).resizableColumns(options);
// $(table.value).css('width', '100%');
}
function set_columns_wrap() {
columns_wrap.value = props.columns.map((c, i) => {
return {
visible: true,
sort: 0,
order: i,
data: c,
};
});
}
async function reset_column_size() {
props.columns.forEach((c) => {
let id = `${props.id}-${props.get_column_id(c)}`;
store.remove(id);
});
await redraw_table_resizable();
}
function change_per_page() {
redraw_select_pages();
change_active_page(0);
}
const select_pages_key = ref(0);
function redraw_select_pages() {
select_pages_key.value += 1;
}
async function change_active_page(new_active_page) {
active_page = new_active_page;
if (props.paging == true) {
await set_rows();
} else {
set_active_rows();
}
}
function change_column_sort(col, col_index) {
col.sort = (col.sort + 1) % 3;
columns_wrap.value.filter((c, i) => i != col_index).forEach((c) => c.sort = 0);
if (col.sort == 0) { return; }
if (props.paging) {
set_rows();
return;
}
let f_sort = get_sort_function();
rows = rows.sort((r0, r1) => {
return f_sort(col, r0, r1);
});
set_active_rows();
}
function get_sort_function() {
if (props.f_sort_rows != null) {
return props.f_sort_rows;
}
return (col, r0, r1) => {
let r0_col = props.print_html_row(col.data, r0);
let r1_col = props.print_html_row(col.data, r1);
if (col.sort == 1) {
return r0_col.localeCompare(r1_col);
}
return r1_col.localeCompare(r0_col);
};
}
function refresh_table() {
set_rows();
}
let first_get_rows = true;
async function set_rows() {
let res = await props.get_rows(active_page, per_page.value, columns_wrap.value, first_get_rows);
first_get_rows = false;
total_rows.value = res.rows.length;
if (props.paging == true) {
total_rows.value = res.total_rows;
}
rows = res.rows;
set_active_rows();
}
function set_active_rows() {
let start_row_index = 0;
if (props.paging == false) {
start_row_index = active_page * per_page.value;
}
active_rows.value = rows.slice(start_row_index, start_row_index + per_page.value);
}
defineExpose({ load_table, refresh_table });
</script>
<style scoped>
.wrap-column {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width:100%;
}
/*table {
table-layout:fixed;
display: block;
overflow-x: auto;
white-space: nowrap;
}*/
</style>