mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:39:05 +08:00
调整为 monorepo
This commit is contained in:
parent
6c99d5fe91
commit
ebb798edf6
4
.gitignore
vendored
4
.gitignore
vendored
@ -25,3 +25,7 @@ package-lock.json
|
||||
/schema.json
|
||||
/npm
|
||||
/mock/cfc/cfc.zip
|
||||
.rollup.cache
|
||||
|
||||
dist
|
||||
tsconfig.tsbuildinfo
|
@ -1,293 +0,0 @@
|
||||
import {
|
||||
registerRenderer,
|
||||
unRegisterRenderer,
|
||||
RendererProps
|
||||
} from '../src/factory';
|
||||
import '../src/themes/default';
|
||||
import {render as amisRender} from '../src/index';
|
||||
import React = require('react');
|
||||
import {render, fireEvent, cleanup, waitFor} from '@testing-library/react';
|
||||
import {wait, makeEnv} from './helper';
|
||||
|
||||
test('factory unregistered Renderer', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender({
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
})
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Error: 找不到对应的渲染器')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('factory custom not found!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => Promise.resolve(() => <div>Not Found</div>)
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory custom not found 2!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => () => <div>Not Found</div>
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory custom not found 3!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => <div>Not Found</div>
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory load Renderer on need', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer2',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
session: 'loadRenderer',
|
||||
loadRenderer: schema => {
|
||||
interface MyProps extends RendererProps {
|
||||
a?: number;
|
||||
}
|
||||
|
||||
class MyComponent extends React.Component<MyProps> {
|
||||
render() {
|
||||
return <div>This is Custom Renderer2, a is {this.props.a}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
registerRenderer({
|
||||
component: MyComponent,
|
||||
test: /\bmy-renderer2$/
|
||||
});
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('This is Custom Renderer2, a is 23')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory:registerRenderer', () => {
|
||||
interface MyProps extends RendererProps {
|
||||
a?: number;
|
||||
}
|
||||
|
||||
class MyComponent extends React.Component<MyProps> {
|
||||
render() {
|
||||
return <div>This is Custom Renderer, a is {this.props.a}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = registerRenderer({
|
||||
component: MyComponent,
|
||||
test: /\bmy-renderer$/
|
||||
});
|
||||
|
||||
const {container} = render(
|
||||
amisRender({
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
})
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
unRegisterRenderer(renderer);
|
||||
});
|
||||
|
||||
test('factory:definitions', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
definitions: {
|
||||
aa: {
|
||||
type: 'text',
|
||||
name: 'jack',
|
||||
value: 'ref value',
|
||||
remark: '通过<code>\\$ref</code>引入的组件'
|
||||
},
|
||||
bb: {
|
||||
type: 'combo',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
remark: '<code>combo</code>中的子项引入自身,实现嵌套的效果',
|
||||
controls: [
|
||||
{
|
||||
label: 'combo 1',
|
||||
type: 'text',
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
label: 'combo 2',
|
||||
name: 'value',
|
||||
$ref: 'aa'
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
label: 'children',
|
||||
$ref: 'bb'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
type: 'page',
|
||||
title: '引用',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: 'api/xxx',
|
||||
actions: [],
|
||||
controls: [
|
||||
{
|
||||
label: 'text2',
|
||||
$ref: 'aa',
|
||||
name: 'ref1'
|
||||
},
|
||||
{
|
||||
label: 'combo',
|
||||
$ref: 'bb',
|
||||
name: 'ref2'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('新增')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('新增'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('combo 1')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('factory:definitions override', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
definitions: {
|
||||
aa: {
|
||||
type: 'text',
|
||||
name: 'jack',
|
||||
remark: '通过<code>\\$ref</code>引入的组件'
|
||||
},
|
||||
bb: {
|
||||
type: 'combo',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
remark: '<code>combo</code>中的子项引入自身,实现嵌套的效果',
|
||||
controls: [
|
||||
{
|
||||
label: 'combo 1',
|
||||
type: 'text',
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
label: 'combo 2',
|
||||
name: 'value',
|
||||
$ref: 'aa'
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
label: 'children',
|
||||
$ref: 'bb'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
type: 'page',
|
||||
title: '引用',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: 'api/xxx',
|
||||
actions: [],
|
||||
controls: [
|
||||
{
|
||||
label: 'text2',
|
||||
$ref: 'aa',
|
||||
name: 'ref1'
|
||||
},
|
||||
{
|
||||
label: 'combo',
|
||||
$ref: 'bb',
|
||||
name: 'ref2',
|
||||
type: 'checkboxes',
|
||||
value: 1,
|
||||
options: [
|
||||
{
|
||||
label: 'Option A',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: 'Option B',
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('combo')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
@ -1,715 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Renderer:breadcrumb 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
class="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
上级页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb className 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Breadcrumb className"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-default itemClassName"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
class="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item is-opened"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default itemClassName"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
上级页面
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-caret"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-caret"
|
||||
icon="caret"
|
||||
/>
|
||||
</span>
|
||||
<ul
|
||||
class="cxd-Breadcrumb-dropdown dropdownClassName"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-dropdown dropdownItemClassName"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
选项一
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-dropdown dropdownItemClassName"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
选项二
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default itemClassName"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb dropdown 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
class="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
上级页面
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-caret"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-caret"
|
||||
icon="caret"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb separator 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
class="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
上级页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 2`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 3`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 4`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb var 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
上级页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-separator"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
class="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
rm -rf npm
|
||||
|
||||
echo "Cloning"
|
||||
git clone -b npm --depth=1 https://$GH_TOKEN@github.com/baidu/amis.git npm
|
||||
|
||||
echo "building"
|
||||
sh build.sh
|
||||
|
||||
cp -rf lib npm
|
||||
cp package.json npm
|
||||
cp schema.json npm
|
||||
cp -rf scss npm
|
||||
cp -rf examples npm
|
||||
cp -rf sdk npm
|
||||
|
||||
echo "pushing"
|
||||
|
||||
cd npm
|
||||
|
||||
git config user.email "liaoxuezhi@icloud.com"
|
||||
git config user.name "liaoxuezhi"
|
||||
|
||||
git add .
|
||||
git commit --allow-empty -m "npm 下一个版本"
|
||||
|
||||
git push --tags https://$GH_TOKEN@github.com/baidu/amis.git npm
|
||||
|
||||
echo "done"
|
@ -1,20 +1,19 @@
|
||||
import React from 'react';
|
||||
import NotFound from '../../src/components/404';
|
||||
import Layout from '../../src/components/Layout';
|
||||
import AsideNav from '../../src/components/AsideNav';
|
||||
import {
|
||||
NotFound,
|
||||
Layout,
|
||||
AsideNav,
|
||||
AlertComponent,
|
||||
Button,
|
||||
Drawer,
|
||||
Spinner,
|
||||
ToastComponent
|
||||
} from '../../src/components/index';
|
||||
import {eachTree, mapTree} from '../../src/utils/helper';
|
||||
import {Icon} from '../../src/components/icons';
|
||||
import '../../src/locale/en-US';
|
||||
ToastComponent,
|
||||
Select,
|
||||
InputBox
|
||||
} from 'amis';
|
||||
import {eachTree, mapTree} from 'amis/lib/utils/helper';
|
||||
import 'amis/lib/locale/en-US';
|
||||
import {withRouter} from 'react-router';
|
||||
import Select from '../../src/components/Select';
|
||||
import InputBox from '../../src/components/InputBox';
|
||||
import DocSearch from './DocSearch';
|
||||
import Doc from './Doc';
|
||||
import DocNavCN from './DocNavCN';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {Switch} from 'react-router-dom';
|
||||
|
||||
import {flattenTree, filterTree, mapTree} from '../../src/utils/helper';
|
||||
import {flattenTree, filterTree, mapTree} from 'amis/lib/utils/helper';
|
||||
import {navigations2route} from './App';
|
||||
|
||||
import DocNavCN from './DocNavCN';
|
||||
|
@ -3,9 +3,7 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import SearchBox from '../../src/components/SearchBox';
|
||||
import Drawer from '../../src/components/Drawer';
|
||||
import {Icon} from '../../src';
|
||||
import {Icon, Drawer, SearchBox} from 'amis';
|
||||
|
||||
let ContextPath = '';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import example from './EChartsEditor/Example';
|
||||
|
||||
import {lazyData} from './LazyData';
|
||||
import React from 'react';
|
||||
import Spinner from '../../src/components/Spinner';
|
||||
import Spinner from 'amis/lib/components/Spinner';
|
||||
|
||||
const LazyComponent = lazyData(
|
||||
async () =>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import Editor from '../../src/editor/Editor';
|
||||
import Switch from '../../src/components/Switch';
|
||||
import Button from '../../src/components/Button';
|
||||
import Editor from 'amis/editor/Editor';
|
||||
import Switch from 'amis/components/Switch';
|
||||
import Button from 'amis/components/Button';
|
||||
import schema from './Form/Test';
|
||||
import Portal from 'react-overlays/Portal';
|
||||
|
||||
|
@ -119,7 +119,7 @@ import Tab3Schema from './Tabs/Tab3';
|
||||
|
||||
import TestComponent from './Test';
|
||||
|
||||
import {normalizeLink} from '../../src/utils/normalizeLink';
|
||||
import {normalizeLink} from 'amis/utils/normalizeLink';
|
||||
import {Switch} from 'react-router-dom';
|
||||
import {navigations2route} from './App';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {FormItem, Renderer} from '../../../src/index';
|
||||
import {FormItem, Renderer} from 'amis';
|
||||
|
||||
@FormItem({
|
||||
type: 'my-custom'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import TitleBar from '../../../src/components/TitleBar';
|
||||
import {render} from '../../../src/index';
|
||||
import TitleBar from 'amis/components/TitleBar';
|
||||
import {render} from 'amis';
|
||||
|
||||
const Schema = {
|
||||
title: 'Person',
|
||||
|
@ -2,13 +2,13 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import {getTheme, render} from '../../src/index';
|
||||
import {getTheme, render} from 'amis';
|
||||
import axios from 'axios';
|
||||
import TitleBar from '../../src/components/TitleBar';
|
||||
import LazyComponent from '../../src/components/LazyComponent';
|
||||
import Overlay from '../../src/components/Overlay';
|
||||
import PopOver from '../../src/components/PopOver';
|
||||
import NestedLinks from '../../src/components/AsideNav';
|
||||
import TitleBar from 'amis/lib/components/TitleBar';
|
||||
import LazyComponent from 'amis/lib/components/LazyComponent';
|
||||
import Overlay from 'amis/lib/components/Overlay';
|
||||
import PopOver from 'amis/lib/components/PopOver';
|
||||
import NestedLinks from 'amis/lib/components/AsideNav';
|
||||
import classnames from 'classnames';
|
||||
import {Link} from 'react-router-dom';
|
||||
import Play from './Play';
|
||||
|
@ -1,16 +1,15 @@
|
||||
import React from 'react';
|
||||
import {toast} from '../../src/components/Toast';
|
||||
import {render, makeTranslator} from '../../src/index';
|
||||
import {normalizeLink} from '../../src/utils/normalizeLink';
|
||||
import {isMobile} from '../../src/utils/helper';
|
||||
import attachmentAdpator from '../../src/utils/attachmentAdpator';
|
||||
import {alert, confirm} from '../../src/components/Alert';
|
||||
import {toast, render, makeTranslator} from 'amis';
|
||||
import {normalizeLink} from 'amis/lib/utils/normalizeLink';
|
||||
import {isMobile} from 'amis/lib/utils/helper';
|
||||
import attachmentAdpator from 'amis/lib/utils/attachmentAdpator';
|
||||
import {alert, confirm} from 'amis/lib/components/Alert';
|
||||
import axios from 'axios';
|
||||
import JSON5 from 'json5';
|
||||
import CodeEditor from '../../src/components/Editor';
|
||||
import CodeEditor from 'amis/lib/components/Editor';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {matchPath} from 'react-router-dom';
|
||||
import Drawer from '../../src/components/Drawer';
|
||||
import Drawer from 'amis/lib/components/Drawer';
|
||||
|
||||
const DEFAULT_CONTENT = `{
|
||||
"$schema": "/schemas/page.json#",
|
||||
|
@ -1,21 +1,17 @@
|
||||
import React from 'react';
|
||||
import {render} from '../../src/index';
|
||||
import {render, toast, Button, LazyComponent, Drawer} from 'amis';
|
||||
import axios from 'axios';
|
||||
import Portal from 'react-overlays/Portal';
|
||||
import {toast} from '../../src/components/Toast';
|
||||
import {normalizeLink} from '../../src/utils/normalizeLink';
|
||||
import Button from '../../src/components/Button';
|
||||
import LazyComponent from '../../src/components/LazyComponent';
|
||||
import {default as DrawerContainer} from '../../src/components/Drawer';
|
||||
|
||||
import {toast} from 'amis';
|
||||
import {normalizeLink} from 'amis/lib/utils/normalizeLink';
|
||||
import {withRouter} from 'react-router';
|
||||
import {matchPath} from 'react-router-dom';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {qsparse} from '../../src/utils/helper';
|
||||
import {qsparse} from 'amis/lib/utils/helper';
|
||||
|
||||
function loadEditor() {
|
||||
return new Promise(resolve =>
|
||||
require(['../../src/components/Editor'], component =>
|
||||
require(['amis/lib/components/Editor'], component =>
|
||||
resolve(component.default))
|
||||
);
|
||||
}
|
||||
@ -266,7 +262,7 @@ export default function (schema, showCode, envOverrides) {
|
||||
<>
|
||||
<div className="schema-wrapper">
|
||||
{finalShowCode !== false ? (
|
||||
<DrawerContainer
|
||||
<Drawer
|
||||
classPrefix={ns}
|
||||
size="lg"
|
||||
onHide={this.close}
|
||||
@ -276,7 +272,7 @@ export default function (schema, showCode, envOverrides) {
|
||||
position="right"
|
||||
>
|
||||
{this.state.open ? this.renderCode() : null}
|
||||
</DrawerContainer>
|
||||
</Drawer>
|
||||
) : null}
|
||||
{this.renderSchema()}
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import TitleBar from '../../../src/components/TitleBar';
|
||||
import {render} from '../../../src/index';
|
||||
import {TitleBar} from 'amis';
|
||||
import {render} from 'amis';
|
||||
|
||||
export default class SdkTest extends React.Component {
|
||||
state = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import Button from '../../src/components/Button';
|
||||
import {Button} from 'amis';
|
||||
|
||||
export default class TestComponent extends React.Component {
|
||||
render() {
|
||||
|
@ -4,7 +4,7 @@ import {createRoot} from 'react-dom/client';
|
||||
import axios from 'axios';
|
||||
import {match} from 'path-to-regexp';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {normalizeLink} from '../src/utils/normalizeLink';
|
||||
import {normalizeLink} from 'amis/lib/utils/normalizeLink';
|
||||
|
||||
import qs from 'qs';
|
||||
import {
|
||||
@ -15,13 +15,13 @@ import {
|
||||
AlertComponent,
|
||||
render as renderAmis,
|
||||
makeTranslator
|
||||
} from '../src/index';
|
||||
} from 'amis';
|
||||
|
||||
import '../src/locale/en-US';
|
||||
import 'amis/lib/locale/en-US';
|
||||
import 'history';
|
||||
import attachmentAdpator from '../src/utils/attachmentAdpator';
|
||||
import attachmentAdpator from 'amis/lib/utils/attachmentAdpator';
|
||||
|
||||
import type {ToastLevel, ToastConf} from '../src/components/Toast';
|
||||
import type {ToastLevel, ToastConf} from 'amis/lib/components/Toast';
|
||||
|
||||
export function embed(
|
||||
container: string | HTMLElement,
|
||||
|
@ -46,48 +46,48 @@
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="ang" ${
|
||||
theme !== 'ang' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/ang.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/ang.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="cxd" ${
|
||||
theme !== 'cxd' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/cxd.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/cxd.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="dark" ${
|
||||
theme !== 'dark' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/dark.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/dark.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="antd" ${
|
||||
theme !== 'antd' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/antd.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/antd.scss')}" />`
|
||||
);
|
||||
} else {
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="ang" ${
|
||||
theme !== 'ang' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/ang-ie11.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/ang-ie11.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="cxd" ${
|
||||
theme !== 'cxd' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/cxd-ie11.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/cxd-ie11.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="dark" ${
|
||||
theme !== 'dark' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/dark-ie11.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/dark-ie11.scss')}" />`
|
||||
);
|
||||
document.write(
|
||||
`<link rel="stylesheet" title="antd" ${
|
||||
theme !== 'antd' ? 'disabled' : ''
|
||||
} href="${__uri('../scss/themes/antd-ie11.scss')}" />`
|
||||
} href="${__uri('amis/scss/themes/antd-ie11.scss')}" />`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<!--ignore-->
|
||||
<link rel="stylesheet" href="../scss/helper.scss" />
|
||||
<link rel="stylesheet" href="amis/scss/helper.scss" />
|
||||
<!--ignore-->
|
||||
</head>
|
||||
|
||||
|
@ -21,8 +21,8 @@
|
||||
'echarts': __moduleId('echarts'),
|
||||
'zrender': __moduleId('zrender'),
|
||||
'sortablejs': __moduleId('sortablejs'),
|
||||
'amis': __moduleId('../src'),
|
||||
'amis@@version': __moduleId('../src'),
|
||||
'amis': __moduleId('amis'),
|
||||
'amis@@version': __moduleId('amis'),
|
||||
'amis/embed': __moduleId('./embed.tsx'),
|
||||
'amis@@version/embed': __moduleId('./embed.tsx'),
|
||||
'prop-types': __moduleId('prop-types'),
|
||||
|
@ -8,10 +8,10 @@ import React from 'react';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import axios from 'axios';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {toast} from '../src/components/Toast';
|
||||
import '../src/locale/en-US';
|
||||
import {toast} from 'amis';
|
||||
import 'amis/lib/locale/en-US';
|
||||
|
||||
import {render as renderAmis} from '../src/index';
|
||||
import {render as renderAmis} from 'amis';
|
||||
|
||||
class AMISComponent extends React.Component {
|
||||
state = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import '../scss/mixins';
|
||||
@import '../scss/functions';
|
||||
@import '../scss/variables';
|
||||
@import 'node_modules/amis/scss/mixins';
|
||||
@import 'node_modules/amis/scss/functions';
|
||||
@import 'node_modules/amis/scss/variables';
|
||||
|
||||
body {
|
||||
background-color: #fff !important;
|
||||
|
119
fis-conf.js
119
fis-conf.js
@ -3,7 +3,7 @@
|
||||
*/
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const package = require('./package.json');
|
||||
const package = require('./packages/amis/package.json');
|
||||
const parserMarkdown = require('./scripts/md-parser');
|
||||
const convertSCSSIE11 = require('./scripts/scss-ie11');
|
||||
const parserCodeMarkdown = require('./scripts/code-md-parser');
|
||||
@ -64,7 +64,6 @@ fis.set('project.files', [
|
||||
'/examples/static/photo/*.png',
|
||||
'/examples/static/audio/*.mp3',
|
||||
'/examples/static/video/*.mp4',
|
||||
'/src/**.html',
|
||||
'mock/**'
|
||||
]);
|
||||
|
||||
@ -287,123 +286,11 @@ fis.media('dev').match('/node_modules/**.js', {
|
||||
packTo: '/pkg/npm.js'
|
||||
});
|
||||
|
||||
fis.match('monaco-editor/**', {
|
||||
fis.match('{monaco-editor,amis,amis-core}/**', {
|
||||
packTo: null
|
||||
});
|
||||
|
||||
if (fis.project.currentMedia() === 'publish') {
|
||||
const publishEnv = fis.media('publish');
|
||||
publishEnv.get('project.ignore').push('lib/**');
|
||||
publishEnv.set('project.files', ['/scss/**', '/src/**']);
|
||||
|
||||
fis.on('compile:end', function (file) {
|
||||
if (
|
||||
file.subpath === '/src/index.tsx' ||
|
||||
file.subpath === '/examples/mod.js'
|
||||
) {
|
||||
file.setContent(file.getContent().replace('@version', package.version));
|
||||
}
|
||||
});
|
||||
|
||||
publishEnv.match('/scss/(**)', {
|
||||
release: '/$1',
|
||||
relative: true
|
||||
});
|
||||
|
||||
publishEnv.match('/src/(**)', {
|
||||
release: '/$1',
|
||||
relative: true
|
||||
});
|
||||
|
||||
publishEnv.match('/src/**.{jsx,tsx,js,ts}', {
|
||||
parser: [
|
||||
// docsGennerator,
|
||||
fis.plugin('typescript', {
|
||||
importHelpers: true,
|
||||
sourceMap: true,
|
||||
experimentalDecorators: true,
|
||||
esModuleInterop: true,
|
||||
allowUmdGlobalAccess: true
|
||||
}),
|
||||
function (contents) {
|
||||
return contents
|
||||
.replace(
|
||||
/(?:\w+\.)?\b__uri\s*\(\s*('|")(.*?)\1\s*\)/g,
|
||||
function (_, quote, value) {
|
||||
let str = quote + value + quote;
|
||||
return (
|
||||
'(function(){try {return __uri(' +
|
||||
str +
|
||||
')} catch(e) {return ' +
|
||||
str +
|
||||
'}})()'
|
||||
);
|
||||
}
|
||||
)
|
||||
.replace(/\(\d+, (tslib_\d+\.__importStar)\)/g, '$1')
|
||||
.replace(
|
||||
/return\s+(tslib_\d+)\.__importStar\(require\(('|")(.*?)\2\)\);/g,
|
||||
function (_, tslib, quto, value) {
|
||||
return `return new Promise(function(resolve){require(['${value}'], function(ret) {resolve(${tslib}.__importStar(ret));})});`;
|
||||
}
|
||||
);
|
||||
}
|
||||
],
|
||||
preprocessor: null
|
||||
});
|
||||
|
||||
publishEnv.match('*', {
|
||||
deploy: fis.plugin('local-deliver', {
|
||||
to: fis.get('options.d') || fis.get('options.desc') || './lib'
|
||||
})
|
||||
});
|
||||
publishEnv.match('/src/**.{jsx,tsx,js,ts,svg}', {
|
||||
isMod: false,
|
||||
standard: false
|
||||
});
|
||||
|
||||
publishEnv.match('/src/**.{jsx,tsx,js,ts}', {
|
||||
postprocessor: function (content, file) {
|
||||
return content
|
||||
.replace(/^''/gm, '')
|
||||
.replace(/\/\/# sourceMappingURL=\//g, '//# sourceMappingURL=./');
|
||||
}
|
||||
});
|
||||
publishEnv.match('*.scss', {
|
||||
postprocessor: function (content, file) {
|
||||
return content.replace(
|
||||
/\/\*# sourceMappingURL=\//g,
|
||||
'/*# sourceMappingURL=./'
|
||||
);
|
||||
}
|
||||
});
|
||||
publishEnv.match('::package', {
|
||||
postpackager: function (ret) {
|
||||
Object.keys(ret.src).forEach(function (subpath) {
|
||||
var file = ret.src[subpath];
|
||||
if (!file.isText()) {
|
||||
return;
|
||||
}
|
||||
var content = file.getContent();
|
||||
if (subpath === '/src/components/icons.tsx') {
|
||||
content = content.replace(/\.svg/g, '.js');
|
||||
} else {
|
||||
content = content.replace(
|
||||
/@require\s+(?:\.\.\/)?node_modules\//g,
|
||||
'@require '
|
||||
);
|
||||
}
|
||||
file.setContent(content);
|
||||
});
|
||||
}
|
||||
});
|
||||
// publishEnv.unhook('node_modules');
|
||||
publishEnv.hook('relative');
|
||||
|
||||
publishEnv.match('_*.scss', {
|
||||
release: false
|
||||
});
|
||||
} else if (fis.project.currentMedia() === 'publish-sdk') {
|
||||
if (fis.project.currentMedia() === 'publish-sdk') {
|
||||
const env = fis.media('publish-sdk');
|
||||
|
||||
fis.on('compile:end', function (file) {
|
||||
|
235
package.json
235
package.json
@ -1,238 +1,13 @@
|
||||
{
|
||||
"name": "amis",
|
||||
"version": "1.10.0",
|
||||
"description": "一种MIS页面生成工具",
|
||||
"main": "lib/index.js",
|
||||
"name": "aisuda",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage",
|
||||
"serve": "fis3 server start --www ./public --port 8888 --no-daemon --no-browse",
|
||||
"start": "concurrently --restart-tries -1 npm:serve npm:dev",
|
||||
"update-snapshot": "jest --updateSnapshot",
|
||||
"stop": "fis3 server stop",
|
||||
"dev": "fis3 release -cwd ./public",
|
||||
"publish-to-internal": "sh build.sh && sh publish.sh",
|
||||
"build": "sh build.sh",
|
||||
"prettier": "prettier --write '{src,scss,examples}/**/**/*.{js,jsx,ts,tsx,scss,json}'",
|
||||
"deploy-gh-page": "sh ./deploy-gh-pages.sh",
|
||||
"build-schemas": "ts-node -O '{\"target\":\"es6\"}' scripts/build-schemas.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/baidu/amis.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"amis",
|
||||
"mis",
|
||||
"renderer",
|
||||
"json",
|
||||
"schema"
|
||||
],
|
||||
"author": "baidu",
|
||||
"license": "Apache-2.0",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache-2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
],
|
||||
"lint-staged": {
|
||||
"{src,examples}/**/**/*.{tsx,jsx,ts}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"amis-formula": "^1.3.15",
|
||||
"ansi-to-react": "^6.1.6",
|
||||
"attr-accept": "2.2.2",
|
||||
"blueimp-canvastoblob": "2.1.0",
|
||||
"classnames": "2.3.1",
|
||||
"codemirror": "^5.63.0",
|
||||
"downshift": "6.1.7",
|
||||
"echarts": "5.3.1",
|
||||
"echarts-stat": "^1.2.0",
|
||||
"exceljs": "^4.3.0",
|
||||
"file-saver": "^2.0.2",
|
||||
"froala-editor": "3.1.1",
|
||||
"hls.js": "1.1.3",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"hotkeys-js": "^3.8.7",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"jsbarcode": "^3.11.5",
|
||||
"keycode": "^2.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"markdown-it": "^12.0.6",
|
||||
"markdown-it-html5-media": "^0.7.1",
|
||||
"match-sorter": "^6.3.1",
|
||||
"mobx": "^4.5.0",
|
||||
"mobx-react": "^6.3.1",
|
||||
"mobx-state-tree": "^3.17.3",
|
||||
"moment": "^2.19.3",
|
||||
"monaco-editor": "0.30.1",
|
||||
"mpegts.js": "^1.6.10",
|
||||
"papaparse": "^5.3.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"punycode": "^2.1.1",
|
||||
"qrcode.react": "^3.0.0",
|
||||
"qs": "6.9.7",
|
||||
"rc-input-number": "^7.3.4",
|
||||
"rc-overflow": "^1.2.4",
|
||||
"rc-progress": "^3.1.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cropper": "^2.1.8",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-hook-form": "7.30.0",
|
||||
"react-input-range": "1.3.0",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-overlays": "5.1.1",
|
||||
"react-textarea-autosize": "8.3.3",
|
||||
"react-transition-group": "4.4.2",
|
||||
"react-visibility-sensor": "5.1.1",
|
||||
"sortablejs": "1.14.0",
|
||||
"tinymce": "^5.10.3",
|
||||
"tslib": "^2.3.1",
|
||||
"uncontrollable": "7.2.1",
|
||||
"video-react": "0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@types/async": "^2.0.45",
|
||||
"@types/codemirror": "^5.60.3",
|
||||
"@types/echarts": "^4.9.2",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/history": "^4.6.0",
|
||||
"@types/hoist-non-react-statics": "^3.3.1",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/json-schema": "^7.0.11",
|
||||
"@types/lodash": "^4.14.175",
|
||||
"@types/markdown-it": "^12.2.1",
|
||||
"@types/mkdirp": "^1.0.1",
|
||||
"@types/node": "^12.7.1",
|
||||
"@types/papaparse": "^5.2.2",
|
||||
"@types/prop-types": "^15.5.2",
|
||||
"@types/qs": "^6.5.1",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-color": "^3.0.5",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react-onclickoutside": "^6.0.2",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-test-renderer": "^17.0.1",
|
||||
"@types/react-transition-group": "4.4.3",
|
||||
"@types/sortablejs": "^1.3.32",
|
||||
"@types/tinymce": "^4.5.24",
|
||||
"axios": "0.25.0",
|
||||
"bce-sdk-js": "^0.2.9",
|
||||
"concurrently": "^7.0.0",
|
||||
"copy-to-clipboard": "3.3.1",
|
||||
"core-js": "^3.21.0",
|
||||
"css": "3.0.0",
|
||||
"fis-optimizer-terser": "^1.0.1",
|
||||
"fis-parser-sass": "^1.1.1",
|
||||
"fis-parser-svgr": "^1.0.0",
|
||||
"fis3": "^3.4.41",
|
||||
"fis3-deploy-skip-packed": "0.0.5",
|
||||
"fis3-hook-commonjs": "^0.1.31",
|
||||
"fis3-hook-node_modules": "^2.3.1",
|
||||
"fis3-hook-relative": "^2.0.3",
|
||||
"fis3-packager-deps-pack": "^0.1.2",
|
||||
"fis3-parser-typescript": "^1.4.0",
|
||||
"fis3-postpackager-loader": "^2.1.12",
|
||||
"fis3-prepackager-stand-alone-pack": "^1.0.0",
|
||||
"fis3-preprocessor-js-require-css": "^0.1.3",
|
||||
"fis3-preprocessor-js-require-file": "^0.1.3",
|
||||
"fs-walk": "0.0.2",
|
||||
"glob": "^7.2.0",
|
||||
"history": "^4.7.2",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^27.5.1",
|
||||
"jest-canvas-mock": "^2.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json5": "^2.2.1",
|
||||
"lint-staged": "^12.3.3",
|
||||
"marked": ">=4.0.12",
|
||||
"mkdirp": "^1.0.4",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"postcss": "^8.4.6",
|
||||
"postcss-cli": "^9.1.0",
|
||||
"postcss-custom-properties": "^12.1.5",
|
||||
"prettier": "^2.6.1",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"prismjs": "^1.25.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-router": "5.2.1",
|
||||
"react-router-dom": "5.3.0",
|
||||
"react-test-renderer": "^18.0.0",
|
||||
"ts-jest": "^27.1.4",
|
||||
"ts-json-schema-generator": "0.96.0",
|
||||
"ts-node": "^10.5.0",
|
||||
"typescript": "~4.5.5"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js"
|
||||
],
|
||||
"transform": {
|
||||
"\\.(ts|tsx)$": "ts-jest"
|
||||
},
|
||||
"setupFiles": [
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"testRegex": "/.*\\.test\\.(ts|tsx|js)$",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
|
||||
"\\.(svg)$": "<rootDir>/__mocks__/svgMock.js"
|
||||
},
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/__tests__/jest.setup.js"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false,
|
||||
"tsconfig": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom",
|
||||
"ES2015",
|
||||
"ES2021"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"rootDir": ".",
|
||||
"importHelpers": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceRoot": ".",
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./types"
|
||||
],
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.6",
|
||||
"react-dom": ">=16.8.6"
|
||||
"deploy-gh-page": "sh ./deploy-gh-pages.sh"
|
||||
}
|
||||
}
|
||||
|
5
packages/amis-core/.gitignore
vendored
Normal file
5
packages/amis-core/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/lib
|
||||
/node_modules
|
||||
/esm
|
||||
/.rollup.cache
|
||||
/tsconfig.tsbuildinfo
|
@ -0,0 +1,67 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`factory custom not found 2! 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
Not Found
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`factory custom not found 3! 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
Not Found
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`factory custom not found! 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
Not Found
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`factory load Renderer on need 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
This is Custom Renderer2, a is
|
||||
23
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`factory unregistered Renderer 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="RuntimeError"
|
||||
>
|
||||
<p>
|
||||
Error: 找不到对应的渲染器
|
||||
</p>
|
||||
<p>
|
||||
Path:
|
||||
my-renderer
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
{
|
||||
"type": "my-renderer",
|
||||
"a": 23
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`factory:registerRenderer 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
This is Custom Renderer, a is
|
||||
23
|
||||
</div>
|
||||
</div>
|
||||
`;
|
1
packages/amis-core/__tests__/compat.test.d.ts
vendored
Normal file
1
packages/amis-core/__tests__/compat.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
642
packages/amis-core/__tests__/compat.test.ts
Normal file
642
packages/amis-core/__tests__/compat.test.ts
Normal file
@ -0,0 +1,642 @@
|
||||
import moment from 'moment';
|
||||
import {
|
||||
resolveVariable,
|
||||
resolveVariableAndFilter
|
||||
} from '../src/utils/tpl-builtin';
|
||||
|
||||
const filters = [
|
||||
{
|
||||
type: 'raw1',
|
||||
data: {
|
||||
value: 1
|
||||
},
|
||||
path: '${value}',
|
||||
filter: '| raw',
|
||||
expectValue: 1
|
||||
},
|
||||
{
|
||||
type: 'raw2',
|
||||
data: {
|
||||
value: '2'
|
||||
},
|
||||
path: '${value}',
|
||||
filter: '| raw',
|
||||
expectValue: '2'
|
||||
},
|
||||
{
|
||||
type: 'raw3',
|
||||
data: {
|
||||
a: 1,
|
||||
b: '2',
|
||||
c: {
|
||||
'1': 'first',
|
||||
'2': 'second'
|
||||
}
|
||||
},
|
||||
path: '${c.${a}}',
|
||||
filter: '| raw',
|
||||
expectValue: 'first'
|
||||
},
|
||||
{
|
||||
type: 'raw4',
|
||||
data: {
|
||||
a: 1,
|
||||
b: '2',
|
||||
c: {
|
||||
'1': 'first',
|
||||
'2': 'second'
|
||||
}
|
||||
},
|
||||
path: '${c.${b}}',
|
||||
filter: '| raw',
|
||||
expectValue: 'second'
|
||||
},
|
||||
{
|
||||
type: 'raw5',
|
||||
data: {
|
||||
a: 1
|
||||
},
|
||||
path: '',
|
||||
filter: '| raw',
|
||||
expectValue: undefined
|
||||
},
|
||||
{
|
||||
type: 'raw6',
|
||||
data: {
|
||||
a: 1
|
||||
},
|
||||
path: '$$',
|
||||
filter: 'raw',
|
||||
expectValue: {a: 1}
|
||||
},
|
||||
{
|
||||
type: 'raw7',
|
||||
data: {
|
||||
a: 1
|
||||
},
|
||||
path: '$a',
|
||||
filter: '| raw',
|
||||
expectValue: 1
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
data: {
|
||||
value: {
|
||||
a: 'a',
|
||||
b: 'b'
|
||||
}
|
||||
},
|
||||
path: '${value | json:0}',
|
||||
filter: '',
|
||||
expectValue: '{"a":"a","b":"b"}'
|
||||
},
|
||||
{
|
||||
type: 'toJson',
|
||||
data: {
|
||||
value: '{"a":"a","b":"b"}'
|
||||
},
|
||||
path: '${value | toJson}',
|
||||
filter: '',
|
||||
expectValue: {
|
||||
a: 'a',
|
||||
b: 'b'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
data: {
|
||||
value: 1559649981
|
||||
},
|
||||
path: '${value | date}',
|
||||
filter: '',
|
||||
expectValue: moment(1559649981, 'X').format('LLL')
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
data: {
|
||||
value: 9999
|
||||
},
|
||||
path: '${value| number}',
|
||||
filter: '',
|
||||
expectValue: '9,999'
|
||||
},
|
||||
{
|
||||
type: 'trim',
|
||||
data: {
|
||||
value: ' abc '
|
||||
},
|
||||
path: '${value| trim}',
|
||||
filter: '',
|
||||
expectValue: 'abc'
|
||||
},
|
||||
{
|
||||
type: 'percent',
|
||||
data: {
|
||||
value: 0.8232343
|
||||
},
|
||||
path: '${value| percent}',
|
||||
filter: '',
|
||||
expectValue: '82%'
|
||||
},
|
||||
// duration
|
||||
{
|
||||
type: 'duration1',
|
||||
data: {
|
||||
value: 1
|
||||
},
|
||||
path: '${value| duration}',
|
||||
filter: '',
|
||||
expectValue: '1秒'
|
||||
},
|
||||
{
|
||||
type: 'duration2',
|
||||
data: {
|
||||
value: 61
|
||||
},
|
||||
path: '${value| duration}',
|
||||
filter: '',
|
||||
expectValue: '1分1秒'
|
||||
},
|
||||
{
|
||||
type: 'duration3',
|
||||
data: {
|
||||
value: 233233
|
||||
},
|
||||
path: '${value| duration}',
|
||||
filter: '',
|
||||
expectValue: '2天16时47分13秒'
|
||||
},
|
||||
// bytes
|
||||
{
|
||||
type: 'bytes1',
|
||||
data: {
|
||||
value: 1024
|
||||
},
|
||||
path: '${value| bytes}',
|
||||
filter: '',
|
||||
expectValue: '1.02 KB'
|
||||
},
|
||||
{
|
||||
type: 'bytes2',
|
||||
data: {
|
||||
value: 1024000
|
||||
},
|
||||
path: '${value| bytes}',
|
||||
filter: '',
|
||||
expectValue: '1.02 MB'
|
||||
},
|
||||
{
|
||||
type: 'bytes3',
|
||||
data: {
|
||||
value: -1024
|
||||
},
|
||||
path: '${value| bytes}',
|
||||
filter: '',
|
||||
expectValue: '-1.02 KB'
|
||||
},
|
||||
{
|
||||
type: 'bytes4',
|
||||
data: {
|
||||
value: 0.5
|
||||
},
|
||||
path: '${value| bytes}',
|
||||
filter: '',
|
||||
expectValue: '0.5 B'
|
||||
},
|
||||
// round
|
||||
{
|
||||
type: 'round1',
|
||||
data: {
|
||||
value: '啥啊'
|
||||
},
|
||||
path: '${value| round}',
|
||||
filter: '',
|
||||
expectValue: 0
|
||||
},
|
||||
{
|
||||
type: 'round2',
|
||||
data: {
|
||||
value: 1.22
|
||||
},
|
||||
path: '${value| round:1}',
|
||||
filter: '',
|
||||
expectValue: '1.2'
|
||||
},
|
||||
{
|
||||
type: 'round3',
|
||||
data: {
|
||||
value: 1.26
|
||||
},
|
||||
path: '${value| round:1}',
|
||||
filter: '',
|
||||
expectValue: '1.3'
|
||||
},
|
||||
{
|
||||
type: 'truncate1',
|
||||
data: {
|
||||
value: 'this is a very loooooong sentence.'
|
||||
},
|
||||
path: '${value| truncate:10}',
|
||||
filter: '',
|
||||
expectValue: 'this is a ...'
|
||||
},
|
||||
{
|
||||
type: 'truncate2',
|
||||
data: {
|
||||
value: 'this is a very loooooong sentence.'
|
||||
},
|
||||
path: '${value| truncate:null}',
|
||||
filter: '',
|
||||
expectValue: 'this is a very loooooong sentence.'
|
||||
},
|
||||
{
|
||||
type: 'url_encode',
|
||||
data: {
|
||||
value: 'http://www.baidu.com?query=123'
|
||||
},
|
||||
path: '${value| url_encode}',
|
||||
filter: '',
|
||||
expectValue: 'http%3A%2F%2Fwww.baidu.com%3Fquery%3D123'
|
||||
},
|
||||
{
|
||||
type: 'url_decode',
|
||||
data: {
|
||||
value: 'http%3A%2F%2Fwww.baidu.com%3Fquery%3D123'
|
||||
},
|
||||
path: '${value| url_decode:10}',
|
||||
filter: '',
|
||||
expectValue: 'http://www.baidu.com?query=123'
|
||||
},
|
||||
{
|
||||
type: 'default1',
|
||||
data: {
|
||||
value: ''
|
||||
},
|
||||
path: '${value| default}',
|
||||
filter: '',
|
||||
expectValue: undefined
|
||||
},
|
||||
{
|
||||
type: 'default2',
|
||||
data: {
|
||||
value: ''
|
||||
},
|
||||
path: '${value| default:-}',
|
||||
filter: '',
|
||||
expectValue: '-'
|
||||
},
|
||||
{
|
||||
type: 'join',
|
||||
data: {
|
||||
value: ['a', 'b', 'c']
|
||||
},
|
||||
path: '${value| join:,}',
|
||||
filter: '',
|
||||
expectValue: 'a,b,c'
|
||||
},
|
||||
{
|
||||
type: 'split',
|
||||
data: {
|
||||
value: 'a,b,c'
|
||||
},
|
||||
path: '${value| split}',
|
||||
filter: '',
|
||||
expectValue: ['a', 'b', 'c']
|
||||
},
|
||||
{
|
||||
type: 'first',
|
||||
data: {
|
||||
value: ['a', 'b', 'c']
|
||||
},
|
||||
path: '${value| first}',
|
||||
filter: '',
|
||||
expectValue: 'a'
|
||||
},
|
||||
{
|
||||
type: 'nth',
|
||||
data: {
|
||||
value: ['a', 'b', 'c']
|
||||
},
|
||||
path: '${value| nth:1}',
|
||||
filter: '',
|
||||
expectValue: 'b'
|
||||
},
|
||||
{
|
||||
type: 'last',
|
||||
data: {
|
||||
value: ['a', 'b', 'c']
|
||||
},
|
||||
path: '${value| last}',
|
||||
filter: '',
|
||||
expectValue: 'c'
|
||||
},
|
||||
{
|
||||
type: 'minus',
|
||||
data: {
|
||||
value: 5
|
||||
},
|
||||
path: '${value| minus:1}',
|
||||
filter: '',
|
||||
expectValue: 4
|
||||
},
|
||||
{
|
||||
type: 'plus',
|
||||
data: {
|
||||
value: 5
|
||||
},
|
||||
path: '${value| plus:1}',
|
||||
filter: '',
|
||||
expectValue: 6
|
||||
},
|
||||
{
|
||||
type: 'pick1',
|
||||
data: {
|
||||
value: {
|
||||
a: '1',
|
||||
b: '2'
|
||||
}
|
||||
},
|
||||
path: '${value| pick:a}',
|
||||
filter: '',
|
||||
expectValue: '1'
|
||||
},
|
||||
{
|
||||
type: 'pick2',
|
||||
data: {
|
||||
value: [
|
||||
{
|
||||
label: 'A',
|
||||
value: 'a'
|
||||
},
|
||||
{
|
||||
label: 'B',
|
||||
value: 'b'
|
||||
},
|
||||
{
|
||||
label: 'C',
|
||||
value: 'c'
|
||||
}
|
||||
]
|
||||
},
|
||||
path: '${value| pick:value}',
|
||||
filter: '',
|
||||
expectValue: ['a', 'b', 'c']
|
||||
},
|
||||
{
|
||||
type: 'pick_if_exist',
|
||||
data: {
|
||||
value: [
|
||||
{
|
||||
label: 'A',
|
||||
value: 'a'
|
||||
},
|
||||
{
|
||||
label: 'B',
|
||||
value: 'b'
|
||||
},
|
||||
{
|
||||
label: 'C',
|
||||
value: 'c'
|
||||
}
|
||||
]
|
||||
},
|
||||
path: '${value| pick_if_exist:value}',
|
||||
filter: '',
|
||||
expectValue: ['a', 'b', 'c']
|
||||
},
|
||||
{
|
||||
type: 'str2date',
|
||||
data: {
|
||||
value: '1559649981'
|
||||
},
|
||||
path: '${value| str2date:X:YYYY-MM-DD HH-mm-ss}',
|
||||
filter: '',
|
||||
expectValue: moment('1559649981', 'X').format('YYYY-MM-DD HH-mm-ss')
|
||||
},
|
||||
{
|
||||
type: 'asArray',
|
||||
data: {
|
||||
value: 'a'
|
||||
},
|
||||
path: '${value| asArray}',
|
||||
filter: '',
|
||||
expectValue: ['a']
|
||||
},
|
||||
{
|
||||
type: 'base64Encode',
|
||||
data: {
|
||||
value: 'I love amis'
|
||||
},
|
||||
path: '${value| base64Encode}',
|
||||
filter: '',
|
||||
expectValue: 'SSBsb3ZlIGFtaXM='
|
||||
},
|
||||
{
|
||||
type: 'base64Decode',
|
||||
data: {
|
||||
value: 'SSBsb3ZlIGFtaXM='
|
||||
},
|
||||
path: '${value| base64Decode}',
|
||||
filter: '',
|
||||
expectValue: 'I love amis'
|
||||
},
|
||||
{
|
||||
type: 'lowerCase',
|
||||
data: {
|
||||
value: 'AbC'
|
||||
},
|
||||
path: '${value| lowerCase}',
|
||||
filter: '',
|
||||
expectValue: 'abc'
|
||||
},
|
||||
{
|
||||
type: 'upperCase',
|
||||
data: {
|
||||
value: 'aBc'
|
||||
},
|
||||
path: '${value| upperCase}',
|
||||
filter: '',
|
||||
expectValue: 'ABC'
|
||||
}
|
||||
];
|
||||
|
||||
filters.forEach(f => {
|
||||
test(`compat:${f.type}`, () => {
|
||||
const result = resolveVariableAndFilter(f.path, f.data, f.filter);
|
||||
expect(result).toEqual(f.expectValue);
|
||||
});
|
||||
});
|
||||
|
||||
test(`compat:filter`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${rows | filter:engine:match:keywords}',
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
engine: 'a'
|
||||
},
|
||||
{
|
||||
engine: 'b'
|
||||
},
|
||||
{
|
||||
engine: 'c'
|
||||
}
|
||||
]
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toMatchObject([
|
||||
{
|
||||
engine: 'a'
|
||||
},
|
||||
{
|
||||
engine: 'b'
|
||||
},
|
||||
{
|
||||
engine: 'c'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${rows | filter:engine:match:keywords}',
|
||||
{
|
||||
keywords: 'a',
|
||||
rows: [
|
||||
{
|
||||
engine: 'a'
|
||||
},
|
||||
{
|
||||
engine: 'b'
|
||||
},
|
||||
{
|
||||
engine: 'c'
|
||||
}
|
||||
]
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toMatchObject([
|
||||
{
|
||||
engine: 'a'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test(`compat:&`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${& | json:0}',
|
||||
{
|
||||
a: 1,
|
||||
b: 2
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toBe('{"a":1,"b":2}');
|
||||
});
|
||||
|
||||
test(`compat:filter-default`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${a | default:undefined}',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toBe(1);
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${a | default:undefined}',
|
||||
{
|
||||
a: [1, 2, 3]
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toMatchObject([1, 2, 3]);
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${b | default:undefined}',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toBe(undefined);
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${b | default:-}',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toBe('-');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${b | default:undefined}',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
'| raw',
|
||||
() => ''
|
||||
)
|
||||
).toBe(undefined);
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'${b}',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
'| raw',
|
||||
() => ''
|
||||
)
|
||||
).toBe('');
|
||||
});
|
||||
|
||||
test(`compat:numberVariable`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'a $1 ',
|
||||
{
|
||||
'1': 233
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toEqual('a 233 ');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter(
|
||||
'a $1',
|
||||
{
|
||||
'1': 233
|
||||
},
|
||||
'| raw'
|
||||
)
|
||||
).toEqual('a 233');
|
||||
});
|
||||
|
||||
test(`compat:test`, () => {
|
||||
const result = resolveVariableAndFilter('', {}, '| raw');
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
test(`compat:test2`, () => {
|
||||
const data = {
|
||||
'123': 123,
|
||||
'123.123': 123,
|
||||
'中文': 123,
|
||||
'obj': {
|
||||
x: 123
|
||||
}
|
||||
};
|
||||
expect(resolveVariable('123', data)).toEqual(123);
|
||||
expect(resolveVariable('123.123', data)).toEqual(123);
|
||||
expect(resolveVariable('中文', data)).toEqual(123);
|
||||
expect(resolveVariable('obj.x', data)).toEqual(123);
|
||||
});
|
1
packages/amis-core/__tests__/factory.test.d.ts
vendored
Normal file
1
packages/amis-core/__tests__/factory.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
292
packages/amis-core/__tests__/factory.test.tsx
Normal file
292
packages/amis-core/__tests__/factory.test.tsx
Normal file
@ -0,0 +1,292 @@
|
||||
import {
|
||||
registerRenderer,
|
||||
unRegisterRenderer,
|
||||
RendererProps
|
||||
} from '../src/factory';
|
||||
import {render as amisRender} from '../src';
|
||||
import React from 'react';
|
||||
import {render, fireEvent, waitFor} from '@testing-library/react';
|
||||
import {makeEnv} from './helper';
|
||||
|
||||
test('factory unregistered Renderer', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender({
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
})
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Error: 找不到对应的渲染器')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('factory custom not found!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => Promise.resolve(() => <div>Not Found</div>)
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory custom not found 2!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => () => <div>Not Found</div>
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory custom not found 3!', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
loadRenderer: () => <div>Not Found</div>
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory load Renderer on need', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'my-renderer2',
|
||||
a: 23
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
session: 'loadRenderer',
|
||||
loadRenderer: schema => {
|
||||
interface MyProps extends RendererProps {
|
||||
a?: number;
|
||||
}
|
||||
|
||||
class MyComponent extends React.Component<MyProps> {
|
||||
render() {
|
||||
return <div>This is Custom Renderer2, a is {this.props.a}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
registerRenderer({
|
||||
component: MyComponent,
|
||||
test: /\bmy-renderer2$/
|
||||
});
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(getByText('This is Custom Renderer2, a is 23')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot(); // not found
|
||||
});
|
||||
|
||||
test('factory:registerRenderer', () => {
|
||||
interface MyProps extends RendererProps {
|
||||
a?: number;
|
||||
}
|
||||
|
||||
class MyComponent extends React.Component<MyProps> {
|
||||
render() {
|
||||
return <div>This is Custom Renderer, a is {this.props.a}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = registerRenderer({
|
||||
component: MyComponent,
|
||||
test: /\bmy-renderer$/
|
||||
});
|
||||
|
||||
const {container} = render(
|
||||
amisRender({
|
||||
type: 'my-renderer',
|
||||
a: 23
|
||||
})
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
unRegisterRenderer(renderer);
|
||||
});
|
||||
|
||||
// test('factory:definitions', async () => {
|
||||
// const {container, getByText} = render(
|
||||
// amisRender(
|
||||
// {
|
||||
// definitions: {
|
||||
// aa: {
|
||||
// type: 'text',
|
||||
// name: 'jack',
|
||||
// value: 'ref value',
|
||||
// remark: '通过<code>\\$ref</code>引入的组件'
|
||||
// },
|
||||
// bb: {
|
||||
// type: 'combo',
|
||||
// multiple: true,
|
||||
// multiLine: true,
|
||||
// remark: '<code>combo</code>中的子项引入自身,实现嵌套的效果',
|
||||
// controls: [
|
||||
// {
|
||||
// label: 'combo 1',
|
||||
// type: 'text',
|
||||
// name: 'key'
|
||||
// },
|
||||
// {
|
||||
// label: 'combo 2',
|
||||
// name: 'value',
|
||||
// $ref: 'aa'
|
||||
// },
|
||||
// {
|
||||
// name: 'children',
|
||||
// label: 'children',
|
||||
// $ref: 'bb'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// type: 'page',
|
||||
// title: '引用',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'form',
|
||||
// api: 'api/xxx',
|
||||
// actions: [],
|
||||
// controls: [
|
||||
// {
|
||||
// label: 'text2',
|
||||
// $ref: 'aa',
|
||||
// name: 'ref1'
|
||||
// },
|
||||
// {
|
||||
// label: 'combo',
|
||||
// $ref: 'bb',
|
||||
// name: 'ref2'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {},
|
||||
// makeEnv({})
|
||||
// )
|
||||
// );
|
||||
|
||||
// await waitFor(() => {
|
||||
// expect(getByText('新增')).toBeInTheDocument();
|
||||
// });
|
||||
// fireEvent.click(getByText('新增'));
|
||||
// await waitFor(() => {
|
||||
// expect(getByText('combo 1')).toBeInTheDocument();
|
||||
// });
|
||||
// expect(container).toMatchSnapshot();
|
||||
// });
|
||||
|
||||
// test('factory:definitions override', async () => {
|
||||
// const {container, getByText} = render(
|
||||
// amisRender(
|
||||
// {
|
||||
// definitions: {
|
||||
// aa: {
|
||||
// type: 'text',
|
||||
// name: 'jack',
|
||||
// remark: '通过<code>\\$ref</code>引入的组件'
|
||||
// },
|
||||
// bb: {
|
||||
// type: 'combo',
|
||||
// multiple: true,
|
||||
// multiLine: true,
|
||||
// remark: '<code>combo</code>中的子项引入自身,实现嵌套的效果',
|
||||
// controls: [
|
||||
// {
|
||||
// label: 'combo 1',
|
||||
// type: 'text',
|
||||
// name: 'key'
|
||||
// },
|
||||
// {
|
||||
// label: 'combo 2',
|
||||
// name: 'value',
|
||||
// $ref: 'aa'
|
||||
// },
|
||||
// {
|
||||
// name: 'children',
|
||||
// label: 'children',
|
||||
// $ref: 'bb'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// type: 'page',
|
||||
// title: '引用',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'form',
|
||||
// api: 'api/xxx',
|
||||
// actions: [],
|
||||
// controls: [
|
||||
// {
|
||||
// label: 'text2',
|
||||
// $ref: 'aa',
|
||||
// name: 'ref1'
|
||||
// },
|
||||
// {
|
||||
// label: 'combo',
|
||||
// $ref: 'bb',
|
||||
// name: 'ref2',
|
||||
// type: 'checkboxes',
|
||||
// value: 1,
|
||||
// options: [
|
||||
// {
|
||||
// label: 'Option A',
|
||||
// value: 1
|
||||
// },
|
||||
// {
|
||||
// label: 'Option B',
|
||||
// value: 2
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {},
|
||||
// makeEnv({})
|
||||
// )
|
||||
// );
|
||||
|
||||
// await waitFor(() => {
|
||||
// expect(getByText('combo')).toBeInTheDocument();
|
||||
// });
|
||||
// expect(container).toMatchSnapshot();
|
||||
// });
|
1
packages/amis-core/__tests__/filter.test.d.ts
vendored
Normal file
1
packages/amis-core/__tests__/filter.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
355
packages/amis-core/__tests__/filter.test.ts
Normal file
355
packages/amis-core/__tests__/filter.test.ts
Normal file
@ -0,0 +1,355 @@
|
||||
import {resolveVariableAndFilter} from '../src/utils/tpl-builtin';
|
||||
import {evaluate} from 'amis-formula';
|
||||
|
||||
test(`filter:map`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a | map: toInt}', {
|
||||
a: ['123', '3434']
|
||||
})
|
||||
).toMatchObject([123, 3434]);
|
||||
});
|
||||
test(`filter:html`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a}', {
|
||||
a: '<html>'
|
||||
})
|
||||
).toEqual('<html>');
|
||||
});
|
||||
|
||||
test(`filter:complex`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${`${a}`}', {
|
||||
a: '<html>'
|
||||
})
|
||||
).toEqual('<html>');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter('${a ? a : a}', {
|
||||
a: '<html>'
|
||||
})
|
||||
).toEqual('<html>');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter('${b.a}', {
|
||||
a: '<html>',
|
||||
b: {
|
||||
a: '<br />'
|
||||
}
|
||||
})
|
||||
).toEqual('<br />');
|
||||
});
|
||||
|
||||
test(`filter:json`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a | json : 0}', {
|
||||
a: {a: 1}
|
||||
})
|
||||
).toEqual('{"a":1}');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter('${a | json : 2}', {
|
||||
a: {a: 1}
|
||||
})
|
||||
).toEqual('{\n "a": 1\n}');
|
||||
});
|
||||
|
||||
test(`filter:toJson`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toJson}', {
|
||||
a: '{"a":1}'
|
||||
})
|
||||
).toMatchObject({a: 1});
|
||||
});
|
||||
|
||||
test(`filter:toInt`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toInt}', {
|
||||
a: '233'
|
||||
})
|
||||
).toBe(233);
|
||||
});
|
||||
|
||||
test(`filter:toFloat`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toFloat}', {
|
||||
a: '233.233'
|
||||
})
|
||||
).toBe(233.233);
|
||||
});
|
||||
|
||||
test(`filter:toDate`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toDate:x|date: YYYY-MM-DD}', {
|
||||
a: 1638028267226
|
||||
})
|
||||
).toBe('2021-11-27');
|
||||
});
|
||||
|
||||
test(`filter:fromNow`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toDate:x|fromNow}', {
|
||||
a: Date.now() - 2 * 60 * 1000
|
||||
})
|
||||
).toBe('2 minutes ago');
|
||||
});
|
||||
|
||||
test(`filter:dateModify`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|toDate:x|dateModify:subtract:2:m|fromNow}', {
|
||||
a: Date.now()
|
||||
})
|
||||
).toBe('2 minutes ago');
|
||||
});
|
||||
|
||||
test(`filter:number`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|number}', {
|
||||
a: 1234
|
||||
})
|
||||
).toBe('1,234');
|
||||
});
|
||||
|
||||
test(`filter:trim`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|trim}', {
|
||||
a: ' ab '
|
||||
})
|
||||
).toBe('ab');
|
||||
});
|
||||
|
||||
test(`filter:duration`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|duration}', {
|
||||
a: 234343
|
||||
})
|
||||
).toBe('2天17时5分43秒');
|
||||
});
|
||||
|
||||
test(`filter:bytes`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|bytes}', {
|
||||
a: 234343
|
||||
})
|
||||
).toBe('234 KB');
|
||||
});
|
||||
test(`filter:round`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|round}', {
|
||||
a: 23.234
|
||||
})
|
||||
).toBe('23.23');
|
||||
});
|
||||
|
||||
test(`filter:truncate`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|truncate:5}', {
|
||||
a: 'abcdefghijklmnopqrst'
|
||||
})
|
||||
).toBe('abcde...');
|
||||
});
|
||||
|
||||
test(`filter:url_encode`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|url_encode}', {
|
||||
a: '='
|
||||
})
|
||||
).toBe('%3D');
|
||||
});
|
||||
|
||||
test(`filter:url_encode`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|url_decode}', {
|
||||
a: '%3D'
|
||||
})
|
||||
).toBe('=');
|
||||
});
|
||||
|
||||
test(`filter:url_encode`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|default:-}', {
|
||||
a: ''
|
||||
})
|
||||
).toBe('-');
|
||||
});
|
||||
|
||||
test(`filter:join`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|join:-}', {
|
||||
a: [1, 2, 3]
|
||||
})
|
||||
).toBe('1-2-3');
|
||||
});
|
||||
|
||||
test(`filter:split`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|split:-}', {
|
||||
a: '1-2-3'
|
||||
})
|
||||
).toMatchObject(['1', '2', '3']);
|
||||
});
|
||||
|
||||
test(`filter:sortBy`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|sortBy:&|join}', {
|
||||
a: ['b', 'c', 'a']
|
||||
})
|
||||
).toBe('a,b,c');
|
||||
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|sortBy:&:numerical|join}', {
|
||||
a: ['023', '20', '44']
|
||||
})
|
||||
).toBe('20,023,44');
|
||||
});
|
||||
|
||||
test(`filter:objectToArray`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|objectToArray}', {
|
||||
a: {
|
||||
a: 1,
|
||||
b: 2,
|
||||
done: 'Done'
|
||||
}
|
||||
})
|
||||
).toMatchObject([
|
||||
{
|
||||
value: 'a',
|
||||
label: 1
|
||||
},
|
||||
{
|
||||
value: 'b',
|
||||
label: 2
|
||||
},
|
||||
{
|
||||
value: 'done',
|
||||
label: 'Done'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test(`filter:substring`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|substring:0:2}', {
|
||||
a: 'abcdefg'
|
||||
})
|
||||
).toBe('ab');
|
||||
expect(
|
||||
resolveVariableAndFilter('${a|substring:1:3}', {
|
||||
a: 'abcdefg'
|
||||
})
|
||||
).toBe('bc');
|
||||
});
|
||||
|
||||
test(`filter:variableInVariable`, () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${a}', {
|
||||
a: 'abc$0defg'
|
||||
})
|
||||
).toBe('abc$0defg');
|
||||
});
|
||||
|
||||
test('filter:isMatch', () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${status | isMatch:2:1|isMatch:5:1:4}', {
|
||||
status: 2
|
||||
})
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('filter:filter:isMatch', () => {
|
||||
expect(
|
||||
resolveVariableAndFilter('${items|filter:text:match:"ab"}', {
|
||||
items: [
|
||||
{
|
||||
text: 'abc'
|
||||
},
|
||||
{
|
||||
text: 'bcd'
|
||||
},
|
||||
{
|
||||
text: 'cde'
|
||||
}
|
||||
]
|
||||
})
|
||||
).toMatchObject([
|
||||
{
|
||||
text: 'abc'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('evalute:conditional', () => {
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isTrue: true : false}',
|
||||
{
|
||||
a: 4
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isTrue: b : false}',
|
||||
{
|
||||
a: 4,
|
||||
b: 5
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe(5);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isTrue: b : false}',
|
||||
{
|
||||
a: null,
|
||||
b: 5
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isEquals: 1 : "1" |isEquals: 2 : "2" | isEquals: 3 : "3" }',
|
||||
{
|
||||
a: 3
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe('3');
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isEquals: 1 : "1" |isEquals: 1 : "2" | isEquals: 1 : "3" }',
|
||||
{
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe('1');
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
'${a | isEquals: 1 : "1" : "12" |isEquals: 2 : "2" | isEquals: 3 : "3" }',
|
||||
{
|
||||
a: 2
|
||||
},
|
||||
{
|
||||
defaultFilter: 'raw'
|
||||
}
|
||||
)
|
||||
).toBe('12');
|
||||
});
|
13
packages/amis-core/__tests__/helper.d.ts
vendored
Normal file
13
packages/amis-core/__tests__/helper.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import { RenderOptions } from '../src/factory';
|
||||
export declare function wait(duration: number, fnOrUseWaitFor?: Function | boolean): Promise<void>;
|
||||
export declare function makeEnv(env?: Partial<RenderOptions>): RenderOptions;
|
||||
export declare const createMockMediaMatcher: (matchesOrMapOfMatches: any) => (qs: any) => {
|
||||
matches: any;
|
||||
media: string;
|
||||
addListener: () => void;
|
||||
addEventListener: () => void;
|
||||
removeEventListener: () => void;
|
||||
onchange: () => void;
|
||||
removeListener: () => void;
|
||||
dispatchEvent: () => boolean;
|
||||
};
|
1
packages/amis-core/__tests__/jest.setup.d.ts
vendored
Normal file
1
packages/amis-core/__tests__/jest.setup.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
24
packages/amis-core/__tests__/jest.setup.js
Normal file
24
packages/amis-core/__tests__/jest.setup.js
Normal file
@ -0,0 +1,24 @@
|
||||
const originalWarn = console.warn.bind(console.warn);
|
||||
require('@testing-library/jest-dom');
|
||||
require('moment-timezone');
|
||||
const moment = require('moment');
|
||||
moment.tz.setDefault('Asia/Shanghai');
|
||||
const cleanup = require('@testing-library/react').cleanup;
|
||||
|
||||
global.beforeAll(() => {
|
||||
console.warn = msg => {
|
||||
// warning 先关了,实在太吵。
|
||||
// const str = msg.toString();
|
||||
// if (
|
||||
// str.includes('componentWillMount') ||
|
||||
// str.includes('componentWillReceiveProps')
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
// originalWarn(msg);
|
||||
};
|
||||
});
|
||||
global.afterAll(() => {
|
||||
console.warn = originalWarn;
|
||||
cleanup();
|
||||
});
|
1
packages/amis-core/__tests__/tokenize.test.d.ts
vendored
Normal file
1
packages/amis-core/__tests__/tokenize.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
26
packages/amis-core/__tests__/tokenize.test.ts
Normal file
26
packages/amis-core/__tests__/tokenize.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {tokenize} from '../src/utils/tpl-builtin';
|
||||
|
||||
test(`tokenize:null`, () => {
|
||||
expect(
|
||||
tokenize('abc${a}', {
|
||||
a: ''
|
||||
})
|
||||
).toBe('abc');
|
||||
expect(
|
||||
tokenize('abc${a}', {
|
||||
a: null
|
||||
})
|
||||
).toBe('abc');
|
||||
|
||||
expect(
|
||||
tokenize('abc${a}', {
|
||||
a: undefined
|
||||
})
|
||||
).toBe('abc');
|
||||
|
||||
expect(
|
||||
tokenize('abc${a}', {
|
||||
a: 0
|
||||
})
|
||||
).toBe('abc0');
|
||||
});
|
122
packages/amis-core/package.json
Normal file
122
packages/amis-core/package.json
Normal file
@ -0,0 +1,122 @@
|
||||
{
|
||||
"name": "amis-core",
|
||||
"version": "1.0.0-beta.8",
|
||||
"description": "amis-core",
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"author": "fex",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@rollup/plugin-typescript": "^8.3.2",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/hoist-non-react-statics": "^3.3.1",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"jest": "^27.2.1",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.73.0",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"rollup-plugin-license": "^2.7.0",
|
||||
"typescript": "^4.6.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && NODE_ENV=production rollup -c ",
|
||||
"dev": "rollup -c -w",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage",
|
||||
"clean-dist": "rimraf lib/* esm/*"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"esm"
|
||||
],
|
||||
"dependencies": {
|
||||
"amis-formula": "^2.0.0-beta.0",
|
||||
"classnames": "2.3.1",
|
||||
"file-saver": "^2.0.2",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"lodash": "^4.17.15",
|
||||
"match-sorter": "^6.3.1",
|
||||
"mobx": "^4.5.0",
|
||||
"mobx-react": "^6.3.1",
|
||||
"mobx-state-tree": "^3.17.3",
|
||||
"moment": "^2.19.3",
|
||||
"papaparse": "^5.3.0",
|
||||
"qs": "6.9.7",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-visibility-sensor": "5.1.1",
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js"
|
||||
],
|
||||
"transform": {
|
||||
"\\.(ts|tsx)$": "ts-jest"
|
||||
},
|
||||
"setupFiles": [
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"testRegex": "/.*\\.test\\.(ts|tsx|js)$",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|less|sass|scss)$": "<rootDir>/../__mocks__/styleMock.js",
|
||||
"\\.(svg)$": "<rootDir>/../__mocks__/svgMock.js"
|
||||
},
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/__tests__/jest.setup.js"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"/.rollup.cache/"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false,
|
||||
"tsconfig": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom",
|
||||
"ES2015"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"rootDir": ".",
|
||||
"importHelpers": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceRoot": ".",
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./types"
|
||||
],
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
packages/amis-core/rollup.config.js
Normal file
135
packages/amis-core/rollup.config.js
Normal file
@ -0,0 +1,135 @@
|
||||
// rollup.config.js
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import json from '@rollup/plugin-json';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import license from 'rollup-plugin-license';
|
||||
import autoExternal from 'rollup-plugin-auto-external';
|
||||
import {
|
||||
name,
|
||||
version,
|
||||
author,
|
||||
main,
|
||||
module,
|
||||
dependencies
|
||||
} from './package.json';
|
||||
import path from 'path';
|
||||
|
||||
const settings = {
|
||||
globals: {}
|
||||
};
|
||||
|
||||
const external = id =>
|
||||
new RegExp(
|
||||
`^(?:${Object.keys(dependencies)
|
||||
.concat([
|
||||
'entities',
|
||||
'linkify-it',
|
||||
'markdown-it',
|
||||
'markdown-it-html5-media',
|
||||
'mdurl',
|
||||
'uc.micro'
|
||||
])
|
||||
.map(value =>
|
||||
value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d')
|
||||
)
|
||||
.join('|')})`
|
||||
).test(id);
|
||||
const input = './src/index.tsx';
|
||||
|
||||
export default [
|
||||
{
|
||||
input,
|
||||
|
||||
output: [
|
||||
{
|
||||
...settings,
|
||||
dir: path.dirname(main),
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
preserveModulesRoot: './src',
|
||||
preserveModules: true // Keep directory structure and files
|
||||
}
|
||||
],
|
||||
external,
|
||||
plugins: getPlugins('cjs')
|
||||
},
|
||||
|
||||
{
|
||||
input,
|
||||
|
||||
output: [
|
||||
{
|
||||
...settings,
|
||||
dir: path.dirname(module),
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
preserveModulesRoot: './src',
|
||||
preserveModules: true // Keep directory structure and files
|
||||
}
|
||||
],
|
||||
external,
|
||||
plugins: getPlugins('esm')
|
||||
}
|
||||
];
|
||||
|
||||
function transpileDynamicImportForCJS(options) {
|
||||
return {
|
||||
name: 'transpile-dynamic-import-for-cjs',
|
||||
renderDynamicImport({format, targetModuleId}) {
|
||||
if (format !== 'cjs') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
left: 'Promise.resolve().then(function() {return new Promise(function(fullfill) {require.ensure([',
|
||||
right:
|
||||
'], function(r) {fullfill(_interopDefaultLegacy(r("' +
|
||||
targetModuleId +
|
||||
'")))})})})'
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getPlugins(format = 'esm') {
|
||||
const typeScriptOptions = {
|
||||
typescript: require('typescript'),
|
||||
sourceMap: false,
|
||||
outputToFilesystem: true,
|
||||
|
||||
...(format === 'esm'
|
||||
? {
|
||||
compilerOptions: {
|
||||
rootDir: './src',
|
||||
outDir: path.dirname(module)
|
||||
}
|
||||
}
|
||||
: {
|
||||
compilerOptions: {
|
||||
rootDir: './src',
|
||||
outDir: path.dirname(main)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return [
|
||||
transpileDynamicImportForCJS(),
|
||||
autoExternal(),
|
||||
json(),
|
||||
resolve({
|
||||
jsnext: true,
|
||||
main: true
|
||||
}),
|
||||
typescript(typeScriptOptions),
|
||||
commonjs({
|
||||
sourceMap: false
|
||||
}),
|
||||
license({
|
||||
banner: `
|
||||
${name} v${version}
|
||||
Copyright 2018<%= moment().format('YYYY') > 2018 ? '-' + moment().format('YYYY') : null %> ${author}
|
||||
`
|
||||
})
|
||||
];
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import React from 'react';
|
||||
import Alert from './components/Alert2';
|
||||
import ImageGallery from './components/ImageGallery';
|
||||
import {RendererEnv} from './env';
|
||||
import {RendererProps} from './factory';
|
||||
import {LocaleContext, TranslateFn} from './locale';
|
||||
@ -11,8 +9,7 @@ import Scoped from './Scoped';
|
||||
import {IRendererStore} from './store';
|
||||
import {ThemeContext} from './theme';
|
||||
import {Schema, SchemaNode} from './types';
|
||||
import getExprProperties from './utils/filter-schema';
|
||||
import {autobind, createObject, isEmpty} from './utils/helper';
|
||||
import {autobind, isEmpty} from './utils/helper';
|
||||
import {RootStoreContext} from './WithRootStore';
|
||||
|
||||
export interface RootRenderProps {
|
||||
@ -65,29 +62,28 @@ export class Root extends React.Component<RootProps> {
|
||||
<RootStoreContext.Provider value={rootStore}>
|
||||
<ThemeContext.Provider value={themeName}>
|
||||
<LocaleContext.Provider value={this.props.locale!}>
|
||||
<ImageGallery modalContainer={env.getModalContainer}>
|
||||
<RootRenderer
|
||||
pathPrefix={pathPrefix || ''}
|
||||
schema={
|
||||
isPlainObject(schema)
|
||||
? {
|
||||
type: 'page',
|
||||
...(schema as any)
|
||||
}
|
||||
: schema
|
||||
}
|
||||
{...rest}
|
||||
rootStore={rootStore}
|
||||
resolveDefinitions={this.resolveDefinitions}
|
||||
location={location}
|
||||
data={data}
|
||||
env={env}
|
||||
classnames={theme.classnames}
|
||||
classPrefix={theme.classPrefix}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
/>
|
||||
</ImageGallery>
|
||||
<RootRenderer
|
||||
pathPrefix={pathPrefix || ''}
|
||||
schema={
|
||||
isPlainObject(schema)
|
||||
? {
|
||||
type: 'page',
|
||||
...(schema as any)
|
||||
}
|
||||
: schema
|
||||
}
|
||||
{...rest}
|
||||
render={renderChild}
|
||||
rootStore={rootStore}
|
||||
resolveDefinitions={this.resolveDefinitions}
|
||||
location={location}
|
||||
data={data}
|
||||
env={env}
|
||||
classnames={theme.classnames}
|
||||
classPrefix={theme.classPrefix}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
/>
|
||||
</LocaleContext.Provider>
|
||||
</ThemeContext.Provider>
|
||||
</RootStoreContext.Provider>
|
||||
@ -130,6 +126,8 @@ export function renderChild(
|
||||
|
||||
if (typeofnode === 'undefined' || node === null) {
|
||||
return null;
|
||||
} else if (React.isValidElement(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
let schema: Schema =
|
@ -1,12 +1,10 @@
|
||||
import {observer} from 'mobx-react';
|
||||
import {getEnv} from 'mobx-state-tree';
|
||||
import React from 'react';
|
||||
import Alert from './components/Alert2';
|
||||
import Spinner from './components/Spinner';
|
||||
import {renderChild, RootProps} from './Root';
|
||||
import type {RootProps} from './Root';
|
||||
import {IScopedContext, ScopedContext} from './Scoped';
|
||||
import {IRootStore, RootStore} from './store/root';
|
||||
import {Action} from './types';
|
||||
import {ActionObject} from './types';
|
||||
import {bulkBindFunctions, guid, isVisible} from './utils/helper';
|
||||
import {filter} from './utils/tpl';
|
||||
import qs from 'qs';
|
||||
@ -14,10 +12,10 @@ import pick from 'lodash/pick';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
import {saveAs} from 'file-saver';
|
||||
import {normalizeApi} from './utils/api';
|
||||
import {AjaxActionSchema} from './renderers/Action';
|
||||
|
||||
export interface RootRendererProps extends RootProps {
|
||||
location?: any;
|
||||
render: (region: string, schema: any, props: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -94,12 +92,12 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
|
||||
handleAction(
|
||||
e: React.UIEvent<any> | void,
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
ctx: object,
|
||||
throwErrors: boolean = false,
|
||||
delegate?: IScopedContext
|
||||
) {
|
||||
const {env, messages, onAction} = this.props;
|
||||
const {env, messages, onAction, render} = this.props;
|
||||
const store = this.store;
|
||||
|
||||
if (
|
||||
@ -164,7 +162,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
env.notify(
|
||||
item.level || 'info',
|
||||
item.body
|
||||
? renderChild('body', item.body, {
|
||||
? render('body', item.body, {
|
||||
...this.props,
|
||||
data: ctx
|
||||
})
|
||||
@ -173,7 +171,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
...action.toast,
|
||||
...item,
|
||||
title: item.title
|
||||
? renderChild('title', item.title, {
|
||||
? render('title', item.title, {
|
||||
...this.props,
|
||||
data: ctx
|
||||
})
|
||||
@ -224,7 +222,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
} else if (action.actionType === 'saveAs') {
|
||||
// 使用 saveAs 实现下载
|
||||
// 不支持 env,除非以后将 saveAs 代码拷过来改
|
||||
const api = normalizeApi((action as AjaxActionSchema).api);
|
||||
const api = normalizeApi((action as any).api);
|
||||
if (typeof api.url === 'string') {
|
||||
let fileName = action.fileName || 'data.txt';
|
||||
if (api.url.indexOf('.') !== -1) {
|
||||
@ -235,7 +233,11 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
}
|
||||
}
|
||||
|
||||
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||
handleDialogConfirm(
|
||||
values: object[],
|
||||
action: ActionObject,
|
||||
...args: Array<any>
|
||||
) {
|
||||
const store = this.store;
|
||||
|
||||
if (action.mergeData && values.length === 1 && values[0]) {
|
||||
@ -259,7 +261,11 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
store.closeDialog(confirmed);
|
||||
}
|
||||
|
||||
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||
handleDrawerConfirm(
|
||||
values: object[],
|
||||
action: ActionObject,
|
||||
...args: Array<any>
|
||||
) {
|
||||
const store = this.store;
|
||||
|
||||
if (action.mergeData && values.length === 1 && values[0]) {
|
||||
@ -302,43 +308,71 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {pathPrefix, schema, ...rest} = this.props;
|
||||
const {pathPrefix, schema, render, ...rest} = this.props;
|
||||
const store = this.store;
|
||||
|
||||
if (store.runtimeError) {
|
||||
return (
|
||||
<Alert level="danger">
|
||||
<h3>{this.store.runtimeError?.toString()}</h3>
|
||||
<pre>
|
||||
<code>{this.store.runtimeErrorStack.componentStack}</code>
|
||||
</pre>
|
||||
</Alert>
|
||||
return render(
|
||||
'error',
|
||||
{
|
||||
type: 'alert',
|
||||
level: 'danger'
|
||||
},
|
||||
{
|
||||
...rest,
|
||||
body: (
|
||||
<>
|
||||
<h3>{this.store.runtimeError?.toString()}</h3>
|
||||
<pre>
|
||||
<code>{this.store.runtimeErrorStack.componentStack}</code>
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
renderChild(pathPrefix!, schema, {
|
||||
render(pathPrefix!, schema, {
|
||||
...rest,
|
||||
data: this.store.downStream,
|
||||
onAction: this.handleAction
|
||||
}) as JSX.Element
|
||||
}
|
||||
|
||||
<Spinner size="lg" overlay key="info" show={store.loading} />
|
||||
{render(
|
||||
'spinner',
|
||||
{
|
||||
type: 'spinner'
|
||||
},
|
||||
{
|
||||
...rest,
|
||||
show: store.loading
|
||||
}
|
||||
)}
|
||||
|
||||
{store.error ? (
|
||||
<Alert level="danger" showCloseButton onClose={store.clearMessage}>
|
||||
{store.msg}
|
||||
</Alert>
|
||||
) : null}
|
||||
{store.error
|
||||
? render(
|
||||
'error',
|
||||
{
|
||||
type: 'alert'
|
||||
},
|
||||
{
|
||||
...rest,
|
||||
body: store.msg,
|
||||
showCloseButton: true,
|
||||
onClose: store.clearMessage
|
||||
}
|
||||
)
|
||||
: null}
|
||||
|
||||
{renderChild(
|
||||
{render(
|
||||
'dialog',
|
||||
{
|
||||
...((store.action as Action) &&
|
||||
((store.action as Action).dialog as object)),
|
||||
...((store.action as ActionObject) &&
|
||||
((store.action as ActionObject).dialog as object)),
|
||||
type: 'dialog'
|
||||
},
|
||||
{
|
||||
@ -352,11 +386,11 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
}
|
||||
)}
|
||||
|
||||
{renderChild(
|
||||
{render(
|
||||
'drawer',
|
||||
{
|
||||
...((store.action as Action) &&
|
||||
((store.action as Action).drawer as object)),
|
||||
...((store.action as ActionObject) &&
|
||||
((store.action as ActionObject).drawer as object)),
|
||||
type: 'drawer'
|
||||
},
|
||||
{
|
@ -11,8 +11,7 @@ import {
|
||||
RendererProps,
|
||||
resolveRenderer
|
||||
} from './factory';
|
||||
import {asFormItem} from './renderers/Form/Item';
|
||||
import {renderChild, renderChildren} from './Root';
|
||||
import {asFormItem} from './renderers/Item';
|
||||
import {ScopedContext} from './Scoped';
|
||||
import {Schema, SchemaNode} from './types';
|
||||
import {DebugWrapper} from './utils/debug';
|
||||
@ -217,7 +216,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
[propName: string]: any;
|
||||
} = {}
|
||||
) {
|
||||
let {schema: _, $path: __, env, ...rest} = this.props;
|
||||
let {schema: _, $path: __, env, render, ...rest} = this.props;
|
||||
let {path: $path} = this.resolveRenderer(this.props);
|
||||
|
||||
const omitList = defaultOmitList.concat();
|
||||
@ -227,7 +226,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
omitList.push.apply(omitList, Component.propsList as Array<string>);
|
||||
}
|
||||
|
||||
return renderChild(`${$path}${region ? `/${region}` : ''}`, node || '', {
|
||||
return render!(`${$path}${region ? `/${region}` : ''}`, node || '', {
|
||||
...omit(rest, omitList),
|
||||
...subProps,
|
||||
data: subProps.data || rest.data,
|
||||
@ -241,7 +240,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
}
|
||||
|
||||
render(): JSX.Element | null {
|
||||
let {$path: _, schema: __, rootStore, ...rest} = this.props;
|
||||
let {$path: _, schema: __, rootStore, render, ...rest} = this.props;
|
||||
|
||||
if (__ == null) {
|
||||
return null;
|
||||
@ -251,7 +250,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
const theme = this.props.env.theme;
|
||||
|
||||
if (Array.isArray(schema)) {
|
||||
return renderChildren($path, schema as any, rest) as JSX.Element;
|
||||
return render!($path, schema as any, rest) as JSX.Element;
|
||||
}
|
||||
|
||||
const detectData =
|
@ -17,12 +17,12 @@ import {
|
||||
findTree,
|
||||
TreeItem
|
||||
} from './utils/helper';
|
||||
import {RendererData, Action} from './types';
|
||||
import {RendererData, ActionObject} from './types';
|
||||
|
||||
export interface ScopedComponentType extends React.Component<RendererProps> {
|
||||
focus?: () => void;
|
||||
doAction?: (
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
data: RendererData,
|
||||
throwErrors?: boolean
|
||||
) => void;
|
@ -39,7 +39,7 @@ export function withRootStore<
|
||||
{...(this.props as JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
React.ComponentProps<T>
|
||||
>)}
|
||||
> as any)}
|
||||
{...injectedProps}
|
||||
/>
|
||||
);
|
@ -1,4 +1,4 @@
|
||||
import {Action} from '../types';
|
||||
import {ActionObject} from '../types';
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {
|
||||
RendererAction,
|
||||
@ -14,7 +14,7 @@ export interface ICustomAction extends ListenerAction {
|
||||
| string
|
||||
| ((
|
||||
renderer: any,
|
||||
doAction: (action: Action, data: Record<string, any>) => void,
|
||||
doAction: (action: ActionObject, data: Record<string, any>) => void,
|
||||
event: RendererEvent<any>,
|
||||
action: ListenerAction
|
||||
) => void); // 自定义JS,actionType: custom
|
||||
@ -25,7 +25,7 @@ export interface ICustomAction extends ListenerAction {
|
||||
*
|
||||
* @export
|
||||
* @class CustomAction
|
||||
* @implements {Action}
|
||||
* @implements {ActionObject}
|
||||
*/
|
||||
export class CustomAction implements RendererAction {
|
||||
async run(
|
@ -1,8 +1,6 @@
|
||||
import {FormControlProps} from '../renderers/Item';
|
||||
import {createObject} from '../utils/helper';
|
||||
|
||||
import type {ListenerAction} from './Action';
|
||||
import type {OptionsControlProps} from '../renderers/Form/Options';
|
||||
import type {FormControlProps} from '../renderers/Form/Item';
|
||||
import type {RendererEvent} from '../utils/renderer-event';
|
||||
|
||||
/**
|
@ -20,4 +20,5 @@ import './ToastAction';
|
||||
import './PageAction';
|
||||
import './Decorators';
|
||||
|
||||
export * from './Decorators';
|
||||
export * from './Action';
|
@ -6,7 +6,6 @@
|
||||
|
||||
import React from 'react';
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
import Spinner from './Spinner';
|
||||
|
||||
export interface LazyComponentProps {
|
||||
component?: React.ReactType;
|
||||
@ -28,7 +27,7 @@ export default class LazyComponent extends React.Component<
|
||||
LazyComponentState
|
||||
> {
|
||||
static defaultProps = {
|
||||
placeholder: <Spinner />,
|
||||
placeholder: <span>Loading...</span>,
|
||||
unMountOnHidden: false,
|
||||
partialVisibility: true
|
||||
};
|
@ -4,18 +4,36 @@
|
||||
import React from 'react';
|
||||
import {RendererConfig} from './factory';
|
||||
import {ThemeInstance} from './theme';
|
||||
import {Action, Api, Payload, Schema} from './types';
|
||||
import {
|
||||
ActionObject,
|
||||
Api,
|
||||
EventTrack,
|
||||
Payload,
|
||||
PlainObject,
|
||||
Schema,
|
||||
ToastConf,
|
||||
ToastLevel
|
||||
} from './types';
|
||||
import hoistNonReactStatic from 'hoist-non-react-statics';
|
||||
import {IScopedContext} from './Scoped';
|
||||
import {RendererEvent} from './utils/renderer-event';
|
||||
|
||||
import type {ToastLevel, ToastConf} from './components/Toast';
|
||||
export interface wsObject {
|
||||
url: string;
|
||||
responseKey?: string;
|
||||
body?: any;
|
||||
}
|
||||
|
||||
export interface RendererEnv {
|
||||
fetcher: (api: Api, data?: any, options?: object) => Promise<Payload>;
|
||||
isCancel: (val: any) => boolean;
|
||||
wsFetcher: (
|
||||
ws: wsObject,
|
||||
onMessage: (data: any) => void,
|
||||
onError: (error: any) => void
|
||||
) => void;
|
||||
notify: (type: ToastLevel, msg: any, conf?: ToastConf) => void;
|
||||
jumpTo: (to: string, action?: Action, ctx?: object) => void;
|
||||
jumpTo: (to: string, action?: ActionObject, ctx?: object) => void;
|
||||
alert: (msg: string) => void;
|
||||
confirm: (msg: string, title?: string) => Promise<boolean>;
|
||||
updateLocation: (location: any, replace?: boolean) => void;
|
||||
@ -33,6 +51,8 @@ export interface RendererEnv {
|
||||
* 监控路由变化,如果 jssdk 需要做单页跳转需要实现这个。
|
||||
*/
|
||||
watchRouteChange?: (fn: () => void) => () => void;
|
||||
// 用于跟踪用户在界面中的各种操作
|
||||
tracker: (eventTrack: EventTrack, props?: PlainObject) => void;
|
||||
rendererResolver?: (
|
||||
path: string,
|
||||
schema: Schema,
|
||||
@ -72,7 +92,20 @@ export interface RendererEnv {
|
||||
data: any,
|
||||
broadcast?: RendererEvent<any>
|
||||
) => void;
|
||||
[propName: string]: any;
|
||||
|
||||
/**
|
||||
* 是否开启 amis 调试
|
||||
*/
|
||||
enableAMISDebug?: boolean;
|
||||
|
||||
/**
|
||||
* 替换文本,用于实现 URL 替换、语言替换等
|
||||
*/
|
||||
replaceText?: {[propName: string]: any};
|
||||
/**
|
||||
* 文本替换的黑名单,因为属性太多了所以改成黑名单的 fangs
|
||||
*/
|
||||
replaceTextIgnoreKeys?: String[];
|
||||
}
|
||||
|
||||
export const EnvContext = React.createContext<RendererEnv | void>(undefined);
|
||||
@ -115,7 +148,7 @@ export function withRendererEnv<
|
||||
{...(this.props as JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
React.ComponentProps<T>
|
||||
>)}
|
||||
> as any)}
|
||||
{...injectedProps}
|
||||
/>
|
||||
);
|
@ -2,8 +2,6 @@
|
||||
* @file 用于在移动端或不同语言环境下使用不同配置
|
||||
*/
|
||||
|
||||
import {SchemaNode, Schema} from './types';
|
||||
import {RendererProps, RendererConfig, addSchemaFilter} from './factory';
|
||||
import {findObjectsWithKey} from './utils/helper';
|
||||
const isMobile = (window as any).matchMedia?.('(max-width: 768px)').matches
|
||||
? true
|
@ -3,39 +3,23 @@ import {RendererStore, IRendererStore, IIRendererStore} from './store/index';
|
||||
import {getEnv, destroy} from 'mobx-state-tree';
|
||||
import {wrapFetcher} from './utils/api';
|
||||
import {normalizeLink} from './utils/normalizeLink';
|
||||
import {
|
||||
findIndex,
|
||||
isObject,
|
||||
JSONTraverse,
|
||||
promisify,
|
||||
qsparse,
|
||||
string2regExp
|
||||
} from './utils/helper';
|
||||
import {findIndex, promisify, qsparse, string2regExp} from './utils/helper';
|
||||
import {
|
||||
fetcherResult,
|
||||
SchemaNode,
|
||||
Schema,
|
||||
Action,
|
||||
EventTrack,
|
||||
PlainObject
|
||||
} from './types';
|
||||
import {observer} from 'mobx-react';
|
||||
import Scoped from './Scoped';
|
||||
import {getTheme, ThemeProps} from './theme';
|
||||
import {ThemeProps} from './theme';
|
||||
import find from 'lodash/find';
|
||||
import Alert from './components/Alert2';
|
||||
import {toast} from './components/Toast';
|
||||
import {alert, confirm, setRenderSchemaFn} from './components/Alert';
|
||||
import {getDefaultLocale, makeTranslator, LocaleProps} from './locale';
|
||||
import ScopedRootRenderer, {RootRenderProps} from './Root';
|
||||
import {LocaleProps} from './locale';
|
||||
import {HocStoreFactory} from './WithStore';
|
||||
import {EnvContext, RendererEnv} from './env';
|
||||
import {envOverwrite} from './envOverwrite';
|
||||
import {RendererEnv} from './env';
|
||||
import {OnEventProps} from './utils/renderer-event';
|
||||
import {enableDebug} from './utils/debug';
|
||||
|
||||
import type {ToastLevel, ToastConf} from './components/Toast';
|
||||
import {replaceText} from './utils/replaceText';
|
||||
import {Placeholder} from './renderers/Placeholder';
|
||||
|
||||
export interface TestFunc {
|
||||
(
|
||||
@ -68,7 +52,11 @@ export interface RendererBasicConfig {
|
||||
}
|
||||
|
||||
export interface RendererProps extends ThemeProps, LocaleProps, OnEventProps {
|
||||
render: (region: string, node: SchemaNode, props?: any) => JSX.Element;
|
||||
render: (
|
||||
region: string,
|
||||
node: SchemaNode,
|
||||
props?: PlainObject
|
||||
) => JSX.Element;
|
||||
env: RendererEnv;
|
||||
$path: string; // 当前组件所在的层级信息
|
||||
$schema: any; // 原始 schema 配置
|
||||
@ -101,51 +89,11 @@ export interface wsObject {
|
||||
body?: any;
|
||||
}
|
||||
|
||||
export interface RenderOptions {
|
||||
export interface RenderOptions
|
||||
extends Partial<Omit<RendererEnv, 'fetcher' | 'theme'>> {
|
||||
session?: string;
|
||||
theme?: string;
|
||||
fetcher?: (config: fetcherConfig) => Promise<fetcherResult>;
|
||||
wsFetcher?: (
|
||||
ws: wsObject,
|
||||
onMessage: (data: any) => void,
|
||||
onError: (error: any) => void
|
||||
) => void;
|
||||
isCancel?: (value: any) => boolean;
|
||||
notify?: (type: ToastLevel, msg: string, conf?: ToastConf) => void;
|
||||
jumpTo?: (to: string, action?: Action, ctx?: object) => void;
|
||||
alert?: (msg: string) => void;
|
||||
confirm?: (msg: string, title?: string) => boolean | Promise<boolean>;
|
||||
rendererResolver?: (
|
||||
path: string,
|
||||
schema: Schema,
|
||||
props: any
|
||||
) => null | RendererConfig;
|
||||
copy?: (contents: string, options?: any) => void;
|
||||
getModalContainer?: () => HTMLElement;
|
||||
loadRenderer?: (
|
||||
schema: Schema,
|
||||
path: string,
|
||||
reRender: Function
|
||||
) => Promise<React.ReactType> | React.ReactType | JSX.Element | void;
|
||||
affixOffsetTop?: number;
|
||||
affixOffsetBottom?: number;
|
||||
richTextToken?: string;
|
||||
/**
|
||||
* 替换文本,用于实现 URL 替换、语言替换等
|
||||
*/
|
||||
replaceText?: {[propName: string]: any};
|
||||
/**
|
||||
* 文本替换的黑名单,因为属性太多了所以改成黑名单的 fangs
|
||||
*/
|
||||
replaceTextIgnoreKeys?: String[];
|
||||
/**
|
||||
* 过滤 html 标签,可用来添加 xss 保护逻辑
|
||||
*/
|
||||
filterHtml?: (input: string) => string;
|
||||
/**
|
||||
* 是否开启 amis 调试
|
||||
*/
|
||||
enableAMISDebug?: boolean;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface fetcherConfig {
|
||||
@ -156,7 +104,9 @@ export interface fetcherConfig {
|
||||
}
|
||||
|
||||
const renderers: Array<RendererConfig> = [];
|
||||
const rendererNames: Array<string> = [];
|
||||
const renderersMap: {
|
||||
[propName: string]: boolean;
|
||||
} = {};
|
||||
const schemaFilters: Array<RenderSchemaFilter> = [];
|
||||
let anonymousIndex = 1;
|
||||
|
||||
@ -202,7 +152,7 @@ export function registerRenderer(config: RendererConfig): RendererConfig {
|
||||
config.Renderer = config.component;
|
||||
config.name = config.name || config.type || `anonymous-${anonymousIndex++}`;
|
||||
|
||||
if (~rendererNames.indexOf(config.name)) {
|
||||
if (renderersMap[config.name]) {
|
||||
throw new Error(
|
||||
`The renderer with name "${config.name}" has already exists, please try another name!`
|
||||
);
|
||||
@ -225,22 +175,13 @@ export function registerRenderer(config: RendererConfig): RendererConfig {
|
||||
item => (config.weight as number) < item.weight
|
||||
);
|
||||
~idx ? renderers.splice(idx, 0, config) : renderers.push(config);
|
||||
rendererNames.push(config.name);
|
||||
renderersMap[config.name] = config.Renderer !== Placeholder;
|
||||
return config;
|
||||
}
|
||||
|
||||
export function unRegisterRenderer(config: RendererConfig | string) {
|
||||
let idx =
|
||||
typeof config === 'string'
|
||||
? findIndex(renderers, item => item.name === config)
|
||||
: renderers.indexOf(config);
|
||||
~idx && renderers.splice(idx, 1);
|
||||
|
||||
let idx2 =
|
||||
typeof config === 'string'
|
||||
? findIndex(rendererNames, item => item === config)
|
||||
: rendererNames.indexOf(config.name || '');
|
||||
~idx2 && rendererNames.splice(idx2, 1);
|
||||
const name = (typeof config === 'string' ? config : config.name)!;
|
||||
delete renderersMap[name];
|
||||
|
||||
// 清空渲染器定位缓存
|
||||
cache = {};
|
||||
@ -248,17 +189,17 @@ export function unRegisterRenderer(config: RendererConfig | string) {
|
||||
|
||||
export function loadRenderer(schema: Schema, path: string) {
|
||||
return (
|
||||
<Alert level="danger">
|
||||
<div className="RuntimeError">
|
||||
<p>Error: 找不到对应的渲染器</p>
|
||||
<p>Path: {path}</p>
|
||||
<pre>
|
||||
<code>{JSON.stringify(schema, null, 2)}</code>
|
||||
</pre>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const defaultOptions: RenderOptions = {
|
||||
export const defaultOptions: RenderOptions = {
|
||||
session: 'global',
|
||||
affixOffsetTop: 0,
|
||||
affixOffsetBottom: 0,
|
||||
@ -273,7 +214,11 @@ const defaultOptions: RenderOptions = {
|
||||
return Promise.reject('fetcher is required');
|
||||
},
|
||||
// 使用 WebSocket 来实时获取数据
|
||||
wsFetcher(ws, onMessage, onError) {
|
||||
wsFetcher(
|
||||
ws: wsObject,
|
||||
onMessage: (data: any) => void,
|
||||
onError: (error: any) => void
|
||||
) {
|
||||
if (ws) {
|
||||
const socket = new WebSocket(ws.url);
|
||||
socket.onopen = event => {
|
||||
@ -318,10 +263,6 @@ const defaultOptions: RenderOptions = {
|
||||
'Please implement updateLocation. see https://baidu.gitee.io/amis/docs/start/getting-started#%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97'
|
||||
);
|
||||
},
|
||||
alert,
|
||||
confirm,
|
||||
notify: (type, msg, conf) =>
|
||||
toast[type] ? toast[type](msg, conf) : console.warn('[Notify]', type, msg),
|
||||
|
||||
jumpTo: (to: string, action?: any) => {
|
||||
if (to === 'goBack') {
|
||||
@ -383,88 +324,9 @@ const defaultOptions: RenderOptions = {
|
||||
*/
|
||||
filterHtml: (input: string) => input
|
||||
};
|
||||
let stores: {
|
||||
export const stores: {
|
||||
[propName: string]: IRendererStore;
|
||||
} = {};
|
||||
export function render(
|
||||
schema: Schema,
|
||||
props: RootRenderProps = {},
|
||||
options: RenderOptions = {},
|
||||
pathPrefix: string = ''
|
||||
): JSX.Element {
|
||||
let locale = props.locale || getDefaultLocale();
|
||||
// 兼容 locale 的不同写法
|
||||
locale = locale.replace('_', '-');
|
||||
locale = locale === 'en' ? 'en-US' : locale;
|
||||
locale = locale === 'zh' ? 'zh-CN' : locale;
|
||||
locale = locale === 'cn' ? 'zh-CN' : locale;
|
||||
const translate = props.translate || makeTranslator(locale);
|
||||
let store = stores[options.session || 'global'];
|
||||
|
||||
// 根据环境覆盖 schema,这个要在最前面做,不然就无法覆盖 validations
|
||||
envOverwrite(schema, locale);
|
||||
|
||||
if (!store) {
|
||||
options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
fetcher: options.fetcher
|
||||
? wrapFetcher(options.fetcher, options.tracker)
|
||||
: defaultOptions.fetcher,
|
||||
confirm: promisify(
|
||||
options.confirm || defaultOptions.confirm || window.confirm
|
||||
),
|
||||
locale,
|
||||
translate
|
||||
} as any;
|
||||
|
||||
if (options.enableAMISDebug) {
|
||||
// 因为里面还有 render
|
||||
setTimeout(() => {
|
||||
enableDebug();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
store = RendererStore.create({}, options);
|
||||
stores[options.session || 'global'] = store;
|
||||
}
|
||||
|
||||
(window as any).amisStore = store; // 为了方便 debug.
|
||||
const env = getEnv(store);
|
||||
|
||||
let theme = props.theme || options.theme || 'cxd';
|
||||
if (theme === 'default') {
|
||||
theme = 'cxd';
|
||||
}
|
||||
env.theme = getTheme(theme);
|
||||
|
||||
if (props.locale !== undefined) {
|
||||
env.translate = translate;
|
||||
env.locale = locale;
|
||||
}
|
||||
|
||||
// 默认将开启移动端原生 UI
|
||||
if (typeof options.useMobileUI) {
|
||||
props.useMobileUI = true;
|
||||
}
|
||||
|
||||
replaceText(schema, env.replaceText, env.replaceTextIgnoreKeys);
|
||||
|
||||
return (
|
||||
<EnvContext.Provider value={env}>
|
||||
<ScopedRootRenderer
|
||||
{...props}
|
||||
schema={schema}
|
||||
pathPrefix={pathPrefix}
|
||||
rootStore={store}
|
||||
env={env}
|
||||
theme={theme}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
/>
|
||||
</EnvContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// 默认 env 会被缓存,所以新传入的 env 不会替换旧的。
|
||||
// 除非先删了旧的,新的才会生效。
|
||||
@ -576,28 +438,4 @@ export function getRendererByName(name: string) {
|
||||
return find(renderers, item => item.name === name);
|
||||
}
|
||||
|
||||
setRenderSchemaFn((controls, value, callback, scopeRef, theme) => {
|
||||
return render(
|
||||
{
|
||||
name: 'form',
|
||||
type: 'form',
|
||||
wrapWithPanel: false,
|
||||
mode: 'horizontal',
|
||||
controls,
|
||||
messages: {
|
||||
validateFailed: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
data: value,
|
||||
onFinished: callback,
|
||||
scopeRef,
|
||||
theme
|
||||
},
|
||||
{
|
||||
session: 'prompt'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export {RendererEnv};
|
237
packages/amis-core/src/index.tsx
Normal file
237
packages/amis-core/src/index.tsx
Normal file
@ -0,0 +1,237 @@
|
||||
/** @license amis v@version
|
||||
*
|
||||
* Copyright Baidu
|
||||
*
|
||||
* This source code is licensed under the Apache license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import {
|
||||
Renderer,
|
||||
getRendererByName,
|
||||
getRenderers,
|
||||
registerRenderer,
|
||||
unRegisterRenderer,
|
||||
resolveRenderer,
|
||||
filterSchema,
|
||||
clearStoresCache,
|
||||
updateEnv,
|
||||
RenderOptions,
|
||||
stores,
|
||||
defaultOptions,
|
||||
addSchemaFilter,
|
||||
RendererProps
|
||||
} from './factory';
|
||||
import './renderers/builtin';
|
||||
export * from './utils/index';
|
||||
export * from './types';
|
||||
export * from './store';
|
||||
import * as utils from './utils/helper';
|
||||
import {getEnv} from 'mobx-state-tree';
|
||||
|
||||
import {RegisterStore, RendererStore} from './store';
|
||||
import {
|
||||
setDefaultLocale,
|
||||
getDefaultLocale,
|
||||
makeTranslator,
|
||||
register as registerLocale,
|
||||
extendLocale,
|
||||
localeable,
|
||||
LocaleProps,
|
||||
TranslateFn
|
||||
} from './locale';
|
||||
|
||||
import Scoped, {IScopedContext, ScopedContext} from './Scoped';
|
||||
|
||||
import {
|
||||
classnames,
|
||||
getClassPrefix,
|
||||
setDefaultTheme,
|
||||
theme,
|
||||
getTheme,
|
||||
ThemeProps,
|
||||
themeable,
|
||||
ClassNamesFn,
|
||||
makeClassnames
|
||||
} from './theme';
|
||||
const classPrefix = getClassPrefix();
|
||||
|
||||
export * from './actions';
|
||||
import FormItem, {
|
||||
FormBaseControl,
|
||||
FormControlProps,
|
||||
FormItemWrap,
|
||||
registerFormItem
|
||||
} from './renderers/Item';
|
||||
import {
|
||||
FormOptionsControl,
|
||||
OptionsControl,
|
||||
OptionsControlProps,
|
||||
registerOptionsControl
|
||||
} from './renderers/Options';
|
||||
import {Schema} from './types';
|
||||
import ScopedRootRenderer, {RootRenderProps} from './Root';
|
||||
import {envOverwrite} from './envOverwrite';
|
||||
import {wrapFetcher} from './utils/api';
|
||||
import {autobind, promisify} from './utils/helper';
|
||||
import {enableDebug} from './utils/debug';
|
||||
import {replaceText} from './utils/replaceText';
|
||||
import {EnvContext, RendererEnv} from './env';
|
||||
import React from 'react';
|
||||
import {
|
||||
evaluate,
|
||||
Evaluator,
|
||||
extendsFilters,
|
||||
FilterContext,
|
||||
filters,
|
||||
getFilters,
|
||||
lexer,
|
||||
parse,
|
||||
registerFilter
|
||||
} from 'amis-formula';
|
||||
import LazyComponent from './components/LazyComponent';
|
||||
import {FormHorizontal, FormRenderer} from './renderers/Form';
|
||||
|
||||
export {
|
||||
clearStoresCache,
|
||||
updateEnv,
|
||||
Renderer,
|
||||
RendererProps,
|
||||
RendererEnv,
|
||||
EnvContext,
|
||||
RegisterStore,
|
||||
FormItem,
|
||||
FormItemWrap,
|
||||
OptionsControl,
|
||||
FormRenderer,
|
||||
FormHorizontal,
|
||||
// 其他功能类方法
|
||||
utils,
|
||||
getRendererByName,
|
||||
registerRenderer,
|
||||
unRegisterRenderer,
|
||||
getRenderers,
|
||||
registerFormItem,
|
||||
registerOptionsControl,
|
||||
resolveRenderer,
|
||||
filterSchema,
|
||||
Scoped,
|
||||
ScopedContext,
|
||||
IScopedContext,
|
||||
setDefaultTheme,
|
||||
theme,
|
||||
themeable,
|
||||
ThemeProps,
|
||||
getTheme,
|
||||
classPrefix,
|
||||
getClassPrefix,
|
||||
classnames,
|
||||
makeClassnames,
|
||||
// 多语言相关
|
||||
getDefaultLocale,
|
||||
setDefaultLocale,
|
||||
registerLocale,
|
||||
makeTranslator,
|
||||
extendLocale,
|
||||
localeable,
|
||||
LocaleProps,
|
||||
TranslateFn,
|
||||
autobind,
|
||||
ClassNamesFn,
|
||||
// amis-formula 相关
|
||||
parse,
|
||||
lexer,
|
||||
Evaluator,
|
||||
FilterContext,
|
||||
filters,
|
||||
getFilters,
|
||||
registerFilter,
|
||||
extendsFilters,
|
||||
evaluate,
|
||||
// 其他
|
||||
LazyComponent,
|
||||
addSchemaFilter,
|
||||
OptionsControlProps,
|
||||
FormOptionsControl,
|
||||
FormControlProps,
|
||||
FormBaseControl
|
||||
};
|
||||
|
||||
export function render(
|
||||
schema: Schema,
|
||||
props: RootRenderProps = {},
|
||||
options: RenderOptions = {},
|
||||
pathPrefix: string = ''
|
||||
): JSX.Element {
|
||||
let locale = props.locale || getDefaultLocale();
|
||||
// 兼容 locale 的不同写法
|
||||
locale = locale.replace('_', '-');
|
||||
locale = locale === 'en' ? 'en-US' : locale;
|
||||
locale = locale === 'zh' ? 'zh-CN' : locale;
|
||||
locale = locale === 'cn' ? 'zh-CN' : locale;
|
||||
const translate = props.translate || makeTranslator(locale);
|
||||
let store = stores[options.session || 'global'];
|
||||
|
||||
// 根据环境覆盖 schema,这个要在最前面做,不然就无法覆盖 validations
|
||||
envOverwrite(schema, locale);
|
||||
|
||||
if (!store) {
|
||||
options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
fetcher: options.fetcher
|
||||
? wrapFetcher(options.fetcher, options.tracker)
|
||||
: defaultOptions.fetcher,
|
||||
confirm: promisify(
|
||||
options.confirm || defaultOptions.confirm || window.confirm
|
||||
),
|
||||
locale,
|
||||
translate
|
||||
} as any;
|
||||
|
||||
if (options.enableAMISDebug) {
|
||||
// 因为里面还有 render
|
||||
setTimeout(() => {
|
||||
enableDebug();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
store = RendererStore.create({}, options);
|
||||
stores[options.session || 'global'] = store;
|
||||
}
|
||||
|
||||
(window as any).amisStore = store; // 为了方便 debug.
|
||||
const env = getEnv(store);
|
||||
|
||||
let theme = props.theme || options.theme || 'cxd';
|
||||
if (theme === 'default') {
|
||||
theme = 'cxd';
|
||||
}
|
||||
env.theme = getTheme(theme);
|
||||
|
||||
if (props.locale !== undefined) {
|
||||
env.translate = translate;
|
||||
env.locale = locale;
|
||||
}
|
||||
|
||||
// 默认将开启移动端原生 UI
|
||||
if (typeof options.useMobileUI) {
|
||||
props.useMobileUI = true;
|
||||
}
|
||||
|
||||
replaceText(schema, env.replaceText, env.replaceTextIgnoreKeys);
|
||||
|
||||
return (
|
||||
<EnvContext.Provider value={env}>
|
||||
<ScopedRootRenderer
|
||||
{...props}
|
||||
schema={schema}
|
||||
pathPrefix={pathPrefix}
|
||||
rootStore={store}
|
||||
env={env}
|
||||
theme={theme}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
/>
|
||||
</EnvContext.Provider>
|
||||
);
|
||||
}
|
@ -19,6 +19,13 @@ export function register(name: string, config: LocaleConfig) {
|
||||
locales[name] = config;
|
||||
}
|
||||
|
||||
export function extendLocale(name: string, config: LocaleConfig) {
|
||||
locales[name] = {
|
||||
...(locales[name] || {}),
|
||||
...config
|
||||
};
|
||||
}
|
||||
|
||||
const fns: {
|
||||
[propName: string]: TranslateFn;
|
||||
} = {};
|
||||
@ -126,7 +133,7 @@ export function localeable<
|
||||
{...(this.props as JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
React.ComponentProps<T>
|
||||
>)}
|
||||
> as any)}
|
||||
{...injectedProps}
|
||||
{...refConfig}
|
||||
/>
|
@ -1,12 +1,16 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Renderer, RendererProps} from '../../factory';
|
||||
import {observer} from 'mobx-react';
|
||||
import {FormStore, IFormStore} from '../../store/form';
|
||||
import {Api, SchemaNode, Schema, Action, ApiObject, Payload} from '../../types';
|
||||
import {filter, evalExpression} from '../../utils/tpl';
|
||||
import cx from 'classnames';
|
||||
import getExprProperties from '../../utils/filter-schema';
|
||||
import {Renderer, RendererProps} from '../factory';
|
||||
import {FormStore, IFormStore} from '../store/form';
|
||||
import {
|
||||
Api,
|
||||
SchemaNode,
|
||||
Schema,
|
||||
ActionObject,
|
||||
Payload,
|
||||
ClassName
|
||||
} from '../types';
|
||||
import {filter, evalExpression} from '../utils/tpl';
|
||||
import getExprProperties from '../utils/filter-schema';
|
||||
import {
|
||||
promisify,
|
||||
difference,
|
||||
@ -22,61 +26,30 @@ import {
|
||||
qsparse,
|
||||
repeatCount,
|
||||
createObject
|
||||
} from '../../utils/helper';
|
||||
} from '../utils/helper';
|
||||
|
||||
import debouce from 'lodash/debounce';
|
||||
import flatten from 'lodash/flatten';
|
||||
import find from 'lodash/find';
|
||||
import Scoped, {
|
||||
ScopedContext,
|
||||
IScopedContext,
|
||||
ScopedComponentType
|
||||
} from '../../Scoped';
|
||||
import {IComboStore} from '../../store/combo';
|
||||
import {dataMapping} from '../../utils/tpl-builtin';
|
||||
import {isApiOutdated, isEffectiveApi} from '../../utils/api';
|
||||
import Spinner from '../../components/Spinner';
|
||||
import {LazyComponent} from '../../components';
|
||||
import {ScopedContext, IScopedContext, ScopedComponentType} from '../Scoped';
|
||||
|
||||
import {IComboStore} from '../store/combo';
|
||||
import {dataMapping} from '../utils/tpl-builtin';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
import LazyComponent from '../components/LazyComponent';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import {asFormItem} from './Item';
|
||||
import {SimpleMap} from '../../utils/SimpleMap';
|
||||
import {trace} from 'mobx';
|
||||
import {
|
||||
BaseSchema,
|
||||
SchemaApi,
|
||||
SchemaClassName,
|
||||
SchemaCollection,
|
||||
SchemaDefaultData,
|
||||
SchemaExpression,
|
||||
SchemaMessage,
|
||||
SchemaName,
|
||||
SchemaObject,
|
||||
SchemaRedirect,
|
||||
SchemaReload
|
||||
} from '../../Schema';
|
||||
import {ActionSchema} from '../Action';
|
||||
import {DialogSchemaBase} from '../Dialog';
|
||||
|
||||
import type {LabelAlign} from './Item';
|
||||
|
||||
export interface FormSchemaHorizontal {
|
||||
export interface FormHorizontal {
|
||||
left?: number;
|
||||
right?: number;
|
||||
leftFixed?: boolean | number | 'xs' | 'sm' | 'md' | 'lg';
|
||||
justify?: boolean; // 两端对齐
|
||||
labelAlign?: 'left' | 'right' // label对齐方式
|
||||
labelAlign?: 'left' | 'right'; // label对齐方式
|
||||
}
|
||||
|
||||
/**
|
||||
* Form 表单渲染器。
|
||||
*
|
||||
* 说明:https://baidu.gitee.io/amis/docs/components/form/index
|
||||
*/
|
||||
export interface FormSchema extends BaseSchema {
|
||||
/**
|
||||
* 指定为表单渲染器。
|
||||
*/
|
||||
type: 'form';
|
||||
|
||||
export interface FormSchemaBase {
|
||||
/**
|
||||
* 表单标题
|
||||
*/
|
||||
@ -85,12 +58,12 @@ export interface FormSchema extends BaseSchema {
|
||||
/**
|
||||
* 按钮集合,会固定在底部显示。
|
||||
*/
|
||||
actions?: Array<ActionSchema>;
|
||||
actions?: Array<any>;
|
||||
|
||||
/**
|
||||
* 表单项集合
|
||||
*/
|
||||
body?: SchemaCollection;
|
||||
body?: any;
|
||||
|
||||
/**
|
||||
* @deprecated 请用类型 tabs
|
||||
@ -102,7 +75,7 @@ export interface FormSchema extends BaseSchema {
|
||||
*/
|
||||
fieldSet?: any;
|
||||
|
||||
data?: SchemaDefaultData;
|
||||
data?: any;
|
||||
|
||||
/**
|
||||
* 是否开启调试,开启后会在顶部实时显示表单项数据。
|
||||
@ -112,12 +85,12 @@ export interface FormSchema extends BaseSchema {
|
||||
/**
|
||||
* 用来初始化表单数据
|
||||
*/
|
||||
initApi?: SchemaApi;
|
||||
initApi?: Api;
|
||||
|
||||
/**
|
||||
* Form 用来获取初始数据的 api,与initApi不同的是,会一直轮询请求该接口,直到返回 finished 属性为 true 才 结束。
|
||||
*/
|
||||
initAsyncApi?: SchemaApi;
|
||||
initAsyncApi?: Api;
|
||||
|
||||
/**
|
||||
* 设置了initAsyncApi后,默认会从返回数据的data.finished来判断是否完成,也可以设置成其他的xxx,就会从data.xxx中获取
|
||||
@ -137,7 +110,7 @@ export interface FormSchema extends BaseSchema {
|
||||
/**
|
||||
* 建议改成 api 的 sendOn 属性。
|
||||
*/
|
||||
initFetchOn?: SchemaExpression;
|
||||
initFetchOn?: string;
|
||||
|
||||
/**
|
||||
* 设置后将轮询调用 initApi
|
||||
@ -169,17 +142,17 @@ export interface FormSchema extends BaseSchema {
|
||||
*
|
||||
* 详情:https://baidu.gitee.io/amis/docs/components/form/index#%E8%A1%A8%E5%8D%95%E6%8F%90%E4%BA%A4
|
||||
*/
|
||||
api?: SchemaApi;
|
||||
api?: Api;
|
||||
|
||||
/**
|
||||
* Form 也可以配置 feedback。
|
||||
*/
|
||||
feedback?: DialogSchemaBase;
|
||||
feedback?: any;
|
||||
|
||||
/**
|
||||
* 设置此属性后,表单提交发送保存接口后,还会继续轮询请求该接口,直到返回 finished 属性为 true 才 结束。
|
||||
*/
|
||||
asyncApi?: SchemaApi;
|
||||
asyncApi?: Api;
|
||||
|
||||
/**
|
||||
* 轮询请求的时间间隔,默认为 3秒。设置 asyncApi 才有效
|
||||
@ -214,7 +187,7 @@ export interface FormSchema extends BaseSchema {
|
||||
/**
|
||||
* 如果是水平排版,这个属性可以细化水平排版的左右宽度占比。
|
||||
*/
|
||||
horizontal?: FormSchemaHorizontal;
|
||||
horizontal?: FormHorizontal;
|
||||
|
||||
/**
|
||||
* 是否自动将第一个表单元素聚焦。
|
||||
@ -229,14 +202,14 @@ export interface FormSchema extends BaseSchema {
|
||||
* 表单验证失败时的提示
|
||||
*/
|
||||
validateFailed?: string;
|
||||
} & SchemaMessage;
|
||||
};
|
||||
|
||||
name?: SchemaName;
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 配置容器 panel className
|
||||
*/
|
||||
panelClassName?: SchemaClassName;
|
||||
panelClassName?: ClassName;
|
||||
|
||||
/**
|
||||
* 设置主键 id, 当设置后,检测表单是否完成时(asyncApi),只会携带此数据。
|
||||
@ -244,9 +217,9 @@ export interface FormSchema extends BaseSchema {
|
||||
*/
|
||||
primaryField?: string;
|
||||
|
||||
redirect?: SchemaRedirect;
|
||||
redirect?: string;
|
||||
|
||||
reload?: SchemaReload;
|
||||
reload?: string;
|
||||
|
||||
/**
|
||||
* 修改的时候是否直接提交表单。
|
||||
@ -310,18 +283,16 @@ export interface FormSchema extends BaseSchema {
|
||||
labelAlign?: LabelAlign;
|
||||
}
|
||||
|
||||
export type FormGroup = FormSchema & {
|
||||
export type FormGroup = FormSchemaBase & {
|
||||
title?: string;
|
||||
className?: string;
|
||||
};
|
||||
export type FormGroupNode = FormGroup | FormGroupArray;
|
||||
export interface FormGroupArray extends Array<FormGroupNode> {}
|
||||
|
||||
export type FormHorizontal = FormSchemaHorizontal;
|
||||
|
||||
export interface FormProps
|
||||
extends RendererProps,
|
||||
Omit<FormSchema, 'mode' | 'className'> {
|
||||
Omit<FormSchemaBase, 'mode' | 'className'> {
|
||||
data: any;
|
||||
store: IFormStore;
|
||||
wrapperComponent: React.ElementType;
|
||||
@ -945,7 +916,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
handleAction(
|
||||
e: React.UIEvent<any> | void,
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
data: object,
|
||||
throwErrors: boolean = false,
|
||||
delegate?: IScopedContext
|
||||
@ -1234,7 +1205,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
handleDialogConfirm(
|
||||
values: object[],
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
ctx: any,
|
||||
targets: Array<any>
|
||||
) {
|
||||
@ -1259,7 +1230,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
handleDrawerConfirm(
|
||||
values: object[],
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
ctx: any,
|
||||
targets: Array<any>
|
||||
) {
|
||||
@ -1327,7 +1298,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
!!~['submit', 'button', 'button-group', 'reset'].indexOf(
|
||||
(item as any)?.body?.[0]?.type ||
|
||||
(item as any)?.body?.type ||
|
||||
(item as SchemaObject).type
|
||||
(item as any).type
|
||||
)
|
||||
))
|
||||
) {
|
||||
@ -1344,7 +1315,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
}
|
||||
|
||||
renderFormItems(
|
||||
schema: Partial<FormSchema> & {
|
||||
schema: Partial<FormSchemaBase> & {
|
||||
controls?: Array<any>;
|
||||
},
|
||||
region: string = '',
|
||||
@ -1409,8 +1380,10 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {classnames: cx} = this.props;
|
||||
|
||||
return (
|
||||
<div className={`${ns}Form-row`}>
|
||||
<div className={cx('Form-row')}>
|
||||
{children.map((control, key) =>
|
||||
~['hidden', 'formula'].indexOf((control as any).type) ||
|
||||
(control as any).mode === 'inline' ? (
|
||||
@ -1418,10 +1391,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
) : (
|
||||
<div
|
||||
key={key}
|
||||
className={cx(
|
||||
`${ns}Form-col`,
|
||||
(control as Schema).columnClassName
|
||||
)}
|
||||
className={cx(`Form-col`, (control as Schema).columnClassName)}
|
||||
>
|
||||
{this.renderChild(control, '', {
|
||||
...otherProps,
|
||||
@ -1568,7 +1538,14 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
</pre>
|
||||
) : null}
|
||||
|
||||
<Spinner show={store.loading} overlay />
|
||||
{render(
|
||||
'spinner',
|
||||
{type: 'spinner'},
|
||||
{
|
||||
overlay: true,
|
||||
show: store.loading
|
||||
}
|
||||
)}
|
||||
|
||||
{this.renderFormItems({
|
||||
body
|
||||
@ -1588,8 +1565,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
{render(
|
||||
'modal',
|
||||
{
|
||||
...((store.action as Action) &&
|
||||
((store.action as Action).dialog as object)),
|
||||
...((store.action as ActionObject) &&
|
||||
((store.action as ActionObject).dialog as object)),
|
||||
type: 'dialog'
|
||||
},
|
||||
{
|
||||
@ -1604,8 +1581,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
{render(
|
||||
'modal',
|
||||
{
|
||||
...((store.action as Action) &&
|
||||
((store.action as Action).drawer as object)),
|
||||
...((store.action as ActionObject) &&
|
||||
((store.action as ActionObject).drawer as object)),
|
||||
type: 'drawer'
|
||||
},
|
||||
{
|
||||
@ -1734,7 +1711,7 @@ export class FormRenderer extends Form {
|
||||
}
|
||||
|
||||
doAction(
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
data: object = this.props.store.data,
|
||||
throwErrors: boolean = false
|
||||
) {
|
||||
@ -1743,7 +1720,7 @@ export class FormRenderer extends Form {
|
||||
|
||||
handleAction(
|
||||
e: React.UIEvent<any> | undefined,
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
ctx: object,
|
||||
throwErrors: boolean = false,
|
||||
delegate?: IScopedContext
|
||||
@ -1779,7 +1756,7 @@ export class FormRenderer extends Form {
|
||||
|
||||
handleDialogConfirm(
|
||||
values: object[],
|
||||
action: Action,
|
||||
action: ActionObject,
|
||||
ctx: any,
|
||||
targets: Array<any>
|
||||
) {
|
@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import hoistNonReactStatic from 'hoist-non-react-statics';
|
||||
import {IFormItemStore, IFormStore} from '../../store/form';
|
||||
import {IMapEntries, reaction} from 'mobx';
|
||||
import {IFormItemStore, IFormStore} from '../store/form';
|
||||
import {reaction} from 'mobx';
|
||||
|
||||
import {
|
||||
RendererProps,
|
||||
registerRenderer,
|
||||
TestFunc,
|
||||
RendererConfig
|
||||
} from '../../factory';
|
||||
} from '../factory';
|
||||
import {
|
||||
anyChanged,
|
||||
ucFirst,
|
||||
@ -17,38 +17,25 @@ import {
|
||||
isMobile,
|
||||
createObject,
|
||||
getVariable
|
||||
} from '../../utils/helper';
|
||||
} from '../utils/helper';
|
||||
import {observer} from 'mobx-react';
|
||||
import {FormHorizontal, FormSchema, FormSchemaHorizontal} from '.';
|
||||
import {Api, Schema} from '../../types';
|
||||
import {filter} from '../../utils/tpl';
|
||||
import {SchemaRemark} from '../Remark';
|
||||
import {FormHorizontal, FormSchemaBase} from './Form';
|
||||
import {
|
||||
BaseSchema,
|
||||
SchemaApi,
|
||||
SchemaApiObject,
|
||||
SchemaClassName,
|
||||
SchemaExpression,
|
||||
SchemaObject,
|
||||
SchemaType
|
||||
} from '../../Schema';
|
||||
import {HocStoreFactory} from '../../WithStore';
|
||||
Api,
|
||||
ApiObject,
|
||||
BaseSchemaWithoutType,
|
||||
ClassName,
|
||||
Schema
|
||||
} from '../types';
|
||||
import {filter} from '../utils/tpl';
|
||||
import {HocStoreFactory} from '../WithStore';
|
||||
import {wrapControl} from './wrapControl';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import debounce from 'lodash/debounce';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
import {dataMapping} from '../../utils/tpl-builtin';
|
||||
import {isEffectiveApi} from '../utils/api';
|
||||
|
||||
export type LabelAlign = 'right' | 'left';
|
||||
|
||||
export type FormControlSchemaAlias = SchemaObject;
|
||||
|
||||
export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 表单项类型
|
||||
*/
|
||||
type: SchemaType;
|
||||
|
||||
export interface FormBaseControl extends BaseSchemaWithoutType {
|
||||
/**
|
||||
* 表单项大小
|
||||
*/
|
||||
@ -67,7 +54,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 配置 label className
|
||||
*/
|
||||
labelClassName?: SchemaClassName;
|
||||
labelClassName?: string;
|
||||
|
||||
/**
|
||||
* 字段名,表单提交时的 key,支持多层级,用.连接,如: a.b.c
|
||||
@ -77,12 +64,12 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 显示一个小图标, 鼠标放上去的时候显示提示内容
|
||||
*/
|
||||
remark?: SchemaRemark;
|
||||
remark?: any;
|
||||
|
||||
/**
|
||||
* 显示一个小图标, 鼠标放上去的时候显示提示内容, 这个小图标跟 label 在一起
|
||||
*/
|
||||
labelRemark?: SchemaRemark;
|
||||
labelRemark?: any;
|
||||
|
||||
/**
|
||||
* 输入提示,聚焦的时候显示
|
||||
@ -102,7 +89,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 只读条件
|
||||
*/
|
||||
readOnlyOn?: SchemaExpression;
|
||||
readOnlyOn?: string;
|
||||
|
||||
/**
|
||||
* 不设置时,当表单提交过后表单项每次修改都会触发重新验证,
|
||||
@ -123,7 +110,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 配置描述上的 className
|
||||
*/
|
||||
descriptionClassName?: SchemaClassName;
|
||||
descriptionClassName?: ClassName;
|
||||
|
||||
/**
|
||||
* 配置当前表单项展示模式
|
||||
@ -133,7 +120,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 当配置为水平布局的时候,用来配置具体的左右分配。
|
||||
*/
|
||||
horizontal?: FormSchemaHorizontal;
|
||||
horizontal?: FormHorizontal;
|
||||
|
||||
/**
|
||||
* 表单 control 是否为 inline 模式。
|
||||
@ -143,7 +130,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 配置 input className
|
||||
*/
|
||||
inputClassName?: SchemaClassName;
|
||||
inputClassName?: ClassName;
|
||||
|
||||
/**
|
||||
* 占位符
|
||||
@ -296,7 +283,7 @@ export interface FormBaseControl extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 远端校验表单项接口
|
||||
*/
|
||||
validateApi?: SchemaApi;
|
||||
validateApi?: Api;
|
||||
}
|
||||
|
||||
export interface FormItemBasicConfig extends Partial<RendererConfig> {
|
||||
@ -350,7 +337,7 @@ export interface FormItemProps extends RendererProps {
|
||||
addHook: (fn: Function, mode?: 'validate' | 'init' | 'flush') => () => void;
|
||||
removeHook: (fn: Function, mode?: 'validate' | 'init' | 'flush') => void;
|
||||
renderFormItems: (
|
||||
schema: Partial<FormSchema>,
|
||||
schema: Partial<FormSchemaBase>,
|
||||
region: string,
|
||||
props: any
|
||||
) => JSX.Element;
|
||||
@ -473,7 +460,7 @@ export class FormItemWrap extends React.Component<FormItemProps> {
|
||||
const result = await formItem?.loadAutoUpdateData(
|
||||
autoFillApi,
|
||||
ctx,
|
||||
!!(autoFillApi as SchemaApiObject)?.silent
|
||||
!!(autoFillApi as ApiObject)?.silent
|
||||
);
|
||||
if (!result) return;
|
||||
|
@ -2,8 +2,14 @@
|
||||
* @file 所有列表选择类控件的父级,比如 Select、Radios、Checkboxes、
|
||||
* List、ButtonGroup 等等
|
||||
*/
|
||||
import {Api, PlainObject, Schema, Action} from '../../types';
|
||||
import {isEffectiveApi, isApiOutdated, isValidApi} from '../../utils/api';
|
||||
import {
|
||||
Api,
|
||||
PlainObject,
|
||||
ActionObject,
|
||||
OptionProps,
|
||||
BaseApiObject
|
||||
} from '../types';
|
||||
import {isEffectiveApi, isApiOutdated} from '../utils/api';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import {
|
||||
anyChanged,
|
||||
@ -19,7 +25,7 @@ import {
|
||||
mapTree,
|
||||
getTreeDepth,
|
||||
flattenTree
|
||||
} from '../../utils/helper';
|
||||
} from '../utils/helper';
|
||||
import {reaction} from 'mobx';
|
||||
import {
|
||||
FormControlProps,
|
||||
@ -28,7 +34,7 @@ import {
|
||||
detectProps as itemDetectProps,
|
||||
FormBaseControl
|
||||
} from './Item';
|
||||
import {IFormItemStore} from '../../store/formItem';
|
||||
import {IFormItemStore} from '../store/formItem';
|
||||
|
||||
export type OptionsControlComponent = React.ComponentType<FormControlProps>;
|
||||
|
||||
@ -37,23 +43,16 @@ import {
|
||||
resolveVariableAndFilter,
|
||||
isPureVariable,
|
||||
dataMapping
|
||||
} from '../../utils/tpl-builtin';
|
||||
import {
|
||||
Option,
|
||||
OptionProps,
|
||||
normalizeOptions,
|
||||
optionValueCompare
|
||||
} from '../../components/Select';
|
||||
import {filter} from '../../utils/tpl';
|
||||
} from '../utils/tpl-builtin';
|
||||
|
||||
import {filter} from '../utils/tpl';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {
|
||||
SchemaApi,
|
||||
SchemaExpression,
|
||||
SchemaObject,
|
||||
SchemaTokenizeableString
|
||||
} from '../../Schema';
|
||||
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import merge from 'lodash/merge';
|
||||
import {normalizeOptions} from '../utils/normalizeOptions';
|
||||
import {optionValueCompare} from '../utils/optionValueCompare';
|
||||
import {Option} from '../types';
|
||||
|
||||
export {Option};
|
||||
|
||||
@ -66,7 +65,7 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
/**
|
||||
* 可用来通过 API 拉取 options。
|
||||
*/
|
||||
source?: SchemaApi | SchemaTokenizeableString;
|
||||
source?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 默认选择选项第一个值。
|
||||
@ -78,7 +77,7 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
*
|
||||
* @deprecated 建议用 source 接口的 sendOn
|
||||
*/
|
||||
initFetchOn?: SchemaExpression;
|
||||
initFetchOn?: string;
|
||||
|
||||
/**
|
||||
* 配置 source 接口初始拉不拉取。
|
||||
@ -123,17 +122,17 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
/**
|
||||
* 延时加载的 API,当选项中有 defer: true 的选项时,点开会通过此接口扩充。
|
||||
*/
|
||||
deferApi?: SchemaApi;
|
||||
deferApi?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 添加时调用的接口
|
||||
*/
|
||||
addApi?: SchemaApi;
|
||||
addApi?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 新增时的表单项。
|
||||
*/
|
||||
addControls?: Array<SchemaObject>;
|
||||
addControls?: Array<PlainObject>;
|
||||
|
||||
/**
|
||||
* 是否可以新增
|
||||
@ -153,12 +152,12 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
/**
|
||||
* 编辑时调用的 API
|
||||
*/
|
||||
editApi?: SchemaApi;
|
||||
editApi?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 选项修改的表单项
|
||||
*/
|
||||
editControls?: Array<SchemaObject>;
|
||||
editControls?: Array<PlainObject>;
|
||||
|
||||
/**
|
||||
* 是否可删除
|
||||
@ -168,7 +167,7 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
/**
|
||||
* 选项删除 API
|
||||
*/
|
||||
deleteApi?: SchemaApi;
|
||||
deleteApi?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 选项删除提示文字。
|
||||
@ -179,7 +178,7 @@ export interface FormOptionsControl extends FormBaseControl {
|
||||
* 自动填充,当选项被选择的时候,将选项中的其他值同步设置到表单内。
|
||||
*/
|
||||
autoFill?: {
|
||||
[propName: string]: SchemaTokenizeableString;
|
||||
[propName: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
@ -471,7 +470,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
return !!rendererEvent?.prevented;
|
||||
}
|
||||
|
||||
doAction(action: Action, data: object, throwErrors: boolean) {
|
||||
doAction(action: ActionObject, data: object, throwErrors: boolean) {
|
||||
const {resetValue, onChange} = this.props;
|
||||
const actionType = action?.actionType as string;
|
||||
|
||||
@ -1300,55 +1299,3 @@ export function OptionsControl(config: OptionsBasicConfig) {
|
||||
return renderer.component as any;
|
||||
};
|
||||
}
|
||||
|
||||
export function highlight(
|
||||
text: string,
|
||||
input?: string,
|
||||
hlClassName: string = 'is-matched'
|
||||
) {
|
||||
if (!input) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = String(text);
|
||||
const reg = new RegExp(input.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'), 'ig');
|
||||
if (!reg.test(text)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const dom: Array<any> = [];
|
||||
|
||||
let start = 0;
|
||||
let match = null;
|
||||
|
||||
reg.lastIndex = 0;
|
||||
while ((match = reg.exec(text))) {
|
||||
const prev = text.substring(start, match.index);
|
||||
prev && dom.push(<span key={dom.length}>{prev}</span>);
|
||||
|
||||
match[0] &&
|
||||
dom.push(
|
||||
<span className={hlClassName} key={dom.length}>
|
||||
{match[0]}
|
||||
</span>
|
||||
);
|
||||
start = match.index + match[0].length;
|
||||
}
|
||||
const rest = text.substring(start);
|
||||
rest && dom.push(<span key={dom.length}>{rest}</span>);
|
||||
|
||||
// const parts = text.split(reg);
|
||||
|
||||
// parts.forEach((text: string, index) => {
|
||||
// text && dom.push(<span key={index}>{text}</span>);
|
||||
// dom.push(
|
||||
// <span className={hlClassName} key={`${index}-hl`}>
|
||||
// {input}
|
||||
// </span>
|
||||
// );
|
||||
// });
|
||||
|
||||
// dom.pop();
|
||||
|
||||
return dom;
|
||||
}
|
12
packages/amis-core/src/renderers/Placeholder.tsx
Normal file
12
packages/amis-core/src/renderers/Placeholder.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import {RendererProps} from '../factory';
|
||||
|
||||
export class Placeholder extends React.Component<RendererProps> {
|
||||
componentDidMount() {
|
||||
console.warn(`Please implement this renderer(${this.props.type})`);
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
23
packages/amis-core/src/renderers/builtin.tsx
Normal file
23
packages/amis-core/src/renderers/builtin.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import {Placeholder} from './Placeholder';
|
||||
import './Form.tsx';
|
||||
import {registerRenderer} from '../factory';
|
||||
|
||||
registerRenderer({
|
||||
type: 'spinner',
|
||||
component: Placeholder
|
||||
});
|
||||
|
||||
registerRenderer({
|
||||
type: 'alert',
|
||||
component: Placeholder
|
||||
});
|
||||
|
||||
registerRenderer({
|
||||
type: 'dialog',
|
||||
component: Placeholder
|
||||
});
|
||||
|
||||
registerRenderer({
|
||||
type: 'drawer',
|
||||
component: Placeholder
|
||||
});
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import {IFormStore, IFormItemStore} from '../../store/form';
|
||||
import {IFormStore, IFormItemStore} from '../store/form';
|
||||
import debouce from 'lodash/debounce';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {RendererProps, Renderer} from '../../factory';
|
||||
import {ComboStore, IComboStore, IUniqueGroup} from '../../store/combo';
|
||||
import {RendererProps, Renderer} from '../factory';
|
||||
import {ComboStore, IComboStore, IUniqueGroup} from '../store/combo';
|
||||
import {
|
||||
anyChanged,
|
||||
promisify,
|
||||
@ -13,24 +13,24 @@ import {
|
||||
autobind,
|
||||
getVariable,
|
||||
createObject
|
||||
} from '../../utils/helper';
|
||||
} from '../utils/helper';
|
||||
import {
|
||||
isNeedFormula,
|
||||
isExpression,
|
||||
FormulaExec,
|
||||
replaceExpression
|
||||
} from '../../utils/formula';
|
||||
import {IIRendererStore, IRendererStore} from '../../store';
|
||||
import {ScopedContext, IScopedContext} from '../../Scoped';
|
||||
} from '../utils/formula';
|
||||
import {IIRendererStore, IRendererStore} from '../store';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
import {reaction} from 'mobx';
|
||||
import {FormItemStore} from '../../store/formItem';
|
||||
import {FormItemStore} from '../store/formItem';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import {observer} from 'mobx-react';
|
||||
import hoistNonReactStatic from 'hoist-non-react-statics';
|
||||
import {withRootStore} from '../../WithRootStore';
|
||||
import {withRootStore} from '../WithRootStore';
|
||||
import {FormBaseControl, FormItemWrap} from './Item';
|
||||
import {Api} from '../../types';
|
||||
import {TableStore} from '../../store/table';
|
||||
import {Api} from '../types';
|
||||
import {TableStore} from '../store/table';
|
||||
|
||||
export interface ControlOutterProps extends RendererProps {
|
||||
formStore?: IFormStore;
|
||||
@ -327,7 +327,10 @@ export function wrapControl<
|
||||
// 此处需要同时考虑 defaultValue 和 value
|
||||
if (model && typeof props.value !== 'undefined') {
|
||||
// 渲染器中的 value 优先
|
||||
if (!isEqual(props.value, prevProps.value) && !isEqual(props.value, model.tmpValue)) {
|
||||
if (
|
||||
!isEqual(props.value, prevProps.value) &&
|
||||
!isEqual(props.value, model.tmpValue)
|
||||
) {
|
||||
// 外部直接传入的 value 无需执行运算器
|
||||
model.changeTmpValue(props.value);
|
||||
}
|
||||
@ -350,7 +353,10 @@ export function wrapControl<
|
||||
prevProps.defaultValue,
|
||||
prevProps.data
|
||||
);
|
||||
if (!isEqual(curResult, prevResult) && !isEqual(curResult, model.tmpValue)) {
|
||||
if (
|
||||
!isEqual(curResult, prevResult) &&
|
||||
!isEqual(curResult, model.tmpValue)
|
||||
) {
|
||||
// 识别上下文变动、自身数值变动、公式运算结果变动
|
||||
model.changeTmpValue(curResult);
|
||||
if (props.onChange) {
|
||||
@ -369,7 +375,8 @@ export function wrapControl<
|
||||
if (
|
||||
// 然后才是查看关联的 name 属性值是否变化
|
||||
!isEqual(props.data, prevProps.data) &&
|
||||
(!model.emitedValue || isEqual(model.emitedValue, model.tmpValue))
|
||||
(!model.emitedValue ||
|
||||
isEqual(model.emitedValue, model.tmpValue))
|
||||
) {
|
||||
model.changeEmitedValue(undefined);
|
||||
const prevValueByName = getVariable(props.data, model.name);
|
||||
@ -377,7 +384,7 @@ export function wrapControl<
|
||||
(!isEqual(valueByName, prevValueByName) ||
|
||||
getVariable(props.data, model.name, false) !==
|
||||
getVariable(prevProps.data, model.name, false)) &&
|
||||
!isEqual(valueByName, model.tmpValue)
|
||||
!isEqual(valueByName, model.tmpValue)
|
||||
) {
|
||||
model.changeTmpValue(valueByName);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import {Instance, SnapshotIn, types} from 'mobx-state-tree';
|
||||
import {Navigation} from '../components/AsideNav';
|
||||
import {RendererEnv} from '../factory';
|
||||
import {NavigationObject} from '../types';
|
||||
import {
|
||||
createObject,
|
||||
filterTree,
|
||||
@ -18,7 +18,7 @@ export const AppStore = ServiceStore.named('AppStore')
|
||||
offScreen: false
|
||||
})
|
||||
.views(self => ({
|
||||
get navigations(): Array<Navigation> {
|
||||
get navigations(): Array<NavigationObject> {
|
||||
if (Array.isArray(self.pages)) {
|
||||
return mapTree(self.pages, item => {
|
||||
let visible = item.visible;
|
@ -1,7 +1,6 @@
|
||||
import {types, SnapshotIn, isAlive, onAction, Instance} from 'mobx-state-tree';
|
||||
import {IIRendererStore, iRendererStore} from './iRenderer';
|
||||
import {FormItemStore} from './formItem';
|
||||
import {FormStore, IFormStore, IFormItemStore} from './form';
|
||||
import {types, SnapshotIn, Instance} from 'mobx-state-tree';
|
||||
import {iRendererStore} from './iRenderer';
|
||||
import type {IFormStore, IFormItemStore} from './form';
|
||||
import {getStoreById} from './manager';
|
||||
|
||||
export const UniqueGroup = types
|
||||
@ -137,7 +136,7 @@ export const ComboStore = iRendererStore
|
||||
}
|
||||
|
||||
function onChildStoreDispose(child: IFormStore) {
|
||||
if (child.storeType === FormStore.name) {
|
||||
if (child.storeType === 'FormStore') {
|
||||
const idx = self.formsRef.indexOf(child.id);
|
||||
if (~idx) {
|
||||
self.formsRef.splice(idx, 1);
|
@ -1,13 +1,5 @@
|
||||
import {saveAs} from 'file-saver';
|
||||
import {
|
||||
types,
|
||||
getParent,
|
||||
flow,
|
||||
getEnv,
|
||||
getRoot,
|
||||
isAlive,
|
||||
Instance
|
||||
} from 'mobx-state-tree';
|
||||
import {types, flow, getEnv, isAlive, Instance} from 'mobx-state-tree';
|
||||
import {IRendererStore} from './index';
|
||||
import {ServiceStore} from './service';
|
||||
import {
|
||||
@ -19,7 +11,7 @@ import {
|
||||
qsstringify,
|
||||
getVariable
|
||||
} from '../utils/helper';
|
||||
import {Api, Payload, fetchOptions, Action, ApiObject} from '../types';
|
||||
import {Api, Payload, fetchOptions, ActionObject, ApiObject} from '../types';
|
||||
import pick from 'lodash/pick';
|
||||
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
|
||||
import {normalizeApiResponseData} from '../utils/api';
|
||||
@ -399,7 +391,7 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
|
||||
perPage && (self.perPage = parseInt(perPage as string, 10));
|
||||
}
|
||||
|
||||
function selectAction(action: Action) {
|
||||
function selectAction(action: ActionObject) {
|
||||
self.selectedAction = action;
|
||||
}
|
||||
|
@ -1,16 +1,7 @@
|
||||
import {
|
||||
types,
|
||||
getEnv,
|
||||
flow,
|
||||
getRoot,
|
||||
detach,
|
||||
destroy,
|
||||
isAlive,
|
||||
Instance
|
||||
} from 'mobx-state-tree';
|
||||
import {types, getEnv, flow, isAlive, Instance} from 'mobx-state-tree';
|
||||
import debounce from 'lodash/debounce';
|
||||
import {ServiceStore} from './service';
|
||||
import {FormItemStore, IFormItemStore, SFormItemStore} from './formItem';
|
||||
import type {IFormItemStore} from './formItem';
|
||||
import {Api, ApiObject, fetchOptions, Payload} from '../types';
|
||||
import {ServerError} from '../utils/errors';
|
||||
import {
|
||||
@ -20,14 +11,12 @@ import {
|
||||
cloneObject,
|
||||
createObject,
|
||||
difference,
|
||||
guid,
|
||||
isEmpty,
|
||||
mapObject,
|
||||
keyToPath
|
||||
} from '../utils/helper';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import flatten from 'lodash/flatten';
|
||||
import {getStoreById, removeStore} from './manager';
|
||||
import {filter} from '../utils/tpl';
|
||||
import {normalizeApiResponseData} from '../utils/api';
|
||||
|
||||
@ -52,7 +41,7 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
while (pool.length) {
|
||||
const current = pool.shift()!;
|
||||
|
||||
if (current.storeType === FormItemStore.name) {
|
||||
if (current.storeType === 'FormItemStore') {
|
||||
formItems.push(current);
|
||||
} else {
|
||||
pool.push(...current.children);
|
||||
@ -326,7 +315,6 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
}
|
||||
|
||||
if (!json.ok) {
|
||||
|
||||
if (json.status === 422 && json.errors) {
|
||||
setFormItemErrors(json.errors);
|
||||
|
||||
@ -359,22 +347,23 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
);
|
||||
if (!ret?.dispatcher?.prevented) {
|
||||
self.msg &&
|
||||
getEnv(self).notify(
|
||||
'success',
|
||||
self.msg,
|
||||
json.msgTimeout !== undefined
|
||||
? {
|
||||
closeButton: true,
|
||||
timeout: json.msgTimeout
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
getEnv(self).notify(
|
||||
'success',
|
||||
self.msg,
|
||||
json.msgTimeout !== undefined
|
||||
? {
|
||||
closeButton: true,
|
||||
timeout: json.msgTimeout
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
return json.data;
|
||||
}
|
||||
} catch (e) {
|
||||
self.markSaving(false);
|
||||
let ret = options && options.onFailed && options.onFailed(e.response || {});
|
||||
let ret =
|
||||
options && options.onFailed && options.onFailed(e.response || {});
|
||||
if (ret?.then) {
|
||||
ret = yield ret;
|
||||
}
|
||||
@ -435,7 +424,7 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
}
|
||||
|
||||
const getItemsByPath = (key: string) => {
|
||||
const paths = keyToPath(key);
|
||||
const paths: Array<string> = keyToPath(key);
|
||||
const len = paths.length;
|
||||
|
||||
return paths.reduce(
|
||||
@ -486,7 +475,7 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
if (dispatcher?.then) {
|
||||
dispatcher = yield dispatcher;
|
||||
}
|
||||
if (!dispatcher?.prevented){
|
||||
if (!dispatcher?.prevented) {
|
||||
msg && env.notify('error', msg);
|
||||
}
|
||||
throw new Error(msg);
|
@ -1,10 +1,7 @@
|
||||
import {
|
||||
types,
|
||||
getParent,
|
||||
SnapshotIn,
|
||||
flow,
|
||||
getRoot,
|
||||
hasParent,
|
||||
isAlive,
|
||||
getEnv,
|
||||
Instance
|
||||
@ -19,27 +16,21 @@ import {isEffectiveApi} from '../utils/api';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {
|
||||
isArrayChildrenModified,
|
||||
isObject,
|
||||
createObject,
|
||||
isObjectShallowModified,
|
||||
findTree,
|
||||
findTreeIndex,
|
||||
spliceTree,
|
||||
isEmpty,
|
||||
getTreeAncestors,
|
||||
filterTree
|
||||
} from '../utils/helper';
|
||||
import {flattenTree} from '../utils/helper';
|
||||
import {IRendererStore} from '.';
|
||||
import {normalizeOptions, optionValueCompare} from '../components/Select';
|
||||
import find from 'lodash/find';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import {SimpleMap} from '../utils/SimpleMap';
|
||||
import memoize from 'lodash/memoize';
|
||||
import {TranslateFn} from '../locale';
|
||||
import {StoreNode} from './node';
|
||||
import {getStoreById} from './manager';
|
||||
import {toast} from '../components';
|
||||
import {normalizeOptions} from '../utils/normalizeOptions';
|
||||
import {optionValueCompare} from '../utils/optionValueCompare';
|
||||
|
||||
interface IOption {
|
||||
value?: string | number | null;
|
||||
@ -663,7 +654,8 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
return json.data;
|
||||
}
|
||||
|
||||
!silent && toast.info(self.__('FormItem.autoUpdateloadFaild'));
|
||||
!silent &&
|
||||
getEnv(self).notify('info', self.__('FormItem.autoUpdateloadFaild'));
|
||||
|
||||
return;
|
||||
});
|
@ -2,29 +2,29 @@ import {
|
||||
types,
|
||||
getEnv,
|
||||
detach,
|
||||
setLivelynessChecking,
|
||||
setLivelinessChecking,
|
||||
isAlive,
|
||||
Instance
|
||||
} from 'mobx-state-tree';
|
||||
import {iRendererStore, IIRendererStore, SIRendererStore} from './iRenderer';
|
||||
import {ServiceStore} from './service';
|
||||
import {ComboStore} from './combo';
|
||||
import {FormStore} from './form';
|
||||
import {CRUDStore} from './crud';
|
||||
import {TableStore} from './table';
|
||||
import {TableStoreV2} from './table-v2';
|
||||
import {ListStore} from './list';
|
||||
import {ModalStore} from './modal';
|
||||
import {IServiceStore, ServiceStore} from './service';
|
||||
import {ComboStore, IComboStore} from './combo';
|
||||
import {FormStore, IFormStore} from './form';
|
||||
import {CRUDStore, ICRUDStore} from './crud';
|
||||
import {IColumn, IRow, ITableStore, TableStore} from './table';
|
||||
import {IColumnV2, IRowV2, ITableStoreV2, TableStoreV2} from './table-v2';
|
||||
import {IListStore, ListStore} from './list';
|
||||
import {IModalStore, ModalStore} from './modal';
|
||||
import {TranslateFn} from '../locale';
|
||||
import find from 'lodash/find';
|
||||
import {IStoreNode} from './node';
|
||||
import {FormItemStore} from './formItem';
|
||||
import {IStoreNode, StoreNode} from './node';
|
||||
import {FormItemStore, IFormItemStore} from './formItem';
|
||||
import {addStore, getStoreById, getStores, removeStore} from './manager';
|
||||
import {PaginationStore} from './pagination';
|
||||
import {AppStore} from './app';
|
||||
import {IPaginationStore, PaginationStore} from './pagination';
|
||||
import {AppStore, IAppStore} from './app';
|
||||
import {RootStore} from './root';
|
||||
|
||||
setLivelynessChecking(
|
||||
setLivelinessChecking(
|
||||
process.env.NODE_ENV === 'production' ? 'ignore' : 'error'
|
||||
);
|
||||
|
||||
@ -121,3 +121,34 @@ export {iRendererStore, IIRendererStore};
|
||||
export const RegisterStore = function (store: any) {
|
||||
allowedStoreList.push(store as any);
|
||||
};
|
||||
|
||||
export {
|
||||
ServiceStore,
|
||||
IServiceStore,
|
||||
FormStore,
|
||||
IFormStore,
|
||||
ComboStore,
|
||||
IComboStore,
|
||||
CRUDStore,
|
||||
ICRUDStore,
|
||||
TableStore,
|
||||
IColumn,
|
||||
IRow,
|
||||
ITableStore,
|
||||
TableStoreV2,
|
||||
ITableStoreV2,
|
||||
IColumnV2,
|
||||
IRowV2,
|
||||
ListStore,
|
||||
IListStore,
|
||||
ModalStore,
|
||||
IModalStore,
|
||||
FormItemStore,
|
||||
IFormItemStore,
|
||||
PaginationStore,
|
||||
IPaginationStore,
|
||||
AppStore,
|
||||
IAppStore,
|
||||
StoreNode,
|
||||
IStoreNode
|
||||
};
|
@ -52,7 +52,7 @@ export const Column = types
|
||||
.actions(self => ({
|
||||
toggleToggle() {
|
||||
self.toggled = !self.toggled;
|
||||
const table = getParent(self, 2) as ITableStore;
|
||||
const table = getParent(self, 2) as ITableStoreV2;
|
||||
|
||||
if (!table.activeToggaleColumns.length) {
|
||||
self.toggled = true;
|
||||
@ -65,8 +65,8 @@ export const Column = types
|
||||
}
|
||||
}));
|
||||
|
||||
export type IColumn = Instance<typeof Column>;
|
||||
export type SColumn = SnapshotIn<typeof Column>;
|
||||
export type IColumnV2 = Instance<typeof Column>;
|
||||
export type SColumnV2 = SnapshotIn<typeof Column>;
|
||||
|
||||
export const Row = types
|
||||
.model('Row', {
|
||||
@ -87,8 +87,8 @@ export const Row = types
|
||||
})
|
||||
.views(self => ({
|
||||
get checked(): boolean {
|
||||
return (getParent(self, self.depth * 2) as ITableStore).isSelected(
|
||||
self as IRow
|
||||
return (getParent(self, self.depth * 2) as ITableStoreV2).isSelected(
|
||||
self as IRowV2
|
||||
);
|
||||
},
|
||||
|
||||
@ -112,9 +112,9 @@ export const Row = types
|
||||
children = self.children.map(item => item.locals);
|
||||
}
|
||||
|
||||
const parent = getParent(self, 2) as ITableStore;
|
||||
const parent = getParent(self, 2) as ITableStoreV2;
|
||||
return createObject(
|
||||
extendObject((getParent(self, self.depth * 2) as ITableStore).data, {
|
||||
extendObject((getParent(self, self.depth * 2) as ITableStoreV2).data, {
|
||||
index: self.index,
|
||||
// todo 以后再支持多层,目前先一层
|
||||
parent: parent.storeType === Row.name ? parent.data : undefined
|
||||
@ -188,8 +188,8 @@ export const Row = types
|
||||
}
|
||||
}));
|
||||
|
||||
export type IRow = Instance<typeof Row>;
|
||||
export type SRow = SnapshotIn<typeof Row>;
|
||||
export type IRowV2 = Instance<typeof Row>;
|
||||
export type SRowV2 = SnapshotIn<typeof Row>;
|
||||
|
||||
export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
.props({
|
||||
@ -235,7 +235,7 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
return getToggableColumns().filter(item => item.toggled);
|
||||
}
|
||||
|
||||
function getAllFilteredColumns(columns?: Array<SColumn>): Array<any> {
|
||||
function getAllFilteredColumns(columns?: Array<SColumnV2>): Array<any> {
|
||||
if (columns) {
|
||||
return columns
|
||||
.filter(
|
||||
@ -263,7 +263,9 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
}
|
||||
|
||||
function getUnSelectedRows() {
|
||||
return flattenTree<IRow>(self.rows).filter((item: IRow) => !item.checked);
|
||||
return flattenTree<IRowV2>(self.rows).filter(
|
||||
(item: IRowV2) => !item.checked
|
||||
);
|
||||
}
|
||||
|
||||
function getData(superData: any): any {
|
||||
@ -274,7 +276,7 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
});
|
||||
}
|
||||
|
||||
function getRowByIndex(rowIndex: number, levels?: Array<string>): IRow {
|
||||
function getRowByIndex(rowIndex: number, levels?: Array<string>): IRowV2 {
|
||||
if (levels && levels.length > 0) {
|
||||
const index = +(levels.shift() || 0);
|
||||
return getRowByIndex(index, levels);
|
||||
@ -282,12 +284,12 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
return self.rows[rowIndex];
|
||||
}
|
||||
|
||||
function isSelected(row: IRow): boolean {
|
||||
function isSelected(row: IRowV2): boolean {
|
||||
return !!~self.selectedRows.indexOf(row);
|
||||
}
|
||||
|
||||
function getMovedRows() {
|
||||
return flattenTree(self.rows).filter((item: IRow) => item.moved);
|
||||
return flattenTree(self.rows).filter((item: IRowV2) => item.moved);
|
||||
}
|
||||
|
||||
function getMoved() {
|
||||
@ -352,13 +354,13 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
|
||||
get movedRows() {
|
||||
return getMovedRows();
|
||||
},
|
||||
}
|
||||
};
|
||||
})
|
||||
.actions(self => {
|
||||
function updateColumns(columns: Array<SColumn>) {
|
||||
function updateColumns(columns: Array<SColumnV2>) {
|
||||
if (columns && Array.isArray(columns)) {
|
||||
let cols: Array<SColumn> = columns.filter(column => column).concat();
|
||||
let cols: Array<SColumnV2> = columns.filter(column => column).concat();
|
||||
|
||||
cols = cols.map((item, index) => ({
|
||||
...item,
|
||||
@ -375,7 +377,7 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
return;
|
||||
}
|
||||
|
||||
function update(config: Partial<STableStore>) {
|
||||
function update(config: Partial<STableStoreV2>) {
|
||||
config.columnsTogglable !== void 0 &&
|
||||
(self.columnsTogglable = config.columnsTogglable);
|
||||
|
||||
@ -391,11 +393,11 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
}
|
||||
}
|
||||
|
||||
function exchange(fromIndex: number, toIndex: number, item?: IRow) {
|
||||
function exchange(fromIndex: number, toIndex: number, item?: IRowV2) {
|
||||
item = item || self.rows[fromIndex];
|
||||
|
||||
if (item.parentId) {
|
||||
const parent: IRow = self.getRowById(item.parentId) as any;
|
||||
const parent: IRowV2 = self.getRowById(item.parentId) as any;
|
||||
const offset = parent.children.indexOf(item) - fromIndex;
|
||||
toIndex += offset;
|
||||
fromIndex += offset;
|
||||
@ -512,7 +514,7 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
}
|
||||
|
||||
// 尽可能的复用 row
|
||||
function replaceRow(arr: Array<SRow>, reUseRow?: boolean) {
|
||||
function replaceRow(arr: Array<SRowV2>, reUseRow?: boolean) {
|
||||
if (reUseRow === false) {
|
||||
self.rows.replace(arr.map(item => Row.create(item)));
|
||||
return;
|
||||
@ -588,7 +590,7 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
|
||||
const key = keyField || 'children';
|
||||
|
||||
let arr: Array<SRow> = rows.map((item, index) => {
|
||||
let arr: Array<SRowV2> = rows.map((item, index) => {
|
||||
let id = getEntryId ? getEntryId(item, index) : guid();
|
||||
|
||||
return {
|
||||
@ -736,5 +738,5 @@ export const TableStoreV2 = ServiceStore.named('TableStoreV2')
|
||||
};
|
||||
});
|
||||
|
||||
export type ITableStore = Instance<typeof TableStoreV2>;
|
||||
export type STableStore = SnapshotIn<typeof TableStoreV2>;
|
||||
export type ITableStoreV2 = Instance<typeof TableStoreV2>;
|
||||
export type STableStoreV2 = SnapshotIn<typeof TableStoreV2>;
|
@ -2,9 +2,6 @@ import {
|
||||
types,
|
||||
getParent,
|
||||
SnapshotIn,
|
||||
flow,
|
||||
getEnv,
|
||||
getRoot,
|
||||
IAnyModelType,
|
||||
isAlive,
|
||||
Instance
|
||||
@ -26,15 +23,12 @@ import {
|
||||
difference,
|
||||
immutableExtends,
|
||||
extendObject,
|
||||
hasVisibleExpression,
|
||||
filterTree
|
||||
hasVisibleExpression
|
||||
} from '../utils/helper';
|
||||
import {evalExpression} from '../utils/tpl';
|
||||
import {IFormStore} from './form';
|
||||
import {getStoreById} from './manager';
|
||||
|
||||
import type {SchemaObject} from '../Schema';
|
||||
|
||||
/**
|
||||
* 内部列的数量 '__checkme' | '__dragme' | '__expandme'
|
||||
*/
|
@ -35,11 +35,13 @@ interface ThemeConfig {
|
||||
const themes: {
|
||||
[propName: string]: ThemeConfig;
|
||||
} = {
|
||||
default: {}
|
||||
default: {},
|
||||
cxd: {}
|
||||
};
|
||||
|
||||
export function theme(name: string, config: Partial<ThemeConfig>) {
|
||||
themes[name] = {
|
||||
...themes[name],
|
||||
...config
|
||||
};
|
||||
}
|
586
packages/amis-core/src/types.ts
Normal file
586
packages/amis-core/src/types.ts
Normal file
@ -0,0 +1,586 @@
|
||||
// https://json-schema.org/draft-07/json-schema-release-notes.html
|
||||
import type {JSONSchema7} from 'json-schema';
|
||||
import {ListenerAction} from './actions/Action';
|
||||
|
||||
export interface Option {
|
||||
/**
|
||||
* 用来显示的文字
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* 可以用来给 Option 标记个范围,让数据展示更清晰。
|
||||
*
|
||||
* 这个只有在数值展示的时候显示。
|
||||
*/
|
||||
scopeLabel?: string;
|
||||
|
||||
/**
|
||||
* 请保证数值唯一,多个选项值一致会认为是同一个选项。
|
||||
*/
|
||||
value?: any;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* 支持嵌套
|
||||
*/
|
||||
children?: Options;
|
||||
|
||||
/**
|
||||
* 是否可见
|
||||
*/
|
||||
visible?: boolean;
|
||||
|
||||
/**
|
||||
* 最好不要用!因为有 visible 就够了。
|
||||
*
|
||||
* @deprecated 用 visible
|
||||
*/
|
||||
hidden?: boolean;
|
||||
|
||||
/**
|
||||
* 描述,部分控件支持
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 标记后数据延时加载
|
||||
*/
|
||||
defer?: boolean;
|
||||
|
||||
/**
|
||||
* 如果设置了,优先级更高,不设置走 source 接口加载。
|
||||
*/
|
||||
deferApi?: BaseApiObject | string;
|
||||
|
||||
/**
|
||||
* 标记正在加载。只有 defer 为 true 时有意义。内部字段不可以外部设置
|
||||
*/
|
||||
loading?: boolean;
|
||||
|
||||
/**
|
||||
* 只有设置了 defer 才有意义,内部字段不可以外部设置
|
||||
*/
|
||||
loaded?: boolean;
|
||||
|
||||
[propName: string]: any;
|
||||
}
|
||||
export interface Options extends Array<Option> {}
|
||||
export type OptionValue = string | number | null | undefined | Option;
|
||||
|
||||
export interface BaseApiObject {
|
||||
/**
|
||||
* API 发送类型
|
||||
*/
|
||||
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp';
|
||||
|
||||
/**
|
||||
* API 发送目标地址
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* 用来控制携带数据. 当key 为 `&` 值为 `$$` 时, 将所有原始数据打平设置到 data 中. 当值为 $$ 将所有原始数据赋值到对应的 key 中. 当值为 $ 打头时, 将变量值设置到 key 中.
|
||||
*/
|
||||
data?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认数据映射中的key如果带点,或者带大括号,会转成对象比如:
|
||||
*
|
||||
* {
|
||||
* 'a.b': '123'
|
||||
* }
|
||||
*
|
||||
* 经过数据映射后变成
|
||||
* {
|
||||
* a: {
|
||||
* b: '123
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 如果想要关闭此功能,请设置 convertKeyToPath 为 false
|
||||
*/
|
||||
convertKeyToPath?: boolean;
|
||||
|
||||
/**
|
||||
* 用来做接口返回的数据映射。
|
||||
*/
|
||||
responseData?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* 如果 method 为 get 的接口,设置了 data 信息。
|
||||
* 默认 data 会自动附带在 query 里面发送给后端。
|
||||
*
|
||||
* 如果想通过 body 发送给后端,那么请把这个配置成 false。
|
||||
*
|
||||
* 但是,浏览器还不支持啊,设置了只是摆设。除非服务端支持 method-override
|
||||
*/
|
||||
attachDataToQuery?: boolean;
|
||||
|
||||
/**
|
||||
* 发送体的格式
|
||||
*/
|
||||
dataType?: 'json' | 'form-data' | 'form';
|
||||
|
||||
/**
|
||||
* 如果是文件下载接口,请配置这个。
|
||||
*/
|
||||
responseType?: 'blob';
|
||||
|
||||
/**
|
||||
* 携带 headers,用法和 data 一样,可以用变量。
|
||||
*/
|
||||
headers?: {
|
||||
[propName: string]: string | number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置发送条件
|
||||
*/
|
||||
sendOn?: string;
|
||||
|
||||
/**
|
||||
* 默认都是追加模式,如果想完全替换把这个配置成 true
|
||||
*/
|
||||
replaceData?: boolean;
|
||||
|
||||
/**
|
||||
* 是否自动刷新,当 url 中的取值结果变化时,自动刷新数据。
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
autoRefresh?: boolean;
|
||||
|
||||
/**
|
||||
* 当开启自动刷新的时候,默认是 api 的 url 来自动跟踪变量变化的。
|
||||
* 如果你希望监控 url 外的变量,请配置 traceExpression。
|
||||
*/
|
||||
trackExpression?: string;
|
||||
|
||||
/**
|
||||
* 如果设置了值,同一个接口,相同参数,指定的时间(单位:ms)内请求将直接走缓存。
|
||||
*/
|
||||
cache?: number;
|
||||
|
||||
/**
|
||||
* 强制将数据附加在 query,默认只有 api 地址中没有用变量的时候 crud 查询接口才会
|
||||
* 自动附加数据到 query 部分,如果想强制附加请设置这个属性。
|
||||
* 对于那种临时加了个变量但是又不想全部参数写一遍的时候配置很有用。
|
||||
*/
|
||||
forceAppendDataToQuery?: boolean;
|
||||
|
||||
/**
|
||||
* qs 配置项
|
||||
*/
|
||||
qsOptions?: {
|
||||
arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma';
|
||||
indices?: boolean;
|
||||
allowDots?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* autoFillApi 是否显示自动填充错误提示
|
||||
*/
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
export type ClassName =
|
||||
| string
|
||||
| {
|
||||
[propName: string]: boolean | undefined | null | string;
|
||||
};
|
||||
|
||||
export interface ApiObject extends BaseApiObject {
|
||||
config?: {
|
||||
withCredentials?: boolean;
|
||||
cancelExecutor?: (cancel: Function) => void;
|
||||
};
|
||||
graphql?: string;
|
||||
operationName?: string;
|
||||
body?: PlainObject;
|
||||
query?: PlainObject;
|
||||
adaptor?: (payload: object, response: fetcherResult, api: ApiObject) => any;
|
||||
requestAdaptor?: (api: ApiObject) => ApiObject;
|
||||
}
|
||||
export type ApiString = string;
|
||||
export type Api = ApiString | ApiObject;
|
||||
|
||||
export interface fetcherResult {
|
||||
data?: {
|
||||
data: object;
|
||||
status: number;
|
||||
msg: string;
|
||||
msgTimeout?: number;
|
||||
errors?: {
|
||||
[propName: string]: string;
|
||||
};
|
||||
type?: string;
|
||||
[propName: string]: any; // 为了兼容其他返回格式
|
||||
};
|
||||
status: number;
|
||||
headers: object;
|
||||
}
|
||||
|
||||
export interface fetchOptions {
|
||||
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp';
|
||||
successMessage?: string;
|
||||
errorMessage?: string;
|
||||
autoAppend?: boolean;
|
||||
beforeSend?: (data: any) => any;
|
||||
onSuccess?: (json: Payload) => any;
|
||||
onFailed?: (json: Payload) => any;
|
||||
silent?: boolean;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface Payload {
|
||||
ok: boolean;
|
||||
msg: string;
|
||||
msgTimeout?: number;
|
||||
data: any;
|
||||
status: number;
|
||||
errors?: {
|
||||
[propName: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Schema {
|
||||
type: string;
|
||||
detectField?: string;
|
||||
visibleOn?: string;
|
||||
hiddenOn?: string;
|
||||
disabledOn?: string;
|
||||
visible?: boolean;
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
children?: JSX.Element | ((props: any, schema?: any) => JSX.Element) | null;
|
||||
definitions?: Definitions;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface ButtonObject {
|
||||
type: 'submit' | 'button' | 'reset';
|
||||
label?: string;
|
||||
icon?: string;
|
||||
size?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export type SchemaNode = Schema | string | Array<Schema | string>;
|
||||
export interface SchemaArray extends Array<SchemaNode> {}
|
||||
export interface Definitions {
|
||||
[propName: string]: SchemaNode;
|
||||
}
|
||||
export interface ActionObject extends ButtonObject {
|
||||
actionType?:
|
||||
| 'submit'
|
||||
| 'copy'
|
||||
| 'reload'
|
||||
| 'ajax'
|
||||
| 'saveAs'
|
||||
| 'dialog'
|
||||
| 'drawer'
|
||||
| 'jump'
|
||||
| 'link'
|
||||
| 'url'
|
||||
| 'email'
|
||||
| 'close'
|
||||
| 'confirm'
|
||||
| 'add'
|
||||
| 'remove'
|
||||
| 'delete'
|
||||
| 'edit'
|
||||
| 'cancel'
|
||||
| 'next'
|
||||
| 'prev'
|
||||
| 'reset'
|
||||
| 'validate'
|
||||
| 'reset-and-submit'
|
||||
| 'clear'
|
||||
| 'clear-and-submit'
|
||||
| 'toast'
|
||||
| 'goto-step'
|
||||
| 'goto-image'
|
||||
| 'expand'
|
||||
| 'collapse'
|
||||
| 'step-submit'
|
||||
| 'selectAll'
|
||||
| 'changeTabKey';
|
||||
api?: BaseApiObject | string;
|
||||
asyncApi?: BaseApiObject | string;
|
||||
payload?: any;
|
||||
dialog?: SchemaNode;
|
||||
to?: string;
|
||||
target?: string;
|
||||
link?: string;
|
||||
url?: string;
|
||||
cc?: string;
|
||||
bcc?: string;
|
||||
subject?: string;
|
||||
body?: string;
|
||||
mergeData?: boolean;
|
||||
reload?: string;
|
||||
messages?: {
|
||||
success?: string;
|
||||
failed?: string;
|
||||
};
|
||||
feedback?: any;
|
||||
required?: Array<string>;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
pathname: string;
|
||||
search: string;
|
||||
state: any;
|
||||
hash: string;
|
||||
key?: string;
|
||||
query?: any;
|
||||
}
|
||||
|
||||
export interface PlainObject {
|
||||
[propsName: string]: any;
|
||||
}
|
||||
|
||||
export interface RendererData {
|
||||
[propsName: string]: any;
|
||||
__prev?: RendererDataAlias;
|
||||
__super?: RendererData;
|
||||
}
|
||||
type RendererDataAlias = RendererData;
|
||||
|
||||
export type FunctionPropertyNames<T> = {
|
||||
[K in keyof T]: T[K] extends Function ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
// 先只支持 JSONSchema draft07 好了
|
||||
|
||||
export type JSONSchema = JSONSchema7;
|
||||
|
||||
// export type Omit<T, K extends keyof T & any> = Pick<T, Exclude<keyof T, K>>;
|
||||
// export type Override<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
|
||||
// export type ExtractProps<
|
||||
// TComponentOrTProps
|
||||
// > = TComponentOrTProps extends React.ComponentType<infer P> ? P : never;
|
||||
|
||||
/**
|
||||
* 事件跟踪的定义
|
||||
*/
|
||||
export interface EventTrack {
|
||||
/**
|
||||
* 事件类型,目前有以下几种
|
||||
*
|
||||
* api: 所有 fetcher 前调用
|
||||
* url: 打开外部链接,组件有可能是 action 也有可能是 link
|
||||
* link: 打开内部链接
|
||||
* dialog: action 的弹框
|
||||
* drawer: action 的抽出式弹框
|
||||
* copy: action 里的复制
|
||||
* reload: action 里的 reload
|
||||
* email: action 里的 email
|
||||
* prev: action 里的 prev
|
||||
* next: action 里的 next
|
||||
* cancel: action 里的 cancel
|
||||
* close: action 里的 close
|
||||
* submit: 有可能是 action 里的 submit,也有可能是表单提交
|
||||
* confirm: action 里的 confirm
|
||||
* add: action 里的 add
|
||||
* reset: action 里的 reset
|
||||
* reset-and-submit: action 里的 reset-and-submit
|
||||
* formItemChange: 表单项内容变化
|
||||
* formError: 表单验证失败
|
||||
* formSubmit: 表单成功提交,在表单验证成功之后才会触发,这个可能会和 api 重合
|
||||
* tabChange: tab 切换
|
||||
* netError: api 报错
|
||||
*/
|
||||
eventType:
|
||||
| 'api'
|
||||
| 'url'
|
||||
| 'link'
|
||||
| 'dialog'
|
||||
| 'drawer'
|
||||
| 'copy'
|
||||
| 'reload'
|
||||
| 'email'
|
||||
| 'prev'
|
||||
| 'next'
|
||||
| 'cancel'
|
||||
| 'close'
|
||||
| 'submit'
|
||||
| 'confirm'
|
||||
| 'reset'
|
||||
| 'reset-and-submit'
|
||||
| 'formItemChange'
|
||||
| 'tabChange'
|
||||
| 'pageHidden'
|
||||
| 'pageVisible';
|
||||
|
||||
/**
|
||||
* 事件数据
|
||||
*/
|
||||
eventData?: PlainObject | Api;
|
||||
}
|
||||
|
||||
export type ToastLevel = 'info' | 'success' | 'error' | 'warning';
|
||||
export type ToastConf = {
|
||||
position?:
|
||||
| 'top-right'
|
||||
| 'top-center'
|
||||
| 'top-left'
|
||||
| 'bottom-center'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right'
|
||||
| 'center';
|
||||
closeButton: boolean;
|
||||
showIcon?: boolean;
|
||||
timeout?: number;
|
||||
errorTimeout?: number;
|
||||
className?: string;
|
||||
items?: Array<any>;
|
||||
useMobileUI?: boolean;
|
||||
};
|
||||
|
||||
export interface OptionProps {
|
||||
className?: string;
|
||||
multi?: boolean;
|
||||
multiple?: boolean;
|
||||
valueField?: string;
|
||||
labelField?: string;
|
||||
simpleValue?: boolean; // 默认onChange 出去是整个 option 节点,如果配置了 simpleValue 就只包含值。
|
||||
options: Options;
|
||||
loading?: boolean;
|
||||
joinValues?: boolean;
|
||||
extractValue?: boolean;
|
||||
delimiter?: string;
|
||||
clearable?: boolean;
|
||||
resetValue: any;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
creatable?: boolean;
|
||||
pathSeparator?: string;
|
||||
hasError?: boolean;
|
||||
block?: boolean;
|
||||
onAdd?: (
|
||||
idx?: number | Array<number>,
|
||||
value?: any,
|
||||
skipForm?: boolean
|
||||
) => void;
|
||||
editable?: boolean;
|
||||
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
|
||||
removable?: boolean;
|
||||
onDelete?: (value: Option) => void;
|
||||
}
|
||||
|
||||
export type LinkItem = LinkItemProps;
|
||||
interface LinkItemProps {
|
||||
id?: number;
|
||||
label: string;
|
||||
hidden?: boolean;
|
||||
open?: boolean;
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
children?: Array<LinkItem>;
|
||||
path?: string;
|
||||
icon?: string;
|
||||
component?: React.ReactType;
|
||||
}
|
||||
|
||||
export interface NavigationObject {
|
||||
label: string;
|
||||
children?: Array<LinkItem>;
|
||||
prefix?: JSX.Element;
|
||||
affix?: JSX.Element;
|
||||
className?: string;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表达式,语法 `data.xxx > 5`。
|
||||
*/
|
||||
export type SchemaExpression = string;
|
||||
|
||||
/**
|
||||
* css类名,配置字符串,或者对象。
|
||||
*
|
||||
* className: "red"
|
||||
*
|
||||
* 用对象配置时意味着你能跟表达式一起搭配使用,如:
|
||||
*
|
||||
* className: {
|
||||
* "red": "data.progress > 80",
|
||||
* "blue": "data.progress > 60"
|
||||
* }
|
||||
*/
|
||||
export type SchemaClassName =
|
||||
| string
|
||||
| {
|
||||
[propName: string]: boolean | undefined | null | SchemaExpression;
|
||||
};
|
||||
export interface BaseSchemaWithoutType {
|
||||
/**
|
||||
* 容器 css 类名
|
||||
*/
|
||||
className?: SchemaClassName;
|
||||
|
||||
/**
|
||||
* 配合 definitions 一起使用,可以实现无限循环的渲染器。
|
||||
*/
|
||||
$ref?: string;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* 是否禁用表达式
|
||||
*/
|
||||
disabledOn?: SchemaExpression;
|
||||
|
||||
/**
|
||||
* 是否隐藏
|
||||
* @deprecated 推荐用 visible
|
||||
*/
|
||||
hidden?: boolean;
|
||||
|
||||
/**
|
||||
* 是否隐藏表达式
|
||||
* @deprecated 推荐用 visibleOn
|
||||
*/
|
||||
hiddenOn?: SchemaExpression;
|
||||
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
|
||||
visible?: boolean;
|
||||
|
||||
/**
|
||||
* 是否显示表达式
|
||||
*/
|
||||
visibleOn?: SchemaExpression;
|
||||
|
||||
/**
|
||||
* 组件唯一 id,主要用于日志采集
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* 事件动作配置
|
||||
*/
|
||||
onEvent?: {
|
||||
[propName: string]: {
|
||||
weight?: number; // 权重
|
||||
actions: ListenerAction[]; // 执行的动作集
|
||||
};
|
||||
};
|
||||
}
|
@ -85,7 +85,7 @@ function rgbaToHex(color: Color) {
|
||||
return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
|
||||
}
|
||||
|
||||
export default class ColorScale {
|
||||
export class ColorScale {
|
||||
private min: number;
|
||||
private max: number;
|
||||
private alpha: number;
|
||||
@ -136,3 +136,5 @@ export default class ColorScale {
|
||||
return new Color(r, g, b, this.alpha);
|
||||
}
|
||||
}
|
||||
|
||||
export default ColorScale;
|
@ -1,5 +1,5 @@
|
||||
import {JSONSchema} from '../types';
|
||||
import {DataScope} from './DataScope';
|
||||
import type {JSONSchema} from './DataScope';
|
||||
import {guid} from './helper';
|
||||
|
||||
/**
|
@ -1,10 +1,6 @@
|
||||
import type {JSONSchema7} from 'json-schema';
|
||||
import {JSONSchema} from '../types';
|
||||
import {guid, keyToPath, mapTree} from './helper';
|
||||
|
||||
// 先只支持 JSONSchema draft07 好了
|
||||
// https://json-schema.org/draft-07/json-schema-release-notes.html
|
||||
export type JSONSchema = JSONSchema7;
|
||||
|
||||
export class DataScope {
|
||||
// 指向父级
|
||||
parent?: DataScope;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user