style: code formatting
This commit is contained in:
parent
5e9959b987
commit
ce4a22fbb9
@ -2,18 +2,18 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('add', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
it('belongsToMany1', async () => {
|
||||
const [Post, Tag] = db.getModels(['posts', 'tags']);
|
||||
let post = await Post.create();
|
||||
let tag1 = await Tag.create({name: 'tag1'});
|
||||
let tag2 = await Tag.create({name: 'tag2'});
|
||||
let tag1 = await Tag.create({ name: 'tag1' });
|
||||
let tag2 = await Tag.create({ name: 'tag2' });
|
||||
await agent.post(`/posts/${post.id}/tags:add/${tag1.id}`);
|
||||
await agent.post(`/posts/${post.id}/tags:add/${tag2.id}`);
|
||||
let [tag01, tag02] = await post.getTags();
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('create', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
describe('single', () => {
|
||||
@ -49,11 +49,11 @@ describe('create', () => {
|
||||
});
|
||||
expect(response.body.sort).toBe(1);
|
||||
expect(response.body.user_id).toBe(1);
|
||||
|
||||
|
||||
const postWithUser = await agent
|
||||
.get(`/posts/${response.body.id}?fields=user`);
|
||||
expect(postWithUser.body.user.id).toBe(1);
|
||||
|
||||
|
||||
const user = await agent
|
||||
.get(`/users/${postWithUser.body.user.id}?fields=profile`);
|
||||
expect(user.body.profile).toBe(null);
|
||||
@ -96,7 +96,7 @@ describe('create', () => {
|
||||
});
|
||||
expect(response.body.post_id).toBe(post.id);
|
||||
expect(response.body.content).toBe('content1');
|
||||
|
||||
|
||||
const comments = await agent
|
||||
.get('/comments?fields=id,content');
|
||||
expect(comments.body.count).toBe(1);
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('destroy', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
describe('single', () => {
|
||||
@ -18,24 +18,24 @@ describe('destroy', () => {
|
||||
// console.log(response.body);
|
||||
expect(response.body.count).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
it('batch delete by filter', async () => {
|
||||
const Post = db.getModel('posts');
|
||||
const posts = await Post.bulkCreate([
|
||||
{ title: 'title1', status: 'published'},
|
||||
{ title: 'title2', status: 'draft'},
|
||||
{ title: 'title3', status: 'published'},
|
||||
{ title: 'title4', status: 'draft'},
|
||||
{ title: 'title1', status: 'published' },
|
||||
{ title: 'title2', status: 'draft' },
|
||||
{ title: 'title3', status: 'published' },
|
||||
{ title: 'title4', status: 'draft' },
|
||||
]);
|
||||
|
||||
|
||||
await agent
|
||||
.delete('/posts?filter[status]=draft');
|
||||
|
||||
|
||||
const published = await Post.findAll();
|
||||
expect(published.length).toBe(2);
|
||||
expect(published.map(({ title, status }) => ({ title, status }))).toEqual([
|
||||
{ title: 'title1', status: 'published'},
|
||||
{ title: 'title3', status: 'published'}
|
||||
{ title: 'title1', status: 'published' },
|
||||
{ title: 'title3', status: 'published' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -62,7 +62,7 @@ describe('destroy', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
comments: [
|
||||
{content: 'content111222'},
|
||||
{ content: 'content111222' },
|
||||
],
|
||||
});
|
||||
const [comment] = await post.getComments();
|
||||
@ -78,9 +78,9 @@ describe('destroy', () => {
|
||||
await post.updateAssociations({
|
||||
comments: [
|
||||
{ content: 'content1', status: 'published' },
|
||||
{ content: 'content2', status: 'draft'},
|
||||
{ content: 'content2', status: 'draft' },
|
||||
{ content: 'content3', status: 'published' },
|
||||
{ content: 'content4', status: 'draft'},
|
||||
{ content: 'content4', status: 'draft' },
|
||||
],
|
||||
});
|
||||
await agent
|
||||
@ -96,7 +96,7 @@ describe('destroy', () => {
|
||||
const Post = db.getModel('posts');
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
user: {name: 'name121234'},
|
||||
user: { name: 'name121234' },
|
||||
});
|
||||
await agent.delete(`/posts/${post.id}/user:destroy`);
|
||||
const user = await post.getUser();
|
||||
@ -110,7 +110,7 @@ describe('destroy', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
tags: [
|
||||
{name: 'tag112233'},
|
||||
{ name: 'tag112233' },
|
||||
],
|
||||
});
|
||||
const [tag] = await post.getTags();
|
||||
@ -129,9 +129,9 @@ describe('destroy', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
tags: [
|
||||
{ name: 'tag1', status: 'enabled'},
|
||||
{ name: 'tag1', status: 'enabled' },
|
||||
{ name: 'tag2', status: 'disabled' },
|
||||
{ name: 'tag3', status: 'enabled'},
|
||||
{ name: 'tag3', status: 'enabled' },
|
||||
{ name: 'tag4', status: 'disabled' },
|
||||
],
|
||||
});
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('get', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
it('common1', async () => {
|
||||
@ -45,7 +45,7 @@ describe('get', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
comments: [
|
||||
{content: 'content111222'},
|
||||
{ content: 'content111222' },
|
||||
],
|
||||
});
|
||||
const [comment] = await post.getComments();
|
||||
@ -67,11 +67,11 @@ describe('get', () => {
|
||||
const Post = db.getModel('posts');
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
user: {name: 'name121234'},
|
||||
user: { name: 'name121234' },
|
||||
});
|
||||
const response = await agent
|
||||
.get(`/posts/${post.id}/user?fields=name`);
|
||||
expect(response.body).toEqual({name: 'name121234'});
|
||||
expect(response.body).toEqual({ name: 'name121234' });
|
||||
});
|
||||
|
||||
it('belongsToMany', async () => {
|
||||
@ -79,7 +79,7 @@ describe('get', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
tags: [
|
||||
{name: 'tag112233'},
|
||||
{ name: 'tag112233' },
|
||||
],
|
||||
});
|
||||
const [tag] = await post.getTags();
|
||||
|
@ -40,7 +40,7 @@ const connection = {
|
||||
define: {
|
||||
hooks: {
|
||||
beforeCreate(model, options) {
|
||||
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -8,7 +8,7 @@ describe('list', () => {
|
||||
let nowString: string;
|
||||
let timestamps: { created_at: Date; updated_at: Date; };
|
||||
let timestampsStrings;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
now = new Date();
|
||||
@ -16,7 +16,7 @@ describe('list', () => {
|
||||
timestamps = { created_at: now, updated_at: now };
|
||||
timestampsStrings = { created_at: nowString, updated_at: nowString };
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
describe('common', () => {
|
||||
@ -49,7 +49,7 @@ describe('list', () => {
|
||||
const response = await agent.get('/posts?filter[status]=published');
|
||||
expect(response.body.count).toBe(await Post.count({ where: { status: 'published' } }));
|
||||
});
|
||||
|
||||
|
||||
it('should be filtered by `title` equal to `title1`', async () => {
|
||||
const Post = db.getModel('posts');
|
||||
const response = await agent.get('/posts?filter[title]=title1');
|
||||
@ -182,22 +182,24 @@ describe('list', () => {
|
||||
expect(response.body.count).toBe(1);
|
||||
expect(response.body.rows[0].name).toBe(expected.name);
|
||||
});
|
||||
|
||||
|
||||
it('$anyOf for all elements in definition', async () => {
|
||||
const User = db.getModel('users');
|
||||
const expected = await User.findOne({
|
||||
where: {
|
||||
nicknames: { [Op.or]: [
|
||||
{ [Op.contains]: 'aaa' },
|
||||
{ [Op.contains]: 'aa' }
|
||||
] }
|
||||
nicknames: {
|
||||
[Op.or]: [
|
||||
{ [Op.contains]: 'aaa' },
|
||||
{ [Op.contains]: 'aa' }
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
const response = await agent.get('/users?filter[nicknames.$anyOf]=aaa,aa');
|
||||
expect(response.body.count).toBe(1);
|
||||
expect(response.body.rows[0].name).toBe(expected.name);
|
||||
});
|
||||
|
||||
|
||||
it('$anyOf for some element not in definition', async () => {
|
||||
const User = db.getModel('users');
|
||||
const expected = await User.findOne({
|
||||
@ -209,7 +211,7 @@ describe('list', () => {
|
||||
expect(response.body.count).toBe(1);
|
||||
expect(response.body.rows[0].name).toBe(expected.name);
|
||||
});
|
||||
|
||||
|
||||
it('$anyOf for no element', async () => {
|
||||
const User = db.getModel('users');
|
||||
const expected = await User.findAll();
|
||||
@ -334,27 +336,27 @@ describe('list', () => {
|
||||
rows: Array(20).fill(null).map((_, index) => ({ title: `title${index}` })),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('page 1 by size(1) should be ok', async () => {
|
||||
const response = await agent.get('/posts?fields=title&page=1&perPage=1');
|
||||
expect(response.body).toEqual({
|
||||
count: 25,
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
rows: [ { title: 'title0' } ],
|
||||
rows: [{ title: 'title0' }],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('page 2 by size(1) should be ok', async () => {
|
||||
const response = await agent.get('/posts?fields=title&page=2&per_page=1');
|
||||
expect(response.body).toEqual({
|
||||
count: 25,
|
||||
page: 2,
|
||||
per_page: 1,
|
||||
rows: [ { title: 'title1' } ],
|
||||
rows: [{ title: 'title1' }],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('page 1 by size(101) should be change to 100', async () => {
|
||||
const response = await agent.get('/posts?fields=title&page=1&per_page=101');
|
||||
expect(response.body).toEqual({
|
||||
@ -364,7 +366,7 @@ describe('list', () => {
|
||||
rows: Array(25).fill(null).map((_, index) => ({ title: `title${index}` })),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('page 2 by size(101) should be change to 100 and result is empty', async () => {
|
||||
const response = await agent.get('/posts?fields=title&page=2&per_page=101');
|
||||
expect(response.body).toEqual({
|
||||
@ -374,7 +376,7 @@ describe('list', () => {
|
||||
rows: [],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('default page by size(-1) should be change to 100 and result will be 25 items', async () => {
|
||||
const response = await agent.get('/posts?fields=title&per_page=-1');
|
||||
expect(response.body).toEqual({
|
||||
@ -384,7 +386,7 @@ describe('list', () => {
|
||||
rows: Array(25).fill(null).map((_, index) => ({ title: `title${index}` })),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('page 2 by size(-1) should be change to 100 and result is empty', async () => {
|
||||
const response = await agent.get('/posts?fields=title&page=2&per_page=-1');
|
||||
expect(response.body).toEqual({
|
||||
@ -395,7 +397,7 @@ describe('list', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('fields', () => {
|
||||
it('custom field', async () => {
|
||||
const response = await agent.get('/posts?fields=title&filter[customTitle]=title0');
|
||||
@ -403,7 +405,7 @@ describe('list', () => {
|
||||
count: 1,
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
rows: [ { title: 'title0' } ]
|
||||
rows: [{ title: 'title0' }]
|
||||
});
|
||||
});
|
||||
|
||||
@ -459,7 +461,8 @@ describe('list', () => {
|
||||
const response = await agent.get('/posts?fields[only]=title&fields[appends]=user.name&filter[title]=title0');
|
||||
expect(response.body.rows[0].user.name).toEqual('a');
|
||||
expect(response.body.rows).toEqual([{
|
||||
title: 'title0', user: { id: 1, nicknames: ['aa', 'aaa'], name: 'a', ...timestampsStrings } }]);
|
||||
title: 'title0', user: { id: 1, nicknames: ['aa', 'aaa'], name: 'a', ...timestampsStrings }
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -490,7 +493,7 @@ describe('list', () => {
|
||||
const response = await agent
|
||||
.get(`/posts/${post.id}/comments?page=2&perPage=2&sort=content&fields=content&filter[published]=1`);
|
||||
expect(response.body).toEqual({
|
||||
rows: [ { content: 'content5' } ],
|
||||
rows: [{ content: 'content5' }],
|
||||
count: 3,
|
||||
page: 2,
|
||||
per_page: 2
|
||||
@ -503,7 +506,7 @@ describe('list', () => {
|
||||
expect(response.body).toEqual({
|
||||
rows: [{
|
||||
title: null,
|
||||
comments: [{ content: 'content4' }, { content: 'content2' }, { content: 'content0'}]
|
||||
comments: [{ content: 'content4' }, { content: 'content2' }, { content: 'content0' }]
|
||||
}],
|
||||
count: 1,
|
||||
page: 1,
|
||||
@ -516,7 +519,7 @@ describe('list', () => {
|
||||
const post = await Post.findByPk(1);
|
||||
const response = await agent
|
||||
.get(`/posts/${post.id}/comments?fields=content,user.name&filter[status]=draft&sort=-content&page=1&perPage=2`);
|
||||
|
||||
|
||||
expect(response.body).toEqual({
|
||||
count: 3,
|
||||
page: 1,
|
||||
@ -551,9 +554,9 @@ describe('list', () => {
|
||||
|
||||
it('count field in hasMany', async () => {
|
||||
try {
|
||||
const response = await agent
|
||||
.get(`/users/1?fields=name,posts_count`);
|
||||
console.log(response.body);
|
||||
const response = await agent
|
||||
.get(`/users/1?fields=name,posts_count`);
|
||||
console.log(response.body);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
@ -561,9 +564,9 @@ describe('list', () => {
|
||||
|
||||
it('count field in hasMany', async () => {
|
||||
try {
|
||||
const response = await agent
|
||||
.get(`/users/1/posts?fields=title,comments_count`);
|
||||
console.log(response.body);
|
||||
const response = await agent
|
||||
.get(`/users/1/posts?fields=title,comments_count`);
|
||||
console.log(response.body);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
@ -574,24 +577,24 @@ describe('list', () => {
|
||||
beforeEach(async () => {
|
||||
const Tag = db.getModel('tags');
|
||||
const tags = await Tag.bulkCreate([
|
||||
{name: 'tag1', status: 'published'},
|
||||
{name: 'tag2', status: 'draft'},
|
||||
{name: 'tag3', status: 'published'},
|
||||
{name: 'tag4', status: 'draft'},
|
||||
{name: 'tag5', status: 'published'},
|
||||
{name: 'tag6', status: 'draft'},
|
||||
{name: 'tag7', status: 'published'},
|
||||
{name: 'tag8', status: 'published'},
|
||||
{name: 'tag9', status: 'draft'},
|
||||
{name: 'tag10', status: 'published'},
|
||||
{ name: 'tag1', status: 'published' },
|
||||
{ name: 'tag2', status: 'draft' },
|
||||
{ name: 'tag3', status: 'published' },
|
||||
{ name: 'tag4', status: 'draft' },
|
||||
{ name: 'tag5', status: 'published' },
|
||||
{ name: 'tag6', status: 'draft' },
|
||||
{ name: 'tag7', status: 'published' },
|
||||
{ name: 'tag8', status: 'published' },
|
||||
{ name: 'tag9', status: 'draft' },
|
||||
{ name: 'tag10', status: 'published' },
|
||||
]);
|
||||
const Post = db.getModel('posts');
|
||||
const [post1, post2] = await Post.bulkCreate([{}, {}]);
|
||||
await post1.updateAssociations({
|
||||
tags: [1,2,3,4,5,6,7]
|
||||
tags: [1, 2, 3, 4, 5, 6, 7]
|
||||
});
|
||||
await post2.updateAssociations({
|
||||
tags: [2,5,8]
|
||||
tags: [2, 5, 8]
|
||||
});
|
||||
const User = db.getModel('users');
|
||||
const user = await User.create();
|
||||
@ -606,7 +609,7 @@ describe('list', () => {
|
||||
const response = await agent
|
||||
.get(`/posts/${post.id}/tags?page=2&perPage=2&sort=-name&fields=name&filter[status]=published`);
|
||||
expect(response.body).toEqual({
|
||||
rows: [ { name: 'tag3' }, { name: 'tag1' } ],
|
||||
rows: [{ name: 'tag3' }, { name: 'tag1' }],
|
||||
count: 4,
|
||||
page: 2,
|
||||
per_page: 2
|
||||
|
@ -5,7 +5,7 @@ import { initDatabase, agent, resourcer } from './index';
|
||||
|
||||
describe('list', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeAll(async () => {
|
||||
resourcer.define({
|
||||
name: 'articles',
|
||||
@ -43,7 +43,7 @@ describe('list', () => {
|
||||
force: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
it('create', async () => {
|
||||
@ -58,7 +58,7 @@ describe('list', () => {
|
||||
it('list', async () => {
|
||||
const response = await agent.get('/articles?fields=title&page=1');
|
||||
expect(response.body).toEqual({
|
||||
data: [ { title: 'title1' } ],
|
||||
data: [{ title: 'title1' }],
|
||||
meta: { count: 1, page: 1, per_page: 20 }
|
||||
});
|
||||
});
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('remove', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
it('hasOne1', async () => {
|
||||
@ -28,7 +28,7 @@ describe('remove', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
comments: [
|
||||
{content: 'content111222'},
|
||||
{ content: 'content111222' },
|
||||
],
|
||||
});
|
||||
let [comment] = await post.getComments();
|
||||
@ -42,7 +42,7 @@ describe('remove', () => {
|
||||
const Post = db.getModel('posts');
|
||||
let post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
user: {name: 'name121234'},
|
||||
user: { name: 'name121234' },
|
||||
});
|
||||
await agent.post(`/posts/${post.id}/user:remove`);
|
||||
post = await Post.findOne({
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('set', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
it('belongsTo1', async () => {
|
||||
@ -28,8 +28,8 @@ describe('set', () => {
|
||||
it.skip('belongsToMany1', async () => {
|
||||
const [Post, Tag] = db.getModels(['posts', 'tags']);
|
||||
let post = await Post.create();
|
||||
let tag1 = await Tag.create({name: 'tag1'});
|
||||
let tag2 = await Tag.create({name: 'tag2'});
|
||||
let tag1 = await Tag.create({ name: 'tag1' });
|
||||
let tag2 = await Tag.create({ name: 'tag2' });
|
||||
await agent.post(`/posts/${post.id}/tags:set/${tag1.id}`);
|
||||
// 单独跑 ok,和上面的 it 一起跑就无法获取到
|
||||
const tags = await post.getTags();
|
||||
|
@ -2,7 +2,7 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('get', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
const User = db.getModel('users');
|
||||
@ -23,7 +23,7 @@ describe('get', () => {
|
||||
}))
|
||||
})), Promise.resolve());
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
describe('sort value initialization', () => {
|
||||
@ -200,7 +200,7 @@ describe('get', () => {
|
||||
field: 'sort_in_status',
|
||||
target: { id: 8 },
|
||||
});
|
||||
|
||||
|
||||
const Post = db.getModel('posts');
|
||||
const posts = await Post.findAll({
|
||||
where: {
|
||||
|
@ -2,11 +2,11 @@ import { initDatabase, agent } from './index';
|
||||
|
||||
describe('update', () => {
|
||||
let db;
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
db = await initDatabase();
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => db.close());
|
||||
|
||||
describe('common', () => {
|
||||
@ -86,7 +86,7 @@ describe('update', () => {
|
||||
|
||||
const result = await agent
|
||||
.get(`/posts/${post.id}`);
|
||||
|
||||
|
||||
expect(result.body.title).toBe(null);
|
||||
expect(result.body.meta).toEqual({
|
||||
location: 'Kunming'
|
||||
@ -106,7 +106,7 @@ describe('update', () => {
|
||||
|
||||
const result = await agent
|
||||
.get(`/posts/${post.id}`);
|
||||
|
||||
|
||||
expect(result.body.title).toBe('title11112222');
|
||||
expect(result.body.meta).toBe(null);
|
||||
});
|
||||
@ -140,12 +140,12 @@ describe('update', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
comments: [
|
||||
{content: 'content111222'},
|
||||
{ content: 'content111222' },
|
||||
],
|
||||
});
|
||||
const [comment] = await post.getComments();
|
||||
const response = await agent
|
||||
.put(`/posts/${post.id}/comments/${comment.id}`).send({content: 'content111222333'});
|
||||
.put(`/posts/${post.id}/comments/${comment.id}`).send({ content: 'content111222333' });
|
||||
expect(response.body.post_id).toBe(post.id);
|
||||
expect(response.body.content).toBe('content111222333');
|
||||
});
|
||||
@ -154,10 +154,10 @@ describe('update', () => {
|
||||
const Post = db.getModel('posts');
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
user: {name: 'name121234'},
|
||||
user: { name: 'name121234' },
|
||||
});
|
||||
const response = await agent
|
||||
.post(`/posts/${post.id}/user:update`).send({name: 'name1212345'});
|
||||
.post(`/posts/${post.id}/user:update`).send({ name: 'name1212345' });
|
||||
expect(response.body.name).toEqual('name1212345');
|
||||
});
|
||||
|
||||
@ -166,7 +166,7 @@ describe('update', () => {
|
||||
const post = await Post.create();
|
||||
await post.updateAssociations({
|
||||
tags: [
|
||||
{name: 'tag112233'},
|
||||
{ name: 'tag112233' },
|
||||
],
|
||||
});
|
||||
const [tag] = await post.getTags();
|
||||
|
@ -24,8 +24,8 @@ export async function set(ctx: Context, next: Next) {
|
||||
resourceField,
|
||||
associatedName,
|
||||
} = ctx.action.params as {
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
resourceField: Relation,
|
||||
values: any,
|
||||
};
|
||||
@ -64,8 +64,8 @@ export async function add(ctx: Context, next: Next) {
|
||||
resourceField,
|
||||
associatedName,
|
||||
} = ctx.action.params as {
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
resourceField: Relation,
|
||||
values: any,
|
||||
};
|
||||
@ -106,8 +106,8 @@ export async function remove(ctx: Context, next: Next) {
|
||||
resourceField,
|
||||
associatedName,
|
||||
} = ctx.action.params as {
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
resourceField: Relation,
|
||||
values: any,
|
||||
};
|
||||
@ -115,7 +115,7 @@ export async function remove(ctx: Context, next: Next) {
|
||||
if (!(associated instanceof AssociatedModel)) {
|
||||
throw new Error(`${associatedName} associated model invalid`);
|
||||
}
|
||||
const {get: getAccessor, remove: removeAccessor, set: setAccessor} = resourceField.getAccessors();
|
||||
const { get: getAccessor, remove: removeAccessor, set: setAccessor } = resourceField.getAccessors();
|
||||
const { resourceKey, resourceKeyAttribute, fields = [] } = ctx.action.params;
|
||||
const TargetModel = ctx.db.getModel(resourceField.getTarget());
|
||||
const options = TargetModel.parseApiJson({
|
||||
@ -132,7 +132,7 @@ export async function remove(ctx: Context, next: Next) {
|
||||
context: ctx,
|
||||
});
|
||||
await associated[removeAccessor](model);
|
||||
ctx.body = {id: model.id};
|
||||
ctx.body = { id: model.id };
|
||||
}
|
||||
await next();
|
||||
}
|
||||
@ -143,8 +143,8 @@ export async function toggle(ctx: Context, next: Next) {
|
||||
resourceField,
|
||||
associatedName,
|
||||
} = ctx.action.params as {
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
associated: Model,
|
||||
associatedName: string,
|
||||
resourceField: Relation,
|
||||
values: any,
|
||||
};
|
||||
@ -152,7 +152,7 @@ export async function toggle(ctx: Context, next: Next) {
|
||||
if (!(associated instanceof AssociatedModel)) {
|
||||
throw new Error(`${associatedName} associated model invalid`);
|
||||
}
|
||||
const {get: getAccessor, remove: removeAccessor, set: setAccessor, add: addAccessor} = resourceField.getAccessors();
|
||||
const { get: getAccessor, remove: removeAccessor, set: setAccessor, add: addAccessor } = resourceField.getAccessors();
|
||||
const { resourceKey, resourceKeyAttribute, fields = [] } = ctx.action.params;
|
||||
const TargetModel = ctx.db.getModel(resourceField.getTarget());
|
||||
const options = TargetModel.parseApiJson({
|
||||
|
@ -2,7 +2,7 @@ import { Utils, Op, Sequelize } from 'sequelize';
|
||||
import _ from 'lodash';
|
||||
import { Context, Next } from '.';
|
||||
import {
|
||||
Model,
|
||||
Model,
|
||||
HASONE,
|
||||
HASMANY,
|
||||
BELONGSTO,
|
||||
@ -187,7 +187,7 @@ export async function list(ctx: Context, next: Next) {
|
||||
}
|
||||
// const getAccessor = resourceField.getAccessors().get;
|
||||
// const countAccessor = resourceField.getAccessors().count;
|
||||
options.scope = options.scopes||[];
|
||||
options.scope = options.scopes || [];
|
||||
const association = AssociatedModel.associations[resourceField.options.name];
|
||||
if (resourceField instanceof BELONGSTOMANY) {
|
||||
data = await belongsToManyGet.call(association, associated, {
|
||||
@ -213,7 +213,7 @@ export async function list(ctx: Context, next: Next) {
|
||||
fields,
|
||||
context: ctx,
|
||||
});
|
||||
data = await Model.scope(options.scopes||[]).findAndCountAll({
|
||||
data = await Model.scope(options.scopes || []).findAndCountAll({
|
||||
...options,
|
||||
// @ts-ignore hooks 里添加 context
|
||||
context: ctx,
|
||||
@ -288,7 +288,7 @@ export async function get(ctx: Context, next: Next) {
|
||||
resourceKeyAttribute,
|
||||
fields = []
|
||||
} = ctx.action.params;
|
||||
console.log({associated, resourceField})
|
||||
console.log({ associated, resourceField })
|
||||
if (associated && resourceField) {
|
||||
const AssociatedModel = ctx.db.getModel(associatedName);
|
||||
if (!(associated instanceof AssociatedModel)) {
|
||||
@ -461,7 +461,7 @@ export async function destroy(ctx: Context, next: Next) {
|
||||
await transaction.rollback();
|
||||
throw new Error(`${associatedName} associated model invalid`);
|
||||
}
|
||||
const {get: getAccessor, remove: removeAccessor, set: setAccessor} = resourceField.getAccessors();
|
||||
const { get: getAccessor, remove: removeAccessor, set: setAccessor } = resourceField.getAccessors();
|
||||
const TargetModel = ctx.db.getModel(resourceField.getTarget());
|
||||
const { where } = TargetModel.parseApiJson({ filter, context: ctx });
|
||||
if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) {
|
||||
|
@ -45,7 +45,7 @@ const api = Api.create({
|
||||
});
|
||||
|
||||
api.resourcer.use(associated);
|
||||
api.resourcer.registerActionHandlers({...actions.common, ...actions.associate});
|
||||
api.resourcer.registerActionHandlers({ ...actions.common, ...actions.associate });
|
||||
|
||||
// api.resourcer.use(async (ctx: actions.Context, next) => {
|
||||
// const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
|
||||
|
@ -21,8 +21,8 @@ export default {
|
||||
interface: 'radio',
|
||||
title: '性别',
|
||||
dataSource: [
|
||||
{value: 'male', label: '男性'},
|
||||
{value: 'female', label: '女性'},
|
||||
{ value: 'male', label: '男性' },
|
||||
{ value: 'female', label: '女性' },
|
||||
],
|
||||
component: {
|
||||
showInTable: true,
|
||||
|
@ -76,9 +76,9 @@ export default {
|
||||
interface: 'select',
|
||||
title: '下拉',
|
||||
dataSource: [
|
||||
{value: 'value1', label: '选项1'},
|
||||
{value: 'value2', label: '选项2'},
|
||||
{value: 'value3', label: '选项3'},
|
||||
{ value: 'value1', label: '选项1' },
|
||||
{ value: 'value2', label: '选项2' },
|
||||
{ value: 'value3', label: '选项3' },
|
||||
],
|
||||
component: {
|
||||
showInTable: true,
|
||||
@ -90,9 +90,9 @@ export default {
|
||||
interface: 'multipleSelect',
|
||||
title: '下拉多选',
|
||||
dataSource: [
|
||||
{value: 'value1', label: '选项1'},
|
||||
{value: 'value2', label: '选项2'},
|
||||
{value: 'value3', label: '选项3'},
|
||||
{ value: 'value1', label: '选项1' },
|
||||
{ value: 'value2', label: '选项2' },
|
||||
{ value: 'value3', label: '选项3' },
|
||||
],
|
||||
component: {
|
||||
showInTable: true,
|
||||
@ -104,9 +104,9 @@ export default {
|
||||
interface: 'radio',
|
||||
title: '单选框',
|
||||
dataSource: [
|
||||
{value: 'value1', label: '选项1'},
|
||||
{value: 'value2', label: '选项2'},
|
||||
{value: 'value3', label: '选项3'},
|
||||
{ value: 'value1', label: '选项1' },
|
||||
{ value: 'value2', label: '选项2' },
|
||||
{ value: 'value3', label: '选项3' },
|
||||
],
|
||||
component: {
|
||||
showInTable: true,
|
||||
@ -118,9 +118,9 @@ export default {
|
||||
interface: 'checkboxes',
|
||||
title: '多选框',
|
||||
dataSource: [
|
||||
{value: 'value1', label: '选项1'},
|
||||
{value: 'value2', label: '选项2'},
|
||||
{value: 'value3', label: '选项3'},
|
||||
{ value: 'value1', label: '选项1' },
|
||||
{ value: 'value2', label: '选项2' },
|
||||
{ value: 'value3', label: '选项3' },
|
||||
],
|
||||
component: {
|
||||
showInTable: true,
|
||||
|
@ -3,7 +3,7 @@ import Database from '@nocobase/database';
|
||||
|
||||
(async () => {
|
||||
await api.loadPlugins();
|
||||
await api.database.getModel('collections').load({skipExisting: true});
|
||||
await api.database.getModel('collections').load({ skipExisting: true });
|
||||
const database: Database = api.database;
|
||||
const [Field] = database.getModels(['fields']);
|
||||
|
||||
|
@ -26,54 +26,65 @@ interface Resource {
|
||||
|
||||
class ApiClient {
|
||||
resource(name: string): Resource {
|
||||
const proxy: any = new Proxy({}, {
|
||||
get(target, method, receiver) {
|
||||
return (params: ActionParams = {}) => {
|
||||
let { associatedKey, resourceKey, filter, sorter, sort = [], values, ...restParams } = params;
|
||||
let url = `/${name}`;
|
||||
sort = sort || [];
|
||||
let options: any = {
|
||||
params: {},
|
||||
const proxy: any = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(target, method, receiver) {
|
||||
return (params: ActionParams = {}) => {
|
||||
let {
|
||||
associatedKey,
|
||||
resourceKey,
|
||||
filter,
|
||||
sorter,
|
||||
sort = [],
|
||||
values,
|
||||
...restParams
|
||||
} = params;
|
||||
let url = `/${name}`;
|
||||
sort = sort || [];
|
||||
let options: any = {
|
||||
params: {},
|
||||
};
|
||||
if (['list', 'get'].indexOf(method as string) !== -1) {
|
||||
options.method = 'get';
|
||||
options.params = restParams;
|
||||
} else {
|
||||
options.method = 'post';
|
||||
options.params = restParams;
|
||||
options.data = values;
|
||||
}
|
||||
if (associatedKey) {
|
||||
url = `/${name.split('.').join(`/${associatedKey}/`)}`;
|
||||
}
|
||||
url += `:${method as string}`;
|
||||
// console.log(name, name.split('.'), associatedKey, name.split('.').join(`/${associatedKey}/`));
|
||||
if (resourceKey) {
|
||||
url += `/${resourceKey}`;
|
||||
}
|
||||
if (filter) {
|
||||
options.params['filter'] = JSON.stringify(filter);
|
||||
}
|
||||
if (sorter) {
|
||||
const arr = Array.isArray(sorter) ? sorter : [sorter];
|
||||
arr.forEach(({ order, field }) => {
|
||||
if (order === 'descend') {
|
||||
sort.push(`-${field}`);
|
||||
} else if (order === 'ascend') {
|
||||
sort.push(field);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sort.length === 0) {
|
||||
delete options.params['sort'];
|
||||
} else {
|
||||
options.params['sort'] = sort.join(',');
|
||||
}
|
||||
console.log({ url, params });
|
||||
return request(url, options);
|
||||
};
|
||||
if (['list', 'get'].indexOf(method as string) !== -1) {
|
||||
options.method = 'get';
|
||||
options.params = restParams;
|
||||
} else {
|
||||
options.method = 'post';
|
||||
options.params = restParams;
|
||||
options.data = values;
|
||||
}
|
||||
if (associatedKey) {
|
||||
url = `/${name.split('.').join(`/${associatedKey}/`)}`;
|
||||
}
|
||||
url += `:${method as string}`;
|
||||
// console.log(name, name.split('.'), associatedKey, name.split('.').join(`/${associatedKey}/`));
|
||||
if (resourceKey) {
|
||||
url += `/${resourceKey}`;
|
||||
}
|
||||
if (filter) {
|
||||
options.params['filter'] = JSON.stringify(filter);
|
||||
}
|
||||
if (sorter) {
|
||||
const arr = Array.isArray(sorter) ? sorter : [sorter];
|
||||
arr.forEach(({order, field}) => {
|
||||
if (order === 'descend') {
|
||||
sort.push(`-${field}`);
|
||||
} else if (order === 'ascend') {
|
||||
sort.push(field);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sort.length === 0) {
|
||||
delete options.params['sort'];
|
||||
} else {
|
||||
options.params['sort'] = sort.join(',');
|
||||
}
|
||||
console.log({url, params});
|
||||
return request(url, options);
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ configResponsive({
|
||||
export const request: RequestConfig = {
|
||||
prefix: process.env.API,
|
||||
errorConfig: {
|
||||
adaptor: (resData) => {
|
||||
adaptor: resData => {
|
||||
return {
|
||||
...resData,
|
||||
success: true,
|
||||
@ -28,23 +28,21 @@ export const request: RequestConfig = {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
await next();
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const pathnames = [
|
||||
'/login',
|
||||
'/register',
|
||||
'/lostpassword',
|
||||
'/resetpassword',
|
||||
];
|
||||
const pathnames = ['/login', '/register', '/lostpassword', '/resetpassword'];
|
||||
|
||||
export async function getInitialState() {
|
||||
const { pathname, search } = location;
|
||||
console.log(location);
|
||||
const { data: systemSettings = {} } = await umiRequest('/system_settings:get?fields[appends]=logo,logo.storage', {
|
||||
method: 'get',
|
||||
});
|
||||
const { data: systemSettings = {} } = await umiRequest(
|
||||
'/system_settings:get?fields[appends]=logo,logo.storage',
|
||||
{
|
||||
method: 'get',
|
||||
},
|
||||
);
|
||||
let redirect = `?redirect=${pathname}${search}`;
|
||||
|
||||
if (!pathnames.includes(pathname)) {
|
||||
@ -66,7 +64,7 @@ export async function getInitialState() {
|
||||
currentUser: data,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
history.push('/login' + redirect);
|
||||
}
|
||||
}
|
||||
@ -75,4 +73,4 @@ export async function getInitialState() {
|
||||
systemSettings,
|
||||
currentUser: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -27,17 +27,23 @@ export function Create(props) {
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<ViewFactory
|
||||
<ViewFactory
|
||||
{...props}
|
||||
reference={drawerRef}
|
||||
viewName={viewName}
|
||||
{...params}
|
||||
/>
|
||||
<Button icon={<PlusOutlined />} type={'primary'} onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
}}>{title}</Button>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Create;
|
||||
|
@ -10,14 +10,19 @@ export function Destroy(props) {
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<Popconfirm title="确认删除吗?" onConfirm={() => {
|
||||
<Popconfirm
|
||||
title="确认删除吗?"
|
||||
onConfirm={() => {
|
||||
console.log('destroy', onTrigger);
|
||||
onTrigger && onTrigger();
|
||||
}}>
|
||||
<Button icon={<DeleteOutlined />} type={'ghost'} danger>{title}</Button>
|
||||
}}
|
||||
>
|
||||
<Button icon={<DeleteOutlined />} type={'ghost'} danger>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Destroy;
|
||||
|
@ -8,7 +8,13 @@ export function Filter(props) {
|
||||
const drawerRef = useRef<any>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { title, viewName, collection_name } = props.schema;
|
||||
const { filterCount, activeTab = {}, item = {}, associatedName, associatedKey } = props;
|
||||
const {
|
||||
filterCount,
|
||||
activeTab = {},
|
||||
item = {},
|
||||
associatedName,
|
||||
associatedKey,
|
||||
} = props;
|
||||
const { associationField } = activeTab;
|
||||
|
||||
const params = {};
|
||||
@ -30,33 +36,40 @@ export function Filter(props) {
|
||||
trigger="click"
|
||||
visible={visible}
|
||||
placement={'bottomLeft'}
|
||||
onVisibleChange={(visible) => {
|
||||
onVisibleChange={visible => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
className={'filters-popover'}
|
||||
style={{
|
||||
}}
|
||||
style={{}}
|
||||
overlayStyle={{
|
||||
minWidth: 500
|
||||
minWidth: 500,
|
||||
}}
|
||||
content={(
|
||||
content={
|
||||
<>
|
||||
<div className={'popover-button-mask'} onClick={() => setVisible(false)}></div>
|
||||
<ViewFactory
|
||||
<div
|
||||
className={'popover-button-mask'}
|
||||
onClick={() => setVisible(false)}
|
||||
></div>
|
||||
<ViewFactory
|
||||
{...props}
|
||||
setVisible={setVisible}
|
||||
viewName={'filter'}
|
||||
{...params}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
}
|
||||
>
|
||||
<Button icon={<FilterOutlined />} onClick={() => {
|
||||
setVisible(true);
|
||||
}}>{filterCount ? `${filterCount} 个${title}项` : title}</Button>
|
||||
<Button
|
||||
icon={<FilterOutlined />}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
{filterCount ? `${filterCount} 个${title}项` : title}
|
||||
</Button>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Filter;
|
||||
|
@ -22,19 +22,25 @@ export function Update(props) {
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<ViewFactory
|
||||
<ViewFactory
|
||||
{...props}
|
||||
reference={drawerRef}
|
||||
viewName={viewName}
|
||||
mode={'update'}
|
||||
{...params}
|
||||
/>
|
||||
<Button icon={<EditOutlined />} type={'primary'} onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
drawerRef.current.getData(item.itemId || resourceKey);
|
||||
}}>{title}</Button>
|
||||
<Button
|
||||
icon={<EditOutlined />}
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
drawerRef.current.getData(item.itemId || resourceKey);
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Update;
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import React from 'react';
|
||||
import Create from './Create';
|
||||
import Update from './Update';
|
||||
@ -27,25 +26,27 @@ export function Action(props) {
|
||||
// cnsole.log(schema);
|
||||
const { type } = schema;
|
||||
const Component = getAction(type);
|
||||
return Component && <Component {...props}/>;
|
||||
return Component && <Component {...props} />;
|
||||
}
|
||||
|
||||
export function Actions(props) {
|
||||
const { onTrigger = {}, style, schema, actions = [], ...restProps } = props;
|
||||
console.log(onTrigger);
|
||||
return actions.length > 0 && (
|
||||
<div className={'action-buttons'} style={style}>
|
||||
{actions.map(action => (
|
||||
<div className={`${action.name}-action-button action-button`}>
|
||||
<Action
|
||||
{...restProps}
|
||||
view={schema}
|
||||
schema={action}
|
||||
onTrigger={onTrigger[action.name]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
return (
|
||||
actions.length > 0 && (
|
||||
<div className={'action-buttons'} style={style}>
|
||||
{actions.map(action => (
|
||||
<div className={`${action.name}-action-button action-button`}>
|
||||
<Action
|
||||
{...restProps}
|
||||
view={schema}
|
||||
schema={action}
|
||||
onTrigger={onTrigger[action.name]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -12,4 +12,4 @@
|
||||
left: 24px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
ISchemaFieldComponentProps,
|
||||
SchemaField
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { toArr, isFn, FormPath } from '@formily/shared'
|
||||
import { ArrayList } from '@formily/react-shared-components'
|
||||
import { CircleButton } from '../circle-button'
|
||||
import { TextButton } from '../text-button'
|
||||
import { Card } from 'antd'
|
||||
SchemaField,
|
||||
} from '@formily/react-schema-renderer';
|
||||
import { toArr, isFn, FormPath } from '@formily/shared';
|
||||
import { ArrayList } from '@formily/react-shared-components';
|
||||
import { CircleButton } from '../circle-button';
|
||||
import { TextButton } from '../text-button';
|
||||
import { Card } from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
UpOutlined
|
||||
} from '@ant-design/icons'
|
||||
import styled from 'styled-components'
|
||||
UpOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ArrayComponents = {
|
||||
CircleButton,
|
||||
@ -22,12 +22,12 @@ const ArrayComponents = {
|
||||
AdditionIcon: () => <PlusOutlined />,
|
||||
RemoveIcon: () => <DeleteOutlined />,
|
||||
MoveDownIcon: () => <DownOutlined />,
|
||||
MoveUpIcon: () => <UpOutlined />
|
||||
}
|
||||
MoveUpIcon: () => <UpOutlined />,
|
||||
};
|
||||
|
||||
export const ArrayCards: any = styled(
|
||||
(props: ISchemaFieldComponentProps & { className: string }) => {
|
||||
const { value, schema, className, editable, path, mutators } = props
|
||||
const { value, schema, className, editable, path, mutators } = props;
|
||||
const {
|
||||
renderAddition,
|
||||
renderRemove,
|
||||
@ -36,17 +36,17 @@ export const ArrayCards: any = styled(
|
||||
renderEmpty,
|
||||
renderExtraOperations,
|
||||
...componentProps
|
||||
} = schema.getExtendsComponentProps() || {}
|
||||
} = schema.getExtendsComponentProps() || {};
|
||||
|
||||
const schemaItems = Array.isArray(schema.items)
|
||||
? schema.items[schema.items.length - 1]
|
||||
: schema.items
|
||||
: schema.items;
|
||||
|
||||
const onAdd = () => {
|
||||
if (schemaItems) {
|
||||
mutators.push(schemaItems.getEmptyValue())
|
||||
mutators.push(schemaItems.getEmptyValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={className}>
|
||||
<ArrayList
|
||||
@ -60,7 +60,7 @@ export const ArrayCards: any = styled(
|
||||
renderRemove,
|
||||
renderMoveDown,
|
||||
renderMoveUp,
|
||||
renderEmpty
|
||||
renderEmpty,
|
||||
}}
|
||||
>
|
||||
{toArr(value).map((item, index) => {
|
||||
@ -72,7 +72,8 @@ export const ArrayCards: any = styled(
|
||||
key={index}
|
||||
title={
|
||||
<span>
|
||||
{index + 1}<span>.</span> {componentProps.title || schema.title}
|
||||
{index + 1}
|
||||
<span>.</span> {componentProps.title || schema.title}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
@ -102,7 +103,7 @@ export const ArrayCards: any = styled(
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
})}
|
||||
<ArrayList.Empty>
|
||||
{({ children, allowAddition }) => {
|
||||
@ -110,12 +111,14 @@ export const ArrayCards: any = styled(
|
||||
<Card
|
||||
{...componentProps}
|
||||
size="small"
|
||||
className={`card-list-item card-list-empty ${allowAddition ? 'add-pointer' : ''}`}
|
||||
className={`card-list-item card-list-empty ${
|
||||
allowAddition ? 'add-pointer' : ''
|
||||
}`}
|
||||
onClick={allowAddition ? onAdd : undefined}
|
||||
>
|
||||
<div className="empty-wrapper">{children}</div>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</ArrayList.Empty>
|
||||
<ArrayList.Addition>
|
||||
@ -125,14 +128,14 @@ export const ArrayCards: any = styled(
|
||||
<div className="array-cards-addition" onClick={onAdd}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
</ArrayList.Addition>
|
||||
</ArrayList>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
)<ISchemaFieldComponentProps>`
|
||||
width: 100%;
|
||||
.ant-card {
|
||||
@ -190,8 +193,8 @@ export const ArrayCards: any = styled(
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
`
|
||||
`;
|
||||
|
||||
ArrayCards.isFieldComponent = true
|
||||
ArrayCards.isFieldComponent = true;
|
||||
|
||||
export default ArrayCards
|
||||
export default ArrayCards;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/card/style/index'
|
||||
import 'antd/lib/card/style/index';
|
||||
|
@ -1,33 +1,37 @@
|
||||
import React, { useContext } from 'react'
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
ISchemaFieldComponentProps,
|
||||
SchemaField,
|
||||
Schema,
|
||||
complieExpression,
|
||||
FormExpressionScopeContext
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { toArr, isFn, isArr, FormPath } from '@formily/shared'
|
||||
import { ArrayList, DragListView } from '@formily/react-shared-components'
|
||||
import { CircleButton } from '../circle-button'
|
||||
import { TextButton } from '../text-button'
|
||||
import { Table, Form, Button } from 'antd'
|
||||
import { FormItemShallowProvider } from '@formily/antd'
|
||||
FormExpressionScopeContext,
|
||||
} from '@formily/react-schema-renderer';
|
||||
import { toArr, isFn, isArr, FormPath } from '@formily/shared';
|
||||
import { ArrayList, DragListView } from '@formily/react-shared-components';
|
||||
import { CircleButton } from '../circle-button';
|
||||
import { TextButton } from '../text-button';
|
||||
import { Table, Form, Button } from 'antd';
|
||||
import { FormItemShallowProvider } from '@formily/antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
UpOutlined
|
||||
} from '@ant-design/icons'
|
||||
import styled from 'styled-components'
|
||||
UpOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ArrayComponents = {
|
||||
CircleButton,
|
||||
TextButton,
|
||||
AdditionIcon: () => <><PlusOutlined/> 新增</>,
|
||||
AdditionIcon: () => (
|
||||
<>
|
||||
<PlusOutlined /> 新增
|
||||
</>
|
||||
),
|
||||
RemoveIcon: () => <DeleteOutlined />,
|
||||
MoveDownIcon: () => <DownOutlined />,
|
||||
MoveUpIcon: () => <UpOutlined />
|
||||
}
|
||||
MoveUpIcon: () => <UpOutlined />,
|
||||
};
|
||||
|
||||
const DragHandler = styled.span`
|
||||
width: 7px;
|
||||
@ -38,12 +42,12 @@ const DragHandler = styled.span`
|
||||
border-bottom: 0;
|
||||
cursor: move;
|
||||
margin-bottom: 24px;
|
||||
`
|
||||
`;
|
||||
|
||||
export const ArrayTable: any = styled(
|
||||
(props: ISchemaFieldComponentProps & { className: string }) => {
|
||||
const expressionScope = useContext(FormExpressionScopeContext)
|
||||
const { value, schema, className, editable, path, mutators } = props
|
||||
const expressionScope = useContext(FormExpressionScopeContext);
|
||||
const { value, schema, className, editable, path, mutators } = props;
|
||||
const {
|
||||
renderAddition,
|
||||
renderRemove,
|
||||
@ -55,31 +59,31 @@ export const ArrayTable: any = styled(
|
||||
operations,
|
||||
draggable,
|
||||
...componentProps
|
||||
} = schema.getExtendsComponentProps() || {}
|
||||
} = schema.getExtendsComponentProps() || {};
|
||||
const schemaItems = Array.isArray(schema.items)
|
||||
? schema.items[schema.items.length - 1]
|
||||
: schema.items
|
||||
: schema.items;
|
||||
const onAdd = () => {
|
||||
if (schemaItems) {
|
||||
mutators.push(schemaItems.getEmptyValue())
|
||||
mutators.push(schemaItems.getEmptyValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
const onMove = (dragIndex, dropIndex) => {
|
||||
mutators.move(dragIndex, dropIndex)
|
||||
}
|
||||
mutators.move(dragIndex, dropIndex);
|
||||
};
|
||||
const renderColumns = (items: Schema) => {
|
||||
return items.mapProperties((props, key) => {
|
||||
const itemProps = {
|
||||
...props.getExtendsItemProps(),
|
||||
...props.getExtendsProps()
|
||||
}
|
||||
...props.getExtendsProps(),
|
||||
};
|
||||
return {
|
||||
title: complieExpression(props.title, expressionScope),
|
||||
...itemProps,
|
||||
key,
|
||||
dataIndex: key,
|
||||
render: (value: any, record: any, index: number) => {
|
||||
const newPath = FormPath.parse(path).concat(index, key)
|
||||
const newPath = FormPath.parse(path).concat(index, key);
|
||||
return (
|
||||
<FormItemShallowProvider
|
||||
key={newPath.toString()}
|
||||
@ -89,19 +93,19 @@ export const ArrayTable: any = styled(
|
||||
>
|
||||
<SchemaField path={newPath} schema={props} />
|
||||
</FormItemShallowProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
// 兼容异步items schema传入
|
||||
let columns = []
|
||||
let columns = [];
|
||||
if (schema.items) {
|
||||
columns = isArr(schema.items)
|
||||
? schema.items.reduce((buf, items) => {
|
||||
return buf.concat(renderColumns(items))
|
||||
return buf.concat(renderColumns(items));
|
||||
}, [])
|
||||
: renderColumns(schema.items)
|
||||
: renderColumns(schema.items);
|
||||
}
|
||||
if (editable && operations !== false) {
|
||||
columns.push({
|
||||
@ -130,32 +134,32 @@ export const ArrayTable: any = styled(
|
||||
: renderExtraOperations}
|
||||
</div>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
if (draggable) {
|
||||
columns.unshift({
|
||||
width: 20,
|
||||
key: 'dragHandler',
|
||||
render: () => {
|
||||
return <DragHandler className="drag-handler" />
|
||||
}
|
||||
})
|
||||
return <DragHandler className="drag-handler" />;
|
||||
},
|
||||
});
|
||||
}
|
||||
const renderTable = () => {
|
||||
return (
|
||||
<Table
|
||||
{...componentProps}
|
||||
rowKey={record => {
|
||||
return toArr(value).indexOf(record)
|
||||
return toArr(value).indexOf(record);
|
||||
}}
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
dataSource={toArr(value)}
|
||||
></Table>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div className={className}>
|
||||
<ArrayList
|
||||
@ -169,7 +173,7 @@ export const ArrayTable: any = styled(
|
||||
renderRemove,
|
||||
renderMoveDown,
|
||||
renderMoveUp,
|
||||
renderEmpty
|
||||
renderEmpty,
|
||||
}}
|
||||
>
|
||||
{draggable ? (
|
||||
@ -191,13 +195,13 @@ export const ArrayTable: any = styled(
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
);
|
||||
}}
|
||||
</ArrayList.Addition>
|
||||
</ArrayList>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
)`
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
@ -227,8 +231,8 @@ export const ArrayTable: any = styled(
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
`
|
||||
`;
|
||||
|
||||
ArrayTable.isFieldComponent = true
|
||||
ArrayTable.isFieldComponent = true;
|
||||
|
||||
export default ArrayTable
|
||||
export default ArrayTable;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/table/style/index'
|
||||
import 'antd/lib/table/style/index';
|
||||
|
@ -1,11 +1,19 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Button, Select, DatePicker, Tag, InputNumber, TimePicker, Input } from 'antd';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
DatePicker,
|
||||
Tag,
|
||||
InputNumber,
|
||||
TimePicker,
|
||||
Input,
|
||||
} from 'antd';
|
||||
import {
|
||||
Select as AntdSelect,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
import moment from 'moment';
|
||||
import './style.less';
|
||||
import api from '@/api-client';
|
||||
@ -14,12 +22,12 @@ import { useRequest } from 'umi';
|
||||
export const DateTime = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})((props) => {
|
||||
})(props => {
|
||||
const { associatedKey, automationType, filter, onChange } = props;
|
||||
const [aKey, setaKey] = useState(associatedKey);
|
||||
const [aType, setaType] = useState(automationType);
|
||||
console.log('Automations.DateTime', aKey, associatedKey)
|
||||
const [value, setValue] = useState(props.value||{});
|
||||
console.log('Automations.DateTime', aKey, associatedKey);
|
||||
const [value, setValue] = useState(props.value || {});
|
||||
const [offsetType, setOffsetType] = useState(() => {
|
||||
if (!value.offset) {
|
||||
return 'current';
|
||||
@ -31,8 +39,8 @@ export const DateTime = connect({
|
||||
return 'before';
|
||||
}
|
||||
return 'current';
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (associatedKey !== aKey || automationType !== aType) {
|
||||
setOffsetType('current');
|
||||
@ -48,90 +56,121 @@ export const DateTime = connect({
|
||||
offset: 0,
|
||||
unit: undefined,
|
||||
});
|
||||
setaKey(associatedKey)
|
||||
setaKey(associatedKey);
|
||||
}
|
||||
}, [ associatedKey, automationType, aKey, aType ]);
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return associatedKey && automationType !== 'schedule' ? api.resource('collections.fields').list({
|
||||
associatedKey,
|
||||
filter: filter||{
|
||||
type: 'date',
|
||||
},
|
||||
}) : Promise.resolve({data: []});
|
||||
}, {
|
||||
refreshDeps: [associatedKey, automationType, filter]
|
||||
});
|
||||
console.log({data});
|
||||
}, [associatedKey, automationType, aKey, aType]);
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return associatedKey && automationType !== 'schedule'
|
||||
? api.resource('collections.fields').list({
|
||||
associatedKey,
|
||||
filter: filter || {
|
||||
type: 'date',
|
||||
},
|
||||
})
|
||||
: Promise.resolve({ data: [] });
|
||||
},
|
||||
{
|
||||
refreshDeps: [associatedKey, automationType, filter],
|
||||
},
|
||||
);
|
||||
console.log({ data });
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{automationType === 'schedule' ? (
|
||||
<DatePicker showTime onChange={(m, dateString) => {
|
||||
onChange({value: m.toISOString()});
|
||||
setValue({value: m.toISOString()});
|
||||
// console.log('Automations.DateTime', m.toISOString(), {m, dateString})
|
||||
}} defaultValue={(() => {
|
||||
if (!value.value) {
|
||||
return undefined;
|
||||
}
|
||||
const m = moment(value.value);
|
||||
return m.isValid() ? m : undefined;
|
||||
})()}/>
|
||||
<DatePicker
|
||||
showTime
|
||||
onChange={(m, dateString) => {
|
||||
onChange({ value: m.toISOString() });
|
||||
setValue({ value: m.toISOString() });
|
||||
// console.log('Automations.DateTime', m.toISOString(), {m, dateString})
|
||||
}}
|
||||
defaultValue={(() => {
|
||||
if (!value.value) {
|
||||
return undefined;
|
||||
}
|
||||
const m = moment(value.value);
|
||||
return m.isValid() ? m : undefined;
|
||||
})()}
|
||||
/>
|
||||
) : (
|
||||
<Input.Group compact>
|
||||
<Select style={{width: 120}} value={value.byField} onChange={(v) => {
|
||||
setValue({...value, byField: v});
|
||||
onChange({...value, byField: v});
|
||||
}} loading={loading} options={data.map(item => ({
|
||||
value: item.name,
|
||||
label: item.title||item.name,
|
||||
}))} placeholder={'选择日期字段'}></Select>
|
||||
<Select onChange={(offsetType) => {
|
||||
let values = {...value};
|
||||
switch (offsetType) {
|
||||
case 'current':
|
||||
values = {byField: values.byField, offset: 0};
|
||||
break;
|
||||
case 'before':
|
||||
if (values.offset) {
|
||||
values.offset = -1 * Math.abs(values.offset);
|
||||
}
|
||||
break;
|
||||
case 'after':
|
||||
if (values.offset) {
|
||||
values.offset = Math.abs(values.offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
setOffsetType(offsetType);
|
||||
setValue(values);
|
||||
onChange(values);
|
||||
}} value={offsetType} placeholder={'选择日期字段'}>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
value={value.byField}
|
||||
onChange={v => {
|
||||
setValue({ ...value, byField: v });
|
||||
onChange({ ...value, byField: v });
|
||||
}}
|
||||
loading={loading}
|
||||
options={data.map(item => ({
|
||||
value: item.name,
|
||||
label: item.title || item.name,
|
||||
}))}
|
||||
placeholder={'选择日期字段'}
|
||||
></Select>
|
||||
<Select
|
||||
onChange={offsetType => {
|
||||
let values = { ...value };
|
||||
switch (offsetType) {
|
||||
case 'current':
|
||||
values = { byField: values.byField, offset: 0 };
|
||||
break;
|
||||
case 'before':
|
||||
if (values.offset) {
|
||||
values.offset = -1 * Math.abs(values.offset);
|
||||
}
|
||||
break;
|
||||
case 'after':
|
||||
if (values.offset) {
|
||||
values.offset = Math.abs(values.offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
setOffsetType(offsetType);
|
||||
setValue(values);
|
||||
onChange(values);
|
||||
}}
|
||||
value={offsetType}
|
||||
placeholder={'选择日期字段'}
|
||||
>
|
||||
<Select.Option value={'current'}>当天</Select.Option>
|
||||
<Select.Option value={'before'}>之前</Select.Option>
|
||||
<Select.Option value={'after'}>之后</Select.Option>
|
||||
</Select>
|
||||
{offsetType !== 'current' && (
|
||||
<InputNumber step={1} min={1} value={Math.abs(value.offset)||undefined} onChange={(offset: number) => {
|
||||
const values = {
|
||||
unit: 'day',...value,
|
||||
}
|
||||
if (offsetType === 'before') {
|
||||
values.offset = -1 * Math.abs(offset);
|
||||
} else if (offsetType === 'after') {
|
||||
values.offset = Math.abs(offset);
|
||||
}
|
||||
setValue(values);
|
||||
onChange(values);
|
||||
console.log('Automations.DateTime', values)
|
||||
// console.log(offsetType);
|
||||
}} placeholder={'数字'}/>
|
||||
<InputNumber
|
||||
step={1}
|
||||
min={1}
|
||||
value={Math.abs(value.offset) || undefined}
|
||||
onChange={(offset: number) => {
|
||||
const values = {
|
||||
unit: 'day',
|
||||
...value,
|
||||
};
|
||||
if (offsetType === 'before') {
|
||||
values.offset = -1 * Math.abs(offset);
|
||||
} else if (offsetType === 'after') {
|
||||
values.offset = Math.abs(offset);
|
||||
}
|
||||
setValue(values);
|
||||
onChange(values);
|
||||
console.log('Automations.DateTime', values);
|
||||
// console.log(offsetType);
|
||||
}}
|
||||
placeholder={'数字'}
|
||||
/>
|
||||
)}
|
||||
{offsetType !== 'current' && (
|
||||
<Select onChange={(v) => {
|
||||
setValue({...value, unit: v});
|
||||
onChange({...value, unit: v});
|
||||
}} value={value.unit} placeholder={'选择单位'}>
|
||||
<Select
|
||||
onChange={v => {
|
||||
setValue({ ...value, unit: v });
|
||||
onChange({ ...value, unit: v });
|
||||
}}
|
||||
value={value.unit}
|
||||
placeholder={'选择单位'}
|
||||
>
|
||||
<Select.Option value={'second'}>秒</Select.Option>
|
||||
<Select.Option value={'minute'}>分钟</Select.Option>
|
||||
<Select.Option value={'hour'}>小时</Select.Option>
|
||||
@ -140,22 +179,27 @@ export const DateTime = connect({
|
||||
<Select.Option value={'month'}>月</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
{offsetType !== 'current' && value.unit && ['day', 'week', 'month'].indexOf(value.unit) !== -1 && (
|
||||
<TimePicker value={(() => {
|
||||
const m = moment(value.time, 'HH:mm:ss');
|
||||
return m.isValid() ? m : undefined;
|
||||
})()} onChange={(m, dateString) => {
|
||||
console.log('Automations.DateTime', m, dateString)
|
||||
setValue({...value, time: dateString});
|
||||
onChange({...value, time: dateString});
|
||||
}}/>
|
||||
)}
|
||||
{offsetType !== 'current' &&
|
||||
value.unit &&
|
||||
['day', 'week', 'month'].indexOf(value.unit) !== -1 && (
|
||||
<TimePicker
|
||||
value={(() => {
|
||||
const m = moment(value.time, 'HH:mm:ss');
|
||||
return m.isValid() ? m : undefined;
|
||||
})()}
|
||||
onChange={(m, dateString) => {
|
||||
console.log('Automations.DateTime', m, dateString);
|
||||
setValue({ ...value, time: dateString });
|
||||
onChange({ ...value, time: dateString });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Input.Group>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const cronmap = {
|
||||
none: '不重复',
|
||||
@ -171,10 +215,10 @@ const cronmap = {
|
||||
export const Cron = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})((props) => {
|
||||
})(props => {
|
||||
const { value, onChange } = props;
|
||||
|
||||
console.log('Automations.DateTime', {value})
|
||||
console.log('Automations.DateTime', { value });
|
||||
|
||||
const re = /every_(\d+)_(.+)/i;
|
||||
|
||||
@ -187,33 +231,42 @@ export const Cron = connect({
|
||||
return 'none';
|
||||
}
|
||||
return match ? 'custom' : cronmap[value];
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input.Group compact>
|
||||
<Select value={cron} onChange={(v) => {
|
||||
setCron(v);
|
||||
onChange(v);
|
||||
}}>
|
||||
<Select
|
||||
value={cron}
|
||||
onChange={v => {
|
||||
setCron(v);
|
||||
onChange(v);
|
||||
}}
|
||||
>
|
||||
{Object.keys(cronmap).map(key => {
|
||||
return (
|
||||
<Select.Option value={key}>{cronmap[key]}</Select.Option>
|
||||
);
|
||||
return <Select.Option value={key}>{cronmap[key]}</Select.Option>;
|
||||
})}
|
||||
</Select>
|
||||
{cron === 'custom' && (
|
||||
<Input type={'number'} onChange={(e) => {
|
||||
const v = parseInt(e.target.value);
|
||||
setNum(v);
|
||||
onChange(`every_${v}_${unit}`);
|
||||
}} defaultValue={num} addonBefore={'每'}/>
|
||||
<Input
|
||||
type={'number'}
|
||||
onChange={e => {
|
||||
const v = parseInt(e.target.value);
|
||||
setNum(v);
|
||||
onChange(`every_${v}_${unit}`);
|
||||
}}
|
||||
defaultValue={num}
|
||||
addonBefore={'每'}
|
||||
/>
|
||||
)}
|
||||
{cron === 'custom' && (
|
||||
<Select onChange={(v) => {
|
||||
setUnit(v);
|
||||
onChange(`every_${num}_${v}`);
|
||||
}} defaultValue={unit}>
|
||||
<Select
|
||||
onChange={v => {
|
||||
setUnit(v);
|
||||
onChange(`every_${num}_${v}`);
|
||||
}}
|
||||
defaultValue={unit}
|
||||
>
|
||||
<Select.Option value={'seconds'}>秒</Select.Option>
|
||||
<Select.Option value={'minutes'}>分钟</Select.Option>
|
||||
<Select.Option value={'hours'}>小时</Select.Option>
|
||||
@ -224,13 +277,13 @@ export const Cron = connect({
|
||||
)}
|
||||
</Input.Group>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
export const EndMode = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})((props) => {
|
||||
})(props => {
|
||||
const { value = 'none', onChange, automationType } = props;
|
||||
const re = /after_(\d+)_times/i;
|
||||
const match = re.exec(value);
|
||||
@ -238,10 +291,13 @@ export const EndMode = connect({
|
||||
const [mode, setMode] = useState(() => {
|
||||
if (automationType === 'schedule' && value === 'byField') {
|
||||
return 'none';
|
||||
} else if (automationType === 'collections:schedule' && value === 'customTime') {
|
||||
} else if (
|
||||
automationType === 'collections:schedule' &&
|
||||
value === 'customTime'
|
||||
) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
|
||||
return match ? 'times' : value;
|
||||
});
|
||||
|
||||
@ -249,38 +305,57 @@ export const EndMode = connect({
|
||||
|
||||
useEffect(() => {
|
||||
if (automationType === 'schedule' && value === 'byField') {
|
||||
setMode('none')
|
||||
onChange('none')
|
||||
} else if (automationType === 'collections:schedule' && value === 'customTime') {
|
||||
setMode('none')
|
||||
onChange('none')
|
||||
setMode('none');
|
||||
onChange('none');
|
||||
} else if (
|
||||
automationType === 'collections:schedule' &&
|
||||
value === 'customTime'
|
||||
) {
|
||||
setMode('none');
|
||||
onChange('none');
|
||||
}
|
||||
}, [automationType]);
|
||||
console.log('Automations.DateTime', {value, automationType, mode})
|
||||
console.log('Automations.DateTime', { value, automationType, mode });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input.Group compact>
|
||||
<Select style={{width: 150}} value={mode} onChange={(v) => {
|
||||
setMode(v);
|
||||
onChange(v);
|
||||
}}>
|
||||
<Select
|
||||
style={{ width: 150 }}
|
||||
value={mode}
|
||||
onChange={v => {
|
||||
setMode(v);
|
||||
onChange(v);
|
||||
}}
|
||||
>
|
||||
<Select.Option value={'none'}>永不结束</Select.Option>
|
||||
<Select.Option value={'times'}>指定重复次数</Select.Option>
|
||||
{automationType === 'schedule' && <Select.Option value={'customTime'}>自定义时间</Select.Option>}
|
||||
{automationType === 'collections:schedule' && <Select.Option value={'byField'}>根据日期字段</Select.Option>}
|
||||
{automationType === 'schedule' && (
|
||||
<Select.Option value={'customTime'}>自定义时间</Select.Option>
|
||||
)}
|
||||
{automationType === 'collections:schedule' && (
|
||||
<Select.Option value={'byField'}>根据日期字段</Select.Option>
|
||||
)}
|
||||
</Select>
|
||||
{mode === 'times' && <Input type={'number'} onChange={(e) => {
|
||||
const v = parseInt(e.target.value);
|
||||
setNum(v);
|
||||
onChange(`after_${v}_times`);
|
||||
}} defaultValue={num} addonAfter={'次'}/>}
|
||||
{mode === 'times' && (
|
||||
<Input
|
||||
type={'number'}
|
||||
onChange={e => {
|
||||
const v = parseInt(e.target.value);
|
||||
setNum(v);
|
||||
onChange(`after_${v}_times`);
|
||||
}}
|
||||
defaultValue={num}
|
||||
addonAfter={'次'}
|
||||
/>
|
||||
)}
|
||||
</Input.Group>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
export const Automations = {
|
||||
DateTime, Cron, EndMode
|
||||
DateTime,
|
||||
Cron,
|
||||
EndMode,
|
||||
};
|
||||
|
||||
|
@ -2,4 +2,4 @@
|
||||
.ant-input-group-wrapper {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,24 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Cascader as AntdCascader } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Cascader as AntdCascader } from 'antd';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import {
|
||||
transformDataSourceKey,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
|
||||
function findTreeNode(tree, values, { value = 'value', children = 'children' }) {
|
||||
function findTreeNode(
|
||||
tree,
|
||||
values,
|
||||
{ value = 'value', children = 'children' },
|
||||
) {
|
||||
let node, i;
|
||||
for (node = tree, i = 0; node && values[i]; i++ ) {
|
||||
node = (node[children] || []).find(item => item[value] === values[i][value]);
|
||||
for (node = tree, i = 0; node && values[i]; i++) {
|
||||
node = (node[children] || []).find(
|
||||
item => item[value] === values[i][value],
|
||||
);
|
||||
}
|
||||
|
||||
return node;
|
||||
@ -21,7 +27,7 @@ function findTreeNode(tree, values, { value = 'value', children = 'children' })
|
||||
export const Cascader = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(function (props) {
|
||||
})(function(props) {
|
||||
const {
|
||||
disabled,
|
||||
// target,
|
||||
@ -36,7 +42,7 @@ export const Cascader = connect({
|
||||
// TODO(feature): 增加静态数据支持
|
||||
// dataSource: []
|
||||
} = props;
|
||||
const {
|
||||
const {
|
||||
target,
|
||||
targetKey: valueField,
|
||||
// 值字段
|
||||
@ -55,71 +61,76 @@ export const Cascader = connect({
|
||||
// 是否可以不选择到最深一级
|
||||
// 'x-component-props': { changeOnSelect: true }
|
||||
incompletely: changeOnSelect,
|
||||
|
||||
} = schema;
|
||||
|
||||
const fieldNames = {
|
||||
label: labelField,
|
||||
value: valueField,
|
||||
children: 'children'
|
||||
children: 'children',
|
||||
};
|
||||
const [options, setOptions] = useState([]);
|
||||
|
||||
const { loading, run } = useRequest(async (selectedOptions = []) => {
|
||||
if (maxLevel != null && selectedOptions.length >= maxLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const last = selectedOptions[selectedOptions.length - 1] || null;
|
||||
if (last) {
|
||||
if (last.isLeaf) {
|
||||
const { loading, run } = useRequest(
|
||||
async (selectedOptions = []) => {
|
||||
if (maxLevel != null && selectedOptions.length >= maxLevel) {
|
||||
return;
|
||||
}
|
||||
last.loading = true;
|
||||
}
|
||||
|
||||
return api.resource(target).list({
|
||||
filter: {
|
||||
[parentField]: last && last[valueField]
|
||||
const last = selectedOptions[selectedOptions.length - 1] || null;
|
||||
if (last) {
|
||||
if (last.isLeaf) {
|
||||
return;
|
||||
}
|
||||
last.loading = true;
|
||||
}
|
||||
|
||||
return api.resource(target).list({
|
||||
filter: {
|
||||
[parentField]: last && last[valueField],
|
||||
},
|
||||
perPage: -1,
|
||||
sort: [valueField],
|
||||
});
|
||||
// TODO(bug): 关联资源加载问题较多,暂时先用 filter 解决
|
||||
// return api.resource(`${target}.${target}`).list({
|
||||
// associatedKey: last,
|
||||
// perPage: -1
|
||||
// });
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
onSuccess(result, [selectedOptions = []]) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = result.map(item => ({
|
||||
...item,
|
||||
isLeaf: maxLevel != null && item.level >= maxLevel,
|
||||
}));
|
||||
// 找到已有值指向的 options 节点
|
||||
const root = { [fieldNames.children]: options };
|
||||
const node = findTreeNode(root, selectedOptions, fieldNames);
|
||||
|
||||
if (node && node !== root) {
|
||||
node.children = data;
|
||||
node.loading = false;
|
||||
// use spread array to avoid popup to be collapsed
|
||||
setOptions([...options]);
|
||||
} else {
|
||||
setOptions(data);
|
||||
}
|
||||
},
|
||||
perPage: -1,
|
||||
sort: [valueField]
|
||||
});
|
||||
// TODO(bug): 关联资源加载问题较多,暂时先用 filter 解决
|
||||
// return api.resource(`${target}.${target}`).list({
|
||||
// associatedKey: last,
|
||||
// perPage: -1
|
||||
// });
|
||||
}, {
|
||||
manual: true,
|
||||
onSuccess(result, [selectedOptions = []]) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = result.map(item => ({
|
||||
...item,
|
||||
isLeaf: maxLevel != null && item.level >= maxLevel
|
||||
}));
|
||||
// 找到已有值指向的 options 节点
|
||||
const root = { [fieldNames.children]: options };
|
||||
const node = findTreeNode(root, selectedOptions, fieldNames);
|
||||
|
||||
if (node && node !== root) {
|
||||
node.children = data;
|
||||
node.loading = false;
|
||||
// use spread array to avoid popup to be collapsed
|
||||
setOptions([...options]);
|
||||
} else {
|
||||
setOptions(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// 根据 value 的值,按需预加载相应的数据
|
||||
useEffect(() => {
|
||||
if (value.length) {
|
||||
value.reduce((promise, option, i) => promise.then(() => run(value.slice(0, i))), Promise.resolve());
|
||||
value.reduce(
|
||||
(promise, option, i) => promise.then(() => run(value.slice(0, i))),
|
||||
Promise.resolve(),
|
||||
);
|
||||
} else {
|
||||
run([]);
|
||||
}
|
||||
@ -143,6 +154,6 @@ export const Cascader = connect({
|
||||
fieldNames={fieldNames}
|
||||
/>
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
export default Cascader
|
||||
export default Cascader;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/cascader/style/index'
|
||||
import 'antd/lib/cascader/style/index';
|
||||
|
@ -1,21 +1,19 @@
|
||||
import {
|
||||
connect
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { Checkbox as AntdCheckbox } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Checkbox as AntdCheckbox } from 'antd';
|
||||
import {
|
||||
transformDataSourceKey,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
|
||||
export const Checkbox = connect<'Group'>({
|
||||
valueName: 'checked',
|
||||
getProps: mapStyledProps
|
||||
})(AntdCheckbox)
|
||||
getProps: mapStyledProps,
|
||||
})(AntdCheckbox);
|
||||
|
||||
Checkbox.Group = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(transformDataSourceKey(AntdCheckbox.Group, 'options'))
|
||||
getComponent: mapTextComponent,
|
||||
})(transformDataSourceKey(AntdCheckbox.Group, 'options'));
|
||||
|
||||
export default Checkbox
|
||||
export default Checkbox;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/checkbox/style/index'
|
||||
import 'antd/lib/checkbox/style/index';
|
||||
|
@ -1,16 +1,16 @@
|
||||
import React from 'react'
|
||||
import { Button } from 'antd'
|
||||
import { ButtonProps } from 'antd/lib/button'
|
||||
import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { ButtonProps } from 'antd/lib/button';
|
||||
|
||||
export const CircleButton: React.FC<ButtonProps> = props => {
|
||||
const hasText = String(props.className || '').indexOf('has-text') > -1
|
||||
const hasText = String(props.className || '').indexOf('has-text') > -1;
|
||||
return (
|
||||
<Button
|
||||
type={hasText ? 'link' : undefined}
|
||||
shape={hasText ? undefined : 'circle'}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default CircleButton
|
||||
export default CircleButton;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/button/style/index'
|
||||
import 'antd/lib/button/style/index';
|
||||
|
@ -1,30 +1,29 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import React from 'react';
|
||||
import { Select, Tag } from 'antd';
|
||||
import {
|
||||
Select as AntdSelect,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
|
||||
export const ColorSelect = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})((props) => {
|
||||
|
||||
})(props => {
|
||||
const colors = {
|
||||
'red': '薄暮',
|
||||
'magenta': '法式洋红',
|
||||
'volcano': '火山',
|
||||
'orange': '日暮',
|
||||
'gold': '金盏花',
|
||||
'lime': '青柠',
|
||||
'green': '极光绿',
|
||||
'cyan': '明青',
|
||||
'blue': '拂晓蓝',
|
||||
'geekblue': '极客蓝',
|
||||
'purple': '酱紫',
|
||||
'default': '默认'
|
||||
red: '薄暮',
|
||||
magenta: '法式洋红',
|
||||
volcano: '火山',
|
||||
orange: '日暮',
|
||||
gold: '金盏花',
|
||||
lime: '青柠',
|
||||
green: '极光绿',
|
||||
cyan: '明青',
|
||||
blue: '拂晓蓝',
|
||||
geekblue: '极客蓝',
|
||||
purple: '酱紫',
|
||||
default: '默认',
|
||||
};
|
||||
|
||||
return (
|
||||
@ -35,7 +34,7 @@ export const ColorSelect = connect({
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
export default ColorSelect
|
||||
export default ColorSelect;
|
||||
|
@ -1,105 +1,105 @@
|
||||
import React from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import moment from 'moment'
|
||||
import { DatePicker as AntdDatePicker } from 'antd'
|
||||
import React from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { DatePicker as AntdDatePicker } from 'antd';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr
|
||||
} from '../shared'
|
||||
isArr,
|
||||
} from '../shared';
|
||||
|
||||
class YearPicker extends React.Component {
|
||||
public render() {
|
||||
return <AntdDatePicker {...this.props} picker={'year'} />
|
||||
return <AntdDatePicker {...this.props} picker={'year'} />;
|
||||
}
|
||||
}
|
||||
|
||||
const transformMoment = (value, format = 'YYYY-MM-DD HH:mm:ss') => {
|
||||
if (value === '') return undefined
|
||||
return value && value.format ? value.format(format) : value
|
||||
}
|
||||
if (value === '') return undefined;
|
||||
return value && value.format ? value.format(format) : value;
|
||||
};
|
||||
|
||||
const mapMomentValue = (props: any, fieldProps: any) => {
|
||||
const { value, showTime = false } = props
|
||||
if (!fieldProps.editable) return props
|
||||
const { value, showTime = false } = props;
|
||||
if (!fieldProps.editable) return props;
|
||||
try {
|
||||
if (isStr(value) && value) {
|
||||
props.value = moment(
|
||||
value,
|
||||
showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'
|
||||
)
|
||||
showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD',
|
||||
);
|
||||
} else if (isArr(value) && value.length) {
|
||||
props.value = value.map(
|
||||
item =>
|
||||
(item &&
|
||||
moment(item, showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')) ||
|
||||
''
|
||||
)
|
||||
'',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
throw new Error(e);
|
||||
}
|
||||
return props
|
||||
}
|
||||
return props;
|
||||
};
|
||||
|
||||
export const DatePicker = connect<
|
||||
'RangePicker' | 'MonthPicker' | 'YearPicker' | 'WeekPicker'
|
||||
>({
|
||||
getValueFromEvent(_, value) {
|
||||
const props = this.props || {}
|
||||
const props = this.props || {};
|
||||
return transformMoment(
|
||||
value,
|
||||
props.format || (props.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
|
||||
)
|
||||
props.format || (props.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'),
|
||||
);
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent
|
||||
})(AntdDatePicker)
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdDatePicker);
|
||||
|
||||
DatePicker.RangePicker = connect({
|
||||
getValueFromEvent(_, [startDate, endDate]) {
|
||||
const props = this.props || {}
|
||||
const props = this.props || {};
|
||||
const format =
|
||||
props.format || (props.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
|
||||
props.format || (props.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
|
||||
return [
|
||||
transformMoment(startDate, format),
|
||||
transformMoment(endDate, format)
|
||||
]
|
||||
transformMoment(endDate, format),
|
||||
];
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent
|
||||
})(AntdDatePicker.RangePicker)
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdDatePicker.RangePicker);
|
||||
|
||||
DatePicker.MonthPicker = connect({
|
||||
getValueFromEvent(_, value) {
|
||||
return transformMoment(value)
|
||||
return transformMoment(value);
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent
|
||||
})(AntdDatePicker.MonthPicker)
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdDatePicker.MonthPicker);
|
||||
|
||||
DatePicker.WeekPicker = connect({
|
||||
getValueFromEvent(_, value) {
|
||||
return transformMoment(value, 'gggg-wo')
|
||||
return transformMoment(value, 'gggg-wo');
|
||||
},
|
||||
getProps: compose(mapStyledProps, props => {
|
||||
if (isStr(props.value) && props.value) {
|
||||
const parsed = props.value.match(/\D*(\d+)\D*(\d+)\D*/) || ['', '', '']
|
||||
props.value = moment(parsed[1], 'YYYY').add(parsed[2] - 1, 'weeks')
|
||||
const parsed = props.value.match(/\D*(\d+)\D*(\d+)\D*/) || ['', '', ''];
|
||||
props.value = moment(parsed[1], 'YYYY').add(parsed[2] - 1, 'weeks');
|
||||
}
|
||||
return props
|
||||
return props;
|
||||
}),
|
||||
getComponent: mapTextComponent
|
||||
})(AntdDatePicker.WeekPicker)
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdDatePicker.WeekPicker);
|
||||
|
||||
DatePicker.YearPicker = connect({
|
||||
getValueFromEvent(_, value) {
|
||||
return transformMoment(value, 'YYYY')
|
||||
return transformMoment(value, 'YYYY');
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent
|
||||
})(YearPicker)
|
||||
getComponent: mapTextComponent,
|
||||
})(YearPicker);
|
||||
|
||||
export default DatePicker
|
||||
export default DatePicker;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/date-picker/style/index'
|
||||
import 'antd/lib/date-picker/style/index';
|
||||
|
@ -1,42 +1,61 @@
|
||||
import React, { useState } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import moment from 'moment'
|
||||
import { Select, Table } from 'antd'
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { Select, Table } from 'antd';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr
|
||||
} from '../shared'
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { Spin } from '@nocobase/client'
|
||||
import { fields2columns, components } from '@/components/views/SortableTable'
|
||||
import { Spin } from '@nocobase/client';
|
||||
import { fields2columns, components } from '@/components/views/SortableTable';
|
||||
|
||||
function DraggableTableComponent(props) {
|
||||
const { mode = 'showInDetail', value, onChange, disabled, rowKey = 'id', fields = [], resourceName, associatedKey, filter, labelField, valueField = 'id', objectValue, placeholder } = props;
|
||||
const { data = [], loading = true, mutate } = useRequest(() => {
|
||||
return api.resource(resourceName).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
perPage: -1,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [resourceName, associatedKey]
|
||||
});
|
||||
const {
|
||||
mode = 'showInDetail',
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
rowKey = 'id',
|
||||
fields = [],
|
||||
resourceName,
|
||||
associatedKey,
|
||||
filter,
|
||||
labelField,
|
||||
valueField = 'id',
|
||||
objectValue,
|
||||
placeholder,
|
||||
} = props;
|
||||
const { data = [], loading = true, mutate } = useRequest(
|
||||
() => {
|
||||
return api.resource(resourceName).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceName, associatedKey],
|
||||
},
|
||||
);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const onTableChange = (selectedRowKeys: React.ReactText[]) => {
|
||||
onChange(selectedRowKeys);
|
||||
}
|
||||
};
|
||||
const tableProps: any = {};
|
||||
tableProps.rowSelection = {
|
||||
selectedRowKeys: Array.isArray(value) ? value : data.map(item => item[rowKey]),
|
||||
selectedRowKeys: Array.isArray(value)
|
||||
? value
|
||||
: data.map(item => item[rowKey]),
|
||||
onChange: onTableChange,
|
||||
}
|
||||
};
|
||||
const dataSource = data.filter(item => {
|
||||
return get(item, ['component', mode])
|
||||
return get(item, ['component', mode]);
|
||||
});
|
||||
// const sortIds = get(value, 'sort')||[];
|
||||
// let values = [];
|
||||
@ -52,12 +71,12 @@ function DraggableTableComponent(props) {
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
scroll={{y: 300}}
|
||||
scroll={{ y: 300 }}
|
||||
size={'small'}
|
||||
dataSource={dataSource}
|
||||
rowKey={rowKey}
|
||||
loading={loading}
|
||||
columns={fields2columns(fields||[])}
|
||||
columns={fields2columns(fields || [])}
|
||||
pagination={false}
|
||||
{...tableProps}
|
||||
// components={components({
|
||||
@ -85,6 +104,6 @@ function DraggableTableComponent(props) {
|
||||
export const DraggableTable = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(DraggableTableComponent)
|
||||
})(DraggableTableComponent);
|
||||
|
||||
export default DraggableTable
|
||||
export default DraggableTable;
|
||||
|
@ -1,28 +1,30 @@
|
||||
import React, { useState } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Select, Button, Space } from 'antd'
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Select, Button, Space } from 'antd';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr
|
||||
} from '../shared'
|
||||
import ViewFactory from '@/components/views'
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import ViewFactory from '@/components/views';
|
||||
import Drawer from '@/components/pages/AdminLoader/Drawer';
|
||||
import View from '@/components/pages/AdminLoader/View';
|
||||
|
||||
function transform({value, multiple, labelField, valueField = 'id'}) {
|
||||
function transform({ value, multiple, labelField, valueField = 'id' }) {
|
||||
let selectedKeys = [];
|
||||
let selectedValue = [];
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
selectedKeys = values.filter(item => item).map(item => item[valueField]);
|
||||
selectedValue = values.filter(item => item).map(item => {
|
||||
return {
|
||||
value: item[valueField],
|
||||
label: item[labelField],
|
||||
}
|
||||
});
|
||||
selectedValue = values
|
||||
.filter(item => item)
|
||||
.map(item => {
|
||||
return {
|
||||
value: item[valueField],
|
||||
label: item[labelField],
|
||||
};
|
||||
});
|
||||
if (!multiple) {
|
||||
return [selectedKeys.shift(), selectedValue.shift()];
|
||||
}
|
||||
@ -30,14 +32,35 @@ function transform({value, multiple, labelField, valueField = 'id'}) {
|
||||
}
|
||||
|
||||
export function DrawerSelectComponent(props) {
|
||||
const { __parent, size, schema = {}, disabled, viewName, target, multiple, filter, resourceName, associatedKey, valueField = 'id', value, onChange } = props;
|
||||
const {
|
||||
__parent,
|
||||
size,
|
||||
schema = {},
|
||||
disabled,
|
||||
viewName,
|
||||
target,
|
||||
multiple,
|
||||
filter,
|
||||
resourceName,
|
||||
associatedKey,
|
||||
valueField = 'id',
|
||||
value,
|
||||
onChange,
|
||||
} = props;
|
||||
const labelField = props.labelField || schema.labelField;
|
||||
const [selectedKeys, selectedValue] = transform({value, multiple, labelField, valueField });
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState(multiple ? selectedKeys : [selectedKeys]);
|
||||
const [selectedKeys, selectedValue] = transform({
|
||||
value,
|
||||
multiple,
|
||||
labelField,
|
||||
valueField,
|
||||
});
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState(
|
||||
multiple ? selectedKeys : [selectedKeys],
|
||||
);
|
||||
const [selectedRows, setSelectedRows] = useState(selectedValue);
|
||||
const [options, setOptions] = useState(selectedValue);
|
||||
const { title = '' } = schema;
|
||||
console.log({schema})
|
||||
console.log({ schema });
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
@ -49,13 +72,13 @@ export function DrawerSelectComponent(props) {
|
||||
allowClear={true}
|
||||
value={options}
|
||||
notFoundContent={''}
|
||||
onChange={(data) => {
|
||||
onChange={data => {
|
||||
setOptions(data);
|
||||
if (Array.isArray(data)) {
|
||||
const srks = data.map(item => item.value);
|
||||
onChange(srks);
|
||||
setSelectedRowKeys(srks);
|
||||
console.log('datadatadatadata', {data, srks});
|
||||
console.log('datadatadatadata', { data, srks });
|
||||
} else if (data && typeof data === 'object') {
|
||||
onChange(data.value);
|
||||
setSelectedRowKeys([data.value]);
|
||||
@ -69,12 +92,19 @@ export function DrawerSelectComponent(props) {
|
||||
if (!disabled) {
|
||||
Drawer.open({
|
||||
title: `选择要关联的${title}数据`,
|
||||
content: ({resolve}) => {
|
||||
console.log('valuevaluevaluevaluevaluevalue', selectedRowKeys, selectedRows, options);
|
||||
content: ({ resolve }) => {
|
||||
console.log(
|
||||
'valuevaluevaluevaluevaluevalue',
|
||||
selectedRowKeys,
|
||||
selectedRows,
|
||||
options,
|
||||
);
|
||||
const [rows, setRows] = useState(selectedRows);
|
||||
const [rowKeys, setRowKeys] = useState(selectedRowKeys)
|
||||
const [selected, setSelected] = useState(Array.isArray(value) ? value : [value]);
|
||||
console.log({selectedRowKeys});
|
||||
const [rowKeys, setRowKeys] = useState(selectedRowKeys);
|
||||
const [selected, setSelected] = useState(
|
||||
Array.isArray(value) ? value : [value],
|
||||
);
|
||||
console.log({ selectedRowKeys });
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
@ -83,34 +113,44 @@ export function DrawerSelectComponent(props) {
|
||||
multiple={multiple}
|
||||
defaultFilter={filter}
|
||||
defaultSelectedRowKeys={selectedRowKeys}
|
||||
onSelected={(values) => {
|
||||
onSelected={values => {
|
||||
setSelected(values);
|
||||
const [selectedKeys, selectedValue] = transform({value: values, multiple: true, labelField, valueField });
|
||||
const [selectedKeys, selectedValue] = transform({
|
||||
value: values,
|
||||
multiple: true,
|
||||
labelField,
|
||||
valueField,
|
||||
});
|
||||
setSelectedRows(selectedValue);
|
||||
setRows(selectedValue);
|
||||
setSelectedRowKeys(selectedKeys);
|
||||
setRowKeys(selectedKeys);
|
||||
console.log({ values, selectedValue, selectedKeys });
|
||||
console.log({selectedRows, selectedRowKeys});
|
||||
console.log({ selectedRows, selectedRowKeys });
|
||||
}}
|
||||
viewName={viewName || `${target}.table`}
|
||||
/>
|
||||
<Drawer.Footer>
|
||||
<Space>
|
||||
<Button onClick={resolve}>取消</Button>
|
||||
<Button onClick={() => {
|
||||
setOptions(rows);
|
||||
// console.log('valuevaluevaluevaluevaluevalue', {selectedRowKeys});
|
||||
onChange(multiple ? selected : selected.shift());
|
||||
// console.log({rows, rowKeys});
|
||||
resolve();
|
||||
}} type={'primary'}>确定</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOptions(rows);
|
||||
// console.log('valuevaluevaluevaluevaluevalue', {selectedRowKeys});
|
||||
onChange(multiple ? selected : selected.shift());
|
||||
// console.log({rows, rowKeys});
|
||||
resolve();
|
||||
}}
|
||||
type={'primary'}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</Space>
|
||||
</Drawer.Footer>
|
||||
</>
|
||||
)
|
||||
);
|
||||
},
|
||||
})
|
||||
});
|
||||
// setVisible(true);
|
||||
}
|
||||
}}
|
||||
@ -122,6 +162,6 @@ export function DrawerSelectComponent(props) {
|
||||
export const DrawerSelect = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(DrawerSelectComponent)
|
||||
})(DrawerSelectComponent);
|
||||
|
||||
export default DrawerSelect
|
||||
export default DrawerSelect;
|
||||
|
@ -1,9 +1,19 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Select, Input, Space, Form, InputNumber, DatePicker, TimePicker, Radio } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
Input,
|
||||
Space,
|
||||
Form,
|
||||
InputNumber,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Radio,
|
||||
} from 'antd';
|
||||
import { PlusCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
|
||||
import useDynamicList from './useDynamicList';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { mapStyledProps } from '../shared';
|
||||
import get from 'lodash/get';
|
||||
import moment from 'moment';
|
||||
import './style.less';
|
||||
@ -11,35 +21,47 @@ import api from '@/api-client';
|
||||
import { useRequest } from 'umi';
|
||||
|
||||
export function FilterGroup(props: any) {
|
||||
const { showDeleteButton = true, fields = [], sourceFields = [], onDelete, onChange, onAdd, dataSource = {} } = props;
|
||||
const { list, getKey, push, remove, replace } = useDynamicList<any>(dataSource.list || [
|
||||
{
|
||||
type: 'item',
|
||||
},
|
||||
]);
|
||||
const {
|
||||
showDeleteButton = true,
|
||||
fields = [],
|
||||
sourceFields = [],
|
||||
onDelete,
|
||||
onChange,
|
||||
onAdd,
|
||||
dataSource = {},
|
||||
} = props;
|
||||
const { list, getKey, push, remove, replace } = useDynamicList<any>(
|
||||
dataSource.list || [
|
||||
{
|
||||
type: 'item',
|
||||
},
|
||||
],
|
||||
);
|
||||
let style: any = {
|
||||
position: 'relative',
|
||||
};
|
||||
if (showDeleteButton) {
|
||||
style = {
|
||||
...style,
|
||||
marginBottom: 14,
|
||||
marginBottom: 14,
|
||||
padding: 14,
|
||||
border: '1px dashed #dedede',
|
||||
}
|
||||
};
|
||||
}
|
||||
return (
|
||||
<div style={style}>
|
||||
<div style={{marginBottom: 14}}>
|
||||
满足组内
|
||||
{' '}
|
||||
<Select style={{width: 80}} onChange={(value) => {
|
||||
onChange({type: 'group', list, andor: value});
|
||||
}} defaultValue={dataSource.andor||'and'}>
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
满足组内{' '}
|
||||
<Select
|
||||
style={{ width: 80 }}
|
||||
onChange={value => {
|
||||
onChange({ type: 'group', list, andor: value });
|
||||
}}
|
||||
defaultValue={dataSource.andor || 'and'}
|
||||
>
|
||||
<Select.Option value={'and'}>全部</Select.Option>
|
||||
<Select.Option value={'or'}>任意</Select.Option>
|
||||
</Select>
|
||||
{' '}
|
||||
</Select>{' '}
|
||||
条件
|
||||
</div>
|
||||
<div>
|
||||
@ -47,47 +69,52 @@ export function FilterGroup(props: any) {
|
||||
// console.log(item);
|
||||
const Component = item.type === 'group' ? FilterGroup : FilterItem;
|
||||
return (
|
||||
<div style={{marginBottom: 8}}>
|
||||
{<Component
|
||||
fields={fields}
|
||||
sourceFields={sourceFields}
|
||||
dataSource={item}
|
||||
// showDeleteButton={list.length > 1}
|
||||
onChange={(value) => {
|
||||
replace(index, value);
|
||||
const newList = [...list];
|
||||
newList[index] = value;
|
||||
onChange({...dataSource, list: newList});
|
||||
// console.log(list, value, index);
|
||||
}}
|
||||
onDelete={() => {
|
||||
remove(index);
|
||||
const newList = [...list];
|
||||
newList.splice(index, 1);
|
||||
onChange({...dataSource, list: newList});
|
||||
// console.log(list, index);
|
||||
}}
|
||||
/>}
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
{
|
||||
<Component
|
||||
fields={fields}
|
||||
sourceFields={sourceFields}
|
||||
dataSource={item}
|
||||
// showDeleteButton={list.length > 1}
|
||||
onChange={value => {
|
||||
replace(index, value);
|
||||
const newList = [...list];
|
||||
newList[index] = value;
|
||||
onChange({ ...dataSource, list: newList });
|
||||
// console.log(list, value, index);
|
||||
}}
|
||||
onDelete={() => {
|
||||
remove(index);
|
||||
const newList = [...list];
|
||||
newList.splice(index, 1);
|
||||
onChange({ ...dataSource, list: newList });
|
||||
// console.log(list, index);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<Space>
|
||||
<Button style={{padding: 0}} type={'link'} onClick={() => {
|
||||
const data = {
|
||||
type: 'item'
|
||||
};
|
||||
push(data);
|
||||
const newList = [...list];
|
||||
newList.push(data);
|
||||
onChange({...dataSource, list: newList});
|
||||
}}>
|
||||
<PlusCircleOutlined /> 添加条件
|
||||
</Button>
|
||||
{' '}
|
||||
<Button
|
||||
style={{padding: 0}}
|
||||
style={{ padding: 0 }}
|
||||
type={'link'}
|
||||
onClick={() => {
|
||||
const data = {
|
||||
type: 'item',
|
||||
};
|
||||
push(data);
|
||||
const newList = [...list];
|
||||
newList.push(data);
|
||||
onChange({ ...dataSource, list: newList });
|
||||
}}
|
||||
>
|
||||
<PlusCircleOutlined /> 添加条件
|
||||
</Button>{' '}
|
||||
<Button
|
||||
style={{ padding: 0 }}
|
||||
type={'link'}
|
||||
onClick={() => {
|
||||
const data = {
|
||||
@ -101,16 +128,29 @@ export function FilterGroup(props: any) {
|
||||
push(data);
|
||||
const newList = [...list];
|
||||
newList.push(data);
|
||||
onChange({...dataSource, list: newList});
|
||||
onChange({ ...dataSource, list: newList });
|
||||
}}
|
||||
>
|
||||
<PlusCircleOutlined /> 添加条件组
|
||||
</Button>
|
||||
{showDeleteButton && <Button className={'filter-remove-link filter-group'} style={{padding: 0, position: 'absolute', top: 0, right: 0, width: 32}} type={'link'} onClick={(e) => {
|
||||
onDelete && onDelete(e);
|
||||
}}>
|
||||
<CloseCircleOutlined />
|
||||
</Button>}
|
||||
{showDeleteButton && (
|
||||
<Button
|
||||
className={'filter-remove-link filter-group'}
|
||||
style={{
|
||||
padding: 0,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: 32,
|
||||
}}
|
||||
type={'link'}
|
||||
onClick={e => {
|
||||
onDelete && onDelete(e);
|
||||
}}
|
||||
>
|
||||
<CloseCircleOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
@ -131,72 +171,72 @@ interface FilterItemProps {
|
||||
|
||||
const OP_MAP = {
|
||||
string: [
|
||||
{label: '包含', value: '$includes', selected: true},
|
||||
{label: '不包含', value: '$notIncludes'},
|
||||
{label: '等于', value: 'eq'},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '包含', value: '$includes', selected: true },
|
||||
{ label: '不包含', value: '$notIncludes' },
|
||||
{ label: '等于', value: 'eq' },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
number: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '大于', value: 'gt'},
|
||||
{label: '大于等于', value: 'gte'},
|
||||
{label: '小于', value: 'lt'},
|
||||
{label: '小于等于', value: 'lte'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '大于', value: 'gt' },
|
||||
{ label: '大于等于', value: 'gte' },
|
||||
{ label: '小于', value: 'lt' },
|
||||
{ label: '小于等于', value: 'lte' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
file: [
|
||||
{label: '存在', value: 'id.gt'},
|
||||
{label: '不存在', value: 'id.$null'},
|
||||
{ label: '存在', value: 'id.gt' },
|
||||
{ label: '不存在', value: 'id.$null' },
|
||||
],
|
||||
boolean: [
|
||||
{label: '是', value: '$isTruly', selected: true},
|
||||
{label: '否', value: '$isFalsy'},
|
||||
{ label: '是', value: '$isTruly', selected: true },
|
||||
{ label: '否', value: '$isFalsy' },
|
||||
],
|
||||
select: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '包含', value: 'in'},
|
||||
{label: '不包含', value: 'notIn'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '包含', value: 'in' },
|
||||
{ label: '不包含', value: 'notIn' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
multipleSelect: [
|
||||
{label: '等于', value: '$match', selected: true},
|
||||
{label: '不等于', value: '$notMatch'},
|
||||
{label: '包含', value: '$anyOf'},
|
||||
{label: '不包含', value: '$noneOf'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '等于', value: '$match', selected: true },
|
||||
{ label: '不等于', value: '$notMatch' },
|
||||
{ label: '包含', value: '$anyOf' },
|
||||
{ label: '不包含', value: '$noneOf' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
datetime: [
|
||||
{label: '等于', value: '$dateOn', selected: true},
|
||||
{label: '不等于', value: '$dateNotOn'},
|
||||
{label: '早于', value: '$dateBefore'},
|
||||
{label: '晚于', value: '$dateAfter'},
|
||||
{label: '不早于', value: '$dateNotBefore'},
|
||||
{label: '不晚于', value: '$dateNotAfter'},
|
||||
{ label: '等于', value: '$dateOn', selected: true },
|
||||
{ label: '不等于', value: '$dateNotOn' },
|
||||
{ label: '早于', value: '$dateBefore' },
|
||||
{ label: '晚于', value: '$dateAfter' },
|
||||
{ label: '不早于', value: '$dateNotBefore' },
|
||||
{ label: '不晚于', value: '$dateNotAfter' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
// {label: '是今天', value: 'now'},
|
||||
// {label: '在今天之前', value: 'before_today'},
|
||||
// {label: '在今天之后', value: 'after_today'},
|
||||
],
|
||||
time: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'neq'},
|
||||
{label: '大于', value: 'gt'},
|
||||
{label: '大于等于', value: 'gte'},
|
||||
{label: '小于', value: 'lt'},
|
||||
{label: '小于等于', value: 'lte'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'neq' },
|
||||
{ label: '大于', value: 'gt' },
|
||||
{ label: '大于等于', value: 'gte' },
|
||||
{ label: '小于', value: 'lt' },
|
||||
{ label: '小于等于', value: 'lte' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
// {label: '是今天', value: 'now'},
|
||||
// {label: '在今天之前', value: 'before_today'},
|
||||
// {label: '在今天之后', value: 'after_today'},
|
||||
@ -227,22 +267,26 @@ const op = {
|
||||
attachment: OP_MAP.file,
|
||||
};
|
||||
|
||||
const StringInput = (props) => {
|
||||
const {value, onChange, ...restProps } = props;
|
||||
const StringInput = props => {
|
||||
const { value, onChange, ...restProps } = props;
|
||||
return (
|
||||
<Input {...restProps} defaultValue={value} onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}/>
|
||||
<Input
|
||||
{...restProps}
|
||||
defaultValue={value}
|
||||
onChange={e => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const controls = {
|
||||
string: StringInput,
|
||||
textarea: StringInput,
|
||||
number: InputNumber,
|
||||
percent: (props) => (
|
||||
<InputNumber
|
||||
formatter={value => value ? `${value}%` : ''}
|
||||
percent: props => (
|
||||
<InputNumber
|
||||
formatter={value => (value ? `${value}%` : '')}
|
||||
parser={value => value.replace('%', '')}
|
||||
{...props}
|
||||
/>
|
||||
@ -265,9 +309,13 @@ function DateControl(props: any) {
|
||||
// }
|
||||
const m = moment(value, format);
|
||||
return (
|
||||
<DatePicker format={format} value={m.isValid() ? m : null} onChange={(value) => {
|
||||
onChange(value ? value.format('YYYY-MM-DD') : null)
|
||||
}}/>
|
||||
<DatePicker
|
||||
format={format}
|
||||
value={m.isValid() ? m : null}
|
||||
onChange={value => {
|
||||
onChange(value ? value.format('YYYY-MM-DD') : null);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
// return (
|
||||
// <DatePicker format={format} showTime={field.showTime} value={m.isValid() ? m : null} onChange={(value) => {
|
||||
@ -280,13 +328,15 @@ function TimeControl(props: any) {
|
||||
const { field, value, onChange, ...restProps } = props;
|
||||
let format = field.timeFormat;
|
||||
const m = moment(value, format);
|
||||
return <TimePicker
|
||||
value={m.isValid() ? m : null}
|
||||
format={field.timeFormat}
|
||||
onChange={(value) => {
|
||||
onChange(value ? value.format('HH:mm:ss') : null)
|
||||
}}
|
||||
/>
|
||||
return (
|
||||
<TimePicker
|
||||
value={m.isValid() ? m : null}
|
||||
format={field.timeFormat}
|
||||
onChange={value => {
|
||||
onChange(value ? value.format('HH:mm:ss') : null);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function OptionControl(props) {
|
||||
@ -296,19 +346,27 @@ function OptionControl(props) {
|
||||
mode = undefined;
|
||||
}
|
||||
return (
|
||||
<Select style={{ minWidth: 120 }} mode={mode} value={value} onChange={(value) => {
|
||||
onChange(value);
|
||||
}} options={options}>
|
||||
</Select>
|
||||
<Select
|
||||
style={{ minWidth: 120 }}
|
||||
mode={mode}
|
||||
value={value}
|
||||
onChange={value => {
|
||||
onChange(value);
|
||||
}}
|
||||
options={options}
|
||||
></Select>
|
||||
);
|
||||
}
|
||||
|
||||
function BooleanControl(props) {
|
||||
const { value, onChange, ...restProps } = props;
|
||||
return (
|
||||
<Radio.Group value={value} onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}>
|
||||
<Radio.Group
|
||||
value={value}
|
||||
onChange={e => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
>
|
||||
<Radio value={true}>是</Radio>
|
||||
<Radio value={false}>否</Radio>
|
||||
</Radio.Group>
|
||||
@ -331,12 +389,20 @@ function getComponentTypeByField(field) {
|
||||
}
|
||||
|
||||
export function FilterItem(props: FilterItemProps) {
|
||||
const { index, fields = [], sourceFields = [], showDeleteButton = true, onDelete, onChange } = props;
|
||||
const defaultField: any = fields.find(field => field.name === props.dataSource.column) || {};
|
||||
const {
|
||||
index,
|
||||
fields = [],
|
||||
sourceFields = [],
|
||||
showDeleteButton = true,
|
||||
onDelete,
|
||||
onChange,
|
||||
} = props;
|
||||
const defaultField: any =
|
||||
fields.find(field => field.name === props.dataSource.column) || {};
|
||||
const componentType = getComponentTypeByField(defaultField);
|
||||
const [type, setType] = useState(defaultField.interface || 'string');
|
||||
const [field, setField] = useState<any>({});
|
||||
const [dataSource, setDataSource] = useState(props.dataSource||{});
|
||||
const [dataSource, setDataSource] = useState(props.dataSource || {});
|
||||
const [valueType, setValueType] = useState('custom');
|
||||
useEffect(() => {
|
||||
const field = fields.find(field => field.name === props.dataSource.column);
|
||||
@ -348,15 +414,15 @@ export function FilterItem(props: FilterItemProps) {
|
||||
}
|
||||
setType(componentType);
|
||||
}
|
||||
setDataSource({...props.dataSource});
|
||||
setDataSource({ ...props.dataSource });
|
||||
if (/^{{.+}}$/.test(props.dataSource.value)) {
|
||||
setValueType('ref');
|
||||
}
|
||||
}, [
|
||||
props.dataSource, type,
|
||||
]);
|
||||
let ValueControl = controls[componentType]||controls.string;
|
||||
if (['$null', '$notNull', '$isTruly', '$isFalsy'].indexOf(dataSource.op) !== -1) {
|
||||
}, [props.dataSource, type]);
|
||||
let ValueControl = controls[componentType] || controls.string;
|
||||
if (
|
||||
['$null', '$notNull', '$isTruly', '$isFalsy'].indexOf(dataSource.op) !== -1
|
||||
) {
|
||||
ValueControl = NullControl;
|
||||
}
|
||||
if (['boolean', 'checkbox'].indexOf(componentType) !== -1) {
|
||||
@ -364,12 +430,13 @@ export function FilterItem(props: FilterItemProps) {
|
||||
}
|
||||
// let multiple = true;
|
||||
// if ()
|
||||
const opOptions = op[defaultField.interface || 'string']||op.string;
|
||||
console.log({componentType, defaultField, field, valueType, opOptions});
|
||||
const opOptions = op[defaultField.interface || 'string'] || op.string;
|
||||
console.log({ componentType, defaultField, field, valueType, opOptions });
|
||||
return (
|
||||
<Space>
|
||||
<Select value={dataSource.column}
|
||||
onChange={(value) => {
|
||||
<Select
|
||||
value={dataSource.column}
|
||||
onChange={value => {
|
||||
const field = fields.find(field => field.name === value);
|
||||
let componentType = field.component.type;
|
||||
if (field.component.type === 'select' && field.multiple) {
|
||||
@ -377,17 +444,25 @@ export function FilterItem(props: FilterItemProps) {
|
||||
}
|
||||
setType(componentType);
|
||||
setValueType('custom');
|
||||
onChange({...dataSource, column: value, op: get(op, [componentType, 0, 'value']), value: undefined});
|
||||
onChange({
|
||||
...dataSource,
|
||||
column: value,
|
||||
op: get(op, [componentType, 0, 'value']),
|
||||
value: undefined,
|
||||
});
|
||||
}}
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}>
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}
|
||||
>
|
||||
{fields.map(field => (
|
||||
<Select.Option value={field.name}>{field.title}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Select value={dataSource.column ? dataSource.op : null} style={{ minWidth: 100 }}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, op: value});
|
||||
<Select
|
||||
value={dataSource.column ? dataSource.op : null}
|
||||
style={{ minWidth: 100 }}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, op: value });
|
||||
}}
|
||||
// value={get(opOptions, [0, 'value'])}
|
||||
options={opOptions}
|
||||
@ -399,44 +474,56 @@ export function FilterItem(props: FilterItemProps) {
|
||||
{sourceFields.length > 0 && (
|
||||
<Select
|
||||
style={{ minWidth: 100 }}
|
||||
onChange={(value) => {
|
||||
setDataSource({...dataSource, value: undefined})
|
||||
onChange({...dataSource, value: undefined});
|
||||
onChange={value => {
|
||||
setDataSource({ ...dataSource, value: undefined });
|
||||
onChange({ ...dataSource, value: undefined });
|
||||
setValueType(value);
|
||||
}}
|
||||
defaultValue={valueType}>
|
||||
defaultValue={valueType}
|
||||
>
|
||||
<Select.Option value={'custom'}>自定义</Select.Option>
|
||||
<Select.Option value={'ref'}>触发表字段</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
{valueType !== 'ref' ? (
|
||||
<ValueControl
|
||||
field={defaultField}
|
||||
multiple={componentType === 'checkboxes' || !!defaultField.multiple}
|
||||
op={dataSource.op}
|
||||
options={defaultField.dataSource}
|
||||
value={dataSource.value}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, value: value});
|
||||
<ValueControl
|
||||
field={defaultField}
|
||||
multiple={componentType === 'checkboxes' || !!defaultField.multiple}
|
||||
op={dataSource.op}
|
||||
options={defaultField.dataSource}
|
||||
value={dataSource.value}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, value: value });
|
||||
}}
|
||||
style={{ width: 180 }}
|
||||
/>
|
||||
) : (sourceFields.length > 0 ? (
|
||||
<Select value={dataSource.value}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, value: value});
|
||||
) : sourceFields.length > 0 ? (
|
||||
<Select
|
||||
value={dataSource.value}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, value: value });
|
||||
}}
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}>
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}
|
||||
>
|
||||
{sourceFields.map(field => (
|
||||
<Select.Option value={`{{ ${field.name} }}`}>{field.title}</Select.Option>
|
||||
<Select.Option value={`{{ ${field.name} }}`}>
|
||||
{field.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
) : null)}
|
||||
) : null}
|
||||
{showDeleteButton && (
|
||||
<Button className={'filter-remove-link filter-item'} type={'link'} style={{padding: 0}} onClick={(e) => {
|
||||
onDelete && onDelete(e);
|
||||
}}><CloseCircleOutlined /></Button>
|
||||
<Button
|
||||
className={'filter-remove-link filter-item'}
|
||||
type={'link'}
|
||||
style={{ padding: 0 }}
|
||||
onClick={e => {
|
||||
onDelete && onDelete(e);
|
||||
}}
|
||||
>
|
||||
<CloseCircleOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
@ -447,18 +534,27 @@ function toFilter(values: any) {
|
||||
let { type, andor = 'and', list = [], column, op, value } = values;
|
||||
if (type === 'group') {
|
||||
filter = {
|
||||
[andor]: list.map(value => toFilter(value)).filter(Boolean)
|
||||
}
|
||||
[andor]: list.map(value => toFilter(value)).filter(Boolean),
|
||||
};
|
||||
} else if (type === 'item' && column && op) {
|
||||
if (['id.$null', 'id.$notNull', '$null', '$notNull', '$isTruly', '$isFalsy'].indexOf(op) !== -1) {
|
||||
if (
|
||||
[
|
||||
'id.$null',
|
||||
'id.$notNull',
|
||||
'$null',
|
||||
'$notNull',
|
||||
'$isTruly',
|
||||
'$isFalsy',
|
||||
].indexOf(op) !== -1
|
||||
) {
|
||||
value = true;
|
||||
}
|
||||
// if (op === 'id.gt') {
|
||||
// value = 0;
|
||||
// }
|
||||
filter = {
|
||||
[`${column}`]: {[op]: value},
|
||||
}
|
||||
[`${column}`]: { [op]: value },
|
||||
};
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
@ -483,45 +579,72 @@ function toValues(filter: any = {}) {
|
||||
|
||||
export const Filter = connect({
|
||||
getProps: mapStyledProps,
|
||||
})((props) => {
|
||||
})(props => {
|
||||
const dataSource = {
|
||||
type: 'group',
|
||||
list: [
|
||||
{
|
||||
type: 'item',
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
const { value, onChange, associatedKey, filter = {}, sourceName, sourceFilter = {}, fields = [], ...restProps } = props;
|
||||
console.log('filter', {associatedKey})
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return associatedKey ? api.resource(`collections.fields`).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
}) : Promise.resolve({
|
||||
data: fields,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [associatedKey]
|
||||
});
|
||||
const {
|
||||
value,
|
||||
onChange,
|
||||
associatedKey,
|
||||
filter = {},
|
||||
sourceName,
|
||||
sourceFilter = {},
|
||||
fields = [],
|
||||
...restProps
|
||||
} = props;
|
||||
console.log('filter', { associatedKey });
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return associatedKey
|
||||
? api.resource(`collections.fields`).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
})
|
||||
: Promise.resolve({
|
||||
data: fields,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [associatedKey],
|
||||
},
|
||||
);
|
||||
|
||||
const { data: sourceFields = [] } = useRequest(
|
||||
() => {
|
||||
return sourceName
|
||||
? api.resource(`collections.fields`).list({
|
||||
associatedKey: sourceName,
|
||||
filter: sourceFilter,
|
||||
})
|
||||
: Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [sourceName],
|
||||
},
|
||||
);
|
||||
console.log({ sourceName, sourceFields });
|
||||
|
||||
const { data: sourceFields = [] } = useRequest(() => {
|
||||
return sourceName ? api.resource(`collections.fields`).list({
|
||||
associatedKey: sourceName,
|
||||
filter: sourceFilter,
|
||||
}) : Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [sourceName]
|
||||
});
|
||||
console.log({sourceName, sourceFields});
|
||||
|
||||
return <FilterGroup showDeleteButton={false} dataSource={value ? toValues(value) : dataSource} onChange={(values) => {
|
||||
console.log(values);
|
||||
onChange(toFilter(values));
|
||||
}} {...restProps} sourceFields={sourceFields} fields={data.filter(item => item.filterable)}/>
|
||||
return (
|
||||
<FilterGroup
|
||||
showDeleteButton={false}
|
||||
dataSource={value ? toValues(value) : dataSource}
|
||||
onChange={values => {
|
||||
console.log(values);
|
||||
onChange(toFilter(values));
|
||||
}}
|
||||
{...restProps}
|
||||
sourceFields={sourceFields}
|
||||
fields={data.filter(item => item.filterable)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Filter;
|
||||
|
@ -1,3 +1,3 @@
|
||||
.filter-remove-link {
|
||||
color:#d9d9d9;
|
||||
}
|
||||
color: #d9d9d9;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export default <T>(initialValue: T[]) => {
|
||||
};
|
||||
|
||||
const insert = (index: number, obj: T) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
const temp = [...l];
|
||||
temp.splice(index, 0, obj);
|
||||
setKey(index);
|
||||
@ -40,10 +40,11 @@ export default <T>(initialValue: T[]) => {
|
||||
|
||||
const getAll = () => list;
|
||||
const getKey = (index: number) => keyList.current[index];
|
||||
const getIndex = (index: number) => keyList.current.findIndex((ele) => ele === index);
|
||||
const getIndex = (index: number) =>
|
||||
keyList.current.findIndex(ele => ele === index);
|
||||
|
||||
const merge = (index: number, obj: T[]) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
const temp = [...l];
|
||||
obj.forEach((_, i) => {
|
||||
setKey(index + i);
|
||||
@ -54,7 +55,7 @@ export default <T>(initialValue: T[]) => {
|
||||
};
|
||||
|
||||
const replace = (index: number, obj: T) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
const temp = [...l];
|
||||
temp[index] = obj;
|
||||
return temp;
|
||||
@ -62,7 +63,7 @@ export default <T>(initialValue: T[]) => {
|
||||
};
|
||||
|
||||
const remove = (index: number) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
const temp = [...l];
|
||||
temp.splice(index, 1);
|
||||
|
||||
@ -80,14 +81,16 @@ export default <T>(initialValue: T[]) => {
|
||||
if (oldIndex === newIndex) {
|
||||
return;
|
||||
}
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
const newList = [...l];
|
||||
const temp = newList.filter((_: {}, index: number) => index !== oldIndex);
|
||||
temp.splice(newIndex, 0, newList[oldIndex]);
|
||||
|
||||
// move keys if necessary
|
||||
try {
|
||||
const keyTemp = keyList.current.filter((_: {}, index: number) => index !== oldIndex);
|
||||
const keyTemp = keyList.current.filter(
|
||||
(_: {}, index: number) => index !== oldIndex,
|
||||
);
|
||||
keyTemp.splice(newIndex, 0, keyList.current[oldIndex]);
|
||||
keyList.current = keyTemp;
|
||||
} catch (e) {
|
||||
@ -99,7 +102,7 @@ export default <T>(initialValue: T[]) => {
|
||||
};
|
||||
|
||||
const push = (obj: T) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
setKey(l.length);
|
||||
return l.concat([obj]);
|
||||
});
|
||||
@ -113,11 +116,11 @@ export default <T>(initialValue: T[]) => {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
setList((l) => l.slice(0, l.length - 1));
|
||||
setList(l => l.slice(0, l.length - 1));
|
||||
};
|
||||
|
||||
const unshift = (obj: T) => {
|
||||
setList((l) => {
|
||||
setList(l => {
|
||||
setKey(0);
|
||||
return [obj].concat(l);
|
||||
});
|
||||
@ -127,8 +130,8 @@ export default <T>(initialValue: T[]) => {
|
||||
result
|
||||
.map((item, index) => ({ key: index, item })) // add index into obj
|
||||
.sort((a, b) => getIndex(a.key) - getIndex(b.key)) // sort based on the index of table
|
||||
.filter((item) => !!item.item) // remove undefined(s)
|
||||
.map((item) => item.item); // retrive the data
|
||||
.filter(item => !!item.item) // remove undefined(s)
|
||||
.map(item => item.item); // retrive the data
|
||||
|
||||
const shift = () => {
|
||||
// remove keys if necessary
|
||||
@ -137,7 +140,7 @@ export default <T>(initialValue: T[]) => {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
setList((l) => l.slice(1, l.length));
|
||||
setList(l => l.slice(1, l.length));
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { Card } from 'antd'
|
||||
import { CardProps } from 'antd/lib/card'
|
||||
import styled from 'styled-components'
|
||||
import React from 'react';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { Card } from 'antd';
|
||||
import { CardProps } from 'antd/lib/card';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FormBlock = createVirtualBox<CardProps>(
|
||||
'block',
|
||||
@ -11,14 +11,14 @@ export const FormBlock = createVirtualBox<CardProps>(
|
||||
<Card className={className} size="small" {...props}>
|
||||
{children}
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
})`
|
||||
margin-bottom: 10px !important;
|
||||
&.ant-card {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
`
|
||||
)
|
||||
`,
|
||||
);
|
||||
|
||||
export default FormBlock
|
||||
export default FormBlock;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/card/style/index'
|
||||
import 'antd/lib/card/style/index';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { Card } from 'antd'
|
||||
import { CardProps } from 'antd/lib/card'
|
||||
import styled from 'styled-components'
|
||||
import React from 'react';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { Card } from 'antd';
|
||||
import { CardProps } from 'antd/lib/card';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FormCard = createVirtualBox<CardProps>(
|
||||
'card',
|
||||
@ -11,10 +11,10 @@ export const FormCard = createVirtualBox<CardProps>(
|
||||
<Card className={className} size="small" {...props}>
|
||||
{children}
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
})`
|
||||
margin-bottom: 10px !important;
|
||||
`
|
||||
)
|
||||
`,
|
||||
);
|
||||
|
||||
export default FormCard
|
||||
export default FormCard;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/card/style/index'
|
||||
import 'antd/lib/card/style/index';
|
||||
|
@ -1,21 +1,30 @@
|
||||
import React from 'react'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { Card } from 'antd'
|
||||
import styled from 'styled-components'
|
||||
import { markdown } from '@/components/views/Field'
|
||||
import React from 'react';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { Card } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import { markdown } from '@/components/views/Field';
|
||||
|
||||
export const FormDescription = createVirtualBox(
|
||||
'description',
|
||||
styled(({ schema = {}, children, className, ...props }) => {
|
||||
const { title, tooltip } = schema as any;
|
||||
console.log({schema})
|
||||
console.log({ schema });
|
||||
return (
|
||||
<Card title={title} size={'small'} headStyle={{padding: 0}} bodyStyle={{
|
||||
padding: 0,
|
||||
}} className={className} {...props}>
|
||||
{typeof tooltip === 'string' && tooltip && <div dangerouslySetInnerHTML={{__html: markdown(tooltip)}}></div>}
|
||||
<Card
|
||||
title={title}
|
||||
size={'small'}
|
||||
headStyle={{ padding: 0 }}
|
||||
bodyStyle={{
|
||||
padding: 0,
|
||||
}}
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
{typeof tooltip === 'string' && tooltip && (
|
||||
<div dangerouslySetInnerHTML={{ __html: markdown(tooltip) }}></div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
})`
|
||||
margin-bottom: 24px !important;
|
||||
&.ant-card {
|
||||
@ -35,7 +44,7 @@ export const FormDescription = createVirtualBox(
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
`,
|
||||
);
|
||||
|
||||
export default FormDescription
|
||||
export default FormDescription;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { Col } from 'antd'
|
||||
import { ColProps } from 'antd/lib/grid'
|
||||
import React from 'react';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { Col } from 'antd';
|
||||
import { ColProps } from 'antd/lib/grid';
|
||||
|
||||
export const FormGridCol = createVirtualBox<ColProps>('grid-col', props => {
|
||||
return <Col {...props}>{props.children}</Col>
|
||||
})
|
||||
return <Col {...props}>{props.children}</Col>;
|
||||
});
|
||||
|
||||
export default FormGridCol
|
||||
export default FormGridCol;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/col/style/index'
|
||||
import 'antd/lib/col/style/index';
|
||||
|
@ -1,25 +1,25 @@
|
||||
import React from 'react'
|
||||
import { AntdSchemaFieldAdaptor, pickFormItemProps } from '@formily/antd'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { Row } from 'antd'
|
||||
import { RowProps } from 'antd/lib/grid'
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form'
|
||||
import { IItemProps } from '../types'
|
||||
import React from 'react';
|
||||
import { AntdSchemaFieldAdaptor, pickFormItemProps } from '@formily/antd';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { Row } from 'antd';
|
||||
import { RowProps } from 'antd/lib/grid';
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form';
|
||||
import { IItemProps } from '../types';
|
||||
|
||||
export const FormGridRow = createVirtualBox<RowProps & ItemProps & IItemProps>(
|
||||
'grid-row',
|
||||
props => {
|
||||
const { title, label } = props
|
||||
const grids = <Row {...props}>{props.children}</Row>
|
||||
const { title, label } = props;
|
||||
const grids = <Row {...props}>{props.children}</Row>;
|
||||
if (title || label) {
|
||||
return (
|
||||
<AntdSchemaFieldAdaptor {...pickFormItemProps(props)}>
|
||||
{grids}
|
||||
</AntdSchemaFieldAdaptor>
|
||||
)
|
||||
);
|
||||
}
|
||||
return grids
|
||||
}
|
||||
)
|
||||
return grids;
|
||||
},
|
||||
);
|
||||
|
||||
export default FormGridRow
|
||||
export default FormGridRow;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/row/style/index'
|
||||
import 'antd/lib/row/style/index';
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
AntdSchemaFieldAdaptor,
|
||||
pickFormItemProps,
|
||||
pickNotFormItemProps
|
||||
} from '@formily/antd'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import { toArr } from '@formily/shared'
|
||||
import { Row, Col } from 'antd'
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form'
|
||||
import { IFormItemGridProps, IItemProps } from '../types'
|
||||
import { normalizeCol } from '../shared'
|
||||
pickNotFormItemProps,
|
||||
} from '@formily/antd';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import { toArr } from '@formily/shared';
|
||||
import { Row, Col } from 'antd';
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form';
|
||||
import { IFormItemGridProps, IItemProps } from '../types';
|
||||
import { normalizeCol } from '../shared';
|
||||
|
||||
export const FormItemGrid = createVirtualBox<
|
||||
React.PropsWithChildren<IFormItemGridProps & ItemProps & IItemProps>
|
||||
@ -18,16 +18,16 @@ export const FormItemGrid = createVirtualBox<
|
||||
cols: rawCols,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
title,
|
||||
label
|
||||
} = props
|
||||
const formItemProps = pickFormItemProps(props)
|
||||
const gridProps = pickNotFormItemProps(props)
|
||||
const children = toArr(props.children)
|
||||
const cols = toArr(rawCols).map(col => normalizeCol(col))
|
||||
const childNum = children.length
|
||||
label,
|
||||
} = props;
|
||||
const formItemProps = pickFormItemProps(props);
|
||||
const gridProps = pickNotFormItemProps(props);
|
||||
const children = toArr(props.children);
|
||||
const cols = toArr(rawCols).map(col => normalizeCol(col));
|
||||
const childNum = children.length;
|
||||
|
||||
if (cols.length < childNum) {
|
||||
let offset: number = childNum - cols.length
|
||||
let offset: number = childNum - cols.length;
|
||||
let lastSpan: number =
|
||||
24 -
|
||||
cols.reduce((buf, col) => {
|
||||
@ -35,10 +35,10 @@ export const FormItemGrid = createVirtualBox<
|
||||
buf +
|
||||
Number(col.span ? col.span : 0) +
|
||||
Number(col.offset ? col.offset : 0)
|
||||
)
|
||||
}, 0)
|
||||
);
|
||||
}, 0);
|
||||
for (let i = 0; i < offset; i++) {
|
||||
cols.push({ span: Math.floor(lastSpan / offset) })
|
||||
cols.push({ span: Math.floor(lastSpan / offset) });
|
||||
}
|
||||
}
|
||||
const grids = (
|
||||
@ -48,21 +48,21 @@ export const FormItemGrid = createVirtualBox<
|
||||
? buf.concat(
|
||||
<Col key={key} {...cols[key]}>
|
||||
{child}
|
||||
</Col>
|
||||
</Col>,
|
||||
)
|
||||
: buf
|
||||
: buf;
|
||||
}, [])}
|
||||
</Row>
|
||||
)
|
||||
);
|
||||
|
||||
if (title || label) {
|
||||
return (
|
||||
<AntdSchemaFieldAdaptor {...formItemProps}>
|
||||
{grids}
|
||||
</AntdSchemaFieldAdaptor>
|
||||
)
|
||||
);
|
||||
}
|
||||
return <Fragment>{grids}</Fragment>
|
||||
})
|
||||
return <Fragment>{grids}</Fragment>;
|
||||
});
|
||||
|
||||
export default FormItemGrid
|
||||
export default FormItemGrid;
|
||||
|
@ -1,2 +1,2 @@
|
||||
import 'antd/lib/row/style/index'
|
||||
import 'antd/lib/col/style/index'
|
||||
import 'antd/lib/row/style/index';
|
||||
import 'antd/lib/col/style/index';
|
||||
|
@ -1,19 +1,19 @@
|
||||
import React from 'react'
|
||||
import { FormItemDeepProvider, useDeepFormItem } from '@formily/antd'
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer'
|
||||
import cls from 'classnames'
|
||||
import { IFormItemTopProps } from '../types'
|
||||
import React from 'react';
|
||||
import { FormItemDeepProvider, useDeepFormItem } from '@formily/antd';
|
||||
import { createVirtualBox } from '@formily/react-schema-renderer';
|
||||
import cls from 'classnames';
|
||||
import { IFormItemTopProps } from '../types';
|
||||
|
||||
export const FormLayout = createVirtualBox<IFormItemTopProps>(
|
||||
'layout',
|
||||
props => {
|
||||
const { inline } = useDeepFormItem()
|
||||
const isInline = props.inline || inline
|
||||
const { inline } = useDeepFormItem();
|
||||
const isInline = props.inline || inline;
|
||||
const children =
|
||||
isInline || props.className || props.style ? (
|
||||
<div
|
||||
className={cls(props.className, {
|
||||
'ant-form ant-form-inline': isInline
|
||||
'ant-form ant-form-inline': isInline,
|
||||
})}
|
||||
style={props.style}
|
||||
>
|
||||
@ -21,9 +21,9 @@ export const FormLayout = createVirtualBox<IFormItemTopProps>(
|
||||
</div>
|
||||
) : (
|
||||
props.children
|
||||
)
|
||||
return <FormItemDeepProvider {...props}>{children}</FormItemDeepProvider>
|
||||
}
|
||||
)
|
||||
);
|
||||
return <FormItemDeepProvider {...props}>{children}</FormItemDeepProvider>;
|
||||
},
|
||||
);
|
||||
|
||||
export default FormLayout
|
||||
export default FormLayout;
|
||||
|
@ -1,6 +1,3 @@
|
||||
import { MegaLayout, FormMegaLayout } from '@formily/antd'
|
||||
import { MegaLayout, FormMegaLayout } from '@formily/antd';
|
||||
|
||||
export {
|
||||
MegaLayout,
|
||||
FormMegaLayout,
|
||||
}
|
||||
export { MegaLayout, FormMegaLayout };
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FormSlot } from '@formily/react-schema-renderer'
|
||||
import { FormSlot } from '@formily/react-schema-renderer';
|
||||
|
||||
export { FormSlot }
|
||||
export { FormSlot };
|
||||
|
||||
export default FormSlot
|
||||
export default FormSlot;
|
||||
|
@ -1,35 +1,35 @@
|
||||
import React, { useRef, Fragment, useEffect } from 'react'
|
||||
import React, { useRef, Fragment, useEffect } from 'react';
|
||||
import {
|
||||
createControllerBox,
|
||||
ISchemaVirtualFieldComponentProps,
|
||||
createEffectHook,
|
||||
useFormEffects,
|
||||
useFieldState,
|
||||
IVirtualBoxProps
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { toArr } from '@formily/shared'
|
||||
import { Steps } from 'antd'
|
||||
import { createMatchUpdate } from '../shared'
|
||||
import { IFormStep } from '../types'
|
||||
IVirtualBoxProps,
|
||||
} from '@formily/react-schema-renderer';
|
||||
import { toArr } from '@formily/shared';
|
||||
import { Steps } from 'antd';
|
||||
import { createMatchUpdate } from '../shared';
|
||||
import { IFormStep } from '../types';
|
||||
|
||||
enum StateMap {
|
||||
ON_FORM_STEP_NEXT = 'onFormStepNext',
|
||||
ON_FORM_STEP_PREVIOUS = 'onFormStepPrevious',
|
||||
ON_FORM_STEP_GO_TO = 'onFormStepGoto',
|
||||
ON_FORM_STEP_CURRENT_CHANGE = 'onFormStepCurrentChange',
|
||||
ON_FORM_STEP_DATA_SOURCE_CHANGED = 'onFormStepDataSourceChanged'
|
||||
ON_FORM_STEP_DATA_SOURCE_CHANGED = 'onFormStepDataSourceChanged',
|
||||
}
|
||||
const EffectHooks = {
|
||||
onStepNext$: createEffectHook<void>(StateMap.ON_FORM_STEP_NEXT),
|
||||
onStepPrevious$: createEffectHook<void>(StateMap.ON_FORM_STEP_PREVIOUS),
|
||||
onStepGoto$: createEffectHook<void>(StateMap.ON_FORM_STEP_GO_TO),
|
||||
onStepCurrentChange$: createEffectHook<{
|
||||
value: number
|
||||
preValue: number
|
||||
}>(StateMap.ON_FORM_STEP_CURRENT_CHANGE)
|
||||
}
|
||||
value: number;
|
||||
preValue: number;
|
||||
}>(StateMap.ON_FORM_STEP_CURRENT_CHANGE),
|
||||
};
|
||||
|
||||
type ExtendsProps = StateMap & typeof EffectHooks
|
||||
type ExtendsProps = StateMap & typeof EffectHooks;
|
||||
|
||||
export const FormStep: React.FC<IVirtualBoxProps<IFormStep>> &
|
||||
ExtendsProps = createControllerBox<IFormStep>(
|
||||
@ -39,54 +39,54 @@ export const FormStep: React.FC<IVirtualBoxProps<IFormStep>> &
|
||||
schema,
|
||||
path,
|
||||
name,
|
||||
children
|
||||
children,
|
||||
}: ISchemaVirtualFieldComponentProps) => {
|
||||
const { dataSource, ...stepProps } = schema.getExtendsComponentProps()
|
||||
const { dataSource, ...stepProps } = schema.getExtendsComponentProps();
|
||||
const [{ current }, setFieldState] = useFieldState({
|
||||
current: stepProps.current || 0
|
||||
})
|
||||
const ref = useRef(current)
|
||||
const itemsRef = useRef([])
|
||||
itemsRef.current = toArr(dataSource)
|
||||
current: stepProps.current || 0,
|
||||
});
|
||||
const ref = useRef(current);
|
||||
const itemsRef = useRef([]);
|
||||
itemsRef.current = toArr(dataSource);
|
||||
|
||||
const matchUpdate = createMatchUpdate(name, path)
|
||||
const matchUpdate = createMatchUpdate(name, path);
|
||||
|
||||
const update = (cur: number) => {
|
||||
form.notify(StateMap.ON_FORM_STEP_CURRENT_CHANGE, {
|
||||
path,
|
||||
name,
|
||||
value: cur,
|
||||
preValue: current
|
||||
})
|
||||
preValue: current,
|
||||
});
|
||||
setFieldState({
|
||||
current: cur
|
||||
})
|
||||
}
|
||||
current: cur,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.notify(StateMap.ON_FORM_STEP_DATA_SOURCE_CHANGED, {
|
||||
path,
|
||||
name,
|
||||
value: itemsRef.current
|
||||
})
|
||||
}, [itemsRef.current.length])
|
||||
value: itemsRef.current,
|
||||
});
|
||||
}, [itemsRef.current.length]);
|
||||
|
||||
useFormEffects(($, { setFieldState }) => {
|
||||
const updateFields = () => {
|
||||
itemsRef.current.forEach(({ name }, index) => {
|
||||
setFieldState(name, (state: any) => {
|
||||
state.display = index === current
|
||||
})
|
||||
})
|
||||
}
|
||||
updateFields()
|
||||
state.display = index === current;
|
||||
});
|
||||
});
|
||||
};
|
||||
updateFields();
|
||||
$(StateMap.ON_FORM_STEP_DATA_SOURCE_CHANGED).subscribe(
|
||||
({ name, path }) => {
|
||||
matchUpdate(name, path, () => {
|
||||
updateFields()
|
||||
})
|
||||
}
|
||||
)
|
||||
updateFields();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$(StateMap.ON_FORM_STEP_CURRENT_CHANGE).subscribe(
|
||||
({ value, name, path }: any = {}) => {
|
||||
@ -95,16 +95,16 @@ export const FormStep: React.FC<IVirtualBoxProps<IFormStep>> &
|
||||
itemsRef.current.forEach(({ name }, index) => {
|
||||
if (!name)
|
||||
throw new Error(
|
||||
'FormStep dataSource must include `name` property'
|
||||
)
|
||||
'FormStep dataSource must include `name` property',
|
||||
);
|
||||
setFieldState(name, (state: any) => {
|
||||
state.display = index === value
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
state.display = index === value;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$(StateMap.ON_FORM_STEP_NEXT).subscribe(({ name, path }: any = {}) => {
|
||||
matchUpdate(name, path, () => {
|
||||
@ -113,45 +113,45 @@ export const FormStep: React.FC<IVirtualBoxProps<IFormStep>> &
|
||||
update(
|
||||
ref.current + 1 > itemsRef.current.length - 1
|
||||
? ref.current
|
||||
: ref.current + 1
|
||||
)
|
||||
: ref.current + 1,
|
||||
);
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(StateMap.ON_FORM_STEP_PREVIOUS).subscribe(
|
||||
({ name, path }: any = {}) => {
|
||||
matchUpdate(name, path, () => {
|
||||
update(ref.current - 1 < 0 ? ref.current : ref.current - 1)
|
||||
})
|
||||
}
|
||||
)
|
||||
update(ref.current - 1 < 0 ? ref.current : ref.current - 1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$(StateMap.ON_FORM_STEP_GO_TO).subscribe(
|
||||
({ name, path, value }: any = {}) => {
|
||||
matchUpdate(name, path, () => {
|
||||
if (!(value < 0 || value > itemsRef.current.length)) {
|
||||
update(value)
|
||||
update(value);
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
ref.current = current
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
ref.current = current;
|
||||
return (
|
||||
<Fragment>
|
||||
<Steps {...stepProps} current={current}>
|
||||
{itemsRef.current.map((props, key) => {
|
||||
return <Steps.Step {...props} key={key} />
|
||||
return <Steps.Step {...props} key={key} />;
|
||||
})}
|
||||
</Steps>{' '}
|
||||
{children}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
) as any
|
||||
);
|
||||
},
|
||||
) as any;
|
||||
|
||||
Object.assign(FormStep, StateMap, EffectHooks)
|
||||
Object.assign(FormStep, StateMap, EffectHooks);
|
||||
|
||||
export default FormStep
|
||||
export default FormStep;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/steps/style/index'
|
||||
import 'antd/lib/steps/style/index';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useEffect, useRef } from 'react'
|
||||
import React, { Fragment, useEffect, useRef } from 'react';
|
||||
import {
|
||||
createControllerBox,
|
||||
ISchemaVirtualFieldComponentProps,
|
||||
@ -8,140 +8,156 @@ import {
|
||||
FormEffectHooks,
|
||||
SchemaField,
|
||||
FormPath,
|
||||
IVirtualBoxProps
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { Tabs, Badge } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import { IFormTab } from '../types'
|
||||
import { createMatchUpdate } from '../shared'
|
||||
IVirtualBoxProps,
|
||||
} from '@formily/react-schema-renderer';
|
||||
import { Tabs, Badge } from 'antd';
|
||||
import { TabPaneProps } from 'antd/lib/tabs';
|
||||
import { IFormTab } from '../types';
|
||||
import { createMatchUpdate } from '../shared';
|
||||
|
||||
enum StateMap {
|
||||
ON_FORM_TAB_ACTIVE_KEY_CHANGE = 'onFormTabActiveKeyChange'
|
||||
ON_FORM_TAB_ACTIVE_KEY_CHANGE = 'onFormTabActiveKeyChange',
|
||||
}
|
||||
|
||||
const { onFormChange$ } = FormEffectHooks
|
||||
const { onFormChange$ } = FormEffectHooks;
|
||||
|
||||
const EffectHooks = {
|
||||
onTabActiveKeyChange$: createEffectHook<{
|
||||
name?: string
|
||||
path?: string
|
||||
value?: any
|
||||
}>(StateMap.ON_FORM_TAB_ACTIVE_KEY_CHANGE)
|
||||
}
|
||||
name?: string;
|
||||
path?: string;
|
||||
value?: any;
|
||||
}>(StateMap.ON_FORM_TAB_ACTIVE_KEY_CHANGE),
|
||||
};
|
||||
|
||||
const parseTabItems = (items: any, hiddenKeys?: string[]) => {
|
||||
return items.reduce((buf: any, { schema, key }) => {
|
||||
if (Array.isArray(hiddenKeys)) {
|
||||
if (hiddenKeys.includes(key)) {
|
||||
return buf
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
if (schema.getExtendsComponent() === 'tabpane') {
|
||||
return buf.concat({
|
||||
props: schema.getExtendsComponentProps(),
|
||||
schema,
|
||||
key
|
||||
})
|
||||
key,
|
||||
});
|
||||
}
|
||||
return buf
|
||||
}, [])
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const parseDefaultActiveKey = (hiddenKeys: Array<string> = [], items: any, defaultActiveKey) => {
|
||||
if(!hiddenKeys.includes(defaultActiveKey))return defaultActiveKey
|
||||
const parseDefaultActiveKey = (
|
||||
hiddenKeys: Array<string> = [],
|
||||
items: any,
|
||||
defaultActiveKey,
|
||||
) => {
|
||||
if (!hiddenKeys.includes(defaultActiveKey)) return defaultActiveKey;
|
||||
|
||||
const index = items.findIndex(item => !hiddenKeys.includes(item.key))
|
||||
return index >= 0 ? items[index].key : ''
|
||||
}
|
||||
const index = items.findIndex(item => !hiddenKeys.includes(item.key));
|
||||
return index >= 0 ? items[index].key : '';
|
||||
};
|
||||
|
||||
const parseChildrenErrors = (errors: any, target: string) => {
|
||||
return errors.filter(({ path }) => {
|
||||
return FormPath.parse(path).includes(target)
|
||||
})
|
||||
}
|
||||
return FormPath.parse(path).includes(target);
|
||||
});
|
||||
};
|
||||
|
||||
const addErrorBadge = (
|
||||
tab: React.ReactNode,
|
||||
currentPath: FormPath,
|
||||
childrenErrors: any[]
|
||||
childrenErrors: any[],
|
||||
) => {
|
||||
const currentErrors = childrenErrors.filter(({ path }) => {
|
||||
return FormPath.parse(path).includes(currentPath)
|
||||
})
|
||||
return FormPath.parse(path).includes(currentPath);
|
||||
});
|
||||
if (currentErrors.length > 0) {
|
||||
return (
|
||||
<Badge offset={[12, 0]} count={currentErrors.length}>
|
||||
{tab}
|
||||
</Badge>
|
||||
)
|
||||
);
|
||||
}
|
||||
return tab
|
||||
}
|
||||
return tab;
|
||||
};
|
||||
|
||||
type ExtendsProps = StateMap &
|
||||
typeof EffectHooks & {
|
||||
TabPane: React.FC<IVirtualBoxProps<TabPaneProps>>
|
||||
}
|
||||
TabPane: React.FC<IVirtualBoxProps<TabPaneProps>>;
|
||||
};
|
||||
|
||||
type ExtendsState = {
|
||||
activeKey?: string
|
||||
childrenErrors?: any
|
||||
}
|
||||
activeKey?: string;
|
||||
childrenErrors?: any;
|
||||
};
|
||||
|
||||
export const FormTab: React.FC<IVirtualBoxProps<IFormTab>> &
|
||||
ExtendsProps = createControllerBox<IFormTab>(
|
||||
'tab',
|
||||
({ form, schema, name, path }: ISchemaVirtualFieldComponentProps) => {
|
||||
const orderProperties = schema.getOrderProperties()
|
||||
let { hiddenKeys, defaultActiveKey, ...componentProps } = schema.getExtendsComponentProps()
|
||||
hiddenKeys = hiddenKeys || []
|
||||
const orderProperties = schema.getOrderProperties();
|
||||
let {
|
||||
hiddenKeys,
|
||||
defaultActiveKey,
|
||||
...componentProps
|
||||
} = schema.getExtendsComponentProps();
|
||||
hiddenKeys = hiddenKeys || [];
|
||||
const [{ activeKey, childrenErrors }, setFieldState] = useFieldState<
|
||||
ExtendsState
|
||||
>({
|
||||
activeKey: parseDefaultActiveKey(hiddenKeys, orderProperties, defaultActiveKey),
|
||||
childrenErrors: []
|
||||
})
|
||||
const itemsRef = useRef([])
|
||||
itemsRef.current = parseTabItems(orderProperties, hiddenKeys)
|
||||
activeKey: parseDefaultActiveKey(
|
||||
hiddenKeys,
|
||||
orderProperties,
|
||||
defaultActiveKey,
|
||||
),
|
||||
childrenErrors: [],
|
||||
});
|
||||
const itemsRef = useRef([]);
|
||||
itemsRef.current = parseTabItems(orderProperties, hiddenKeys);
|
||||
const update = (cur: string) => {
|
||||
form.notify(StateMap.ON_FORM_TAB_ACTIVE_KEY_CHANGE, {
|
||||
name,
|
||||
path,
|
||||
value: cur
|
||||
})
|
||||
}
|
||||
const matchUpdate = createMatchUpdate(name, path)
|
||||
value: cur,
|
||||
});
|
||||
};
|
||||
const matchUpdate = createMatchUpdate(name, path);
|
||||
useEffect(() => {
|
||||
if (Array.isArray(hiddenKeys)) {
|
||||
setFieldState({
|
||||
activeKey: parseDefaultActiveKey(hiddenKeys, orderProperties, defaultActiveKey)
|
||||
})
|
||||
activeKey: parseDefaultActiveKey(
|
||||
hiddenKeys,
|
||||
orderProperties,
|
||||
defaultActiveKey,
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [hiddenKeys.length])
|
||||
}, [hiddenKeys.length]);
|
||||
useFormEffects(({ hasChanged }) => {
|
||||
onFormChange$().subscribe(formState => {
|
||||
const errorsChanged = hasChanged(formState, 'errors')
|
||||
const errorsChanged = hasChanged(formState, 'errors');
|
||||
if (errorsChanged) {
|
||||
setFieldState({
|
||||
childrenErrors: parseChildrenErrors(formState.errors, path)
|
||||
})
|
||||
childrenErrors: parseChildrenErrors(formState.errors, path),
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
EffectHooks.onTabActiveKeyChange$().subscribe(
|
||||
({ value, name, path }: any = {}) => {
|
||||
if(!itemsRef.current.map(item => item.key).includes(value))return
|
||||
if (!itemsRef.current.map(item => item.key).includes(value)) return;
|
||||
matchUpdate(name, path, () => {
|
||||
setFieldState({
|
||||
activeKey: value
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
activeKey: value,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Tabs {...componentProps} activeKey={activeKey} onChange={update}>
|
||||
{itemsRef.current.map(({ props, schema, key }) => {
|
||||
const currentPath = FormPath.parse(path).concat(key)
|
||||
const currentPath = FormPath.parse(path).concat(key);
|
||||
return (
|
||||
<Tabs.TabPane
|
||||
{...props}
|
||||
@ -159,20 +175,20 @@ export const FormTab: React.FC<IVirtualBoxProps<IFormTab>> &
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
) as any
|
||||
);
|
||||
},
|
||||
) as any;
|
||||
|
||||
FormTab.TabPane = createControllerBox<TabPaneProps>(
|
||||
'tabpane',
|
||||
({ children }) => {
|
||||
return <Fragment>{children}</Fragment>
|
||||
}
|
||||
)
|
||||
return <Fragment>{children}</Fragment>;
|
||||
},
|
||||
);
|
||||
|
||||
Object.assign(FormTab, StateMap, EffectHooks)
|
||||
Object.assign(FormTab, StateMap, EffectHooks);
|
||||
|
||||
export default FormTab
|
||||
export default FormTab;
|
||||
|
@ -1,2 +1,2 @@
|
||||
import 'antd/lib/tabs/style/index'
|
||||
import 'antd/lib/badge/style/index'
|
||||
import 'antd/lib/tabs/style/index';
|
||||
import 'antd/lib/badge/style/index';
|
||||
|
@ -1,64 +1,64 @@
|
||||
import React, { useRef, useLayoutEffect } from 'react'
|
||||
import { createControllerBox, Schema } from '@formily/react-schema-renderer'
|
||||
import { IFormTextBox } from '../types'
|
||||
import { toArr } from '@formily/shared'
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form'
|
||||
import { version } from 'antd'
|
||||
import { AntdSchemaFieldAdaptor, pickFormItemProps } from '@formily/antd'
|
||||
import styled from 'styled-components'
|
||||
import React, { useRef, useLayoutEffect } from 'react';
|
||||
import { createControllerBox, Schema } from '@formily/react-schema-renderer';
|
||||
import { IFormTextBox } from '../types';
|
||||
import { toArr } from '@formily/shared';
|
||||
import { FormItemProps as ItemProps } from 'antd/lib/form';
|
||||
import { version } from 'antd';
|
||||
import { AntdSchemaFieldAdaptor, pickFormItemProps } from '@formily/antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const isV4 = /^4\./.test(version)
|
||||
const isV4 = /^4\./.test(version);
|
||||
|
||||
export const FormTextBox = createControllerBox<IFormTextBox & ItemProps>(
|
||||
'text-box',
|
||||
styled(({ props, form, className, children }) => {
|
||||
const schema = new Schema(props)
|
||||
const mergeProps = schema.getExtendsComponentProps()
|
||||
const schema = new Schema(props);
|
||||
const mergeProps = schema.getExtendsComponentProps();
|
||||
const { title, label, text, gutter, style } = Object.assign(
|
||||
{
|
||||
gutter: 5
|
||||
gutter: 5,
|
||||
},
|
||||
mergeProps
|
||||
)
|
||||
const formItemProps = pickFormItemProps(mergeProps)
|
||||
const ref: React.RefObject<HTMLDivElement> = useRef()
|
||||
const arrChildren = toArr(children)
|
||||
const split = text.split('%s')
|
||||
let index = 0
|
||||
mergeProps,
|
||||
);
|
||||
const formItemProps = pickFormItemProps(mergeProps);
|
||||
const ref: React.RefObject<HTMLDivElement> = useRef();
|
||||
const arrChildren = toArr(children);
|
||||
const split = text.split('%s');
|
||||
let index = 0;
|
||||
useLayoutEffect(() => {
|
||||
if (ref.current) {
|
||||
const elements = ref.current.querySelectorAll('.text-box-field')
|
||||
const elements = ref.current.querySelectorAll('.text-box-field');
|
||||
const syncLayouts = Array.prototype.map.call(
|
||||
elements,
|
||||
(el: HTMLElement) => {
|
||||
return [
|
||||
el,
|
||||
() => {
|
||||
const ctrl = el.querySelector('.ant-form-item-children')
|
||||
const ctrl = el.querySelector('.ant-form-item-children');
|
||||
setTimeout(() => {
|
||||
if (ctrl) {
|
||||
const editable = form.getFormState(state => state.editable)
|
||||
const editable = form.getFormState(state => state.editable);
|
||||
el.style.width = editable
|
||||
? ctrl.getBoundingClientRect().width + 'px'
|
||||
: 'auto'
|
||||
: 'auto';
|
||||
}
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
});
|
||||
},
|
||||
];
|
||||
},
|
||||
);
|
||||
syncLayouts.forEach(([el, handler]) => {
|
||||
handler()
|
||||
el.addEventListener('DOMSubtreeModified', handler)
|
||||
})
|
||||
handler();
|
||||
el.addEventListener('DOMSubtreeModified', handler);
|
||||
});
|
||||
|
||||
return () => {
|
||||
syncLayouts.forEach(([el, handler]) => {
|
||||
el.removeEventListener('DOMSubtreeModified', handler)
|
||||
})
|
||||
}
|
||||
el.removeEventListener('DOMSubtreeModified', handler);
|
||||
});
|
||||
};
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
const newChildren = split.reduce((buf, item, key) => {
|
||||
return buf.concat(
|
||||
item ? (
|
||||
@ -68,7 +68,7 @@ export const FormTextBox = createControllerBox<IFormTextBox & ItemProps>(
|
||||
style={{
|
||||
marginRight: gutter / 2,
|
||||
marginLeft: gutter / 2,
|
||||
...style
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
@ -78,29 +78,29 @@ export const FormTextBox = createControllerBox<IFormTextBox & ItemProps>(
|
||||
<div key={index++} className="text-box-field">
|
||||
{arrChildren[key]}
|
||||
</div>
|
||||
) : null
|
||||
)
|
||||
}, [])
|
||||
) : null,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const textChildren = (
|
||||
<div
|
||||
className={`${className} ${mergeProps.className}`}
|
||||
style={{
|
||||
marginRight: -gutter / 2,
|
||||
marginLeft: -gutter / 2
|
||||
marginLeft: -gutter / 2,
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
{newChildren}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
if (!title && !label) return textChildren
|
||||
if (!title && !label) return textChildren;
|
||||
return (
|
||||
<AntdSchemaFieldAdaptor {...formItemProps}>
|
||||
{textChildren}
|
||||
</AntdSchemaFieldAdaptor>
|
||||
)
|
||||
);
|
||||
})`
|
||||
display: flex;
|
||||
.text-box-words:nth-child(1) {
|
||||
@ -122,7 +122,7 @@ export const FormTextBox = createControllerBox<IFormTextBox & ItemProps>(
|
||||
.preview-text {
|
||||
text-align: center !important;
|
||||
}
|
||||
`
|
||||
)
|
||||
`,
|
||||
);
|
||||
|
||||
export default FormTextBox
|
||||
export default FormTextBox;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Popover, Button } from 'antd'
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Popover, Button } from 'antd';
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared';
|
||||
import { icons, hasIcon, Icon as IconComponent } from '@/components/icons';
|
||||
|
||||
function IconField(props: any) {
|
||||
@ -9,19 +9,33 @@ function IconField(props: any) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<div>
|
||||
<Popover placement={'bottom'} visible={visible} onVisibleChange={(val) => {
|
||||
setVisible(val)
|
||||
}} content={(
|
||||
<div>
|
||||
{[...icons.keys()].map(key => (
|
||||
<span style={{fontSize: 18, marginRight: 10, cursor: 'pointer'}} onClick={() => {
|
||||
onChange(key);
|
||||
setVisible(false)
|
||||
}}><IconComponent type={key}/></span>
|
||||
))}
|
||||
</div>
|
||||
)} title="图标" trigger="click">
|
||||
<Button>{ hasIcon(value) ? <IconComponent type={value}/> : '选择图标'}</Button>
|
||||
<Popover
|
||||
placement={'bottom'}
|
||||
visible={visible}
|
||||
onVisibleChange={val => {
|
||||
setVisible(val);
|
||||
}}
|
||||
content={
|
||||
<div>
|
||||
{[...icons.keys()].map(key => (
|
||||
<span
|
||||
style={{ fontSize: 18, marginRight: 10, cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
onChange(key);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<IconComponent type={key} />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
title="图标"
|
||||
trigger="click"
|
||||
>
|
||||
<Button>
|
||||
{hasIcon(value) ? <IconComponent type={value} /> : '选择图标'}
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
@ -29,7 +43,7 @@ function IconField(props: any) {
|
||||
|
||||
export const Icon = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(IconField)
|
||||
getComponent: mapTextComponent,
|
||||
})(IconField);
|
||||
|
||||
export default Icon
|
||||
export default Icon;
|
||||
|
@ -1,30 +1,30 @@
|
||||
export * from './text-button'
|
||||
export * from './time-picker'
|
||||
export * from './transfer'
|
||||
export * from './switch'
|
||||
export * from './array-cards'
|
||||
export * from './array-table'
|
||||
export * from './checkbox'
|
||||
export * from './circle-button'
|
||||
export * from './date-picker'
|
||||
export * from './form-block'
|
||||
export * from './form-card'
|
||||
export * from './form-tab'
|
||||
export * from './form-grid-col'
|
||||
export * from './form-grid-row'
|
||||
export * from './form-item-grid'
|
||||
export * from './form-layout'
|
||||
export * from './form-mega-layout'
|
||||
export * from './form-description'
|
||||
export * from './form-step'
|
||||
export * from './form-text-box'
|
||||
export * from './form-slot'
|
||||
export * from './input'
|
||||
export * from './select'
|
||||
export * from './number-picker'
|
||||
export * from './password'
|
||||
export * from './radio'
|
||||
export * from './range'
|
||||
export * from './rating'
|
||||
export * from './upload'
|
||||
export * from './registry'
|
||||
export * from './text-button';
|
||||
export * from './time-picker';
|
||||
export * from './transfer';
|
||||
export * from './switch';
|
||||
export * from './array-cards';
|
||||
export * from './array-table';
|
||||
export * from './checkbox';
|
||||
export * from './circle-button';
|
||||
export * from './date-picker';
|
||||
export * from './form-block';
|
||||
export * from './form-card';
|
||||
export * from './form-tab';
|
||||
export * from './form-grid-col';
|
||||
export * from './form-grid-row';
|
||||
export * from './form-item-grid';
|
||||
export * from './form-layout';
|
||||
export * from './form-mega-layout';
|
||||
export * from './form-description';
|
||||
export * from './form-step';
|
||||
export * from './form-text-box';
|
||||
export * from './form-slot';
|
||||
export * from './input';
|
||||
export * from './select';
|
||||
export * from './number-picker';
|
||||
export * from './password';
|
||||
export * from './radio';
|
||||
export * from './range';
|
||||
export * from './rating';
|
||||
export * from './upload';
|
||||
export * from './registry';
|
||||
|
@ -1,25 +1,31 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import React from 'react';
|
||||
import { Input as AntdInput } from 'antd'
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared'
|
||||
import { Input as AntdInput } from 'antd';
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared';
|
||||
|
||||
export const Input = connect<'TextArea'>({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(acceptEnum(({onChange, ...restProps}) => (
|
||||
<AntdInput
|
||||
autoComplete={'off'}
|
||||
{...restProps}
|
||||
onChange={(e) => {
|
||||
// 文本字段,如果空要 null 处理
|
||||
onChange(e.target.value ? e : null);
|
||||
}}
|
||||
/>
|
||||
)))
|
||||
getComponent: mapTextComponent,
|
||||
})(
|
||||
acceptEnum(({ onChange, ...restProps }) => (
|
||||
<AntdInput
|
||||
autoComplete={'off'}
|
||||
{...restProps}
|
||||
onChange={e => {
|
||||
// 文本字段,如果空要 null 处理
|
||||
onChange(e.target.value ? e : null);
|
||||
}}
|
||||
/>
|
||||
)),
|
||||
);
|
||||
|
||||
Input.TextArea = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(acceptEnum((props) => <AntdInput.TextArea autoSize={{minRows: 2, maxRows: 12}} {...props}/>))
|
||||
getComponent: mapTextComponent,
|
||||
})(
|
||||
acceptEnum(props => (
|
||||
<AntdInput.TextArea autoSize={{ minRows: 2, maxRows: 12 }} {...props} />
|
||||
)),
|
||||
);
|
||||
|
||||
export default Input
|
||||
export default Input;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/input/style/index'
|
||||
import 'antd/lib/input/style/index';
|
||||
|
@ -1,11 +1,15 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import React from 'react';
|
||||
import { Input as AntdInput } from 'antd'
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared'
|
||||
import { Input as AntdInput } from 'antd';
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared';
|
||||
|
||||
export const Markdown = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(acceptEnum((props) => <AntdInput.TextArea autoSize={{minRows: 2, maxRows: 12}} {...props}/>))
|
||||
getComponent: mapTextComponent,
|
||||
})(
|
||||
acceptEnum(props => (
|
||||
<AntdInput.TextArea autoSize={{ minRows: 2, maxRows: 12 }} {...props} />
|
||||
)),
|
||||
);
|
||||
|
||||
export default Markdown
|
||||
export default Markdown;
|
||||
|
@ -1,22 +1,24 @@
|
||||
import React from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { InputNumber } from 'antd'
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { InputNumber } from 'antd';
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared';
|
||||
|
||||
export const NumberPicker = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(acceptEnum(InputNumber))
|
||||
getComponent: mapTextComponent,
|
||||
})(acceptEnum(InputNumber));
|
||||
|
||||
export const Percent = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(acceptEnum((props) => (
|
||||
<InputNumber
|
||||
formatter={value => value ? `${value}%` : ''}
|
||||
parser={value => value.replace('%', '')}
|
||||
{...props}
|
||||
/>
|
||||
)))
|
||||
getComponent: mapTextComponent,
|
||||
})(
|
||||
acceptEnum(props => (
|
||||
<InputNumber
|
||||
formatter={value => (value ? `${value}%` : '')}
|
||||
parser={value => value.replace('%', '')}
|
||||
{...props}
|
||||
/>
|
||||
)),
|
||||
);
|
||||
|
||||
export default NumberPicker
|
||||
export default NumberPicker;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/input-number/style/index'
|
||||
import 'antd/lib/input-number/style/index';
|
||||
|
@ -1,27 +1,27 @@
|
||||
import React, { useState } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Input } from 'antd'
|
||||
import { PasswordProps } from 'antd/lib/input'
|
||||
import { PasswordStrength } from '@formily/react-shared-components'
|
||||
import styled from 'styled-components'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Input } from 'antd';
|
||||
import { PasswordProps } from 'antd/lib/input';
|
||||
import { PasswordStrength } from '@formily/react-shared-components';
|
||||
import styled from 'styled-components';
|
||||
import { mapStyledProps } from '../shared';
|
||||
|
||||
export interface IPasswordProps extends PasswordProps {
|
||||
checkStrength: boolean
|
||||
checkStrength: boolean;
|
||||
}
|
||||
|
||||
export const Password = connect({
|
||||
getProps: mapStyledProps
|
||||
getProps: mapStyledProps,
|
||||
})(styled((props: IPasswordProps) => {
|
||||
const { value, className, checkStrength, onChange, ...others } = props
|
||||
const { value, className, checkStrength, onChange, ...others } = props;
|
||||
|
||||
return (
|
||||
<span className={className}>
|
||||
<Input.Password
|
||||
<Input.Password
|
||||
autoComplete={'new-password'}
|
||||
{...others}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange={e => {
|
||||
// 密码字段,如果没有设置不处理
|
||||
onChange(e.target.value ? e : undefined);
|
||||
}}
|
||||
@ -38,16 +38,16 @@ export const Password = connect({
|
||||
<div
|
||||
className="password-strength-bar"
|
||||
style={{
|
||||
clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`
|
||||
clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</PasswordStrength>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
})`
|
||||
.password-strength-wrapper {
|
||||
background: #e0e0e0;
|
||||
@ -83,6 +83,6 @@ export const Password = connect({
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
`)
|
||||
`);
|
||||
|
||||
export default Password
|
||||
export default Password;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/input/style/index'
|
||||
import 'antd/lib/input/style/index';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Input as AntdInput, Table, Checkbox, Select, Tag } from 'antd'
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared'
|
||||
import { Input as AntdInput, Table, Checkbox, Select, Tag } from 'antd';
|
||||
import { acceptEnum, mapStyledProps, mapTextComponent } from '../shared';
|
||||
import api from '@/api-client';
|
||||
import { useRequest } from 'umi';
|
||||
import { useDynamicList } from 'ahooks';
|
||||
@ -10,132 +10,166 @@ import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
import { DrawerSelectComponent } from '../drawer-select';
|
||||
|
||||
export const Permissions = {} as {Actions: any, Fields: any, Tabs: any};
|
||||
export const Permissions = {} as { Actions: any; Fields: any; Tabs: any };
|
||||
|
||||
Permissions.Actions = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(({onChange, value = [], resourceKey, ...restProps}) => {
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return api.resource('collections.actions').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [resourceKey]
|
||||
});
|
||||
getComponent: mapTextComponent,
|
||||
})(({ onChange, value = [], resourceKey, ...restProps }) => {
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return api.resource('collections.actions').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceKey],
|
||||
},
|
||||
);
|
||||
|
||||
return <Table size={'small'} pagination={false} dataSource={data} columns={[
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: ['title'],
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: ['type'],
|
||||
render: (type) => {
|
||||
return type === 'create' ? <Tag color={'green'}>对新数据操作</Tag> : <Tag color={'blue'}>对已有数据操作</Tag>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '允许操作',
|
||||
dataIndex: ['name'],
|
||||
render: (val, record) => {
|
||||
const values = [...value||[]];
|
||||
const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
console.log(values);
|
||||
return (
|
||||
<Checkbox defaultChecked={index >= 0} onChange={(e) => {
|
||||
// const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
if (index >= 0) {
|
||||
if (!e.target.checked) {
|
||||
values.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
values.push({
|
||||
name: `${resourceKey}:${record.name}`,
|
||||
});
|
||||
return (
|
||||
<Table
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
columns={[
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: ['title'],
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: ['type'],
|
||||
render: type => {
|
||||
return type === 'create' ? (
|
||||
<Tag color={'green'}>对新数据操作</Tag>
|
||||
) : (
|
||||
<Tag color={'blue'}>对已有数据操作</Tag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '允许操作',
|
||||
dataIndex: ['name'],
|
||||
render: (val, record) => {
|
||||
const values = [...(value || [])];
|
||||
const index = findIndex(
|
||||
values,
|
||||
(item: any) =>
|
||||
item && item.name === `${resourceKey}:${record.name}`,
|
||||
);
|
||||
console.log(values);
|
||||
return (
|
||||
<Checkbox
|
||||
defaultChecked={index >= 0}
|
||||
onChange={e => {
|
||||
// const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
if (index >= 0) {
|
||||
if (!e.target.checked) {
|
||||
values.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
values.push({
|
||||
name: `${resourceKey}:${record.name}`,
|
||||
});
|
||||
}
|
||||
onChange(values);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '可操作的数据范围',
|
||||
dataIndex: ['scope'],
|
||||
render: (scope, record) => {
|
||||
if (['filter', 'create'].indexOf(record.type) !== -1) {
|
||||
return null;
|
||||
}
|
||||
onChange(values);
|
||||
}}/>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '可操作的数据范围',
|
||||
dataIndex: ['scope'],
|
||||
render: (scope, record) => {
|
||||
if (['filter', 'create'].indexOf(record.type) !== -1) {
|
||||
return null;
|
||||
}
|
||||
const values = [...value||[]];
|
||||
const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
console.log(values, index, `${resourceKey}:${record.name}`, get(values, [index, 'scope']));
|
||||
return (
|
||||
<DrawerSelectComponent
|
||||
schema={{
|
||||
title: '选择可操作的数据范围',
|
||||
}}
|
||||
size={'small'}
|
||||
associatedKey={resourceKey}
|
||||
viewName={'collections.scopes.table'}
|
||||
target={'scopes'}
|
||||
multiple={false}
|
||||
labelField={'title'}
|
||||
valueField={'id'}
|
||||
value={get(values, [index, 'scope'])}
|
||||
onChange={(data) => {
|
||||
const values = [...value||[]];
|
||||
const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
if (index === -1) {
|
||||
values.push({
|
||||
name: `${resourceKey}:${record.name}`,
|
||||
scope_id: data.id,
|
||||
});
|
||||
} else {
|
||||
set(values, [index, 'scope_id'], data.id);
|
||||
}
|
||||
console.log('valvalvalvalval', {values})
|
||||
onChange(values);
|
||||
console.log('valvalvalvalval', data);
|
||||
}}
|
||||
/>
|
||||
// <Scope
|
||||
// resourceTarget={'scopes'}
|
||||
// associatedName={'collections'}
|
||||
// associatedKey={resourceKey}
|
||||
// target={'scopes'}
|
||||
// multiple={false}
|
||||
// labelField={'title'}
|
||||
// valueField={'id'}
|
||||
// value={get(values, [index, 'scope'])}
|
||||
// onChange={(data) => {
|
||||
// const values = [...value||[]];
|
||||
// const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
// if (index === -1) {
|
||||
// values.push({
|
||||
// name: `${resourceKey}:${record.name}`,
|
||||
// scope_id: data,
|
||||
// });
|
||||
// } else {
|
||||
// set(values, [index, 'scope_id'], data);
|
||||
// }
|
||||
// console.log('valvalvalvalval', {values})
|
||||
// onChange(values);
|
||||
// console.log('valvalvalvalval', data);
|
||||
// }}
|
||||
// />
|
||||
)
|
||||
}
|
||||
},
|
||||
]} loading={loading}/>
|
||||
})
|
||||
const values = [...(value || [])];
|
||||
const index = findIndex(
|
||||
values,
|
||||
(item: any) =>
|
||||
item && item.name === `${resourceKey}:${record.name}`,
|
||||
);
|
||||
console.log(
|
||||
values,
|
||||
index,
|
||||
`${resourceKey}:${record.name}`,
|
||||
get(values, [index, 'scope']),
|
||||
);
|
||||
return (
|
||||
<DrawerSelectComponent
|
||||
schema={{
|
||||
title: '选择可操作的数据范围',
|
||||
}}
|
||||
size={'small'}
|
||||
associatedKey={resourceKey}
|
||||
viewName={'collections.scopes.table'}
|
||||
target={'scopes'}
|
||||
multiple={false}
|
||||
labelField={'title'}
|
||||
valueField={'id'}
|
||||
value={get(values, [index, 'scope'])}
|
||||
onChange={data => {
|
||||
const values = [...(value || [])];
|
||||
const index = findIndex(
|
||||
values,
|
||||
(item: any) =>
|
||||
item && item.name === `${resourceKey}:${record.name}`,
|
||||
);
|
||||
if (index === -1) {
|
||||
values.push({
|
||||
name: `${resourceKey}:${record.name}`,
|
||||
scope_id: data.id,
|
||||
});
|
||||
} else {
|
||||
set(values, [index, 'scope_id'], data.id);
|
||||
}
|
||||
console.log('valvalvalvalval', { values });
|
||||
onChange(values);
|
||||
console.log('valvalvalvalval', data);
|
||||
}}
|
||||
/>
|
||||
// <Scope
|
||||
// resourceTarget={'scopes'}
|
||||
// associatedName={'collections'}
|
||||
// associatedKey={resourceKey}
|
||||
// target={'scopes'}
|
||||
// multiple={false}
|
||||
// labelField={'title'}
|
||||
// valueField={'id'}
|
||||
// value={get(values, [index, 'scope'])}
|
||||
// onChange={(data) => {
|
||||
// const values = [...value||[]];
|
||||
// const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
|
||||
// if (index === -1) {
|
||||
// values.push({
|
||||
// name: `${resourceKey}:${record.name}`,
|
||||
// scope_id: data,
|
||||
// });
|
||||
// } else {
|
||||
// set(values, [index, 'scope_id'], data);
|
||||
// }
|
||||
// console.log('valvalvalvalval', {values})
|
||||
// onChange(values);
|
||||
// console.log('valvalvalvalval', data);
|
||||
// }}
|
||||
// />
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
loading={loading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Permissions.Fields = connect<'TextArea'>({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(({onChange, value = [], resourceKey, ...restProps}) => {
|
||||
|
||||
getComponent: mapTextComponent,
|
||||
})(({ onChange, value = [], resourceKey, ...restProps }) => {
|
||||
const actions = {};
|
||||
value.forEach(item => {
|
||||
actions[item.field_id] = item.actions;
|
||||
@ -143,110 +177,141 @@ Permissions.Fields = connect<'TextArea'>({
|
||||
|
||||
// console.log(actions);
|
||||
|
||||
const [fields, setFields] = useState(value||[]);
|
||||
const [fields, setFields] = useState(value || []);
|
||||
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return api.resource('collections.fields').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [resourceKey]
|
||||
});
|
||||
console.log({resourceKey, data});
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return api.resource('collections.fields').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceKey],
|
||||
},
|
||||
);
|
||||
console.log({ resourceKey, data });
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '字段名称',
|
||||
dataIndex: ['title'],
|
||||
}
|
||||
].concat([
|
||||
{
|
||||
title: '查看',
|
||||
action: `${resourceKey}:list`,
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
action: `${resourceKey}:update`,
|
||||
},
|
||||
{
|
||||
title: '新增',
|
||||
action: `${resourceKey}:create`,
|
||||
},
|
||||
].map(({title, action}) => {
|
||||
let checked = value.filter(({ actions = [] }) => actions.indexOf(action) !== -1).length === data.length;
|
||||
return {
|
||||
title: <><Checkbox checked={checked} onChange={(e) => {
|
||||
const values = data.map(field => {
|
||||
const items = actions[field.id] || [];
|
||||
const index = items.indexOf(action);
|
||||
if (index > -1) {
|
||||
if (!e.target.checked) {
|
||||
items.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
if (e.target.checked) {
|
||||
items.push(action);
|
||||
}
|
||||
}
|
||||
return {
|
||||
field_id: field.id,
|
||||
actions: items,
|
||||
}
|
||||
});
|
||||
// console.log(values);
|
||||
setFields([...values]);
|
||||
onChange([...values]);
|
||||
}}/> {title}</>,
|
||||
dataIndex: ['id'],
|
||||
render: (val, record) => {
|
||||
const items = actions[record.id]||[]
|
||||
// console.log({items}, items.indexOf(action));
|
||||
return (
|
||||
<Checkbox checked={items.indexOf(action) !== -1} onChange={e => {
|
||||
const values = [...value];
|
||||
const index = findIndex(values, ({field_id, actions = []}) => {
|
||||
return field_id === record.id;
|
||||
});
|
||||
if (e.target.checked && index === -1) {
|
||||
values.push({
|
||||
field_id: record.id,
|
||||
actions: [action],
|
||||
});
|
||||
} else {
|
||||
const items = values[index].actions || [];
|
||||
const actionIndex = items.indexOf(action);
|
||||
if (!e.target.checked && actionIndex > -1) {
|
||||
items.splice(actionIndex, 1);
|
||||
// values[index].actions = items;
|
||||
} else if (e.target.checked && actionIndex === -1) {
|
||||
items.push(action);
|
||||
}
|
||||
}
|
||||
onChange(values);
|
||||
setFields(values);
|
||||
}}/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}) as any)
|
||||
].concat(
|
||||
[
|
||||
{
|
||||
title: '查看',
|
||||
action: `${resourceKey}:list`,
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
action: `${resourceKey}:update`,
|
||||
},
|
||||
{
|
||||
title: '新增',
|
||||
action: `${resourceKey}:create`,
|
||||
},
|
||||
].map(({ title, action }) => {
|
||||
let checked =
|
||||
value.filter(({ actions = [] }) => actions.indexOf(action) !== -1)
|
||||
.length === data.length;
|
||||
return {
|
||||
title: (
|
||||
<>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onChange={e => {
|
||||
const values = data.map(field => {
|
||||
const items = actions[field.id] || [];
|
||||
const index = items.indexOf(action);
|
||||
if (index > -1) {
|
||||
if (!e.target.checked) {
|
||||
items.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
if (e.target.checked) {
|
||||
items.push(action);
|
||||
}
|
||||
}
|
||||
return {
|
||||
field_id: field.id,
|
||||
actions: items,
|
||||
};
|
||||
});
|
||||
// console.log(values);
|
||||
setFields([...values]);
|
||||
onChange([...values]);
|
||||
}}
|
||||
/>{' '}
|
||||
{title}
|
||||
</>
|
||||
),
|
||||
dataIndex: ['id'],
|
||||
render: (val, record) => {
|
||||
const items = actions[record.id] || [];
|
||||
// console.log({items}, items.indexOf(action));
|
||||
return (
|
||||
<Checkbox
|
||||
checked={items.indexOf(action) !== -1}
|
||||
onChange={e => {
|
||||
const values = [...value];
|
||||
const index = findIndex(
|
||||
values,
|
||||
({ field_id, actions = [] }) => {
|
||||
return field_id === record.id;
|
||||
},
|
||||
);
|
||||
if (e.target.checked && index === -1) {
|
||||
values.push({
|
||||
field_id: record.id,
|
||||
actions: [action],
|
||||
});
|
||||
} else {
|
||||
const items = values[index].actions || [];
|
||||
const actionIndex = items.indexOf(action);
|
||||
if (!e.target.checked && actionIndex > -1) {
|
||||
items.splice(actionIndex, 1);
|
||||
// values[index].actions = items;
|
||||
} else if (e.target.checked && actionIndex === -1) {
|
||||
items.push(action);
|
||||
}
|
||||
}
|
||||
onChange(values);
|
||||
setFields(values);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
}) as any,
|
||||
);
|
||||
|
||||
return <Table size={'small'} loading={loading} pagination={false} dataSource={data} columns={columns}/>
|
||||
})
|
||||
return (
|
||||
<Table
|
||||
size={'small'}
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
columns={columns}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Permissions.Tabs = connect<'TextArea'>({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(({onChange, value = [], resourceKey, ...restProps}) => {
|
||||
|
||||
const { data = [], loading = true, mutate } = useRequest(() => {
|
||||
return api.resource('collections.tabs').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [resourceKey]
|
||||
});
|
||||
getComponent: mapTextComponent,
|
||||
})(({ onChange, value = [], resourceKey, ...restProps }) => {
|
||||
const { data = [], loading = true, mutate } = useRequest(
|
||||
() => {
|
||||
return api.resource('collections.tabs').list({
|
||||
associatedKey: resourceKey,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceKey],
|
||||
},
|
||||
);
|
||||
|
||||
// const [checked, setChecked] = useState(false);
|
||||
|
||||
@ -259,36 +324,53 @@ Permissions.Tabs = connect<'TextArea'>({
|
||||
// data,
|
||||
// ]);
|
||||
|
||||
return <Table size={'small'} pagination={false} dataSource={data} columns={[
|
||||
{
|
||||
title: '标签页',
|
||||
dataIndex: ['title'],
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<>
|
||||
<Checkbox checked={data.length === value.length} onChange={(e) => {
|
||||
onChange(e.target.checked ? data.map(record => record.id) : []);
|
||||
}}/> 查看
|
||||
</>
|
||||
),
|
||||
dataIndex: ['id'],
|
||||
render: (val, record) => {
|
||||
const values = [...value];
|
||||
return (
|
||||
<Checkbox checked={values.indexOf(record.id) !== -1} onChange={(e) => {
|
||||
const index = values.indexOf(record.id);
|
||||
if (index !== -1) {
|
||||
if (!e.target.checked) {
|
||||
values.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
values.push(record.id);
|
||||
}
|
||||
onChange(values);
|
||||
}}/>
|
||||
)
|
||||
}
|
||||
},
|
||||
]} loading={loading}/>
|
||||
return (
|
||||
<Table
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
columns={[
|
||||
{
|
||||
title: '标签页',
|
||||
dataIndex: ['title'],
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<>
|
||||
<Checkbox
|
||||
checked={data.length === value.length}
|
||||
onChange={e => {
|
||||
onChange(
|
||||
e.target.checked ? data.map(record => record.id) : [],
|
||||
);
|
||||
}}
|
||||
/>{' '}
|
||||
查看
|
||||
</>
|
||||
),
|
||||
dataIndex: ['id'],
|
||||
render: (val, record) => {
|
||||
const values = [...value];
|
||||
return (
|
||||
<Checkbox
|
||||
checked={values.indexOf(record.id) !== -1}
|
||||
onChange={e => {
|
||||
const index = values.indexOf(record.id);
|
||||
if (index !== -1) {
|
||||
if (!e.target.checked) {
|
||||
values.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
values.push(record.id);
|
||||
}
|
||||
onChange(values);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
loading={loading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Radio as AntdRadio } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Radio as AntdRadio } from 'antd';
|
||||
import {
|
||||
transformDataSourceKey,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
|
||||
export const Radio = connect<'Group'>({
|
||||
valueName: 'checked',
|
||||
getProps: mapStyledProps
|
||||
})(AntdRadio)
|
||||
getProps: mapStyledProps,
|
||||
})(AntdRadio);
|
||||
|
||||
Radio.Group = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent
|
||||
})(transformDataSourceKey(AntdRadio.Group, 'options'))
|
||||
getComponent: mapTextComponent,
|
||||
})(transformDataSourceKey(AntdRadio.Group, 'options'));
|
||||
|
||||
export default Radio
|
||||
export default Radio;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/radio/style/index'
|
||||
import 'antd/lib/radio/style/index';
|
||||
|
@ -1,47 +1,47 @@
|
||||
import React from 'react'
|
||||
import { Slider } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import React from 'react';
|
||||
import { Slider } from 'antd';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { mapStyledProps } from '../shared';
|
||||
|
||||
export interface ISliderMarks {
|
||||
[key: number]:
|
||||
| React.ReactNode
|
||||
| {
|
||||
style: React.CSSProperties
|
||||
label: React.ReactNode
|
||||
}
|
||||
style: React.CSSProperties;
|
||||
label: React.ReactNode;
|
||||
};
|
||||
}
|
||||
|
||||
export declare type SliderValue = number | [number, number]
|
||||
export declare type SliderValue = number | [number, number];
|
||||
|
||||
// TODO 并不是方法,最好能引用组件的 typescript 接口定义
|
||||
export interface ISliderProps {
|
||||
min?: number
|
||||
max?: number
|
||||
marks?: ISliderMarks
|
||||
value?: SliderValue
|
||||
defaultValue?: SliderValue
|
||||
onChange?: (value: SliderValue) => void
|
||||
min?: number;
|
||||
max?: number;
|
||||
marks?: ISliderMarks;
|
||||
value?: SliderValue;
|
||||
defaultValue?: SliderValue;
|
||||
onChange?: (value: SliderValue) => void;
|
||||
}
|
||||
|
||||
export const Range = connect({
|
||||
defaultProps: {
|
||||
style: {
|
||||
width: 320
|
||||
}
|
||||
width: 320,
|
||||
},
|
||||
},
|
||||
getProps: mapStyledProps
|
||||
getProps: mapStyledProps,
|
||||
})(
|
||||
class Component extends React.Component<ISliderProps> {
|
||||
public render() {
|
||||
const { onChange, value, min, max, marks, ...rest } = this.props
|
||||
let newMarks = {}
|
||||
const { onChange, value, min, max, marks, ...rest } = this.props;
|
||||
let newMarks = {};
|
||||
if (Array.isArray(marks)) {
|
||||
marks.forEach(mark => {
|
||||
newMarks[mark] = mark
|
||||
})
|
||||
newMarks[mark] = mark;
|
||||
});
|
||||
} else {
|
||||
newMarks = marks
|
||||
newMarks = marks;
|
||||
}
|
||||
return (
|
||||
<Slider
|
||||
@ -52,9 +52,9 @@ export const Range = connect({
|
||||
max={max}
|
||||
marks={newMarks}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
export default Range
|
||||
export default Range;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/slider/style/index'
|
||||
import 'antd/lib/slider/style/index';
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Rate } from 'antd'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Rate } from 'antd';
|
||||
import { mapStyledProps } from '../shared';
|
||||
|
||||
export const Rating = connect({
|
||||
getProps: mapStyledProps
|
||||
})(Rate)
|
||||
getProps: mapStyledProps,
|
||||
})(Rate);
|
||||
|
||||
export default Rating
|
||||
export default Rating;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/rate/style/index'
|
||||
import 'antd/lib/rate/style/index';
|
||||
|
@ -1,31 +1,31 @@
|
||||
import { registerFormFields } from '@formily/antd'
|
||||
import { TimePicker } from './time-picker'
|
||||
import { Transfer } from './transfer'
|
||||
import { Switch } from './switch'
|
||||
import { ArrayCards } from './array-cards'
|
||||
import { ArrayTable } from './array-table'
|
||||
import { Checkbox } from './checkbox'
|
||||
import { DatePicker } from './date-picker'
|
||||
import { Input } from './input'
|
||||
import { NumberPicker, Percent } from './number-picker'
|
||||
import { Password } from './password'
|
||||
import { Radio } from './radio'
|
||||
import { Range } from './range'
|
||||
import { Rating } from './rating'
|
||||
import { Upload } from './upload'
|
||||
import { Filter } from './filter'
|
||||
import { RemoteSelect } from './remote-select'
|
||||
import { DrawerSelect } from './drawer-select'
|
||||
import { SubTable } from './sub-table'
|
||||
import { Cascader } from './cascader'
|
||||
import { Icon } from './icons'
|
||||
import { ColorSelect } from './color-select'
|
||||
import { Permissions } from './permissions'
|
||||
import { DraggableTable } from './draggable-table'
|
||||
import { Values } from './values'
|
||||
import { Automations } from './automations'
|
||||
import { Wysiwyg } from './wysiwyg'
|
||||
import { Markdown } from './markdown'
|
||||
import { registerFormFields } from '@formily/antd';
|
||||
import { TimePicker } from './time-picker';
|
||||
import { Transfer } from './transfer';
|
||||
import { Switch } from './switch';
|
||||
import { ArrayCards } from './array-cards';
|
||||
import { ArrayTable } from './array-table';
|
||||
import { Checkbox } from './checkbox';
|
||||
import { DatePicker } from './date-picker';
|
||||
import { Input } from './input';
|
||||
import { NumberPicker, Percent } from './number-picker';
|
||||
import { Password } from './password';
|
||||
import { Radio } from './radio';
|
||||
import { Range } from './range';
|
||||
import { Rating } from './rating';
|
||||
import { Upload } from './upload';
|
||||
import { Filter } from './filter';
|
||||
import { RemoteSelect } from './remote-select';
|
||||
import { DrawerSelect } from './drawer-select';
|
||||
import { SubTable } from './sub-table';
|
||||
import { Cascader } from './cascader';
|
||||
import { Icon } from './icons';
|
||||
import { ColorSelect } from './color-select';
|
||||
import { Permissions } from './permissions';
|
||||
import { DraggableTable } from './draggable-table';
|
||||
import { Values } from './values';
|
||||
import { Automations } from './automations';
|
||||
import { Wysiwyg } from './wysiwyg';
|
||||
import { Markdown } from './markdown';
|
||||
|
||||
export const setup = () => {
|
||||
registerFormFields({
|
||||
@ -72,4 +72,4 @@ export const setup = () => {
|
||||
'automations.endmode': Automations.EndMode,
|
||||
'automations.cron': Automations.Cron,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,22 +1,35 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import moment from 'moment'
|
||||
import { Select } from 'antd'
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { Select } from 'antd';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr
|
||||
} from '../shared'
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { Spin } from '@nocobase/client'
|
||||
import { Spin } from '@nocobase/client';
|
||||
import get from 'lodash/get';
|
||||
|
||||
function RemoteSelectComponent(props) {
|
||||
let { schema = {}, value, onChange, disabled, resourceName, associatedKey, filter, labelField, valueField, objectValue, placeholder, multiple } = props;
|
||||
console.log({schema});
|
||||
let {
|
||||
schema = {},
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
resourceName,
|
||||
associatedKey,
|
||||
filter,
|
||||
labelField,
|
||||
valueField,
|
||||
objectValue,
|
||||
placeholder,
|
||||
multiple,
|
||||
} = props;
|
||||
console.log({ schema });
|
||||
if (!resourceName) {
|
||||
resourceName = get(schema, 'component.resourceName');
|
||||
}
|
||||
@ -32,29 +45,32 @@ function RemoteSelectComponent(props) {
|
||||
if (!valueField) {
|
||||
valueField = 'id';
|
||||
}
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return api.resource(resourceName).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [resourceName, associatedKey]
|
||||
});
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return api.resource(resourceName).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceName, associatedKey],
|
||||
},
|
||||
);
|
||||
const selectProps: any = {};
|
||||
if (multiple) {
|
||||
selectProps.mode = 'multiple'
|
||||
selectProps.mode = 'multiple';
|
||||
}
|
||||
console.log({ data, props, associatedKey })
|
||||
console.log({ data, props, associatedKey });
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
<Select
|
||||
{...selectProps}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
notFoundContent={loading ? <Spin/> : undefined}
|
||||
allowClear
|
||||
disabled={disabled}
|
||||
notFoundContent={loading ? <Spin /> : undefined}
|
||||
allowClear
|
||||
loading={loading}
|
||||
value={value && typeof value === 'object' ? value[valueField] : value}
|
||||
value={value && typeof value === 'object' ? value[valueField] : value}
|
||||
onChange={(value, option) => {
|
||||
if (value === null || typeof value === 'undefined') {
|
||||
onChange(undefined);
|
||||
@ -65,7 +81,12 @@ function RemoteSelectComponent(props) {
|
||||
onChange(objectValue ? item : value);
|
||||
}}
|
||||
>
|
||||
{!loading && data.map(item => (<Select.Option item={item} value={item[valueField]}>{item[labelField]}</Select.Option>))}
|
||||
{!loading &&
|
||||
data.map(item => (
|
||||
<Select.Option item={item} value={item[valueField]}>
|
||||
{item[labelField]}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
);
|
||||
@ -74,6 +95,6 @@ function RemoteSelectComponent(props) {
|
||||
export const RemoteSelect = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(RemoteSelectComponent)
|
||||
})(RemoteSelectComponent);
|
||||
|
||||
export default RemoteSelect
|
||||
export default RemoteSelect;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import {
|
||||
Select as AntdSelect,
|
||||
mapStyledProps,
|
||||
mapTextComponent
|
||||
} from '../shared'
|
||||
mapTextComponent,
|
||||
} from '../shared';
|
||||
|
||||
export const Select = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdSelect)
|
||||
})(AntdSelect);
|
||||
|
||||
export default Select
|
||||
export default Select;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/select/style/index'
|
||||
import 'antd/lib/select/style/index';
|
||||
|
@ -1,47 +1,47 @@
|
||||
import React from 'react'
|
||||
import { mapTextComponent, mapStyledProps, normalizeCol } from '@formily/antd'
|
||||
import { Select as AntSelect } from 'antd'
|
||||
import { SelectProps as AntSelectProps } from 'antd/lib/select'
|
||||
import styled from 'styled-components'
|
||||
import { isArr, FormPath } from '@formily/shared'
|
||||
export * from '@formily/shared'
|
||||
import React from 'react';
|
||||
import { mapTextComponent, mapStyledProps, normalizeCol } from '@formily/antd';
|
||||
import { Select as AntSelect } from 'antd';
|
||||
import { SelectProps as AntSelectProps } from 'antd/lib/select';
|
||||
import styled from 'styled-components';
|
||||
import { isArr, FormPath } from '@formily/shared';
|
||||
export * from '@formily/shared';
|
||||
|
||||
export const compose = (...args: any[]) => {
|
||||
return (payload: any, ...extra: any[]) => {
|
||||
return args.reduce((buf, fn) => {
|
||||
return buf !== undefined ? fn(buf, ...extra) : fn(payload, ...extra)
|
||||
}, payload)
|
||||
}
|
||||
}
|
||||
return buf !== undefined ? fn(buf, ...extra) : fn(payload, ...extra);
|
||||
}, payload);
|
||||
};
|
||||
};
|
||||
|
||||
interface SelectOption {
|
||||
label: React.ReactText
|
||||
value: any
|
||||
[key: string]: any
|
||||
label: React.ReactText;
|
||||
value: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type SelectProps = AntSelectProps & {
|
||||
dataSource?: SelectOption[]
|
||||
}
|
||||
dataSource?: SelectOption[];
|
||||
};
|
||||
|
||||
const createEnum = (enums: any) => {
|
||||
if (isArr(enums)) {
|
||||
return enums.map(item => {
|
||||
if (typeof item === 'object') {
|
||||
return {
|
||||
...item
|
||||
}
|
||||
...item,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
label: item,
|
||||
value: item
|
||||
}
|
||||
value: item,
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
export const Select: React.FC<SelectProps> = styled((props: SelectProps) => {
|
||||
const { dataSource = [], onChange, value, ...others } = props;
|
||||
@ -50,7 +50,7 @@ export const Select: React.FC<SelectProps> = styled((props: SelectProps) => {
|
||||
if (children.length) {
|
||||
return (
|
||||
<AntSelect.OptGroup key={key} label={label}>
|
||||
{children.map(({value, label, ...others}: any) => (
|
||||
{children.map(({ value, label, ...others }: any) => (
|
||||
<AntSelect.Option
|
||||
key={value}
|
||||
{...others}
|
||||
@ -72,8 +72,8 @@ export const Select: React.FC<SelectProps> = styled((props: SelectProps) => {
|
||||
>
|
||||
{label}
|
||||
</AntSelect.Option>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
return (
|
||||
<AntSelect
|
||||
className={props.className}
|
||||
@ -85,59 +85,59 @@ export const Select: React.FC<SelectProps> = styled((props: SelectProps) => {
|
||||
isArr(options)
|
||||
? options.map(item => ({
|
||||
...item,
|
||||
props: undefined
|
||||
props: undefined,
|
||||
}))
|
||||
: {
|
||||
...options,
|
||||
props: undefined //干掉循环引用
|
||||
}
|
||||
)
|
||||
props: undefined, //干掉循环引用
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AntSelect>
|
||||
)
|
||||
);
|
||||
})`
|
||||
min-width: 100px;
|
||||
width: 100%;
|
||||
`
|
||||
`;
|
||||
export const acceptEnum = (component: React.JSXElementConstructor<any>) => {
|
||||
return ({ dataSource, ...others }) => {
|
||||
if (dataSource) {
|
||||
return React.createElement(Select, { dataSource, ...others })
|
||||
return React.createElement(Select, { dataSource, ...others });
|
||||
} else {
|
||||
return React.createElement(component, others)
|
||||
return React.createElement(component, others);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const transformDataSourceKey = (component, dataSourceKey) => {
|
||||
return ({ dataSource, ...others }) => {
|
||||
return React.createElement(component, {
|
||||
[dataSourceKey]: dataSource,
|
||||
...others
|
||||
})
|
||||
}
|
||||
}
|
||||
...others,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const createMatchUpdate = (name: string, path: string) => (
|
||||
targetName: string,
|
||||
targetPath: string,
|
||||
callback: () => void
|
||||
callback: () => void,
|
||||
) => {
|
||||
if (targetName || targetPath) {
|
||||
if (targetName) {
|
||||
if (FormPath.parse(targetName).matchAliasGroup(name, path)) {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
} else if (targetPath) {
|
||||
if (FormPath.parse(targetPath).matchAliasGroup(name, path)) {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { mapTextComponent, mapStyledProps, normalizeCol }
|
||||
export { mapTextComponent, mapStyledProps, normalizeCol };
|
||||
|
@ -1,4 +1,10 @@
|
||||
import React, { useState, useEffect, useImperativeHandle, forwardRef, useRef } from 'react';
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
forwardRef,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { Button, Drawer } from 'antd';
|
||||
import { Tooltip, Input, Space, Modal } from 'antd';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
@ -27,13 +33,12 @@ const actions = createFormActions();
|
||||
|
||||
export default forwardRef((props: any, ref) => {
|
||||
console.log(props);
|
||||
const {
|
||||
target,
|
||||
onFinish,
|
||||
} = props;
|
||||
const { data: schema = {}, loading } = useRequest(() => api.resource(target).getView({
|
||||
resourceKey: 'form'
|
||||
}));
|
||||
const { target, onFinish } = props;
|
||||
const { data: schema = {}, loading } = useRequest(() =>
|
||||
api.resource(target).getView({
|
||||
resourceKey: 'form',
|
||||
}),
|
||||
);
|
||||
const [state, setState] = useState<any>({});
|
||||
const [form, setForm] = useState<any>({});
|
||||
const [changed, setChanged] = useState(false);
|
||||
@ -47,10 +52,10 @@ export default forwardRef((props: any, ref) => {
|
||||
setTitle,
|
||||
setIndex,
|
||||
}));
|
||||
console.log({onFinish});
|
||||
console.log({ onFinish });
|
||||
const { fields = {} } = schema;
|
||||
if (loading) {
|
||||
return <Spin/>;
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<Drawer
|
||||
@ -66,7 +71,7 @@ export default forwardRef((props: any, ref) => {
|
||||
onOk() {
|
||||
setChanged(false);
|
||||
setVisible(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setChanged(false);
|
||||
@ -74,35 +79,44 @@ export default forwardRef((props: any, ref) => {
|
||||
}
|
||||
}}
|
||||
title={title}
|
||||
footer={(
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'right',
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<Button onClick={() => {
|
||||
setVisible(false);
|
||||
setChanged(false);
|
||||
}}>取消</Button>
|
||||
<Button type={'primary'} onClick={async () => {
|
||||
await form.submit();
|
||||
// const { values = {} } = await actions.submit();
|
||||
// setVisible(false);
|
||||
// onFinish && onFinish(values, index);
|
||||
}}>提交</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(false);
|
||||
setChanged(false);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={async () => {
|
||||
await form.submit();
|
||||
// const { values = {} } = await actions.submit();
|
||||
// setVisible(false);
|
||||
// onFinish && onFinish(values, index);
|
||||
}}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
>
|
||||
<SchemaForm
|
||||
<SchemaForm
|
||||
colon={true}
|
||||
layout={'vertical'}
|
||||
initialValues={data}
|
||||
onChange={(values) => {
|
||||
onChange={values => {
|
||||
setChanged(true);
|
||||
}}
|
||||
onSubmit={async (values) => {
|
||||
onSubmit={async values => {
|
||||
setVisible(false);
|
||||
setChanged(false);
|
||||
onFinish && onFinish(values, index);
|
||||
@ -118,29 +132,29 @@ export default forwardRef((props: any, ref) => {
|
||||
selector={[
|
||||
LifeCycleTypes.ON_FORM_MOUNT,
|
||||
LifeCycleTypes.ON_FORM_SUBMIT_START,
|
||||
LifeCycleTypes.ON_FORM_SUBMIT_END
|
||||
LifeCycleTypes.ON_FORM_SUBMIT_END,
|
||||
]}
|
||||
reducer={(state, action) => {
|
||||
switch (action.type) {
|
||||
case LifeCycleTypes.ON_FORM_SUBMIT_START:
|
||||
return {
|
||||
...state,
|
||||
submitting: true
|
||||
}
|
||||
submitting: true,
|
||||
};
|
||||
case LifeCycleTypes.ON_FORM_SUBMIT_END:
|
||||
return {
|
||||
...state,
|
||||
submitting: false
|
||||
}
|
||||
submitting: false,
|
||||
};
|
||||
default:
|
||||
return state
|
||||
return state;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ state, form }) => {
|
||||
setState(state)
|
||||
setState(state);
|
||||
setForm(form);
|
||||
return <div/>
|
||||
return <div />;
|
||||
}}
|
||||
</FormSpy>
|
||||
</SchemaForm>
|
||||
|
@ -21,17 +21,21 @@ export interface SimpleTableProps {
|
||||
}
|
||||
|
||||
export function generateIndex(): string {
|
||||
return `${Math.random().toString(36).replace('0.', '').slice(-4).padStart(4, '0')}`;
|
||||
return `${Math.random()
|
||||
.toString(36)
|
||||
.replace('0.', '')
|
||||
.slice(-4)
|
||||
.padStart(4, '0')}`;
|
||||
}
|
||||
|
||||
export default function Table(props: SimpleTableProps) {
|
||||
const { schema = {}, associatedKey, value, onChange, __index } = props;
|
||||
const { collection_name, name } = schema;
|
||||
const viewName = `${collection_name}.${name}.${schema.viewName||'table'}`;
|
||||
console.log({props, associatedKey, schema, __index, viewName, schema})
|
||||
const viewName = `${collection_name}.${name}.${schema.viewName || 'table'}`;
|
||||
console.log({ props, associatedKey, schema, __index, viewName, schema });
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
<View
|
||||
// __parent={__parent}
|
||||
data={value}
|
||||
onChange={onChange}
|
||||
@ -40,5 +44,5 @@ export default function Table(props: SimpleTableProps) {
|
||||
type={'subTable'}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import moment from 'moment'
|
||||
import { Select, Button, Table as AntdTable } from 'antd'
|
||||
import React, { useRef } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { Select, Button, Table as AntdTable } from 'antd';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr
|
||||
} from '../shared'
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import ViewFactory from '@/components/views';
|
||||
import Table from './Table';
|
||||
|
||||
export const SubTable = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(Table)
|
||||
})(Table);
|
||||
|
||||
export default SubTable
|
||||
export default SubTable;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Switch as AntdSwitch } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { acceptEnum, mapStyledProps } from '../shared'
|
||||
import { Switch as AntdSwitch } from 'antd';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { acceptEnum, mapStyledProps } from '../shared';
|
||||
|
||||
export const Switch = connect({
|
||||
valueName: 'checked',
|
||||
getProps: mapStyledProps
|
||||
})(acceptEnum(AntdSwitch))
|
||||
getProps: mapStyledProps,
|
||||
})(acceptEnum(AntdSwitch));
|
||||
|
||||
export default Switch;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/switch/style/index'
|
||||
import 'antd/lib/switch/style/index';
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import { Button } from 'antd'
|
||||
import { ButtonProps } from 'antd/lib/button'
|
||||
import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { ButtonProps } from 'antd/lib/button';
|
||||
|
||||
export const TextButton: React.FC<ButtonProps> = props => (
|
||||
<Button type="link" {...props} />
|
||||
)
|
||||
);
|
||||
|
||||
export default TextButton
|
||||
export default TextButton;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/button/style/index'
|
||||
import 'antd/lib/button/style/index';
|
||||
|
@ -1,50 +1,48 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import moment from 'moment'
|
||||
import { TimePicker as AntdTimePicker } from 'antd'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { TimePicker as AntdTimePicker } from 'antd';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr,
|
||||
} from '../shared'
|
||||
} from '../shared';
|
||||
|
||||
const transformMoment = (value) => {
|
||||
if (value === '') return undefined
|
||||
return value
|
||||
}
|
||||
const transformMoment = value => {
|
||||
if (value === '') return undefined;
|
||||
return value;
|
||||
};
|
||||
|
||||
const mapMomentValue = (props: any, fieldProps: any) => {
|
||||
const { value } = props
|
||||
if (!fieldProps.editable) return props
|
||||
const { value } = props;
|
||||
if (!fieldProps.editable) return props;
|
||||
try {
|
||||
if (isStr(value) && value) {
|
||||
props.value = moment(value, 'HH:mm:ss')
|
||||
props.value = moment(value, 'HH:mm:ss');
|
||||
} else if (isArr(value) && value.length) {
|
||||
props.value = value.map(
|
||||
(item) => (item && moment(item, 'HH:mm:ss')) || ''
|
||||
)
|
||||
props.value = value.map(item => (item && moment(item, 'HH:mm:ss')) || '');
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
throw new Error(e);
|
||||
}
|
||||
return props
|
||||
}
|
||||
return props;
|
||||
};
|
||||
|
||||
export const TimePicker = connect<'RangePicker'>({
|
||||
getValueFromEvent(_, value) {
|
||||
return transformMoment(value)
|
||||
return transformMoment(value);
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdTimePicker)
|
||||
})(AntdTimePicker);
|
||||
|
||||
TimePicker.RangePicker = connect({
|
||||
getValueFromEvent(_, [startDate, endDate]) {
|
||||
return [transformMoment(startDate), transformMoment(endDate)]
|
||||
return [transformMoment(startDate), transformMoment(endDate)];
|
||||
},
|
||||
getProps: compose(mapStyledProps, mapMomentValue),
|
||||
getComponent: mapTextComponent,
|
||||
})(AntdTimePicker.RangePicker)
|
||||
})(AntdTimePicker.RangePicker);
|
||||
|
||||
export default TimePicker
|
||||
export default TimePicker;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/time-picker/style/index'
|
||||
import 'antd/lib/time-picker/style/index';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Transfer as AntdTransfer } from 'antd'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Transfer as AntdTransfer } from 'antd';
|
||||
import { mapStyledProps } from '../shared';
|
||||
|
||||
export const Transfer = connect({
|
||||
getProps: mapStyledProps,
|
||||
valueName: 'targetKeys'
|
||||
})(AntdTransfer)
|
||||
valueName: 'targetKeys',
|
||||
})(AntdTransfer);
|
||||
|
||||
export default Transfer
|
||||
export default Transfer;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/transfer/style/index'
|
||||
import 'antd/lib/transfer/style/index';
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { ButtonProps } from 'antd/lib/button'
|
||||
import { FormProps, FormItemProps as ItemProps } from 'antd/lib/form'
|
||||
import { ButtonProps } from 'antd/lib/button';
|
||||
import { FormProps, FormItemProps as ItemProps } from 'antd/lib/form';
|
||||
import {
|
||||
StepsProps as StepProps,
|
||||
StepProps as StepItemProps
|
||||
} from 'antd/lib/steps'
|
||||
import { TabsProps } from 'antd/lib/tabs'
|
||||
StepProps as StepItemProps,
|
||||
} from 'antd/lib/steps';
|
||||
import { TabsProps } from 'antd/lib/tabs';
|
||||
import {
|
||||
ISchemaFormProps,
|
||||
IMarkupSchemaFieldProps,
|
||||
ISchemaFieldComponentProps,
|
||||
FormPathPattern
|
||||
} from '@formily/react-schema-renderer'
|
||||
import { PreviewTextConfigProps } from '@formily/react-shared-components'
|
||||
import { StyledComponent } from 'styled-components'
|
||||
FormPathPattern,
|
||||
} from '@formily/react-schema-renderer';
|
||||
import { PreviewTextConfigProps } from '@formily/react-shared-components';
|
||||
import { StyledComponent } from 'styled-components';
|
||||
|
||||
type ColSpanType = number | string
|
||||
type ColSpanType = number | string;
|
||||
|
||||
export type IAntdSchemaFormProps = Omit<
|
||||
FormProps,
|
||||
@ -22,18 +22,18 @@ export type IAntdSchemaFormProps = Omit<
|
||||
> &
|
||||
IFormItemTopProps &
|
||||
PreviewTextConfigProps &
|
||||
ISchemaFormProps
|
||||
ISchemaFormProps;
|
||||
|
||||
export type IAntdSchemaFieldProps = IMarkupSchemaFieldProps
|
||||
export type IAntdSchemaFieldProps = IMarkupSchemaFieldProps;
|
||||
|
||||
export interface ISubmitProps extends ButtonProps {
|
||||
onSubmit?: ISchemaFormProps['onSubmit']
|
||||
showLoading?: boolean
|
||||
onSubmit?: ISchemaFormProps['onSubmit'];
|
||||
showLoading?: boolean;
|
||||
}
|
||||
|
||||
export interface IResetProps extends ButtonProps {
|
||||
forceClear?: boolean
|
||||
validate?: boolean
|
||||
forceClear?: boolean;
|
||||
validate?: boolean;
|
||||
}
|
||||
|
||||
export type IFormItemTopProps = React.PropsWithChildren<
|
||||
@ -41,63 +41,63 @@ export type IFormItemTopProps = React.PropsWithChildren<
|
||||
Pick<ItemProps, 'prefixCls' | 'labelCol' | 'wrapperCol' | 'labelAlign'>,
|
||||
'labelCol' | 'wrapperCol'
|
||||
> & {
|
||||
inline?: boolean
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
labelCol?: number | { span: number; offset?: number }
|
||||
wrapperCol?: number | { span: number; offset?: number }
|
||||
inline?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
labelCol?: number | { span: number; offset?: number };
|
||||
wrapperCol?: number | { span: number; offset?: number };
|
||||
}
|
||||
>
|
||||
>;
|
||||
|
||||
export type ISchemaFieldAdaptorProps = Omit<
|
||||
ItemProps,
|
||||
'labelCol' | 'wrapperCol'
|
||||
> &
|
||||
Partial<ISchemaFieldComponentProps> & {
|
||||
labelCol?: number | { span: number; offset?: number }
|
||||
wrapperCol?: number | { span: number; offset?: number }
|
||||
}
|
||||
labelCol?: number | { span: number; offset?: number };
|
||||
wrapperCol?: number | { span: number; offset?: number };
|
||||
};
|
||||
|
||||
export type StyledCP<P extends {}> = StyledComponent<
|
||||
(props: React.PropsWithChildren<P>) => React.ReactElement,
|
||||
any,
|
||||
{},
|
||||
never
|
||||
>
|
||||
>;
|
||||
|
||||
export type StyledCC<Props, Statics = {}> = StyledCP<Props> & Statics
|
||||
export type StyledCC<Props, Statics = {}> = StyledCP<Props> & Statics;
|
||||
|
||||
export interface IFormButtonGroupProps {
|
||||
sticky?: boolean
|
||||
style?: React.CSSProperties
|
||||
itemStyle?: React.CSSProperties
|
||||
className?: string
|
||||
align?: 'left' | 'right' | 'start' | 'end' | 'top' | 'bottom' | 'center'
|
||||
triggerDistance?: number
|
||||
zIndex?: number
|
||||
span?: ColSpanType
|
||||
offset?: ColSpanType
|
||||
sticky?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
itemStyle?: React.CSSProperties;
|
||||
className?: string;
|
||||
align?: 'left' | 'right' | 'start' | 'end' | 'top' | 'bottom' | 'center';
|
||||
triggerDistance?: number;
|
||||
zIndex?: number;
|
||||
span?: ColSpanType;
|
||||
offset?: ColSpanType;
|
||||
}
|
||||
|
||||
export interface IItemProps {
|
||||
title?: React.ReactText
|
||||
description?: React.ReactText
|
||||
title?: React.ReactText;
|
||||
description?: React.ReactText;
|
||||
}
|
||||
|
||||
export interface IFormItemGridProps extends IItemProps {
|
||||
cols?: Array<number | { span: number; offset: number }>
|
||||
gutter?: number
|
||||
cols?: Array<number | { span: number; offset: number }>;
|
||||
gutter?: number;
|
||||
}
|
||||
|
||||
export interface IFormTextBox extends IItemProps {
|
||||
text?: string
|
||||
gutter?: number
|
||||
text?: string;
|
||||
gutter?: number;
|
||||
}
|
||||
|
||||
export interface IFormStep extends StepProps {
|
||||
dataSource: Array<StepItemProps & { name: FormPathPattern }>
|
||||
dataSource: Array<StepItemProps & { name: FormPathPattern }>;
|
||||
}
|
||||
|
||||
export interface IFormTab extends TabsProps {
|
||||
hiddenKeys?: string[]
|
||||
hiddenKeys?: string[];
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { useState } from 'react'
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { Button, Upload as AntdUpload, Popconfirm } from 'antd'
|
||||
import { toArr, isArr, isEqual, mapStyledProps } from '../shared'
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { Button, Upload as AntdUpload, Popconfirm } from 'antd';
|
||||
import { toArr, isArr, isEqual, mapStyledProps } from '../shared';
|
||||
import {
|
||||
LoadingOutlined,
|
||||
PlusOutlined,
|
||||
UploadOutlined,
|
||||
InboxOutlined
|
||||
} from '@ant-design/icons'
|
||||
const { Dragger: UploadDragger } = AntdUpload
|
||||
InboxOutlined,
|
||||
} from '@ant-design/icons';
|
||||
const { Dragger: UploadDragger } = AntdUpload;
|
||||
import get from 'lodash/get';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import Lightbox from 'react-image-lightbox';
|
||||
@ -16,77 +16,77 @@ import Lightbox from 'react-image-lightbox';
|
||||
const exts = [
|
||||
{
|
||||
ext: /\.docx?$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1n8jfr1uSBuNjy1XcXXcYjFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1n8jfr1uSBuNjy1XcXXcYjFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.pptx?$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1ItgWr_tYBeNjy1XdXXXXyVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1ItgWr_tYBeNjy1XdXXXXyVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.jpe?g$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1wrT5r9BYBeNjy0FeXXbnmFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1wrT5r9BYBeNjy0FeXXbnmFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.pdf$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1GwD8r9BYBeNjy0FeXXbnmFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1GwD8r9BYBeNjy0FeXXbnmFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.png$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1BHT5r9BYBeNjy0FeXXbnmFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1BHT5r9BYBeNjy0FeXXbnmFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.eps$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1G_iGrVOWBuNjy0FiXXXFxVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1G_iGrVOWBuNjy0FiXXXFxVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.ai$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1B2cVr_tYBeNjy1XdXXXXyVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1B2cVr_tYBeNjy1XdXXXXyVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.gif$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1DTiGrVOWBuNjy0FiXXXFxVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1DTiGrVOWBuNjy0FiXXXFxVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.svg$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1uUm9rY9YBuNjy0FgXXcxcXXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1uUm9rY9YBuNjy0FgXXcxcXXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.xlsx?$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1any1r1OSBuNjy0FdXXbDnVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1any1r1OSBuNjy0FdXXbDnVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.psd?$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1_nu1r1OSBuNjy0FdXXbDnVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1_nu1r1OSBuNjy0FdXXbDnVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.(wav|aif|aiff|au|mp1|mp2|mp3|ra|rm|ram|mid|rmi)$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1jPvwr49YBuNjy0FfXXXIsVXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1jPvwr49YBuNjy0FfXXXIsVXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.(avi|wmv|mpg|mpeg|vob|dat|3gp|mp4|mkv|rm|rmvb|mov|flv)$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB1FrT5r9BYBeNjy0FeXXbnmFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB1FrT5r9BYBeNjy0FeXXbnmFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.(zip|rar|arj|z|gz|iso|jar|ace|tar|uue|dmg|pkg|lzh|cab)$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB10jmfr29TBuNjy0FcXXbeiFXa-200-200.png'
|
||||
icon: '//img.alicdn.com/tfs/TB10jmfr29TBuNjy0FcXXbeiFXa-200-200.png',
|
||||
},
|
||||
{
|
||||
ext: /\.[^.]+$/i,
|
||||
icon: '//img.alicdn.com/tfs/TB10.R4r3mTBuNjy1XbXXaMrVXa-200-200.png'
|
||||
}
|
||||
]
|
||||
icon: '//img.alicdn.com/tfs/TB10.R4r3mTBuNjy1XbXXaMrVXa-200-200.png',
|
||||
},
|
||||
];
|
||||
|
||||
const testOpts = (ext, options) => {
|
||||
if (options && isArr(options.include)) {
|
||||
return options.include.some(url => ext.test(url))
|
||||
return options.include.some(url => ext.test(url));
|
||||
}
|
||||
|
||||
if (options && isArr(options.exclude)) {
|
||||
return !options.exclude.some(url => ext.test(url))
|
||||
return !options.exclude.some(url => ext.test(url));
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const testUrl = (url, options) => {
|
||||
for (let i = 0; i < exts.length; i++) {
|
||||
@ -96,24 +96,24 @@ export const testUrl = (url, options) => {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const getImageByUrl = (url, options) => {
|
||||
for (let i = 0; i < exts.length; i++) {
|
||||
if (exts[i].ext.test(url) && testOpts(exts[i].ext, options)) {
|
||||
return exts[i].icon || url
|
||||
return exts[i].icon || url;
|
||||
}
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
function toFileObject(item) {
|
||||
console.log(item);
|
||||
if (typeof item === 'number') {
|
||||
return {
|
||||
id: item,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (item.id && item.uid && item.url) {
|
||||
return item;
|
||||
@ -145,7 +145,7 @@ function toValue(item) {
|
||||
if (typeof item === 'number') {
|
||||
return {
|
||||
id: item,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (item.id && item.uid && item.url) {
|
||||
return item;
|
||||
@ -176,14 +176,18 @@ function toValues(fileList) {
|
||||
|
||||
export function getImgUrls(value) {
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
return values.filter(item => testUrl(item.url, {
|
||||
exclude: ['.png', '.jpg', '.jpeg', '.gif']
|
||||
})).map(item => toValue(item));
|
||||
return values
|
||||
.filter(item =>
|
||||
testUrl(item.url, {
|
||||
exclude: ['.png', '.jpg', '.jpeg', '.gif'],
|
||||
}),
|
||||
)
|
||||
.map(item => toValue(item));
|
||||
}
|
||||
|
||||
export const Upload = connect({
|
||||
getProps: mapStyledProps
|
||||
})((props) => {
|
||||
getProps: mapStyledProps,
|
||||
})(props => {
|
||||
const { multiple = true, value, onChange } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [imgIndex, setImgIndex] = useState(0);
|
||||
@ -193,13 +197,13 @@ export const Upload = connect({
|
||||
action: `${process.env.API}/attachments:upload`,
|
||||
onChange({ fileList }) {
|
||||
console.log(fileList);
|
||||
setFileList((fileList));
|
||||
setFileList(fileList);
|
||||
const list = toValues(fileList);
|
||||
onChange(multiple ? list : (list.shift()||null));
|
||||
onChange(multiple ? list : list.shift() || null);
|
||||
},
|
||||
};
|
||||
const images = getImgUrls(fileList);
|
||||
console.log({fileList});
|
||||
console.log({ fileList });
|
||||
return (
|
||||
<div>
|
||||
<AntdUpload
|
||||
@ -207,14 +211,14 @@ export const Upload = connect({
|
||||
{...uploadProps}
|
||||
fileList={fileList}
|
||||
multiple={true}
|
||||
onPreview={(file) => {
|
||||
const value = toValue(file)||{};
|
||||
onPreview={file => {
|
||||
const value = toValue(file) || {};
|
||||
const index = findIndex(images, image => image.id === value.id);
|
||||
if (index >= 0) {
|
||||
setImgIndex(index);
|
||||
setVisible(true);
|
||||
} else {
|
||||
window.open(value.url)
|
||||
window.open(value.url);
|
||||
// window.location.href = value.url;
|
||||
}
|
||||
}}
|
||||
@ -228,7 +232,7 @@ export const Upload = connect({
|
||||
// );
|
||||
// }}
|
||||
// onRemove={(file) => {
|
||||
|
||||
|
||||
// }}
|
||||
>
|
||||
{(multiple || fileList.length < 1) && (
|
||||
@ -237,20 +241,22 @@ export const Upload = connect({
|
||||
</>
|
||||
)}
|
||||
</AntdUpload>
|
||||
{visible && <Lightbox
|
||||
mainSrc={get(images, [imgIndex, 'url'])}
|
||||
nextSrc={get(images, [imgIndex + 1, 'url'])}
|
||||
prevSrc={get(images, [imgIndex - 1, 'url'])}
|
||||
onCloseRequest={() => setVisible(false)}
|
||||
onMovePrevRequest={() => {
|
||||
setImgIndex((imgIndex + images.length - 1) % images.length);
|
||||
}}
|
||||
onMoveNextRequest={() => {
|
||||
setImgIndex((imgIndex + 1) % images.length);
|
||||
}}
|
||||
/>}
|
||||
{visible && (
|
||||
<Lightbox
|
||||
mainSrc={get(images, [imgIndex, 'url'])}
|
||||
nextSrc={get(images, [imgIndex + 1, 'url'])}
|
||||
prevSrc={get(images, [imgIndex - 1, 'url'])}
|
||||
onCloseRequest={() => setVisible(false)}
|
||||
onMovePrevRequest={() => {
|
||||
setImgIndex((imgIndex + images.length - 1) % images.length);
|
||||
}}
|
||||
onMoveNextRequest={() => {
|
||||
setImgIndex((imgIndex + 1) % images.length);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
export default Upload;
|
||||
export default Upload;
|
||||
|
@ -1 +1 @@
|
||||
import 'antd/lib/upload/style/index'
|
||||
import 'antd/lib/upload/style/index';
|
||||
|
@ -1,9 +1,19 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Select, Input, Space, Form, InputNumber, DatePicker, TimePicker, Radio } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
Input,
|
||||
Space,
|
||||
Form,
|
||||
InputNumber,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Radio,
|
||||
} from 'antd';
|
||||
import { PlusCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
|
||||
import useDynamicList from './useDynamicList';
|
||||
import { connect } from '@formily/react-schema-renderer'
|
||||
import { mapStyledProps } from '../shared'
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import { mapStyledProps } from '../shared';
|
||||
import get from 'lodash/get';
|
||||
import moment from 'moment';
|
||||
import './style.less';
|
||||
@ -11,8 +21,17 @@ import api from '@/api-client';
|
||||
import { useRequest } from 'umi';
|
||||
|
||||
export function FilterGroup(props: any) {
|
||||
const { fields = [], sourceFields = [], onDelete, onChange, onAdd, dataSource = [] } = props;
|
||||
const { list, getKey, push, remove, replace } = useDynamicList<any>(dataSource);
|
||||
const {
|
||||
fields = [],
|
||||
sourceFields = [],
|
||||
onDelete,
|
||||
onChange,
|
||||
onAdd,
|
||||
dataSource = [],
|
||||
} = props;
|
||||
const { list, getKey, push, remove, replace } = useDynamicList<any>(
|
||||
dataSource,
|
||||
);
|
||||
let style: any = {
|
||||
position: 'relative',
|
||||
};
|
||||
@ -23,40 +42,46 @@ export function FilterGroup(props: any) {
|
||||
// console.log(item);
|
||||
// const Component = item.type === 'group' ? FilterGroup : FilterItem;
|
||||
return (
|
||||
<div style={{marginBottom: 8}}>
|
||||
{<FilterItem
|
||||
fields={fields}
|
||||
sourceFields={sourceFields}
|
||||
dataSource={item}
|
||||
// showDeleteButton={list.length > 1}
|
||||
onChange={(value) => {
|
||||
replace(index, value);
|
||||
const newList = [...list];
|
||||
newList[index] = value;
|
||||
onChange(newList);
|
||||
// console.log(list, value, index);
|
||||
}}
|
||||
onDelete={() => {
|
||||
remove(index);
|
||||
const newList = [...list];
|
||||
newList.splice(index, 1);
|
||||
onChange(newList);
|
||||
// console.log(list, index);
|
||||
}}
|
||||
/>}
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
{
|
||||
<FilterItem
|
||||
fields={fields}
|
||||
sourceFields={sourceFields}
|
||||
dataSource={item}
|
||||
// showDeleteButton={list.length > 1}
|
||||
onChange={value => {
|
||||
replace(index, value);
|
||||
const newList = [...list];
|
||||
newList[index] = value;
|
||||
onChange(newList);
|
||||
// console.log(list, value, index);
|
||||
}}
|
||||
onDelete={() => {
|
||||
remove(index);
|
||||
const newList = [...list];
|
||||
newList.splice(index, 1);
|
||||
onChange(newList);
|
||||
// console.log(list, index);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<Space>
|
||||
<Button style={{padding: 0}} type={'link'} onClick={() => {
|
||||
const data = {};
|
||||
push(data);
|
||||
const newList = [...list];
|
||||
newList.push(data);
|
||||
onChange(newList);
|
||||
}}>
|
||||
<Button
|
||||
style={{ padding: 0 }}
|
||||
type={'link'}
|
||||
onClick={() => {
|
||||
const data = {};
|
||||
push(data);
|
||||
const newList = [...list];
|
||||
newList.push(data);
|
||||
onChange(newList);
|
||||
}}
|
||||
>
|
||||
<PlusCircleOutlined /> 新增一个字段赋值
|
||||
</Button>
|
||||
</Space>
|
||||
@ -79,72 +104,72 @@ interface FilterItemProps {
|
||||
|
||||
const OP_MAP = {
|
||||
string: [
|
||||
{label: '包含', value: '$includes', selected: true},
|
||||
{label: '不包含', value: '$notIncludes'},
|
||||
{label: '等于', value: 'eq'},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '包含', value: '$includes', selected: true },
|
||||
{ label: '不包含', value: '$notIncludes' },
|
||||
{ label: '等于', value: 'eq' },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
number: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '大于', value: 'gt'},
|
||||
{label: '大于等于', value: 'gte'},
|
||||
{label: '小于', value: 'lt'},
|
||||
{label: '小于等于', value: 'lte'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '大于', value: 'gt' },
|
||||
{ label: '大于等于', value: 'gte' },
|
||||
{ label: '小于', value: 'lt' },
|
||||
{ label: '小于等于', value: 'lte' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
file: [
|
||||
{label: '存在', value: 'id.gt'},
|
||||
{label: '不存在', value: 'id.$null'},
|
||||
{ label: '存在', value: 'id.gt' },
|
||||
{ label: '不存在', value: 'id.$null' },
|
||||
],
|
||||
boolean: [
|
||||
{label: '是', value: '$isTruly', selected: true},
|
||||
{label: '否', value: '$isFalsy'},
|
||||
{ label: '是', value: '$isTruly', selected: true },
|
||||
{ label: '否', value: '$isFalsy' },
|
||||
],
|
||||
select: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'ne'},
|
||||
{label: '包含', value: 'in'},
|
||||
{label: '不包含', value: 'notIn'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'ne' },
|
||||
{ label: '包含', value: 'in' },
|
||||
{ label: '不包含', value: 'notIn' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
multipleSelect: [
|
||||
{label: '等于', value: '$match', selected: true},
|
||||
{label: '不等于', value: '$notMatch'},
|
||||
{label: '包含', value: '$anyOf'},
|
||||
{label: '不包含', value: '$noneOf'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '等于', value: '$match', selected: true },
|
||||
{ label: '不等于', value: '$notMatch' },
|
||||
{ label: '包含', value: '$anyOf' },
|
||||
{ label: '不包含', value: '$noneOf' },
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
],
|
||||
datetime: [
|
||||
{label: '等于', value: '$dateOn', selected: true},
|
||||
{label: '不等于', value: '$dateNotOn'},
|
||||
{label: '早于', value: '$dateBefore'},
|
||||
{label: '晚于', value: '$dateAfter'},
|
||||
{label: '不早于', value: '$dateNotBefore'},
|
||||
{label: '不晚于', value: '$dateNotAfter'},
|
||||
{ label: '等于', value: '$dateOn', selected: true },
|
||||
{ label: '不等于', value: '$dateNotOn' },
|
||||
{ label: '早于', value: '$dateBefore' },
|
||||
{ label: '晚于', value: '$dateAfter' },
|
||||
{ label: '不早于', value: '$dateNotBefore' },
|
||||
{ label: '不晚于', value: '$dateNotAfter' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
// {label: '是今天', value: 'now'},
|
||||
// {label: '在今天之前', value: 'before_today'},
|
||||
// {label: '在今天之后', value: 'after_today'},
|
||||
],
|
||||
time: [
|
||||
{label: '等于', value: 'eq', selected: true},
|
||||
{label: '不等于', value: 'neq'},
|
||||
{label: '大于', value: 'gt'},
|
||||
{label: '大于等于', value: 'gte'},
|
||||
{label: '小于', value: 'lt'},
|
||||
{label: '小于等于', value: 'lte'},
|
||||
{ label: '等于', value: 'eq', selected: true },
|
||||
{ label: '不等于', value: 'neq' },
|
||||
{ label: '大于', value: 'gt' },
|
||||
{ label: '大于等于', value: 'gte' },
|
||||
{ label: '小于', value: 'lt' },
|
||||
{ label: '小于等于', value: 'lte' },
|
||||
// {label: '介于', value: 'between'},
|
||||
{label: '非空', value: '$notNull'},
|
||||
{label: '为空', value: '$null'},
|
||||
{ label: '非空', value: '$notNull' },
|
||||
{ label: '为空', value: '$null' },
|
||||
// {label: '是今天', value: 'now'},
|
||||
// {label: '在今天之前', value: 'before_today'},
|
||||
// {label: '在今天之后', value: 'after_today'},
|
||||
@ -175,22 +200,26 @@ const op = {
|
||||
attachment: OP_MAP.file,
|
||||
};
|
||||
|
||||
const StringInput = (props) => {
|
||||
const {value, onChange, ...restProps } = props;
|
||||
const StringInput = props => {
|
||||
const { value, onChange, ...restProps } = props;
|
||||
return (
|
||||
<Input {...restProps} defaultValue={value} onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}/>
|
||||
<Input
|
||||
{...restProps}
|
||||
defaultValue={value}
|
||||
onChange={e => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const controls = {
|
||||
string: StringInput,
|
||||
textarea: StringInput,
|
||||
number: InputNumber,
|
||||
percent: (props) => (
|
||||
<InputNumber
|
||||
formatter={value => value ? `${value}%` : ''}
|
||||
percent: props => (
|
||||
<InputNumber
|
||||
formatter={value => (value ? `${value}%` : '')}
|
||||
parser={value => value.replace('%', '')}
|
||||
{...props}
|
||||
/>
|
||||
@ -213,9 +242,13 @@ function DateControl(props: any) {
|
||||
// }
|
||||
const m = moment(value, format);
|
||||
return (
|
||||
<DatePicker format={format} value={m.isValid() ? m : null} onChange={(value) => {
|
||||
onChange(value ? value.format('YYYY-MM-DD') : null)
|
||||
}}/>
|
||||
<DatePicker
|
||||
format={format}
|
||||
value={m.isValid() ? m : null}
|
||||
onChange={value => {
|
||||
onChange(value ? value.format('YYYY-MM-DD') : null);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
// return (
|
||||
// <DatePicker format={format} showTime={field.showTime} value={m.isValid() ? m : null} onChange={(value) => {
|
||||
@ -228,13 +261,15 @@ function TimeControl(props: any) {
|
||||
const { field, value, onChange, ...restProps } = props;
|
||||
let format = field.timeFormat;
|
||||
const m = moment(value, format);
|
||||
return <TimePicker
|
||||
value={m.isValid() ? m : null}
|
||||
format={field.timeFormat}
|
||||
onChange={(value) => {
|
||||
onChange(value ? value.format('HH:mm:ss') : null)
|
||||
}}
|
||||
/>
|
||||
return (
|
||||
<TimePicker
|
||||
value={m.isValid() ? m : null}
|
||||
format={field.timeFormat}
|
||||
onChange={value => {
|
||||
onChange(value ? value.format('HH:mm:ss') : null);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function OptionControl(props) {
|
||||
@ -244,19 +279,27 @@ function OptionControl(props) {
|
||||
mode = undefined;
|
||||
}
|
||||
return (
|
||||
<Select style={{ minWidth: 120 }} mode={mode} value={value} onChange={(value) => {
|
||||
onChange(value);
|
||||
}} options={options}>
|
||||
</Select>
|
||||
<Select
|
||||
style={{ minWidth: 120 }}
|
||||
mode={mode}
|
||||
value={value}
|
||||
onChange={value => {
|
||||
onChange(value);
|
||||
}}
|
||||
options={options}
|
||||
></Select>
|
||||
);
|
||||
}
|
||||
|
||||
function BooleanControl(props) {
|
||||
const { value, onChange, ...restProps } = props;
|
||||
return (
|
||||
<Radio.Group value={value} onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}>
|
||||
<Radio.Group
|
||||
value={value}
|
||||
onChange={e => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
>
|
||||
<Radio value={true}>是</Radio>
|
||||
<Radio value={false}>否</Radio>
|
||||
</Radio.Group>
|
||||
@ -268,10 +311,17 @@ function NullControl(props) {
|
||||
}
|
||||
|
||||
export function FilterItem(props: FilterItemProps) {
|
||||
const { index, fields = [], sourceFields = [], showDeleteButton = true, onDelete, onChange } = props;
|
||||
const {
|
||||
index,
|
||||
fields = [],
|
||||
sourceFields = [],
|
||||
showDeleteButton = true,
|
||||
onDelete,
|
||||
onChange,
|
||||
} = props;
|
||||
const [type, setType] = useState('string');
|
||||
const [field, setField] = useState<any>({});
|
||||
const [dataSource, setDataSource] = useState(props.dataSource||{});
|
||||
const [dataSource, setDataSource] = useState(props.dataSource || {});
|
||||
useEffect(() => {
|
||||
const field = fields.find(field => field.name === props.dataSource.column);
|
||||
if (field) {
|
||||
@ -282,59 +332,68 @@ export function FilterItem(props: FilterItemProps) {
|
||||
}
|
||||
setType(componentType);
|
||||
}
|
||||
setDataSource({...props.dataSource});
|
||||
}, [
|
||||
props.dataSource, type,
|
||||
]);
|
||||
let ValueControl = controls[type]||controls.string;
|
||||
setDataSource({ ...props.dataSource });
|
||||
}, [props.dataSource, type]);
|
||||
let ValueControl = controls[type] || controls.string;
|
||||
if (['truncate'].indexOf(dataSource.op) !== -1) {
|
||||
ValueControl = NullControl;
|
||||
} else if (dataSource.op === 'ref') {
|
||||
ValueControl = () => {
|
||||
return (
|
||||
<Select value={dataSource.value}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, value: value});
|
||||
<Select
|
||||
value={dataSource.value}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, value: value });
|
||||
}}
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}>
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}
|
||||
>
|
||||
{sourceFields.map(field => (
|
||||
<Select.Option value={field.name}>{field.title}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
// let multiple = true;
|
||||
// if ()
|
||||
// const opOptions = op[type]||op.string;
|
||||
const opOptions = [
|
||||
{label: '自定义填写', value: 'eq', selected: true},
|
||||
{label: '等于触发数据', value: 'ref'},
|
||||
{label: '清空数据', value: 'truncate'},
|
||||
{ label: '自定义填写', value: 'eq', selected: true },
|
||||
{ label: '等于触发数据', value: 'ref' },
|
||||
{ label: '清空数据', value: 'truncate' },
|
||||
];
|
||||
console.log({field, dataSource, type, ValueControl});
|
||||
console.log({ field, dataSource, type, ValueControl });
|
||||
return (
|
||||
<Space>
|
||||
<Select value={dataSource.column}
|
||||
onChange={(value) => {
|
||||
<Select
|
||||
value={dataSource.column}
|
||||
onChange={value => {
|
||||
const field = fields.find(field => field.name === value);
|
||||
let componentType = field.component.type;
|
||||
if (field.component.type === 'select' && field.multiple) {
|
||||
componentType = 'multipleSelect';
|
||||
}
|
||||
setType(componentType);
|
||||
onChange({...dataSource, column: value, op: get(opOptions, [0, 'value']), value: undefined});
|
||||
onChange({
|
||||
...dataSource,
|
||||
column: value,
|
||||
op: get(opOptions, [0, 'value']),
|
||||
value: undefined,
|
||||
});
|
||||
}}
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}>
|
||||
style={{ width: 120 }}
|
||||
placeholder={'选择字段'}
|
||||
>
|
||||
{fields.map(field => (
|
||||
<Select.Option value={field.name}>{field.title}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Select value={dataSource.column ? dataSource.op : null} style={{ minWidth: 130 }}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, op: value, value: undefined});
|
||||
<Select
|
||||
value={dataSource.column ? dataSource.op : null}
|
||||
style={{ minWidth: 130 }}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, op: value, value: undefined });
|
||||
}}
|
||||
defaultValue={get(opOptions, [0, 'value'])}
|
||||
options={opOptions}
|
||||
@ -343,21 +402,28 @@ export function FilterItem(props: FilterItemProps) {
|
||||
<Select.Option value={option.value}>{option.label}</Select.Option>
|
||||
))} */}
|
||||
</Select>
|
||||
<ValueControl
|
||||
field={field}
|
||||
multiple={type === 'checkboxes' || !!field.multiple}
|
||||
op={dataSource.op}
|
||||
options={field.dataSource}
|
||||
value={dataSource.value}
|
||||
onChange={(value) => {
|
||||
onChange({...dataSource, value: value});
|
||||
<ValueControl
|
||||
field={field}
|
||||
multiple={type === 'checkboxes' || !!field.multiple}
|
||||
op={dataSource.op}
|
||||
options={field.dataSource}
|
||||
value={dataSource.value}
|
||||
onChange={value => {
|
||||
onChange({ ...dataSource, value: value });
|
||||
}}
|
||||
style={{ width: 180 }}
|
||||
/>
|
||||
{showDeleteButton && (
|
||||
<Button className={'filter-remove-link filter-item'} type={'link'} style={{padding: 0}} onClick={(e) => {
|
||||
onDelete && onDelete(e);
|
||||
}}><CloseCircleOutlined /></Button>
|
||||
<Button
|
||||
className={'filter-remove-link filter-item'}
|
||||
type={'link'}
|
||||
style={{ padding: 0 }}
|
||||
onClick={e => {
|
||||
onDelete && onDelete(e);
|
||||
}}
|
||||
>
|
||||
<CloseCircleOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
@ -365,35 +431,65 @@ export function FilterItem(props: FilterItemProps) {
|
||||
|
||||
export const Values = connect({
|
||||
getProps: mapStyledProps,
|
||||
})((props) => {
|
||||
|
||||
const { value = [], onChange, associatedKey, sourceName, sourceFilter = {}, filter = {}, fields = [], ...restProps } = props;
|
||||
})(props => {
|
||||
const {
|
||||
value = [],
|
||||
onChange,
|
||||
associatedKey,
|
||||
sourceName,
|
||||
sourceFilter = {},
|
||||
filter = {},
|
||||
fields = [],
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const { data = [], loading = true } = useRequest(() => {
|
||||
return associatedKey ? api.resource(`collections.fields`).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
}) : Promise.resolve({
|
||||
data: fields,
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [associatedKey]
|
||||
});
|
||||
const { data = [], loading = true } = useRequest(
|
||||
() => {
|
||||
return associatedKey
|
||||
? api.resource(`collections.fields`).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
})
|
||||
: Promise.resolve({
|
||||
data: fields,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [associatedKey],
|
||||
},
|
||||
);
|
||||
|
||||
const { data: sourceFields = [] } = useRequest(() => {
|
||||
return sourceName ? api.resource(`collections.fields`).list({
|
||||
associatedKey: sourceName,
|
||||
filter: sourceFilter,
|
||||
}) : Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
}, {
|
||||
refreshDeps: [sourceName]
|
||||
});
|
||||
const { data: sourceFields = [] } = useRequest(
|
||||
() => {
|
||||
return sourceName
|
||||
? api.resource(`collections.fields`).list({
|
||||
associatedKey: sourceName,
|
||||
filter: sourceFilter,
|
||||
})
|
||||
: Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [sourceName],
|
||||
},
|
||||
);
|
||||
|
||||
return <FilterGroup dataSource={Array.isArray(value) ? value.filter(item => Object.keys(item).length) : []} onChange={(values) => {
|
||||
onChange(values.filter(item => Object.keys(item).length));
|
||||
}} {...restProps} fields={data} sourceFields={sourceFields}/>
|
||||
return (
|
||||
<FilterGroup
|
||||
dataSource={
|
||||
Array.isArray(value)
|
||||
? value.filter(item => Object.keys(item).length)
|
||||
: []
|
||||
}
|
||||
onChange={values => {
|
||||
onChange(values.filter(item => Object.keys(item).length));
|
||||
}}
|
||||
{...restProps}
|
||||
fields={data}
|
||||
sourceFields={sourceFields}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Values;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user