chenos 0e70e3848a
feat: improve collection manager (#1013)
* feat: 图形化管理数据表

* feat: 图形化管理数据表

* feat: 图形化管理数据表

* feat: 图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 完善图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat: 样式优化图形化管理数据表

* feat(collection-manager): add foreignKey Field and support relate field record foreignKey info

through collection record into collections and foreignKey field record info fields

* fix(collection-manager): if has through collection then don't create through collections record

* fix(client/route-switch): skip sub routes

* feat: 添加graphpostion

* feat: 图形化collection新增表时刷新数据

* fix(collection-manager): refactor afterCreateForRelateField

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化collection存储位置

* feat: 图形化样式优化

* feat: styling

* feat: 图形化样式优化

* feat: 图形化样式优化

* feat: 图形化数据表多语言完善

* feat: 图形化数据表多语言完善

* feat: improve code

* feat: 图形化数据表连线样式修改

* feat: 图形化数据表样式修改

* feat: 图形化数据表样式修改

* feat: 图形化数据表样式修改

* feat: 图形化数据表样式修改

* fix(collection-manager): fix afterCreateForRelateField

* feat: 样式优化

* feat: 样式优化

* feat: afterCreateForForeignKeyField

* fix: timestamps: false

* feat: 连线锚点优化

* fix(collection-manager): when del foreign key field, relate fields will be del too

* fix: update package.json

* fix: update package.json

* feat: 文件名大小写

* feat: 连线锚点优化

* feat: 连线锚点通过计算得到样式优化

* feat: 连线锚点通过计算得到样式优化

* fix: fk

* fix: remove index

* feat: 连线hover时高亮

* fix: test error

* feat: 初始化计算位置

* feat: 初始化时计算坐标位置

* feat: 初始化时计算坐标位置

* feat: improve code (#933)

* fix: built in

* feat: 没有关系字段时也要连线

* feat: 自关联也要连线

* fix: styling

* feat: 滚动条问题

* feat: 拖拽优化

* feat: 画布paddig优化

* feat: 编辑时支持反向关联字段配置

* feat: 画布拖拽滚动优化

* feat: 画布拖拽滚动优化

* fix: reload

* feat: 修复数据表新建重叠

* fix: refreshCM & refreshGM

* feat: 修复表达式输入框显示异常

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* fix: 消除代码警告

* fix: 消除代码警告

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化(增量渲染)

* feat: 渲染性能优化

* feat: 渲染性能优化

* feat: 外键生成在位置在前面

* feat: 限制表最多显示10个字段其余滚动

* feat: 移动表位置的连线重新计算最优位置

* fix: error

* feat: 布局自动换行

* fix: test error

* fix: xpipe.eq

* fix: upgrade error

* fix: upgrade error

* feat: 选中表时只显示和目标表关联的表和连线

* feat: 连线优化

* fix: maxListenersExceededWarning

* feat: 连线优化

* feat: powerby样式优化

* feat: 表筛选优化

* feat: 新建字段优化

* feat: 点击线高亮主外键和关联字段

* feat: 点击线高亮主外键和关联字段

* feat: 鼠标hover连线高亮主外键和关联字段

* fix(collection-manager): foreign key sorting should follow ID

* fix(client/config-relation-field): set Relation field's ReverseField default value is false

* feat: 卡片默认显示主外键和关联字段其余通过折叠展示且分组区分显示

* fix(client/collection-manager): don't display auto create through collections and foreign key

only display in graph menu

* feat: 样式优化

* feat: 添加字段时默认展开折叠

* feat: 样式优化

* feat: foreign field migration (#1001)

* feat: 补充多语言

* feat: settings center tabs

* feat: 主键判断primaryKey

* fix(collection-manager): foreign key sorting should follow primaryKey

* fix(client/block-select-collection): filter auto create through collections

* fix(client/block-config-fields): filter isForeignKey fields

* fix(client/configuration-table): relation fileds select collection filter auto create through

* feat: 多对多连线高亮时全亮

* feat: 选中多对多中的一张表另一张表也显示

* feat: 连线mouseleave事件

* feat: 多语言更新

* feat: 计算新建表位置优化

* feat: 添加自动布局

* feat(client/configure-fields): categorize fields

* fix(client/configure-fields): display foreign key fields

* fix(client): package reference

* fix: remove graph

* fix: remove

Co-authored-by: 唐小爱 <tangxiaoai@192.168.0.103>
Co-authored-by: lyf-coder <lyf-coder@foxmail.com>
Co-authored-by: katherinehhh <katherine_15995@163.com>
Co-authored-by: ChengLei Shao <chareice@live.com>
Co-authored-by: mytharcher <mytharcher@gmail.com>
2022-11-02 22:13:25 +08:00

171 lines
5.1 KiB
TypeScript

import path from 'path';
import lodash from 'lodash';
import { UniqueConstraintError } from 'sequelize';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import { Plugin } from '@nocobase/server';
import { CollectionRepository } from '.';
import {
afterCreateForForeignKeyField,
afterCreateForReverseField,
beforeCreateForChildrenCollection,
beforeCreateForReverseField,
beforeDestroyForeignKey,
beforeInitOptions,
} from './hooks';
import { CollectionModel, FieldModel } from './models';
export class CollectionManagerPlugin extends Plugin {
async beforeLoad() {
this.app.db.registerModels({
CollectionModel,
FieldModel,
});
this.db.addMigrations({
namespace: 'collection-manager',
directory: path.resolve(__dirname, './migrations'),
context: {
plugin: this,
},
});
this.app.db.registerRepositories({
CollectionRepository,
});
this.app.db.on('fields.beforeUpdate', async (model, options) => {
const newValue = options.values;
if (
model.get('reverseKey') &&
lodash.get(newValue, 'reverseField') &&
!lodash.get(newValue, 'reverseField.key')
) {
const field = await this.app.db
.getModel('fields')
.findByPk(model.get('reverseKey'), { transaction: options.transaction });
if (field) {
throw new Error('cant update field without a reverseField key');
}
}
});
// 要在 beforeInitOptions 之前处理
this.app.db.on('fields.beforeCreate', beforeCreateForReverseField(this.app.db));
this.app.db.on('fields.beforeCreate', beforeCreateForChildrenCollection(this.app.db));
this.app.db.on('fields.beforeCreate', async (model, options) => {
const type = model.get('type');
const fn = beforeInitOptions[type];
if (fn) {
await fn(model, { database: this.app.db });
}
});
this.app.db.on('fields.afterCreate', afterCreateForReverseField(this.app.db));
this.app.db.on('collections.afterCreateWithAssociations', async (model, { context, transaction }) => {
if (context) {
await model.migrate({
isNew: true,
transaction,
});
}
});
this.app.db.on('fields.afterCreate', async (model: FieldModel, { context, transaction }) => {
if (context) {
await model.migrate({
isNew: true,
transaction,
});
}
});
// after migrate
this.app.db.on('fields.afterCreate', afterCreateForForeignKeyField(this.app.db));
this.app.db.on('fields.afterUpdate', async (model: FieldModel, { context, transaction }) => {
const prevOptions = model.previous('options');
const currentOptions = model.get('options');
if (context) {
const prev = prevOptions['unique'];
const next = currentOptions['unique'];
if (Boolean(prev) !== Boolean(next)) {
await model.migrate({ transaction });
}
}
const prevDefaultValue = prevOptions['defaultValue'];
const currentDefaultValue = currentOptions['defaultValue'];
if (prevDefaultValue != currentDefaultValue) {
await model.syncDefaultValue({ transaction, defaultValue: currentDefaultValue });
}
});
this.app.db.on('fields.afterSaveWithAssociations', async (model, { context, transaction }) => {
if (context) {
await model.load({ transaction });
}
});
// before field remove
this.app.db.on('fields.beforeDestroy', beforeDestroyForeignKey(this.app.db));
this.app.db.on('fields.beforeDestroy', async (model, options) => {
await model.remove(options);
});
this.app.db.on('collections.beforeDestroy', async (model, options) => {
await model.remove(options);
});
this.app.on('afterLoad', async (app, options) => {
if (options?.method === 'install') {
return;
}
const exists = await this.app.db.collectionExistsInDb('collections');
if (exists) {
await this.app.db.getRepository<CollectionRepository>('collections').load();
}
});
this.app.resourcer.use(async (ctx, next) => {
const { resourceName, actionName } = ctx.action;
if (resourceName === 'collections.fields' && actionName === 'update') {
const { updateAssociationValues = [] } = ctx.action.params;
updateAssociationValues.push('uiSchema');
ctx.action.mergeParams({
updateAssociationValues,
});
}
await next();
});
this.app.acl.allow('collections', 'list', 'loggedIn');
this.app.acl.allow('collections', ['create', 'update', 'destroy'], 'allowConfigure');
}
async load() {
await this.app.db.import({
directory: path.resolve(__dirname, './collections'),
});
const errorHandlerPlugin = <PluginErrorHandler>this.app.getPlugin('error-handler');
errorHandlerPlugin.errorHandler.register(
(err) => {
return err instanceof UniqueConstraintError;
},
(err, ctx) => {
return ctx.throw(400, ctx.t(`The value of ${Object.keys(err.fields)} field duplicated`));
},
);
}
}
export default CollectionManagerPlugin;