vue-laravel-crud 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vue-laravel-crud.esm.js +1359 -228
- package/dist/vue-laravel-crud.min.js +3 -3
- package/dist/vue-laravel-crud.ssr.js +1378 -241
- package/package.json +2 -1
- package/src/ItemCard.vue +68 -5
- package/src/components/CrudCards.vue +24 -4
- package/src/components/CrudCustom.vue +14 -2
- package/src/components/CrudFilters.vue +247 -32
- package/src/components/CrudHeader.vue +12 -2
- package/src/components/CrudKanban.vue +19 -3
- package/src/components/CrudModals.vue +49 -21
- package/src/components/CrudTable.vue +18 -2
- package/src/components/kanban/KanbanBoard.vue +9 -1
- package/src/components/kanban/KanbanCard.vue +68 -5
- package/src/components/kanban/KanbanColumn.vue +11 -3
- package/src/components/table/TableCell.vue +95 -24
- package/src/components/table/TableHeader.vue +46 -22
- package/src/components/table/TableRow.vue +5 -1
- package/src/vue-laravel-crud.vue +27 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-laravel-crud",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://github.com/clonixdev/vue-laravel-crud",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"cors": "^2.8.5",
|
|
54
54
|
"cross-env": "^7.0.3",
|
|
55
55
|
"express": "^4.18.2",
|
|
56
|
+
"marked": "^4.3.0",
|
|
56
57
|
"minimist": "^1.2.5",
|
|
57
58
|
"node-sass": "^8.0.0",
|
|
58
59
|
"rollup": "^2.68.0",
|
package/src/ItemCard.vue
CHANGED
|
@@ -15,11 +15,29 @@
|
|
|
15
15
|
{{ itemValue(column, item) }}
|
|
16
16
|
</span>
|
|
17
17
|
<span v-else-if="column.type === 'state'">
|
|
18
|
-
|
|
18
|
+
<template v-if="getStateOptionsForColumn(column, item).length > 0">
|
|
19
|
+
<b-badge
|
|
20
|
+
v-for="(option, optIndex) in getStateOptionsForColumn(column, item)"
|
|
21
|
+
:key="optIndex"
|
|
22
|
+
:variant="getStateBadgeVariant(option)"
|
|
23
|
+
class="mr-1"
|
|
24
|
+
>
|
|
25
|
+
{{ option.text }}
|
|
26
|
+
</b-badge>
|
|
27
|
+
</template>
|
|
28
|
+
<span v-else>
|
|
29
|
+
{{ itemValue(column, item) }}
|
|
30
|
+
</span>
|
|
19
31
|
</span>
|
|
20
32
|
<span v-else-if="column.type === 'array'">
|
|
21
33
|
{{ getArrayValue(itemValue(column, item), column.displayProp, column.options) }}
|
|
22
34
|
</span>
|
|
35
|
+
<span v-else-if="column.type === 'money' || column.type === 'price'">
|
|
36
|
+
{{ formatMoney(itemValue(column, item), column) }}
|
|
37
|
+
</span>
|
|
38
|
+
<span v-else-if="column.type === 'number' && (column.thousandsSeparator || column.decimalSeparator || column.decimals !== undefined)">
|
|
39
|
+
{{ formatNumber(itemValue(column, item), column) }}
|
|
40
|
+
</span>
|
|
23
41
|
<span v-else>
|
|
24
42
|
{{ itemValue(column, item) }}
|
|
25
43
|
</span>
|
|
@@ -29,11 +47,14 @@
|
|
|
29
47
|
</slot>
|
|
30
48
|
<template v-slot:footer>
|
|
31
49
|
<b-button-group>
|
|
32
|
-
<slot name="
|
|
50
|
+
<slot name="rowActions" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
|
|
33
51
|
v-bind:updateItem="updateItem" v-bind:removeItem="removeItem">
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
<slot name="rowAction" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
|
|
53
|
+
v-bind:updateItem="updateItem" v-bind:removeItem="removeItem">
|
|
54
|
+
<b-button variant="primary" @click="showItem(item.id, index)"><b-icon-eye></b-icon-eye></b-button>
|
|
55
|
+
<b-button variant="secondary" @click="updateItem(item.id, index)"><b-icon-pencil></b-icon-pencil></b-button>
|
|
56
|
+
<b-button variant="danger" @click="removeItem(item.id, index)"><b-icon-trash></b-icon-trash></b-button>
|
|
57
|
+
</slot>
|
|
37
58
|
</slot>
|
|
38
59
|
</b-button-group>
|
|
39
60
|
</template>
|
|
@@ -55,11 +76,53 @@
|
|
|
55
76
|
cardHideFooter: Boolean,
|
|
56
77
|
itemValue: Function,
|
|
57
78
|
getStateValue: Function,
|
|
79
|
+
getStateOptions: Function,
|
|
80
|
+
getStateBadgeVariant: Function,
|
|
58
81
|
getArrayValue: Function,
|
|
59
82
|
showItem: Function,
|
|
60
83
|
updateItem: Function,
|
|
61
84
|
removeItem: Function,
|
|
62
85
|
},
|
|
86
|
+
methods: {
|
|
87
|
+
getStateOptionsForColumn(column, item) {
|
|
88
|
+
if (column.type === 'state' && column.options) {
|
|
89
|
+
return this.getStateOptions(this.itemValue(column, item), column.options);
|
|
90
|
+
}
|
|
91
|
+
return [];
|
|
92
|
+
},
|
|
93
|
+
formatNumber(value, column) {
|
|
94
|
+
if (value === null || value === undefined || value === '') {
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const numValue = parseFloat(value);
|
|
99
|
+
if (isNaN(numValue)) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const thousandsSep = column.thousandsSeparator || '.';
|
|
104
|
+
const decimalSep = column.decimalSeparator || ',';
|
|
105
|
+
const decimals = column.decimals !== undefined ? column.decimals : (numValue % 1 === 0 ? 0 : 2);
|
|
106
|
+
|
|
107
|
+
// Formatear número con separadores
|
|
108
|
+
const parts = numValue.toFixed(decimals).split('.');
|
|
109
|
+
const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep);
|
|
110
|
+
const decimalPart = parts[1] || '';
|
|
111
|
+
|
|
112
|
+
if (decimals > 0 && decimalPart) {
|
|
113
|
+
return `${integerPart}${decimalSep}${decimalPart}`;
|
|
114
|
+
}
|
|
115
|
+
return integerPart;
|
|
116
|
+
},
|
|
117
|
+
formatMoney(value, column) {
|
|
118
|
+
const formatted = this.formatNumber(value, column);
|
|
119
|
+
if (formatted === '') {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
const symbol = column.symbol || '$';
|
|
123
|
+
return `${symbol}${formatted}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
63
126
|
};
|
|
64
127
|
</script>
|
|
65
128
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="
|
|
2
|
+
<div v-if="currentDisplayMode == displayModes.MODE_CARDS">
|
|
3
3
|
<draggable
|
|
4
4
|
v-model="items"
|
|
5
5
|
:group="draggableGroup"
|
|
@@ -24,12 +24,18 @@
|
|
|
24
24
|
:cardClass="cardClass"
|
|
25
25
|
:cardHideFooter="cardHideFooter"
|
|
26
26
|
:itemValue="itemValue"
|
|
27
|
-
:getStateValue="getStateValue"
|
|
27
|
+
:getStateValue="getStateValue"
|
|
28
|
+
:getStateOptions="getStateOptions"
|
|
29
|
+
:getStateBadgeVariant="getStateBadgeVariant"
|
|
28
30
|
:getArrayValue="getArrayValue"
|
|
29
31
|
:showItem="showItem"
|
|
30
32
|
:updateItem="updateItem"
|
|
31
|
-
:removeItem="removeItem"
|
|
32
|
-
|
|
33
|
+
:removeItem="removeItem"
|
|
34
|
+
>
|
|
35
|
+
<template v-for="(slot, name) in $scopedSlots" v-slot:[name]="slotProps">
|
|
36
|
+
<slot :name="name" v-bind="slotProps" />
|
|
37
|
+
</template>
|
|
38
|
+
</ItemCard>
|
|
33
39
|
</slot>
|
|
34
40
|
</div>
|
|
35
41
|
</masonry>
|
|
@@ -74,6 +80,8 @@ export default {
|
|
|
74
80
|
'cardHideFooter',
|
|
75
81
|
'itemValue',
|
|
76
82
|
'getStateValue',
|
|
83
|
+
'getStateOptions',
|
|
84
|
+
'getStateBadgeVariant',
|
|
77
85
|
'getArrayValue',
|
|
78
86
|
'showItem',
|
|
79
87
|
'updateItem',
|
|
@@ -89,6 +97,18 @@ export default {
|
|
|
89
97
|
return {
|
|
90
98
|
drag: false
|
|
91
99
|
};
|
|
100
|
+
},
|
|
101
|
+
computed: {
|
|
102
|
+
currentDisplayMode() {
|
|
103
|
+
if (!this.displayMode) return 1;
|
|
104
|
+
if (this.displayMode.value !== undefined) {
|
|
105
|
+
return this.displayMode.value;
|
|
106
|
+
}
|
|
107
|
+
if (typeof this.displayMode === 'function') {
|
|
108
|
+
return this.displayMode();
|
|
109
|
+
}
|
|
110
|
+
return this.displayMode;
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
113
|
};
|
|
94
114
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="
|
|
2
|
+
<div v-if="currentDisplayMode == displayModes.MODE_CUSTOM">
|
|
3
3
|
<div :class="listContainerClass">
|
|
4
4
|
<p v-if="!loading && itemsList && itemsList.length == 0 && !infiniteScroll" class="p-3">
|
|
5
5
|
{{ messageEmptyResults }}
|
|
@@ -24,6 +24,18 @@ export default {
|
|
|
24
24
|
'infiniteScroll',
|
|
25
25
|
'messageEmptyResults',
|
|
26
26
|
'itemsList'
|
|
27
|
-
]
|
|
27
|
+
],
|
|
28
|
+
computed: {
|
|
29
|
+
currentDisplayMode() {
|
|
30
|
+
if (!this.displayMode) return 1;
|
|
31
|
+
if (this.displayMode.value !== undefined) {
|
|
32
|
+
return this.displayMode.value;
|
|
33
|
+
}
|
|
34
|
+
if (typeof this.displayMode === 'function') {
|
|
35
|
+
return this.displayMode();
|
|
36
|
+
}
|
|
37
|
+
return this.displayMode;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
28
40
|
};
|
|
29
41
|
</script>
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<div v-for="(column, indexc) in columns" :key="indexc">
|
|
4
4
|
<div v-if="isColumnHasFilter(column)">
|
|
5
5
|
<slot :name="'sidebar-filter-' + column.prop" v-bind:column="column" v-bind:filter="filter"
|
|
6
|
-
v-bind:internalFilterByProp="internalFilterByProp" v-
|
|
6
|
+
v-bind:internalFilterByProp="internalFilterByProp" v-bind:getFilterForColumn="getFilterForColumn">
|
|
7
7
|
<div class="form-group" v-if="column.type == 'boolean'">
|
|
8
8
|
<label>{{ column.label }}</label>
|
|
9
9
|
|
|
10
|
-
<select class="form-control" v-model="
|
|
10
|
+
<select class="form-control" v-model="getFilterForColumn(column).value"
|
|
11
11
|
@change="onChangeFilter($event)">
|
|
12
12
|
<option value=""></option>
|
|
13
13
|
<option value="1">Sí</option>
|
|
@@ -17,31 +17,49 @@
|
|
|
17
17
|
<div class="form-group" v-else-if="column.type == 'date'">
|
|
18
18
|
<div class="row">
|
|
19
19
|
<div class="col-6">
|
|
20
|
-
<b-form-datepicker v-model="
|
|
20
|
+
<b-form-datepicker v-model="getFilterForDateFrom(column).value
|
|
21
21
|
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
22
22
|
</div>
|
|
23
23
|
<div class="col-6">
|
|
24
|
-
<b-form-datepicker v-model="
|
|
24
|
+
<b-form-datepicker v-model="getFilterForDateTo(column).value
|
|
25
25
|
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
|
|
30
|
+
<div class="form-group" v-else-if="column.type == 'number' || column.type == 'money'">
|
|
31
|
+
<label>{{ column.label }}</label>
|
|
32
|
+
<div class="row">
|
|
33
|
+
<div class="col-6">
|
|
34
|
+
<input
|
|
35
|
+
type="number"
|
|
36
|
+
class="form-control"
|
|
37
|
+
v-model.number="getFilterForDateFrom(column).value"
|
|
38
|
+
:step="column.type == 'money' ? '0.01' : '1'"
|
|
39
|
+
@change="onChangeFilter($event)"
|
|
40
|
+
placeholder="Desde" />
|
|
41
|
+
</div>
|
|
42
|
+
<div class="col-6">
|
|
43
|
+
<input
|
|
44
|
+
type="number"
|
|
45
|
+
class="form-control"
|
|
46
|
+
v-model.number="getFilterForDateTo(column).value"
|
|
47
|
+
:step="column.type == 'money' ? '0.01' : '1'"
|
|
48
|
+
@change="onChangeFilter($event)"
|
|
49
|
+
placeholder="Hasta" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
30
54
|
<div class="form-group" v-else-if="column.type == 'state'">
|
|
31
55
|
<label>{{ column.label }}</label>
|
|
32
56
|
|
|
33
|
-
<select class="form-control" v-model="
|
|
34
|
-
@change="onChangeFilter($event)" v-if="
|
|
57
|
+
<select class="form-control" v-model="getFilterForColumn(column).value"
|
|
58
|
+
@change="onChangeFilter($event)" v-if="column.options && Array.isArray(column.options)">
|
|
35
59
|
<option value=""></option>
|
|
36
|
-
<option :value="option.
|
|
37
|
-
:key="option.
|
|
38
|
-
{{
|
|
39
|
-
option.text
|
|
40
|
-
? option.text
|
|
41
|
-
: option.label
|
|
42
|
-
? option.label
|
|
43
|
-
: ""
|
|
44
|
-
}}
|
|
60
|
+
<option :value="option.value" v-for="option in column.options"
|
|
61
|
+
:key="option.value || option.id">
|
|
62
|
+
{{ option.text }}
|
|
45
63
|
</option>
|
|
46
64
|
</select>
|
|
47
65
|
</div>
|
|
@@ -49,34 +67,132 @@
|
|
|
49
67
|
<div class="form-group" v-else-if="column.type == 'array'">
|
|
50
68
|
<label>{{ column.label }}</label>
|
|
51
69
|
|
|
52
|
-
<select class="form-control" v-model="
|
|
53
|
-
@change="onChangeFilter($event)" v-if="
|
|
70
|
+
<select class="form-control" v-model="getFilterForColumn(column).value"
|
|
71
|
+
@change="onChangeFilter($event)" v-if="column.options && Array.isArray(column.options)">
|
|
54
72
|
<option value=""></option>
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
option.text
|
|
60
|
-
? option.text
|
|
61
|
-
: option.label
|
|
62
|
-
? option.label
|
|
63
|
-
: ""
|
|
64
|
-
}}
|
|
65
|
-
</option>
|
|
66
|
-
</template>
|
|
73
|
+
<option :value="option.value" v-for="option in column.options"
|
|
74
|
+
:key="option.value || option.id">
|
|
75
|
+
{{ option.text }}
|
|
76
|
+
</option>
|
|
67
77
|
</select>
|
|
68
78
|
</div>
|
|
69
79
|
|
|
70
80
|
<div class="form-group" v-else>
|
|
71
81
|
<label>{{ column.label }}</label>
|
|
72
82
|
|
|
73
|
-
<input class="form-control" v-model.lazy="
|
|
83
|
+
<input class="form-control" v-model.lazy="getFilterForColumn(column).value"
|
|
74
84
|
@change="onChangeFilter($event)" />
|
|
75
85
|
</div>
|
|
76
86
|
</slot>
|
|
77
87
|
</div>
|
|
78
88
|
</div>
|
|
79
89
|
|
|
90
|
+
<!-- Filtros custom -->
|
|
91
|
+
<div v-for="(customFilter, indexcf) in customFilters" :key="'custom-' + indexcf">
|
|
92
|
+
<div v-if="isCustomFilterEnabled(customFilter)">
|
|
93
|
+
<!-- Slot personalizado para filtro custom -->
|
|
94
|
+
<slot :name="'sidebar-filter-custom-' + customFilter.prop" v-bind:column="customFilter" v-bind:filter="filter"
|
|
95
|
+
v-bind:internalFilterByProp="internalFilterByProp" v-bind:getFilterForColumn="getFilterForColumn">
|
|
96
|
+
|
|
97
|
+
<!-- Si type es una función callback -->
|
|
98
|
+
<RenderCustomFilter
|
|
99
|
+
v-if="typeof customFilter.type === 'function'"
|
|
100
|
+
:render-function="customFilter.type"
|
|
101
|
+
:custom-filter="customFilter"
|
|
102
|
+
:filter="filter"
|
|
103
|
+
:internal-filter-by-prop="internalFilterByProp"
|
|
104
|
+
:get-filter-for-column="getFilterForColumn"
|
|
105
|
+
:on-change-filter="onChangeFilter"
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
<!-- Si type es string, usar la misma lógica que las columnas -->
|
|
109
|
+
<template v-else>
|
|
110
|
+
<div class="form-group" v-if="customFilter.type == 'boolean'">
|
|
111
|
+
<label>{{ customFilter.label }}</label>
|
|
112
|
+
|
|
113
|
+
<select class="form-control" v-model="getFilterForColumn(customFilter).value"
|
|
114
|
+
@change="onChangeFilter($event)">
|
|
115
|
+
<option value=""></option>
|
|
116
|
+
<option value="1">Sí</option>
|
|
117
|
+
<option value="0">No</option>
|
|
118
|
+
</select>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="form-group" v-else-if="customFilter.type == 'date'">
|
|
122
|
+
<label>{{ customFilter.label }}</label>
|
|
123
|
+
<div class="row">
|
|
124
|
+
<div class="col-6">
|
|
125
|
+
<b-form-datepicker v-model="getFilterForDateFrom(customFilter).value
|
|
126
|
+
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="col-6">
|
|
129
|
+
<b-form-datepicker v-model="getFilterForDateTo(customFilter).value
|
|
130
|
+
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="form-group" v-else-if="customFilter.type == 'number' || customFilter.type == 'money'">
|
|
136
|
+
<label>{{ customFilter.label }}</label>
|
|
137
|
+
<div class="row">
|
|
138
|
+
<div class="col-6">
|
|
139
|
+
<input
|
|
140
|
+
type="number"
|
|
141
|
+
class="form-control"
|
|
142
|
+
v-model.number="getFilterForDateFrom(customFilter).value"
|
|
143
|
+
:step="customFilter.type == 'money' ? '0.01' : '1'"
|
|
144
|
+
@change="onChangeFilter($event)"
|
|
145
|
+
placeholder="Desde" />
|
|
146
|
+
</div>
|
|
147
|
+
<div class="col-6">
|
|
148
|
+
<input
|
|
149
|
+
type="number"
|
|
150
|
+
class="form-control"
|
|
151
|
+
v-model.number="getFilterForDateTo(customFilter).value"
|
|
152
|
+
:step="customFilter.type == 'money' ? '0.01' : '1'"
|
|
153
|
+
@change="onChangeFilter($event)"
|
|
154
|
+
placeholder="Hasta" />
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div class="form-group" v-else-if="customFilter.type == 'state'">
|
|
160
|
+
<label>{{ customFilter.label }}</label>
|
|
161
|
+
|
|
162
|
+
<select class="form-control" v-model="getFilterForColumn(customFilter).value"
|
|
163
|
+
@change="onChangeFilter($event)" v-if="customFilter.options && Array.isArray(customFilter.options)">
|
|
164
|
+
<option value=""></option>
|
|
165
|
+
<option :value="option.value" v-for="option in customFilter.options"
|
|
166
|
+
:key="option.value || option.id">
|
|
167
|
+
{{ option.text }}
|
|
168
|
+
</option>
|
|
169
|
+
</select>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="form-group" v-else-if="customFilter.type == 'array'">
|
|
173
|
+
<label>{{ customFilter.label }}</label>
|
|
174
|
+
|
|
175
|
+
<select class="form-control" v-model="getFilterForColumn(customFilter).value"
|
|
176
|
+
@change="onChangeFilter($event)" v-if="customFilter.options && Array.isArray(customFilter.options)">
|
|
177
|
+
<option value=""></option>
|
|
178
|
+
<option :value="option.value" v-for="option in customFilter.options"
|
|
179
|
+
:key="option.value || option.id">
|
|
180
|
+
{{ option.text }}
|
|
181
|
+
</option>
|
|
182
|
+
</select>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="form-group" v-else>
|
|
186
|
+
<label>{{ customFilter.label }}</label>
|
|
187
|
+
|
|
188
|
+
<input class="form-control" v-model.lazy="getFilterForColumn(customFilter).value"
|
|
189
|
+
@change="onChangeFilter($event)" />
|
|
190
|
+
</div>
|
|
191
|
+
</template>
|
|
192
|
+
</slot>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
80
196
|
<div class="mt-3 d-flex justify-content-center">
|
|
81
197
|
<button class="btn btn-light" @click="resetFilters()">
|
|
82
198
|
Reset
|
|
@@ -89,16 +205,115 @@
|
|
|
89
205
|
</template>
|
|
90
206
|
|
|
91
207
|
<script>
|
|
208
|
+
// Componente funcional para renderizar filtros custom con callback
|
|
209
|
+
const RenderCustomFilter = {
|
|
210
|
+
functional: true,
|
|
211
|
+
props: {
|
|
212
|
+
renderFunction: {
|
|
213
|
+
type: Function,
|
|
214
|
+
required: true
|
|
215
|
+
},
|
|
216
|
+
customFilter: {
|
|
217
|
+
type: Object,
|
|
218
|
+
required: true
|
|
219
|
+
},
|
|
220
|
+
filter: {
|
|
221
|
+
type: Array,
|
|
222
|
+
default: () => []
|
|
223
|
+
},
|
|
224
|
+
internalFilterByProp: {
|
|
225
|
+
type: Function,
|
|
226
|
+
required: true
|
|
227
|
+
},
|
|
228
|
+
getFilterForColumn: {
|
|
229
|
+
type: Function,
|
|
230
|
+
required: true
|
|
231
|
+
},
|
|
232
|
+
onChangeFilter: {
|
|
233
|
+
type: Function,
|
|
234
|
+
required: true
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
render(h, context) {
|
|
238
|
+
const { renderFunction, customFilter, filter, internalFilterByProp, getFilterForColumn, onChangeFilter } = context.props;
|
|
239
|
+
return renderFunction(h, {
|
|
240
|
+
column: customFilter,
|
|
241
|
+
filter: filter,
|
|
242
|
+
internalFilterByProp: internalFilterByProp,
|
|
243
|
+
getFilterForColumn: getFilterForColumn,
|
|
244
|
+
onChangeFilter: onChangeFilter
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
92
249
|
export default {
|
|
93
250
|
name: 'CrudFilters',
|
|
251
|
+
components: {
|
|
252
|
+
RenderCustomFilter
|
|
253
|
+
},
|
|
94
254
|
inject: [
|
|
95
255
|
'columns',
|
|
256
|
+
'customFilters',
|
|
96
257
|
'isColumnHasFilter',
|
|
258
|
+
'isCustomFilterEnabled',
|
|
97
259
|
'filter',
|
|
98
260
|
'internalFilterByProp',
|
|
99
261
|
'optionsLoaded',
|
|
100
262
|
'onChangeFilter',
|
|
101
|
-
'resetFilters'
|
|
102
|
-
|
|
263
|
+
'resetFilters',
|
|
264
|
+
'setupFilters',
|
|
265
|
+
'internalFilters'
|
|
266
|
+
],
|
|
267
|
+
methods: {
|
|
268
|
+
// Método helper para obtener el filtro de forma segura, creándolo si no existe
|
|
269
|
+
getFilterForColumn(column) {
|
|
270
|
+
let filter = this.internalFilterByProp(column.prop);
|
|
271
|
+
|
|
272
|
+
// Si el filtro no existe, intentar inicializar los filtros
|
|
273
|
+
if (!filter) {
|
|
274
|
+
// Verificar si hay filtros inicializados
|
|
275
|
+
if (this.internalFilters && this.internalFilters.length === 0) {
|
|
276
|
+
this.setupFilters();
|
|
277
|
+
// Intentar obtener el filtro nuevamente después de inicializar
|
|
278
|
+
filter = this.internalFilterByProp(column.prop);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Si aún no existe, crear un objeto temporal para evitar errores
|
|
283
|
+
if (!filter) {
|
|
284
|
+
return {
|
|
285
|
+
value: null
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return filter;
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// Método helper específico para campos de fecha (from)
|
|
293
|
+
getFilterForDateFrom(column) {
|
|
294
|
+
const filter = this.internalFilterByProp(column.prop + '_from');
|
|
295
|
+
if (!filter) {
|
|
296
|
+
if (this.internalFilters && this.internalFilters.length === 0) {
|
|
297
|
+
this.setupFilters();
|
|
298
|
+
return this.internalFilterByProp(column.prop + '_from') || { value: null };
|
|
299
|
+
}
|
|
300
|
+
return { value: null };
|
|
301
|
+
}
|
|
302
|
+
return filter;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// Método helper específico para campos de fecha (to)
|
|
306
|
+
getFilterForDateTo(column) {
|
|
307
|
+
const filter = this.internalFilterByProp(column.prop + '_to');
|
|
308
|
+
if (!filter) {
|
|
309
|
+
if (this.internalFilters && this.internalFilters.length === 0) {
|
|
310
|
+
this.setupFilters();
|
|
311
|
+
return this.internalFilterByProp(column.prop + '_to') || { value: null };
|
|
312
|
+
}
|
|
313
|
+
return { value: null };
|
|
314
|
+
}
|
|
315
|
+
return filter;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
103
318
|
};
|
|
104
319
|
</script>
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
<b-button variant="info" v-if="enableFilters" @click="toggleFilters()">Filtros</b-button>
|
|
31
31
|
<b-button variant="info" @click="refresh()"><b-icon-arrow-clockwise></b-icon-arrow-clockwise></b-button>
|
|
32
32
|
<b-button variant="info" @click="toggleDisplayMode()" :disabled="loading" v-if="displayModeToggler">
|
|
33
|
-
<b-icon-card-list v-if="
|
|
34
|
-
<b-icon-table v-else-if="
|
|
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
35
|
</b-button>
|
|
36
36
|
|
|
37
37
|
<div class="crud-search m-0" v-if="showSearch">
|
|
@@ -94,6 +94,16 @@ export default {
|
|
|
94
94
|
sidebarVisible() {
|
|
95
95
|
// Acceder directamente al componente padre para obtener reactividad
|
|
96
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;
|
|
97
107
|
}
|
|
98
108
|
},
|
|
99
109
|
methods: {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="
|
|
3
|
-
<KanbanBoard
|
|
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>
|
|
4
8
|
</div>
|
|
5
9
|
</template>
|
|
6
10
|
|
|
@@ -15,6 +19,18 @@ export default {
|
|
|
15
19
|
inject: [
|
|
16
20
|
'displayMode',
|
|
17
21
|
'displayModes'
|
|
18
|
-
]
|
|
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
|
+
}
|
|
19
35
|
};
|
|
20
36
|
</script>
|