@drax/crud-vue 0.11.4 → 0.12.1
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/EntityCrud.ts +142 -135
- package/src/components/Crud.vue +23 -1
- package/src/components/CrudAutocomplete.vue +2 -5
- package/src/components/CrudDialog.vue +1 -1
- package/src/components/CrudForm.vue +6 -2
- package/src/components/CrudFormField.vue +1 -2
- package/src/components/CrudFormList.vue +11 -1
- package/src/components/CrudSearch.vue +22 -2
- package/src/composables/UseCrud.ts +32 -14
- package/src/composables/UseInputErrorI18n.ts +19 -0
- package/src/helpers/getItemId.ts +11 -0
- package/src/index.ts +2 -0
- package/src/stores/UseCrudStore.ts +21 -11
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.12.1",
|
|
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": "^0.
|
|
28
|
-
"@drax/crud-front": "^0.
|
|
29
|
-
"@drax/crud-share": "^0.
|
|
30
|
-
"@drax/media-vue": "^0.
|
|
27
|
+
"@drax/common-front": "^0.12.1",
|
|
28
|
+
"@drax/crud-front": "^0.12.1",
|
|
29
|
+
"@drax/crud-share": "^0.12.1",
|
|
30
|
+
"@drax/media-vue": "^0.12.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"pinia": "^2.2.2",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"vue-tsc": "^2.1.10",
|
|
65
65
|
"vuetify": "^3.7.1"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "76fd366e12449f5f605662197f985a28d98058a2"
|
|
68
68
|
}
|
package/src/EntityCrud.ts
CHANGED
|
@@ -1,184 +1,191 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
IEntityCrud, IEntityCrudForm, IEntityCrudHeader, IEntityCrudRefs,
|
|
3
|
+
IEntityCrudRules, IEntityCrudField, IEntityCrudPermissions,
|
|
4
|
+
IDraxCrudProvider, IEntityCrudFilter, IEntityCrudFormFilter
|
|
5
5
|
} from "@drax/crud-share";
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class EntityCrud implements IEntityCrud {
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
name: string = ''
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
constructor() {
|
|
14
|
-
}
|
|
12
|
+
constructor() {
|
|
13
|
+
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
static get instance(): IEntityCrud {
|
|
16
|
+
throw new Error('EntityCrud instance not found')
|
|
17
|
+
}
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
get headers(): IEntityCrudHeader[] {
|
|
21
|
+
return [
|
|
22
|
+
{title: 'ID', key: '_id'},
|
|
23
|
+
]
|
|
24
|
+
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
get actionHeaders(): IEntityCrudHeader[] {
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
title: 'action.actions',
|
|
30
|
+
key: 'actions',
|
|
31
|
+
sortable: false,
|
|
32
|
+
align: 'center',
|
|
33
|
+
minWidth: '190px'
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
}
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
get permissions(): IEntityCrudPermissions {
|
|
40
|
+
return {
|
|
41
|
+
manage: 'manage', view: 'view', create: 'create', update: 'update', delete: 'delete'
|
|
42
|
+
}
|
|
43
43
|
}
|
|
44
|
-
}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
get provider(): IDraxCrudProvider<any, any, any> {
|
|
46
|
+
throw new Error('provider not implemented')
|
|
47
|
+
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
get fields(): IEntityCrudField[] {
|
|
50
|
+
return [
|
|
51
|
+
{name: '_id', type: 'string', label: 'ID', default: ''},
|
|
52
|
+
]
|
|
53
|
+
}
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
get createFields() {
|
|
56
|
+
return this.fields
|
|
57
|
+
}
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
get updateFields() {
|
|
60
|
+
return this.fields
|
|
61
|
+
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
get deleteFields() {
|
|
64
|
+
return this.fields
|
|
65
|
+
}
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
get viewFields() {
|
|
68
|
+
return this.fields
|
|
69
|
+
}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
get filters(): IEntityCrudFilter[] {
|
|
72
|
+
return [
|
|
73
|
+
{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq'},
|
|
74
|
+
]
|
|
75
|
+
}
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
objectFields(field: IEntityCrudField) {
|
|
78
|
+
let value: any = {}
|
|
79
|
+
if (field.objectFields) {
|
|
80
|
+
field.objectFields.forEach(subField => {
|
|
81
|
+
|
|
82
|
+
if (subField.default != undefined) {
|
|
83
|
+
value[subField.name] = field.default
|
|
84
|
+
} else if (subField.type === 'object') {
|
|
85
|
+
value[subField.name] = this.objectFields(field)
|
|
86
|
+
} else if (/array/.test(field.type)) {
|
|
87
|
+
value[subField.name] = []
|
|
88
|
+
} else {
|
|
89
|
+
value = null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
})
|
|
88
93
|
}
|
|
89
|
-
|
|
90
|
-
})
|
|
94
|
+
return value
|
|
91
95
|
}
|
|
92
|
-
return value
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
get form():IEntityCrudForm{
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
let value = null
|
|
99
|
-
if(field.type === 'object'){
|
|
100
|
-
value = this.objectFields(field)
|
|
101
|
-
}else if(field.default != undefined){
|
|
102
|
-
value = field.default
|
|
103
|
-
}else{
|
|
104
|
-
value = null
|
|
105
|
-
}
|
|
97
|
+
get form(): IEntityCrudForm {
|
|
106
98
|
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
return this.fields.reduce((acc, field) => {
|
|
100
|
+
let value = null
|
|
101
|
+
if (field.default != undefined) {
|
|
102
|
+
value = field.default
|
|
103
|
+
} else if (field.type === 'object') {
|
|
104
|
+
value = this.objectFields(field)
|
|
105
|
+
} else if (/array/.test(field.type)) {
|
|
106
|
+
value = []
|
|
107
|
+
} else {
|
|
108
|
+
value = null
|
|
109
|
+
}
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
return {...acc, [field.name]: value}
|
|
112
|
+
}, {})
|
|
111
113
|
|
|
114
|
+
}
|
|
112
115
|
|
|
113
|
-
get formFilters():IEntityCrudFormFilter[]{
|
|
114
|
-
return this.filters.map(
|
|
115
|
-
(filter:IEntityCrudFilter) =>
|
|
116
|
-
({field:filter.name, value: null, operator: filter.operator })
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
116
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
get formFilters(): IEntityCrudFormFilter[] {
|
|
118
|
+
return this.filters.map(
|
|
119
|
+
(filter: IEntityCrudFilter) =>
|
|
120
|
+
({field: filter.name, value: null, operator: filter.operator})
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
throw new Error("Ref not found: " + ref)
|
|
124
|
+
get refs(): IEntityCrudRefs {
|
|
125
|
+
return {}
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
getRef(ref: string): IEntityCrud {
|
|
129
|
+
if (!this.refs.hasOwnProperty(ref)) {
|
|
130
|
+
throw new Error("Ref not found: " + ref)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this.refs[ref]
|
|
134
|
+
}
|
|
131
135
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
get rules(): IEntityCrudRules {
|
|
137
|
+
return {}
|
|
138
|
+
}
|
|
135
139
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
getRule(field: string | undefined): Array<Function> | undefined {
|
|
141
|
+
return field && this.rules[field] && this.rules[field].length > 0 ? this.rules[field] : undefined
|
|
142
|
+
}
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
get isViewable() {
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
143
147
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
get isEditable() {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
get isCreatable() {
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
get isDeletable() {
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
155
159
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
get isExportable() {
|
|
161
|
+
return true
|
|
162
|
+
}
|
|
159
163
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
get exportFormats() {
|
|
165
|
+
return ['CSV', 'JSON']
|
|
166
|
+
}
|
|
163
167
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
get exportHeaders() {
|
|
169
|
+
return ['_id']
|
|
170
|
+
}
|
|
167
171
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
get isImportable() {
|
|
173
|
+
return true
|
|
174
|
+
}
|
|
171
175
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
get importFormats() {
|
|
177
|
+
return ['CSV', 'JSON']
|
|
178
|
+
}
|
|
175
179
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
180
|
+
get dialogFullscreen() {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
179
183
|
|
|
184
|
+
get dialogMaxWidth() {
|
|
185
|
+
return ''
|
|
186
|
+
}
|
|
180
187
|
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
export default EntityCrud;
|
|
184
|
-
export {
|
|
191
|
+
export {EntityCrud}
|
package/src/components/Crud.vue
CHANGED
|
@@ -22,6 +22,28 @@ onBeforeMount(() => {
|
|
|
22
22
|
prepareFilters()
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
+
const emit = defineEmits(['created', 'updated', 'deleted', 'viewed'])
|
|
26
|
+
|
|
27
|
+
async function submit() {
|
|
28
|
+
let result = await onSubmit(form.value)
|
|
29
|
+
switch (result.status) {
|
|
30
|
+
case "created":
|
|
31
|
+
emit("created", result.item)
|
|
32
|
+
break
|
|
33
|
+
case "updated":
|
|
34
|
+
emit("updated", result.item)
|
|
35
|
+
break
|
|
36
|
+
case "deleted":
|
|
37
|
+
emit("deleted")
|
|
38
|
+
break
|
|
39
|
+
case "viewed":
|
|
40
|
+
emit("deleted")
|
|
41
|
+
break
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
</script>
|
|
26
48
|
|
|
27
49
|
<template>
|
|
@@ -73,7 +95,7 @@ onBeforeMount(() => {
|
|
|
73
95
|
:error="error"
|
|
74
96
|
:operation="operation"
|
|
75
97
|
:readonly="operation === 'delete'"
|
|
76
|
-
@submit="
|
|
98
|
+
@submit="submit"
|
|
77
99
|
@cancel="onCancel"
|
|
78
100
|
>
|
|
79
101
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {debounce} from "@drax/common-front"
|
|
3
3
|
import { type PropType, type Ref} from "vue";
|
|
4
4
|
import {ref, onBeforeMount} from "vue";
|
|
5
|
+
import {getItemId} from "../helpers/getItemId"
|
|
5
6
|
import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
|
|
6
7
|
|
|
7
8
|
const valueModel = defineModel<string | string[]>({type: [String, Array], required: false})
|
|
@@ -57,7 +58,6 @@ async function search(value: any) {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
onBeforeMount(async () => {
|
|
60
|
-
|
|
61
61
|
await search('')
|
|
62
62
|
await checkIds()
|
|
63
63
|
})
|
|
@@ -68,7 +68,7 @@ async function checkIds(ids: Array<string> = []) {
|
|
|
68
68
|
if(valueModel.value) {
|
|
69
69
|
let ids = Array.isArray(valueModel.value) ? valueModel.value : [valueModel.value]
|
|
70
70
|
for (let id of ids) {
|
|
71
|
-
if (!items.value.some((item: any) => item
|
|
71
|
+
if (!items.value.some((item: any) => getItemId(item) === id)) {
|
|
72
72
|
if (!entity) {
|
|
73
73
|
throw new Error('CrudAutocomplete Entity is required')
|
|
74
74
|
}
|
|
@@ -88,9 +88,6 @@ async function checkIds(ids: Array<string> = []) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
91
|
defineEmits(['updateValue'])
|
|
95
92
|
|
|
96
93
|
</script>
|
|
@@ -30,7 +30,7 @@ const title = computed(() => {
|
|
|
30
30
|
</script>
|
|
31
31
|
|
|
32
32
|
<template>
|
|
33
|
-
<v-dialog v-model="dialog" :fullscreen="entity.dialogFullscreen">
|
|
33
|
+
<v-dialog v-model="dialog" :fullscreen="entity.dialogFullscreen" :max-width="entity.dialogMaxWidth">
|
|
34
34
|
<v-card>
|
|
35
35
|
<v-toolbar>
|
|
36
36
|
<v-toolbar-title>{{title}}</v-toolbar-title>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {useI18n} from "vue-i18n";
|
|
3
3
|
import type {IEntityCrud, IEntityCrudField, IEntityCrudOperation} from "@drax/crud-share";
|
|
4
4
|
import {useFormUtils} from "../composables/UseFormUtils";
|
|
5
|
+
import {getItemId} from "../helpers/getItemId";
|
|
5
6
|
import CrudFormField from "./CrudFormField.vue";
|
|
6
7
|
import {computed, defineEmits, defineModel, defineProps, ref} from "vue";
|
|
7
8
|
import type {PropType} from "vue";
|
|
@@ -11,6 +12,8 @@ import {useAuth} from '@drax/identity-vue'
|
|
|
11
12
|
const {hasPermission} = useAuth()
|
|
12
13
|
const {t, te} = useI18n()
|
|
13
14
|
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
const valueModel = defineModel({type: [Object]})
|
|
15
18
|
|
|
16
19
|
const {entity, operation} = defineProps({
|
|
@@ -20,6 +23,7 @@ const {entity, operation} = defineProps({
|
|
|
20
23
|
error: {type: String, required: false},
|
|
21
24
|
})
|
|
22
25
|
|
|
26
|
+
|
|
23
27
|
const emit = defineEmits(['submit', 'cancel'])
|
|
24
28
|
|
|
25
29
|
const store = useCrudStore()
|
|
@@ -76,7 +80,7 @@ const {
|
|
|
76
80
|
<v-form ref="formRef" @submit.prevent>
|
|
77
81
|
<v-card flat>
|
|
78
82
|
|
|
79
|
-
<v-card-subtitle v-if="valueModel
|
|
83
|
+
<v-card-subtitle v-if="getItemId(valueModel)">ID: {{ getItemId(valueModel) }}</v-card-subtitle>
|
|
80
84
|
|
|
81
85
|
<v-card-text v-if="error">
|
|
82
86
|
<v-alert color="error">{{ te(error) ? t(error) : error }}</v-alert>
|
|
@@ -115,7 +119,7 @@ const {
|
|
|
115
119
|
<v-card-actions>
|
|
116
120
|
<v-spacer></v-spacer>
|
|
117
121
|
<v-btn variant="text" color="grey" @click="cancel">{{ t('action.cancel') }}</v-btn>
|
|
118
|
-
<v-btn variant="flat" v-if="operation != 'view'" :color="submitColor" @click="submit">
|
|
122
|
+
<v-btn variant="flat" v-if="operation != 'view'" :color="submitColor" @click="submit" :loading="store.loading">
|
|
119
123
|
{{ operation ? t('action.' + operation) : t('action.sent') }}
|
|
120
124
|
</v-btn>
|
|
121
125
|
</v-card-actions>
|
|
@@ -59,8 +59,7 @@ const rules = computed(() => {
|
|
|
59
59
|
const inputErrors = computed(() => {
|
|
60
60
|
let sIndex = (index != null && index >= 0) ? `${index}.` : ''
|
|
61
61
|
let name = parentField ? `${parentField}.${sIndex}${field.name}` : field.name
|
|
62
|
-
|
|
63
|
-
return store.getInputErrors(name).map((error: string) => t(te(error) ? t(error) : error))
|
|
62
|
+
return store.getFieldInputErrors(name).map((error: string) =>te(error) ? t(error) : error)
|
|
64
63
|
}
|
|
65
64
|
)
|
|
66
65
|
|
|
@@ -3,7 +3,9 @@ import {computed, type PropType} from "vue";
|
|
|
3
3
|
import CrudFormField from "./CrudFormField.vue";
|
|
4
4
|
import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
|
|
5
5
|
import {useI18n} from "vue-i18n";
|
|
6
|
+
import {useCrudStore} from "../stores/UseCrudStore";
|
|
6
7
|
|
|
8
|
+
const store = useCrudStore()
|
|
7
9
|
const {t, te} = useI18n()
|
|
8
10
|
const valueModel = defineModel({type: Array, default: () => []});
|
|
9
11
|
|
|
@@ -49,6 +51,14 @@ const label = computed(() => {
|
|
|
49
51
|
return te(i18n) ? t(i18n) : field.label
|
|
50
52
|
})
|
|
51
53
|
|
|
54
|
+
const hasError = computed(() => {
|
|
55
|
+
return (index:number) => {
|
|
56
|
+
const fieldListName = field.name + '.' + index
|
|
57
|
+
return store.hasFieldListInputErrors(fieldListName)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
})
|
|
61
|
+
|
|
52
62
|
defineEmits(['updateValue'])
|
|
53
63
|
|
|
54
64
|
</script>
|
|
@@ -62,7 +72,7 @@ defineEmits(['updateValue'])
|
|
|
62
72
|
<v-expansion-panel v-for="(item,index) in valueModel" :key="index">
|
|
63
73
|
|
|
64
74
|
<v-expansion-panel-title>
|
|
65
|
-
{{ index }}
|
|
75
|
+
<v-chip class="mr-2" :color="hasError(index) ? 'red':'teal'">{{ index }}</v-chip> {{valueModel[index][Object.keys(valueModel[index] as any)[0]]}}
|
|
66
76
|
|
|
67
77
|
<template v-slot:actions="{expanded}">
|
|
68
78
|
<v-icon>{{expanded ? "mdi-menu-down" : "mdi-menu-up"}}</v-icon>
|
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import {useI18n} from "vue-i18n";
|
|
3
|
+
import {debounce} from "@drax/common-front"
|
|
4
|
+
import {ref, defineModel} from "vue"
|
|
5
|
+
|
|
6
|
+
|
|
3
7
|
const {t} = useI18n()
|
|
4
8
|
const model = defineModel<any>()
|
|
9
|
+
|
|
10
|
+
let input = ref(model.value)
|
|
11
|
+
|
|
12
|
+
const debouncedSearch = debounce(updateModel, 500)
|
|
13
|
+
|
|
14
|
+
function updateModel() {
|
|
15
|
+
model.value = input.value
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function clear() {
|
|
19
|
+
input.value = ''
|
|
20
|
+
updateModel()
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
</script>
|
|
6
24
|
|
|
7
25
|
<template>
|
|
8
|
-
<v-text-field v-model="
|
|
26
|
+
<v-text-field v-model="input"
|
|
9
27
|
density="compact"
|
|
10
28
|
class="mr-2"
|
|
11
29
|
variant="outlined"
|
|
12
30
|
append-inner-icon="mdi-magnify"
|
|
13
31
|
:label="t('action.search')"
|
|
14
32
|
single-line hide-details
|
|
15
|
-
|
|
33
|
+
@update:modelValue="debouncedSearch"
|
|
34
|
+
clearable
|
|
35
|
+
@click:clear="clear"
|
|
16
36
|
/>
|
|
17
37
|
</template>
|
|
18
38
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type {IDraxPaginateResult, IEntityCrud} from "@drax/crud-share";
|
|
2
2
|
import {useCrudStore} from "../stores/UseCrudStore";
|
|
3
|
-
import {computed} from "vue";
|
|
3
|
+
import {computed, toRaw} from "vue";
|
|
4
|
+
import getItemId from "../helpers/getItemId";
|
|
4
5
|
|
|
5
6
|
export function useCrud(entity: IEntityCrud) {
|
|
6
7
|
|
|
7
8
|
const store = useCrudStore()
|
|
8
9
|
|
|
10
|
+
|
|
9
11
|
const dialog = computed({
|
|
10
12
|
get() {
|
|
11
13
|
return store.dialog
|
|
@@ -198,13 +200,16 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
198
200
|
|
|
199
201
|
entity.fields.filter(field => field.type === 'ref')
|
|
200
202
|
.forEach(field => {
|
|
201
|
-
item[field.name] = item[field.name]
|
|
203
|
+
item[field.name] = getItemId(item[field.name]) ? getItemId(item[field.name]) : item[field.name]
|
|
204
|
+
|
|
202
205
|
})
|
|
203
206
|
|
|
204
207
|
entity.fields.filter(field => field.type === 'array.ref')
|
|
205
208
|
.forEach(field => {
|
|
206
209
|
if (item[field.name] && Array.isArray(item[field.name])) {
|
|
207
|
-
item[field.name] = item[field.name].map(((i: any) => i
|
|
210
|
+
item[field.name] = item[field.name].map(((i: any) => getItemId(i) ? getItemId(i) : i))
|
|
211
|
+
}else{
|
|
212
|
+
item[field.name] = []
|
|
208
213
|
}
|
|
209
214
|
})
|
|
210
215
|
|
|
@@ -242,22 +247,20 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
242
247
|
store.setInputErrors(null)
|
|
243
248
|
}
|
|
244
249
|
|
|
245
|
-
function onSubmit(formData: any) {
|
|
250
|
+
async function onSubmit(formData: any): Promise<{status:string,item?:any}> {
|
|
246
251
|
store.setInputErrors(null)
|
|
247
252
|
switch (store.operation) {
|
|
248
253
|
case "view":
|
|
249
254
|
closeDialog()
|
|
250
|
-
|
|
255
|
+
return {status: 'viewed'}
|
|
251
256
|
case "create":
|
|
252
|
-
doCreate(formData)
|
|
253
|
-
break
|
|
257
|
+
return doCreate(formData)
|
|
254
258
|
case "edit":
|
|
255
|
-
doUpdate(formData)
|
|
256
|
-
break
|
|
259
|
+
return doUpdate(formData)
|
|
257
260
|
case "delete":
|
|
258
|
-
doDelete(formData)
|
|
259
|
-
break
|
|
261
|
+
return doDelete(formData)
|
|
260
262
|
}
|
|
263
|
+
return {status: 'unknown'}
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
function openDialog() {
|
|
@@ -270,26 +273,33 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
270
273
|
|
|
271
274
|
async function doCreate(formData: any) {
|
|
272
275
|
try {
|
|
273
|
-
|
|
276
|
+
store.setLoading(true)
|
|
277
|
+
let item = await entity?.provider.create(toRaw(formData))
|
|
274
278
|
await doPaginate()
|
|
275
279
|
closeDialog()
|
|
276
280
|
store.showMessage("Entity created successfully!")
|
|
281
|
+
return {status: 'created', item: item}
|
|
277
282
|
} catch (e: any) {
|
|
278
283
|
if (e.inputErrors) {
|
|
279
284
|
store.setInputErrors(e.inputErrors)
|
|
280
285
|
}
|
|
281
286
|
store.setError(e.message || "An error occurred while creating the entity")
|
|
282
287
|
console.error("Error creating entity", e)
|
|
288
|
+
return {status: 'error'}
|
|
289
|
+
} finally {
|
|
290
|
+
store.setLoading(false)
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
}
|
|
286
294
|
|
|
287
295
|
async function doUpdate(formData: any) {
|
|
288
296
|
try {
|
|
289
|
-
|
|
297
|
+
store.setLoading(true)
|
|
298
|
+
let item = await entity?.provider.update(getItemId(formData), toRaw(formData))
|
|
290
299
|
await doPaginate()
|
|
291
300
|
closeDialog()
|
|
292
301
|
store.showMessage("Entity updated successfully!")
|
|
302
|
+
return {status: 'updated', item: item}
|
|
293
303
|
} catch (e: any) {
|
|
294
304
|
//console.log("inputErrors", e.inputErrors)
|
|
295
305
|
if (e.inputErrors) {
|
|
@@ -297,19 +307,27 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
297
307
|
}
|
|
298
308
|
store.setError(e.message || "An error occurred while updating the entity")
|
|
299
309
|
console.error("Error updating entity", e)
|
|
310
|
+
return {status: 'error'}
|
|
311
|
+
} finally {
|
|
312
|
+
store.setLoading(false)
|
|
300
313
|
}
|
|
301
314
|
|
|
302
315
|
}
|
|
303
316
|
|
|
304
317
|
async function doDelete(formData: any) {
|
|
305
318
|
try {
|
|
306
|
-
|
|
319
|
+
store.setLoading(true)
|
|
320
|
+
await entity?.provider.delete(getItemId(formData))
|
|
307
321
|
await doPaginate()
|
|
308
322
|
closeDialog()
|
|
309
323
|
store.showMessage("Entity deleted successfully!")
|
|
324
|
+
return {status: 'deleted'}
|
|
310
325
|
} catch (e: any) {
|
|
311
326
|
store.setError(e.message || "An error occurred while deleting the entity")
|
|
312
327
|
console.error("Error updating entity", e)
|
|
328
|
+
return {status: 'error'}
|
|
329
|
+
} finally {
|
|
330
|
+
store.setLoading(false)
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {useCrudStore} from "../stores/UseCrudStore";
|
|
2
|
+
import {useI18n} from "vue-i18n";
|
|
3
|
+
import {computed} from "vue";
|
|
4
|
+
|
|
5
|
+
export function useInputErrorI18n() {
|
|
6
|
+
|
|
7
|
+
const store = useCrudStore()
|
|
8
|
+
|
|
9
|
+
const {t, te} = useI18n()
|
|
10
|
+
|
|
11
|
+
const inputErrorsI18n = computed(() => {
|
|
12
|
+
return (name:string) => {
|
|
13
|
+
return store.getFieldInputErrors(name).map((error: string) => te(error) ? t(error) : error)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
return {inputErrorsI18n}
|
|
19
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import CrudAutocomplete from "./components/CrudAutocomplete.vue";
|
|
|
10
10
|
import {useCrudStore} from "./stores/UseCrudStore";
|
|
11
11
|
import {useCrud} from "./composables/UseCrud";
|
|
12
12
|
import {useFormUtils} from "./composables/UseFormUtils";
|
|
13
|
+
import {useInputErrorI18n} from "./composables/UseInputErrorI18n";
|
|
13
14
|
import {EntityCrud} from "./EntityCrud";
|
|
14
15
|
|
|
15
16
|
|
|
@@ -26,6 +27,7 @@ export {
|
|
|
26
27
|
useCrud,
|
|
27
28
|
useFormUtils,
|
|
28
29
|
useCrudStore,
|
|
30
|
+
useInputErrorI18n,
|
|
29
31
|
EntityCrud
|
|
30
32
|
|
|
31
33
|
}
|
|
@@ -14,7 +14,7 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
14
14
|
filters: [] as any[],
|
|
15
15
|
items: [] as any[],
|
|
16
16
|
totalItems: 0 as number,
|
|
17
|
-
itemsPerPage:
|
|
17
|
+
itemsPerPage: 10 as number,
|
|
18
18
|
page: 1 as number,
|
|
19
19
|
search: '' as string,
|
|
20
20
|
sortBy: [] as any[],
|
|
@@ -25,15 +25,25 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
25
25
|
exportListVisible: false,
|
|
26
26
|
}
|
|
27
27
|
),
|
|
28
|
-
getters:{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
getters: {
|
|
29
|
+
getFieldInputErrors(state: any) {
|
|
30
|
+
return (fieldName: string) => {
|
|
31
|
+
if (state.inputErrors && state.inputErrors[fieldName]) {
|
|
32
|
+
return state.inputErrors[fieldName]
|
|
33
|
+
}
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
hasFieldListInputErrors(state: any) {
|
|
38
|
+
return (fieldListName: string) => {
|
|
39
|
+
if(state.inputErrors && typeof state.inputErrors === 'object'){
|
|
40
|
+
for(const key in state.inputErrors) {
|
|
41
|
+
return key.startsWith(fieldListName)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
},
|
|
38
48
|
actions: {
|
|
39
49
|
setOperation(operation: IEntityCrudOperation) {
|
|
@@ -85,7 +95,7 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
85
95
|
setInputErrors(inputErrors: any) {
|
|
86
96
|
this.inputErrors = inputErrors
|
|
87
97
|
},
|
|
88
|
-
resetErrors(){
|
|
98
|
+
resetErrors() {
|
|
89
99
|
this.inputErrors = null
|
|
90
100
|
this.error = ''
|
|
91
101
|
},
|