feat: generate reverse field for linkTo fields
This commit is contained in:
parent
a771aade5c
commit
8bd5a83947
11
packages/api/src/migrations/sync.ts
Normal file
11
packages/api/src/migrations/sync.ts
Normal 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();
|
||||||
|
})();
|
@ -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;
|
||||||
|
@ -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({
|
||||||
|
@ -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({
|
||||||
|
@ -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();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user