innoboxrr-vue-datatable 1.3.4 → 2.0.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/README.md +198 -198
- package/index.js +3 -3
- package/package.json +59 -25
- package/src/DataTable.vue +426 -389
- package/src/components/DataTableComponent.vue +183 -175
- package/src/components/DisabledLinkComponent.vue +39 -49
- package/src/components/IconLinkComponent.vue +65 -79
- package/src/components/IconRouteComponent.vue +71 -85
- package/src/components/NavDropdownComponent.vue +55 -62
- package/src/components/PaginationComponent.vue +79 -110
- package/src/components/SelectPaginationComponent.vue +110 -142
package/src/DataTable.vue
CHANGED
|
@@ -1,389 +1,426 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<div v-if="showTopbar">
|
|
4
|
-
<div class="
|
|
5
|
-
<div
|
|
6
|
-
<div class="
|
|
7
|
-
<button
|
|
8
|
-
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
|
|
9
|
-
@click="actionButtonClicked(crudActions)">
|
|
10
|
-
Acciones
|
|
11
|
-
</button>
|
|
12
|
-
<
|
|
13
|
-
<li v-for="action in crudActions" :key="action.id">
|
|
14
|
-
<template v-if="action.route">
|
|
15
|
-
<
|
|
16
|
-
v-if="action.policy"
|
|
17
|
-
:name="action.params.to.name"
|
|
18
|
-
:params="{...action.params.to.params, ...extraParams}"
|
|
19
|
-
:query="action.params.to.query ? {...action.params.to.query, ...extraQuery} : {...extraQuery}"
|
|
20
|
-
:icon="action.icon"
|
|
21
|
-
:text="action.name" />
|
|
22
|
-
<
|
|
23
|
-
v-else
|
|
24
|
-
:icon="action.icon"
|
|
25
|
-
:text="action.name"/>
|
|
26
|
-
</template>
|
|
27
|
-
<template v-else>
|
|
28
|
-
<
|
|
29
|
-
v-if="action.policy"
|
|
30
|
-
:icon="action.icon"
|
|
31
|
-
:text="action.name"
|
|
32
|
-
@click="actionClicked(action) , closeDropdown($event)" />
|
|
33
|
-
<
|
|
34
|
-
v-else
|
|
35
|
-
:icon="action.icon"
|
|
36
|
-
:text="action.name"/>
|
|
37
|
-
</template>
|
|
38
|
-
</li>
|
|
39
|
-
</
|
|
40
|
-
</div>
|
|
41
|
-
<div v-else>
|
|
42
|
-
<div class="
|
|
43
|
-
</div>
|
|
44
|
-
<div v-if="hasFilter" class="
|
|
45
|
-
<div class="
|
|
46
|
-
<div>
|
|
47
|
-
<span
|
|
48
|
-
class="
|
|
49
|
-
:uk-tooltip="`title: ${'Update results'}`"
|
|
50
|
-
@click="updateFilters">
|
|
51
|
-
<svg class="w-6 h-6 text-slate-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
|
|
52
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
|
|
53
|
-
</svg>
|
|
54
|
-
</span>
|
|
55
|
-
</div>
|
|
56
|
-
<div>
|
|
57
|
-
<span
|
|
58
|
-
class="
|
|
59
|
-
uk-toggle="target: .filter-form; animation: uk-animation-scale-up;"
|
|
60
|
-
uk-tooltip="title: Buscar">
|
|
61
|
-
<svg class="w-6 h-6 text-slate-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
|
62
|
-
<path d="M18.85 1.1A1.99 1.99 0 0 0 17.063 0H2.937a2 2 0 0 0-1.566 3.242L6.99 9.868 7 14a1 1 0 0 0 .4.8l4 3A1 1 0 0 0 13 17l.01-7.134 5.66-6.676a1.99 1.99 0 0 0 .18-2.09Z"/>
|
|
63
|
-
</svg>
|
|
64
|
-
</span>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
<div
|
|
71
|
-
v-if="hasFilter"
|
|
72
|
-
class="filter-form
|
|
73
|
-
hidden>
|
|
74
|
-
<slot name="filterForm"></slot>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
<div
|
|
78
|
-
class="
|
|
79
|
-
:class="{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
:
|
|
89
|
-
:data-table="
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
import
|
|
109
|
-
import
|
|
110
|
-
import
|
|
111
|
-
import
|
|
112
|
-
import
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="showTopbar">
|
|
4
|
+
<div class="fe-container fe-container-wide pt-4">
|
|
5
|
+
<div fe-grid>
|
|
6
|
+
<div class="fe-w-expand" v-if="hasActions">
|
|
7
|
+
<button
|
|
8
|
+
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
|
|
9
|
+
@click="actionButtonClicked(crudActions)">
|
|
10
|
+
Acciones
|
|
11
|
+
</button>
|
|
12
|
+
<NavDropdownComponent id="actionCrudDropdown" pos="right">
|
|
13
|
+
<li v-for="action in crudActions" :key="action.id">
|
|
14
|
+
<template v-if="action.route">
|
|
15
|
+
<IconRouteComponent
|
|
16
|
+
v-if="action.policy"
|
|
17
|
+
:name="action.params.to.name"
|
|
18
|
+
:params="{...action.params.to.params, ...extraParams}"
|
|
19
|
+
:query="action.params.to.query ? {...action.params.to.query, ...extraQuery} : {...extraQuery}"
|
|
20
|
+
:icon="action.icon"
|
|
21
|
+
:text="action.name" />
|
|
22
|
+
<DisabledLinkComponent
|
|
23
|
+
v-else
|
|
24
|
+
:icon="action.icon"
|
|
25
|
+
:text="action.name"/>
|
|
26
|
+
</template>
|
|
27
|
+
<template v-else>
|
|
28
|
+
<IconLinkComponent
|
|
29
|
+
v-if="action.policy"
|
|
30
|
+
:icon="action.icon"
|
|
31
|
+
:text="action.name"
|
|
32
|
+
@click="actionClicked(action) , closeDropdown($event)" />
|
|
33
|
+
<DisabledLinkComponent
|
|
34
|
+
v-else
|
|
35
|
+
:icon="action.icon"
|
|
36
|
+
:text="action.name"/>
|
|
37
|
+
</template>
|
|
38
|
+
</li>
|
|
39
|
+
</NavDropdownComponent>
|
|
40
|
+
</div>
|
|
41
|
+
<div v-else>
|
|
42
|
+
<div class="fe-w-expand"></div>
|
|
43
|
+
</div>
|
|
44
|
+
<div v-if="hasFilter" class="fe-w-auto">
|
|
45
|
+
<div class="fe-grid-divider fe-children-expand fe-text-center" fe-grid>
|
|
46
|
+
<div>
|
|
47
|
+
<span
|
|
48
|
+
class="fe-text-right pointer"
|
|
49
|
+
:uk-tooltip="`title: ${'Update results'}`"
|
|
50
|
+
@click="updateFilters">
|
|
51
|
+
<svg class="w-6 h-6 text-slate-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
|
|
52
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
|
|
53
|
+
</svg>
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<span
|
|
58
|
+
class="fe-text-right pointer"
|
|
59
|
+
uk-toggle="target: .filter-form; animation: uk-animation-scale-up;"
|
|
60
|
+
uk-tooltip="title: Buscar">
|
|
61
|
+
<svg class="w-6 h-6 text-slate-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
|
62
|
+
<path d="M18.85 1.1A1.99 1.99 0 0 0 17.063 0H2.937a2 2 0 0 0-1.566 3.242L6.99 9.868 7 14a1 1 0 0 0 .4.8l4 3A1 1 0 0 0 13 17l.01-7.134 5.66-6.676a1.99 1.99 0 0 0 .18-2.09Z"/>
|
|
63
|
+
</svg>
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
v-if="hasFilter"
|
|
72
|
+
class="filter-form fe-card fe-card-body fe-pt-0"
|
|
73
|
+
hidden>
|
|
74
|
+
<slot name="filterForm"></slot>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
<div
|
|
78
|
+
class="fe-container fe-container-wide"
|
|
79
|
+
:class="{ 'ptb-20': showTopbar }">
|
|
80
|
+
<div
|
|
81
|
+
class="fe-p-sm"
|
|
82
|
+
:class="{ 'bg-white p-6 rounded-lg shadow dark:border-slate-700 dark:bg-slate-800': cardWrapper }">
|
|
83
|
+
<DataTableComponent
|
|
84
|
+
:actions="hasActions"
|
|
85
|
+
:data-table="dataTable"
|
|
86
|
+
:extra-params="extraParams"
|
|
87
|
+
:extra-query="extraQuery"
|
|
88
|
+
:show-table-header="showTableHeader"
|
|
89
|
+
:data-table-components="dataTableComponents"
|
|
90
|
+
@sortColumn="sortColumn"
|
|
91
|
+
@actionButtonClicked="actionButtonClicked"
|
|
92
|
+
@actionClicked="actionClicked" />
|
|
93
|
+
<SelectPaginationComponent
|
|
94
|
+
:meta="pagination.meta"
|
|
95
|
+
:links="pagination.links"
|
|
96
|
+
@updatePage="updatePage" />
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<script setup>
|
|
103
|
+
|
|
104
|
+
import { markRaw, onMounted, reactive, ref, watch } from 'vue'
|
|
105
|
+
import axios from 'axios'
|
|
106
|
+
|
|
107
|
+
import NavDropdownComponent from './components/NavDropdownComponent.vue'
|
|
108
|
+
import IconRouteComponent from './components/IconRouteComponent.vue'
|
|
109
|
+
import IconLinkComponent from './components/IconLinkComponent.vue'
|
|
110
|
+
import DisabledLinkComponent from './components/DisabledLinkComponent.vue'
|
|
111
|
+
import DataTableComponent from './components/DataTableComponent.vue'
|
|
112
|
+
import SelectPaginationComponent from './components/SelectPaginationComponent.vue'
|
|
113
|
+
|
|
114
|
+
const props = defineProps({
|
|
115
|
+
dataUrl: {
|
|
116
|
+
type: String,
|
|
117
|
+
required: true
|
|
118
|
+
},
|
|
119
|
+
dataMethod: {
|
|
120
|
+
type: String,
|
|
121
|
+
default: 'post',
|
|
122
|
+
},
|
|
123
|
+
model: {
|
|
124
|
+
type: Object,
|
|
125
|
+
required: true
|
|
126
|
+
},
|
|
127
|
+
policyUrl: {
|
|
128
|
+
type: String,
|
|
129
|
+
required: true
|
|
130
|
+
},
|
|
131
|
+
policyMethod: {
|
|
132
|
+
type: String,
|
|
133
|
+
default: 'post'
|
|
134
|
+
},
|
|
135
|
+
showTopbar:{
|
|
136
|
+
type: Boolean,
|
|
137
|
+
default: true
|
|
138
|
+
},
|
|
139
|
+
hasActions: {
|
|
140
|
+
type: Boolean,
|
|
141
|
+
default: true,
|
|
142
|
+
},
|
|
143
|
+
hasFilter: {
|
|
144
|
+
type: Boolean,
|
|
145
|
+
default: true,
|
|
146
|
+
},
|
|
147
|
+
// Vue 3 exige factoria en los defaults de objeto y array.
|
|
148
|
+
formFilters: {
|
|
149
|
+
type: Object,
|
|
150
|
+
default: () => ({})
|
|
151
|
+
},
|
|
152
|
+
externalFilters: {
|
|
153
|
+
type: Object,
|
|
154
|
+
default: () => ({})
|
|
155
|
+
},
|
|
156
|
+
extraParams: {
|
|
157
|
+
type: Object,
|
|
158
|
+
default: () => ({})
|
|
159
|
+
},
|
|
160
|
+
extraQuery: {
|
|
161
|
+
type: Object,
|
|
162
|
+
default: () => ({})
|
|
163
|
+
},
|
|
164
|
+
hideColumns: {
|
|
165
|
+
type: Array,
|
|
166
|
+
default: () => []
|
|
167
|
+
},
|
|
168
|
+
cardWrapper: {
|
|
169
|
+
type: Boolean,
|
|
170
|
+
default: true
|
|
171
|
+
},
|
|
172
|
+
showTableHeader: {
|
|
173
|
+
type: Boolean,
|
|
174
|
+
default: true,
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Se leia de la global `csrf_token`, que la aplicacion anfitriona tenia
|
|
180
|
+
* que definir en window: el componente no se podia montar fuera de ella.
|
|
181
|
+
*/
|
|
182
|
+
const csrfToken = () => globalThis.csrf_token
|
|
183
|
+
?? document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
|
|
184
|
+
?? ''
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sustituye a `_.isEqual` de lodash, que se usaba como global sin
|
|
188
|
+
* declararla como dependencia.
|
|
189
|
+
*/
|
|
190
|
+
const isEqual = (a, b) => {
|
|
191
|
+
|
|
192
|
+
if (a === b) {
|
|
193
|
+
return true
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const keysA = Object.keys(a)
|
|
201
|
+
const keysB = Object.keys(b)
|
|
202
|
+
|
|
203
|
+
return keysA.length === keysB.length && keysA.every((key) => isEqual(a[key], b[key]))
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const hiddenColumnIds = () => props.hideColumns.map(
|
|
208
|
+
// Se admite tanto ['name'] como [{ id: 'name' }]: el contrato nunca
|
|
209
|
+
// estuvo documentado y por ahi circulan las dos formas.
|
|
210
|
+
(column) => typeof column === 'string' ? column : column?.id
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
const buildHead = () => {
|
|
214
|
+
|
|
215
|
+
const hidden = hiddenColumnIds()
|
|
216
|
+
|
|
217
|
+
return props.model.dataTableHead().filter((column) => ! hidden.includes(column.id))
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const registerComponents = () => {
|
|
222
|
+
|
|
223
|
+
if (typeof props.model.dataTableComponents !== 'function') {
|
|
224
|
+
return {}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const declared = props.model.dataTableComponents()
|
|
228
|
+
|
|
229
|
+
return Object.keys(declared).reduce((components, key) => {
|
|
230
|
+
components[key] = markRaw(declared[key])
|
|
231
|
+
|
|
232
|
+
return components
|
|
233
|
+
}, {})
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const crudActions = ref(props.model.crudActions())
|
|
238
|
+
|
|
239
|
+
const dataTable = reactive({
|
|
240
|
+
head: buildHead(),
|
|
241
|
+
body: [],
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
const pagination = reactive({
|
|
245
|
+
meta: [],
|
|
246
|
+
links: [],
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const dataTableComponents = registerComponents()
|
|
250
|
+
|
|
251
|
+
const sort = reactive(props.model.dataTableSort())
|
|
252
|
+
|
|
253
|
+
const orderBy = ref('id')
|
|
254
|
+
const internalSort = ref(false)
|
|
255
|
+
const page = ref(1)
|
|
256
|
+
|
|
257
|
+
let fetchDataAttempts = 0
|
|
258
|
+
let fetchPoliciesAttempts = 0
|
|
259
|
+
|
|
260
|
+
const getFilters = () => {
|
|
261
|
+
|
|
262
|
+
const params = {
|
|
263
|
+
_token: csrfToken(),
|
|
264
|
+
managed: true,
|
|
265
|
+
except_view_any: true,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const order = {
|
|
269
|
+
orderBy: orderBy.value,
|
|
270
|
+
orderMode: sort[orderBy.value],
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Con orden interno, el del usuario gana a lo que traigan los filtros
|
|
274
|
+
// externos; sin el, es al reves.
|
|
275
|
+
return internalSort.value
|
|
276
|
+
? { ...params, ...props.formFilters, ...props.externalFilters, ...order, page: page.value }
|
|
277
|
+
: { ...params, ...props.formFilters, ...order, ...props.externalFilters, page: page.value }
|
|
278
|
+
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const fetchData = () => {
|
|
282
|
+
|
|
283
|
+
const filters = getFilters()
|
|
284
|
+
|
|
285
|
+
return axios({
|
|
286
|
+
method: props.dataMethod,
|
|
287
|
+
url: props.dataUrl,
|
|
288
|
+
data: props.dataMethod === 'post' ? filters : null,
|
|
289
|
+
params: props.dataMethod === 'get' ? filters : null,
|
|
290
|
+
}).then((res) => {
|
|
291
|
+
|
|
292
|
+
fetchDataAttempts = 0
|
|
293
|
+
dataTable.body = res.data.data
|
|
294
|
+
pagination.meta = res.data.meta
|
|
295
|
+
pagination.links = res.data.links
|
|
296
|
+
|
|
297
|
+
}).catch((error) => {
|
|
298
|
+
|
|
299
|
+
// Un fallo de red no trae respuesta: leer error.response.status
|
|
300
|
+
// sin comprobarlo lanzaba un TypeError dentro del propio manejador.
|
|
301
|
+
if (error.response?.status === 403) {
|
|
302
|
+
return
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (fetchDataAttempts <= 3) {
|
|
306
|
+
setTimeout(() => {
|
|
307
|
+
++fetchDataAttempts
|
|
308
|
+
fetchData()
|
|
309
|
+
}, 1500)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const updateFilters = () => {
|
|
317
|
+
props.model.setFilters(getFilters())
|
|
318
|
+
|
|
319
|
+
return fetchData()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const sortColumn = (column) => {
|
|
323
|
+
|
|
324
|
+
if (column.sortable !== true) {
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// A partir de aqui el orden elegido por el usuario manda sobre el que
|
|
329
|
+
// puedan traer los filtros externos.
|
|
330
|
+
internalSort.value = true
|
|
331
|
+
orderBy.value = column.id
|
|
332
|
+
sort[column.id] = sort[column.id] === 'asc' ? 'desc' : 'asc'
|
|
333
|
+
|
|
334
|
+
updateFilters()
|
|
335
|
+
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const updatePage = (newPage) => {
|
|
339
|
+
page.value = newPage
|
|
340
|
+
|
|
341
|
+
updateFilters()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const actionClicked = (action) => {
|
|
345
|
+
return props.model[action.callback](action.params)
|
|
346
|
+
.then(() => updateFilters())
|
|
347
|
+
.catch((error) => console.error(error))
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const actionButtonClicked = (actions) => {
|
|
351
|
+
|
|
352
|
+
const requestData = {
|
|
353
|
+
_token: csrfToken(),
|
|
354
|
+
id: actions[0]?.params?.id ?? null,
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return axios({
|
|
358
|
+
method: props.policyMethod,
|
|
359
|
+
url: props.policyUrl,
|
|
360
|
+
data: props.policyMethod === 'post' ? requestData : null,
|
|
361
|
+
params: props.policyMethod === 'get' ? requestData : null,
|
|
362
|
+
}).then((res) => {
|
|
363
|
+
|
|
364
|
+
fetchPoliciesAttempts = 0
|
|
365
|
+
|
|
366
|
+
actions.forEach((action) => {
|
|
367
|
+
if (res.data[action.id]) {
|
|
368
|
+
action.policy = true
|
|
369
|
+
}
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
}).catch(() => {
|
|
373
|
+
|
|
374
|
+
if (fetchPoliciesAttempts <= 3) {
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
++fetchPoliciesAttempts
|
|
377
|
+
actionButtonClicked(actions)
|
|
378
|
+
}, 1500)
|
|
379
|
+
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
setTimeout(() => {
|
|
384
|
+
fetchPoliciesAttempts = 0
|
|
385
|
+
}, 3000)
|
|
386
|
+
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const closeDropdown = (event) => {
|
|
392
|
+
|
|
393
|
+
const dropdown = event.target.closest('.uk-dropdown')
|
|
394
|
+
|
|
395
|
+
if (dropdown) {
|
|
396
|
+
globalThis.UIkit?.dropdown(dropdown)?.hide(false)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
watch(() => props.formFilters, () => {
|
|
402
|
+
page.value = 1
|
|
403
|
+
updateFilters()
|
|
404
|
+
}, { deep: true })
|
|
405
|
+
|
|
406
|
+
watch(() => props.externalFilters, (value, previous) => {
|
|
407
|
+
if (! isEqual(value, previous)) {
|
|
408
|
+
updateFilters()
|
|
409
|
+
}
|
|
410
|
+
}, { deep: true })
|
|
411
|
+
|
|
412
|
+
onMounted(fetchData)
|
|
413
|
+
|
|
414
|
+
// El test y el anfitrion consultan el estado de las acciones; con
|
|
415
|
+
// <script setup> los bindings son privados si no se exponen.
|
|
416
|
+
defineExpose({ crudActions, dataTable, pagination })
|
|
417
|
+
|
|
418
|
+
</script>
|
|
419
|
+
|
|
420
|
+
<style scoped>
|
|
421
|
+
.tools-icon {
|
|
422
|
+
font-size: 30px;
|
|
423
|
+
color: var(--secondary-color);
|
|
424
|
+
padding: 10px 15px;
|
|
425
|
+
}
|
|
426
|
+
</style>
|