feat: improve code
This commit is contained in:
parent
aa1c84e72a
commit
afab15327e
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ export const updatedAt: IField = {
|
||||
sortable: true,
|
||||
default: {
|
||||
type: 'date',
|
||||
field: 'updated_at',
|
||||
field: 'updatedAt',
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'datetime',
|
||||
|
@ -34,7 +34,7 @@ const InternalAdminLayout = (props: any) => {
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header style={{ height: 46, lineHeight: '46px', position: 'relative', paddingLeft: 0 }}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div style={{ display: 'flex', height: '100%' }}>
|
||||
<div style={{ width: 200, display: 'inline-flex', color: '#fff', padding: '0', alignItems: 'center' }}>
|
||||
<img
|
||||
className={css`
|
||||
|
@ -11,6 +11,7 @@ export const LinkToFieldInitializer = (props) => {
|
||||
item.find,
|
||||
item.remove,
|
||||
);
|
||||
const targetKey = item.field.targetKey || 'id';
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
@ -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,62 +84,31 @@ 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: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 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: {
|
||||
// actions: {
|
||||
// type: 'void',
|
||||
// title: 'Name',
|
||||
// title: '{{ t("Actions") }}',
|
||||
// 'x-decorator': 'Table.Column.ActionBar',
|
||||
// 'x-component': 'Table.Column',
|
||||
// 'x-designer': 'Table.RowActionDesigner',
|
||||
// 'x-initializer': 'TableRecordActionInitializers',
|
||||
// properties: {
|
||||
// name: {
|
||||
// type: 'string',
|
||||
// 'x-component': 'Input',
|
||||
// 'x-read-pretty': true,
|
||||
// },
|
||||
// },
|
||||
// actions: {
|
||||
// type: 'void',
|
||||
// 'x-decorator': 'DndContext',
|
||||
// 'x-component': 'Space',
|
||||
// 'x-component-props': {
|
||||
// split: '|',
|
||||
// },
|
||||
// properties: {},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
item: {
|
||||
'x-component': 'RecordPicker.SelectedItem',
|
||||
properties: {
|
||||
|
@ -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 (
|
||||
<div className={'general-schema-designer'}>
|
||||
{title && <div className={classNames('general-schema-designer-title', titleCss)}>{title}</div>}
|
||||
{title && <div className={classNames('general-schema-designer-title', titleCss)}>{compile(title)}</div>}
|
||||
<div className={'general-schema-designer-icons'}>
|
||||
<Space size={2} align={'center'}>
|
||||
<DragHandler>
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -184,7 +184,7 @@ describe('collection sync', () => {
|
||||
|
||||
const tableFields = await (<any>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 (<any>model).queryInterface.describeTable(`${db.getTablePrefix()}posts_tags`);
|
||||
const tableFields = await (<any>model).queryInterface.describeTable(`${db.getTablePrefix()}postsTags`);
|
||||
expect(tableFields['postId']).toBeDefined();
|
||||
expect(tableFields['tagId']).toBeDefined();
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -158,10 +158,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
getRepository<R extends RelationRepository>(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) {
|
||||
|
@ -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 ||
|
||||
Utils.camelize(
|
||||
[this.context.collection.model.name, this.target]
|
||||
.map((name) => name.toLowerCase())
|
||||
.sort()
|
||||
.join('_')
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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'],
|
||||
});
|
||||
|
||||
|
@ -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({
|
||||
|
@ -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',
|
||||
|
@ -259,6 +259,13 @@ export class PluginACL extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
async install() {
|
||||
const repo = this.db.getRepository<any>('collections');
|
||||
if (repo) {
|
||||
await repo.db2cm('roles');
|
||||
}
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.app.db.import({
|
||||
directory: resolve(__dirname, 'collections'),
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -83,5 +83,9 @@ export default class UsersPlugin extends Plugin {
|
||||
roles: ['admin'],
|
||||
},
|
||||
});
|
||||
const repo = this.db.getRepository<any>('collections');
|
||||
if (repo) {
|
||||
await repo.db2cm('users');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user