feat: improve code

This commit is contained in:
chenos 2022-03-02 18:35:49 +08:00
parent aa1c84e72a
commit afab15327e
22 changed files with 135 additions and 109 deletions

View File

@ -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);

View File

@ -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);
};
},
};

View File

@ -11,7 +11,7 @@ export const updatedAt: IField = {
sortable: true,
default: {
type: 'date',
field: 'updated_at',
field: 'updatedAt',
// name,
uiSchema: {
type: 'datetime',

View File

@ -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`

View File

@ -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,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: {

View File

@ -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>

View File

@ -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);
},
};
};

View File

@ -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();
});

View File

@ -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();

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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'],
});

View File

@ -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({

View File

@ -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',

View File

@ -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'),

View File

@ -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,
},
});
}
}

View File

@ -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',
},

View File

@ -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');
}
}
}