diff --git a/packages/actions/src/__tests__/list-action.test.ts b/packages/actions/src/__tests__/list-action.test.ts index 1032d2972..0f0932cf2 100644 --- a/packages/actions/src/__tests__/list-action.test.ts +++ b/packages/actions/src/__tests__/list-action.test.ts @@ -1,5 +1,5 @@ -import { mockServer } from './index'; import { registerActions } from '@nocobase/actions'; +import { mockServer } from './index'; describe('list action', () => { let app; @@ -112,7 +112,7 @@ describe('list action', () => { const response = await app .agent() .resource('posts.tags', 1) - .list({ fields: ['id', 'posts_tags.createdAt'], sort: ['id'] }); + .list({ fields: ['id', 'postsTags.createdAt'], sort: ['id'] }); const body = response.body; expect(body.count).toEqual(2); diff --git a/packages/client/src/api-client/APIClient.ts b/packages/client/src/api-client/APIClient.ts index 64bb115bc..a9feba385 100644 --- a/packages/client/src/api-client/APIClient.ts +++ b/packages/client/src/api-client/APIClient.ts @@ -101,13 +101,13 @@ export class APIClient { } else { config['method'] = 'post'; } - return (params?: ActionParams) => { + return async (params?: ActionParams) => { const { values, ...others } = params || {}; config['params'] = others; if (config.method !== 'get') { config['data'] = values || {}; } - return this.request(config); + return await this.request(config); }; }, }; diff --git a/packages/client/src/collection-manager/interfaces/updatedAt.ts b/packages/client/src/collection-manager/interfaces/updatedAt.ts index 427a707ee..57b237e60 100644 --- a/packages/client/src/collection-manager/interfaces/updatedAt.ts +++ b/packages/client/src/collection-manager/interfaces/updatedAt.ts @@ -11,7 +11,7 @@ export const updatedAt: IField = { sortable: true, default: { type: 'date', - field: 'updated_at', + field: 'updatedAt', // name, uiSchema: { type: 'datetime', diff --git a/packages/client/src/route-switch/antd/admin-layout/index.tsx b/packages/client/src/route-switch/antd/admin-layout/index.tsx index 3e1761f5d..af8ef4db2 100644 --- a/packages/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/client/src/route-switch/antd/admin-layout/index.tsx @@ -34,7 +34,7 @@ const InternalAdminLayout = (props: any) => { return ( -
+
{ item.find, item.remove, ); + const targetKey = item.field.targetKey || 'id'; return ( { @@ -20,17 +21,17 @@ export const LinkToFieldInitializer = (props) => { } insert({ ...item.schema, - default: [ - { id: 1, name: 'name1' }, - { id: 2, name: 'name2' }, - ], + // default: [ + // { id: 1, name: 'name1' }, + // { id: 2, name: 'name2' }, + // ], // 'x-decorator': 'FormItem', 'x-component': 'CollectionField', 'x-component-props': { mode: 'tags', fieldNames: { - label: 'name', - value: 'id', + label: targetKey, + value: targetKey, }, }, properties: { @@ -59,22 +60,22 @@ export const LinkToFieldInitializer = (props) => { 'x-designer': 'Table.Void.Designer', 'x-component': 'CardItem', properties: { - actions: { - type: 'void', - 'x-initializer': 'TableActionInitializers', - 'x-component': 'ActionBar', - 'x-component-props': { - style: { - marginBottom: 16, - }, - }, - properties: {}, - }, + // actions: { + // type: 'void', + // 'x-initializer': 'TableActionInitializers', + // 'x-component': 'ActionBar', + // 'x-component-props': { + // style: { + // marginBottom: 16, + // }, + // }, + // properties: {}, + // }, table: { // type: 'void', 'x-component': 'Table.RowSelection', 'x-component-props': { - rowKey: 'id', + rowKey: targetKey, objectValue: true, rowSelection: { type: 'checkbox', @@ -83,60 +84,29 @@ export const LinkToFieldInitializer = (props) => { }, 'x-initializer': 'TableColumnInitializers', properties: { - actions: { - type: 'void', - title: '{{ t("Actions") }}', - 'x-decorator': 'Table.Column.ActionBar', - 'x-component': 'Table.Column', - 'x-designer': 'Table.RowActionDesigner', - 'x-initializer': 'TableRecordActionInitializers', - properties: { - actions: { - type: 'void', - 'x-decorator': 'DndContext', - 'x-component': 'Space', - 'x-component-props': { - split: '|', - }, - properties: {}, - }, - }, - }, + // actions: { + // type: 'void', + // title: '{{ t("Actions") }}', + // 'x-decorator': 'Table.Column.ActionBar', + // 'x-component': 'Table.Column', + // 'x-designer': 'Table.RowActionDesigner', + // 'x-initializer': 'TableRecordActionInitializers', + // properties: { + // actions: { + // type: 'void', + // 'x-decorator': 'DndContext', + // 'x-component': 'Space', + // 'x-component-props': { + // split: '|', + // }, + // properties: {}, + // }, + // }, + // }, }, }, }, }, - // input: { - // type: 'array', - // title: `编辑模式`, - // 'x-component': 'Table.RowSelection', - // 'x-component-props': { - // rowKey: 'id', - // objectValue: true, - // rowSelection: { - // type: 'checkbox', - // }, - // dataSource: [ - // { id: 1, name: 'Name1' }, - // { id: 2, name: 'Name2' }, - // { id: 3, name: 'Name3' }, - // ], - // }, - // properties: { - // column1: { - // type: 'void', - // title: 'Name', - // 'x-component': 'Table.Column', - // properties: { - // name: { - // type: 'string', - // 'x-component': 'Input', - // 'x-read-pretty': true, - // }, - // }, - // }, - // }, - // }, }, }, item: { diff --git a/packages/client/src/schema-settings/GeneralSchemaDesigner.tsx b/packages/client/src/schema-settings/GeneralSchemaDesigner.tsx index b46ca0f3e..82dca0c9c 100644 --- a/packages/client/src/schema-settings/GeneralSchemaDesigner.tsx +++ b/packages/client/src/schema-settings/GeneralSchemaDesigner.tsx @@ -4,7 +4,7 @@ import { useField, useFieldSchema } from '@formily/react'; import { Space } from 'antd'; import classNames from 'classnames'; import React from 'react'; -import { DragHandler, useDesignable } from '../schema-component'; +import { DragHandler, useCompile, useDesignable } from '../schema-component'; import { SchemaSettings } from './SchemaSettings'; const titleCss = css` @@ -27,6 +27,7 @@ export const GeneralSchemaDesigner = (props: any) => { const { dn, designable } = useDesignable(); const field = useField(); const fieldSchema = useFieldSchema(); + const compile = useCompile(); const schemaSettingsProps = { dn, field, @@ -37,7 +38,7 @@ export const GeneralSchemaDesigner = (props: any) => { } return (
- {title &&
{title}
} + {title &&
{compile(title)}
}
diff --git a/packages/client/src/user/SignupPage.tsx b/packages/client/src/user/SignupPage.tsx index 8f7559db4..bdcc0fe58 100644 --- a/packages/client/src/user/SignupPage.tsx +++ b/packages/client/src/user/SignupPage.tsx @@ -91,15 +91,13 @@ const useSignup = () => { return { async run() { await form.submit(); - const response = await api.resource('users').signup({ + await api.resource('users').signup({ values: form.values, }); - if (response?.data) { - message.success('注册成功,即将跳转登录页'); - setTimeout(() => { - history.push('/signin'); - }, 2000); - } + message.success('注册成功,即将跳转登录页'); + setTimeout(() => { + history.push('/signin'); + }, 2000); }, }; }; diff --git a/packages/database/src/__tests__/collection.test.ts b/packages/database/src/__tests__/collection.test.ts index 16cc5f2b4..827c73f27 100644 --- a/packages/database/src/__tests__/collection.test.ts +++ b/packages/database/src/__tests__/collection.test.ts @@ -184,7 +184,7 @@ describe('collection sync', () => { const tableFields = await (model).queryInterface.describeTable(`${db.getTablePrefix()}posts`); - expect(tableFields['user_id']).toBeUndefined(); + expect(tableFields['userId']).toBeUndefined(); }); test('sync with association', async () => { @@ -211,7 +211,7 @@ describe('collection sync', () => { const model = collection.model; await collection.sync(); - const tableFields = await (model).queryInterface.describeTable(`${db.getTablePrefix()}posts_tags`); + const tableFields = await (model).queryInterface.describeTable(`${db.getTablePrefix()}postsTags`); expect(tableFields['postId']).toBeDefined(); expect(tableFields['tagId']).toBeDefined(); }); diff --git a/packages/database/src/__tests__/fields/belongs-to-many-field.test.ts b/packages/database/src/__tests__/fields/belongs-to-many-field.test.ts index 8d2ef9cd3..c58f21567 100644 --- a/packages/database/src/__tests__/fields/belongs-to-many-field.test.ts +++ b/packages/database/src/__tests__/fields/belongs-to-many-field.test.ts @@ -1,5 +1,5 @@ -import { Database } from '../../database'; import { mockDatabase } from '../'; +import { Database } from '../../database'; describe('belongs to many field', () => { let db: Database; @@ -22,14 +22,14 @@ describe('belongs to many field', () => { }); expect(Post.model.associations.tags).toBeUndefined(); - expect(db.getCollection('posts_tags')).toBeUndefined(); + expect(db.getCollection('postsTags')).toBeUndefined(); const Tag = db.collection({ name: 'tags', fields: [{ type: 'string', name: 'name' }], }); expect(Post.model.associations.tags).toBeDefined(); - const Through = db.getCollection('posts_tags'); + const Through = db.getCollection('postsTags'); expect(Through).toBeDefined(); expect(Through.model.rawAttributes['postId']).toBeDefined(); @@ -46,14 +46,14 @@ describe('belongs to many field', () => { }); expect(Post.model.associations.tags).toBeUndefined(); - expect(db.getCollection('posts_tags')).toBeUndefined(); + expect(db.getCollection('postsTags')).toBeUndefined(); const Tag = db.collection({ name: 'tags', fields: [{ type: 'string', name: 'name' }], }); - const PostTag = db.collection({ name: 'posts_tags' }); + const PostTag = db.collection({ name: 'postsTags' }); expect(PostTag.model.rawAttributes['postId']).toBeDefined(); expect(PostTag.model.rawAttributes['tagId']).toBeDefined(); diff --git a/packages/database/src/collection.ts b/packages/database/src/collection.ts index ea9922676..ebc6169bd 100644 --- a/packages/database/src/collection.ts +++ b/packages/database/src/collection.ts @@ -159,7 +159,7 @@ export class Collection< collection: this, }, ); - + this.removeField(name); this.fields.set(name, field); this.emit('field.afterAdd', field); return field; @@ -187,6 +187,9 @@ export class Collection< } removeField(name) { + if (!this.fields.has(name)) { + return; + } const field = this.fields.get(name); const bool = this.fields.delete(name); if (bool) { diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index 980057653..3a024e95c 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -158,10 +158,10 @@ export class Database extends EventEmitter implements AsyncEmitter { getRepository(name: string, relationId?: string | number): Repository | R { if (relationId) { const [collection, relation] = name.split('.'); - return this.getRepository(collection).relation(relation).of(relationId) as R; + return this.getRepository(collection)?.relation(relation)?.of(relationId) as R; } - return this.getCollection(name).repository; + return this.getCollection(name)?.repository; } addPendingField(field: RelationField) { diff --git a/packages/database/src/fields/belongs-to-many-field.ts b/packages/database/src/fields/belongs-to-many-field.ts index cb8de0725..5ae69d521 100644 --- a/packages/database/src/fields/belongs-to-many-field.ts +++ b/packages/database/src/fields/belongs-to-many-field.ts @@ -1,16 +1,18 @@ import { omit } from 'lodash'; -import { BelongsToManyOptions as SequelizeBelongsToManyOptions } from 'sequelize'; +import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize'; import { Collection } from '../collection'; -import { BaseRelationFieldOptions, MultipleRelationFieldOptions, RelationField } from './relation-field'; +import { MultipleRelationFieldOptions, RelationField } from './relation-field'; export class BelongsToManyField extends RelationField { get through() { return ( this.options.through || - [this.context.collection.model.name, this.target] - .map((name) => name.toLowerCase()) - .sort() - .join('_') + Utils.camelize( + [this.context.collection.model.name, this.target] + .map((name) => name.toLowerCase()) + .sort() + .join('_'), + ) ); } @@ -49,6 +51,12 @@ export class BelongsToManyField extends RelationField { if (!this.options.sourceKey) { this.options.sourceKey = association.sourceKey; } + if (!this.options.otherKey) { + this.options.otherKey = association.otherKey; + } + if (!this.options.through) { + this.options.through = this.through; + } return true; } diff --git a/packages/plugin-acl/src/__tests__/prepare.ts b/packages/plugin-acl/src/__tests__/prepare.ts index 1ea1ca258..abdd5f70b 100644 --- a/packages/plugin-acl/src/__tests__/prepare.ts +++ b/packages/plugin-acl/src/__tests__/prepare.ts @@ -1,6 +1,6 @@ -import { mockServer } from '@nocobase/test'; -import PluginUiSchema from '@nocobase/plugin-ui-schema-storage'; import PluginCollectionManager from '@nocobase/plugin-collection-manager'; +import PluginUiSchema from '@nocobase/plugin-ui-schema-storage'; +import { mockServer } from '@nocobase/test'; import PluginACL from '../server'; let mockRole: string = 'admin'; @@ -33,5 +33,7 @@ export async function prepareApp() { app.plugin(PluginACL); await app.loadAndInstall(); + await app.db.sync(); + return app; } diff --git a/packages/plugin-acl/src/__tests__/role-resource.test.ts b/packages/plugin-acl/src/__tests__/role-resource.test.ts index e342cfc79..b48b908b0 100644 --- a/packages/plugin-acl/src/__tests__/role-resource.test.ts +++ b/packages/plugin-acl/src/__tests__/role-resource.test.ts @@ -1,7 +1,7 @@ -import { MockServer } from '@nocobase/test'; import { Database, Model } from '@nocobase/database'; -import { prepareApp } from './prepare'; import { CollectionRepository } from '@nocobase/plugin-collection-manager'; +import { MockServer } from '@nocobase/test'; +import { prepareApp } from './prepare'; describe('role resource api', () => { let app: MockServer; @@ -54,6 +54,9 @@ describe('role resource api', () => { .agent() .resource('roles.collections', 'admin') .list({ + filter: { + $or: [{ name: 'c1' }, { name: 'c2' }], + }, sort: ['sort'], }); diff --git a/packages/plugin-acl/src/actions/role-collections.ts b/packages/plugin-acl/src/actions/role-collections.ts index e1be02452..e51927051 100644 --- a/packages/plugin-acl/src/actions/role-collections.ts +++ b/packages/plugin-acl/src/actions/role-collections.ts @@ -12,7 +12,9 @@ const roleCollectionsResource = { const collectionRepository = db.getRepository('collections'); // all collections - const collections = await collectionRepository.find(); + const collections = await collectionRepository.find({ + filter: ctx.action.params.filter, + }); // role collections const roleResources = await db.getRepository('rolesResources').find({ diff --git a/packages/plugin-acl/src/collections/roles.ts b/packages/plugin-acl/src/collections/roles.ts index a95c1646a..d4f2c73f6 100644 --- a/packages/plugin-acl/src/collections/roles.ts +++ b/packages/plugin-acl/src/collections/roles.ts @@ -2,6 +2,7 @@ import { CollectionOptions } from '@nocobase/database'; export default { name: 'roles', + title: '{{t("Roles")}}', autoGenId: false, model: 'RoleModel', fields: [ @@ -10,11 +11,21 @@ export default { name: 'name', prefix: 'r_', primaryKey: true, + uiSchema: { + type: 'string', + title: '{{t("Role UID")}}', + 'x-component': 'Input', + }, }, { type: 'string', name: 'title', unique: true, + uiSchema: { + type: 'string', + title: '{{t("Role name")}}', + 'x-component': 'Input', + }, }, { type: 'boolean', @@ -45,6 +56,7 @@ export default { type: 'belongsToMany', name: 'menuUiSchemas', target: 'uiSchemas', + targetKey: 'x-uid', }, { type: 'hasMany', diff --git a/packages/plugin-acl/src/server.ts b/packages/plugin-acl/src/server.ts index dc013ef3e..0cadbdb7e 100644 --- a/packages/plugin-acl/src/server.ts +++ b/packages/plugin-acl/src/server.ts @@ -259,6 +259,13 @@ export class PluginACL extends Plugin { }); } + async install() { + const repo = this.db.getRepository('collections'); + if (repo) { + await repo.db2cm('roles'); + } + } + async load() { await this.app.db.import({ directory: resolve(__dirname, 'collections'), diff --git a/packages/plugin-collection-manager/src/repositories/collection-repository.ts b/packages/plugin-collection-manager/src/repositories/collection-repository.ts index fcbe01b10..c413c25ca 100644 --- a/packages/plugin-collection-manager/src/repositories/collection-repository.ts +++ b/packages/plugin-collection-manager/src/repositories/collection-repository.ts @@ -14,4 +14,22 @@ export class CollectionRepository extends Repository { await instance.load({ skipExist }); } } + + async db2cm(collectionName: string) { + const collection = this.database.getCollection(collectionName); + const options = collection.options; + const fields = []; + for (const [name, field] of collection.fields) { + fields.push({ + name, + ...field.options, + }); + } + await this.create({ + values: { + ...options, + fields, + }, + }); + } } diff --git a/packages/plugin-ui-schema-storage/src/collections/ui_schema_tree_path.ts b/packages/plugin-ui-schema-storage/src/collections/uiSchemaTreePath.ts similarity index 100% rename from packages/plugin-ui-schema-storage/src/collections/ui_schema_tree_path.ts rename to packages/plugin-ui-schema-storage/src/collections/uiSchemaTreePath.ts diff --git a/packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts b/packages/plugin-ui-schema-storage/src/collections/uiSchemas.ts similarity index 100% rename from packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts rename to packages/plugin-ui-schema-storage/src/collections/uiSchemas.ts diff --git a/packages/plugin-users/src/collections/users.ts b/packages/plugin-users/src/collections/users.ts index 0df15a56d..ef50382e7 100644 --- a/packages/plugin-users/src/collections/users.ts +++ b/packages/plugin-users/src/collections/users.ts @@ -6,7 +6,7 @@ export default { sortable: 'sort', fields: [ { - interface: 'string', + interface: 'input', type: 'string', name: 'nickname', uiSchema: { @@ -47,10 +47,11 @@ export default { otherKey: 'roleName', sourceKey: 'id', targetKey: 'name', + through: 'rolesUsers', uiSchema: { type: 'array', title: '{{t("Roles")}}', - 'x-component': 'Select.Drawer', + 'x-component': 'RecordPicker', 'x-component-props': { multiple: true, fieldNames: { @@ -58,12 +59,9 @@ export default { value: 'name', }, }, - 'x-decorator': 'FormItem', - 'x-designable-bar': 'Select.Drawer.DesignableBar', }, }, { - interface: 'select', type: 'string', name: 'appLang', }, diff --git a/packages/plugin-users/src/server.ts b/packages/plugin-users/src/server.ts index 92ce973a7..c5e6a5796 100644 --- a/packages/plugin-users/src/server.ts +++ b/packages/plugin-users/src/server.ts @@ -83,5 +83,9 @@ export default class UsersPlugin extends Plugin { roles: ['admin'], }, }); + const repo = this.db.getRepository('collections'); + if (repo) { + await repo.db2cm('users'); + } } }