@drax/crud-vue 3.10.0 → 3.11.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/package.json +6 -6
- package/src/components/Crud.vue +7 -1
- package/src/components/CrudImportList.vue +72 -0
- package/src/components/CrudList.vue +8 -1
- package/src/components/CrudListGallery.vue +10 -1
- package/src/components/CrudListTable.vue +10 -1
- package/src/components/buttons/CrudImportButton.vue +42 -5
- package/src/composables/UseCrud.ts +58 -2
- package/src/stores/UseCrudStore.ts +19 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.11.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"format": "prettier --write src/"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@drax/common-front": "^3.
|
|
28
|
-
"@drax/crud-front": "^3.
|
|
29
|
-
"@drax/crud-share": "^3.
|
|
30
|
-
"@drax/media-vue": "^3.
|
|
27
|
+
"@drax/common-front": "^3.11.0",
|
|
28
|
+
"@drax/crud-front": "^3.11.0",
|
|
29
|
+
"@drax/crud-share": "^3.11.0",
|
|
30
|
+
"@drax/media-vue": "^3.11.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"pinia": "^3.0.4",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"vue-tsc": "^3.2.4",
|
|
51
51
|
"vuetify": "^3.11.8"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "9f9de6fe51c4f11d885385bd31a12edec799823e"
|
|
54
54
|
}
|
package/src/components/Crud.vue
CHANGED
|
@@ -15,7 +15,7 @@ const {entity} = defineProps({
|
|
|
15
15
|
|
|
16
16
|
const {
|
|
17
17
|
onView, onCreate, onEdit, onDelete, resetCrudStore,
|
|
18
|
-
operation, dialog, notify, message, doExport,
|
|
18
|
+
operation, dialog, notify, message, doExport, doImport,
|
|
19
19
|
prepareFilters
|
|
20
20
|
} = useCrud(entity);
|
|
21
21
|
|
|
@@ -56,6 +56,7 @@ const {xs} = useDisplay()
|
|
|
56
56
|
@edit="onEdit"
|
|
57
57
|
@delete="onDelete"
|
|
58
58
|
@export="doExport"
|
|
59
|
+
@import="doImport"
|
|
59
60
|
@view="onView"
|
|
60
61
|
>
|
|
61
62
|
|
|
@@ -112,6 +113,11 @@ const {xs} = useDisplay()
|
|
|
112
113
|
</slot>
|
|
113
114
|
</template>
|
|
114
115
|
|
|
116
|
+
<template v-slot:import-table="{ importFiles }">
|
|
117
|
+
<slot name="import-table" :importFiles="importFiles">
|
|
118
|
+
</slot>
|
|
119
|
+
</template>
|
|
120
|
+
|
|
115
121
|
</component>
|
|
116
122
|
</v-card>
|
|
117
123
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {useCrud} from "../composables/UseCrud";
|
|
3
|
+
import type {PropType} from "vue";
|
|
4
|
+
import type {IEntityCrud} from "@drax/crud-share";
|
|
5
|
+
import {useI18n} from "vue-i18n";
|
|
6
|
+
|
|
7
|
+
const {t} = useI18n()
|
|
8
|
+
const {entity} = defineProps({
|
|
9
|
+
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
importFiles, importListVisible, importLoading, importError
|
|
14
|
+
} = useCrud(entity);
|
|
15
|
+
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<v-card
|
|
20
|
+
v-if="importListVisible"
|
|
21
|
+
:loading="importLoading"
|
|
22
|
+
class="ma-3" density="compact" variant="outlined" color="secondary"
|
|
23
|
+
>
|
|
24
|
+
<v-card-title>
|
|
25
|
+
{{ t('action.imports') }}
|
|
26
|
+
</v-card-title>
|
|
27
|
+
<v-card-text>
|
|
28
|
+
<v-alert v-if="importError" type="error">
|
|
29
|
+
{{ t('error.crud.import') }}
|
|
30
|
+
</v-alert>
|
|
31
|
+
<template v-else>
|
|
32
|
+
<slot name="import-table" :importFiles="importFiles">
|
|
33
|
+
<v-table density="compact">
|
|
34
|
+
<thead>
|
|
35
|
+
<tr>
|
|
36
|
+
<th> {{t('crud.import.link')}}</th>
|
|
37
|
+
<th> {{t('crud.import.rows')}}</th>
|
|
38
|
+
<th> {{t('crud.import.success')}}</th>
|
|
39
|
+
<th> {{t('crud.import.error')}}</th>
|
|
40
|
+
<th> {{t('crud.import.time')}}</th>
|
|
41
|
+
</tr>
|
|
42
|
+
</thead>
|
|
43
|
+
<tbody>
|
|
44
|
+
<tr v-for="importFile in importFiles">
|
|
45
|
+
<td>
|
|
46
|
+
<a v-if="importFile.url" :href="importFile.url" target="_blank">
|
|
47
|
+
{{t('crud.import.report')}}
|
|
48
|
+
</a>
|
|
49
|
+
<span v-else>-</span>
|
|
50
|
+
</td>
|
|
51
|
+
<td>{{ importFile.rowCount }}</td>
|
|
52
|
+
<td>{{ importFile.successCount ?? '-' }}</td>
|
|
53
|
+
<td>{{ importFile.errorCount ?? '-' }}</td>
|
|
54
|
+
<td>{{ importFile.time }}</td>
|
|
55
|
+
</tr>
|
|
56
|
+
</tbody>
|
|
57
|
+
</v-table>
|
|
58
|
+
</slot>
|
|
59
|
+
</template>
|
|
60
|
+
</v-card-text>
|
|
61
|
+
|
|
62
|
+
<v-card-actions>
|
|
63
|
+
<v-spacer></v-spacer>
|
|
64
|
+
<v-btn @click="importFiles = []" :loading="importLoading">{{ t('action.clear') }}</v-btn>
|
|
65
|
+
<v-btn @click="importListVisible = false">{{ t('action.close') }}</v-btn>
|
|
66
|
+
</v-card-actions>
|
|
67
|
+
</v-card>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
|
|
72
|
+
</style>
|
|
@@ -13,6 +13,7 @@ import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
|
|
|
13
13
|
import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
|
|
14
14
|
import CrudFilterButton from "./buttons/CrudFilterButton.vue";
|
|
15
15
|
import CrudExportList from "./CrudExportList.vue";
|
|
16
|
+
import CrudImportList from "./CrudImportList.vue";
|
|
16
17
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
17
18
|
import {useI18n} from "vue-i18n";
|
|
18
19
|
import CrudFilters from "./CrudFilters.vue";
|
|
@@ -96,7 +97,7 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
96
97
|
|
|
97
98
|
<crud-import-button
|
|
98
99
|
:entity="entity"
|
|
99
|
-
@import="(
|
|
100
|
+
@import="(file:any, format:any) => $emit('import', file, format)"
|
|
100
101
|
/>
|
|
101
102
|
|
|
102
103
|
<crud-export-button
|
|
@@ -132,6 +133,12 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
132
133
|
</template>
|
|
133
134
|
</crud-export-list>
|
|
134
135
|
|
|
136
|
+
<crud-import-list :entity="entity">
|
|
137
|
+
<template #import-table="{ importFiles }">
|
|
138
|
+
<slot name="import-table" :importFiles="importFiles" />
|
|
139
|
+
</template>
|
|
140
|
+
</crud-import-list>
|
|
141
|
+
|
|
135
142
|
<v-card variant="flat">
|
|
136
143
|
<v-card-text v-if="entity.searchEnable">
|
|
137
144
|
<crud-search
|
|
@@ -13,6 +13,7 @@ import CrudViewButton from "./buttons/CrudViewButton.vue";
|
|
|
13
13
|
import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
|
|
14
14
|
import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
|
|
15
15
|
import CrudExportList from "./CrudExportList.vue";
|
|
16
|
+
import CrudImportList from "./CrudImportList.vue";
|
|
16
17
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
17
18
|
import {useI18n} from "vue-i18n";
|
|
18
19
|
import CrudFilters from "./CrudFilters.vue";
|
|
@@ -67,7 +68,7 @@ onMounted(() => {
|
|
|
67
68
|
|
|
68
69
|
<crud-import-button
|
|
69
70
|
:entity="entity"
|
|
70
|
-
@import="(
|
|
71
|
+
@import="(file:any, format:any) => $emit('import', file, format)"
|
|
71
72
|
/>
|
|
72
73
|
|
|
73
74
|
<crud-export-button
|
|
@@ -115,6 +116,14 @@ onMounted(() => {
|
|
|
115
116
|
</template>
|
|
116
117
|
</crud-export-list>
|
|
117
118
|
|
|
119
|
+
<crud-import-list
|
|
120
|
+
:entity="entity"
|
|
121
|
+
>
|
|
122
|
+
<template #import-table="{ importFiles }">
|
|
123
|
+
<slot name="import-table" :importFiles="importFiles" />
|
|
124
|
+
</template>
|
|
125
|
+
</crud-import-list>
|
|
126
|
+
|
|
118
127
|
<v-card variant="flat">
|
|
119
128
|
<v-card-text v-if="entity.searchEnable">
|
|
120
129
|
<crud-search
|
|
@@ -12,6 +12,7 @@ import CrudViewButton from "./buttons/CrudViewButton.vue";
|
|
|
12
12
|
import CrudGroupByButton from "./buttons/CrudGroupByButton.vue";
|
|
13
13
|
import CrudColumnsButton from "./buttons/CrudColumnsButton.vue";
|
|
14
14
|
import CrudExportList from "./CrudExportList.vue";
|
|
15
|
+
import CrudImportList from "./CrudImportList.vue";
|
|
15
16
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
16
17
|
import {useI18n} from "vue-i18n";
|
|
17
18
|
import CrudFilters from "./CrudFilters.vue";
|
|
@@ -97,7 +98,7 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
97
98
|
|
|
98
99
|
<crud-import-button
|
|
99
100
|
:entity="entity"
|
|
100
|
-
@import="(
|
|
101
|
+
@import="(file:any, format:any) => $emit('import', file, format)"
|
|
101
102
|
/>
|
|
102
103
|
|
|
103
104
|
<crud-export-button
|
|
@@ -142,6 +143,14 @@ defineEmits(['import', 'export', 'create', 'update', 'delete', 'view', 'edit'])
|
|
|
142
143
|
</template>
|
|
143
144
|
</crud-export-list>
|
|
144
145
|
|
|
146
|
+
<crud-import-list
|
|
147
|
+
:entity="entity"
|
|
148
|
+
>
|
|
149
|
+
<template #import-table="{ importFiles }">
|
|
150
|
+
<slot name="import-table" :importFiles="importFiles" />
|
|
151
|
+
</template>
|
|
152
|
+
</crud-import-list>
|
|
153
|
+
|
|
145
154
|
<v-card variant="flat">
|
|
146
155
|
<v-card-text v-if="entity.searchEnable">
|
|
147
156
|
<crud-search
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import {ref} from "vue";
|
|
2
3
|
import type {PropType} from "vue";
|
|
3
4
|
import type {IEntityCrud} from "@drax/crud-share"
|
|
4
5
|
import {useCrud} from "../../composables/UseCrud";
|
|
@@ -10,26 +11,62 @@ const {entity} = defineProps({
|
|
|
10
11
|
})
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
|
-
|
|
14
|
+
importLoading
|
|
14
15
|
} = useCrud(entity)
|
|
16
|
+
|
|
17
|
+
const fileInput = ref<HTMLInputElement | null>(null)
|
|
18
|
+
const selectedFormat = ref<'CSV' | 'JSON'>('CSV')
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits(['import'])
|
|
21
|
+
|
|
22
|
+
function selectFormat(format: string) {
|
|
23
|
+
selectedFormat.value = format as 'CSV' | 'JSON'
|
|
24
|
+
fileInput.value?.click()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function onFileChange(event: Event) {
|
|
28
|
+
const target = event.target as HTMLInputElement
|
|
29
|
+
const file = target.files?.[0]
|
|
30
|
+
if (!file) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
emit('import', file, selectedFormat.value)
|
|
35
|
+
target.value = ''
|
|
36
|
+
}
|
|
15
37
|
</script>
|
|
16
38
|
|
|
17
39
|
<template>
|
|
18
40
|
<div v-if="entity.isImportable">
|
|
41
|
+
<input
|
|
42
|
+
ref="fileInput"
|
|
43
|
+
class="d-none"
|
|
44
|
+
type="file"
|
|
45
|
+
:accept="entity.importFormats.includes('CSV') && entity.importFormats.includes('JSON') ? '.csv,.json,application/json,text/csv' : entity.importFormats.includes('CSV') ? '.csv,text/csv' : '.json,application/json'"
|
|
46
|
+
@change="onFileChange"
|
|
47
|
+
/>
|
|
48
|
+
<v-menu>
|
|
49
|
+
<template v-slot:activator="{ props: mp }">
|
|
19
50
|
<v-tooltip location="top">
|
|
20
51
|
<template v-slot:activator="{ props }">
|
|
21
52
|
<v-btn
|
|
22
|
-
v-bind="props"
|
|
23
|
-
:disabled="
|
|
53
|
+
v-bind="{...mp, ...props}"
|
|
54
|
+
:disabled="importLoading"
|
|
24
55
|
class="mr-1"
|
|
25
56
|
variant="text"
|
|
26
|
-
:loading="
|
|
57
|
+
:loading="importLoading"
|
|
27
58
|
icon="mdi-database-import-outline"
|
|
28
59
|
></v-btn>
|
|
29
60
|
</template>
|
|
30
61
|
{{ t('action.import')}}
|
|
31
62
|
</v-tooltip>
|
|
32
|
-
|
|
63
|
+
</template>
|
|
64
|
+
<v-list>
|
|
65
|
+
<v-list-item v-for="format in entity.importFormats" @click="selectFormat(format)">
|
|
66
|
+
<v-list-item-title>{{ format }}</v-list-item-title>
|
|
67
|
+
</v-list-item>
|
|
68
|
+
</v-list>
|
|
69
|
+
</v-menu>
|
|
33
70
|
</div>
|
|
34
71
|
</template>
|
|
35
72
|
|
|
@@ -159,6 +159,14 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
159
159
|
}
|
|
160
160
|
})
|
|
161
161
|
|
|
162
|
+
const importFiles = computed({
|
|
163
|
+
get() {
|
|
164
|
+
return store.importFiles
|
|
165
|
+
}, set(value) {
|
|
166
|
+
store.setImportFiles(value)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
162
170
|
const exportLoading = computed({
|
|
163
171
|
get() {
|
|
164
172
|
return store.exportLoading
|
|
@@ -167,6 +175,14 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
167
175
|
}
|
|
168
176
|
})
|
|
169
177
|
|
|
178
|
+
const importLoading = computed({
|
|
179
|
+
get() {
|
|
180
|
+
return store.importLoading
|
|
181
|
+
}, set(value) {
|
|
182
|
+
store.setImportLoading(value)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
|
|
170
186
|
const exportListVisible = computed({
|
|
171
187
|
get() {
|
|
172
188
|
return store.exportListVisible
|
|
@@ -175,6 +191,22 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
175
191
|
}
|
|
176
192
|
})
|
|
177
193
|
|
|
194
|
+
const importListVisible = computed({
|
|
195
|
+
get() {
|
|
196
|
+
return store.importListVisible
|
|
197
|
+
}, set(value) {
|
|
198
|
+
store.setImportListVisible(value)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const importError = computed({
|
|
203
|
+
get() {
|
|
204
|
+
return store.importError
|
|
205
|
+
}, set(value) {
|
|
206
|
+
store.setImportError(value)
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
|
|
178
210
|
const filters = computed({
|
|
179
211
|
get() {
|
|
180
212
|
return store.filters
|
|
@@ -270,6 +302,30 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
270
302
|
}
|
|
271
303
|
}
|
|
272
304
|
|
|
305
|
+
async function doImport(file: File, format: 'CSV' | 'JSON') {
|
|
306
|
+
store.setImportLoading(true)
|
|
307
|
+
store.setImportListVisible(true)
|
|
308
|
+
store.setImportError(false)
|
|
309
|
+
try {
|
|
310
|
+
if (!entity?.provider.import) {
|
|
311
|
+
throw new Error("provider.import not implemented")
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const result = await entity.provider.import(file, format)
|
|
315
|
+
store.addImportFile(result)
|
|
316
|
+
await doPaginate()
|
|
317
|
+
store.showMessage(result?.message || "Import successful")
|
|
318
|
+
return result
|
|
319
|
+
} catch (e: any) {
|
|
320
|
+
store.setImportError(true)
|
|
321
|
+
store.setError(e.message || "An error occurred while importing")
|
|
322
|
+
console.error("Error importing", e)
|
|
323
|
+
return {status: 'error'}
|
|
324
|
+
} finally {
|
|
325
|
+
store.setImportLoading(false)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
273
329
|
|
|
274
330
|
function cast(item: any) {
|
|
275
331
|
entity.fields.filter(field => field.type === 'date')
|
|
@@ -475,12 +531,12 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
475
531
|
|
|
476
532
|
|
|
477
533
|
return {
|
|
478
|
-
doPaginate, doExport, doUpdate, doCreate, doDelete,
|
|
534
|
+
doPaginate, doExport, doImport, doUpdate, doCreate, doDelete,
|
|
479
535
|
onView, onCreate, onEdit, onDelete, onCancel, onSubmit, resetCrudStore,
|
|
480
536
|
operation, dialog, form, notify, error, paginationError, message, formValid,
|
|
481
537
|
loading, itemsPerPage, page, sortBy, search, totalItems, items,
|
|
482
538
|
prepareFilters, filters, clearFilters, applyFilters,
|
|
483
|
-
exportFiles, exportLoading, exportListVisible, exportError,
|
|
539
|
+
exportFiles, importFiles, exportLoading, importLoading, exportListVisible, importListVisible, exportError, importError,
|
|
484
540
|
cloneItem, cast, prepareEdit,
|
|
485
541
|
isDynamicFiltersEnable
|
|
486
542
|
}
|
|
@@ -32,9 +32,13 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
|
|
|
32
32
|
loading: false,
|
|
33
33
|
inputErrors: null,
|
|
34
34
|
exportLoading: false,
|
|
35
|
+
importLoading: false,
|
|
35
36
|
exportFiles: [] as string[],
|
|
37
|
+
importFiles: [] as any[],
|
|
36
38
|
exportListVisible: false,
|
|
39
|
+
importListVisible: false,
|
|
37
40
|
exportError: false,
|
|
41
|
+
importError: false,
|
|
38
42
|
visibleColumns: []
|
|
39
43
|
}
|
|
40
44
|
),
|
|
@@ -134,15 +138,30 @@ export const useCrudStore = (id: string = 'entity') => defineStore('CrudStore'+i
|
|
|
134
138
|
addExportFile(exportFile: string) {
|
|
135
139
|
this.exportFiles.push(exportFile)
|
|
136
140
|
},
|
|
141
|
+
setImportFiles(importFiles: any[]) {
|
|
142
|
+
this.importFiles = importFiles
|
|
143
|
+
},
|
|
144
|
+
addImportFile(importFile: any) {
|
|
145
|
+
this.importFiles.push(importFile)
|
|
146
|
+
},
|
|
137
147
|
setExportLoading(exportLoading: boolean) {
|
|
138
148
|
this.exportLoading = exportLoading
|
|
139
149
|
},
|
|
150
|
+
setImportLoading(importLoading: boolean) {
|
|
151
|
+
this.importLoading = importLoading
|
|
152
|
+
},
|
|
140
153
|
setExportListVisible(exportListVisible: boolean) {
|
|
141
154
|
this.exportListVisible = exportListVisible
|
|
142
155
|
},
|
|
156
|
+
setImportListVisible(importListVisible: boolean) {
|
|
157
|
+
this.importListVisible = importListVisible
|
|
158
|
+
},
|
|
143
159
|
setExportError(error: boolean){
|
|
144
160
|
this.exportError = error
|
|
145
161
|
},
|
|
162
|
+
setImportError(error: boolean){
|
|
163
|
+
this.importError = error
|
|
164
|
+
},
|
|
146
165
|
setFilters(filters: IDraxFieldFilter[]) {
|
|
147
166
|
this.filters = filters
|
|
148
167
|
},
|