v-sistec-features 1.4.1 → 1.6.0
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "v-sistec-features",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"author": "Márlon Bento Azevedo (https://github.com/marlon-bento)",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@tabler/icons-vue": "^3.35.0",
|
|
48
|
-
"vue": "^3.2.0"
|
|
48
|
+
"vue": "^3.2.0",
|
|
49
|
+
"vuedraggable": "^4.1.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@semantic-release/changelog": "^6.0.3",
|
|
@@ -65,8 +66,8 @@
|
|
|
65
66
|
"vue-tsc": "^3.1.0"
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
69
|
+
"@formkit/drag-and-drop": "^0.5.3",
|
|
68
70
|
"@tabler/core": "^1.4.0",
|
|
69
|
-
"v3-infinite-loading": "^1.3.2"
|
|
70
|
-
"vue": "^3.2.0"
|
|
71
|
+
"v3-infinite-loading": "^1.3.2"
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -18,6 +18,12 @@ interface VColumnProps {
|
|
|
18
18
|
limite_text?: number | string | null;
|
|
19
19
|
transform_function?: ((value: any) => any) | null;
|
|
20
20
|
click?: Function | null;
|
|
21
|
+
// bloqueia a coluna para não ser movida
|
|
22
|
+
locked?: boolean;
|
|
23
|
+
use_ordering?: boolean;
|
|
24
|
+
param_ordering?: string;
|
|
25
|
+
decreasing_value?: string;
|
|
26
|
+
increasing_value?: string;
|
|
21
27
|
}
|
|
22
28
|
const props = withDefaults(defineProps<VColumnProps>(), {
|
|
23
29
|
field: null,
|
|
@@ -43,6 +49,12 @@ const props = withDefaults(defineProps<VColumnProps>(), {
|
|
|
43
49
|
/* recebe função para alterar o que é mostrado */
|
|
44
50
|
transform_function: null ,
|
|
45
51
|
click: null,
|
|
52
|
+
locked: false,
|
|
53
|
+
|
|
54
|
+
use_ordering: false,
|
|
55
|
+
param_ordering: '',
|
|
56
|
+
decreasing_value: '',
|
|
57
|
+
increasing_value: '',
|
|
46
58
|
});
|
|
47
59
|
|
|
48
60
|
const slots = useSlots();
|
|
@@ -87,6 +99,11 @@ onMounted(() => {
|
|
|
87
99
|
class_item: props.class_item,
|
|
88
100
|
click: props.click,
|
|
89
101
|
transform_function: props.transform_function,
|
|
102
|
+
locked: props.locked,
|
|
103
|
+
use_ordering: props.use_ordering,
|
|
104
|
+
param_ordering: props.param_ordering,
|
|
105
|
+
decreasing_value: props.decreasing_value,
|
|
106
|
+
increasing_value: props.increasing_value,
|
|
90
107
|
|
|
91
108
|
bodySlot: slots.body,
|
|
92
109
|
...(props.type === 'text' && { limite_text: Number(props.limite_text) }),
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
<div v-if="items.length > 0">
|
|
137
137
|
<table class="table table-vcenter table-selectable" :class="props.class_table">
|
|
138
138
|
<thead>
|
|
139
|
-
<tr>
|
|
139
|
+
<!-- <tr>
|
|
140
140
|
<th v-if="props.use_checkbox" class="w-1">
|
|
141
141
|
<input class="form-check-input m-0" type="checkbox" ref="selectAllCheckbox"
|
|
142
142
|
@change="toggleSelectAll" aria-label="Selecionar todos os itens na página" />
|
|
@@ -144,15 +144,134 @@
|
|
|
144
144
|
<th v-for="col in columns" :key="col.field || col.header" :class="col.class_column">
|
|
145
145
|
{{ col.header }}
|
|
146
146
|
</th>
|
|
147
|
-
</tr>
|
|
147
|
+
</tr> -->
|
|
148
|
+
|
|
149
|
+
<draggable v-model="draggableColumns" tag="tr" item-key="header" :animation="400"
|
|
150
|
+
ghost-class="ghost-item" drag-class="dragging-item">
|
|
151
|
+
<template #header>
|
|
152
|
+
<th v-if="props.use_checkbox" class="w-1">
|
|
153
|
+
<input class="form-check-input m-0" type="checkbox" ref="selectAllCheckbox"
|
|
154
|
+
@change="toggleSelectAll" aria-label="Selecionar todos os itens na página" />
|
|
155
|
+
</th>
|
|
156
|
+
</template>
|
|
157
|
+
|
|
158
|
+
<template #item="{ element: col }">
|
|
159
|
+
<template v-if="col.use_ordering">
|
|
160
|
+
<th class="header-draggable" :class="col.class_column">
|
|
161
|
+
<div class="header-ordering">
|
|
162
|
+
<span>{{ col.header }}</span>
|
|
163
|
+
|
|
164
|
+
<span @click="() => toggleOrderingState(col.header)" class="ms-2 cursor-pointer">
|
|
165
|
+
<svg v-if="!orderings_state[col.header] || orderings_state[col.header] === 'none'"
|
|
166
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
|
|
167
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
168
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
169
|
+
<path d="m11 16-4 4-4-4"></path>
|
|
170
|
+
<path d="M7 4v16"></path>
|
|
171
|
+
<path d="M15 8h6"></path>
|
|
172
|
+
<path d="M15 16h6"></path>
|
|
173
|
+
<path d="M13 12h8"></path>
|
|
174
|
+
</svg>
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
<svg v-else-if="orderings_state[col.header] === 'decreasing'"
|
|
178
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
|
|
179
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
180
|
+
<path d="m3 16 4 4 4-4"></path>
|
|
181
|
+
<path d="M7 20V4"></path>
|
|
182
|
+
<path d="M11 4h10"></path>
|
|
183
|
+
<path d="M11 8h7"></path>
|
|
184
|
+
<path d="M11 12h4"></path>
|
|
185
|
+
</svg>
|
|
186
|
+
|
|
187
|
+
<svg v-else-if="orderings_state[col.header] === 'increasing'"
|
|
188
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
|
|
189
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
190
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
191
|
+
<path d="M7 4v16"></path>
|
|
192
|
+
<path d="M11 12h4"></path>
|
|
193
|
+
<path d="M11 16h7"></path>
|
|
194
|
+
<path d="M11 20h10"></path>
|
|
195
|
+
</svg>
|
|
196
|
+
|
|
197
|
+
</span>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
</th>
|
|
201
|
+
</template>
|
|
202
|
+
<template v-else>
|
|
203
|
+
<th class="header-draggable" :class="col.class_column">
|
|
204
|
+
{{ col.header }}
|
|
205
|
+
</th>
|
|
206
|
+
</template>
|
|
207
|
+
|
|
208
|
+
</template>
|
|
209
|
+
|
|
210
|
+
<template #footer>
|
|
211
|
+
<template v-for="col in lockedColumns" :key="col.field || col.header">
|
|
212
|
+
<template v-if="col.use_ordering">
|
|
213
|
+
<th class="header-locked header-ordering" :class="col.class_column">
|
|
214
|
+
<div class="header-ordering">
|
|
215
|
+
<span>{{ col.header }}</span>
|
|
216
|
+
|
|
217
|
+
<span @click="() => toggleOrderingState(col.header)" class="ms-2 cursor-pointer">
|
|
218
|
+
<svg v-if="!orderings_state[col.header] || orderings_state[col.header] === 'none'"
|
|
219
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
|
|
220
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
221
|
+
stroke-linejoin="round">
|
|
222
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
223
|
+
<path d="m11 16-4 4-4-4"></path>
|
|
224
|
+
<path d="M7 4v16"></path>
|
|
225
|
+
<path d="M15 8h6"></path>
|
|
226
|
+
<path d="M15 16h6"></path>
|
|
227
|
+
<path d="M13 12h8"></path>
|
|
228
|
+
</svg>
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
<svg v-else-if="orderings_state[col.header] === 'decreasing'"
|
|
232
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
|
|
233
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
234
|
+
stroke-linejoin="round">
|
|
235
|
+
<path d="m3 16 4 4 4-4"></path>
|
|
236
|
+
<path d="M7 20V4"></path>
|
|
237
|
+
<path d="M11 4h10"></path>
|
|
238
|
+
<path d="M11 8h7"></path>
|
|
239
|
+
<path d="M11 12h4"></path>
|
|
240
|
+
</svg>
|
|
241
|
+
|
|
242
|
+
<svg v-else-if="orderings_state[col.header] === 'increasing'"
|
|
243
|
+
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
|
|
244
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
245
|
+
stroke-linejoin="round">
|
|
246
|
+
<path d="m3 8 4-4 4 4"></path>
|
|
247
|
+
<path d="M7 4v16"></path>
|
|
248
|
+
<path d="M11 12h4"></path>
|
|
249
|
+
<path d="M11 16h7"></path>
|
|
250
|
+
<path d="M11 20h10"></path>
|
|
251
|
+
</svg>
|
|
252
|
+
</span>
|
|
253
|
+
</div>
|
|
254
|
+
</th>
|
|
255
|
+
</template>
|
|
256
|
+
|
|
257
|
+
<template v-else>
|
|
258
|
+
<th class="header-locked" :class="col.class_column">
|
|
259
|
+
{{ col.header }}
|
|
260
|
+
</th>
|
|
261
|
+
</template>
|
|
262
|
+
|
|
263
|
+
</template>
|
|
264
|
+
</template>
|
|
265
|
+
</draggable>
|
|
266
|
+
|
|
148
267
|
</thead>
|
|
149
268
|
<tbody>
|
|
150
|
-
<tr v-for="item in items" :key="item[props.item_key]">
|
|
269
|
+
<TransitionGroup tag="tr" v-for="item in items" :key="item[props.item_key]" name="column-move">
|
|
151
270
|
<td v-if="props.use_checkbox" class="w-1">
|
|
152
271
|
<input class="form-check-input m-0" type="checkbox" :checked="isSelected(item)"
|
|
153
272
|
@change="toggleItemSelection(item)" aria-label="Selecionar este item" />
|
|
154
273
|
</td>
|
|
155
|
-
<td v-for="col in
|
|
274
|
+
<td v-for="col in renderedColumns" :key="col.field || col.header" :class="col.class_row">
|
|
156
275
|
<component v-if="col.bodySlot" :is="col.bodySlot" :item="item" :is-selected="isSelected(item)" />
|
|
157
276
|
<span @click="col.click ? col.click(item) : null"
|
|
158
277
|
:class="col.class_item + (col.click ? ' cursor-pointer' : '')" v-else-if="col.type === 'text'">
|
|
@@ -163,10 +282,10 @@
|
|
|
163
282
|
<span @click="col.click ? col.click(item) : null" v-else-if="col.type === 'date'"
|
|
164
283
|
:class="col.class_item + (col.click ? ' cursor-pointer' : '')">
|
|
165
284
|
<span v-if="col.format === 'complete'">{{ new Date(getSubItem(col.field, item)).toLocaleString()
|
|
166
|
-
|
|
285
|
+
}}</span>
|
|
167
286
|
<span v-if="col.format === 'simple'"> {{ new Date(getSubItem(col.field,
|
|
168
287
|
item)).toLocaleDateString()
|
|
169
|
-
|
|
288
|
+
}} </span>
|
|
170
289
|
</span>
|
|
171
290
|
<div @click="col.click ? col.click(item) : null"
|
|
172
291
|
:class="col.class_item + (col.click ? ' cursor-pointer' : '')" v-else-if="col.type === 'html'"
|
|
@@ -195,7 +314,7 @@
|
|
|
195
314
|
<span class="text-danger erro-custom-container" v-else>tipo <span
|
|
196
315
|
class="badge bg-orange text-white erro-custom-text">{{ col.type }}</span> não suportado</span>
|
|
197
316
|
</td>
|
|
198
|
-
</
|
|
317
|
+
</TransitionGroup>
|
|
199
318
|
</tbody>
|
|
200
319
|
</table>
|
|
201
320
|
</div>
|
|
@@ -203,10 +322,7 @@
|
|
|
203
322
|
<p class="m-0">Nenhum item encontrado.</p>
|
|
204
323
|
</div>
|
|
205
324
|
</div>
|
|
206
|
-
|
|
207
325
|
</div>
|
|
208
|
-
|
|
209
|
-
|
|
210
326
|
</div>
|
|
211
327
|
<slot name="pagination" :pagination="pagination" :tradePage="fetchDataWithDelay" :error="error">
|
|
212
328
|
<div v-if="!error && pagination.count > 0" class="px-3" :class="props.class_pagination">
|
|
@@ -228,6 +344,7 @@ import PaginationDatatable from './PaginationDatatable.vue';
|
|
|
228
344
|
import Search from './SearchDatatable.vue';
|
|
229
345
|
import { useImagePreview } from '../composables/useImagePreview';
|
|
230
346
|
import { dataTableApiKey, type ColumnConfiguration, type PaginationObject } from '../keys';
|
|
347
|
+
import draggable from 'vuedraggable';
|
|
231
348
|
|
|
232
349
|
const {
|
|
233
350
|
isHovering,
|
|
@@ -344,6 +461,7 @@ const props = withDefaults(defineProps<VDataTableProps>(), {
|
|
|
344
461
|
// =======================================================
|
|
345
462
|
|
|
346
463
|
|
|
464
|
+
const orderings_state = ref<Record<string, 'none' | 'increasing' | 'decreasing'>>({});
|
|
347
465
|
const columns = ref<ColumnConfiguration[]>([]);
|
|
348
466
|
const items = ref<T[]>([]) as Ref<T[]>;
|
|
349
467
|
const totalItems = ref<number>(0);
|
|
@@ -367,24 +485,29 @@ const pagination = ref<PaginationObject>({
|
|
|
367
485
|
// =======================================================
|
|
368
486
|
const { data: response, pending, error, execute, attempt } = props.fetch(props.endpoint, {
|
|
369
487
|
params: () => {
|
|
370
|
-
|
|
371
488
|
if (props.deactivate_default_params) {
|
|
372
489
|
if (props.add_params && typeof props.add_params === 'function') {
|
|
373
|
-
return
|
|
490
|
+
return {
|
|
491
|
+
...props.add_params(),
|
|
492
|
+
...params_ordering.value
|
|
493
|
+
};
|
|
374
494
|
}
|
|
375
495
|
return {
|
|
376
496
|
...props.add_params,
|
|
497
|
+
...params_ordering.value
|
|
377
498
|
};
|
|
378
499
|
}
|
|
379
500
|
else if (props.add_params && typeof props.add_params === 'function') {
|
|
380
501
|
return {
|
|
381
502
|
...default_params.value,
|
|
382
503
|
...props.add_params(),
|
|
504
|
+
...params_ordering.value
|
|
383
505
|
}
|
|
384
506
|
}
|
|
385
507
|
return {
|
|
386
508
|
...default_params.value,
|
|
387
509
|
...props.add_params,
|
|
510
|
+
...params_ordering.value
|
|
388
511
|
};
|
|
389
512
|
},
|
|
390
513
|
retry: props.retry_attempts,
|
|
@@ -396,6 +519,26 @@ const { data: response, pending, error, execute, attempt } = props.fetch(props.e
|
|
|
396
519
|
// =======================================================
|
|
397
520
|
// 4. PROPRIEDADES COMPUTADAS
|
|
398
521
|
// =======================================================
|
|
522
|
+
|
|
523
|
+
// colunas TRAVADAS (apenas leitura)
|
|
524
|
+
const lockedColumns = computed(() =>
|
|
525
|
+
columns.value.filter(c => c.locked)
|
|
526
|
+
);
|
|
527
|
+
// 'v-model' para as colunas ARRASTÁVEIS (com get/set)
|
|
528
|
+
const draggableColumns = computed({
|
|
529
|
+
get() {
|
|
530
|
+
return columns.value.filter(c => !c.locked);
|
|
531
|
+
},
|
|
532
|
+
set(newUnlockedOrder) {
|
|
533
|
+
const locked = lockedColumns.value;
|
|
534
|
+
columns.value = [...newUnlockedOrder, ...locked];
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
// colunas RENDERIZADAS (ordem final)
|
|
538
|
+
const renderedColumns = computed(() => {
|
|
539
|
+
return [...draggableColumns.value, ...lockedColumns.value];
|
|
540
|
+
});
|
|
541
|
+
|
|
399
542
|
const item_use = computed<number[]>(() => {
|
|
400
543
|
let use = [1]
|
|
401
544
|
if (props.list_filter.length > 0) {
|
|
@@ -403,6 +546,24 @@ const item_use = computed<number[]>(() => {
|
|
|
403
546
|
}
|
|
404
547
|
return use;
|
|
405
548
|
});
|
|
549
|
+
const params_ordering = computed(() => {
|
|
550
|
+
const objectOrdering: Record<string, any> = {};
|
|
551
|
+
for (const col of columns.value) {
|
|
552
|
+
if (col.use_ordering) {
|
|
553
|
+
if (orderings_state.value[col.header] === 'increasing') {
|
|
554
|
+
objectOrdering[col.param_ordering] = col.increasing_value || 'increasing';
|
|
555
|
+
} else if (orderings_state.value[col.header] === 'decreasing') {
|
|
556
|
+
objectOrdering[col.param_ordering] = col.decreasing_value || 'decreasing';
|
|
557
|
+
} else {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return objectOrdering;
|
|
566
|
+
});
|
|
406
567
|
|
|
407
568
|
const default_params = computed<Record<string, any>>(() => ({
|
|
408
569
|
[props.page_param_name]: pagination.value.current_page + 1,
|
|
@@ -574,6 +735,27 @@ function limiteText(text: string | null, limite: number | null): string | null {
|
|
|
574
735
|
return text;
|
|
575
736
|
}
|
|
576
737
|
|
|
738
|
+
function toggleOrderingState(header: string) {
|
|
739
|
+
// desabilita todos que não são o header clicado
|
|
740
|
+
for (const key in orderings_state.value) {
|
|
741
|
+
if (key !== header) {
|
|
742
|
+
orderings_state.value[key] = 'none';
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const currentState = orderings_state.value[header] || 'none';
|
|
747
|
+
if (currentState === 'none') {
|
|
748
|
+
orderings_state.value[header] = 'increasing';
|
|
749
|
+
} else if (currentState === 'increasing') {
|
|
750
|
+
orderings_state.value[header] = 'decreasing';
|
|
751
|
+
} else {
|
|
752
|
+
orderings_state.value[header] = 'none';
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
reSearch();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
|
|
577
759
|
// =======================================================
|
|
578
760
|
// 7. EXPOSE E CICLO DE VIDA
|
|
579
761
|
// =======================================================
|
|
@@ -766,4 +948,48 @@ $max-width-preview: 250px;
|
|
|
766
948
|
[data-bs-theme=dark] .form-check-input {
|
|
767
949
|
border-color: rgba(255, 255, 255, 0.374) !important;
|
|
768
950
|
}
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
/*
|
|
955
|
+
Estilos para arrastar e soltar colunas
|
|
956
|
+
*/
|
|
957
|
+
|
|
958
|
+
.ghost-item {
|
|
959
|
+
opacity: 0.5;
|
|
960
|
+
background: var(--tblr-primary-lt, #e6f0ff);
|
|
961
|
+
border-radius: 8px;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
.dragging-item {
|
|
965
|
+
cursor: grabbing;
|
|
966
|
+
background: var(--tblr-primary);
|
|
967
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
968
|
+
opacity: 0.5;
|
|
969
|
+
background-color: var(--tblr-primary-bg-subtle);
|
|
970
|
+
filter: grayscale(0) invert(0);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.header-draggable {
|
|
974
|
+
cursor: grab;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
/*
|
|
979
|
+
Animações para movimentação de colunas
|
|
980
|
+
*/
|
|
981
|
+
.column-move-move {
|
|
982
|
+
transition: transform 0.4s ease;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.column-move-enter-active,
|
|
986
|
+
.column-move-leave-active {
|
|
987
|
+
transition: all 0.4s ease;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
.header-ordering {
|
|
992
|
+
display: flex;
|
|
993
|
+
justify-content: space-between;
|
|
994
|
+
}
|
|
769
995
|
</style>
|
package/src/DatatableVue/keys.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface ColumnConfiguration {
|
|
|
14
14
|
deactivate_img_preview?: boolean;
|
|
15
15
|
format?: 'complete' | 'simple';
|
|
16
16
|
click: Function | null;
|
|
17
|
+
locked: boolean;
|
|
18
|
+
use_ordering: boolean;
|
|
19
|
+
param_ordering: string;
|
|
20
|
+
decreasing_value: string;
|
|
21
|
+
increasing_value: string;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
// A API que o VDataTable "fornece" para os filhos
|