feat: generate reverse field for linkTo fields

This commit is contained in:
chenos 2021-09-03 14:08:15 +08:00
parent a771aade5c
commit 8bd5a83947
10 changed files with 126 additions and 38 deletions

View File

@ -0,0 +1,11 @@
import Database from '@nocobase/database';
import api from '../app';
(async () => {
await api.loadPlugins();
const database: Database = api.database;
await database.sync({
// tables: ['collections', 'fields', 'actions', 'views', 'tabs'],
});
await database.close();
})();

View File

@ -10,6 +10,12 @@ const [CollectionsProvider, useCollectionsContext] = constate(() => {
findCollection(name) { findCollection(name) {
return result?.data?.find((item) => item.name === name); return result?.data?.find((item) => item.name === name);
}, },
async loadCollections(field: any) {
return result?.data?.map((item: any) => ({
label: item.title,
value: item.name,
}));
},
getFieldsByCollection(collectionName) { getFieldsByCollection(collectionName) {
const collection = result?.data?.find((item) => item.name === collectionName); const collection = result?.data?.find((item) => item.name === collectionName);
return collection?.generalFields; return collection?.generalFields;

View File

@ -1370,6 +1370,7 @@ AddNew.FormItem = observer((props: any) => {
const { schema, insertBefore, insertAfter, appendChild, deepRemove } = const { schema, insertBefore, insertAfter, appendChild, deepRemove } =
useDesignable(); useDesignable();
const path = useSchemaPath(); const path = useSchemaPath();
const { loadCollections } = useCollectionsContext();
const { collection, fields, refresh } = useCollectionContext(); const { collection, fields, refresh } = useCollectionContext();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const displayed = useDisplayedMapContext(); const displayed = useDisplayedMapContext();
@ -1562,7 +1563,7 @@ AddNew.FormItem = observer((props: any) => {
const values = await FormDialog(`添加字段`, () => { const values = await FormDialog(`添加字段`, () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<SchemaField schema={item} /> <SchemaField scope={{ loadCollections }} schema={item} />
</FormLayout> </FormLayout>
); );
}).open({ }).open({

View File

@ -405,6 +405,7 @@ const useTableColumns = () => {
function AddColumn() { function AddColumn() {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { appendChild, remove } = useDesignable(); const { appendChild, remove } = useDesignable();
const { loadCollections } = useCollectionsContext();
const { collection, fields, refresh } = useCollectionContext(); const { collection, fields, refresh } = useCollectionContext();
const displayed = useDisplayedMapContext(); const displayed = useDisplayedMapContext();
const { service } = useTable(); const { service } = useTable();
@ -592,7 +593,7 @@ function AddColumn() {
const values = await FormDialog(`添加字段`, () => { const values = await FormDialog(`添加字段`, () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<SchemaField schema={item} /> <SchemaField scope={{ loadCollections }} schema={item} />
</FormLayout> </FormLayout>
); );
}).open({ }).open({

View File

@ -18,40 +18,40 @@ export default async function() {
addAll(database.getModel(table.options.name)); addAll(database.getModel(table.options.name));
}); });
const Collection = database.getModel('collections'); // const Collection = database.getModel('collections');
Collection.addHook('afterCreate', async (model, options) => { // Collection.addHook('afterCreate', async (model, options) => {
if (!model.get('logging')) { // if (!model.get('logging')) {
return; // return;
} // }
const { transaction = await model.sequelize.transaction() } = options; // const { transaction = await model.sequelize.transaction() } = options;
const exists = await model.countFields({ // const exists = await model.countFields({
where: { // where: {
dataType: { [Op.iLike]: 'hasMany' }, // dataType: { [Op.iLike]: 'hasMany' },
name: 'action_logs' // name: 'action_logs'
}, // },
transaction // transaction
}); // });
if (!exists) { // if (!exists) {
await model.createSystemField({ // await model.createSystemField({
interface: 'linkTo', // interface: 'linkTo',
dataType: 'hasMany', // dataType: 'hasMany',
name: 'action_logs', // name: 'action_logs',
target: 'action_logs', // target: 'action_logs',
title: '数据动态', // title: '数据动态',
foreignKey: 'index', // foreignKey: 'index',
state: 0, // state: 0,
scope: { // scope: {
collection_name: model.get('name') // collection_name: model.get('name')
}, // },
constraints: false // constraints: false
}, { transaction }); // }, { transaction });
} // }
if (!options.transaction) { // if (!options.transaction) {
await transaction.commit(); // await transaction.commit();
} // }
}); // });
} }

View File

@ -6,6 +6,7 @@ import { cloneDeep, omit } from 'lodash';
export const create = async (ctx: actions.Context, next: actions.Next) => { export const create = async (ctx: actions.Context, next: actions.Next) => {
await actions.common.create(ctx, async () => {}); await actions.common.create(ctx, async () => {});
const { associated } = ctx.action.params; const { associated } = ctx.action.params;
await ctx.body.generateReverseField();
await associated.migrate(); await associated.migrate();
// console.log('associated.migrate'); // console.log('associated.migrate');
await next(); await next();

View File

@ -38,6 +38,17 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
}) })
} }
await collection.updateAssociations(values); await collection.updateAssociations(values);
const fields = await collection.getGeneralFields({
where: {
interface: 'linkTo',
}
});
for (const field of fields) {
await field.generateReverseField();
}
await collection.migrate(); await collection.migrate();
} catch (error) { } catch (error) {
// console.log('error.errors', error.errors) // console.log('error.errors', error.errors)

View File

@ -46,12 +46,18 @@ export default {
foreignKey: 'parentKey', foreignKey: 'parentKey',
}, },
{ {
interface: 'linkTo',
type: 'belongsTo', type: 'belongsTo',
name: 'collection', name: 'collection',
target: 'collections', target: 'collections',
targetKey: 'name', targetKey: 'name',
}, },
{
type: 'belongsTo',
name: 'reverseField',
target: 'fields',
sourceKey: 'key',
foreignKey: 'reverseKey',
},
{ {
type: 'belongsTo', type: 'belongsTo',
name: 'uiSchema', name: 'uiSchema',

View File

@ -48,4 +48,54 @@ export class Field extends Model {
} }
return items; return items;
} }
async generateReverseField(opts: any = {}) {
const { migrate = true } = opts;
if (this.get('interface') !== 'linkTo') {
return;
}
if (this.get('reverseKey')) {
return;
}
const fieldCollection = await this.getCollection();
const options: any = this.get('options') || {};
const Collection = this.database.getModel('collections');
let collection = await Collection.findOne({
where: {
name: options.target,
},
});
if (!collection) {
return;
}
const reverseField = await collection.createField({
interface: 'linkTo',
dataType: 'belongsToMany',
through: options.through,
foreignKey: options.otherKey,
otherKey: options.foreignKey,
sourceKey: options.targetKey,
targetKey: options.sourceKey,
target: this.get('collection_name'),
reverseKey: this.get('key'),
});
await reverseField.updateAssociations({
uiSchema: {
type: 'array',
title: `${fieldCollection?.title}`,
'x-component': 'Select.Drawer',
'x-component-props': {
multiple: true,
},
'x-decorator': 'FormItem',
'x-designable-bar': 'Select.Drawer.DesignableBar',
}
});
await this.update({
reverseKey: reverseField.key,
});
if (migrate) {
await collection.migrate();
}
}
} }

View File

@ -1,6 +1,6 @@
import path from 'path'; import path from 'path';
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import { registerModels, Table } from '@nocobase/database'; import { registerModels, Table, uid } from '@nocobase/database';
import * as models from './models'; import * as models from './models';
import { createOrUpdate, findAll } from './actions'; import { createOrUpdate, findAll } from './actions';
import { create } from './actions/fields'; import { create } from './actions/fields';
@ -27,6 +27,7 @@ export default async function (this: Application, options = {}) {
} }
}); });
Field.beforeUpdate(async (model) => { Field.beforeUpdate(async (model) => {
console.log('beforeUpdate', model.key);
if (!model.get('collection_name') && model.get('parentKey')) { if (!model.get('collection_name') && model.get('parentKey')) {
const field = await Field.findByPk(model.get('parentKey')); const field = await Field.findByPk(model.get('parentKey'));
if (field) { if (field) {
@ -37,8 +38,8 @@ export default async function (this: Application, options = {}) {
} }
} }
}); });
Field.afterCreate(async (model) => { Field.afterCreate(async (model, options) => {
console.log('afterCreate'); console.log('afterCreate', model.key, model.get('collection_name'));
if (model.get('interface') !== 'subTable') { if (model.get('interface') !== 'subTable') {
return; return;
} }