mirror of
https://github.com/directus/directus.git
synced 2026-02-03 08:54:59 -05:00
Allow dynamic m2o registration
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
v-model="junctionCollection"
|
||||
:placeholder="$t('select_one')"
|
||||
:disabled="isExisting"
|
||||
allow-other
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
@@ -111,7 +112,7 @@ export default defineComponent({
|
||||
value: field.field,
|
||||
disabled:
|
||||
state.relations[0].many_field === field.field ||
|
||||
field.schema?.is_primary_key ||
|
||||
field.schema?.is_primary_key ||
|
||||
state.relations[1].many_field === field.field,
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -8,16 +8,32 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:placeholder="$t('select_one')"
|
||||
:items="items"
|
||||
v-model="relations[0].one_collection"
|
||||
:disabled="isExisting"
|
||||
/>
|
||||
<v-input db-safe key="related-collection" v-model="relations[0].one_collection" :disabled="isExisting" :placeholder="$t('collection')">
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:active="relations[0].one_collection === item.value"
|
||||
@click="relations[0].one_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<v-input disabled :value="fieldData.field" />
|
||||
<v-input disabled :value="relatedPrimary" />
|
||||
<v-icon name="arrow_back" />
|
||||
<v-input disabled :value="relations[0].many_field" />
|
||||
<v-input db-safe :disabled="isNewCollection === false" v-model="relations[0].one_primary" :placeholder="$t('primary_key')" />
|
||||
<v-icon class="arrow" name="arrow_back" />
|
||||
</div>
|
||||
|
||||
<v-divider v-if="!isExisting" />
|
||||
@@ -31,7 +47,7 @@
|
||||
<div class="type-label">{{ $t('corresponding_field_name') }}</div>
|
||||
<v-input :disabled="hasCorresponding === false" v-model="correspondingField" db-safe />
|
||||
</div>
|
||||
<v-icon name="arrow_forward" />
|
||||
<v-icon name="arrow_forward" class="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -66,17 +82,21 @@ export default defineComponent({
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const { items, relatedPrimary } = useRelation();
|
||||
const { items } = useRelation();
|
||||
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
|
||||
|
||||
const isNewCollection = computed(() => {
|
||||
return collectionsStore.getCollection(state.relations[0].one_collection) === null;
|
||||
});
|
||||
|
||||
return {
|
||||
relations: state.relations,
|
||||
items,
|
||||
relatedPrimary,
|
||||
hasCorresponding,
|
||||
correspondingField,
|
||||
correspondingLabel,
|
||||
fieldData: state.fieldData,
|
||||
isNewCollection,
|
||||
};
|
||||
|
||||
function useRelation() {
|
||||
@@ -97,13 +117,7 @@ export default defineComponent({
|
||||
}))
|
||||
);
|
||||
|
||||
const relatedPrimary = computed(() => {
|
||||
return state.relations[0].one_collection
|
||||
? fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection)?.field
|
||||
: null;
|
||||
});
|
||||
|
||||
return { items, relatedPrimary };
|
||||
return { items };
|
||||
}
|
||||
|
||||
function useCorresponding() {
|
||||
@@ -175,7 +189,7 @@ export default defineComponent({
|
||||
gap: 20px 32px;
|
||||
margin-top: 48px;
|
||||
|
||||
.v-icon {
|
||||
.arrow {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
|
||||
position: absolute;
|
||||
@@ -185,6 +199,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.v-list {
|
||||
--v-list-item-content-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
margin: 48px 0;
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ import SetupDisplay from './components/display.vue';
|
||||
import { i18n } from '@/lang';
|
||||
import { isEmpty } from 'lodash';
|
||||
import api from '@/api';
|
||||
import { Relation } from '@/types';
|
||||
import { useFieldsStore, useRelationsStore } from '@/stores/';
|
||||
import { Relation, Collection } from '@/types';
|
||||
import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores/';
|
||||
import { Field } from '@/types';
|
||||
import router from '@/router';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
@@ -99,6 +99,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
@@ -190,7 +191,8 @@ export default defineComponent({
|
||||
state.relations.length === 0 ||
|
||||
isEmpty(state.relations[0].many_collection) ||
|
||||
isEmpty(state.relations[0].many_field) ||
|
||||
isEmpty(state.relations[0].one_collection)
|
||||
isEmpty(state.relations[0].one_collection) ||
|
||||
isEmpty(state.relations[0].one_primary)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,6 +226,12 @@ export default defineComponent({
|
||||
await api.post(`/fields/${props.collection}`, state.fieldData);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
state.newCollections.map((newCollection: Partial<Collection>) => {
|
||||
return api.post(`/collections`, newCollection);
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
state.newFields.map((newField: Partial<Field>) => {
|
||||
return api.post(`/fields/${newField.collection}`, newField);
|
||||
@@ -240,6 +248,7 @@ export default defineComponent({
|
||||
})
|
||||
);
|
||||
|
||||
await collectionsStore.hydrate();
|
||||
await fieldsStore.hydrate();
|
||||
await relationsStore.hydrate();
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* It's reset every time the modal opens and shouldn't be used outside of the field-detail flow.
|
||||
*/
|
||||
|
||||
import { useFieldsStore, useRelationsStore } from '@/stores/';
|
||||
import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores/';
|
||||
import { reactive, watch, computed, ComputedRef } from '@vue/composition-api';
|
||||
import { clone } from 'lodash';
|
||||
import { clone, throttle } from 'lodash';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import { getDisplays } from '@/displays';
|
||||
import { InterfaceConfig } from '@/interfaces/types';
|
||||
@@ -27,6 +27,7 @@ function initLocalStore(
|
||||
field: string,
|
||||
type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation'
|
||||
) {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const interfaces = getInterfaces();
|
||||
const displays = getDisplays();
|
||||
|
||||
@@ -51,6 +52,7 @@ function initLocalStore(
|
||||
},
|
||||
},
|
||||
relations: [],
|
||||
newCollections: [],
|
||||
newFields: [],
|
||||
});
|
||||
|
||||
@@ -141,7 +143,33 @@ function initLocalStore(
|
||||
}
|
||||
|
||||
if (type === 'm2o') {
|
||||
if (!isExisting) {
|
||||
const syncNewCollectionsM2O = throttle(() => {
|
||||
const collectionName = state.relations[0].one_collection;
|
||||
|
||||
if (collectionExists(collectionName)) {
|
||||
state.newCollections = [];
|
||||
} else {
|
||||
state.newCollections = [
|
||||
{
|
||||
collection: collectionName,
|
||||
fields: [
|
||||
{
|
||||
field: state.relations[0].one_primary,
|
||||
type: 'integer',
|
||||
schema: {
|
||||
has_auto_increment: true,
|
||||
},
|
||||
system: {
|
||||
interface: 'text-input',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}, 50);
|
||||
|
||||
if (isExisting === false) {
|
||||
state.relations = [
|
||||
{
|
||||
many_collection: collection,
|
||||
@@ -165,9 +193,13 @@ function initLocalStore(
|
||||
watch(
|
||||
() => state.relations[0].one_collection,
|
||||
() => {
|
||||
const field = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection);
|
||||
state.fieldData.type = field.type;
|
||||
state.relations[0].one_primary = field.field;
|
||||
if (collectionExists(state.relations[0].one_collection)) {
|
||||
const field = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection);
|
||||
state.fieldData.type = field.type;
|
||||
state.relations[0].one_primary = field.field;
|
||||
} else {
|
||||
state.fieldData.type = 'integer';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -180,6 +212,8 @@ function initLocalStore(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch([() => state.relations[0].one_collection, () => state.relations[0].one_primary], syncNewCollectionsM2O);
|
||||
}
|
||||
|
||||
if (type === 'o2m') {
|
||||
@@ -322,6 +356,10 @@ function initLocalStore(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function collectionExists(collection: string) {
|
||||
return collectionsStore.getCollection(collection) !== null;
|
||||
}
|
||||
}
|
||||
|
||||
function clearLocalStore() {
|
||||
|
||||
Reference in New Issue
Block a user