vue-laravel-crud 2.0.6 → 2.0.8

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,152 +1,154 @@
1
- <template>
2
- <div class="crud-header" v-if="showHeader">
3
- <h4 class="crud-title" v-if="showTitle">{{ title }}</h4>
4
-
5
- <b-sidebar :visible="sidebarVisible" @hidden="closeSidebar" title="Filtrar" right shadow>
6
- <CrudFilters />
7
- </b-sidebar>
8
-
9
- <div class="table-options">
10
- <b-button-group class="mr-1">
11
- <slot name="tableActions" v-bind:createItem="createItem" v-bind:toggleDisplayMode="toggleDisplayMode"
12
- v-bind:loading="loading">
13
- <slot name="tableActionsPrepend" v-bind:loading="loading"> </slot>
14
-
15
- <b-button variant="info" @click="showImportModal()" v-if="showImport">
16
- <b-icon-cloud-upload></b-icon-cloud-upload>{{ messageImport }}
17
- </b-button>
18
- <b-button variant="info" @click="showExportModal()" v-if="showExport">
19
- <b-icon-cloud-download></b-icon-cloud-download>{{ messageExport }}
20
- </b-button>
21
- <b-button variant="info" v-if="showPrincipalSortBtn" @click="togglePrincipalSort()" :disabled="loading">
22
- <b-icon-sort-numeric-down v-if="principalSort"></b-icon-sort-numeric-down>
23
- <b-icon-sort-numeric-up v-else></b-icon-sort-numeric-up>
24
- </b-button>
25
- <b-button variant="danger" @click="confirmBulkDelete()"
26
- v-if="bulkDelete"><b-icon-trash></b-icon-trash></b-button>
27
- <b-button variant="success" v-if="showCreateBtn" @click="createItem()" :disabled="loading">
28
- <b-icon-plus></b-icon-plus>{{ messageNew }}
29
- </b-button>
30
- <b-button variant="info" v-if="enableFilters" @click="toggleFilters()">Filtros</b-button>
31
- <b-button variant="info" @click="refresh()"><b-icon-arrow-clockwise></b-icon-arrow-clockwise></b-button>
32
- <b-button variant="info" @click="toggleDisplayMode()" :disabled="loading" v-if="displayModeToggler">
33
- <b-icon-card-list v-if="currentDisplayMode == displayModes.MODE_TABLE"></b-icon-card-list>
34
- <b-icon-table v-else-if="currentDisplayMode == displayModes.MODE_CARDS"></b-icon-table>
35
- </b-button>
36
-
37
- <div class="crud-search m-0" v-if="showSearch">
38
- <b-input-group>
39
- <b-input-group-prepend>
40
- <b-button variant="info" @click="displaySearch = !displaySearch"
41
- :class="{ open: displaySearch }"><b-icon-search></b-icon-search></b-button>
42
- </b-input-group-prepend>
43
- <b-form-input v-if="displaySearch" v-model="search" class="pl-2" type="search" required
44
- :placeholder="searchPlaceholder" debounce="500"></b-form-input>
45
- </b-input-group>
46
-
47
- <slot name="tableActionsAppend" v-bind:loading="loading"> </slot>
48
- </div>
49
- </slot>
50
- </b-button-group>
51
- </div>
52
- </div>
53
- </template>
54
-
55
- <script>
56
- import CrudFilters from './CrudFilters.vue';
57
-
58
- export default {
59
- name: 'CrudHeader',
60
- components: {
61
- CrudFilters
62
- },
63
- inject: [
64
- 'showHeader',
65
- 'showTitle',
66
- 'title',
67
- 'filterSidebarOpen',
68
- 'showImport',
69
- 'showExport',
70
- 'showPrincipalSortBtn',
71
- 'principalSort',
72
- 'bulkDelete',
73
- 'showCreateBtn',
74
- 'enableFilters',
75
- 'displayModeToggler',
76
- 'displayMode',
77
- 'displayModes',
78
- 'showSearch',
79
- 'displaySearch',
80
- 'search',
81
- 'searchPlaceholder',
82
- 'loading',
83
- 'messageImport',
84
- 'messageExport',
85
- 'messageNew',
86
- 'createItem',
87
- 'toggleDisplayMode',
88
- 'togglePrincipalSort',
89
- 'confirmBulkDelete',
90
- 'toggleFilters',
91
- 'refresh'
92
- ],
93
- computed: {
94
- sidebarVisible() {
95
- // Acceder directamente al componente padre para obtener reactividad
96
- return this.$parent ? this.$parent.filterSidebarOpen : this.filterSidebarOpen;
97
- },
98
- currentDisplayMode() {
99
- if (!this.displayMode) return 1;
100
- if (this.displayMode.value !== undefined) {
101
- return this.displayMode.value;
102
- }
103
- if (typeof this.displayMode === 'function') {
104
- return this.displayMode();
105
- }
106
- return this.displayMode;
107
- }
108
- },
109
- methods: {
110
- closeSidebar() {
111
- if (this.filterSidebarOpen) {
112
- this.toggleFilters();
113
- }
114
- }
115
- }
116
- };
117
- </script>
118
-
119
- <style scoped>
120
- .crud-header {
121
- display: flex;
122
- justify-content: space-between;
123
- max-height: 3rem;
124
- }
125
-
126
- .crud-title {
127
- margin: 0;
128
- }
129
-
130
- .crud-search {
131
- max-width: 15rem;
132
- }
133
-
134
- .crud-search .btn {
135
- border-top-left-radius: 0;
136
- border-bottom-left-radius: 0;
137
- border-top-right-radius: 0.375rem;
138
- border-bottom-right-radius: 0.375rem;
139
- }
140
-
141
- .crud-search .btn.open {
142
- border-top-right-radius: 0;
143
- border-bottom-right-radius: 0;
144
- }
145
-
146
- .table-options {
147
- margin-bottom: 1rem;
148
- display: flex;
149
- align-items: center;
150
- justify-content: flex-end;
151
- }
152
- </style>
1
+ <template>
2
+ <div class="crud-header" v-if="showHeader">
3
+ <h4 class="crud-title" v-if="showTitle">{{ title }}</h4>
4
+
5
+ <b-sidebar :visible="sidebarVisible" @hidden="closeSidebar" title="Filtrar" right shadow>
6
+ <CrudFilters />
7
+ </b-sidebar>
8
+
9
+ <div class="table-options">
10
+ <b-button-group class="mr-1">
11
+ <slot name="tableActions" v-bind:createItem="createItem" v-bind:toggleDisplayMode="toggleDisplayMode"
12
+ v-bind:loading="loading">
13
+ <slot name="tableActionsPrepend" v-bind:loading="loading"> </slot>
14
+
15
+ <b-button variant="info" @click="showImportModal()" v-if="showImport">
16
+ <b-icon-cloud-upload></b-icon-cloud-upload>{{ messageImport }}
17
+ </b-button>
18
+ <b-button variant="info" @click="showExportModal()" v-if="showExport">
19
+ <b-icon-cloud-download></b-icon-cloud-download>{{ messageExport }}
20
+ </b-button>
21
+ <b-button variant="info" v-if="showPrincipalSortBtn" @click="togglePrincipalSort()" :disabled="loading">
22
+ <b-icon-sort-numeric-down v-if="principalSort"></b-icon-sort-numeric-down>
23
+ <b-icon-sort-numeric-up v-else></b-icon-sort-numeric-up>
24
+ </b-button>
25
+ <b-button variant="danger" @click="confirmBulkDelete()"
26
+ v-if="bulkDelete"><b-icon-trash></b-icon-trash></b-button>
27
+ <b-button variant="success" v-if="showCreateBtn" @click="createItem()" :disabled="loading">
28
+ <b-icon-plus></b-icon-plus>{{ messageNew }}
29
+ </b-button>
30
+ <b-button variant="info" v-if="enableFilters" @click="toggleFilters()">Filtros</b-button>
31
+ <b-button variant="info" @click="refresh()"><b-icon-arrow-clockwise></b-icon-arrow-clockwise></b-button>
32
+ <b-button variant="info" @click="toggleDisplayMode()" :disabled="loading" v-if="displayModeToggler">
33
+ <b-icon-card-list v-if="currentDisplayMode == displayModes.MODE_TABLE"></b-icon-card-list>
34
+ <b-icon-table v-else-if="currentDisplayMode == displayModes.MODE_CARDS"></b-icon-table>
35
+ </b-button>
36
+
37
+ <div class="crud-search m-0" v-if="showSearch">
38
+ <b-input-group>
39
+ <b-input-group-prepend>
40
+ <b-button variant="info" @click="displaySearch = !displaySearch"
41
+ :class="{ open: displaySearch }"><b-icon-search></b-icon-search></b-button>
42
+ </b-input-group-prepend>
43
+ <b-form-input v-if="displaySearch" v-model="search" class="pl-2" type="search" required
44
+ :placeholder="searchPlaceholder" debounce="500"></b-form-input>
45
+ </b-input-group>
46
+
47
+ <slot name="tableActionsAppend" v-bind:loading="loading"> </slot>
48
+ </div>
49
+ </slot>
50
+ </b-button-group>
51
+ </div>
52
+ </div>
53
+ </template>
54
+
55
+ <script>
56
+ import CrudFilters from './CrudFilters.vue';
57
+
58
+ export default {
59
+ name: 'CrudHeader',
60
+ components: {
61
+ CrudFilters
62
+ },
63
+ inject: [
64
+ 'showHeader',
65
+ 'showTitle',
66
+ 'title',
67
+ 'filterSidebarOpen',
68
+ 'showImport',
69
+ 'showExport',
70
+ 'showPrincipalSortBtn',
71
+ 'principalSort',
72
+ 'bulkDelete',
73
+ 'showCreateBtn',
74
+ 'enableFilters',
75
+ 'displayModeToggler',
76
+ 'displayMode',
77
+ 'displayModes',
78
+ 'showSearch',
79
+ 'displaySearch',
80
+ 'search',
81
+ 'searchPlaceholder',
82
+ 'loading',
83
+ 'messageImport',
84
+ 'messageExport',
85
+ 'messageNew',
86
+ 'createItem',
87
+ 'toggleDisplayMode',
88
+ 'togglePrincipalSort',
89
+ 'confirmBulkDelete',
90
+ 'toggleFilters',
91
+ 'refresh',
92
+ 'showImportModal',
93
+ 'showExportModal'
94
+ ],
95
+ computed: {
96
+ sidebarVisible() {
97
+ // Acceder directamente al componente padre para obtener reactividad
98
+ return this.$parent ? this.$parent.filterSidebarOpen : this.filterSidebarOpen;
99
+ },
100
+ currentDisplayMode() {
101
+ if (!this.displayMode) return 1;
102
+ if (this.displayMode.value !== undefined) {
103
+ return this.displayMode.value;
104
+ }
105
+ if (typeof this.displayMode === 'function') {
106
+ return this.displayMode();
107
+ }
108
+ return this.displayMode;
109
+ }
110
+ },
111
+ methods: {
112
+ closeSidebar() {
113
+ if (this.filterSidebarOpen) {
114
+ this.toggleFilters();
115
+ }
116
+ }
117
+ }
118
+ };
119
+ </script>
120
+
121
+ <style scoped>
122
+ .crud-header {
123
+ display: flex;
124
+ justify-content: space-between;
125
+ max-height: 3rem;
126
+ }
127
+
128
+ .crud-title {
129
+ margin: 0;
130
+ }
131
+
132
+ .crud-search {
133
+ max-width: 15rem;
134
+ }
135
+
136
+ .crud-search .btn {
137
+ border-top-left-radius: 0;
138
+ border-bottom-left-radius: 0;
139
+ border-top-right-radius: 0.375rem;
140
+ border-bottom-right-radius: 0.375rem;
141
+ }
142
+
143
+ .crud-search .btn.open {
144
+ border-top-right-radius: 0;
145
+ border-bottom-right-radius: 0;
146
+ }
147
+
148
+ .table-options {
149
+ margin-bottom: 1rem;
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: flex-end;
153
+ }
154
+ </style>
@@ -1,36 +1,36 @@
1
- <template>
2
- <div v-if="currentDisplayMode == displayModes.MODE_KANBAN">
3
- <KanbanBoard>
4
- <template v-for="(slot, name) in $scopedSlots" v-slot:[name]="slotProps">
5
- <slot :name="name" v-bind="slotProps" />
6
- </template>
7
- </KanbanBoard>
8
- </div>
9
- </template>
10
-
11
- <script>
12
- import KanbanBoard from './kanban/KanbanBoard.vue';
13
-
14
- export default {
15
- name: 'CrudKanban',
16
- components: {
17
- KanbanBoard
18
- },
19
- inject: [
20
- 'displayMode',
21
- 'displayModes'
22
- ],
23
- computed: {
24
- currentDisplayMode() {
25
- if (!this.displayMode) return 1;
26
- if (this.displayMode.value !== undefined) {
27
- return this.displayMode.value;
28
- }
29
- if (typeof this.displayMode === 'function') {
30
- return this.displayMode();
31
- }
32
- return this.displayMode;
33
- }
34
- }
35
- };
36
- </script>
1
+ <template>
2
+ <div v-if="currentDisplayMode == displayModes.MODE_KANBAN">
3
+ <KanbanBoard>
4
+ <template v-for="(slot, name) in $scopedSlots" v-slot:[name]="slotProps">
5
+ <slot :name="name" v-bind="slotProps" />
6
+ </template>
7
+ </KanbanBoard>
8
+ </div>
9
+ </template>
10
+
11
+ <script>
12
+ import KanbanBoard from './kanban/KanbanBoard.vue';
13
+
14
+ export default {
15
+ name: 'CrudKanban',
16
+ components: {
17
+ KanbanBoard
18
+ },
19
+ inject: [
20
+ 'displayMode',
21
+ 'displayModes'
22
+ ],
23
+ computed: {
24
+ currentDisplayMode() {
25
+ if (!this.displayMode) return 1;
26
+ if (this.displayMode.value !== undefined) {
27
+ return this.displayMode.value;
28
+ }
29
+ if (typeof this.displayMode === 'function') {
30
+ return this.displayMode();
31
+ }
32
+ return this.displayMode;
33
+ }
34
+ }
35
+ };
36
+ </script>
@@ -2,7 +2,7 @@
2
2
  <div>
3
3
  <!-- Modal de formulario -->
4
4
  <b-modal :id="'modal-form-item-' + modelName" hide-footer size="xl" :title="title" no-close-on-backdrop>
5
- <b-overlay :show="loading" rounded="sm">
5
+ <b-overlay :show="loadingValue" rounded="sm">
6
6
  <template v-if="validate">
7
7
  <form @submit="saveItem">
8
8
  <template v-if="reactiveItem">
@@ -12,8 +12,8 @@
12
12
  </b-form-group>
13
13
  </slot>
14
14
  </template>
15
- <b-button block type="submit" variant="success" :disabled="loading">
16
- <b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
15
+ <b-button block type="submit" variant="success" :disabled="loadingValue">
16
+ <b-spinner small v-if="loadingValue"></b-spinner>{{ messageSave }}
17
17
  </b-button>
18
18
  </form>
19
19
  </template>
@@ -25,8 +25,8 @@
25
25
  </b-form-group>
26
26
  </slot>
27
27
  </template>
28
- <b-button block type="submit" variant="success" :disabled="loading" @click="saveItem()">
29
- <b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
28
+ <b-button block type="submit" variant="success" :disabled="loadingValue" @click="saveItem()">
29
+ <b-spinner small v-if="loadingValue"></b-spinner>{{ messageSave }}
30
30
  </b-button>
31
31
  </template>
32
32
  </b-overlay>
@@ -51,13 +51,13 @@
51
51
  <!-- Modal de importación -->
52
52
  <b-modal ref="modal-import" title="Importar" hide-footer v-if="showImport">
53
53
  <slot name="import" v-bind:item="item" v-if="item">
54
- <b-overlay :show="loading" rounded="sm">
54
+ <b-overlay :show="loadingValue" rounded="sm">
55
55
  <b-form-file v-model="fileImport" :state="Boolean(fileImport)" browse-text="Explorar"
56
56
  placeholder="Importar..." drop-placeholder="Arrastrar Archivo aquí..."></b-form-file>
57
57
  <div class="text-center mt-3">
58
- <b-button variant="info" v-on:click="importItems()" :disabled="loading">
58
+ <b-button variant="info" v-on:click="importItems()" :disabled="loadingValue">
59
59
  <b-icon-cloud-upload></b-icon-cloud-upload>
60
- {{ loading ? "Cargando..." : "Importar" }}
60
+ {{ loadingValue ? "Cargando..." : "Importar" }}
61
61
  </b-button>
62
62
  </div>
63
63
  </b-overlay>
@@ -67,20 +67,36 @@
67
67
  <!-- Modal de exportación -->
68
68
  <b-modal ref="modal-export" title="Exportar" hide-footer v-if="showExport">
69
69
  <slot name="export" v-bind:item="item" v-if="item">
70
- <b-overlay :show="loading" rounded="sm">
70
+ <b-overlay :show="loadingValue" rounded="sm">
71
71
 
72
72
  <p v-if="selectedItems.length">Se exportará {{ selectedItems.length }} elementos.</p>
73
73
  <p v-else>Se exportará la consulta actual.</p>
74
74
 
75
- <select class="form-control" v-model="exportFormat">
76
- <option value="JSON">JSON</option>
77
- <option value="XLSX">XLSX</option>
78
- </select>
75
+ <b-form-group label="Seleccione el formato de exportación:" class="mt-3">
76
+ <div class="export-format-options">
77
+ <b-form-radio
78
+ v-model="exportFormat"
79
+ value="JSON"
80
+ class="export-format-radio"
81
+ >
82
+ <b-icon-file-text class="mr-2"></b-icon-file-text>
83
+ JSON
84
+ </b-form-radio>
85
+ <b-form-radio
86
+ v-model="exportFormat"
87
+ value="XLSX"
88
+ class="export-format-radio"
89
+ >
90
+ <b-icon-table class="mr-2"></b-icon-table>
91
+ XLSX
92
+ </b-form-radio>
93
+ </div>
94
+ </b-form-group>
79
95
 
80
96
  <div class="text-center mt-3">
81
- <b-button variant="info" v-on:click="exportItems()" :disabled="loading">
97
+ <b-button variant="info" v-on:click="exportItems()" :disabled="loadingValue">
82
98
  <b-icon-cloud-upload></b-icon-cloud-upload>
83
- {{ loading ? "Cargando..." : "Exportar" }}
99
+ {{ loadingValue ? "Cargando..." : "Exportar" }}
84
100
  </b-button>
85
101
  </div>
86
102
  </b-overlay>
@@ -118,6 +134,10 @@ export default {
118
134
  }
119
135
  // Si no, usar el item inyectado directamente
120
136
  return this.item;
137
+ },
138
+ // Computed property para manejar loading como objeto reactivo o booleano
139
+ loadingValue() {
140
+ return this.loading && this.loading.value !== undefined ? this.loading.value : this.loading;
121
141
  }
122
142
  },
123
143
  watch: {
@@ -132,3 +152,66 @@ export default {
132
152
  }
133
153
  };
134
154
  </script>
155
+
156
+ <style scoped>
157
+ .export-format-options {
158
+ display: flex;
159
+ gap: 1rem;
160
+ justify-content: center;
161
+ flex-wrap: wrap;
162
+ }
163
+
164
+ .export-format-radio {
165
+ flex: 1;
166
+ min-width: 150px;
167
+ padding: 1rem;
168
+ border: 2px solid #dee2e6;
169
+ border-radius: 0.5rem;
170
+ cursor: pointer;
171
+ transition: all 0.3s ease;
172
+ text-align: center;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ background-color: #fff;
177
+ }
178
+
179
+ .export-format-radio:hover {
180
+ border-color: #007bff;
181
+ background-color: #f8f9fa;
182
+ transform: translateY(-2px);
183
+ box-shadow: 0 2px 8px rgba(0, 123, 255, 0.2);
184
+ }
185
+
186
+ .export-format-radio >>> .custom-control-input:checked ~ .custom-control-label {
187
+ color: #007bff;
188
+ font-weight: 600;
189
+ }
190
+
191
+ .export-format-radio >>> .custom-control-input:checked ~ .custom-control-label::before {
192
+ border-color: #007bff;
193
+ background-color: #007bff;
194
+ }
195
+
196
+ .export-format-radio >>> .custom-control-label {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: center;
200
+ width: 100%;
201
+ cursor: pointer;
202
+ font-size: 1rem;
203
+ }
204
+
205
+ .export-format-radio >>> .custom-control-label::before {
206
+ margin-right: 0.5rem;
207
+ }
208
+
209
+ .export-format-radio >>> svg {
210
+ font-size: 1.5rem;
211
+ color: #495057;
212
+ }
213
+
214
+ .export-format-radio >>> .custom-control-input:checked ~ .custom-control-label svg {
215
+ color: #007bff;
216
+ }
217
+ </style>