mirror of
https://github.com/directus/directus.git
synced 2026-02-03 13:54:59 -05:00
Merge pull request #296 from directus/relational-setup
Relational setup tweaks
This commit is contained in:
@@ -73,8 +73,10 @@ export default class CollectionsService {
|
||||
}
|
||||
});
|
||||
|
||||
const collectionInfo = omit(payload, 'fields');
|
||||
await collectionItemsService.create(collectionInfo);
|
||||
await collectionItemsService.create({
|
||||
...(payload.meta || {}),
|
||||
collection: payload.collection,
|
||||
});
|
||||
|
||||
const fieldPayloads = payload
|
||||
.fields!.filter((field) => field.meta)
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function login(credentials: LoginCredentials) {
|
||||
await hydrate();
|
||||
}
|
||||
|
||||
let refreshTimeout: number;
|
||||
let refreshTimeout: any;
|
||||
|
||||
export async function refresh({ navigate }: LogoutOptions = { navigate: true }) {
|
||||
const appStore = useAppStore();
|
||||
|
||||
@@ -538,6 +538,10 @@
|
||||
"inline_title": "Inline Title",
|
||||
"auto_format_casing": "Auto-format casing",
|
||||
|
||||
"auto_fill": "Auto Fill",
|
||||
|
||||
"corresponding_field": "Corresponding Field",
|
||||
|
||||
"errors": {
|
||||
"COLLECTION_NOT_FOUND": "Collection doesn't exist.",
|
||||
"FIELD_NOT_FOUND": "Field not found.",
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('junction_collection') }}</div>
|
||||
<v-input :class="{ matches: junctionCollectionExists }" v-model="junctionCollection" :placeholder="$t('collection') + '...'" :disabled="isExisting" db-safe>
|
||||
<v-input :class="{ matches: junctionCollectionExists }" v-model="junctionCollection" :placeholder="$t('collection') + '...'" :disabled="autoFill || isExisting" db-safe>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="autoFill || isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -20,6 +20,7 @@
|
||||
v-for="item in collectionItems"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_collection === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].many_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -33,11 +34,11 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-input :class="{ matches: relatedCollectionExists }" v-model="relations[1].one_collection" :placeholder="$t('collection') + '...'" :disabled="type === 'files' || isExisting" db-safe>
|
||||
<v-input :autofocus="autoFill" :class="{ matches: relatedCollectionExists }" v-model="relations[1].one_collection" :placeholder="$t('collection') + '...'" :disabled="type === 'files' || isExisting" db-safe>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="type === 'files' || isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -45,6 +46,7 @@
|
||||
v-for="item in collectionItems"
|
||||
:key="item.value"
|
||||
:active="relations[1].one_collection === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[1].one_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -57,11 +59,11 @@
|
||||
</v-input>
|
||||
</div>
|
||||
<v-input disabled :value="relations[0].one_primary" />
|
||||
<v-input v-model="relations[0].many_field" :placeholder="$t('foreign_key') + '...'" :disabled="isExisting" db-safe>
|
||||
<v-input :class="{ matches: junctionFieldExists(relations[0].many_field)}" v-model="relations[0].many_field" :placeholder="$t('foreign_key') + '...'" :disabled="autoFill || isExisting" db-safe>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="autoFill || isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -69,6 +71,7 @@
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_field === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -81,11 +84,11 @@
|
||||
</v-input>
|
||||
<div class="spacer" />
|
||||
<div class="spacer" />
|
||||
<v-input v-model="relations[1].many_field" :placeholder="$t('foreign_key') + '...'" :disabled="isExisting" db-safe>
|
||||
<v-input :class="{ matches: junctionFieldExists(relations[1].many_field)}" v-model="relations[1].many_field" :placeholder="$t('foreign_key') + '...'" :disabled="autoFill || isExisting" db-safe>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="autoFill || isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -93,6 +96,7 @@
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[1].many_field === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[1].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -104,17 +108,34 @@
|
||||
</template>
|
||||
</v-input>
|
||||
<v-input db-safe :disabled="relatedCollectionExists" v-model="relations[1].one_primary" :placeholder="$t('primary_key') + '...'" />
|
||||
<div class="spacer" />
|
||||
<v-checkbox block v-model="autoFill" :label="$t('auto_fill')" />
|
||||
<v-icon class="arrow" name="arrow_forward" />
|
||||
<v-icon class="arrow" name="arrow_backward" />
|
||||
</div>
|
||||
|
||||
<v-divider large :inline-title="false" v-if="!isExisting">{{ $t('corresponding_field') }}</v-divider>
|
||||
|
||||
<div class="corresponding" v-if="!isExisting">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('create_field') }}</div>
|
||||
<v-checkbox block :label="correspondingLabel" v-model="hasCorresponding" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('field_name') }}</div>
|
||||
<v-input :disabled="hasCorresponding === false" v-model="correspondingField" :placeholder="$t('field_name') + '...'" db-safe />
|
||||
</div>
|
||||
<v-icon name="arrow_forward" class="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import { defineComponent, computed, ref } from '@vue/composition-api';
|
||||
import { orderBy } from 'lodash';
|
||||
import { useCollectionsStore, useFieldsStore } from '@/stores/';
|
||||
import { Field } from '@/types';
|
||||
import i18n from '@/lang';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
@@ -137,13 +158,19 @@ export default defineComponent({
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const autoFill = computed({
|
||||
get() {
|
||||
return state.autoFillJunctionRelation;
|
||||
},
|
||||
set(newAuto: boolean) {
|
||||
state.autoFillJunctionRelation = newAuto;
|
||||
}
|
||||
})
|
||||
|
||||
const availableCollections = computed(() => {
|
||||
return orderBy(
|
||||
collectionsStore.state.collections.filter((collection) => {
|
||||
return (
|
||||
collection.collection.startsWith('directus_') === false &&
|
||||
collection.collection !== props.collection
|
||||
);
|
||||
return (collection.collection.startsWith('directus_') === false);
|
||||
}),
|
||||
['collection'],
|
||||
['asc']
|
||||
@@ -188,7 +215,72 @@ export default defineComponent({
|
||||
}));
|
||||
});
|
||||
|
||||
return { relations: state.relations, collectionItems, junctionCollection, junctionFields, junctionCollectionExists, relatedCollectionExists };
|
||||
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
|
||||
|
||||
return { relations: state.relations, autoFill, collectionItems, junctionCollection, junctionFields, junctionCollectionExists, relatedCollectionExists, junctionFieldExists, hasCorresponding, correspondingField, correspondingLabel };
|
||||
|
||||
function junctionFieldExists(fieldKey: string) {
|
||||
if (!junctionCollection.value) return false;
|
||||
return !!fieldsStore.getField(junctionCollection.value, fieldKey);
|
||||
}
|
||||
|
||||
function useCorresponding() {
|
||||
const hasCorresponding = computed({
|
||||
get() {
|
||||
return !!state.newFields.find((field: any) => field.$type === 'corresponding');
|
||||
},
|
||||
set(enabled: boolean) {
|
||||
if (enabled === true) {
|
||||
state.newFields = [
|
||||
{
|
||||
$type: 'corresponding',
|
||||
field: state.relations[0].one_collection,
|
||||
collection: state.relations[1].one_collection,
|
||||
meta: {
|
||||
special: 'm2m',
|
||||
interface: 'many-to-many',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
state.relations[1].one_field = state.relations[0].one_collection;
|
||||
} else {
|
||||
state.newFields = state.newFields.filter((field: any) => field.$type !== 'corresponding');
|
||||
state.relations[1].one_field = null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingField = computed({
|
||||
get() {
|
||||
return state.newFields?.find((field: any) => field.$type === 'corresponding')?.field || null;
|
||||
},
|
||||
set(field: string | null) {
|
||||
state.newFields = state.newFields.map((newField: any) => {
|
||||
if (newField.$type === 'corresponding') {
|
||||
return {
|
||||
...newField,
|
||||
field: field
|
||||
}
|
||||
}
|
||||
|
||||
return newField;
|
||||
})
|
||||
|
||||
state.relations[1].one_field = field;
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingLabel = computed(() => {
|
||||
if (state.relations[0].one_collection) {
|
||||
return i18n.t('add_m2m_to_collection', { collection: state.relations[1].one_collection });
|
||||
}
|
||||
|
||||
return i18n.t('add_field_related');
|
||||
});
|
||||
|
||||
return { hasCorresponding, correspondingField, correspondingLabel };
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -216,13 +308,13 @@ export default defineComponent({
|
||||
pointer-events: none;
|
||||
|
||||
&:first-of-type {
|
||||
bottom: 78px;
|
||||
left: 32.7%;
|
||||
bottom: 141px;
|
||||
left: 32.5%;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
bottom: 14px;
|
||||
left: 67.5%;
|
||||
bottom: 76px;
|
||||
left: 67.4%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,4 +322,29 @@ export default defineComponent({
|
||||
.type-label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
margin: 48px 0;
|
||||
}
|
||||
|
||||
.v-list {
|
||||
--v-list-item-content-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.corresponding {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px 32px;
|
||||
margin-top: 48px;
|
||||
|
||||
.arrow {
|
||||
--v-icon-color: var(--primary);
|
||||
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-input :class="{ matches: isNewCollection === false }" db-safe key="related-collection" v-model="relations[0].one_collection" :disabled="isExisting" :placeholder="$t('collection') + '...'">
|
||||
<v-input :class="{ matches: relatedCollectionExists }" 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="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -20,6 +20,7 @@
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:active="relations[0].one_collection === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].one_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -32,19 +33,19 @@
|
||||
</v-input>
|
||||
</div>
|
||||
<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-input db-safe :disabled="relatedCollectionExists" v-model="relations[0].one_primary" :placeholder="$t('primary_key') + '...'" />
|
||||
<v-icon class="arrow" name="arrow_back" />
|
||||
</div>
|
||||
|
||||
<v-divider v-if="!isExisting" />
|
||||
<v-divider large :inline-title="false" v-if="!isExisting">{{ $t('corresponding_field') }}</v-divider>
|
||||
|
||||
<div class="grid" v-if="!isExisting">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('create_corresponding_field') }}</div>
|
||||
<div class="type-label">{{ $t('create_field') }}</div>
|
||||
<v-checkbox block :label="correspondingLabel" v-model="hasCorresponding" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('corresponding_field_name') }}</div>
|
||||
<div class="type-label">{{ $t('field_name') }}</div>
|
||||
<v-input :disabled="hasCorresponding === false" v-model="correspondingField" :placeholder="$t('field_name') + '...'" db-safe />
|
||||
</div>
|
||||
<v-icon name="arrow_forward" class="arrow" />
|
||||
@@ -85,8 +86,8 @@ export default defineComponent({
|
||||
const { items } = useRelation();
|
||||
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
|
||||
|
||||
const isNewCollection = computed(() => {
|
||||
return collectionsStore.getCollection(state.relations[0].one_collection) === null;
|
||||
const relatedCollectionExists = computed(() => {
|
||||
return !!collectionsStore.getCollection(state.relations[0].one_collection);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -96,7 +97,7 @@ export default defineComponent({
|
||||
correspondingField,
|
||||
correspondingLabel,
|
||||
fieldData: state.fieldData,
|
||||
isNewCollection,
|
||||
relatedCollectionExists,
|
||||
};
|
||||
|
||||
function useRelation() {
|
||||
@@ -127,40 +128,38 @@ export default defineComponent({
|
||||
},
|
||||
set(enabled: boolean) {
|
||||
if (enabled === true) {
|
||||
state.newFields = [
|
||||
{
|
||||
field: state.relations[0].one_collection,
|
||||
collection: state.relations[0].one_collection,
|
||||
meta: {
|
||||
special: 'o2m',
|
||||
interface: 'one-to-many',
|
||||
},
|
||||
state.newFields.push({
|
||||
$type: 'corresponding',
|
||||
field: state.relations[0].one_collection,
|
||||
collection: state.relations[0].one_collection,
|
||||
meta: {
|
||||
special: 'o2m',
|
||||
interface: 'one-to-many',
|
||||
},
|
||||
];
|
||||
});
|
||||
} else {
|
||||
state.newFields = [];
|
||||
state.newFields = state.newFields.filter((field: any) => field.$type !== 'corresponding');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingField = computed({
|
||||
get() {
|
||||
return state.newFields?.[0]?.field || null;
|
||||
return state.newFields?.find((field: any) => field.$type === 'corresponding')?.field || null;
|
||||
},
|
||||
set(field: string | null) {
|
||||
state.newFields = [
|
||||
{
|
||||
...(state.newFields[0] || {}),
|
||||
field: field || '',
|
||||
},
|
||||
];
|
||||
state.newFields = state.newFields.map((newField: any) => {
|
||||
if (newField.$type === 'corresponding') {
|
||||
return {
|
||||
...newField,
|
||||
field
|
||||
}
|
||||
}
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
...state.relations[0],
|
||||
one_field: field,
|
||||
},
|
||||
];
|
||||
return newField;
|
||||
});
|
||||
|
||||
state.relations[0].one_field = field;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
:placeholder="$t('collection') + '...'"
|
||||
v-model="relations[0].many_collection"
|
||||
:disabled="isExisting"
|
||||
:class="{ matches: isExisting }"
|
||||
:class="{ matches: relatedCollectionExists }"
|
||||
>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" :disabled="isExisting" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
@@ -26,6 +26,7 @@
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_collection === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].many_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
@@ -43,6 +44,7 @@
|
||||
v-model="relations[0].many_field"
|
||||
:disabled="isExisting"
|
||||
:placeholder="$t('foreign_key') + '...'"
|
||||
:class="{ matches: relatedFieldExists }"
|
||||
>
|
||||
<template #append v-if="fields && fields.length > 0">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
@@ -68,6 +70,20 @@
|
||||
</v-input>
|
||||
<v-icon class="arrow" name="arrow_forward" />
|
||||
</div>
|
||||
|
||||
<v-divider large :inline-title="false" v-if="!isExisting">{{ $t('corresponding_field') }}</v-divider>
|
||||
|
||||
<div class="corresponding" v-if="!isExisting">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('create_field') }}</div>
|
||||
<v-checkbox block :label="correspondingLabel" v-model="hasCorresponding" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('field_name') }}</div>
|
||||
<v-input disabled v-model="relations[0].many_field" :placeholder="$t('field_name') + '...'" db-safe />
|
||||
</div>
|
||||
<v-icon name="arrow_forward" class="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -101,8 +117,18 @@ export default defineComponent({
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const { items, fields, currentCollectionPrimaryKey, collectionMany } = useRelation();
|
||||
const { hasCorresponding, correspondingLabel } = useCorresponding();
|
||||
|
||||
return { relations: state.relations, items, fields, currentCollectionPrimaryKey, collectionMany };
|
||||
const relatedCollectionExists = computed(() => {
|
||||
return collectionsStore.state.collections.find((col) => col.collection === state.relations?.[0].many_collection);
|
||||
});
|
||||
|
||||
const relatedFieldExists = computed(() => {
|
||||
if (!state?.relations?.[0].many_collection || !state?.relations?.[0].many_field) return false;
|
||||
return !!fieldsStore.getField(state.relations[0].many_collection, state.relations[0].many_field);
|
||||
});
|
||||
|
||||
return { relations: state.relations, items, fields, currentCollectionPrimaryKey, collectionMany, hasCorresponding, correspondingLabel, relatedCollectionExists, relatedFieldExists };
|
||||
|
||||
function useRelation() {
|
||||
const availableCollections = computed(() => {
|
||||
@@ -156,6 +182,74 @@ export default defineComponent({
|
||||
|
||||
return { availableCollections, items, fields, currentCollectionPrimaryKey, collectionMany };
|
||||
}
|
||||
|
||||
function useCorresponding() {
|
||||
const hasCorresponding = computed({
|
||||
get() {
|
||||
if (!state?.relations?.[0]?.many_collection || !state?.relations?.[0]?.many_field) return false;
|
||||
|
||||
if (relatedFieldExists.value === true) {
|
||||
return state.updateFields.find((updateField: any) => updateField.field === state.relations[0].many_field)?.meta?.interface === 'many-to-one' || fieldsStore.getField(state.relations[0].many_collection, state.relations[0].many_field)?.meta?.interface === 'many-to-one';
|
||||
} else {
|
||||
return state.newFields.find((newField: any) => newField.$type === 'manyRelated')?.meta?.interface === 'many-to-one';
|
||||
}
|
||||
},
|
||||
set(enabled: boolean) {
|
||||
if (!state?.relations?.[0]?.many_field) return;
|
||||
|
||||
if (relatedFieldExists.value === true) {
|
||||
if (enabled === true) {
|
||||
state.updateFields = [
|
||||
{
|
||||
collection: state.relations[0].one_collection,
|
||||
field: state.relations[0].many_field,
|
||||
meta: {
|
||||
interface: 'many-to-one',
|
||||
special: 'm2o',
|
||||
}
|
||||
}
|
||||
]
|
||||
} else {
|
||||
state.updateFields = [
|
||||
{
|
||||
collection: state.relations[0].one_collection,
|
||||
field: state.relations[0].many_field,
|
||||
meta: {
|
||||
interface: null,
|
||||
special: null,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
state.newFields = state.newFields.map((newField: any) => {
|
||||
if (newField.$type === 'manyRelated') {
|
||||
if (!newField.meta) newField.meta = {};
|
||||
if (enabled === true) {
|
||||
newField.meta.interface = 'many-to-one';
|
||||
newField.meta.special = 'many-to-one';
|
||||
} else {
|
||||
newField.meta.interface = null;
|
||||
newField.meta.special = null;
|
||||
}
|
||||
}
|
||||
|
||||
return newField;
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingLabel = computed(() => {
|
||||
if (state.relations[0].many_collection) {
|
||||
return i18n.t('add_m2o_to_collection', { collection: state.relations[0].many_collection });
|
||||
}
|
||||
|
||||
return i18n.t('add_field_related');
|
||||
});
|
||||
|
||||
return { hasCorresponding, correspondingLabel };
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -192,4 +286,25 @@ export default defineComponent({
|
||||
.type-label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
margin: 48px 0;
|
||||
}
|
||||
|
||||
.corresponding {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px 32px;
|
||||
margin-top: 48px;
|
||||
|
||||
.arrow {
|
||||
--v-icon-color: var(--primary);
|
||||
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -220,8 +220,6 @@ export default defineComponent({
|
||||
async function saveField() {
|
||||
saving.value = true;
|
||||
|
||||
console.log(state);
|
||||
|
||||
try {
|
||||
if (props.field !== '+') {
|
||||
await api.patch(`/fields/${props.collection}/${props.field}`, state.fieldData);
|
||||
@@ -243,6 +241,13 @@ export default defineComponent({
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
state.updateFields.map((updateField: Partial<Field> & { $type: string }) => {
|
||||
delete updateField.$type;
|
||||
return api.post(`/fields/${updateField.collection}/${updateField.field}`, updateField);
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
state.relations.map((relation: Partial<Relation>) => {
|
||||
if (relation.id) {
|
||||
@@ -300,6 +305,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
if (relations.length === 2) {
|
||||
const relationForCurrent = relations.find((relation: Relation) => (relation.many_collection === collection && relation.many_field === field) || (relation.one_collection === collection && relation.one_field === field));
|
||||
|
||||
if (relationForCurrent?.many_collection === collection && relationForCurrent?.many_field === field) return 'm2o';
|
||||
|
||||
if (
|
||||
relations[0].one_collection === 'directus_files' ||
|
||||
relations[1].one_collection === 'directus_files'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores/';
|
||||
import { reactive, watch, computed, ComputedRef } from '@vue/composition-api';
|
||||
import { reactive, watch, computed, ComputedRef, WatchStopHandle } from '@vue/composition-api';
|
||||
import { clone, throttle } from 'lodash';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import { getDisplays } from '@/displays';
|
||||
@@ -55,6 +55,9 @@ function initLocalStore(
|
||||
relations: [],
|
||||
newCollections: [],
|
||||
newFields: [],
|
||||
updateFields: [],
|
||||
|
||||
autoFillJunctionRelation: true,
|
||||
});
|
||||
|
||||
availableInterfaces = computed<InterfaceConfig[]>(() => {
|
||||
@@ -245,6 +248,8 @@ function initLocalStore(
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
state.relations[0].many_primary = 'id';
|
||||
}
|
||||
|
||||
if (collectionExists(collectionName)) {
|
||||
@@ -253,6 +258,7 @@ function initLocalStore(
|
||||
} else {
|
||||
state.newFields = [
|
||||
{
|
||||
$type: 'manyRelated',
|
||||
collection: collectionName,
|
||||
field: fieldName,
|
||||
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.type,
|
||||
@@ -263,6 +269,7 @@ function initLocalStore(
|
||||
} else {
|
||||
state.newFields = [
|
||||
{
|
||||
$type: 'manyRelated',
|
||||
collection: collectionName,
|
||||
field: fieldName,
|
||||
type: 'integer',
|
||||
@@ -270,6 +277,8 @@ function initLocalStore(
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
console.log(state.newFields);
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
@@ -324,6 +333,10 @@ function initLocalStore(
|
||||
state.newCollections.push({
|
||||
$type: 'junction',
|
||||
collection: junctionCollection,
|
||||
meta: {
|
||||
hidden: true,
|
||||
icon: 'import_export',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
field: 'id',
|
||||
@@ -337,6 +350,9 @@ function initLocalStore(
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
state.relations[0].many_primary = 'id';
|
||||
state.relations[1].many_primary = 'id';
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, manyCurrent) === false) {
|
||||
@@ -383,8 +399,6 @@ function initLocalStore(
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
console.log(state.newCollections, state.newFields);
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
@@ -414,6 +428,19 @@ function initLocalStore(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].one_field = state.fieldData.field;
|
||||
|
||||
if (collectionExists(state.fieldData.field)) {
|
||||
state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`;
|
||||
state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`;
|
||||
state.relations[1].one_collection = state.fieldData.field;
|
||||
state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field;
|
||||
state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`;
|
||||
state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`;
|
||||
|
||||
if (state.relations[0].many_field === state.relations[1].many_field) {
|
||||
state.relations[1].many_field = `${state.relations[1].one_collection}_related_${state.relations[1].one_primary}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -462,6 +489,30 @@ function initLocalStore(
|
||||
],
|
||||
syncNewCollectionsM2M
|
||||
)
|
||||
|
||||
let stop: WatchStopHandle;
|
||||
|
||||
watch(() => state.autoFillJunctionRelation, (startWatching) => {
|
||||
if (startWatching) {
|
||||
stop = watch([() => state.relations[1].one_collection, () => state.relations[1].one_primary], ([newRelatedCollection, newRelatedPrimary]: string[]) => {
|
||||
if (newRelatedCollection) {
|
||||
state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`;
|
||||
state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`;
|
||||
state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`;
|
||||
}
|
||||
|
||||
if (newRelatedPrimary) {
|
||||
state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`;
|
||||
}
|
||||
|
||||
if (state.relations[0].many_field === state.relations[1].many_field) {
|
||||
state.relations[1].many_field = `${state.relations[1].one_collection}_related_${state.relations[1].one_primary}`;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
stop?.();
|
||||
}
|
||||
}, { immediate: true });
|
||||
}
|
||||
|
||||
if (type === 'presentation') {
|
||||
|
||||
@@ -204,7 +204,7 @@ export default defineComponent({
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
router.push('/settings/data-model');
|
||||
router.push(`/settings/data-model/${collectionName.value}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
saveError.value = error;
|
||||
|
||||
Reference in New Issue
Block a user