@drax/crud-vue 0.5.1 → 0.5.3
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 +4 -4
- package/src/EntityCrud.ts +17 -9
- package/src/components/Crud.vue +9 -6
- package/src/components/CrudAutocomplete.vue +13 -1
- package/src/components/CrudFilters.vue +64 -0
- package/src/components/CrudForm.vue +10 -3
- package/src/components/CrudFormField.vue +78 -8
- package/src/components/CrudFormList.vue +14 -0
- package/src/components/CrudList.vue +32 -8
- package/src/components/CrudSearch.vue +5 -3
- package/src/components/buttons/CrudExportButton.vue +2 -2
- package/src/components/buttons/CrudImportButton.vue +2 -2
- package/src/composables/UseCrud.ts +34 -21
- package/src/stores/UseCrudStore.ts +8 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.3",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@drax/common-front": "^0.5.1",
|
|
28
|
-
"@drax/crud-front": "^0.5.
|
|
29
|
-
"@drax/crud-share": "^0.5.
|
|
28
|
+
"@drax/crud-front": "^0.5.3",
|
|
29
|
+
"@drax/crud-share": "^0.5.3"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"pinia": "^2.2.2",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"vue-tsc": "^2.0.11",
|
|
64
64
|
"vuetify": "^3.7.1"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "56ea5a743bce196d6322ae6c26ea5b041450d7a3"
|
|
67
67
|
}
|
package/src/EntityCrud.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
IEntityCrud, IEntityCrudForm, IEntityCrudHeader, IEntityCrudRefs,
|
|
3
3
|
IEntityCrudRules, IEntityCrudField, IEntityCrudPermissions,
|
|
4
|
-
IDraxCrudProvider
|
|
4
|
+
IDraxCrudProvider, IEntityCrudFilter, IEntityCrudFormFilter
|
|
5
5
|
} from "@drax/crud-share";
|
|
6
6
|
|
|
7
7
|
|
|
@@ -36,7 +36,13 @@ class EntityCrud implements IEntityCrud{
|
|
|
36
36
|
|
|
37
37
|
get fields():IEntityCrudField[]{
|
|
38
38
|
return [
|
|
39
|
-
{name: '
|
|
39
|
+
{name: '_id', type: 'string', label: 'ID', default: '' },
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get filters():IEntityCrudFilter[]{
|
|
44
|
+
return [
|
|
45
|
+
{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq' },
|
|
40
46
|
]
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -57,7 +63,7 @@ class EntityCrud implements IEntityCrud{
|
|
|
57
63
|
|
|
58
64
|
get form():IEntityCrudForm{
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
return this.fields.reduce((acc, field) => {
|
|
61
67
|
let value = null
|
|
62
68
|
if(field.type === 'object'){
|
|
63
69
|
value = this.objectFields(field)
|
|
@@ -68,10 +74,14 @@ class EntityCrud implements IEntityCrud{
|
|
|
68
74
|
return {...acc, [field.name]: value }
|
|
69
75
|
}, {})
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
}
|
|
72
78
|
|
|
73
|
-
return form
|
|
74
79
|
|
|
80
|
+
get formFilters():IEntityCrudFormFilter[]{
|
|
81
|
+
return this.filters.map(
|
|
82
|
+
(filter:IEntityCrudFilter) =>
|
|
83
|
+
({field:filter.name, value: null, operator: filter.operator })
|
|
84
|
+
)
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
get refs():IEntityCrudRefs{
|
|
@@ -90,10 +100,8 @@ class EntityCrud implements IEntityCrud{
|
|
|
90
100
|
return {}
|
|
91
101
|
}
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return field && this.rules[field] ? this.rules[field] : []
|
|
96
|
-
}
|
|
103
|
+
getRule(field:string|undefined):Array<Function>|undefined {
|
|
104
|
+
return field && this.rules[field] && this.rules[field].length > 0 ? this.rules[field] : undefined
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
get isEditable(){
|
package/src/components/Crud.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
2
|
+
import {onBeforeMount, type PropType} from "vue";
|
|
3
3
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
4
4
|
import CrudList from "./CrudList.vue";
|
|
5
5
|
import CrudForm from "./CrudForm.vue";
|
|
@@ -7,24 +7,27 @@ import CrudNotify from "./CrudNotify.vue";
|
|
|
7
7
|
import CrudDialog from "./CrudDialog.vue";
|
|
8
8
|
import {useCrud} from "../composables/UseCrud";
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
const {entity} = defineProps({
|
|
12
11
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
13
12
|
})
|
|
14
13
|
|
|
15
14
|
const {
|
|
16
|
-
onCreate, onEdit, onDelete, onCancel, onSubmit,
|
|
17
|
-
operation, dialog, form, notify, error, message, doExport
|
|
15
|
+
onCreate, onEdit, onDelete, onCancel, onSubmit,resetCrudStore,
|
|
16
|
+
operation, dialog, form, notify, error, message, doExport,
|
|
17
|
+
prepareFilters
|
|
18
18
|
} = useCrud(entity);
|
|
19
19
|
|
|
20
|
+
onBeforeMount(() => {
|
|
21
|
+
resetCrudStore()
|
|
22
|
+
prepareFilters()
|
|
23
|
+
})
|
|
24
|
+
|
|
20
25
|
</script>
|
|
21
26
|
|
|
22
27
|
<template>
|
|
23
28
|
<v-container fluid class="mt-5">
|
|
24
29
|
<v-card>
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
31
|
<crud-list
|
|
29
32
|
:entity="entity"
|
|
30
33
|
@create="onCreate"
|
|
@@ -3,6 +3,7 @@ import {debounce} from "@drax/common-front"
|
|
|
3
3
|
import { type PropType, type Ref} from "vue";
|
|
4
4
|
import {ref, onBeforeMount} from "vue";
|
|
5
5
|
import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
|
|
6
|
+
import {VDateInput} from "vuetify/lib/labs/VDateInput";
|
|
6
7
|
|
|
7
8
|
const valueModel = defineModel<string | string[]>({type: [String, Array], required: false})
|
|
8
9
|
|
|
@@ -18,6 +19,10 @@ const {entity, multiple} = defineProps({
|
|
|
18
19
|
itemTitle: {type: [String], default: 'name'},
|
|
19
20
|
rules: {type: Array as PropType<any>, default: []},
|
|
20
21
|
errorMessages: {type: Array as PropType<string[]>, default: []},
|
|
22
|
+
hideDetails: {type: Boolean, default: false},
|
|
23
|
+
singleLine: {type: Boolean, default: false},
|
|
24
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
25
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
21
26
|
})
|
|
22
27
|
|
|
23
28
|
if(!entity){
|
|
@@ -86,6 +91,8 @@ async function search(value: any) {
|
|
|
86
91
|
|
|
87
92
|
}
|
|
88
93
|
|
|
94
|
+
defineEmits(['updateValue'])
|
|
95
|
+
|
|
89
96
|
</script>
|
|
90
97
|
|
|
91
98
|
<template>
|
|
@@ -97,13 +104,18 @@ async function search(value: any) {
|
|
|
97
104
|
:multiple="multiple"
|
|
98
105
|
:chips="chips"
|
|
99
106
|
:closable-chips="closableChips"
|
|
100
|
-
:clearable="clearable"
|
|
101
107
|
:item-value="itemValue"
|
|
102
108
|
:item-title="itemTitle"
|
|
103
109
|
:loading="loading"
|
|
104
110
|
:rules="rules"
|
|
111
|
+
:density="density"
|
|
112
|
+
:variant="variant"
|
|
113
|
+
:hide-details="hideDetails"
|
|
114
|
+
:single-line="singleLine"
|
|
115
|
+
:clearable="clearable"
|
|
105
116
|
:error-messages="errorMessages"
|
|
106
117
|
@update:search="debouncedSearch"
|
|
118
|
+
@update:modelValue="$emit('updateValue')"
|
|
107
119
|
></v-autocomplete>
|
|
108
120
|
</template>
|
|
109
121
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type {PropType} from "vue";
|
|
3
|
+
import {ref} from "vue";
|
|
4
|
+
import CrudFormField from "./CrudFormField.vue";
|
|
5
|
+
import type {IEntityCrud} from "@drax/crud-share";
|
|
6
|
+
import {useI18n} from "vue-i18n";
|
|
7
|
+
|
|
8
|
+
const {t} = useI18n()
|
|
9
|
+
const valueModel = defineModel({type: [Object]})
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const {entity} = defineProps({
|
|
13
|
+
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
14
|
+
actions: {type: Boolean, default: false},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const formRef = ref()
|
|
18
|
+
|
|
19
|
+
async function filter() {
|
|
20
|
+
emit('filter')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function clear() {
|
|
24
|
+
emit('filter')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits(['filter', 'clear','updateValue'])
|
|
29
|
+
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<v-card flat >
|
|
34
|
+
<v-row dense class="mt-1" justify="space-between">
|
|
35
|
+
<v-col v-for="(filter,index) in entity.filters" :key="filter.name"
|
|
36
|
+
cols="12" sm="6" md="4"
|
|
37
|
+
>
|
|
38
|
+
<crud-form-field
|
|
39
|
+
:field="filter"
|
|
40
|
+
:entity="entity"
|
|
41
|
+
v-model="valueModel[index].value"
|
|
42
|
+
density="compact"
|
|
43
|
+
variant="outlined"
|
|
44
|
+
hide-details single-line disable-rules
|
|
45
|
+
@updateValue="$emit('updateValue')"
|
|
46
|
+
/>
|
|
47
|
+
</v-col>
|
|
48
|
+
|
|
49
|
+
</v-row>
|
|
50
|
+
|
|
51
|
+
<v-card-actions v-if="actions" class="pb-0">
|
|
52
|
+
<v-spacer />
|
|
53
|
+
<v-btn variant="text" density="compact" color="grey" @click="clear">{{ t('action.clear') }}</v-btn>
|
|
54
|
+
<v-btn variant="flat" density="compact" color="primary" @click="filter">
|
|
55
|
+
{{ t('action.filter') }}
|
|
56
|
+
</v-btn>
|
|
57
|
+
</v-card-actions>
|
|
58
|
+
|
|
59
|
+
</v-card>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<style scoped>
|
|
63
|
+
|
|
64
|
+
</style>
|
|
@@ -5,8 +5,9 @@ import CrudFormField from "./CrudFormField.vue";
|
|
|
5
5
|
import type {TOperation} from "../interfaces/TOperation";
|
|
6
6
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
7
7
|
import {useI18n} from "vue-i18n";
|
|
8
|
+
import {useCrudStore} from "../stores/UseCrudStore";
|
|
8
9
|
const {t,te} = useI18n()
|
|
9
|
-
|
|
10
|
+
const store = useCrudStore()
|
|
10
11
|
const valueModel = defineModel({type: [Object]})
|
|
11
12
|
|
|
12
13
|
|
|
@@ -20,10 +21,13 @@ const {entity} = defineProps({
|
|
|
20
21
|
const valid = ref()
|
|
21
22
|
const formRef = ref()
|
|
22
23
|
|
|
23
|
-
function submit() {
|
|
24
|
-
|
|
24
|
+
async function submit() {
|
|
25
|
+
store.resetErrors()
|
|
26
|
+
await formRef.value.validate()
|
|
25
27
|
if(valid.value) {
|
|
26
28
|
emit('submit',valueModel.value)
|
|
29
|
+
}else{
|
|
30
|
+
console.log('Invalid form')
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
|
|
@@ -38,6 +42,9 @@ const emit = defineEmits(['submit', 'cancel'])
|
|
|
38
42
|
<template>
|
|
39
43
|
<v-form v-model="valid" ref="formRef" @submit.prevent >
|
|
40
44
|
<v-card flat>
|
|
45
|
+
|
|
46
|
+
<v-card-subtitle v-if="valueModel._id">ID: {{valueModel._id}}</v-card-subtitle>
|
|
47
|
+
|
|
41
48
|
<v-card-text v-if="error">
|
|
42
49
|
<v-alert color="error">{{ te(error) ? t(error) : error }}</v-alert>
|
|
43
50
|
</v-card-text>
|
|
@@ -6,20 +6,28 @@ import CrudAutocomplete from "./CrudAutocomplete.vue";
|
|
|
6
6
|
import {useI18n} from "vue-i18n";
|
|
7
7
|
import {useCrudStore} from "../stores/UseCrudStore";
|
|
8
8
|
import {VDateInput} from 'vuetify/labs/VDateInput'
|
|
9
|
-
import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
|
|
9
|
+
import type {IEntityCrud, IEntityCrudField, IEntityCrudFilter} from "@drax/crud-share";
|
|
10
10
|
const {t, te} = useI18n()
|
|
11
11
|
|
|
12
12
|
const store = useCrudStore()
|
|
13
13
|
|
|
14
14
|
const valueModel = defineModel<any>({type: [String, Number, Boolean, Object, Array], default: false})
|
|
15
15
|
|
|
16
|
-
const {index, entity, field} = defineProps({
|
|
16
|
+
const {index, entity, field, disableRules} = defineProps({
|
|
17
17
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
18
|
-
field: {type: Object as PropType<IEntityCrudField|undefined>, required: true},
|
|
18
|
+
field: {type: Object as PropType<IEntityCrudField|IEntityCrudFilter|undefined>, required: true},
|
|
19
19
|
readonly: {type: Boolean, default: false},
|
|
20
|
+
hideDetails: {type: Boolean, default: false},
|
|
21
|
+
singleLine: {type: Boolean, default: false},
|
|
22
|
+
clearable: {type: Boolean, default: true},
|
|
23
|
+
disableRules: {type: Boolean, default: false},
|
|
20
24
|
index: {type: Number, default: 0},
|
|
25
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
26
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
21
27
|
})
|
|
22
28
|
|
|
29
|
+
|
|
30
|
+
|
|
23
31
|
if(!field){
|
|
24
32
|
throw new Error("CrudFormField must be provided with a field object")
|
|
25
33
|
}
|
|
@@ -32,19 +40,21 @@ const label = computed(() => {
|
|
|
32
40
|
})
|
|
33
41
|
|
|
34
42
|
const rules = computed(() => {
|
|
35
|
-
|
|
43
|
+
if(disableRules) return undefined
|
|
44
|
+
return entity.getRule(field.name) as any
|
|
36
45
|
})
|
|
37
46
|
|
|
38
47
|
const inputErrors = computed(() =>
|
|
39
48
|
store.getInputErrors(field.name).map((error: string) => t(te(error) ? t(error) : error))
|
|
40
49
|
)
|
|
41
50
|
|
|
51
|
+
defineEmits(['updateValue'])
|
|
52
|
+
|
|
42
53
|
</script>
|
|
43
54
|
|
|
44
55
|
<template>
|
|
45
56
|
|
|
46
57
|
<div v-if="field && field.type">
|
|
47
|
-
|
|
48
58
|
<v-text-field
|
|
49
59
|
v-if="field.type === 'string'"
|
|
50
60
|
type="text"
|
|
@@ -54,6 +64,12 @@ const inputErrors = computed(() =>
|
|
|
54
64
|
:readonly="readonly"
|
|
55
65
|
:error-messages="inputErrors"
|
|
56
66
|
:rules="rules"
|
|
67
|
+
:density="density"
|
|
68
|
+
:variant="variant"
|
|
69
|
+
:clearable="clearable"
|
|
70
|
+
:hide-details="hideDetails"
|
|
71
|
+
:single-line="singleLine"
|
|
72
|
+
@update:modelValue="$emit('updateValue')"
|
|
57
73
|
>
|
|
58
74
|
</v-text-field>
|
|
59
75
|
|
|
@@ -66,6 +82,12 @@ const inputErrors = computed(() =>
|
|
|
66
82
|
:readonly="readonly"
|
|
67
83
|
:error-messages="inputErrors"
|
|
68
84
|
:rules="rules"
|
|
85
|
+
:density="density"
|
|
86
|
+
:variant="variant"
|
|
87
|
+
:clearable="clearable"
|
|
88
|
+
:hide-details="hideDetails"
|
|
89
|
+
:single-line="singleLine"
|
|
90
|
+
@update:modelValue="$emit('updateValue')"
|
|
69
91
|
>
|
|
70
92
|
</v-text-field>
|
|
71
93
|
|
|
@@ -77,6 +99,12 @@ const inputErrors = computed(() =>
|
|
|
77
99
|
:readonly="readonly"
|
|
78
100
|
:error-messages="inputErrors"
|
|
79
101
|
:rules="rules"
|
|
102
|
+
:density="density"
|
|
103
|
+
:variant="variant"
|
|
104
|
+
:clearable="clearable"
|
|
105
|
+
:hide-details="hideDetails"
|
|
106
|
+
:single-line="singleLine"
|
|
107
|
+
@update:modelValue="$emit('updateValue')"
|
|
80
108
|
>
|
|
81
109
|
</v-checkbox>
|
|
82
110
|
|
|
@@ -92,6 +120,12 @@ const inputErrors = computed(() =>
|
|
|
92
120
|
prepend-inner-icon="mdi-calendar"
|
|
93
121
|
prepend-icon=""
|
|
94
122
|
:rules="rules"
|
|
123
|
+
:density="density"
|
|
124
|
+
:variant="variant"
|
|
125
|
+
:clearable="clearable"
|
|
126
|
+
:hide-details="hideDetails"
|
|
127
|
+
:single-line="singleLine"
|
|
128
|
+
@update:modelValue="$emit('updateValue')"
|
|
95
129
|
/>
|
|
96
130
|
|
|
97
131
|
<crud-autocomplete
|
|
@@ -102,6 +136,12 @@ const inputErrors = computed(() =>
|
|
|
102
136
|
:label="label"
|
|
103
137
|
:error-messages="inputErrors"
|
|
104
138
|
:rules="rules"
|
|
139
|
+
:density="density"
|
|
140
|
+
:variant="variant"
|
|
141
|
+
:clearable="clearable"
|
|
142
|
+
:hide-details="hideDetails"
|
|
143
|
+
:single-line="singleLine"
|
|
144
|
+
@updateValue="$emit('updateValue')"
|
|
105
145
|
/>
|
|
106
146
|
|
|
107
147
|
<v-card v-if="field.type === 'object'" class="mt-3" variant="flat" border>
|
|
@@ -113,6 +153,12 @@ const inputErrors = computed(() =>
|
|
|
113
153
|
:entity="entity"
|
|
114
154
|
:field="oField"
|
|
115
155
|
v-model="valueModel[oField.name]"
|
|
156
|
+
:density="density"
|
|
157
|
+
:variant="variant"
|
|
158
|
+
:clearable="clearable"
|
|
159
|
+
:hide-details="hideDetails"
|
|
160
|
+
:single-line="singleLine"
|
|
161
|
+
@updateValue="$emit('updateValue')"
|
|
116
162
|
></crud-form-field>
|
|
117
163
|
</v-card-text>
|
|
118
164
|
|
|
@@ -127,9 +173,15 @@ const inputErrors = computed(() =>
|
|
|
127
173
|
:multiple="true"
|
|
128
174
|
:chips="true"
|
|
129
175
|
:closable-chips="true"
|
|
130
|
-
:clearable="true"
|
|
131
176
|
:readonly="readonly"
|
|
132
177
|
:error-messages="inputErrors"
|
|
178
|
+
:density="density"
|
|
179
|
+
:variant="variant"
|
|
180
|
+
:clearable="clearable"
|
|
181
|
+
:hide-details="hideDetails"
|
|
182
|
+
:single-line="singleLine"
|
|
183
|
+
:rules="rules"
|
|
184
|
+
@update:modelValue="$emit('updateValue')"
|
|
133
185
|
>
|
|
134
186
|
</v-combobox>
|
|
135
187
|
|
|
@@ -141,9 +193,15 @@ const inputErrors = computed(() =>
|
|
|
141
193
|
v-model="valueModel"
|
|
142
194
|
:multiple="true"
|
|
143
195
|
:chips="true"
|
|
144
|
-
:clearable="true"
|
|
145
196
|
:label="label"
|
|
197
|
+
:rules="rules"
|
|
146
198
|
:error-messages="inputErrors"
|
|
199
|
+
:density="density"
|
|
200
|
+
:variant="variant"
|
|
201
|
+
:clearable="clearable"
|
|
202
|
+
:hide-details="hideDetails"
|
|
203
|
+
:single-line="singleLine"
|
|
204
|
+
@updateValue="$emit('updateValue')"
|
|
147
205
|
/>
|
|
148
206
|
|
|
149
207
|
|
|
@@ -155,9 +213,15 @@ const inputErrors = computed(() =>
|
|
|
155
213
|
v-model="valueModel"
|
|
156
214
|
:multiple="true"
|
|
157
215
|
:chips="true"
|
|
158
|
-
:clearable="true"
|
|
159
216
|
:readonly="readonly"
|
|
160
217
|
:error-messages="inputErrors"
|
|
218
|
+
:density="density"
|
|
219
|
+
:variant="variant"
|
|
220
|
+
:clearable="clearable"
|
|
221
|
+
:hide-details="hideDetails"
|
|
222
|
+
:single-line="singleLine"
|
|
223
|
+
:rules="rules"
|
|
224
|
+
@update:modelValue="$emit('updateValue')"
|
|
161
225
|
>
|
|
162
226
|
</v-combobox>
|
|
163
227
|
|
|
@@ -168,6 +232,12 @@ const inputErrors = computed(() =>
|
|
|
168
232
|
:field="field"
|
|
169
233
|
v-model="valueModel"
|
|
170
234
|
:readonly="readonly"
|
|
235
|
+
:density="density"
|
|
236
|
+
:variant="variant"
|
|
237
|
+
:clearable="clearable"
|
|
238
|
+
:hide-details="hideDetails"
|
|
239
|
+
:single-line="singleLine"
|
|
240
|
+
@updateValue="$emit('updateValue')"
|
|
171
241
|
/>
|
|
172
242
|
|
|
173
243
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type {PropType} from "vue";
|
|
3
3
|
import CrudFormField from "./CrudFormField.vue";
|
|
4
4
|
import type {IEntityCrud, IEntityCrudField} from "@drax/crud-share";
|
|
5
|
+
import {VDateInput} from "vuetify/lib/labs/VDateInput";
|
|
5
6
|
|
|
6
7
|
const valueModel = defineModel({type: Array, default: () => []});
|
|
7
8
|
|
|
@@ -9,6 +10,11 @@ const {field} = defineProps({
|
|
|
9
10
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
10
11
|
field: {type: Object as PropType<IEntityCrudField>, required: true},
|
|
11
12
|
readonly: {type: Boolean, default: false},
|
|
13
|
+
hideDetails: {type: Boolean, default: false},
|
|
14
|
+
singleLine: {type: Boolean, default: false},
|
|
15
|
+
clearable: {type: Boolean, default: true},
|
|
16
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
17
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
12
18
|
})
|
|
13
19
|
|
|
14
20
|
function newItem() {
|
|
@@ -31,6 +37,8 @@ function removeItem(index: number) {
|
|
|
31
37
|
valueModel.value.splice(index, 1);
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
defineEmits(['updateValue'])
|
|
41
|
+
|
|
34
42
|
</script>
|
|
35
43
|
|
|
36
44
|
<template>
|
|
@@ -50,6 +58,12 @@ function removeItem(index: number) {
|
|
|
50
58
|
v-model="(valueModel[index] as any)[key]"
|
|
51
59
|
:readonly="readonly"
|
|
52
60
|
:index="index"
|
|
61
|
+
:density="density"
|
|
62
|
+
:variant="variant"
|
|
63
|
+
:clearable="clearable"
|
|
64
|
+
:hide-details="hideDetails"
|
|
65
|
+
:single-line="singleLine"
|
|
66
|
+
@updateValue="$emit('updateValue')"
|
|
53
67
|
/>
|
|
54
68
|
</template>
|
|
55
69
|
|
|
@@ -12,27 +12,38 @@ import CrudExportList from "./CrudExportList.vue";
|
|
|
12
12
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
13
13
|
import {useI18n} from "vue-i18n";
|
|
14
14
|
import type {IEntityCrudHeader} from "@drax/crud-share";
|
|
15
|
+
import CrudFilters from "./CrudFilters.vue";
|
|
15
16
|
|
|
16
|
-
const {t} = useI18n()
|
|
17
|
+
const {t, te} = useI18n()
|
|
17
18
|
const {hasPermission} = useAuth()
|
|
18
19
|
|
|
19
20
|
const {entity} = defineProps({
|
|
20
21
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
22
|
+
|
|
21
23
|
})
|
|
22
24
|
|
|
23
25
|
const {
|
|
24
26
|
loading, itemsPerPage, page, sortBy, search, totalItems, items,
|
|
25
|
-
|
|
27
|
+
doPaginate, filters
|
|
26
28
|
} = useCrud(entity)
|
|
27
29
|
|
|
28
|
-
const actions: IEntityCrudHeader[] = [{
|
|
29
|
-
|
|
30
|
+
const actions: IEntityCrudHeader[] = [{
|
|
31
|
+
title: t('action.actions'),
|
|
32
|
+
key: 'actions',
|
|
33
|
+
sortable: false,
|
|
34
|
+
align: 'end',
|
|
35
|
+
minWidth: '140px'
|
|
36
|
+
}]
|
|
37
|
+
const tHeaders: IEntityCrudHeader[] = entity.headers.map(header => ({
|
|
38
|
+
...header,
|
|
39
|
+
title: te(`${entity.name.toLowerCase()}.fields.${header.title}`) ? t(`${entity.name.toLowerCase()}.fields.${header.title}`) : header.title
|
|
40
|
+
}))
|
|
30
41
|
|
|
31
42
|
const headers: IEntityCrudHeader[] = [...tHeaders, ...actions]
|
|
32
43
|
|
|
33
44
|
|
|
34
45
|
defineExpose({
|
|
35
|
-
|
|
46
|
+
doPaginate
|
|
36
47
|
});
|
|
37
48
|
|
|
38
49
|
</script>
|
|
@@ -52,7 +63,7 @@ defineExpose({
|
|
|
52
63
|
:search="search"
|
|
53
64
|
:multi-sort="false"
|
|
54
65
|
item-value="name"
|
|
55
|
-
@update:options="
|
|
66
|
+
@update:options="doPaginate"
|
|
56
67
|
>
|
|
57
68
|
<template v-slot:top>
|
|
58
69
|
<v-toolbar density="compact">
|
|
@@ -77,12 +88,25 @@ defineExpose({
|
|
|
77
88
|
|
|
78
89
|
</v-toolbar>
|
|
79
90
|
|
|
80
|
-
<crud-export-list
|
|
91
|
+
<crud-export-list
|
|
92
|
+
:entity="entity"
|
|
93
|
+
/>
|
|
81
94
|
|
|
82
95
|
<v-card>
|
|
83
96
|
<v-card-text>
|
|
84
|
-
<crud-search
|
|
97
|
+
<crud-search
|
|
98
|
+
v-model="search"
|
|
99
|
+
/>
|
|
85
100
|
</v-card-text>
|
|
101
|
+
|
|
102
|
+
<v-card-text class="pt-0">
|
|
103
|
+
<crud-filters
|
|
104
|
+
:entity="entity"
|
|
105
|
+
v-model="filters"
|
|
106
|
+
@updateValue="doPaginate()"
|
|
107
|
+
/>
|
|
108
|
+
</v-card-text>
|
|
109
|
+
|
|
86
110
|
</v-card>
|
|
87
111
|
|
|
88
112
|
</template>
|
|
@@ -5,12 +5,14 @@ const model = defineModel<any>()
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<template>
|
|
8
|
-
<v-text-field v-model="model"
|
|
9
|
-
density="compact"
|
|
8
|
+
<v-text-field v-model="model"
|
|
9
|
+
density="compact"
|
|
10
|
+
class="mr-2"
|
|
10
11
|
variant="outlined"
|
|
11
12
|
append-inner-icon="mdi-magnify"
|
|
12
13
|
:label="t('action.search')"
|
|
13
|
-
single-line
|
|
14
|
+
single-line hide-details
|
|
15
|
+
clearable @click:clear="() => model = ''"
|
|
14
16
|
/>
|
|
15
17
|
</template>
|
|
16
18
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type {PropType} from "vue";
|
|
3
|
-
import
|
|
3
|
+
import type {IEntityCrud} from "@drax/crud-share"
|
|
4
4
|
import {useCrud} from "../../composables/UseCrud";
|
|
5
5
|
import {useI18n} from "vue-i18n";
|
|
6
6
|
|
|
7
7
|
const {t} = useI18n()
|
|
8
8
|
|
|
9
9
|
const {entity} = defineProps({
|
|
10
|
-
entity: {type: Object as PropType<
|
|
10
|
+
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
11
11
|
})
|
|
12
12
|
|
|
13
13
|
const {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type {PropType} from "vue";
|
|
3
|
-
import
|
|
3
|
+
import type {IEntityCrud} from "@drax/crud-share"
|
|
4
4
|
import {useCrud} from "../../composables/UseCrud";
|
|
5
5
|
import {useI18n} from "vue-i18n";
|
|
6
6
|
|
|
7
7
|
const {t} = useI18n()
|
|
8
8
|
const {entity} = defineProps({
|
|
9
|
-
entity: {type: Object as PropType<
|
|
9
|
+
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
const {
|
|
@@ -6,15 +6,37 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
6
6
|
|
|
7
7
|
const store = useCrudStore()
|
|
8
8
|
|
|
9
|
+
const dialog = computed({get(){return store.dialog} , set(value){store.setDialog(value)}})
|
|
10
|
+
const operation = computed({get(){return store.operation} , set(value){store.setOperation(value)}})
|
|
11
|
+
const form = computed({get(){return store.form} , set(value){store.setForm(value)}})
|
|
12
|
+
const formValid = computed({get(){return store.formValid} , set(value){store.setFormValid(value)}})
|
|
13
|
+
const notify = computed({get(){return store.notify} , set(value){store.setNotify(value)}})
|
|
14
|
+
const error = computed({get(){return store.error} , set(value){store.setError(value)}})
|
|
15
|
+
const message = computed({get(){return store.message} , set(value){store.setMessage(value)}})
|
|
16
|
+
const loading = computed({get(){return store.loading} , set(value){store.setLoading(value)}})
|
|
17
|
+
const itemsPerPage = computed({get(){return store.itemsPerPage} , set(value){store.setItemsPerPage(value)}})
|
|
18
|
+
const page = computed({get(){return store.page} , set(value){store.setPage(value)}})
|
|
19
|
+
const sortBy = computed({get(){return store.sortBy} , set(value){store.setSortBy(value)}})
|
|
20
|
+
const search = computed({get(){return store.search} , set(value){store.setSearch(value)}})
|
|
21
|
+
const totalItems = computed({get(){return store.totalItems} , set(value){store.setTotalItems(value)}})
|
|
22
|
+
const items = computed({get(){return store.items} , set(value){store.setItems(value)}})
|
|
23
|
+
const exportFiles = computed({get(){return store.exportFiles} , set(value){store.setExportFiles(value)}})
|
|
24
|
+
const exportLoading = computed({get(){return store.exportLoading} , set(value){store.setExportLoading(value)}})
|
|
25
|
+
const exportListVisible = computed({get(){return store.exportListVisible} , set(value){store.setExportListVisible(value)}})
|
|
26
|
+
const filters = computed({get(){return store.filters} , set(value){store.setFilters(value)}})
|
|
27
|
+
|
|
28
|
+
|
|
9
29
|
async function doPaginate() {
|
|
10
30
|
store.setLoading(true)
|
|
11
31
|
try {
|
|
32
|
+
|
|
12
33
|
const r: IDraxPaginateResult<any> = await entity?.provider.paginate({
|
|
13
34
|
page: store.page,
|
|
14
35
|
limit: store.itemsPerPage,
|
|
15
36
|
orderBy: store.sortBy[0]?.key,
|
|
16
37
|
order: store.sortBy[0]?.order,
|
|
17
|
-
search: store.search
|
|
38
|
+
search: store.search,
|
|
39
|
+
filters: store.filters
|
|
18
40
|
})
|
|
19
41
|
store.setItems(r.items)
|
|
20
42
|
store.setTotalItems(r.total)
|
|
@@ -31,7 +53,7 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
31
53
|
try {
|
|
32
54
|
|
|
33
55
|
if(!entity?.provider.export) {
|
|
34
|
-
throw new Error("
|
|
56
|
+
throw new Error("provider.export not implemented")
|
|
35
57
|
}
|
|
36
58
|
|
|
37
59
|
const headers: string = entity.exportHeaders.join(',')
|
|
@@ -106,7 +128,6 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
106
128
|
}
|
|
107
129
|
|
|
108
130
|
function onSubmit(formData: any) {
|
|
109
|
-
console.log("formData", formData)
|
|
110
131
|
store.setInputErrors(null)
|
|
111
132
|
switch (store.operation) {
|
|
112
133
|
case "create":
|
|
@@ -167,28 +188,20 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
167
188
|
|
|
168
189
|
}
|
|
169
190
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const itemsPerPage = computed({get(){return store.itemsPerPage} , set(value){store.setItemsPerPage(value)}})
|
|
179
|
-
const page = computed({get(){return store.page} , set(value){store.setPage(value)}})
|
|
180
|
-
const sortBy = computed({get(){return store.sortBy} , set(value){store.setSortBy(value)}})
|
|
181
|
-
const search = computed({get(){return store.search} , set(value){store.setSearch(value)}})
|
|
182
|
-
const totalItems = computed({get(){return store.totalItems} , set(value){store.setTotalItems(value)}})
|
|
183
|
-
const items = computed({get(){return store.items} , set(value){store.setItems(value)}})
|
|
184
|
-
const exportFiles = computed({get(){return store.exportFiles} , set(value){store.setExportFiles(value)}})
|
|
185
|
-
const exportLoading = computed({get(){return store.exportLoading} , set(value){store.setExportLoading(value)}})
|
|
186
|
-
const exportListVisible = computed({get(){return store.exportListVisible} , set(value){store.setExportListVisible(value)}})
|
|
191
|
+
function resetCrudStore(){
|
|
192
|
+
store.$reset()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function prepareFilters(){
|
|
196
|
+
store.setFilters(entity.formFilters)
|
|
197
|
+
}
|
|
198
|
+
|
|
187
199
|
|
|
188
200
|
return {
|
|
189
|
-
|
|
201
|
+
doPaginate, doExport, onCreate, onEdit, onDelete, onCancel, onSubmit,resetCrudStore,
|
|
190
202
|
operation, dialog, form, notify, error, message, formValid,
|
|
191
203
|
loading, itemsPerPage, page, sortBy, search, totalItems, items,
|
|
204
|
+
prepareFilters,filters,
|
|
192
205
|
exportFiles,exportLoading,exportListVisible
|
|
193
206
|
}
|
|
194
207
|
|
|
@@ -11,6 +11,7 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
11
11
|
notify: false as boolean,
|
|
12
12
|
message: '' as string,
|
|
13
13
|
error: '' as string,
|
|
14
|
+
filters: [] as any[],
|
|
14
15
|
items: [] as any[],
|
|
15
16
|
totalItems: 0 as number,
|
|
16
17
|
itemsPerPage: 5 as number,
|
|
@@ -84,6 +85,10 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
84
85
|
setInputErrors(inputErrors: any) {
|
|
85
86
|
this.inputErrors = inputErrors
|
|
86
87
|
},
|
|
88
|
+
resetErrors(){
|
|
89
|
+
this.inputErrors = null
|
|
90
|
+
this.error = ''
|
|
91
|
+
},
|
|
87
92
|
setExportFiles(exportFiles: string[]) {
|
|
88
93
|
this.exportFiles = exportFiles
|
|
89
94
|
},
|
|
@@ -95,6 +100,9 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
95
100
|
},
|
|
96
101
|
setExportListVisible(exportListVisible: boolean) {
|
|
97
102
|
this.exportListVisible = exportListVisible
|
|
103
|
+
},
|
|
104
|
+
setFilters(filters: any[]) {
|
|
105
|
+
this.filters = filters
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|