v-sistec-features 1.25.0 → 1.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/v-sistec-features.css +1 -1
- package/dist/vDataTable.d.ts +5 -2
- package/dist/vDataTable.js +753 -624
- package/package.json +1 -1
- package/src/DatatableVue/components/SearchDatatable.vue +55 -52
- package/src/DatatableVue/components/VColumn.vue +2 -1
- package/src/DatatableVue/components/VDataTable.vue +150 -13
- package/src/DatatableVue/components/VExtraActions.vue +45 -0
- package/src/DatatableVue/components/VOrderingIcon.vue +50 -0
- package/src/DatatableVue/components/VThDataTable.vue +19 -49
- package/src/DatatableVue/composables/useDataTableFetch.ts +1 -2
- package/src/DatatableVue/keys.ts +2 -0
- package/src/DatatableVue/types/v-data-table.ts +5 -0
package/package.json
CHANGED
|
@@ -1,58 +1,60 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<a v-if="item_use.includes(2)" href="#" class="btn dropdown-toggle" data-bs-toggle="dropdown"
|
|
5
|
+
title="Pesquisas Prédefinidas">
|
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-filter" width="24" height="24"
|
|
7
|
+
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
|
8
|
+
stroke-linejoin="round">
|
|
9
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
10
|
+
<path
|
|
11
|
+
d="M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227z">
|
|
12
|
+
</path>
|
|
13
|
+
</svg>
|
|
14
|
+
</a>
|
|
15
|
+
<div class="input-icon">
|
|
16
|
+
<input type="text" class="form-control ms-1" id="inputSearchVDataTable" v-model="modelSearch"
|
|
17
|
+
@keyup.enter="searchEnter" :placeholder="placeholder_search">
|
|
18
|
+
|
|
19
|
+
<span v-if="modelSearch" @click="cleanSearch()" class=" inputClose" title="Limpar pesquisa">
|
|
20
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
21
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
22
|
+
class="icon icon-tabler icons-tabler-outline icon-tabler-x">
|
|
23
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
24
|
+
<path d="M18 6l-12 12" />
|
|
25
|
+
<path d="M6 6l12 12" />
|
|
26
|
+
</svg>
|
|
27
|
+
</span>
|
|
28
|
+
<span v-else class="input-icon-addon">
|
|
29
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
30
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
|
8
31
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
9
|
-
<path
|
|
10
|
-
|
|
11
|
-
</path>
|
|
32
|
+
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"></path>
|
|
33
|
+
<path d="M21 21l-6 -6"></path>
|
|
12
34
|
</svg>
|
|
13
|
-
</
|
|
14
|
-
<div class="input-icon">
|
|
15
|
-
<input type="text" class="form-control ms-1" id="inputSearchVDataTable" v-model="modelSearch"
|
|
16
|
-
@keyup.enter="searchEnter" :placeholder="placeholder_search">
|
|
17
|
-
|
|
18
|
-
<span v-if="modelSearch" @click="cleanSearch()" class=" inputClose" title="Limpar pesquisa">
|
|
19
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
20
|
-
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
21
|
-
class="icon icon-tabler icons-tabler-outline icon-tabler-x">
|
|
22
|
-
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
23
|
-
<path d="M18 6l-12 12" />
|
|
24
|
-
<path d="M6 6l12 12" />
|
|
25
|
-
</svg>
|
|
26
|
-
</span>
|
|
27
|
-
<span v-else class="input-icon-addon">
|
|
28
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
29
|
-
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
|
30
|
-
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
31
|
-
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"></path>
|
|
32
|
-
<path d="M21 21l-6 -6"></path>
|
|
33
|
-
</svg>
|
|
34
|
-
</span>
|
|
35
|
-
</div>
|
|
36
|
-
<div v-if="item_use.includes(2)" class="dropdown-menu">
|
|
37
|
-
<template v-for="(filter, index) in props.list_filter" :key="index">
|
|
38
|
-
<router-link v-if="('type' in filter ? filter.type === 1 : false) && ('visible' in filter ? filter.visible : true)" :to="filter?.to"
|
|
39
|
-
class="dropdown-item cursor-pointer">
|
|
40
|
-
{{ filter.text }}
|
|
41
|
-
</router-link>
|
|
42
|
-
<a v-else-if="('type' in filter ? filter.type === 2 : true) && ('visible' in filter ? filter.visible : true)"
|
|
43
|
-
@click.prevent="tradeFilter(String(filter.value))" class="dropdown-item cursor-pointer"
|
|
44
|
-
:class="modelFilter === filter?.value ? 'bg-info text-dark selected' : ''">
|
|
45
|
-
{{ filter.text }}
|
|
46
|
-
</a>
|
|
47
|
-
<a v-else-if="('type' in filter ? filter.type === 3 : true) && ('visible' in filter ? filter.visible : true)" @click.prevent="clickItem(filter)"
|
|
48
|
-
class="dropdown-item cursor-pointer"
|
|
49
|
-
:class="filter?.active ? 'bg-info text-dark selected' : ''"
|
|
50
|
-
>
|
|
51
|
-
{{ filter.text }}
|
|
52
|
-
</a>
|
|
53
|
-
</template>
|
|
54
|
-
</div>
|
|
35
|
+
</span>
|
|
55
36
|
</div>
|
|
37
|
+
<div v-if="item_use.includes(2)" class="dropdown-menu">
|
|
38
|
+
<template v-for="(filter, index) in props.list_filter" :key="index">
|
|
39
|
+
<router-link
|
|
40
|
+
v-if="('type' in filter ? filter.type === 1 : false) && ('visible' in filter ? filter.visible : true)"
|
|
41
|
+
:to="filter?.to" class="dropdown-item cursor-pointer">
|
|
42
|
+
{{ filter.text }}
|
|
43
|
+
</router-link>
|
|
44
|
+
<a v-else-if="('type' in filter ? filter.type === 2 : true) && ('visible' in filter ? filter.visible : true)"
|
|
45
|
+
@click.prevent="tradeFilter(String(filter.value))" class="dropdown-item cursor-pointer"
|
|
46
|
+
:class="modelFilter === filter?.value ? 'bg-info text-dark selected' : ''">
|
|
47
|
+
{{ filter.text }}
|
|
48
|
+
</a>
|
|
49
|
+
<a v-else-if="('type' in filter ? filter.type === 3 : true) && ('visible' in filter ? filter.visible : true)"
|
|
50
|
+
@click.prevent="clickItem(filter)" class="dropdown-item cursor-pointer"
|
|
51
|
+
:class="filter?.active ? 'bg-info text-dark selected' : ''">
|
|
52
|
+
{{ filter.text }}
|
|
53
|
+
</a>
|
|
54
|
+
</template>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
|
|
56
58
|
</template>
|
|
57
59
|
<script setup lang="ts">
|
|
58
60
|
import { computed, watch } from 'vue';
|
|
@@ -64,6 +66,7 @@ interface SearchProps {
|
|
|
64
66
|
placeholder_search?: string;
|
|
65
67
|
deactivate_search_on_clear?: boolean;
|
|
66
68
|
deactivate_search_empty?: boolean;
|
|
69
|
+
|
|
67
70
|
}
|
|
68
71
|
const props = withDefaults(defineProps<SearchProps>(), {
|
|
69
72
|
filter: "",
|
|
@@ -148,7 +151,7 @@ function tradeFilter(newFilter: string): void {
|
|
|
148
151
|
|
|
149
152
|
function cleanSearch(): void {
|
|
150
153
|
modelSearch.value = ""
|
|
151
|
-
if (!props.deactivate_search_on_clear){
|
|
154
|
+
if (!props.deactivate_search_on_clear) {
|
|
152
155
|
emit('search'); // emite o evento de busca para atualizar a lista
|
|
153
156
|
}
|
|
154
157
|
emit('clickedClearSearch');
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { inject, onMounted, useSlots } from 'vue';
|
|
7
|
+
import type { Component } from 'vue';
|
|
7
8
|
import { dataTableApiKey } from '../keys';
|
|
8
9
|
const column_internal_id = crypto.randomUUID();
|
|
9
10
|
defineSlots<{
|
|
10
11
|
// props para o slot body
|
|
11
12
|
body?: (props: { item: any }) => any,
|
|
12
|
-
col_header?: (props: { col: any, locked: boolean }) => any,
|
|
13
|
+
col_header?: (props: { col: any, locked: boolean, OrderingComponent: Component }) => any,
|
|
13
14
|
col_header_midle?: (props: { col: any, locked: boolean }) => any,
|
|
14
15
|
}>();
|
|
15
16
|
interface VColumnProps {
|
|
@@ -19,23 +19,41 @@
|
|
|
19
19
|
<slot name="fieldMiddle">
|
|
20
20
|
|
|
21
21
|
</slot>
|
|
22
|
+
|
|
22
23
|
<Teleport :to="options.search_teleport || 'body'" :disabled="!options.search_teleport">
|
|
23
|
-
<
|
|
24
|
-
:list_filter="options.list_filter" :item_use="item_use">
|
|
24
|
+
<div class="dropdown d-flex">
|
|
25
25
|
<Search v-if="!options.disable_search" v-model:search="pagination.search"
|
|
26
26
|
v-model:filter="pagination.filter" :list_filter="options.list_filter" :item_use="item_use"
|
|
27
27
|
@search="reSearch" :deactivate_search_on_clear="options.deactivate_search_on_clear"
|
|
28
28
|
:placeholder_search="options.placeholder_search"
|
|
29
29
|
:deactivate_search_empty="options.deactivate_search_empty"
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
:use_column_manager="options.use_column_manager" :toggleColumnVisibility="toggleColumnVisibility"
|
|
31
|
+
:columns_list="columns_list" @clicked-clear-search="$emit('clickedClearSearch')">
|
|
32
|
+
|
|
33
|
+
<template #search-field>
|
|
34
|
+
<slot name="search-field" :search="pagination.search" :filter="pagination.filter"
|
|
35
|
+
:reSearch="reSearch">
|
|
36
|
+
</slot>
|
|
37
|
+
</template>
|
|
38
|
+
</Search>
|
|
39
|
+
|
|
40
|
+
<VExtraActions :use_column_manager="options.use_column_manager" :columns_list="columns_list"
|
|
41
|
+
:toggleColumnVisibility="toggleColumnVisibility">
|
|
42
|
+
<template #extra-actions>
|
|
43
|
+
<slot name="extra-actions">
|
|
44
|
+
</slot>
|
|
45
|
+
</template>
|
|
46
|
+
</VExtraActions>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
32
49
|
</Teleport>
|
|
33
50
|
</div>
|
|
34
51
|
<slot name="item-selected-info" :selected_items="selected_items" :clearSelection="() => selected_items = []">
|
|
35
52
|
<div v-if="(options.use_checkbox && selected_items.length > 0) && !options.deactivate_selected_info"
|
|
36
53
|
class="alert alert-cyan d-flex justify-content-center align-items-center py-2" role="alert">
|
|
37
54
|
<h4 class="alert-title m-0"> <strong>oi Itens Selecionados:</strong> <span
|
|
38
|
-
class="badge bg-azure text-azure-fg">{{
|
|
55
|
+
class="badge bg-azure text-azure-fg">{{
|
|
56
|
+
selected_items.length }}</span></h4>
|
|
39
57
|
<a class=" cursor-pointer " @click="selected_items = []">
|
|
40
58
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
41
59
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
@@ -72,9 +90,8 @@
|
|
|
72
90
|
<table class="table table-vcenter table-selectable" :class="options.class_table">
|
|
73
91
|
<thead>
|
|
74
92
|
|
|
75
|
-
<draggable v-model="draggableColumns" tag="tr" item-key="id" :animation="400"
|
|
76
|
-
|
|
77
|
-
@end="() => onDragEnd()">
|
|
93
|
+
<draggable v-model="draggableColumns" tag="tr" item-key="id" :animation="400" ghost-class="ghost-item"
|
|
94
|
+
drag-class="dragging-item" @start="isDraggingColumns = true" @end="() => onDragEnd()">
|
|
78
95
|
|
|
79
96
|
<template #header>
|
|
80
97
|
<th v-if="options.use_expandable_items"></th>
|
|
@@ -247,6 +264,7 @@ import { readonly, ref, provide, computed, watch, nextTick, inject, onMounted }
|
|
|
247
264
|
import { DATA_TABLE_CONFIG } from '@/config/datatableConfig'
|
|
248
265
|
import PaginationDatatable from '@/Pagination/Pagination.vue';
|
|
249
266
|
import Search from './SearchDatatable.vue';
|
|
267
|
+
import VExtraActions from './VExtraActions.vue';
|
|
250
268
|
import { useImagePreview } from '../composables/useImagePreview';
|
|
251
269
|
import { dataTableApiKey, type ColumnConfiguration } from '../keys';
|
|
252
270
|
import draggable from 'vuedraggable';
|
|
@@ -256,6 +274,7 @@ import { useCheckBox } from '../composables/useCheckBox.ts';
|
|
|
256
274
|
import VDataTableLoading from './VDataTableLoading.vue';
|
|
257
275
|
import VThDataTable from './VThDataTable.vue';
|
|
258
276
|
|
|
277
|
+
|
|
259
278
|
const globalConfig = inject(DATA_TABLE_CONFIG, {});
|
|
260
279
|
const {
|
|
261
280
|
isHovering,
|
|
@@ -295,6 +314,8 @@ const props = withDefaults(defineProps<VDataTableProps>(), {
|
|
|
295
314
|
pagination_teleport: null,
|
|
296
315
|
search_teleport: null,
|
|
297
316
|
show_header_when_empty: false,
|
|
317
|
+
storage_id: '',
|
|
318
|
+
use_column_manager: false,
|
|
298
319
|
});
|
|
299
320
|
const options = computed(() => {
|
|
300
321
|
return {
|
|
@@ -334,6 +355,8 @@ const options = computed(() => {
|
|
|
334
355
|
retry_attempts: props.retry_attempts ?? globalConfig.retry_attempts ?? 3,
|
|
335
356
|
retry_delay: props.retry_delay ?? globalConfig.retry_delay ?? 2000,
|
|
336
357
|
show_header_when_empty: props.show_header_when_empty ?? globalConfig.show_header_when_empty ?? false,
|
|
358
|
+
storage_id: props.storage_id ?? globalConfig.storage_id ?? '',
|
|
359
|
+
use_column_manager: props.use_column_manager ?? globalConfig.use_column_manager ?? false,
|
|
337
360
|
};
|
|
338
361
|
});
|
|
339
362
|
const emit = defineEmits(['tradePage', 'beforeFetch', 'afterFetch', 'clickedClearSearch']);
|
|
@@ -388,6 +411,92 @@ const {
|
|
|
388
411
|
atLeastOneSelected, toggleSelectAll, toggleItemSelection
|
|
389
412
|
} = useCheckBox(options.value, items);
|
|
390
413
|
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
// Variável computada que forma uma string exclusiva para garantir que este cache não atropele o cache de outra tabela.
|
|
417
|
+
const storageKey = computed(() => {
|
|
418
|
+
if (!options.value.storage_id) return null;
|
|
419
|
+
|
|
420
|
+
return `vdatatable_${options.value.storage_id}_${options.value.endpoint}`;
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
/*
|
|
424
|
+
Recebe: Nada.
|
|
425
|
+
Devolve: Nada.
|
|
426
|
+
Por que é usada: Inspeciona cada coluna para gerar um ID confiável de cache. Se for incapaz de gerar IDs únicos sem conflitos, a aplicação é encerrada propositalmente.
|
|
427
|
+
*/
|
|
428
|
+
function validateAndAssignKeys(): void {
|
|
429
|
+
for (const col of columns.value) {
|
|
430
|
+
let key = '';
|
|
431
|
+
const fieldCount = columns.value.filter(c => c.field === col.field).length;
|
|
432
|
+
|
|
433
|
+
if (col.field && fieldCount === 1) {
|
|
434
|
+
key = col.field;
|
|
435
|
+
} else {
|
|
436
|
+
const headerCount = columns.value.filter(c => c.header === col.header).length;
|
|
437
|
+
if (col.header && headerCount === 1) {
|
|
438
|
+
key = col.header;
|
|
439
|
+
} else {
|
|
440
|
+
throw new Error(`[VDataTable] Falha de integridade: Impossível designar um ID único de armazenamento. O 'field' e 'header' estão duplicados ou ausentes. Header ofensor: ${col.header}`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
col.storage_key = key;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/*
|
|
448
|
+
Recebe: Nada.
|
|
449
|
+
Devolve: Nada.
|
|
450
|
+
Por que é usada: Converte o estado de visibilidade e a ordem das colunas para JSON e grava no navegador do usuário.
|
|
451
|
+
*/
|
|
452
|
+
function saveColumnsState(): void {
|
|
453
|
+
if (!storageKey.value) return;
|
|
454
|
+
const state = columns.value.map(col => ({
|
|
455
|
+
storage_key: col.storage_key,
|
|
456
|
+
visible: col.visible
|
|
457
|
+
}));
|
|
458
|
+
localStorage.setItem(storageKey.value, JSON.stringify(state));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/*
|
|
462
|
+
Recebe: Nada.
|
|
463
|
+
Devolve: Nada.
|
|
464
|
+
Por que é usada: Tenta recuperar o estado anterior da tabela. Caso detecte mudanças na estrutura das colunas, ele expurga o cache velho e impõe a nova realidade.
|
|
465
|
+
*/
|
|
466
|
+
function loadColumnsState(): void {
|
|
467
|
+
if (!storageKey.value) return;
|
|
468
|
+
const saved = localStorage.getItem(storageKey.value);
|
|
469
|
+
if (!saved) return;
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
const parsed = JSON.parse(saved);
|
|
473
|
+
const currentKeys = columns.value.map(c => c.storage_key).sort();
|
|
474
|
+
const savedKeys = parsed.map((p: any) => p.storage_key).sort();
|
|
475
|
+
|
|
476
|
+
if (JSON.stringify(currentKeys) !== JSON.stringify(savedKeys)) {
|
|
477
|
+
localStorage.removeItem(storageKey.value);
|
|
478
|
+
saveColumnsState();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const newColumnsOrder = [];
|
|
483
|
+
for (const savedCol of parsed) {
|
|
484
|
+
const found = columns.value.find(c => c.storage_key === savedCol.storage_key);
|
|
485
|
+
if (found) {
|
|
486
|
+
found.visible = savedCol.visible;
|
|
487
|
+
newColumnsOrder.push(found);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Atualiza o array in-place para não destruir a instância que o vuedraggable vigia
|
|
492
|
+
columns.value.splice(0, columns.value.length, ...newColumnsOrder);
|
|
493
|
+
|
|
494
|
+
} catch (e) {
|
|
495
|
+
localStorage.removeItem(storageKey.value);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
|
|
391
500
|
// =======================================================
|
|
392
501
|
// 4. PROPRIEDADES COMPUTADAS
|
|
393
502
|
// =======================================================
|
|
@@ -404,7 +513,9 @@ const draggableColumns = computed({
|
|
|
404
513
|
set(newUnlockedOrder) {
|
|
405
514
|
const locked = columns.value.filter(c => c.locked);
|
|
406
515
|
const hidden = columns.value.filter(c => !c.locked && c.visible === false);
|
|
407
|
-
|
|
516
|
+
// Usa splice para não quebrar a referência de reatividade do array original!
|
|
517
|
+
columns.value.splice(0, columns.value.length, ...newUnlockedOrder, ...hidden, ...locked);
|
|
518
|
+
saveColumnsState();
|
|
408
519
|
}
|
|
409
520
|
});
|
|
410
521
|
// colunas RENDERIZADAS (ordem final)
|
|
@@ -435,6 +546,7 @@ function toggleColumnVisibility(id: string): void {
|
|
|
435
546
|
const coluna = columns.value.find(c => c.id === id);
|
|
436
547
|
if (coluna) {
|
|
437
548
|
coluna.visible = !coluna.visible;
|
|
549
|
+
saveColumnsState();
|
|
438
550
|
}
|
|
439
551
|
}
|
|
440
552
|
|
|
@@ -470,7 +582,6 @@ watch(response, (newResponse: any) => {
|
|
|
470
582
|
}, { immediate: true });
|
|
471
583
|
|
|
472
584
|
|
|
473
|
-
|
|
474
585
|
// =======================================================
|
|
475
586
|
// 6. MÉTODOS
|
|
476
587
|
// =======================================================
|
|
@@ -635,14 +746,40 @@ onMounted(() => {
|
|
|
635
746
|
reSearch();
|
|
636
747
|
});
|
|
637
748
|
}
|
|
749
|
+
nextTick(() => {
|
|
750
|
+
if (options.value.storage_id) {
|
|
751
|
+
validateAndAssignKeys();
|
|
752
|
+
loadColumnsState();
|
|
753
|
+
}
|
|
754
|
+
});
|
|
638
755
|
});
|
|
639
756
|
|
|
640
757
|
watch(
|
|
641
|
-
() => options.value.add_params,
|
|
642
758
|
() => {
|
|
643
|
-
|
|
759
|
+
const params = typeof options.value.add_params === 'function'
|
|
760
|
+
? options.value.add_params()
|
|
761
|
+
: options.value.add_params;
|
|
762
|
+
return JSON.stringify(params);
|
|
644
763
|
},
|
|
645
|
-
|
|
764
|
+
(newVal, oldVal) => {
|
|
765
|
+
if (newVal !== oldVal && oldVal !== undefined) {
|
|
766
|
+
reSearch();
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
/*
|
|
771
|
+
========
|
|
772
|
+
Observa mudanças no endpoint.
|
|
773
|
+
Caso a rota da API mude dinamicamente, ele reseta a tabela para a página inicial com segurança.
|
|
774
|
+
========
|
|
775
|
+
*/
|
|
776
|
+
watch(
|
|
777
|
+
() => options.value.endpoint,
|
|
778
|
+
(newVal, oldVal) => {
|
|
779
|
+
if (newVal !== oldVal && oldVal !== undefined) {
|
|
780
|
+
pagination.value.current_page = options.value.page_starts_at;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
646
783
|
);
|
|
647
784
|
</script>
|
|
648
785
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
|
|
3
|
+
<div v-if="use_column_manager" class="dropdown d-inline-block ms-2">
|
|
4
|
+
<button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"
|
|
5
|
+
title="Gerenciar Colunas visíveis">
|
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-columns" width="24" height="24"
|
|
7
|
+
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
|
8
|
+
stroke-linejoin="round">
|
|
9
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
10
|
+
<path d="M4 6l5.5 0" />
|
|
11
|
+
<path d="M4 10l5.5 0" />
|
|
12
|
+
<path d="M4 14l5.5 0" />
|
|
13
|
+
<path d="M4 18l5.5 0" />
|
|
14
|
+
<path d="M14.5 6l5.5 0" />
|
|
15
|
+
<path d="M14.5 10l5.5 0" />
|
|
16
|
+
<path d="M14.5 14l5.5 0" />
|
|
17
|
+
<path d="M14.5 18l5.5 0" />
|
|
18
|
+
</svg>
|
|
19
|
+
</button>
|
|
20
|
+
<div class="dropdown-menu dropdown-menu-end p-3 shadow-sm" style="min-width: 220px; z-index: 1050;">
|
|
21
|
+
<h6 class="dropdown-header text-uppercase fw-bold mb-2">Exibir Colunas</h6>
|
|
22
|
+
<div class="d-flex flex-column gap-2">
|
|
23
|
+
<label v-for="coluna in columns_list" :key="coluna.id" class="form-check cursor-pointer m-0">
|
|
24
|
+
<input class="form-check-input" type="checkbox" :checked="coluna.visible" :disabled="coluna.locked"
|
|
25
|
+
@change="toggleColumnVisibility(coluna.id)">
|
|
26
|
+
<span class="form-check-label user-select-none">{{ coluna.header }}</span>
|
|
27
|
+
</label>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
interface SearchProps {
|
|
34
|
+
columns_list?: any[];
|
|
35
|
+
use_column_manager?: boolean;
|
|
36
|
+
toggleColumnVisibility?: (columnId: string) => void;
|
|
37
|
+
}
|
|
38
|
+
withDefaults(defineProps<SearchProps>(), {
|
|
39
|
+
columns_list: () => [],
|
|
40
|
+
use_column_manager: false,
|
|
41
|
+
toggleColumnVisibility: () => { }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
</script>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span v-if="use_ordering" @click="emit('toggle')" class="cursor-pointer">
|
|
3
|
+
<svg v-if="!props.orderings_state || props.orderings_state === 'none'" xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
5
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
6
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
7
|
+
<path d="m11 16-4 4-4-4"></path>
|
|
8
|
+
<path d="M7 4v16"></path>
|
|
9
|
+
<path d="M15 8h6"></path>
|
|
10
|
+
<path d="M15 16h6"></path>
|
|
11
|
+
<path d="M13 12h8"></path>
|
|
12
|
+
</svg>
|
|
13
|
+
|
|
14
|
+
<svg v-else-if="props.orderings_state === 'decreasing'" xmlns="http://www.w3.org/2000/svg" width="16"
|
|
15
|
+
height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
16
|
+
stroke-linejoin="round">
|
|
17
|
+
<path d="m3 16 4 4 4-4"></path>
|
|
18
|
+
<path d="M7 20V4"></path>
|
|
19
|
+
<path d="M11 4h10"></path>
|
|
20
|
+
<path d="M11 8h7"></path>
|
|
21
|
+
<path d="M11 12h4"></path>
|
|
22
|
+
</svg>
|
|
23
|
+
|
|
24
|
+
<svg v-else-if="props.orderings_state === 'increasing'" xmlns="http://www.w3.org/2000/svg" width="16"
|
|
25
|
+
height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
26
|
+
stroke-linejoin="round">
|
|
27
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
28
|
+
<path d="M7 4v16"></path>
|
|
29
|
+
<path d="M11 12h4"></path>
|
|
30
|
+
<path d="M11 16h7"></path>
|
|
31
|
+
<path d="M11 20h10"></path>
|
|
32
|
+
</svg>
|
|
33
|
+
</span>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
/*
|
|
38
|
+
========
|
|
39
|
+
Componente interno de uso exclusivo do VDataTable para renderizar o ícone de ordenação.
|
|
40
|
+
========
|
|
41
|
+
*/
|
|
42
|
+
const props = defineProps<{
|
|
43
|
+
orderings_state?: 'none' | 'increasing' | 'decreasing';
|
|
44
|
+
use_ordering?: boolean;
|
|
45
|
+
}>();
|
|
46
|
+
|
|
47
|
+
const emit = defineEmits<{
|
|
48
|
+
toggle: [void];
|
|
49
|
+
}>();
|
|
50
|
+
</script>
|
|
@@ -1,56 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<th :class="class_all">
|
|
3
3
|
<div :class="{ 'header-ordering': use_ordering }">
|
|
4
|
-
|
|
5
|
-
<component
|
|
6
|
-
v-if="hasCustomHeader"
|
|
7
|
-
:is="col.colHeaderSlot"
|
|
8
|
-
v-bind="{ col: props.col, locked: props.locked }"
|
|
9
|
-
></component>
|
|
10
|
-
|
|
11
|
-
<template v-else>
|
|
12
|
-
<span>
|
|
13
|
-
{{ props.header }}
|
|
14
|
-
</span>
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
<component v-if="hasCustomHeader" :is="col.colHeaderSlot"
|
|
6
|
+
v-bind="{ col: props.col, locked: props.locked, OrderingComponent }"></component>
|
|
7
|
+
|
|
8
|
+
<template v-else>
|
|
9
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
10
|
+
<span>
|
|
11
|
+
{{ props.header }}
|
|
12
|
+
</span>
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
25
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
26
|
-
<path d="m3 8 4-4 4 4"></path>
|
|
27
|
-
<path d="m11 16-4 4-4-4"></path>
|
|
28
|
-
<path d="M7 4v16"></path>
|
|
29
|
-
<path d="M15 8h6"></path>
|
|
30
|
-
<path d="M15 16h6"></path>
|
|
31
|
-
<path d="M13 12h8"></path>
|
|
32
|
-
</svg>
|
|
14
|
+
<component v-if="col.colHeaderMidleSlot" :is="col.colHeaderMidleSlot"
|
|
15
|
+
v-bind="{ col: props.col, locked: props.locked }"></component>
|
|
33
16
|
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
stroke-linejoin="round">
|
|
37
|
-
<path d="m3 16 4 4 4-4"></path>
|
|
38
|
-
<path d="M7 20V4"></path>
|
|
39
|
-
<path d="M11 4h10"></path>
|
|
40
|
-
<path d="M11 8h7"></path>
|
|
41
|
-
<path d="M11 12h4"></path>
|
|
42
|
-
</svg>
|
|
17
|
+
<component :is="OrderingComponent" />
|
|
18
|
+
</div>
|
|
43
19
|
|
|
44
|
-
<svg v-else-if="props.orderings_state === 'increasing'" xmlns="http://www.w3.org/2000/svg" width="16"
|
|
45
|
-
height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
46
|
-
stroke-linejoin="round">
|
|
47
|
-
<path d="m3 8 4-4 4 4"></path>
|
|
48
|
-
<path d="M7 4v16"></path>
|
|
49
|
-
<path d="M11 12h4"></path>
|
|
50
|
-
<path d="M11 16h7"></path>
|
|
51
|
-
<path d="M11 20h10"></path>
|
|
52
|
-
</svg>
|
|
53
|
-
</span>
|
|
54
20
|
</template>
|
|
55
21
|
|
|
56
22
|
</div>
|
|
@@ -58,8 +24,8 @@
|
|
|
58
24
|
</template>
|
|
59
25
|
|
|
60
26
|
<script setup lang="ts">
|
|
61
|
-
import { computed, Comment, Fragment, Text } from 'vue';
|
|
62
|
-
|
|
27
|
+
import { computed, Comment, Fragment, Text, h } from 'vue';
|
|
28
|
+
import VOrderingIcon from './VOrderingIcon.vue';
|
|
63
29
|
const props = defineProps<{
|
|
64
30
|
header: string;
|
|
65
31
|
class_column: string;
|
|
@@ -68,7 +34,11 @@ const props = defineProps<{
|
|
|
68
34
|
orderings_state: 'none' | 'increasing' | 'decreasing';
|
|
69
35
|
col: any;
|
|
70
36
|
}>();
|
|
71
|
-
|
|
37
|
+
const OrderingComponent = () => h(VOrderingIcon, {
|
|
38
|
+
orderings_state: props.orderings_state,
|
|
39
|
+
use_ordering: props.use_ordering,
|
|
40
|
+
onToggle: () => emit('toggleOrderingState')
|
|
41
|
+
});
|
|
72
42
|
const class_all = computed(() => {
|
|
73
43
|
return {
|
|
74
44
|
'header-draggable': !props.locked,
|
|
@@ -15,10 +15,9 @@ export function useDataTableFetch<T>(
|
|
|
15
15
|
const items = ref<T[]>([]) as Ref<T[]>;
|
|
16
16
|
|
|
17
17
|
const urlReativa = computed(() => {
|
|
18
|
-
pagination.value.current_page = props.page_starts_at;
|
|
19
18
|
return props.endpoint;
|
|
20
19
|
});
|
|
21
|
-
|
|
20
|
+
|
|
22
21
|
const default_params = computed<Record<string, any>>(() => ({
|
|
23
22
|
[props.page_param_name]: pagination.value.current_page + 1,
|
|
24
23
|
[props.page_size_param_name]: pagination.value.limit_per_page,
|
package/src/DatatableVue/keys.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface ColumnConfiguration {
|
|
|
26
26
|
// não é prop, é uma propriedade interna do VDataTable para controlar a visibilidade da coluna
|
|
27
27
|
visible?: boolean;
|
|
28
28
|
id: string; // ID único da coluna, gerado internamente pelo VDataTable
|
|
29
|
+
// Chave gerada dinamicamente para salvar e recuperar o estado da coluna no localstorage
|
|
30
|
+
storage_key?: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// A API que o VDataTable "fornece" para os filhos
|
|
@@ -78,6 +78,9 @@ export interface VDataTableProps {
|
|
|
78
78
|
pagination_teleport?: string | HTMLElement | null;
|
|
79
79
|
search_teleport?: string | HTMLElement | null;
|
|
80
80
|
show_header_when_empty?: boolean;
|
|
81
|
+
// Propriedade para ativar a persistência de configuração da tabela no localstorage
|
|
82
|
+
storage_id?: string;
|
|
83
|
+
use_column_manager?: boolean;
|
|
81
84
|
}
|
|
82
85
|
export type DataTablePropsWithDefaults = VDataTableProps & {
|
|
83
86
|
// Strings
|
|
@@ -129,6 +132,8 @@ export type DataTablePropsWithDefaults = VDataTableProps & {
|
|
|
129
132
|
add_params: Record<string, any> | (() => Record<string, any>);
|
|
130
133
|
list_filter: any[];
|
|
131
134
|
custom_loading: any | null;
|
|
135
|
+
storage_id: string;
|
|
136
|
+
use_column_manager: boolean;
|
|
132
137
|
};
|
|
133
138
|
export interface ExposedFunctions<T extends Record<string, any>> {
|
|
134
139
|
execute: () => void;
|