vue-laravel-crud 1.8.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.
@@ -1,82 +1,213 @@
1
1
  <script>
2
- import draggable from "vuedraggable";
3
- import moment from "moment";
4
- import InfiniteLoading from 'vue-infinite-loading';
5
- import VueMasonry from 'vue-masonry-css'
6
- import ItemCard from "./ItemCard.vue";
7
-
8
-
2
+ import CrudHeader from "./components/CrudHeader.vue";
3
+ import CrudTable from "./components/CrudTable.vue";
4
+ import CrudCards from "./components/CrudCards.vue";
5
+ import CrudKanban from "./components/CrudKanban.vue";
6
+ import CrudCustom from "./components/CrudCustom.vue";
7
+ import CrudModals from "./components/CrudModals.vue";
8
+ import CrudPagination from "./components/CrudPagination.vue";
9
+
10
+ // Import mixins
11
+ import crudData from "./mixins/crudData.js";
12
+ import crudApi from "./mixins/crudApi.js";
13
+ import crudFilters from "./mixins/crudFilters.js";
14
+ import crudValidation from "./mixins/crudValidation.js";
15
+ import crudHelpers from "./mixins/crudHelpers.js";
9
16
 
10
17
  export default /*#__PURE__*/ {
11
18
  name: "VueLaravelCrud",
12
19
  components: {
13
- draggable,
14
- InfiniteLoading,
15
- VueMasonry,
16
- ItemCard
20
+ CrudHeader,
21
+ CrudTable,
22
+ CrudCards,
23
+ CrudKanban,
24
+ CrudCustom,
25
+ CrudModals,
26
+ CrudPagination
17
27
  },
18
- data() {
28
+ mixins: [
29
+ crudData,
30
+ crudApi,
31
+ crudFilters,
32
+ crudValidation,
33
+ crudHelpers
34
+ ],
35
+ provide() {
19
36
  return {
20
-
21
- crudUuid: "",
22
- moment: moment,
23
- loading: false,
24
- firstLoad: false,
25
- item: {
26
- id: null,
27
- },
28
- items: [],
29
- selectedItems: [],
30
- pagination: {
31
-
32
- current_page: 1,
33
- last_page: 1,
34
- next_page_url: "",
35
- prev_page_url: "",
36
- per_page: 20,
37
- total: 0,
38
- },
39
- displaySearch: false,
40
- itemDefault: null,
41
- filters: [],
42
- filtersVisible: false,
43
- filterSidebarOpen: false,
44
- internalFilters: [],
45
- forceRecomputeCounter: 0,
46
-
47
- displayModes: {
48
- MODE_TABLE: 1,
49
- MODE_CARDS: 2,
50
- MODE_CUSTOM: 3,
51
- MODE_KANBAN: 4
52
- },
53
- infiniteScrollKey: 1,
54
- optionsLoaded: false,
55
- isMobile: false,
56
- refreshing: false,
57
- fetchError: false,
58
- principalSort: false,
59
- exportFormat: 'JSON',
37
+ // Props
38
+ modelName: this.modelName,
39
+ title: this.title,
40
+ model: this.model,
41
+ models: this.models,
42
+ ajax: this.ajax,
43
+ useVuexORM: this.useVuexORM,
44
+ vuexInitRelations: this.vuexInitRelations,
45
+ vuexLocalforage: this.vuexLocalforage,
46
+ columns: this.columns,
47
+ filter: this.filter,
48
+ enableFilters: this.enableFilters,
49
+ infiniteScroll: this.infiniteScroll,
50
+ sortable: this.sortable,
51
+ orderable: this.orderable,
52
+ validate: this.validate,
53
+ orderProp: this.orderProp,
54
+ createMultipart: this.createMultipart,
55
+ apiUrl: this.apiUrl,
56
+ search: this.search,
57
+ hideModalAfterSave: this.hideModalAfterSave,
58
+ hideModalAfterCreate: this.hideModalAfterCreate,
59
+ hideModalAfterUpdate: this.hideModalAfterUpdate,
60
+ refreshAfterSave: this.refreshAfterSave,
61
+ showPaginator: this.showPaginator,
62
+ showCreateBtn: this.showCreateBtn,
63
+ showSearch: this.showSearch,
64
+ showPrincipalSortBtn: this.showPrincipalSortBtn,
65
+ showHeader: this.showHeader,
66
+ showTitle: this.showTitle,
67
+ limit: this.limit,
68
+ displayMode: this.displayMode,
69
+ displayModeToggler: this.displayModeToggler,
70
+ colXs: this.colXs,
71
+ colSm: this.colSm,
72
+ colMd: this.colMd,
73
+ colLg: this.colLg,
74
+ colXl: this.colXl,
75
+ selectHover: this.selectHover,
76
+ selectClick: this.selectClick,
77
+ cardClass: this.cardClass,
78
+ listContainerClass: this.listContainerClass,
79
+ listItemClass: this.listItemClass,
80
+ cardHideFooter: this.cardHideFooter,
81
+ messageRemoveConfirm: this.messageRemoveConfirm,
82
+ messageRemoveBulkConfirm: this.messageRemoveBulkConfirm,
83
+ messageRemove: this.messageRemove,
84
+ messageNew: this.messageNew,
85
+ messageImport: this.messageImport,
86
+ messageExport: this.messageExport,
87
+ messageEmptyResults: this.messageEmptyResults,
88
+ messageNoMore: this.messageNoMore,
89
+ messageLoading: this.messageLoading,
90
+ messageSave: this.messageSave,
91
+ messageDefaultValidationError: this.messageDefaultValidationError,
92
+ searchPlaceholder: this.searchPlaceholder,
93
+ tableContainerClass: this.tableContainerClass,
94
+ tableClass: this.tableClass,
95
+ grouped: this.grouped,
96
+ groupedAttribute: this.groupedAttribute,
97
+ groupedLabelPre: this.groupedLabelPre,
98
+ groupedLabelAfter: this.groupedLabelAfter,
99
+ groupedSplit: this.groupedSplit,
100
+ draggableGroup: this.draggableGroup,
101
+ draggableOptions: this.draggableOptions,
102
+ masonryEnabled: this.masonryEnabled,
103
+ masonrySort: this.masonrySort,
104
+ masonryColumns: this.masonryColumns,
105
+ principalSortColumn: this.principalSortColumn,
106
+ bulkDelete: this.bulkDelete,
107
+ showImport: this.showImport,
108
+ showExport: this.showExport,
109
+ markDirty: this.markDirty,
110
+
111
+ // Data from mixins
112
+ crudUuid: this.crudUuid,
113
+ moment: this.moment,
114
+ loading: this.loading,
115
+ firstLoad: this.firstLoad,
116
+ item: this.item,
117
+ items: this.items,
118
+ selectedItems: this.selectedItems,
119
+ pagination: this.pagination,
120
+ displaySearch: this.displaySearch,
121
+ itemDefault: this.itemDefault,
122
+ filters: this.filters,
123
+ filtersVisible: this.filtersVisible,
124
+ filterSidebarOpen: this.filterSidebarOpen,
125
+ internalFilters: this.internalFilters,
126
+ forceRecomputeCounter: this.forceRecomputeCounter,
127
+ displayModes: this.displayModes,
128
+ infiniteScrollKey: this.infiniteScrollKey,
129
+ optionsLoaded: this.optionsLoaded,
130
+ isMobile: this.isMobile,
131
+ refreshing: this.refreshing,
132
+ fetchError: this.fetchError,
133
+ principalSort: this.principalSort,
134
+ exportFormat: this.exportFormat,
135
+
136
+ // Computed from mixins
137
+ itemValue: this.itemValue,
138
+ isSplitGroups: this.isSplitGroups,
139
+ itemsList: this.itemsList,
140
+ paginationIndexStart: this.paginationIndexStart,
141
+ paginationIndexEnd: this.paginationIndexEnd,
142
+ finalFilters: this.finalFilters,
143
+ sortFilter: this.sortFilter,
144
+ groupFilter: this.groupFilter,
145
+ internalFilter: this.internalFilter,
146
+ internalFilterByProp: this.internalFilterByProp,
147
+ columnOptions: this.columnOptions,
148
+
149
+ // Methods from mixins
150
+ handleResize: this.handleResize,
151
+ rearrangeArray: this.rearrangeArray,
152
+ clearItems: this.clearItems,
153
+ updateData: this.updateData,
154
+ externalUpdate: this.externalUpdate,
155
+ makePagination: this.makePagination,
156
+ fetchItemsVuex: this.fetchItemsVuex,
157
+ fetchItemsLocal: this.fetchItemsLocal,
158
+ fetchItems: this.fetchItems,
159
+ groupItems: this.groupItems,
160
+ saveItemVuex: this.saveItemVuex,
161
+ saveItemLocal: this.saveItemLocal,
162
+ saveItem: this.saveItem,
163
+ deleteItem: this.deleteItem,
164
+ deleteItemLocal: this.deleteItemLocal,
165
+ deleteItemVuex: this.deleteItemVuex,
166
+ deleteItemBulk: this.deleteItemBulk,
167
+ deleteItemBulkLocal: this.deleteItemBulkLocal,
168
+ deleteItemBulkVuex: this.deleteItemBulkVuex,
169
+ saveSort: this.saveSort,
170
+ exportItems: this.exportItems,
171
+ importItems: this.importItems,
172
+ refresh: this.refresh,
173
+ onPaginationChange: this.onPaginationChange,
174
+ infiniteHandler: this.infiniteHandler,
175
+ setupFilters: this.setupFilters,
176
+ toggleSortFilter: this.toggleSortFilter,
177
+ toggleFilters: this.toggleFilters,
178
+ resetFilters: this.resetFilters,
179
+ isColumnHasFilter: this.isColumnHasFilter,
180
+ setFilter: this.setFilter,
181
+ onChangeFilter: this.onChangeFilter,
182
+ togglePrincipalSort: this.togglePrincipalSort,
183
+ loadOptions: this.loadOptions,
184
+ getArrayValue: this.getArrayValue,
185
+ getStateValue: this.getStateValue,
186
+ onRowHover: this.onRowHover,
187
+ onRowClick: this.onRowClick,
188
+ onSort: this.onSort,
189
+ onCheckSelect: this.onCheckSelect,
190
+ toggleAll: this.toggleAll,
191
+ unSelectItem: this.unSelectItem,
192
+ selectItem: this.selectItem,
193
+ getSelectedItems: this.getSelectedItems,
194
+ onSelect: this.onSelect,
195
+ showItem: this.showItem,
196
+ createItem: this.createItem,
197
+ updateItem: this.updateItem,
198
+ removeItem: this.removeItem,
199
+ confirmBulkDelete: this.confirmBulkDelete,
200
+ toggleDisplayMode: this.toggleDisplayMode,
201
+ showExportModal: this.showExportModal,
202
+ showImportModal: this.showImportModal,
203
+ onDraggableAdded: this.onDraggableAdded,
204
+ onDraggableChange: this.onDraggableChange,
205
+ onDragEnd: this.onDragEnd,
206
+ toastError: this.toastError,
207
+ toastSuccess: this.toastSuccess,
208
+ downloadBlobResponse: this.downloadBlobResponse
60
209
  };
61
210
  },
62
- watch: {
63
- search(val) {
64
- if (val && val != "") {
65
- this.filters = [];
66
- this.filters.push(["search", "LIKE", val]);
67
- this.fetchItems();
68
- } else {
69
- this.filters = [];
70
- this.fetchItems();
71
- }
72
- },
73
-
74
- models(val) {
75
- if (!this.ajax) {
76
- this.items = val;
77
- }
78
- },
79
- },
80
211
  props: {
81
212
  modelName: String,
82
213
 
@@ -388,1760 +519,22 @@ export default /*#__PURE__*/ {
388
519
  }
389
520
  },
390
521
 
391
- mounted() {
392
- const now = Math.floor(Date.now() / 1000);
393
- this.crudUuid = '' + now;
394
- this.isMobile = window.matchMedia("(max-width: 1024px)").matches;
395
-
396
- // Agregar un oyente de eventos para actualizar isMobile cuando cambia el tamaño de la pantalla
397
- window.addEventListener("resize", this.handleResize);
398
-
399
- if (this.useVuexORM) {
400
-
401
- if (this.vuexLocalforage) {
402
- this.item = {};
403
- } else {
404
- this.item = new this.model();
405
- }
406
- let itemVuexOrmDefault = {};
407
- const fields = this.model.fields();
408
- // Inicializa el objeto "itemDefault" con los valores por defecto
409
- const itemDefault = {};
410
- const primaryKey = this.model.primaryKey;
411
-
412
- for (const fieldName of Object.keys(fields)) {
413
- const field = fields[fieldName];
414
- if (fieldName === primaryKey) {
415
- continue; // Salta este campo
416
- }
417
-
418
- console.debug("debug field", field);
419
-
420
- if (field.type === 'relation') {
421
- // Si es una relación, inicializa como un objeto vacío.
422
- console.debug("Relation", field);
423
-
424
- if (this.vuexInitRelations == true || (Array.isArray(this.vuexInitRelations) && this.vuexInitRelations.includes(fieldName))) {
425
- itemDefault[fieldName] = {};
426
- }
427
-
428
- } else {
429
-
430
-
431
- console.debug("Field", field);
432
-
433
-
434
- // Si no tiene un valor por defecto definido, inicializa según su tipo
435
- /*switch (field.constructor.name) {
436
- case 'StringField':
437
- itemDefault[fieldName] = '';
438
- break;
439
- case 'NumberField':
440
- itemDefault[fieldName] = 0;
441
- break;
442
- // Agrega más casos según los tipos de campos que uses en tu modelo
443
- default:
444
-
445
- console.debug("Undefined constructor ",fieldName,field.constructor.name);
446
- // Tipo de campo no reconocido, puedes manejarlo de acuerdo a tus necesidades
447
- itemDefault[fieldName] = null;
448
- }*/
449
-
450
-
451
- if (typeof field.value === 'function') {
452
- itemDefault[fieldName] = field.value();
453
- } else if (field.value) {
454
- itemDefault[fieldName] = field.value;
455
- } else {
456
- itemDefault[fieldName] = null;
457
- }
458
-
459
-
460
- }
461
- }
462
-
463
- this.itemDefault = JSON.parse(JSON.stringify(itemDefault));
464
-
465
-
466
- } else {
467
- this.item = this.model;
468
- this.itemDefault = JSON.parse(JSON.stringify(this.item));
469
- }
470
-
471
- console.debug("crud mounted columns", this.columns);
472
- this.internalFilters = [];
473
- this.setupFilters();
474
- this.fetchItems();
475
- this.loadOptions();
476
- },
477
- computed: {
478
- itemValue() {
479
- return (column, item) => {
480
- if (
481
- column.prop &&
482
- column.prop.split(".").length > 1 &&
483
- column.prop.split(".")[1]
484
- ) {
485
- return item[column.prop.split(".")[0]] &&
486
- item[column.prop.split(".")[0]][column.prop.split(".")[1]]
487
- ? item[column.prop.split(".")[0]][column.prop.split(".")[1]]
488
- : "";
489
- } else {
490
- return item[column.prop];
491
- }
492
- };
493
- },
494
-
495
- isSplitGroups(){
496
-
497
- if(this.groupedSplit){
498
- return true;
499
- }
500
- return this.displayMode == this.displayModes.MODE_KANBAN;
501
-
502
- },
503
- itemsList() {
504
- const items = this.ajax ? this.items : this.items.slice(this.paginationIndexStart, this.paginationIndexEnd);
505
- if (this.masonrySort && !this.isMobile) {
506
- return this.rearrangeArray(items, this.masonryColumns);
507
- }
508
- return items;
509
- },
510
- paginationIndexStart() {
511
- return (this.pagination.current_page - 1) * this.pagination.per_page;
512
- },
513
- paginationIndexEnd() {
514
- return this.paginationIndexStart + this.pagination.per_page;
515
- },
516
- finalFilters() {
517
- return [
518
- ...this.filters,
519
- ...this.filter,
520
- ...this.internalFilter,
521
- ...this.sortFilter,
522
- ...this.groupFilter
523
- ];
524
- },
525
- sortFilter() {
526
- if (this.showPrincipalSortBtn) {
527
- if (this.principalSort) {
528
- return [[this.principalSortColumn, 'SORTASC', '']];
529
- } else {
530
- return [[this.principalSortColumn, 'SORTDESC', '']];
531
- }
532
- } else {
533
- return [];
534
- }
535
-
536
- },
537
-
538
- groupFilter() {
539
- if (this.grouped && this.groupedAttribute) {
540
- return [['', 'GROUPBY', this.groupedAttribute]];
541
- } else {
542
- return [];
543
- }
544
- },
545
-
546
- internalFilter() {
547
- let filter = [];
548
- this.forceRecomputeCounter;
549
- this.internalFilters.forEach((f) => {
550
- if (f.value) {
551
- let colname = f.column.replace("_sort", "").replace("_from", "").replace("_to", "");
552
- filter.push([colname, f.op, f.value]);
553
- }
554
- });
555
- return filter;
556
- },
557
- internalFilterByProp() {
558
- return (prop) => {
559
- return this.internalFilters.find((inf) => inf.column == prop);
560
- };
561
- },
562
- columnOptions() {
563
- return (column) => {
564
-
565
- }
566
- }
567
- },
568
- methods: {
569
- handleResize() {
570
- // Actualizar isMobile cuando cambia el tamaño de la pantalla
571
- this.isMobile = window.matchMedia("(max-width: 1024px)").matches;
572
- },
573
-
574
- togglePrincipalSort() {
575
- this.principalSort = !this.principalSort;
576
- setTimeout(() => {
577
- this.refresh();
578
- }, 1);
579
- },
580
- infiniteHandler($state) {
581
-
582
-
583
- const hasNextPage = (this.pagination.total > 0 || !this.firstLoad) && (!this.firstLoad || (this.pagination.current_page * this.pagination.per_page) <= this.pagination.total);
584
- console.debug("Has next page", hasNextPage, this.pagination);
585
- if (hasNextPage) {
586
- const page = this.pagination.current_page + 1;
587
- this.fetchItems(page, true).then(() => {
588
- console.debug("infinite handler then");
589
- $state.loaded();
590
- }).catch(error => {
591
- console.debug("infinite handler error", error);
592
- $state.error();
593
- });
594
- } else {
595
- $state.complete();
596
- }
597
- },
598
- rearrangeArray(originalArray, columns = 3) {
599
- const rearrangedArray = [];
600
- for (let i = 0; i < columns; i++) {
601
- for (let j = i; j < originalArray.length; j += columns) {
602
- rearrangedArray.push(originalArray[j]);
603
- }
604
- }
605
- return rearrangedArray;
606
- },
607
- onDraggableAdded(event) {
608
- this.$emit("draggableAdded", event);
609
- },
610
- onDraggableChange(event) {
611
- this.$emit("draggableChange", event);
612
- },
613
- setupFilters() {
614
- this.columns.forEach((column) => {
615
- if (this.isColumnHasFilter(column)) {
616
- if (column.type == "date") {
617
- this.internalFilters.push({
618
- column: column.prop + "_from",
619
- op: column.filterOp ? column.filterOp : "=",
620
- value: null,
621
- });
622
-
623
- this.internalFilters.push({
624
- column: column.prop + "_to",
625
- op: column.filterOp ? column.filterOp : "=",
626
- value: null,
627
- });
628
- } else {
629
- this.internalFilters.push({
630
- column: column.prop,
631
- op: column.filterOp ? column.filterOp : "=",
632
- value: null,
633
- });
634
- }
635
- }
636
- if (this.sortable) {
637
- this.internalFilters.push({
638
- column: column.prop + "_sort",
639
- op: column.filterOp ? column.filterOp : "=",
640
- value: null,
641
- });
642
- }
643
- });
644
- },
645
-
646
- toggleSortFilter(column) {
647
- let value = this.internalFilterByProp(column.prop + "_sort").value;
648
- if (!value) {
649
- this.internalFilterByProp(column.prop + "_sort").value = "ASC";
650
- } else if (value == "ASC") {
651
- this.internalFilterByProp(column.prop + "_sort").value = "DESC";
652
- } else if (value == "DESC") {
653
- this.internalFilterByProp(column.prop + "_sort").value = null;
654
- }
655
- },
656
- toggleFilters() {
657
- this.filtersVisible = !this.filtersVisible;
658
- if (this.displayMode == this.displayModes.MODE_CARDS) {
659
- this.filterSidebarOpen = this.filtersVisible;
660
- } else {
661
- this.filterSidebarOpen = false;
662
- }
663
- },
664
- resetFilters(refresh = true) {
665
- this.internalFilters = [];
666
- this.setupFilters();
667
- this.forceRecomputeCounter++;
668
-
669
- if (refresh) {
670
- setTimeout(() => {
671
- this.refresh();
672
- }, 1);
673
- }
674
-
675
- },
676
- toggleDisplayMode() {
677
- if (this.displayMode == this.displayModes.MODE_TABLE)
678
- this.displayMode = this.displayModes.MODE_CARDS;
679
- else if (this.displayMode == this.displayModes.MODE_CARDS)
680
- this.displayMode = this.displayModes.MODE_TABLE;
681
- },
682
- onRowHover(item, itemIndex) {
683
- if (this.selectHover) {
684
- this.item = this.items[itemIndex];
685
- this.selectItem();
686
- this.onSelect();
687
- }
688
- },
689
- onRowClick(item, itemIndex) {
690
- if (this.selectClick) {
691
- this.item = this.items[itemIndex];
692
- this.selectItem();
693
- this.onSelect();
694
- }
695
- },
696
- onSort() {
697
- let event = {};
698
- let i =
699
- 1 +
700
- (this.pagination.current_page * this.pagination.per_page -
701
- this.pagination.per_page);
702
- this.items.forEach((item, index) => {
703
- //console.debug(s, i);
704
- item[this.orderProp] = i;
705
- i++;
706
- });
707
- this.$emit("sort", event);
708
- },
709
- onCheckSelect(value, item) {
710
- console.debug("ON CHECK SELECT", value, item);
711
- if (value) {
712
- this.item = item;
713
- this.selectItem();
714
- } else {
715
- this.unSelectItem(item);
716
- }
717
- this.onSelect();
718
- console.debug("Selected Items", this.selectedItems);
719
- },
720
- toggleAll(value) {
721
-
722
-
723
- if (value) {
724
- this.selectedItems = this.items;
725
-
726
- this.selectedItems.forEach(
727
- (item) => item.selected = true
728
- );
729
- } else {
730
- this.selectedItems.forEach(
731
- (item) => item.selected = false
732
- );
733
- this.items.forEach(
734
- (item) => item.selected = false
735
- );
736
- this.selectedItems = [];
737
-
738
- }
739
-
740
- this.onSelect();
741
-
742
- console.debug("toggle all", this.selectedItems);
743
- this.$forceUpdate();
744
- },
745
- unSelectItem(item) {
746
-
747
- item.selected = false;
748
-
749
- this.selectedItems = this.selectedItems.filter(
750
- (e) => e.id != item.id
751
- );
752
-
753
- },
754
- selectItem() {
755
- let sitem = this.selectedItems.find((e) => e.id == this.item.id);
756
- if (sitem) {
757
- this.item.selected = false;
758
- this.selectedItems = this.selectedItems.filter(
759
- (e) => e.id != this.item.id
760
- );
761
- } else {
762
- this.item.selected = true;
763
- this.selectedItems.push(this.item);
764
- }
765
- },
766
- externalUpdate(itemsUpdate, addIfNotExist = true, key = 'id') {
767
- itemsUpdate.forEach(itemUpdate => {
768
- let itemInList = this.items.find(item => item[key] === itemUpdate[key]);
769
- if (itemInList) Object.assign(itemInList, itemUpdate);
770
- else {
771
- if (addIfNotExist) {
772
- this.items.push(itemUpdate);
773
- }
774
- }
775
- });
776
- },
777
- getSelectedItems() {
778
- return this.selectedItems;
779
- },
780
- onSelect() {
781
- this.$emit("select", this.item);
782
- this.$emit("selectItems", this.selectedItems);
783
- },
784
-
785
- updateData(data, allowCreate = true) {
786
- // Convertir this.items a un mapa para acceso rápido por id
787
- const itemsMap = new Map(this.items.map(item => [item.id, item]));
788
-
789
- // Recorrer cada elemento de data
790
- data.forEach(newItem => {
791
- if (itemsMap.has(newItem.id)) {
792
- // Actualizar el item existente
793
- const existingItem = itemsMap.get(newItem.id);
794
- Object.assign(existingItem, newItem);
795
- } else if (allowCreate) {
796
- // Agregar el nuevo item si allowCreate es true
797
- this.items.push(newItem);
798
- }
799
- });
800
-
801
- // Convertir el mapa de vuelta a un array, si es necesario
802
- this.items = Array.from(itemsMap.values());
803
- },
804
- showItem(id, itemIndex = null) {
805
- if (itemIndex == null) {
806
- let item = this.items.find((it) => it.id == id);
807
- this.item = item;
808
- } else {
809
- this.item = this.items[itemIndex];
810
- }
811
- this.onSelect();
812
- this.$bvModal.show("modal-show-item-" + this.modelName);
813
- },
814
- createItem() {
815
- if (this.useVuexORM) {
816
-
817
- if (this.vuexLocalforage) {
818
- this.item = JSON.parse(JSON.stringify(this.itemDefault));
819
- } else {
820
- this.item = new this.model(JSON.parse(JSON.stringify(this.itemDefault)));
821
- }
822
-
823
- } else {
824
- this.item = JSON.parse(JSON.stringify(this.itemDefault));
825
- }
826
- this.onSelect();
827
- this.$bvModal.show("modal-form-item-" + this.modelName);
828
- },
829
- updateItem(id, itemIndex = null) {
830
- if (itemIndex == null) {
831
- let item = this.items.find((it) => it.id == id);
832
- this.item = item;
833
- } else {
834
- this.item = this.items[itemIndex];
835
- }
836
- //console.debug(itemIndex);
837
- this.onSelect();
838
- this.$bvModal.show("modal-form-item-" + this.modelName);
839
- },
840
-
841
- refresh() {
842
- this.$emit("refresh", {});
843
-
844
- if (this.infiniteScroll) {
845
- this.pagination.current_page = 1;
846
- this.infiniteScrollKey++;
847
- }
848
-
849
- const fetchPromise = this.fetchItems(this.pagination.current_page);
850
-
851
- if (this.infiniteScroll && fetchPromise) {
852
- this.refreshing = true;
853
- fetchPromise.then(() => {
854
- const infiniteLoadingRef = this.$refs.infiniteLoading;
855
- if (infiniteLoadingRef) {
856
- infiniteLoadingRef.stateChanger.reset();
857
- } else {
858
- console.debug("infiniteLoadingRef not set");
859
- }
860
- this.refreshing = false;
861
- });
862
- }
863
- },
864
- isColumnHasFilter(column) {
865
- return column && !column.hideFilter && column.type != "actions";
866
- },
867
- setFilter(column, value) {
868
- let filter = this.filter.find((f) => f.column == column);
869
- filter.value = value;
870
- this.forceRecomputeCounter++;
871
- setTimeout(() => {
872
- this.refresh();
873
- }, 1);
874
- },
875
- async fetchItemsVuex(page = 1, concat = false) {
876
- this.loading = true;
877
- this.$emit("beforeFetch", {});
878
-
879
- let result;
880
-
881
- if (this.vuexLocalforage) {
882
- await this.model.$fetch();
883
-
884
- } else {
885
- this.model.deleteAll();
886
-
887
- result = await this.model.api().get(this.apiUrl + "/" + this.modelName, {
888
- params: {
889
- page: page,
890
- limit: this.pagination.perPage,
891
- filters: JSON.stringify(this.finalFilters),
892
- }
893
- });
894
-
895
- }
896
-
897
- let itemsResult = this.model.query().withAll().get();
898
- //let itemsResult = result.entities[this.model.entity];
899
-
900
- if (itemsResult) {
901
- this.items = itemsResult;
902
- }
903
- console.debug("fetch page vuex ", itemsResult, page, this.items, result);
904
- this.loading = false;
905
- this.firstLoad = true;
906
- },
907
- fetchItemsLocal() {
908
- if (this.grouped) {
909
- this.groupItems(this.models);
910
- } else {
911
- this.items = this.models;
912
- }
913
-
914
- this.pagination.total = this.items.length;
915
- this.firstLoad = true;
916
-
917
- },
918
- fetchItems(page = 1, concat = false) {
919
-
920
-
921
- this.$emit("beforeFetch", {});
922
- if (this.useVuexORM) {
923
- return this.fetchItemsVuex(page, concat);
924
- }
925
-
926
-
927
- if (!this.ajax) {
928
- return this.fetchItemsLocal(page, concat);
929
- }
930
-
931
- this.loading = true;
932
- return axios
933
- .get(this.apiUrl + "/" + this.modelName, {
934
- params: {
935
- page: page,
936
- limit: this.limit,
937
- filters: JSON.stringify(this.finalFilters),
938
- },
939
- })
940
- .then((response) => {
941
- this.makePagination(response.data);
942
- let items = response.data.data;
943
- if (this.grouped) {
944
- //this.items = items;
945
- this.groupItems(items, concat,this.isSplitGroups);
946
- } else {
947
- if (concat) {
948
- this.items = this.items.concat(items);
949
- } else {
950
- this.items = items;
951
- }
952
- }
953
-
954
- this.loading = false;
955
- this.firstLoad = true;
956
- this.$emit("afterFetch", {});
957
- })
958
- .catch((error) => {
959
- //console.debug(error);
960
- this.toastError(error);
961
- this.loading = false;
962
- this.firstLoad = true;
963
- this.fetchError = true;
964
- });
965
- },
966
- groupItems(items, concat = false, splitGroups = false) {
967
- const groupedAttribute = this.groupedAttribute;
968
- const groupLabelPre = this.groupedLabelPre || '';
969
- const groupLabelAfter = this.groupedLabelAfter || '';
970
- const itemsWithGroup = [];
971
-
972
- // Usamos un objeto para agrupar los elementos por groupedAttribute
973
- const groupedMap = items.reduce((acc, item) => {
974
- const groupKey = item[groupedAttribute] || 'undefined';
975
- if (!acc[groupKey]) {
976
- acc[groupKey] = [];
977
- }
978
- acc[groupKey].push(item);
979
- return acc;
980
- }, {});
981
-
982
- if (splitGroups) {
983
- // Dividimos los grupos en arrays separados
984
- this.items = Object.entries(groupedMap).map(([groupKey, groupItems]) => ({
985
- groupKey,
986
- groupLabel: groupLabelPre + groupKey + groupLabelAfter,
987
- items: groupItems,
988
- }));
989
- } else {
990
- // Creamos la estructura agrupada en un solo array
991
- for (const [groupKey, groupItems] of Object.entries(groupedMap)) {
992
- itemsWithGroup.push({
993
- crudgrouplabel: groupLabelPre + groupKey + groupLabelAfter,
994
- crudgroup: true,
995
- });
996
- itemsWithGroup.push(...groupItems);
997
- }
998
-
999
- // Decidimos si concatenar o reemplazar los items existentes
1000
- if (concat) {
1001
- this.items = this.items.concat(itemsWithGroup);
1002
- } else {
1003
- this.items = itemsWithGroup;
1004
- }
1005
- }
1006
- },
1007
- removeItem(id, index) {
1008
- this.$bvModal
1009
- .msgBoxConfirm(this.messageRemoveConfirm, {
1010
- size: "sm",
1011
- buttonSize: "sm",
1012
- okVariant: "danger",
1013
- okTitle: this.messageRemove,
1014
- cancelTitle: "NO",
1015
- centered: true,
1016
- })
1017
- .then((value) => {
1018
- if (value) {
1019
- this.deleteItem(id, index);
1020
- }
1021
- })
1022
- .catch((error) => {
1023
- this.toastError(error);
1024
- this.loading = false;
1025
- });
1026
- },
1027
-
1028
- confirmBulkDelete() {
1029
- this.$bvModal
1030
- .msgBoxConfirm(this.messageRemoveBulkConfirm, {
1031
- size: "sm",
1032
- buttonSize: "sm",
1033
- okVariant: "danger",
1034
- okTitle: this.messageRemove,
1035
- cancelTitle: "NO",
1036
- centered: true,
1037
- })
1038
- .then((value) => {
1039
- if (value) {
1040
- this.deleteItemBulk();
1041
- }
1042
- })
1043
- .catch((error) => {
1044
- this.toastError(error);
1045
- this.loading = false;
1046
- });
1047
- },
1048
- deleteItemBulk() {
1049
- if (this.useVuexORM) {
1050
- return this.deleteItemBulkVuex();
1051
- }
1052
-
1053
- if (!this.ajax) {
1054
- return this.deleteItemBulkLocal();
1055
- }
1056
-
1057
-
1058
- let ids = this.selectedItems.map(it => it.id);
1059
-
1060
- this.loading = true;
1061
- axios
1062
- .delete(this.apiUrl + "/" + this.modelName + "/bulk-destroy", { params: { ids: ids }, })
1063
- .then((response) => {
1064
- this.items = this.items.filter(it => !ids.includes(it.id));
1065
- this.toastSuccess("Elemento/s eliminado.");
1066
- this.$emit("itemDeleted", {});
1067
- this.loading = false;
1068
- })
1069
- .catch((error) => {
1070
- this.toastError(error);
1071
- this.loading = false;
1072
- });
1073
- },
1074
- async deleteItemBulkLocal() {
1075
- let ids = this.selectedItems.map(it => it.id);
1076
- this.items = this.items.filter(it => !ids.includes(it.id));
1077
- this.item = null;
1078
- this.toastSuccess("Elemento Eliminado");
1079
- this.$emit("itemDeleted", {});
1080
- this.loading = false;
1081
- },
1082
- async deleteItemBulkVuex() {
1083
-
1084
- let ids = this.selectedItems.map(it => it.id);
1085
-
1086
-
1087
- if (this.vuexLocalforage) {
1088
- await this.model.$delete(ids);
1089
-
1090
- } else {
1091
- let result = await this.model.api().delete(this.apiUrl + "/" + this.modelName + '/bulk-destroy', {
1092
- params: { ids: ids },
1093
- delete: ids
1094
- });
1095
-
1096
- console.debug("delete item vuex", result);
1097
- let responseStatus = result.response.status;
1098
-
1099
- if (result.response.data.error) {
1100
- this.toastError(result.response.data.error);
1101
- this.loading = false;
1102
- return;
1103
- }
1104
- }
1105
-
1106
- this.toastSuccess("Elemento eliminados.");
1107
- },
1108
- deleteItem(id, index) {
1109
-
1110
- if (this.useVuexORM) {
1111
- return this.deleteItemVuex(id, index);
1112
- }
1113
-
1114
- if (!this.ajax) {
1115
- return this.deleteItemLocal(id, index);
1116
- }
1117
-
1118
- this.loading = true;
1119
- axios
1120
- .delete(this.apiUrl + "/" + this.modelName + "/" + id)
1121
- .then((response) => {
1122
- this.items.splice(index, 1);
1123
- this.toastSuccess("Elemento eliminado.");
1124
- this.$emit("itemDeleted", {});
1125
- this.loading = false;
1126
- })
1127
- .catch((error) => {
1128
- this.toastError(error);
1129
- this.loading = false;
1130
- });
1131
- },
1132
-
1133
-
1134
- async deleteItemLocal(id, index) {
1135
- if (id || index) {
1136
- let itemIndex;
1137
-
1138
- if (id) {
1139
- itemIndex = this.items.findIndex((item) => item.id == this.item.id);
1140
- } else {
1141
- itemIndex = index;
1142
- }
1143
-
1144
- // Assuming this.items is an array
1145
- this.items.splice(itemIndex, 1);
1146
- this.item = null;
1147
- this.toastSuccess("Elemento Eliminado");
1148
- this.$emit("itemDeleted", {});
1149
-
1150
- } else {
1151
- // Handle the case where there's no item.id or item.index
1152
- console.error("Cannot delete item without ID or index");
1153
- // You might want to show an error message or handle it in a way that fits your application.
1154
- }
1155
-
1156
- this.loading = false;
1157
- },
1158
- async deleteItemVuex(id, index) {
1159
-
1160
-
1161
- if (this.vuexLocalforage) {
1162
- await this.model.$delete(id);
1163
-
1164
- } else {
1165
- let result = await this.model.api().delete(this.apiUrl + "/" + this.modelName + '/' + id, {
1166
- delete: 1
1167
- });
1168
-
1169
- console.debug("delete item vuex", result);
1170
- let responseStatus = result.response.status;
1171
-
1172
- if (result.response.data.error) {
1173
- this.toastError(result.response.data.error);
1174
- this.loading = false;
1175
- return;
1176
- }
1177
- }
1178
-
1179
-
1180
-
1181
- this.toastSuccess("Elemento eliminado.");
1182
- },
1183
- saveSort() {
1184
- if (this.orderable) {
1185
- this.loading = true;
1186
- let order = [];
1187
- this.items.forEach((v, k) => {
1188
- order.push({ id: v.id, order: v[this.orderProp] });
1189
- });
1190
-
1191
- if (!this.ajax) {
1192
- return;
1193
- }
1194
- axios
1195
- .post(this.apiUrl + "/" + this.modelName + "/sort", {
1196
- order: order,
1197
- })
1198
- .then((response) => {
1199
- let data = response.data;
1200
- this.toastSuccess("Orden Actualizado");
1201
- if (this.refreshAfterSave) this.refresh();
1202
- this.loading = false;
1203
- })
1204
- .catch((error) => {
1205
- //console.debug(error);
1206
- this.toastError(error);
1207
- this.loading = false;
1208
- });
1209
- }
1210
- },
1211
-
1212
- showExportModal() {
1213
- this.$refs["modal-export"].show();
1214
- },
1215
-
1216
-
1217
- exportItems() {
1218
- if (this.useVuexORM) {
1219
- return;
1220
- }
1221
-
1222
- if (!this.ajax) {
1223
- return;
1224
- }
1225
-
1226
- let exportItems = true;
1227
- let params;
1228
- let ids = this.selectedItems.map(it => it.id);
1229
- if (ids.length) {
1230
- params = { ids: ids, exportItems: exportItems, };
1231
- } else {
1232
- params = { filters: JSON.stringify(this.finalFilters), exportItems: exportItems, };
1233
- }
1234
- params.format = this.exportFormat;
1235
- this.loading = true;
1236
- axios
1237
- .get(this.apiUrl + "/" + this.modelName + "/export", { params: params, responseType: "blob", })
1238
- .then((response) => {
1239
- this.downloadBlobResponse(response);
1240
- this.loading = false;
1241
- })
1242
- .catch((error) => {
1243
- this.toastError(error);
1244
- this.loading = false;
1245
- });
1246
- },
1247
-
1248
-
1249
- showImportModal() {
1250
- this.$refs["modal-import"].show();
1251
- },
1252
- importItems() {
1253
- let formData = new FormData();
1254
- formData.append("file", this.fileImport);
1255
- axios
1256
- .post(this.apiUrl + "/" + this.modelName + "/import", formData, {
1257
- headers: {
1258
- "Content-Type": "multipart/form-data",
1259
- },
1260
- })
1261
- .then((response) => {
1262
- if (response && response.data && response.data.success == true) {
1263
- this.$refs["modal-import"].hide();
1264
- this.toastSuccess("Datos Importados con Éxito");
1265
- this.refresh();
1266
- } else {
1267
- this.toastError("No se pudo importar los datos.");
1268
- }
1269
- })
1270
- .catch((error) => {
1271
- console.error(error);
1272
- this.toastError(error);
1273
-
1274
- });
1275
- },
1276
-
1277
-
1278
- getArrayValue(value, displayProp, options = []) {
1279
- if (!Array.isArray(value)) return "N/A";
1280
- let values = [];
1281
- let valuesFinal = [];
1282
-
1283
- if (value.length > 0) {
1284
- if (typeof value[0] === "object" && displayProp) {
1285
- values = value.map((vv) => vv[displayProp]);
1286
- } else {
1287
- values = value.join(",");
1288
- }
1289
- } else {
1290
- return "";
1291
- }
1292
-
1293
- values.forEach(val => {
1294
- valuesFinal.push(this.getStateValue(val, options));
1295
- })
1296
-
1297
- return values.join(",");
1298
- },
1299
- getStateValue(value, options) {
1300
- if (!options) {
1301
- console.debug(
1302
- "State Column Not hast options returning value",
1303
- value,
1304
- options
1305
- );
1306
- return value;
1307
- }
1308
- let ops = options.filter((option) => {
1309
- if (Array.isArray(value)) {
1310
- return value.includes(option.id);
1311
- } else {
1312
- return option.id == value;
1313
- }
1314
- });
1315
- ops = ops.map((option) => {
1316
- return option.text ? option.text : option.label ? option.label : "";
1317
- });
1318
- return ops.join(", ");
1319
- },
1320
- async saveItemVuex(event = null) {
1321
- console.debug("save item 1", this.item);
1322
- let result;
1323
- let create = false;
1324
-
1325
-
1326
- if (this.vuexLocalforage) {
1327
-
1328
- if (this.markDirty) {
1329
- this.item.dirty = true;
1330
- }
1331
-
1332
- if (this.item.id) {
1333
-
1334
- result = await this.model.$create({ data: this.item });
1335
- console.debug("save item 4", this.item, result);
1336
- create = false;
1337
- } else {
1338
-
1339
- result = await this.model.$create({ data: this.item });
1340
- console.debug("save item 5", this.item, result);
1341
- create = true;
1342
- }
1343
-
1344
- } else {
1345
-
1346
- let jsondata = this.item.$toJson();
1347
- console.debug("save item 2", this.item, jsondata);
1348
- if (this.item.id) {
1349
- result = await this.model.api().put(this.apiUrl + "/" + this.modelName + '/' + this.item.id, jsondata);
1350
- create = false;
1351
- } else {
1352
- result = await this.model.api().post(this.apiUrl + "/" + this.modelName, jsondata);
1353
- create = true;
1354
- }
1355
-
1356
-
1357
- let responseStatus = result.response.status;
1358
- if (result.response.data.error) {
1359
- this.toastError(result.response.data.error);
1360
- this.loading = false;
1361
- return;
1362
- //throw new Error('Something is wrong.')
1363
- }
1364
-
1365
- result.save();
1366
- }
1367
-
1368
-
1369
- if (this.refreshAfterSave) this.refresh();
1370
- this.loading = false;
1371
- this.toastSuccess("Elemento Modificado");
1372
-
1373
- if (this.hideModalAfterSave || ((create && this.hideModalAfterCreate) || (!create && this.hideModalAfterUpdate))) {
1374
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1375
- }
1376
-
1377
- },
1378
-
1379
- async saveItemLocal(event = null) {
1380
-
1381
- const itemSave = JSON.parse(JSON.stringify(this.item));
1382
- if (this.item.id || this.item.index) {
1383
-
1384
- let itemIndex;
1385
-
1386
- if (this.item.id) {
1387
- itemIndex = this.items.findIndex(
1388
- (item) => item.id == this.item.id
1389
- );
1390
- } else {
1391
-
1392
- itemIndex = this.items.findIndex(
1393
- (item) => item.index == this.item.index
1394
- );
1395
- }
1396
-
1397
- this.items[itemIndex] = itemSave;
1398
- if (this.hideModalAfterSave || this.hideModalAfterUpdate) {
1399
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1400
- }
1401
- } else {
1402
-
1403
- itemSave.index = this.items.length + 1;
1404
- this.items.push(itemSave);
1405
- if (this.hideModalAfterSave || this.hideModalAfterCreate) {
1406
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1407
- }
1408
- }
1409
- this.toastSuccess("Elemento Modificado");
1410
- this.loading = false;
1411
-
1412
- },
1413
- async loadOptions() {
1414
- for (let i = 0; i < this.columns.length; i++) {
1415
- const column = this.columns[i];
1416
-
1417
- if (column.options instanceof Promise) {
1418
- // Si las opciones son una función (promesa), esperar y actualizar
1419
- const options = await column.options;
1420
- this.$set(this.columns, i, { ...column, options });
1421
-
1422
- console.debug("Options promise", this.columns);
1423
- }
1424
- }
1425
-
1426
- this.optionsLoaded = true;
1427
- },
1428
- async saveItem(event = null) {
1429
- this.loading = true;
1430
- if (this.validate) {
1431
- let validation_result = true;
1432
- let validation_error_message = this.messageDefaultValidationError;
1433
- if (!validation_result) {
1434
- this.toastError(validation_error_message);
1435
- return;
1436
- }
1437
- } else {
1438
- if (event) event.preventDefault();
1439
- }
1440
-
1441
- if (this.useVuexORM) {
1442
- return this.saveItemVuex(event);
1443
- }
1444
-
1445
- if (!this.ajax) {
1446
- return this.saveItemLocal(event);
1447
- }
1448
- if (this.item.id) {
1449
- axios
1450
- .put(
1451
- this.apiUrl + "/" + this.modelName + "/" + this.item.id,
1452
- this.item
1453
- )
1454
- .then((response) => {
1455
- if (this.hideModalAfterSave || this.hideModalAfterUpdate) {
1456
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1457
- }
1458
- let itemSv = response.data;
1459
- let itemIndex = this.items.findIndex(
1460
- (item) => item.id == this.item.id
1461
- );
1462
- this.items[itemIndex] = itemSv;
1463
- this.item = itemSv;
1464
- this.loading = false;
1465
- if (this.refreshAfterSave) this.refresh();
1466
- this.toastSuccess("Elemento Modificado");
1467
- this.$emit("itemSaved", { item: this.item });
1468
- this.$emit("itemUpdated", { item: this.item });
1469
- })
1470
- .catch((error) => {
1471
- this.toastError(error);
1472
- this.loading = false;
1473
- });
1474
- } else {
1475
- if (this.createMultipart) {
1476
- const formData = new FormData();
1477
- Object.keys(this.item).forEach((key) => {
1478
- if (this.item[key][0] && this.item[key][0].name) {
1479
- let files = this.item[key];
1480
- for (var x = 0; x < files.length; x++) {
1481
- formData.append(
1482
- key + "[]",
1483
- this.item[key][x],
1484
- this.item[key][x].name
1485
- );
1486
- }
1487
- } else formData.append(key, this.item[key]);
1488
- });
1489
-
1490
- axios
1491
- .post(this.apiUrl + "/" + this.modelName, formData)
1492
- .then((response) => {
1493
- this.loading = false;
1494
- if (this.hideModalAfterSave || this.hideModalAfterCreate) {
1495
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1496
- }
1497
- if (response.data.success) {
1498
- if (response.data.message) {
1499
- this.toastSuccess(response.data.message);
1500
- }
1501
- return;
1502
- }
1503
- let itemSv = response.data;
1504
- this.items.push(itemSv);
1505
- this.item = itemSv;
1506
- if (this.refreshAfterSave) this.refresh();
1507
- this.toastSuccess("Elemento Creado");
1508
- this.$emit("itemSaved", { item: this.item });
1509
- this.$emit("itemCreated", { item: this.item });
1510
- })
1511
- .catch((error) => {
1512
- this.toastError(error);
1513
- this.loading = false;
1514
- });
1515
- } else {
1516
- axios
1517
- .post(this.apiUrl + "/" + this.modelName, this.item)
1518
- .then((response) => {
1519
- this.loading = false;
1520
- if (this.hideModalAfterSave || this.hideModalAfterUpdate) {
1521
- this.$bvModal.hide("modal-form-item-" + this.modelName);
1522
- }
1523
- if (response.data.success) {
1524
- if (response.data.message) {
1525
- this.toastSuccess(response.data.message);
1526
- }
1527
- return;
1528
- }
1529
-
1530
- let itemSv = response.data;
1531
- this.items.push(itemSv);
1532
- this.item = itemSv;
1533
- if (this.refreshAfterSave) this.refresh();
1534
- this.toastSuccess("Elemento Creado");
1535
- this.$emit("itemSaved", { item: this.item });
1536
- this.$emit("itemCreated", { item: this.item });
1537
- })
1538
- .catch((error) => {
1539
- this.toastError(error);
1540
- this.loading = false;
1541
- });
1542
- }
1543
- }
1544
- if (event) event.preventDefault();
1545
- },
1546
- clearItems() {
1547
- this.items = [];
1548
- },
1549
-
1550
- toastError(error) {
1551
- let error_message = "Ha ocurrido un error";
1552
-
1553
- if (typeof error === "string") {
1554
- error_message = error;
1555
- } else if (error.response) {
1556
- // handle API errors
1557
- if (error.response.status === 401) {
1558
- error_message = "No estás autorizado para realizar esta acción";
1559
- } else if (error.response.status === 404) {
1560
- error_message = "El recurso solicitado no se encontró";
1561
- } else if (error.response.status >= 400 && error.response.status < 500) {
1562
- error_message = "Hubo un problema con la solicitud realizada";
1563
- } else if (error.response.status >= 500) {
1564
- error_message = "El servidor no pudo procesar la solicitud";
1565
- }
1566
-
1567
- if (error.response.data) {
1568
- if (typeof error.response.data === "object") {
1569
- if (error.response.data.errors) {
1570
- let errors = error.response.data.errors;
1571
- this.responseErrors = errors;
1572
- error_message = Object.values(errors)[0][0];
1573
- } else if (error.response.data.message) {
1574
- error_message = error.response.data.message;
1575
- }
1576
- } else if (typeof error.response.data === "string") {
1577
- error_message = error.response.data;
1578
- }
1579
- }
1580
- } else if (error.request) {
1581
- // handle network errors
1582
- error_message = "No se pudo conectar con el servidor. Verifique su conexión a Internet.";
1583
- } else if (error.message) {
1584
- // handle other errors
1585
- error_message = error.message;
1586
- }
1587
-
1588
-
1589
-
1590
- this.$bvToast.toast(error_message, {
1591
- title: `Error`,
1592
- toaster: "b-toaster-bottom-right",
1593
- variant: "danger",
1594
- solid: true,
1595
- appendToast: true,
1596
- });
1597
- },
1598
- toastSuccess(message) {
1599
- this.$bvToast.toast(message, {
1600
- title: `Listo`,
1601
- toaster: "b-toaster-bottom-right",
1602
- variant: "success",
1603
- solid: true,
1604
- appendToast: true,
1605
- });
1606
- },
1607
- downloadBlobResponse(response, extension = null) {
1608
- const url = window.URL.createObjectURL(new Blob([response.data]));
1609
- const link = document.createElement("a");
1610
- link.href = url;
1611
-
1612
- let contentdisposition = response.headers['content-disposition'];
1613
- let filename = "Export";
1614
-
1615
-
1616
- if (contentdisposition) {
1617
- filename = contentdisposition.split('filename=')[1].split('.')[0];
1618
- filename = filename.replace('_', '');
1619
- filename = filename.replace('"', '');
1620
- extension = contentdisposition.split('.')[1].split(';')[0];
1621
- extension = extension.replace('_', '');
1622
- extension = extension.replace('"', '');
1623
- }
1624
-
1625
-
1626
- console.debug("DOWNLOAD ", filename, extension);
1627
- link.setAttribute("download", filename + '.' + extension);
1628
- document.body.appendChild(link);
1629
- link.click();
1630
- },
1631
- onChangeFilter(event) {
1632
- this.forceRecomputeCounter++;
1633
- console.debug("Filters debug ", this.finalFilters, this.internalFilter, this.internalFilters, this.filter, this.filters);
1634
- setTimeout(() => {
1635
- this.refresh();
1636
- }, 1);
1637
- },
1638
- onPaginationChange(page) {
1639
- this.fetchItems(page);
1640
- },
1641
- makePagination: function (data) {
1642
- let pagination = {
1643
- current_page: data.current_page,
1644
- last_page: data.last_page,
1645
- next_page_url: data.next_page_url,
1646
- prev_page_url: data.prev_page_url,
1647
- total: data.total,
1648
- per_page: data.per_page,
1649
- };
1650
- this.pagination = pagination;
1651
- },
1652
- },
1653
-
1654
- beforeDestroy() {
1655
- // Eliminar el oyente de eventos al destruir el componente para evitar pérdidas de memoria
1656
- window.removeEventListener("resize", this.handleResize);
1657
- },
1658
522
  };
1659
523
  </script>
1660
524
 
1661
525
  <template>
1662
526
  <div class="crud">
1663
- <div class="crud-header" v-if="showHeader">
1664
- <h4 class="crud-title" v-if="showTitle">{{ title }}</h4>
1665
- <b-sidebar v-model="filterSidebarOpen" title="Filtrar" right shadow>
1666
- <slot name="sidebarFilters" v-bind:createItem="createItem" v-bind:toggleDisplayMode="toggleDisplayMode"
1667
- v-bind:loading="loading" v-bind:isColumnHasFilter="isColumnHasFilter" v-bind:setFilter="setFilter">
1668
- <div class="px-3 py-2">
1669
- <div v-for="(column, indexc) in columns" :key="indexc">
1670
- <div v-if="isColumnHasFilter(column)">
1671
- <slot :name="'sidebar-filter-' + column.prop" v-bind:column="column" v-bind:filter="filter"
1672
- v-bind:internalFilterByProp="internalFilterByProp" v-if="internalFilterByProp(column.prop)">
1673
- <div class="form-group" v-if="column.type == 'boolean'">
1674
- <label>{{ column.label }}</label>
1675
-
1676
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
1677
- @change="onChangeFilter($event)">
1678
- <option value=""></option>
1679
- <option value="1">Sí</option>
1680
- <option value="0">No</option>
1681
- </select>
1682
- </div>
1683
- <div class="form-group" v-else-if="column.type == 'date'">
1684
- <div class="row">
1685
- <div class="col-6">
1686
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_from').value
1687
- " today-button reset-button close-button locale="es"></b-form-datepicker>
1688
- </div>
1689
- <div class="col-6">
1690
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_to').value
1691
- " today-button reset-button close-button locale="es"></b-form-datepicker>
1692
- </div>
1693
- </div>
1694
- </div>
1695
-
1696
- <div class="form-group" v-else-if="column.type == 'state'">
1697
- <label>{{ column.label }}</label>
1698
-
1699
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
1700
- @change="onChangeFilter($event)" v-if="optionsLoaded">
1701
- <option value=""></option>
1702
- <option :value="option.id ? option.id : option.value" v-for="option in column.options"
1703
- :key="option.id ? option.id : option.value">
1704
- {{
1705
- option.text
1706
- ? option.text
1707
- : option.label
1708
- ? option.label
1709
- : ""
1710
- }}
1711
- </option>
1712
- </select>
1713
- </div>
1714
-
1715
- <div class="form-group" v-else-if="column.type == 'array'">
1716
- <label>{{ column.label }}</label>
1717
-
1718
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
1719
- @change="onChangeFilter($event)" v-if="optionsLoaded">
1720
- <option value=""></option>
1721
- <template v-if="column.options">
1722
- <option :value="option.id ? option.id : option.value" v-for="option in column.options"
1723
- :key="option.id ? option.id : option.value">
1724
- {{
1725
- option.text
1726
- ? option.text
1727
- : option.label
1728
- ? option.label
1729
- : ""
1730
- }}
1731
- </option>
1732
- </template>
1733
- </select>
1734
- </div>
1735
-
1736
-
1737
- <div class="form-group" v-else>
1738
- <label>{{ column.label }}</label>
1739
-
1740
- <input class="form-control" v-model.lazy="internalFilterByProp(column.prop).value"
1741
- @change="onChangeFilter($event)" />
1742
- </div>
1743
- </slot>
1744
- </div>
1745
- </div>
1746
-
1747
- <div class="mt-3 d-flex justify-content-center">
1748
- <button class="btn btn-light" @click="resetFilters()">
1749
- Reset
1750
- </button>
1751
- <button class="btn btn-info" @click="onChangeFilter($event)">
1752
- Filtrar
1753
- </button>
1754
- </div>
1755
- </div>
1756
- </slot>
1757
- </b-sidebar>
1758
-
1759
- <div class="table-options">
1760
- <b-button-group class="mr-1">
1761
- <slot name="tableActions" v-bind:createItem="createItem" v-bind:toggleDisplayMode="toggleDisplayMode"
1762
- v-bind:loading="loading">
1763
- <slot name="tableActionsPrepend" v-bind:loading="loading"> </slot>
1764
-
1765
- <b-button variant="info" @click="showImportModal()" v-if="showImport">
1766
- <b-icon-cloud-upload></b-icon-cloud-upload>{{ messageImport }}
1767
- </b-button>
1768
- <b-button variant="info" @click="showExportModal()" v-if="showExport">
1769
- <b-icon-cloud-download></b-icon-cloud-download>{{ messageExport }}
1770
- </b-button>
1771
- <b-button variant="info" v-if="showPrincipalSortBtn" @click="togglePrincipalSort()" :disabled="loading">
1772
- <b-icon-sort-numeric-down v-if="principalSort"></b-icon-sort-numeric-down>
1773
- <b-icon-sort-numeric-up v-else></b-icon-sort-numeric-up>
1774
- </b-button>
1775
- <b-button variant="danger" @click="confirmBulkDelete()"
1776
- v-if="bulkDelete"><b-icon-trash></b-icon-trash></b-button>
1777
- <b-button variant="success" v-if="showCreateBtn" @click="createItem()" :disabled="loading">
1778
- <b-icon-plus></b-icon-plus>{{ messageNew }}
1779
- </b-button>
1780
- <b-button variant="info" v-if="enableFilters" @click="toggleFilters()">Filtros</b-button>
1781
- <b-button variant="info" @click="refresh()"><b-icon-arrow-clockwise></b-icon-arrow-clockwise></b-button>
1782
- <b-button variant="info" @click="toggleDisplayMode()" :disabled="loading" v-if="displayModeToggler">
1783
- <b-icon-card-list v-if="displayMode == displayModes.MODE_TABLE"></b-icon-card-list>
1784
- <b-icon-table v-else-if="displayMode == displayModes.MODE_CARDS"></b-icon-table>
1785
- </b-button>
1786
-
1787
- <div class="crud-search m-0" v-if="showSearch">
1788
- <b-input-group>
1789
- <b-input-group-prepend>
1790
- <b-button variant="info" @click="displaySearch = !displaySearch"
1791
- :class="{ open: displaySearch }"><b-icon-search></b-icon-search></b-button>
1792
- </b-input-group-prepend>
1793
- <b-form-input v-if="displaySearch" v-model="search" class="pl-2" type="search" required
1794
- :placeholder="searchPlaceholder" debounce="500"></b-form-input>
1795
- </b-input-group>
1796
-
1797
- <slot name="tableActionsAppend" v-bind:loading="loading"> </slot>
1798
- </div>
1799
- </slot>
1800
- </b-button-group>
1801
- </div>
1802
- </div>
1803
-
1804
- <div :class="['table-responsive', tableContainerClass]" v-if="displayMode == displayModes.MODE_TABLE">
1805
- <table :class="['table table-hover table-striped w-100', tableClass]">
1806
- <thead class="thead-light">
1807
- <tr>
1808
- <slot name="rowHead">
1809
- <th v-for="(column, indexc) in columns" :key="indexc"
1810
- :style="{ width: column.width ? column.width : 'inherit' }" scope="col">
1811
- <slot :name="'filter-' + column.prop" v-bind:column="column" v-bind:filter="filter"
1812
- v-bind:internalFilterByProp="internalFilterByProp" v-if="enableFilters &&
1813
- filtersVisible &&
1814
- isColumnHasFilter(column) &&
1815
- internalFilterByProp(column.prop)
1816
- ">
1817
-
1818
- <div class="form-group">
1819
- <select v-if="column.type == 'boolean'" class="form-control form-control-md p-2"
1820
- v-model="internalFilterByProp(column.prop).value" @change="onChangeFilter($event)">
1821
- <option value="">{{ column.label }}</option>
1822
- <option value="1">Sí</option>
1823
- <option value="0">No</option>
1824
- </select>
1825
-
1826
- <div class="row" v-else-if="column.type == 'date'">
1827
- <div class="col-6">
1828
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_from').value
1829
- " today-button reset-button close-button locale="es"
1830
- class="form-control-md p-2"></b-form-datepicker>
1831
- </div>
1832
- <div class="col-6">
1833
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_to').value
1834
- " today-button reset-button close-button locale="es"
1835
- class="form-control-md p-2"></b-form-datepicker>
1836
- </div>
1837
- </div>
1838
-
1839
- <select v-else-if="column.type == 'state' && optionsLoaded" class="form-control form-control-md p-2"
1840
- v-model="internalFilterByProp(column.prop).value" @change="onChangeFilter($event)"
1841
- :placeholder="column.label">
1842
- <option value="">{{ column.label }}</option>
1843
- <option :value="option.id" v-for="(option, indexo) in column.options" :key="indexo">
1844
- {{
1845
- option.text
1846
- ? option.text
1847
- : option.label
1848
- ? option.label
1849
- : ""
1850
- }}
1851
- </option>
1852
- </select>
1853
-
1854
- <select v-else-if="column.type == 'array' && optionsLoaded" class="form-control form-control-md p-2"
1855
- v-model="internalFilterByProp(column.prop).value" @change="onChangeFilter($event)"
1856
- :placeholder="column.label">
1857
- <option value="">{{ column.label }}</option>
1858
- <option :value="option.id" v-for="(option, indexo) in column.options" :key="indexo">
1859
- {{
1860
- option.text
1861
- ? option.text
1862
- : option.label
1863
- ? option.label
1864
- : ""
1865
- }}
1866
- </option>
1867
- </select>
1868
-
1869
- <b-form-checkbox v-else-if="column.type == 'checkbox'" name="select-all"
1870
- @change="toggleAll($event)">
1871
- </b-form-checkbox>
1872
-
1873
- <b-form-checkbox v-else-if="column.type == 'select'" name="select-all" @change="toggleAll($event)">
1874
- </b-form-checkbox>
1875
-
1876
- <input v-else class="form-control form-control-md p-2"
1877
- v-model="internalFilterByProp(column.prop).value" :placeholder="column.label"
1878
- @change="onChangeFilter($event)" />
1879
-
1880
- </div>
1881
- </slot>
1882
- <span v-else-if="column.type == 'select'">
1883
- <b-form-checkbox name="select-all" @change="toggleAll($event)"></b-form-checkbox>
1884
- </span>
1885
- <span v-else>{{ column.label }}</span>
1886
-
1887
-
1888
- <span
1889
- v-if="sortable && column.type != 'select' && column.type != 'checkbox' && internalFilterByProp(column.prop + '_sort')"
1890
- class="sort-filter" @click="toggleSortFilter(column)"><b-icon-sort-down
1891
- v-if="!internalFilterByProp(column.prop + '_sort').value"></b-icon-sort-down><b-icon-sort-up
1892
- v-if="internalFilterByProp(column.prop + '_sort').value == 'ASC'"></b-icon-sort-up>
1893
- <b-icon-sort-down
1894
- v-if="internalFilterByProp(column.prop + '_sort').value == 'DESC'"></b-icon-sort-down>
1895
- </span>
1896
- </th>
1897
- </slot>
1898
- </tr>
1899
- </thead>
1900
-
1901
- <draggable v-model="items" :group="draggableGroup" tag="tbody" :draggable="orderable ? '.item' : '.none'"
1902
- @start="drag = true" @end="drag = false" @sort="onSort()" @add="onDraggableAdded($event)"
1903
- @change="onDraggableChange($event)" :options="draggableOptions">
1904
- <tr v-for="(item, index) in itemsList" v-bind:key="index" @mouseover="onRowHover(item, index)"
1905
- @click="onRowClick(item, index)" class="item">
1906
-
1907
- <th :colspan="columns.length" v-if="grouped && item.crudgroup">
1908
- <span>{{ item.crudgrouplabel }}</span>
1909
- </th>
1910
-
1911
- <slot name="row" v-bind:item="item" v-else>
1912
- <td v-for="(column, indexc) in columns" :key="indexc" :scope="column.prop == 'id' ? 'row' : ''">
1913
- <slot :name="'cell-' + column.prop" v-bind:item="item" v-bind:index="index" v-bind:itemindex="index"
1914
- v-bind:columnindex="indexc">
1915
- <span v-if="column.type == 'boolean'">
1916
- <b-badge variant="success" v-if="itemValue(column, item) == 'true' ||
1917
- itemValue(column, item) == 1 ||
1918
- itemValue(column, item) == '1'
1919
- "><b-icon-check-circle></b-icon-check-circle></b-badge>
1920
- <b-badge variant="danger" v-if="!itemValue(column, item) ||
1921
- itemValue(column, item) == '0' ||
1922
- itemValue(column, item) == 'false'
1923
- "><b-icon-x-circle></b-icon-x-circle></b-badge>
1924
- </span>
1925
- <span v-else-if="column.type == 'date'">
1926
- {{
1927
- itemValue(column, item)
1928
- ? moment(itemValue(column, item)).format(
1929
- column.format ? column.format : 'L LT'
1930
- )
1931
- : itemValue(column, item)
1932
- }}
1933
- </span>
1934
- <span v-else-if="column.type == 'select'">
1935
- <b-form-checkbox v-model="item.selected" @change="onCheckSelect($event, item)">
1936
- </b-form-checkbox>
1937
- </span>
1938
- <span v-else-if="column.type == 'state' && optionsLoaded">
1939
- {{
1940
- getStateValue(itemValue(column, item), column.options)
1941
- }}
1942
- </span>
1943
- <span v-else-if="column.type == 'array' && optionsLoaded">
1944
- {{
1945
- getArrayValue(
1946
- itemValue(column, item),
1947
- column.displayProp,
1948
- column.options
1949
- )
1950
- }}
1951
- </span>
1952
- <span v-else>
1953
- {{ itemValue(column, item) }}
1954
- </span>
1955
- </slot>
1956
-
1957
- <b-button-group v-if="column.type == 'actions'">
1958
- <slot name="rowAction" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
1959
- v-bind:updateItem="updateItem" v-bind:removeItem="removeItem">
1960
- <b-button variant="primary" @click="showItem(item.id, index)">
1961
- <b-icon-eye></b-icon-eye>
1962
- </b-button>
1963
- <b-button variant="secondary" @click="updateItem(item.id, index)">
1964
- <b-icon-pencil></b-icon-pencil>
1965
- </b-button>
1966
- <b-button variant="danger" @click="removeItem(item.id, index)">
1967
- <b-icon-trash></b-icon-trash>
1968
- </b-button>
1969
- </slot>
1970
- </b-button-group>
1971
- </td>
1972
- </slot>
1973
-
1974
- </tr>
1975
-
1976
- </draggable>
1977
-
1978
- </table>
1979
- <p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
1980
- {{ messageEmptyResults }}
1981
- </p>
1982
- </div>
1983
-
1984
- <div v-else-if="displayMode == displayModes.MODE_CARDS">
1985
- <draggable v-model="items" :group="draggableGroup" :draggable="orderable ? '.item' : '.none'" @start="drag = true"
1986
- @end="drag = false" @sort="onSort()" @add="onDraggableAdded($event)" @change="onDraggableChange($event)"
1987
- :options="draggableOptions">
1988
- <masonry
1989
- :cols="{ default: 12 / colLg, 1400: 12 / colXl, 1200: 12 / colLg, 1000: 12 / colMd, 700: 12 / colSm, 400: 12 / colXs }"
1990
- :gutter="{ default: '15px', 700: '15px' }">
1991
- <div v-for="(item, itemIndex) in itemsList" v-bind:key="itemIndex" class="item">
1992
- <slot name="card" v-bind:item="item">
1993
- <ItemCard :item="item" :columns="columns" :index="itemIndex"
1994
- :cardClass="cardClass" :cardHideFooter="cardHideFooter" :itemValue="itemValue"
1995
- :getStateValue="getStateValue" :getArrayValue="getArrayValue" :showItem="showItem"
1996
- :updateItem="updateItem" :removeItem="removeItem" />
1997
- </slot>
1998
- </div>
1999
- </masonry>
2000
- </draggable>
2001
-
2002
- <p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
2003
- {{ messageEmptyResults }}
2004
- </p>
2005
-
2006
- </div>
2007
-
2008
- <div v-else-if="displayMode == displayModes.MODE_KANBAN">
2009
- {{ JSON.stringify(items) }}
2010
-
2011
- <div v-for="(column, colIndex) in items" :key="colIndex" class="kanban-column">
2012
- <div class="kanban-column-header">
2013
- {{ column.groupLabel }}
2014
- </div>
2015
-
2016
- {{ JSON.stringify(column) }}
2017
-
2018
-
2019
- <draggable v-model="column.items" group="kanban" class="kanban-column-body" @end="onDragEnd">
2020
-
2021
- <div v-for="(item, itemIndex) in column.items" v-bind:key="itemIndex" class="item">
2022
- <slot name="card" v-bind:item="item">
2023
- <ItemCard :key="itemIndex" :item="item" :columns="columns" :index="index"
2024
- :cardClass="cardClass" :cardHideFooter="cardHideFooter" :itemValue="itemValue"
2025
- :getStateValue="getStateValue" :getArrayValue="getArrayValue" :showItem="showItem"
2026
- :updateItem="updateItem" :removeItem="removeItem" />
2027
- </slot>
2028
- </div>
2029
-
2030
- </draggable>
2031
-
2032
- </div>
2033
-
2034
- </div>
2035
-
2036
- <div v-else-if="displayMode == displayModes.MODE_CUSTOM">
2037
- <div :class="listContainerClass">
2038
- <p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
2039
- {{ messageEmptyResults }}
2040
- </p>
2041
- <div :class="listItemClass" v-for="(item, index) in itemsList" v-bind:key="index">
2042
- <slot name="card" v-bind:item="item"> </slot>
2043
- </div>
2044
- </div>
2045
- </div>
527
+ <CrudHeader />
528
+
529
+ <CrudTable />
530
+ <CrudCards />
531
+ <CrudKanban />
532
+ <CrudCustom />
533
+
2046
534
  <b-overlay :show="loading" rounded="sm"></b-overlay>
2047
- <infinite-loading ref="infiniteLoading" @infinite="infiniteHandler" v-if="infiniteScroll"
2048
- :forceUseInfiniteWrapper="true" :key="infiniteScrollKey">
2049
- <div slot="spinner">
2050
- <div class="text-center">{{ messageLoading }}</div>
2051
- </div>
2052
- <div slot="no-more">
2053
- <div class="text-center" v-if="!loading">{{ messageNoMore }}</div>
2054
- </div>
2055
- <div slot="no-results">
2056
- <div class="text-center" v-if="!loading">{{ items.length == 0 ? messageEmptyResults : messageNoMore }}</div>
2057
- </div>
2058
- </infinite-loading>
2059
- <div class="paginator-data" v-if="!infiniteScroll">
2060
- Filas: {{ pagination.total }} | xPág: {{ pagination.per_page }} | Pág: {{ pagination.current_page }} |
2061
- Seleccionados:
2062
- {{
2063
- selectedItems.length }}
2064
- </div>
2065
- <div class="crud-paginator" v-if="!infiniteScroll">
2066
- <b-pagination v-if="showPaginator" v-model="pagination.current_page" :total-rows="pagination.total"
2067
- :per-page="pagination.per_page" @change="onPaginationChange($event)"></b-pagination>
2068
- </div>
2069
- <b-modal :id="'modal-form-item-' + modelName" hide-footer size="xl" :title="title" no-close-on-backdrop>
2070
- <b-overlay :show="loading" rounded="sm">
2071
- <template v-if="validate">
2072
- <form @submit="saveItem">
2073
- <slot name="form" v-bind:item="item" v-if="item">
2074
- <b-form-group label="Nombre:" description="Nombre ">
2075
- <b-form-input v-model="item.title" type="text" required placeholder="Nombre"></b-form-input>
2076
- </b-form-group>
2077
- </slot>
2078
- <b-button block type="submit" variant="success" :disabled="loading">
2079
- <b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
2080
- </b-button>
2081
- </form>
2082
- </template>
2083
- <template v-if="!validate">
2084
- <slot name="form" v-bind:item="item" v-if="item">
2085
- <b-form-group :label="key" v-for="(value, key) in item" :key="key">
2086
- <b-form-input v-model="item[key]" type="text" required></b-form-input>
2087
- </b-form-group>
2088
- </slot>
2089
- <b-button block type="submit" variant="success" :disabled="loading" @click="saveItem()">
2090
- <b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
2091
- </b-button>
2092
- </template>
2093
- </b-overlay>
2094
- </b-modal>
2095
- <b-modal :id="'modal-show-item-' + modelName" hide-footer size="xl" :title="title" no-close-on-backdrop>
2096
- <slot name="show" v-bind:item="item" v-if="item">
2097
- <b-list-group>
2098
- <b-list-group-item v-for="(value, key) in item" :key="key">
2099
- <b-row class="w-100">
2100
- <b-col cols="4" class="font-weight-bold">{{ key }}</b-col>
2101
- <b-col cols="8">{{ JSON.stringify(value) }}</b-col>
2102
- </b-row>
2103
- </b-list-group-item>
2104
- </b-list-group>
2105
- </slot>
2106
- </b-modal>
2107
-
2108
-
2109
- <b-modal ref="modal-import" title="Importar" hide-footer v-if="showImport">
2110
- <slot name="import" v-bind:item="item" v-if="item">
2111
- <b-overlay :show="loading" rounded="sm">
2112
- <b-form-file v-model="fileImport" :state="Boolean(fileImport)" browse-text="Explorar"
2113
- placeholder="Importar..." drop-placeholder="Arrastrar Archivo aquí..."></b-form-file>
2114
- <div class="text-center mt-3">
2115
- <b-button variant="info" v-on:click="importItems()" :disabled="loading">
2116
- <b-icon-cloud-upload></b-icon-cloud-upload>
2117
- {{ loading ? "Cargando..." : "Importar" }}
2118
- </b-button>
2119
- </div>
2120
- </b-overlay>
2121
- </slot>
2122
- </b-modal>
2123
-
2124
- <b-modal ref="modal-export" title="Exportar" hide-footer v-if="showExport">
2125
- <slot name="export" v-bind:item="item" v-if="item">
2126
- <b-overlay :show="loading" rounded="sm">
2127
-
2128
- <p v-if="selectedItems.length">Se exportará {{ selectedItems.length }} elementos.</p>
2129
- <p v-else>Se exportará la consulta actual.</p>
2130
-
2131
- <select class="form-control" v-model="exportFormat">
2132
- <option value="JSON">JSON</option>
2133
- <option value="XLSX">XLSX</option>
2134
- </select>
2135
-
2136
- <div class="text-center mt-3">
2137
- <b-button variant="info" v-on:click="exportItems()" :disabled="loading">
2138
- <b-icon-cloud-upload></b-icon-cloud-upload>
2139
- {{ loading ? "Cargando..." : "Exportar" }}
2140
- </b-button>
2141
- </div>
2142
- </b-overlay>
2143
- </slot>
2144
- </b-modal>
535
+
536
+ <CrudPagination />
537
+ <CrudModals />
2145
538
  </div>
2146
539
  </template>
2147
540