@drax/crud-vue 0.5.2 → 0.5.4
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 +5 -5
- package/src/EntityCrud.ts +27 -6
- package/src/components/Crud.vue +5 -2
- package/src/components/CrudAutocomplete.vue +55 -38
- package/src/components/CrudDialog.vue +2 -3
- package/src/components/CrudFilters.vue +65 -0
- package/src/components/CrudForm.vue +40 -13
- package/src/components/CrudFormField.vue +123 -15
- package/src/components/CrudFormList.vue +13 -0
- package/src/components/CrudList.vue +36 -7
- package/src/components/CrudSearch.vue +5 -3
- package/src/components/buttons/CrudViewButton.vue +25 -0
- package/src/composables/UseCrud.ts +61 -34
- package/src/composables/UseFormUtils.ts +42 -0
- package/src/index.ts +2 -0
- package/src/stores/UseCrudStore.ts +7 -3
- package/src/interfaces/TOperation.ts +0 -6
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.4",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"format": "prettier --write src/"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@drax/common-front": "^0.5.
|
|
28
|
-
"@drax/crud-front": "^0.5.
|
|
29
|
-
"@drax/crud-share": "^0.5.
|
|
27
|
+
"@drax/common-front": "^0.5.4",
|
|
28
|
+
"@drax/crud-front": "^0.5.3",
|
|
29
|
+
"@drax/crud-share": "^0.5.4"
|
|
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": "6db0bebb2df9edecd2c3caa7288fb5c7001c4243"
|
|
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,21 @@ 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 createFields(){
|
|
44
|
+
return this.fields
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get updateFields(){
|
|
48
|
+
return this.fields
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get filters():IEntityCrudFilter[]{
|
|
52
|
+
return [
|
|
53
|
+
{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq' },
|
|
40
54
|
]
|
|
41
55
|
}
|
|
42
56
|
|
|
@@ -57,7 +71,7 @@ class EntityCrud implements IEntityCrud{
|
|
|
57
71
|
|
|
58
72
|
get form():IEntityCrudForm{
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
return this.fields.reduce((acc, field) => {
|
|
61
75
|
let value = null
|
|
62
76
|
if(field.type === 'object'){
|
|
63
77
|
value = this.objectFields(field)
|
|
@@ -68,10 +82,14 @@ class EntityCrud implements IEntityCrud{
|
|
|
68
82
|
return {...acc, [field.name]: value }
|
|
69
83
|
}, {})
|
|
70
84
|
|
|
71
|
-
|
|
85
|
+
}
|
|
72
86
|
|
|
73
|
-
return form
|
|
74
87
|
|
|
88
|
+
get formFilters():IEntityCrudFormFilter[]{
|
|
89
|
+
return this.filters.map(
|
|
90
|
+
(filter:IEntityCrudFilter) =>
|
|
91
|
+
({field:filter.name, value: null, operator: filter.operator })
|
|
92
|
+
)
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
get refs():IEntityCrudRefs{
|
|
@@ -91,10 +109,13 @@ class EntityCrud implements IEntityCrud{
|
|
|
91
109
|
}
|
|
92
110
|
|
|
93
111
|
getRule(field:string|undefined):Array<Function>|undefined {
|
|
94
|
-
console.log("Getting rule for field: ", field, this.rules)
|
|
95
112
|
return field && this.rules[field] && this.rules[field].length > 0 ? this.rules[field] : undefined
|
|
96
113
|
}
|
|
97
114
|
|
|
115
|
+
get isViewable(){
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
get isEditable(){
|
|
99
120
|
return true
|
|
100
121
|
}
|
package/src/components/Crud.vue
CHANGED
|
@@ -12,12 +12,14 @@ const {entity} = defineProps({
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
|
-
onCreate, onEdit, onDelete, onCancel, onSubmit,resetCrudStore,
|
|
16
|
-
operation, dialog, form, notify, error, message, doExport
|
|
15
|
+
onView, onCreate, onEdit, onDelete, onCancel, onSubmit,resetCrudStore,
|
|
16
|
+
operation, dialog, form, notify, error, message, doExport,
|
|
17
|
+
prepareFilters
|
|
17
18
|
} = useCrud(entity);
|
|
18
19
|
|
|
19
20
|
onBeforeMount(() => {
|
|
20
21
|
resetCrudStore()
|
|
22
|
+
prepareFilters()
|
|
21
23
|
})
|
|
22
24
|
|
|
23
25
|
</script>
|
|
@@ -32,6 +34,7 @@ onBeforeMount(() => {
|
|
|
32
34
|
@edit="onEdit"
|
|
33
35
|
@delete="onDelete"
|
|
34
36
|
@export="doExport"
|
|
37
|
+
@view="onView"
|
|
35
38
|
>
|
|
36
39
|
<template v-for="header in entity.headers" :key="header.key" v-slot:[`item.${header.key}`]="{item, value}">
|
|
37
40
|
<slot :name="`item.${header.key}`" v-bind="{item, value}">
|
|
@@ -9,15 +9,24 @@ const valueModel = defineModel<string | string[]>({type: [String, Array], requir
|
|
|
9
9
|
const {entity, multiple} = defineProps({
|
|
10
10
|
entity: {type: Object as PropType<IEntityCrud|undefined>, required: true},
|
|
11
11
|
field: {type: Object as PropType<IEntityCrudField>, required: true},
|
|
12
|
+
prependIcon: {type: String},
|
|
13
|
+
prependInnerIcon: {type: String},
|
|
14
|
+
appendIcon: {type: String},
|
|
15
|
+
appendInnerIcon: {type: String},
|
|
12
16
|
multiple: {type: Boolean, default: false},
|
|
13
17
|
chips: {type: Boolean, default: false},
|
|
14
18
|
closableChips: {type: Boolean, default: true},
|
|
19
|
+
readonly: {type: Boolean, default: false},
|
|
15
20
|
clearable: {type: Boolean, default: true},
|
|
16
21
|
label: {type: String},
|
|
17
22
|
itemValue: {type: [String], default: '_id'},
|
|
18
23
|
itemTitle: {type: [String], default: 'name'},
|
|
19
24
|
rules: {type: Array as PropType<any>, default: []},
|
|
20
25
|
errorMessages: {type: Array as PropType<string[]>, default: []},
|
|
26
|
+
hideDetails: {type: Boolean, default: false},
|
|
27
|
+
singleLine: {type: Boolean, default: false},
|
|
28
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
29
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
21
30
|
})
|
|
22
31
|
|
|
23
32
|
if(!entity){
|
|
@@ -29,63 +38,61 @@ const items: Ref<Array<any>> = ref([])
|
|
|
29
38
|
|
|
30
39
|
const debouncedSearch = debounce(search, 300)
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
if(valueModel.value && valueModel.value.length > 0){
|
|
34
|
-
|
|
35
|
-
if(multiple && Array.isArray(valueModel.value) ){
|
|
36
|
-
items.value = valueModel.value
|
|
37
|
-
|
|
38
|
-
// valueModel.value = valueModel.value.map((item:any) => item._id)
|
|
39
|
-
await findByIds(valueModel.value)
|
|
40
|
-
}else if(!Array.isArray(valueModel.value)){
|
|
41
|
-
// items.value = [valueModel.value]
|
|
42
|
-
await findByIds([valueModel.value])
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
async function findByIds(ids: Array<string> = []) {
|
|
41
|
+
async function search(value: any) {
|
|
51
42
|
try{
|
|
43
|
+
loading.value = true
|
|
52
44
|
if(!entity){
|
|
53
45
|
throw new Error('Entity is required')
|
|
54
46
|
}
|
|
55
|
-
if(!entity.provider){
|
|
56
|
-
throw new Error('Provider
|
|
57
|
-
}
|
|
58
|
-
if (typeof entity.provider.findByIds !== 'function') {
|
|
59
|
-
throw new Error('Provider does not have a findByIds method');
|
|
47
|
+
if(!entity.provider.search){
|
|
48
|
+
throw new Error('Provider does not have a search method')
|
|
60
49
|
}
|
|
61
|
-
|
|
62
|
-
items.value = await entity.provider.findByIds(ids)
|
|
50
|
+
items.value = await entity.provider.search(value)
|
|
63
51
|
}catch (e){
|
|
64
52
|
console.error(e)
|
|
65
53
|
}finally{
|
|
66
54
|
loading.value = false
|
|
67
55
|
}
|
|
56
|
+
|
|
68
57
|
}
|
|
69
58
|
|
|
59
|
+
onBeforeMount(async () => {
|
|
70
60
|
|
|
71
|
-
|
|
61
|
+
await search('')
|
|
62
|
+
await checkIds()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
async function checkIds(ids: Array<string> = []) {
|
|
72
66
|
try{
|
|
73
|
-
|
|
74
|
-
if(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
|
|
68
|
+
if(valueModel.value) {
|
|
69
|
+
let ids = Array.isArray(valueModel.value) ? valueModel.value : [valueModel.value]
|
|
70
|
+
for (let id of ids) {
|
|
71
|
+
if (!items.value.some((item: any) => item._id === id)) {
|
|
72
|
+
if (!entity) {
|
|
73
|
+
throw new Error('CrudAutocomplete Entity is required')
|
|
74
|
+
}
|
|
75
|
+
if (!entity.provider) {
|
|
76
|
+
throw new Error('CrudAutocomplete Provider is not defined')
|
|
77
|
+
}
|
|
78
|
+
if (typeof entity.provider.findById !== 'function') {
|
|
79
|
+
throw new Error('CrudAutocomplete Provider does not have a findById method');
|
|
80
|
+
}
|
|
81
|
+
let item = await entity.provider.findById(id)
|
|
82
|
+
items.value.push(item)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
|
-
items.value = await entity.provider.search(value)
|
|
81
86
|
}catch (e){
|
|
82
87
|
console.error(e)
|
|
83
|
-
}finally{
|
|
84
|
-
loading.value = false
|
|
85
88
|
}
|
|
86
|
-
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
defineEmits(['updateValue'])
|
|
95
|
+
|
|
89
96
|
</script>
|
|
90
97
|
|
|
91
98
|
<template>
|
|
@@ -97,13 +104,23 @@ 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
|
+
:readonly="readonly"
|
|
112
|
+
:density="density"
|
|
113
|
+
:variant="variant"
|
|
114
|
+
:hide-details="hideDetails"
|
|
115
|
+
:single-line="singleLine"
|
|
116
|
+
:clearable="clearable"
|
|
105
117
|
:error-messages="errorMessages"
|
|
106
118
|
@update:search="debouncedSearch"
|
|
119
|
+
@update:modelValue="$emit('updateValue')"
|
|
120
|
+
:prepend-icon="prependIcon"
|
|
121
|
+
:append-icon="appendIcon"
|
|
122
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
123
|
+
:append-inner-icon="appendInnerIcon"
|
|
107
124
|
></v-autocomplete>
|
|
108
125
|
</template>
|
|
109
126
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {TOperation} from "../interfaces/TOperation";
|
|
3
2
|
import type {PropType} from "vue";
|
|
4
|
-
import type {IEntityCrud} from "@drax/crud-share";
|
|
3
|
+
import type {IEntityCrud, IEntityCrudOperation} from "@drax/crud-share";
|
|
5
4
|
const dialog = defineModel({type: Boolean, default: false})
|
|
6
5
|
import {useI18n} from "vue-i18n";
|
|
7
6
|
const {t,te} = useI18n()
|
|
8
7
|
|
|
9
8
|
defineProps({
|
|
10
9
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
11
|
-
operation: {type: String as PropType<
|
|
10
|
+
operation: {type: String as PropType<IEntityCrudOperation>}
|
|
12
11
|
})
|
|
13
12
|
|
|
14
13
|
defineEmits(
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
:clearable="true"
|
|
43
|
+
density="compact"
|
|
44
|
+
variant="outlined"
|
|
45
|
+
hide-details single-line disable-rules
|
|
46
|
+
@updateValue="$emit('updateValue')"
|
|
47
|
+
/>
|
|
48
|
+
</v-col>
|
|
49
|
+
|
|
50
|
+
</v-row>
|
|
51
|
+
|
|
52
|
+
<v-card-actions v-if="actions" class="pb-0">
|
|
53
|
+
<v-spacer />
|
|
54
|
+
<v-btn variant="text" density="compact" color="grey" @click="clear">{{ t('action.clear') }}</v-btn>
|
|
55
|
+
<v-btn variant="flat" density="compact" color="primary" @click="filter">
|
|
56
|
+
{{ t('action.filter') }}
|
|
57
|
+
</v-btn>
|
|
58
|
+
</v-card-actions>
|
|
59
|
+
|
|
60
|
+
</v-card>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<style scoped>
|
|
64
|
+
|
|
65
|
+
</style>
|
|
@@ -1,26 +1,43 @@
|
|
|
1
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 {TOperation} from "../interfaces/TOperation";
|
|
6
|
-
import type {IEntityCrud} from "@drax/crud-share";
|
|
7
2
|
import {useI18n} from "vue-i18n";
|
|
3
|
+
import type {IEntityCrud, IEntityCrudOperation} from "@drax/crud-share";
|
|
4
|
+
import {useFormUtils} from "../composables/UseFormUtils";
|
|
5
|
+
import CrudFormField from "./CrudFormField.vue";
|
|
6
|
+
import {computed, defineEmits, defineModel, defineProps, ref} from "vue";
|
|
7
|
+
import type { PropType} from "vue";
|
|
8
8
|
import {useCrudStore} from "../stores/UseCrudStore";
|
|
9
|
-
const {t,te} = useI18n()
|
|
10
|
-
const store = useCrudStore()
|
|
11
|
-
const valueModel = defineModel({type: [Object]})
|
|
12
9
|
|
|
13
10
|
|
|
14
|
-
const {
|
|
11
|
+
const {t, te} = useI18n()
|
|
12
|
+
|
|
13
|
+
const valueModel = defineModel({type: [Object]})
|
|
14
|
+
|
|
15
|
+
const {entity, operation} = defineProps({
|
|
15
16
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
16
|
-
operation: {type: String as PropType<
|
|
17
|
+
operation: {type: String as PropType<IEntityCrudOperation>, required: true},
|
|
17
18
|
readonly: {type: Boolean, default: false},
|
|
18
19
|
error: {type: String, required: false},
|
|
19
20
|
})
|
|
20
21
|
|
|
22
|
+
const emit = defineEmits(['submit', 'cancel'])
|
|
23
|
+
|
|
24
|
+
const store = useCrudStore()
|
|
25
|
+
|
|
21
26
|
const valid = ref()
|
|
22
27
|
const formRef = ref()
|
|
23
28
|
|
|
29
|
+
const fields = computed(() => {
|
|
30
|
+
if(operation === 'create') {
|
|
31
|
+
return entity.createFields
|
|
32
|
+
}else if(operation === 'edit') {
|
|
33
|
+
return entity.updateFields
|
|
34
|
+
}else if(operation === 'delete') {
|
|
35
|
+
return entity.updateFields
|
|
36
|
+
}else if(operation === 'view') {
|
|
37
|
+
return entity.updateFields
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
24
41
|
async function submit() {
|
|
25
42
|
store.resetErrors()
|
|
26
43
|
await formRef.value.validate()
|
|
@@ -35,7 +52,10 @@ function cancel() {
|
|
|
35
52
|
emit('cancel')
|
|
36
53
|
}
|
|
37
54
|
|
|
38
|
-
const
|
|
55
|
+
const {
|
|
56
|
+
variant, submitColor, readonly
|
|
57
|
+
} = useFormUtils(operation)
|
|
58
|
+
|
|
39
59
|
|
|
40
60
|
</script>
|
|
41
61
|
|
|
@@ -49,11 +69,18 @@ const emit = defineEmits(['submit', 'cancel'])
|
|
|
49
69
|
<v-alert color="error">{{ te(error) ? t(error) : error }}</v-alert>
|
|
50
70
|
</v-card-text>
|
|
51
71
|
<v-card-text>
|
|
52
|
-
<template v-for="field in
|
|
72
|
+
<template v-for="field in fields" :key="field.name">
|
|
53
73
|
<crud-form-field
|
|
54
74
|
:field="field"
|
|
55
75
|
:entity="entity"
|
|
56
76
|
v-model="valueModel[field.name]"
|
|
77
|
+
:clearable="false"
|
|
78
|
+
:readonly="readonly"
|
|
79
|
+
:variant="variant"
|
|
80
|
+
:prepend-inner-icon="field?.prependInnerIcon"
|
|
81
|
+
:prepend-icon="field?.prependIcon"
|
|
82
|
+
:append-icon="field?.appendIcon"
|
|
83
|
+
:append-inner-icon="field?.appendInnerIcon"
|
|
57
84
|
/>
|
|
58
85
|
</template>
|
|
59
86
|
</v-card-text>
|
|
@@ -61,7 +88,7 @@ const emit = defineEmits(['submit', 'cancel'])
|
|
|
61
88
|
<v-card-actions>
|
|
62
89
|
<v-spacer></v-spacer>
|
|
63
90
|
<v-btn variant="text" color="grey" @click="cancel">{{ t('action.cancel') }}</v-btn>
|
|
64
|
-
<v-btn variant="flat" color="
|
|
91
|
+
<v-btn variant="flat" v-if="operation != 'view'" :color="submitColor" @click="submit">
|
|
65
92
|
{{ operation ? t('action.' + operation) : t('action.sent') }}
|
|
66
93
|
</v-btn>
|
|
67
94
|
</v-card-actions>
|
|
@@ -6,20 +6,32 @@ 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
|
+
prependIcon: {type: String, default: ''},
|
|
20
|
+
prependInnerIcon: {type: String, default: ''},
|
|
21
|
+
appendIcon: {type: String, default: ''},
|
|
22
|
+
appendInnerIcon: {type: String, default: ''},
|
|
19
23
|
readonly: {type: Boolean, default: false},
|
|
24
|
+
hideDetails: {type: Boolean, default: false},
|
|
25
|
+
singleLine: {type: Boolean, default: false},
|
|
26
|
+
clearable: {type: Boolean, default: false},
|
|
27
|
+
disableRules: {type: Boolean, default: false},
|
|
20
28
|
index: {type: Number, default: 0},
|
|
29
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
30
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
21
31
|
})
|
|
22
32
|
|
|
33
|
+
|
|
34
|
+
|
|
23
35
|
if(!field){
|
|
24
36
|
throw new Error("CrudFormField must be provided with a field object")
|
|
25
37
|
}
|
|
@@ -32,6 +44,7 @@ const label = computed(() => {
|
|
|
32
44
|
})
|
|
33
45
|
|
|
34
46
|
const rules = computed(() => {
|
|
47
|
+
if(disableRules) return undefined
|
|
35
48
|
return entity.getRule(field.name) as any
|
|
36
49
|
})
|
|
37
50
|
|
|
@@ -39,6 +52,8 @@ const inputErrors = computed(() =>
|
|
|
39
52
|
store.getInputErrors(field.name).map((error: string) => t(te(error) ? t(error) : error))
|
|
40
53
|
)
|
|
41
54
|
|
|
55
|
+
defineEmits(['updateValue'])
|
|
56
|
+
|
|
42
57
|
</script>
|
|
43
58
|
|
|
44
59
|
<template>
|
|
@@ -53,8 +68,17 @@ const inputErrors = computed(() =>
|
|
|
53
68
|
:readonly="readonly"
|
|
54
69
|
:error-messages="inputErrors"
|
|
55
70
|
:rules="rules"
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
:density="density"
|
|
72
|
+
:variant="variant"
|
|
73
|
+
:clearable="clearable"
|
|
74
|
+
:hide-details="hideDetails"
|
|
75
|
+
:single-line="singleLine"
|
|
76
|
+
:prepend-icon="prependIcon"
|
|
77
|
+
:append-icon="appendIcon"
|
|
78
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
79
|
+
:append-inner-icon="appendInnerIcon"
|
|
80
|
+
@update:modelValue="$emit('updateValue')"
|
|
81
|
+
/>
|
|
58
82
|
|
|
59
83
|
<v-text-field
|
|
60
84
|
v-if="field.type === 'number'"
|
|
@@ -65,10 +89,19 @@ const inputErrors = computed(() =>
|
|
|
65
89
|
:readonly="readonly"
|
|
66
90
|
:error-messages="inputErrors"
|
|
67
91
|
:rules="rules"
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
:density="density"
|
|
93
|
+
:variant="variant"
|
|
94
|
+
:clearable="clearable"
|
|
95
|
+
:hide-details="hideDetails"
|
|
96
|
+
:single-line="singleLine"
|
|
97
|
+
@update:modelValue="$emit('updateValue')"
|
|
98
|
+
:prepend-icon="prependIcon"
|
|
99
|
+
:append-icon="appendIcon"
|
|
100
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
101
|
+
:append-inner-icon="appendInnerIcon"
|
|
102
|
+
/>
|
|
70
103
|
|
|
71
|
-
<v-
|
|
104
|
+
<v-switch
|
|
72
105
|
v-if="field.type === 'boolean'"
|
|
73
106
|
:name="name"
|
|
74
107
|
:label="label"
|
|
@@ -76,8 +109,18 @@ const inputErrors = computed(() =>
|
|
|
76
109
|
:readonly="readonly"
|
|
77
110
|
:error-messages="inputErrors"
|
|
78
111
|
:rules="rules"
|
|
79
|
-
|
|
80
|
-
|
|
112
|
+
:density="density"
|
|
113
|
+
:variant="variant"
|
|
114
|
+
:clearable="clearable"
|
|
115
|
+
:hide-details="hideDetails"
|
|
116
|
+
:single-line="singleLine"
|
|
117
|
+
@update:modelValue="$emit('updateValue')"
|
|
118
|
+
:prepend-icon="prependIcon"
|
|
119
|
+
:append-icon="appendIcon"
|
|
120
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
121
|
+
:append-inner-icon="appendInnerIcon"
|
|
122
|
+
color="primary"
|
|
123
|
+
/>
|
|
81
124
|
|
|
82
125
|
|
|
83
126
|
<v-date-input
|
|
@@ -88,9 +131,17 @@ const inputErrors = computed(() =>
|
|
|
88
131
|
v-model="valueModel"
|
|
89
132
|
:readonly="readonly"
|
|
90
133
|
:error-messages="inputErrors"
|
|
91
|
-
prepend-inner-icon="mdi-calendar"
|
|
92
|
-
prepend-icon=""
|
|
93
134
|
:rules="rules"
|
|
135
|
+
:density="density"
|
|
136
|
+
:variant="variant"
|
|
137
|
+
:clearable="clearable"
|
|
138
|
+
:hide-details="hideDetails"
|
|
139
|
+
:single-line="singleLine"
|
|
140
|
+
@update:modelValue="$emit('updateValue')"
|
|
141
|
+
:prepend-icon="prependIcon"
|
|
142
|
+
:append-icon="appendIcon"
|
|
143
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
144
|
+
:append-inner-icon="appendInnerIcon"
|
|
94
145
|
/>
|
|
95
146
|
|
|
96
147
|
<crud-autocomplete
|
|
@@ -101,6 +152,17 @@ const inputErrors = computed(() =>
|
|
|
101
152
|
:label="label"
|
|
102
153
|
:error-messages="inputErrors"
|
|
103
154
|
:rules="rules"
|
|
155
|
+
:density="density"
|
|
156
|
+
:variant="variant"
|
|
157
|
+
:readonly="readonly"
|
|
158
|
+
:clearable="clearable"
|
|
159
|
+
:hide-details="hideDetails"
|
|
160
|
+
:single-line="singleLine"
|
|
161
|
+
@updateValue="$emit('updateValue')"
|
|
162
|
+
:prepend-icon="prependIcon"
|
|
163
|
+
:append-icon="appendIcon"
|
|
164
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
165
|
+
:append-inner-icon="appendInnerIcon"
|
|
104
166
|
/>
|
|
105
167
|
|
|
106
168
|
<v-card v-if="field.type === 'object'" class="mt-3" variant="flat" border>
|
|
@@ -112,6 +174,16 @@ const inputErrors = computed(() =>
|
|
|
112
174
|
:entity="entity"
|
|
113
175
|
:field="oField"
|
|
114
176
|
v-model="valueModel[oField.name]"
|
|
177
|
+
:density="density"
|
|
178
|
+
:variant="variant"
|
|
179
|
+
:clearable="clearable"
|
|
180
|
+
:hide-details="hideDetails"
|
|
181
|
+
:single-line="singleLine"
|
|
182
|
+
@updateValue="$emit('updateValue')"
|
|
183
|
+
:prepend-icon="prependIcon"
|
|
184
|
+
:append-icon="appendIcon"
|
|
185
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
186
|
+
:append-inner-icon="appendInnerIcon"
|
|
115
187
|
></crud-form-field>
|
|
116
188
|
</v-card-text>
|
|
117
189
|
|
|
@@ -126,9 +198,19 @@ const inputErrors = computed(() =>
|
|
|
126
198
|
:multiple="true"
|
|
127
199
|
:chips="true"
|
|
128
200
|
:closable-chips="true"
|
|
129
|
-
:clearable="true"
|
|
130
201
|
:readonly="readonly"
|
|
131
202
|
:error-messages="inputErrors"
|
|
203
|
+
:density="density"
|
|
204
|
+
:variant="variant"
|
|
205
|
+
:clearable="clearable"
|
|
206
|
+
:hide-details="hideDetails"
|
|
207
|
+
:single-line="singleLine"
|
|
208
|
+
:rules="rules"
|
|
209
|
+
@update:modelValue="$emit('updateValue')"
|
|
210
|
+
:prepend-icon="prependIcon"
|
|
211
|
+
:append-icon="appendIcon"
|
|
212
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
213
|
+
:append-inner-icon="appendInnerIcon"
|
|
132
214
|
>
|
|
133
215
|
</v-combobox>
|
|
134
216
|
|
|
@@ -140,9 +222,19 @@ const inputErrors = computed(() =>
|
|
|
140
222
|
v-model="valueModel"
|
|
141
223
|
:multiple="true"
|
|
142
224
|
:chips="true"
|
|
143
|
-
:clearable="true"
|
|
144
225
|
:label="label"
|
|
226
|
+
:rules="rules"
|
|
145
227
|
:error-messages="inputErrors"
|
|
228
|
+
:density="density"
|
|
229
|
+
:variant="variant"
|
|
230
|
+
:clearable="clearable"
|
|
231
|
+
:hide-details="hideDetails"
|
|
232
|
+
:single-line="singleLine"
|
|
233
|
+
@updateValue="$emit('updateValue')"
|
|
234
|
+
:prepend-icon="prependIcon"
|
|
235
|
+
:append-icon="appendIcon"
|
|
236
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
237
|
+
:append-inner-icon="appendInnerIcon"
|
|
146
238
|
/>
|
|
147
239
|
|
|
148
240
|
|
|
@@ -154,9 +246,19 @@ const inputErrors = computed(() =>
|
|
|
154
246
|
v-model="valueModel"
|
|
155
247
|
:multiple="true"
|
|
156
248
|
:chips="true"
|
|
157
|
-
:clearable="true"
|
|
158
249
|
:readonly="readonly"
|
|
159
250
|
:error-messages="inputErrors"
|
|
251
|
+
:density="density"
|
|
252
|
+
:variant="variant"
|
|
253
|
+
:clearable="clearable"
|
|
254
|
+
:hide-details="hideDetails"
|
|
255
|
+
:single-line="singleLine"
|
|
256
|
+
:rules="rules"
|
|
257
|
+
@update:modelValue="$emit('updateValue')"
|
|
258
|
+
:prepend-icon="prependIcon"
|
|
259
|
+
:append-icon="appendIcon"
|
|
260
|
+
:prepend-inner-icon="prependInnerIcon"
|
|
261
|
+
:append-inner-icon="appendInnerIcon"
|
|
160
262
|
>
|
|
161
263
|
</v-combobox>
|
|
162
264
|
|
|
@@ -167,6 +269,12 @@ const inputErrors = computed(() =>
|
|
|
167
269
|
:field="field"
|
|
168
270
|
v-model="valueModel"
|
|
169
271
|
:readonly="readonly"
|
|
272
|
+
:density="density"
|
|
273
|
+
:variant="variant"
|
|
274
|
+
:clearable="clearable"
|
|
275
|
+
:hide-details="hideDetails"
|
|
276
|
+
:single-line="singleLine"
|
|
277
|
+
@updateValue="$emit('updateValue')"
|
|
170
278
|
/>
|
|
171
279
|
|
|
172
280
|
</div>
|
|
@@ -9,6 +9,11 @@ const {field} = defineProps({
|
|
|
9
9
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
10
10
|
field: {type: Object as PropType<IEntityCrudField>, required: true},
|
|
11
11
|
readonly: {type: Boolean, default: false},
|
|
12
|
+
hideDetails: {type: Boolean, default: false},
|
|
13
|
+
singleLine: {type: Boolean, default: false},
|
|
14
|
+
clearable: {type: Boolean, default: true},
|
|
15
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
16
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
12
17
|
})
|
|
13
18
|
|
|
14
19
|
function newItem() {
|
|
@@ -31,6 +36,8 @@ function removeItem(index: number) {
|
|
|
31
36
|
valueModel.value.splice(index, 1);
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
defineEmits(['updateValue'])
|
|
40
|
+
|
|
34
41
|
</script>
|
|
35
42
|
|
|
36
43
|
<template>
|
|
@@ -50,6 +57,12 @@ function removeItem(index: number) {
|
|
|
50
57
|
v-model="(valueModel[index] as any)[key]"
|
|
51
58
|
:readonly="readonly"
|
|
52
59
|
:index="index"
|
|
60
|
+
:density="density"
|
|
61
|
+
:variant="variant"
|
|
62
|
+
:clearable="clearable"
|
|
63
|
+
:hide-details="hideDetails"
|
|
64
|
+
:single-line="singleLine"
|
|
65
|
+
@updateValue="$emit('updateValue')"
|
|
53
66
|
/>
|
|
54
67
|
</template>
|
|
55
68
|
|
|
@@ -8,24 +8,33 @@ import CrudImportButton from "./buttons/CrudImportButton.vue";
|
|
|
8
8
|
import CrudCreateButton from "./buttons/CrudCreateButton.vue";
|
|
9
9
|
import CrudUpdateButton from "./buttons/CrudUpdateButton.vue";
|
|
10
10
|
import CrudDeleteButton from "./buttons/CrudDeleteButton.vue";
|
|
11
|
+
import CrudViewButton from "./buttons/CrudViewButton.vue";
|
|
11
12
|
import CrudExportList from "./CrudExportList.vue";
|
|
12
13
|
import type {IEntityCrud} from "@drax/crud-share";
|
|
13
14
|
import {useI18n} from "vue-i18n";
|
|
14
15
|
import type {IEntityCrudHeader} from "@drax/crud-share";
|
|
16
|
+
import CrudFilters from "./CrudFilters.vue";
|
|
15
17
|
|
|
16
|
-
const {t,te} = useI18n()
|
|
18
|
+
const {t, te} = useI18n()
|
|
17
19
|
const {hasPermission} = useAuth()
|
|
18
20
|
|
|
19
21
|
const {entity} = defineProps({
|
|
20
22
|
entity: {type: Object as PropType<IEntityCrud>, required: true},
|
|
23
|
+
|
|
21
24
|
})
|
|
22
25
|
|
|
23
26
|
const {
|
|
24
27
|
loading, itemsPerPage, page, sortBy, search, totalItems, items,
|
|
25
|
-
|
|
28
|
+
doPaginate, filters
|
|
26
29
|
} = useCrud(entity)
|
|
27
30
|
|
|
28
|
-
const actions: IEntityCrudHeader[] = [{
|
|
31
|
+
const actions: IEntityCrudHeader[] = [{
|
|
32
|
+
title: t('action.actions'),
|
|
33
|
+
key: 'actions',
|
|
34
|
+
sortable: false,
|
|
35
|
+
align: 'center',
|
|
36
|
+
minWidth: '190px'
|
|
37
|
+
}]
|
|
29
38
|
const tHeaders: IEntityCrudHeader[] = entity.headers.map(header => ({
|
|
30
39
|
...header,
|
|
31
40
|
title: te(`${entity.name.toLowerCase()}.fields.${header.title}`) ? t(`${entity.name.toLowerCase()}.fields.${header.title}`) : header.title
|
|
@@ -35,9 +44,11 @@ const headers: IEntityCrudHeader[] = [...tHeaders, ...actions]
|
|
|
35
44
|
|
|
36
45
|
|
|
37
46
|
defineExpose({
|
|
38
|
-
|
|
47
|
+
doPaginate
|
|
39
48
|
});
|
|
40
49
|
|
|
50
|
+
defineEmits(['import', 'export', 'create', 'update', 'delete', 'view'])
|
|
51
|
+
|
|
41
52
|
</script>
|
|
42
53
|
|
|
43
54
|
<template>
|
|
@@ -55,7 +66,7 @@ defineExpose({
|
|
|
55
66
|
:search="search"
|
|
56
67
|
:multi-sort="false"
|
|
57
68
|
item-value="name"
|
|
58
|
-
@update:options="
|
|
69
|
+
@update:options="doPaginate"
|
|
59
70
|
>
|
|
60
71
|
<template v-slot:top>
|
|
61
72
|
<v-toolbar density="compact">
|
|
@@ -80,12 +91,25 @@ defineExpose({
|
|
|
80
91
|
|
|
81
92
|
</v-toolbar>
|
|
82
93
|
|
|
83
|
-
<crud-export-list
|
|
94
|
+
<crud-export-list
|
|
95
|
+
:entity="entity"
|
|
96
|
+
/>
|
|
84
97
|
|
|
85
98
|
<v-card>
|
|
86
99
|
<v-card-text>
|
|
87
|
-
<crud-search
|
|
100
|
+
<crud-search
|
|
101
|
+
v-model="search"
|
|
102
|
+
/>
|
|
103
|
+
</v-card-text>
|
|
104
|
+
|
|
105
|
+
<v-card-text class="pt-0">
|
|
106
|
+
<crud-filters
|
|
107
|
+
:entity="entity"
|
|
108
|
+
v-model="filters"
|
|
109
|
+
@updateValue="doPaginate()"
|
|
110
|
+
/>
|
|
88
111
|
</v-card-text>
|
|
112
|
+
|
|
89
113
|
</v-card>
|
|
90
114
|
|
|
91
115
|
</template>
|
|
@@ -99,6 +123,11 @@ defineExpose({
|
|
|
99
123
|
|
|
100
124
|
|
|
101
125
|
<template v-slot:item.actions="{item}">
|
|
126
|
+
<crud-view-button
|
|
127
|
+
v-if="entity.isViewable && hasPermission(entity.permissions.view)"
|
|
128
|
+
@click="$emit('view', item)"
|
|
129
|
+
/>
|
|
130
|
+
|
|
102
131
|
<crud-update-button
|
|
103
132
|
v-if="entity.isEditable && hasPermission(entity.permissions.update)"
|
|
104
133
|
@click="$emit('edit', item)"
|
|
@@ -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
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {useI18n} from "vue-i18n";
|
|
3
|
+
const {t} = useI18n()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<v-tooltip location="top">
|
|
8
|
+
<template v-slot:activator="{ props}">
|
|
9
|
+
<v-btn
|
|
10
|
+
v-bind="{ ...$attrs, ...props }"
|
|
11
|
+
icon="mdi-magnify"
|
|
12
|
+
class="mr-1"
|
|
13
|
+
variant="text"
|
|
14
|
+
color="secondary"
|
|
15
|
+
slim
|
|
16
|
+
>
|
|
17
|
+
</v-btn>
|
|
18
|
+
</template>
|
|
19
|
+
{{ t('action.view')}}
|
|
20
|
+
</v-tooltip>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
|
|
25
|
+
</style>
|
|
@@ -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)
|
|
@@ -61,13 +83,6 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
61
83
|
|
|
62
84
|
|
|
63
85
|
|
|
64
|
-
|
|
65
|
-
function onCreate() {
|
|
66
|
-
store.setOperation("create")
|
|
67
|
-
store.setForm(entity.form)
|
|
68
|
-
store.setDialog(true)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
86
|
function cast(item: any){
|
|
72
87
|
entity.fields.filter(field => field.type === 'date')
|
|
73
88
|
.forEach(field => {
|
|
@@ -76,31 +91,44 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
76
91
|
|
|
77
92
|
entity.fields.filter(field => field.type === 'ref')
|
|
78
93
|
.forEach(field => {
|
|
79
|
-
item[field.name] = item[field.name]._id
|
|
94
|
+
item[field.name] = item[field.name]?._id ? item[field.name]._id : item[field.name]
|
|
80
95
|
})
|
|
81
96
|
|
|
82
97
|
entity.fields.filter(field => field.type === 'array.ref')
|
|
83
98
|
.forEach(field => {
|
|
84
|
-
item[field.name] = item[field.name].map(((i:any) => i._id))
|
|
99
|
+
item[field.name] = item[field.name].map(((i:any) => i?._id ? i._id : i))
|
|
85
100
|
})
|
|
86
101
|
|
|
87
102
|
return item
|
|
88
103
|
}
|
|
89
104
|
|
|
105
|
+
function onView(item: object) {
|
|
106
|
+
store.setOperation("view")
|
|
107
|
+
store.setForm(cast({...item}))
|
|
108
|
+
openDialog()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
function onCreate() {
|
|
113
|
+
store.setOperation("create")
|
|
114
|
+
store.setForm(entity.form)
|
|
115
|
+
openDialog()
|
|
116
|
+
}
|
|
117
|
+
|
|
90
118
|
function onEdit(item: object) {
|
|
91
119
|
store.setOperation("edit")
|
|
92
120
|
store.setForm(cast({...item}))
|
|
93
|
-
|
|
121
|
+
openDialog()
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
function onDelete(item: object) {
|
|
97
125
|
store.setOperation("delete")
|
|
98
126
|
store.setForm(cast({...item}))
|
|
99
|
-
|
|
127
|
+
openDialog()
|
|
100
128
|
}
|
|
101
129
|
|
|
102
130
|
function onCancel() {
|
|
103
|
-
|
|
131
|
+
closeDialog()
|
|
104
132
|
store.setError("")
|
|
105
133
|
store.setInputErrors(null)
|
|
106
134
|
}
|
|
@@ -108,6 +136,9 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
108
136
|
function onSubmit(formData: any) {
|
|
109
137
|
store.setInputErrors(null)
|
|
110
138
|
switch (store.operation) {
|
|
139
|
+
case "view":
|
|
140
|
+
closeDialog()
|
|
141
|
+
break
|
|
111
142
|
case "create":
|
|
112
143
|
doCreate(formData)
|
|
113
144
|
break
|
|
@@ -120,11 +151,19 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
120
151
|
}
|
|
121
152
|
}
|
|
122
153
|
|
|
154
|
+
function openDialog() {
|
|
155
|
+
store.setDialog(true)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function closeDialog() {
|
|
159
|
+
store.setDialog(false)
|
|
160
|
+
}
|
|
161
|
+
|
|
123
162
|
async function doCreate(formData: any) {
|
|
124
163
|
try {
|
|
125
164
|
await entity?.provider.create(formData)
|
|
126
165
|
await doPaginate()
|
|
127
|
-
|
|
166
|
+
closeDialog()
|
|
128
167
|
store.showMessage("Entity created successfully!")
|
|
129
168
|
} catch (e: any) {
|
|
130
169
|
if(e.inputErrors){
|
|
@@ -140,7 +179,7 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
140
179
|
try {
|
|
141
180
|
await entity?.provider.update(formData._id, formData)
|
|
142
181
|
await doPaginate()
|
|
143
|
-
|
|
182
|
+
closeDialog()
|
|
144
183
|
store.showMessage("Entity updated successfully!")
|
|
145
184
|
} catch (e: any) {
|
|
146
185
|
console.log("inputErrors", e.inputErrors)
|
|
@@ -157,7 +196,7 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
157
196
|
try {
|
|
158
197
|
await entity?.provider.delete(formData._id)
|
|
159
198
|
await doPaginate()
|
|
160
|
-
|
|
199
|
+
closeDialog()
|
|
161
200
|
store.showMessage("Entity deleted successfully!")
|
|
162
201
|
} catch (e: any) {
|
|
163
202
|
store.setError(e.message || "An error occurred while deleting the entity")
|
|
@@ -170,28 +209,16 @@ export function useCrud(entity: IEntityCrud) {
|
|
|
170
209
|
store.$reset()
|
|
171
210
|
}
|
|
172
211
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const notify = computed({get(){return store.notify} , set(value){store.setNotify(value)}})
|
|
178
|
-
const error = computed({get(){return store.error} , set(value){store.setError(value)}})
|
|
179
|
-
const message = computed({get(){return store.message} , set(value){store.setMessage(value)}})
|
|
180
|
-
const loading = computed({get(){return store.loading} , set(value){store.setLoading(value)}})
|
|
181
|
-
const itemsPerPage = computed({get(){return store.itemsPerPage} , set(value){store.setItemsPerPage(value)}})
|
|
182
|
-
const page = computed({get(){return store.page} , set(value){store.setPage(value)}})
|
|
183
|
-
const sortBy = computed({get(){return store.sortBy} , set(value){store.setSortBy(value)}})
|
|
184
|
-
const search = computed({get(){return store.search} , set(value){store.setSearch(value)}})
|
|
185
|
-
const totalItems = computed({get(){return store.totalItems} , set(value){store.setTotalItems(value)}})
|
|
186
|
-
const items = computed({get(){return store.items} , set(value){store.setItems(value)}})
|
|
187
|
-
const exportFiles = computed({get(){return store.exportFiles} , set(value){store.setExportFiles(value)}})
|
|
188
|
-
const exportLoading = computed({get(){return store.exportLoading} , set(value){store.setExportLoading(value)}})
|
|
189
|
-
const exportListVisible = computed({get(){return store.exportListVisible} , set(value){store.setExportListVisible(value)}})
|
|
212
|
+
function prepareFilters(){
|
|
213
|
+
store.setFilters(entity.formFilters)
|
|
214
|
+
}
|
|
215
|
+
|
|
190
216
|
|
|
191
217
|
return {
|
|
192
|
-
|
|
218
|
+
doPaginate, doExport, onView, onCreate, onEdit, onDelete, onCancel, onSubmit,resetCrudStore,
|
|
193
219
|
operation, dialog, form, notify, error, message, formValid,
|
|
194
220
|
loading, itemsPerPage, page, sortBy, search, totalItems, items,
|
|
221
|
+
prepareFilters,filters,
|
|
195
222
|
exportFiles,exportLoading,exportListVisible
|
|
196
223
|
}
|
|
197
224
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {IEntityCrudOperation} from "@drax/crud-share";
|
|
2
|
+
import {computed} from "vue";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function useFormUtils(operation:IEntityCrudOperation) {
|
|
6
|
+
|
|
7
|
+
const readonly = computed(() => {
|
|
8
|
+
return operation === 'delete' || operation === 'view';
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const submitColor = computed(() => {
|
|
12
|
+
if(operation === 'create') {
|
|
13
|
+
return 'primary'
|
|
14
|
+
}else if(operation === 'edit') {
|
|
15
|
+
return 'primary'
|
|
16
|
+
}else if(operation === 'delete') {
|
|
17
|
+
return 'red'
|
|
18
|
+
}else if(operation === 'view') {
|
|
19
|
+
return 'secondary'
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const variant = computed(() => {
|
|
24
|
+
if(operation === 'create') {
|
|
25
|
+
return 'filled'
|
|
26
|
+
}else if(operation === 'edit') {
|
|
27
|
+
return 'filled'
|
|
28
|
+
}else if(operation === 'delete') {
|
|
29
|
+
return 'underlined'
|
|
30
|
+
}else if(operation === 'view') {
|
|
31
|
+
return 'underlined'
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
readonly,
|
|
39
|
+
variant,
|
|
40
|
+
submitColor,
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import CrudNotify from "./components/CrudNotify.vue";
|
|
|
8
8
|
import CrudSearch from "./components/CrudSearch.vue";
|
|
9
9
|
import {useCrudStore} from "./stores/UseCrudStore";
|
|
10
10
|
import {useCrud} from "./composables/UseCrud";
|
|
11
|
+
import {useFormUtils} from "./composables/UseFormUtils";
|
|
11
12
|
import {EntityCrud} from "./EntityCrud";
|
|
12
13
|
|
|
13
14
|
|
|
@@ -21,6 +22,7 @@ export {
|
|
|
21
22
|
CrudNotify,
|
|
22
23
|
CrudSearch,
|
|
23
24
|
useCrud,
|
|
25
|
+
useFormUtils,
|
|
24
26
|
useCrudStore,
|
|
25
27
|
EntityCrud
|
|
26
28
|
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {defineStore} from "pinia";
|
|
2
|
-
import type {
|
|
2
|
+
import type {IEntityCrudOperation} from "@drax/crud-share";
|
|
3
3
|
|
|
4
4
|
export const useCrudStore = defineStore('CrudStore', {
|
|
5
5
|
state: () => (
|
|
6
6
|
{
|
|
7
|
-
operation: null as
|
|
7
|
+
operation: null as IEntityCrudOperation,
|
|
8
8
|
dialog: false as boolean,
|
|
9
9
|
form: {} as any,
|
|
10
10
|
formValid: {} as any,
|
|
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,
|
|
@@ -35,7 +36,7 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
35
36
|
}
|
|
36
37
|
},
|
|
37
38
|
actions: {
|
|
38
|
-
setOperation(operation:
|
|
39
|
+
setOperation(operation: IEntityCrudOperation) {
|
|
39
40
|
this.operation = operation
|
|
40
41
|
},
|
|
41
42
|
setDialog(dialog: boolean) {
|
|
@@ -99,6 +100,9 @@ export const useCrudStore = defineStore('CrudStore', {
|
|
|
99
100
|
},
|
|
100
101
|
setExportListVisible(exportListVisible: boolean) {
|
|
101
102
|
this.exportListVisible = exportListVisible
|
|
103
|
+
},
|
|
104
|
+
setFilters(filters: any[]) {
|
|
105
|
+
this.filters = filters
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
|