vue-laravel-crud 1.8.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vue-laravel-crud.esm.js +8831 -3010
- package/dist/vue-laravel-crud.min.js +3 -3
- package/dist/vue-laravel-crud.ssr.js +18602 -12923
- package/package.json +7 -1
- package/src/components/CrudCards.vue +91 -0
- package/src/components/CrudCustom.vue +29 -0
- package/src/components/CrudFilters.vue +104 -0
- package/src/components/CrudHeader.vue +129 -0
- package/src/components/CrudKanban.vue +20 -0
- package/src/components/CrudModals.vue +106 -0
- package/src/components/CrudPagination.vue +73 -0
- package/src/components/CrudTable.vue +70 -0
- package/src/components/kanban/KanbanBoard.vue +65 -0
- package/src/components/kanban/KanbanCard.vue +103 -0
- package/src/components/kanban/KanbanColumn.vue +110 -0
- package/src/components/table/TableCell.vue +91 -0
- package/src/components/table/TableHeader.vue +113 -0
- package/src/components/table/TableRow.vue +39 -0
- package/src/vue-laravel-crud.vue +211 -1819
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-laravel-crud",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/clonixdev/vue-laravel-crud",
|
|
6
6
|
"main": "dist/vue-laravel-crud.ssr.js",
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"scripts": {
|
|
16
16
|
"serve": "vue-cli-service serve dev/serve.js",
|
|
17
|
+
"demo": "concurrently \"npm run demo:api\" \"npm run demo:app\"",
|
|
18
|
+
"demo:api": "node dev/api/mockServer.js",
|
|
19
|
+
"demo:app": "vue-cli-service serve dev/demo/DemoApp.vue",
|
|
17
20
|
"build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js",
|
|
18
21
|
"build:ssr": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs",
|
|
19
22
|
"build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es",
|
|
@@ -42,7 +45,10 @@
|
|
|
42
45
|
"@vue/cli-service": "^5.0.8",
|
|
43
46
|
"@vuex-orm/core": "^0.36.4",
|
|
44
47
|
"@vuex-orm/plugin-axios": "^0.9.4",
|
|
48
|
+
"concurrently": "^7.6.0",
|
|
49
|
+
"cors": "^2.8.5",
|
|
45
50
|
"cross-env": "^7.0.3",
|
|
51
|
+
"express": "^4.18.2",
|
|
46
52
|
"minimist": "^1.2.5",
|
|
47
53
|
"node-sass": "^8.0.0",
|
|
48
54
|
"rollup": "^2.68.0",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-else-if="displayMode == displayModes.MODE_CARDS">
|
|
3
|
+
<draggable
|
|
4
|
+
v-model="items"
|
|
5
|
+
:group="draggableGroup"
|
|
6
|
+
:draggable="orderable ? '.item' : '.none'"
|
|
7
|
+
@start="drag = true"
|
|
8
|
+
@end="drag = false"
|
|
9
|
+
@sort="onSort()"
|
|
10
|
+
@add="onDraggableAdded($event)"
|
|
11
|
+
@change="onDraggableChange($event)"
|
|
12
|
+
:options="draggableOptions"
|
|
13
|
+
>
|
|
14
|
+
<masonry
|
|
15
|
+
:cols="{ default: 12 / colLg, 1400: 12 / colXl, 1200: 12 / colLg, 1000: 12 / colMd, 700: 12 / colSm, 400: 12 / colXs }"
|
|
16
|
+
:gutter="{ default: '15px', 700: '15px' }"
|
|
17
|
+
>
|
|
18
|
+
<div v-for="(item, itemIndex) in itemsList" v-bind:key="itemIndex" class="item">
|
|
19
|
+
<slot name="card" v-bind:item="item">
|
|
20
|
+
<ItemCard
|
|
21
|
+
:item="item"
|
|
22
|
+
:columns="columns"
|
|
23
|
+
:index="itemIndex"
|
|
24
|
+
:cardClass="cardClass"
|
|
25
|
+
:cardHideFooter="cardHideFooter"
|
|
26
|
+
:itemValue="itemValue"
|
|
27
|
+
:getStateValue="getStateValue"
|
|
28
|
+
:getArrayValue="getArrayValue"
|
|
29
|
+
:showItem="showItem"
|
|
30
|
+
:updateItem="updateItem"
|
|
31
|
+
:removeItem="removeItem"
|
|
32
|
+
/>
|
|
33
|
+
</slot>
|
|
34
|
+
</div>
|
|
35
|
+
</masonry>
|
|
36
|
+
</draggable>
|
|
37
|
+
|
|
38
|
+
<p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
|
|
39
|
+
{{ messageEmptyResults }}
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
import draggable from "vuedraggable";
|
|
46
|
+
import VueMasonry from 'vue-masonry-css';
|
|
47
|
+
import ItemCard from '../ItemCard.vue';
|
|
48
|
+
|
|
49
|
+
export default {
|
|
50
|
+
name: 'CrudCards',
|
|
51
|
+
components: {
|
|
52
|
+
draggable,
|
|
53
|
+
VueMasonry,
|
|
54
|
+
ItemCard
|
|
55
|
+
},
|
|
56
|
+
inject: [
|
|
57
|
+
'displayMode',
|
|
58
|
+
'displayModes',
|
|
59
|
+
'items',
|
|
60
|
+
'draggableGroup',
|
|
61
|
+
'orderable',
|
|
62
|
+
'draggableOptions',
|
|
63
|
+
'itemsList',
|
|
64
|
+
'colLg',
|
|
65
|
+
'colXl',
|
|
66
|
+
'colMd',
|
|
67
|
+
'colSm',
|
|
68
|
+
'colXs',
|
|
69
|
+
'columns',
|
|
70
|
+
'cardClass',
|
|
71
|
+
'cardHideFooter',
|
|
72
|
+
'itemValue',
|
|
73
|
+
'getStateValue',
|
|
74
|
+
'getArrayValue',
|
|
75
|
+
'showItem',
|
|
76
|
+
'updateItem',
|
|
77
|
+
'removeItem',
|
|
78
|
+
'loading',
|
|
79
|
+
'infiniteScroll',
|
|
80
|
+
'messageEmptyResults',
|
|
81
|
+
'onSort',
|
|
82
|
+
'onDraggableAdded',
|
|
83
|
+
'onDraggableChange'
|
|
84
|
+
],
|
|
85
|
+
data() {
|
|
86
|
+
return {
|
|
87
|
+
drag: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
</script>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-else-if="displayMode == displayModes.MODE_CUSTOM">
|
|
3
|
+
<div :class="listContainerClass">
|
|
4
|
+
<p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
|
|
5
|
+
{{ messageEmptyResults }}
|
|
6
|
+
</p>
|
|
7
|
+
<div :class="listItemClass" v-for="(item, index) in itemsList" v-bind:key="index">
|
|
8
|
+
<slot name="card" v-bind:item="item"> </slot>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
export default {
|
|
16
|
+
name: 'CrudCustom',
|
|
17
|
+
inject: [
|
|
18
|
+
'displayMode',
|
|
19
|
+
'displayModes',
|
|
20
|
+
'listContainerClass',
|
|
21
|
+
'listItemClass',
|
|
22
|
+
'loading',
|
|
23
|
+
'items',
|
|
24
|
+
'infiniteScroll',
|
|
25
|
+
'messageEmptyResults',
|
|
26
|
+
'itemsList'
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
</script>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="px-3 py-2">
|
|
3
|
+
<div v-for="(column, indexc) in columns" :key="indexc">
|
|
4
|
+
<div v-if="isColumnHasFilter(column)">
|
|
5
|
+
<slot :name="'sidebar-filter-' + column.prop" v-bind:column="column" v-bind:filter="filter"
|
|
6
|
+
v-bind:internalFilterByProp="internalFilterByProp" v-if="internalFilterByProp(column.prop)">
|
|
7
|
+
<div class="form-group" v-if="column.type == 'boolean'">
|
|
8
|
+
<label>{{ column.label }}</label>
|
|
9
|
+
|
|
10
|
+
<select class="form-control" v-model="internalFilterByProp(column.prop).value"
|
|
11
|
+
@change="onChangeFilter($event)">
|
|
12
|
+
<option value=""></option>
|
|
13
|
+
<option value="1">Sí</option>
|
|
14
|
+
<option value="0">No</option>
|
|
15
|
+
</select>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="form-group" v-else-if="column.type == 'date'">
|
|
18
|
+
<div class="row">
|
|
19
|
+
<div class="col-6">
|
|
20
|
+
<b-form-datepicker v-model="internalFilterByProp(column.prop + '_from').value
|
|
21
|
+
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="col-6">
|
|
24
|
+
<b-form-datepicker v-model="internalFilterByProp(column.prop + '_to').value
|
|
25
|
+
" today-button reset-button close-button locale="es"></b-form-datepicker>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="form-group" v-else-if="column.type == 'state'">
|
|
31
|
+
<label>{{ column.label }}</label>
|
|
32
|
+
|
|
33
|
+
<select class="form-control" v-model="internalFilterByProp(column.prop).value"
|
|
34
|
+
@change="onChangeFilter($event)" v-if="optionsLoaded">
|
|
35
|
+
<option value=""></option>
|
|
36
|
+
<option :value="option.id ? option.id : option.value" v-for="option in column.options"
|
|
37
|
+
:key="option.id ? option.id : option.value">
|
|
38
|
+
{{
|
|
39
|
+
option.text
|
|
40
|
+
? option.text
|
|
41
|
+
: option.label
|
|
42
|
+
? option.label
|
|
43
|
+
: ""
|
|
44
|
+
}}
|
|
45
|
+
</option>
|
|
46
|
+
</select>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="form-group" v-else-if="column.type == 'array'">
|
|
50
|
+
<label>{{ column.label }}</label>
|
|
51
|
+
|
|
52
|
+
<select class="form-control" v-model="internalFilterByProp(column.prop).value"
|
|
53
|
+
@change="onChangeFilter($event)" v-if="optionsLoaded">
|
|
54
|
+
<option value=""></option>
|
|
55
|
+
<template v-if="column.options">
|
|
56
|
+
<option :value="option.id ? option.id : option.value" v-for="option in column.options"
|
|
57
|
+
:key="option.id ? option.id : option.value">
|
|
58
|
+
{{
|
|
59
|
+
option.text
|
|
60
|
+
? option.text
|
|
61
|
+
: option.label
|
|
62
|
+
? option.label
|
|
63
|
+
: ""
|
|
64
|
+
}}
|
|
65
|
+
</option>
|
|
66
|
+
</template>
|
|
67
|
+
</select>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="form-group" v-else>
|
|
71
|
+
<label>{{ column.label }}</label>
|
|
72
|
+
|
|
73
|
+
<input class="form-control" v-model.lazy="internalFilterByProp(column.prop).value"
|
|
74
|
+
@change="onChangeFilter($event)" />
|
|
75
|
+
</div>
|
|
76
|
+
</slot>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="mt-3 d-flex justify-content-center">
|
|
81
|
+
<button class="btn btn-light" @click="resetFilters()">
|
|
82
|
+
Reset
|
|
83
|
+
</button>
|
|
84
|
+
<button class="btn btn-info" @click="onChangeFilter($event)">
|
|
85
|
+
Filtrar
|
|
86
|
+
</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
90
|
+
|
|
91
|
+
<script>
|
|
92
|
+
export default {
|
|
93
|
+
name: 'CrudFilters',
|
|
94
|
+
inject: [
|
|
95
|
+
'columns',
|
|
96
|
+
'isColumnHasFilter',
|
|
97
|
+
'filter',
|
|
98
|
+
'internalFilterByProp',
|
|
99
|
+
'optionsLoaded',
|
|
100
|
+
'onChangeFilter',
|
|
101
|
+
'resetFilters'
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
</script>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="crud-header" v-if="showHeader">
|
|
3
|
+
<h4 class="crud-title" v-if="showTitle">{{ title }}</h4>
|
|
4
|
+
|
|
5
|
+
<b-sidebar v-model="filterSidebarOpen" 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="displayMode == displayModes.MODE_TABLE"></b-icon-card-list>
|
|
34
|
+
<b-icon-table v-else-if="displayMode == 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
|
+
};
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<style scoped>
|
|
97
|
+
.crud-header {
|
|
98
|
+
display: flex;
|
|
99
|
+
justify-content: space-between;
|
|
100
|
+
max-height: 3rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.crud-title {
|
|
104
|
+
margin: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.crud-search {
|
|
108
|
+
max-width: 15rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.crud-search .btn {
|
|
112
|
+
border-top-left-radius: 0;
|
|
113
|
+
border-bottom-left-radius: 0;
|
|
114
|
+
border-top-right-radius: 0.375rem;
|
|
115
|
+
border-bottom-right-radius: 0.375rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.crud-search .btn.open {
|
|
119
|
+
border-top-right-radius: 0;
|
|
120
|
+
border-bottom-right-radius: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.table-options {
|
|
124
|
+
margin-bottom: 1rem;
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: flex-end;
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-else-if="displayMode == displayModes.MODE_KANBAN">
|
|
3
|
+
<KanbanBoard />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
import KanbanBoard from './kanban/KanbanBoard.vue';
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
name: 'CrudKanban',
|
|
12
|
+
components: {
|
|
13
|
+
KanbanBoard
|
|
14
|
+
},
|
|
15
|
+
inject: [
|
|
16
|
+
'displayMode',
|
|
17
|
+
'displayModes'
|
|
18
|
+
]
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!-- Modal de formulario -->
|
|
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">
|
|
6
|
+
<template v-if="validate">
|
|
7
|
+
<form @submit="saveItem">
|
|
8
|
+
<slot name="form" v-bind:item="item" v-if="item">
|
|
9
|
+
<b-form-group label="Nombre:" description="Nombre ">
|
|
10
|
+
<b-form-input v-model="item.title" type="text" required placeholder="Nombre"></b-form-input>
|
|
11
|
+
</b-form-group>
|
|
12
|
+
</slot>
|
|
13
|
+
<b-button block type="submit" variant="success" :disabled="loading">
|
|
14
|
+
<b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
|
|
15
|
+
</b-button>
|
|
16
|
+
</form>
|
|
17
|
+
</template>
|
|
18
|
+
<template v-if="!validate">
|
|
19
|
+
<slot name="form" v-bind:item="item" v-if="item">
|
|
20
|
+
<b-form-group :label="key" v-for="(value, key) in item" :key="key">
|
|
21
|
+
<b-form-input v-model="item[key]" type="text" required></b-form-input>
|
|
22
|
+
</b-form-group>
|
|
23
|
+
</slot>
|
|
24
|
+
<b-button block type="submit" variant="success" :disabled="loading" @click="saveItem()">
|
|
25
|
+
<b-spinner small v-if="loading"></b-spinner>{{ messageSave }}
|
|
26
|
+
</b-button>
|
|
27
|
+
</template>
|
|
28
|
+
</b-overlay>
|
|
29
|
+
</b-modal>
|
|
30
|
+
|
|
31
|
+
<!-- Modal de visualización -->
|
|
32
|
+
<b-modal :id="'modal-show-item-' + modelName" hide-footer size="xl" :title="title" no-close-on-backdrop>
|
|
33
|
+
<slot name="show" v-bind:item="item" v-if="item">
|
|
34
|
+
<b-list-group>
|
|
35
|
+
<b-list-group-item v-for="(value, key) in item" :key="key">
|
|
36
|
+
<b-row class="w-100">
|
|
37
|
+
<b-col cols="4" class="font-weight-bold">{{ key }}</b-col>
|
|
38
|
+
<b-col cols="8">{{ JSON.stringify(value) }}</b-col>
|
|
39
|
+
</b-row>
|
|
40
|
+
</b-list-group-item>
|
|
41
|
+
</b-list-group>
|
|
42
|
+
</slot>
|
|
43
|
+
</b-modal>
|
|
44
|
+
|
|
45
|
+
<!-- Modal de importación -->
|
|
46
|
+
<b-modal ref="modal-import" title="Importar" hide-footer v-if="showImport">
|
|
47
|
+
<slot name="import" v-bind:item="item" v-if="item">
|
|
48
|
+
<b-overlay :show="loading" rounded="sm">
|
|
49
|
+
<b-form-file v-model="fileImport" :state="Boolean(fileImport)" browse-text="Explorar"
|
|
50
|
+
placeholder="Importar..." drop-placeholder="Arrastrar Archivo aquí..."></b-form-file>
|
|
51
|
+
<div class="text-center mt-3">
|
|
52
|
+
<b-button variant="info" v-on:click="importItems()" :disabled="loading">
|
|
53
|
+
<b-icon-cloud-upload></b-icon-cloud-upload>
|
|
54
|
+
{{ loading ? "Cargando..." : "Importar" }}
|
|
55
|
+
</b-button>
|
|
56
|
+
</div>
|
|
57
|
+
</b-overlay>
|
|
58
|
+
</slot>
|
|
59
|
+
</b-modal>
|
|
60
|
+
|
|
61
|
+
<!-- Modal de exportación -->
|
|
62
|
+
<b-modal ref="modal-export" title="Exportar" hide-footer v-if="showExport">
|
|
63
|
+
<slot name="export" v-bind:item="item" v-if="item">
|
|
64
|
+
<b-overlay :show="loading" rounded="sm">
|
|
65
|
+
|
|
66
|
+
<p v-if="selectedItems.length">Se exportará {{ selectedItems.length }} elementos.</p>
|
|
67
|
+
<p v-else>Se exportará la consulta actual.</p>
|
|
68
|
+
|
|
69
|
+
<select class="form-control" v-model="exportFormat">
|
|
70
|
+
<option value="JSON">JSON</option>
|
|
71
|
+
<option value="XLSX">XLSX</option>
|
|
72
|
+
</select>
|
|
73
|
+
|
|
74
|
+
<div class="text-center mt-3">
|
|
75
|
+
<b-button variant="info" v-on:click="exportItems()" :disabled="loading">
|
|
76
|
+
<b-icon-cloud-upload></b-icon-cloud-upload>
|
|
77
|
+
{{ loading ? "Cargando..." : "Exportar" }}
|
|
78
|
+
</b-button>
|
|
79
|
+
</div>
|
|
80
|
+
</b-overlay>
|
|
81
|
+
</slot>
|
|
82
|
+
</b-modal>
|
|
83
|
+
</div>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<script>
|
|
87
|
+
export default {
|
|
88
|
+
name: 'CrudModals',
|
|
89
|
+
inject: [
|
|
90
|
+
'modelName',
|
|
91
|
+
'title',
|
|
92
|
+
'loading',
|
|
93
|
+
'validate',
|
|
94
|
+
'item',
|
|
95
|
+
'messageSave',
|
|
96
|
+
'showImport',
|
|
97
|
+
'showExport',
|
|
98
|
+
'fileImport',
|
|
99
|
+
'selectedItems',
|
|
100
|
+
'exportFormat',
|
|
101
|
+
'saveItem',
|
|
102
|
+
'importItems',
|
|
103
|
+
'exportItems'
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
</script>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!-- Infinite Loading -->
|
|
4
|
+
<infinite-loading
|
|
5
|
+
ref="infiniteLoading"
|
|
6
|
+
@infinite="infiniteHandler"
|
|
7
|
+
v-if="infiniteScroll"
|
|
8
|
+
:forceUseInfiniteWrapper="true"
|
|
9
|
+
:key="infiniteScrollKey"
|
|
10
|
+
>
|
|
11
|
+
<div slot="spinner">
|
|
12
|
+
<div class="text-center">{{ messageLoading }}</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div slot="no-more">
|
|
15
|
+
<div class="text-center" v-if="!loading">{{ messageNoMore }}</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div slot="no-results">
|
|
18
|
+
<div class="text-center" v-if="!loading">{{ items.length == 0 ? messageEmptyResults : messageNoMore }}</div>
|
|
19
|
+
</div>
|
|
20
|
+
</infinite-loading>
|
|
21
|
+
|
|
22
|
+
<!-- Paginador -->
|
|
23
|
+
<div class="paginator-data" v-if="!infiniteScroll">
|
|
24
|
+
Filas: {{ pagination.total }} | xPág: {{ pagination.per_page }} | Pág: {{ pagination.current_page }} |
|
|
25
|
+
Seleccionados: {{ selectedItems.length }}
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="crud-paginator" v-if="!infiniteScroll">
|
|
29
|
+
<b-pagination
|
|
30
|
+
v-if="showPaginator"
|
|
31
|
+
v-model="pagination.current_page"
|
|
32
|
+
:total-rows="pagination.total"
|
|
33
|
+
:per-page="pagination.per_page"
|
|
34
|
+
@change="onPaginationChange($event)"
|
|
35
|
+
></b-pagination>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
import InfiniteLoading from 'vue-infinite-loading';
|
|
42
|
+
|
|
43
|
+
export default {
|
|
44
|
+
name: 'CrudPagination',
|
|
45
|
+
components: {
|
|
46
|
+
InfiniteLoading
|
|
47
|
+
},
|
|
48
|
+
inject: [
|
|
49
|
+
'infiniteScroll',
|
|
50
|
+
'infiniteScrollKey',
|
|
51
|
+
'messageLoading',
|
|
52
|
+
'messageNoMore',
|
|
53
|
+
'messageEmptyResults',
|
|
54
|
+
'loading',
|
|
55
|
+
'items',
|
|
56
|
+
'pagination',
|
|
57
|
+
'selectedItems',
|
|
58
|
+
'showPaginator',
|
|
59
|
+
'infiniteHandler',
|
|
60
|
+
'onPaginationChange'
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<style scoped>
|
|
66
|
+
.crud-pagination {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
width: 100%;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
margin-top: 1rem;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="['table-responsive', tableContainerClass]" v-if="displayMode == displayModes.MODE_TABLE">
|
|
3
|
+
<table :class="['table table-hover table-striped w-100', tableClass]">
|
|
4
|
+
<TableHeader />
|
|
5
|
+
|
|
6
|
+
<draggable
|
|
7
|
+
v-model="items"
|
|
8
|
+
:group="draggableGroup"
|
|
9
|
+
tag="tbody"
|
|
10
|
+
:draggable="orderable ? '.item' : '.none'"
|
|
11
|
+
@start="drag = true"
|
|
12
|
+
@end="drag = false"
|
|
13
|
+
@sort="onSort()"
|
|
14
|
+
@add="onDraggableAdded($event)"
|
|
15
|
+
@change="onDraggableChange($event)"
|
|
16
|
+
:options="draggableOptions"
|
|
17
|
+
>
|
|
18
|
+
<TableRow
|
|
19
|
+
v-for="(item, index) in itemsList"
|
|
20
|
+
v-bind:key="index"
|
|
21
|
+
:item="item"
|
|
22
|
+
:index="index"
|
|
23
|
+
:grouped="grouped"
|
|
24
|
+
/>
|
|
25
|
+
</draggable>
|
|
26
|
+
</table>
|
|
27
|
+
|
|
28
|
+
<p v-if="!loading && items && items.length == 0 && !infiniteScroll" class="p-3">
|
|
29
|
+
{{ messageEmptyResults }}
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script>
|
|
35
|
+
import draggable from "vuedraggable";
|
|
36
|
+
import TableHeader from './table/TableHeader.vue';
|
|
37
|
+
import TableRow from './table/TableRow.vue';
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
name: 'CrudTable',
|
|
41
|
+
components: {
|
|
42
|
+
draggable,
|
|
43
|
+
TableHeader,
|
|
44
|
+
TableRow
|
|
45
|
+
},
|
|
46
|
+
inject: [
|
|
47
|
+
'displayMode',
|
|
48
|
+
'displayModes',
|
|
49
|
+
'tableContainerClass',
|
|
50
|
+
'tableClass',
|
|
51
|
+
'items',
|
|
52
|
+
'draggableGroup',
|
|
53
|
+
'orderable',
|
|
54
|
+
'draggableOptions',
|
|
55
|
+
'itemsList',
|
|
56
|
+
'grouped',
|
|
57
|
+
'loading',
|
|
58
|
+
'infiniteScroll',
|
|
59
|
+
'messageEmptyResults',
|
|
60
|
+
'onSort',
|
|
61
|
+
'onDraggableAdded',
|
|
62
|
+
'onDraggableChange'
|
|
63
|
+
],
|
|
64
|
+
data() {
|
|
65
|
+
return {
|
|
66
|
+
drag: false
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
</script>
|