first commit

This commit is contained in:
liaoxuezhi 2019-04-30 11:11:25 +08:00
commit f7f071ffbf
403 changed files with 86268 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Files
.DS_Store
.ruby-version
test.sass
npm-debug.log
# Folders
.idea/
.sass-cache
_gh_pages
_site
node_modules
/dist
/lib
/public
/.vscode
/output
/toolkit/amis-renderer
/toolkit/output
/coverage
/package-lock.json

17
.npmignore Normal file
View File

@ -0,0 +1,17 @@
/public
/node_moduels
/mock
/build
/.vscode
server.conf
build.js
BCLOUD
fis-conf.js
/toolkit
/output
/build.sh
/types
/__tests__
/__mocks__
/coverage
/publish.sh

1
BCLOUD Normal file
View File

@ -0,0 +1 @@
BUILD_SUBMITTER -x -e FIS -m baidu/amis/renderer -c "cd baidu/amis/renderer && sh build.sh" -u ./

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright Baidu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
# amis
一种页面渲染器,可以直接基于特定格式的 JSON 配置将页面渲染出来,结合业务方 API 可快速完成各类管理页面的开发。
目前用于百度内部 [AMIS](http://amis.baidu.com) 平台,已有 100+ 部门接入使用,创建 1.2w+ 页面,欢迎大家使用和提建议。
## 快速开始
```
# 安装项目 npm 依赖。
npm i
# 开始编译,把代码产出到刚开启的服务的 webroot 目录。
npm run dev
# 开启 fis3 服务,请通过 http://127.0.0.1:8888/examples/pages/simple 访问。
npm start
```
## 测试
```bash
# 安装依赖
npm i
# 执行测试用率
npm test
# 查看测试用率覆盖率
npm run coverage
```
## 使用文档
为了更好的阅读体验,建议本地运行此项目后,通过 [http://127.0.0.1:8888/v2/docs/getting-started](http://127.0.0.1:8888/v2/docs/getting-started) 阅读。
* [快速开始](/docs/getting_started.md)
* [高级用法](/docs/advanced.md)
* [渲染器手册](/docs/renderers.md)
* [如何使用](/docs/sdk.md)
* [自定义组件](/docs/dev.md)
* [辅助样式](/docs/style.md)
## 如何贡献
请采用 typescript 编写,所有合理的改动、新的公用渲染器、用率或者文档的提交都会被接收。
## 维护者
* [2betop](https://github.com/2betop)
* [RickCole21](https://github.com/RickCole21)
* [catchonme](https://github.com/catchonme)
## 讨论
欢迎提 ISSUE 讨论。

6
__mocks__/styleMock.js Normal file
View File

@ -0,0 +1,6 @@
/**
* @file
*
* 用于 jest 加载 css mock
*/
module.exports = {};

View File

@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`factory unregistered Renderer 1`] = `
<div>
<div
class="Alert Alert--danger"
>
<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>
`;

View File

@ -0,0 +1,25 @@
import React = require('react');
import NotFound from '../../src/components/404';
import * as renderer from 'react-test-renderer';
import {render, fireEvent, cleanup} from 'react-testing-library';
afterEach(cleanup);
test('Components:404 default View', () => {
const component = renderer.create(
<NotFound />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('Components:404 Custom code & messages', () => {
const component = renderer.create(
<NotFound code={500} description="Internal Error" />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

View File

@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Components:404 Custom code & messages 1`] = `
<div
className="container w-xxl w-auto-xs"
>
<div
className="text-center m-b-lg"
>
<h1
className="text-shadow text-white"
>
500
</h1>
<div
className="text-danger"
>
Internal Error
</div>
</div>
</div>
`;
exports[`Components:404 default View 1`] = `
<div
className="container w-xxl w-auto-xs"
>
<div
className="text-center m-b-lg"
>
<h1
className="text-shadow text-white"
>
404
</h1>
</div>
</div>
`;

View File

@ -0,0 +1,46 @@
import {
registerRenderer,
unRegisterRenderer,
RendererProps,
render as amisRender
} from '../src/factory';
import React = require('react');
import {render, fireEvent, cleanup} from 'react-testing-library';
test('factory unregistered Renderer', () => {
const {
container,
} = render(amisRender({
type: 'my-renderer',
a: 23
}));
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);
});

23
__tests__/helper.tsx Normal file
View File

@ -0,0 +1,23 @@
import { RenderOptions } from "../src/factory";
// jest.useFakeTimers 会修改 global 的 setTimeout 所以需要把原始的记录下来。
const timerFn = setTimeout;
export function wait(duration:number, fn?:Function) {
return new Promise((resolve) => {
timerFn(() => {
fn && fn();
resolve();
}, duration);
});
}
export function makeEnv(env?:Partial<RenderOptions>):RenderOptions {
return {
session: 'test-case',
isCancel: () => false,
notify: (msg:string) => null,
jumpTo: (to:string) => console.info('Now should jump to ' + to),
alert: (msg) => console.info(`Alert: ${msg}`),
...env
}
}

View File

@ -0,0 +1,118 @@
import React = require('react');
import Action from '../../src/renderers/Action';
import * as renderer from 'react-test-renderer';
import {render, fireEvent, cleanup} from 'react-testing-library';
import '../../src/themes/default';
afterEach(cleanup);
test('Renderers:Action MenuItem changes class when actived & disabled', () => {
const component = renderer.create(
<Action isMenuItem className="a" label="123" />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.update(
<Action isMenuItem className="a" label="233" active />
);
tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.update(
<Action isMenuItem className="a" label="233" active disabled />
);
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('Renderers:Action MenuItem display icon', () => {
const component = renderer.create(
<Action isMenuItem className="a" label="123" />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.update(
<Action isMenuItem className="a" label="123" icon="fa fa-cloud" />
);
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('Renderers:Action [actionType = "link"] show active class', () => {
const isCurrentUrl = (link:string) => link === "b";
const component = renderer.create(
<Action actionType="link" link="a" className="a" label="123" isCurrentUrl={isCurrentUrl} />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.update(
<Action actionType="link" link="b" className="a" label="123" isCurrentUrl={isCurrentUrl} />
);
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('Renderers:Action custom activeClass', () => {
const component = renderer.create(
<Action className="a" label="123" activeClassName="custom-active" />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.update(
<Action className="a" label="123" activeClassName="custom-active" active />
);
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('Renderers:Action onAction called?', () => {
const onAction = jest.fn();
const {getByText} = render(
<Action className="a" label="123" onAction={onAction}></Action>
);
fireEvent.click(getByText(/123/));
expect(onAction).toHaveBeenCalled();
});
test('Renderers:Action disabled onAction called?', () => {
const onAction = jest.fn();
const {getByText} = render(
<Action disabled className="a" label="123" onAction={onAction}></Action>
);
fireEvent.click(getByText(/123/));
expect(onAction).not.toHaveBeenCalled();
});
test('Renderers:Action onClick cancel onAction?', () => {
const onAction = jest.fn();
const onClick = jest.fn(e => e.preventDefault());
const {getByText} = render(
<Action
isMenuItem
className="a"
label="123"
onClick={onClick}
onAction={onAction}>
</Action>
);
fireEvent.click(getByText(/123/));
expect(onClick).toHaveBeenCalled();
expect(onAction).not.toHaveBeenCalled();
});

View File

@ -0,0 +1,5 @@
test('todo', () => {
});

View File

@ -0,0 +1,750 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Renderer:Form 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
Label
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="123"
/>
</div>
</div>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form 2`] = `
Object {
"config": Object {
"errorMessage": "保存失败",
"method": "post",
"onSuccess": [Function],
"successMessage": "保存成功",
},
"data": Object {
"a": "123",
},
"method": "post",
"url": "/api/xxx",
}
`;
exports[`Renderer:Form initApi 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
A
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="1"
/>
</div>
</div>
</div>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
B
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="b"
placeholder=""
type="text"
value="2"
/>
</div>
</div>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--primary"
type="submit"
>
<span>
提交
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form sendOn:true 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
A
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="1"
/>
</div>
</div>
</div>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
B
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="b"
placeholder=""
type="text"
value="2"
/>
</div>
</div>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--primary"
type="submit"
>
<span>
提交
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:onValidate 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal is-error"
>
<label
class="a-Form-label"
>
<span>
A
</span>
</label>
<div
class="a-Form-control is-error a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="1"
/>
</div>
</div>
<ul
class="a-Form-feedback"
>
<li>
a is wrong
</li>
</ul>
</div>
<div
class="a-Form-item a-Form-item--normal is-error"
>
<label
class="a-Form-label"
>
<span>
B
</span>
</label>
<div
class="a-Form-control is-error a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="b"
placeholder=""
type="text"
value="2"
/>
</div>
</div>
<ul
class="a-Form-feedback"
>
<li>
b is wrong
</li>
<li>
b is wrong 2
</li>
</ul>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:onValidate 2`] = `
Object {
"a": 1,
"b": 2,
}
`;
exports[`Renderer:Form:onValidate 3`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
A
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="1"
/>
</div>
</div>
</div>
<div
class="a-Form-item a-Form-item--normal"
>
<label
class="a-Form-label"
>
<span>
B
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="b"
placeholder=""
type="text"
value="2"
/>
</div>
</div>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:onValidate 4`] = `
Object {
"a": 1,
"b": 2,
}
`;
exports[`Renderer:Form:remoteValidate 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal is-error"
>
<label
class="a-Form-label"
>
<span>
Label
</span>
</label>
<div
class="a-Form-control is-error a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value=""
/>
</div>
</div>
<ul
class="a-Form-feedback"
>
<li>
这个字段服务端验证失败
</li>
</ul>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:valdiate 1`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal is-error is-required"
>
<label
class="a-Form-label"
>
<span>
Label
<span
class="a-Form-star"
>
*
</span>
</span>
</label>
<div
class="a-Form-control is-error a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value=""
/>
</div>
</div>
<ul
class="a-Form-feedback"
>
<li>
这是必填项
</li>
</ul>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:valdiate 2`] = `
<div>
<div
class="a-Panel a-Panel--default a-Panel--form"
>
<div
class="a-Panel-heading"
>
<h3
class="a-Panel-title"
>
<span
class="a-TplField"
>
The form
</span>
</h3>
</div>
<div
class="a-Panel-body"
>
<form
class="a-Form a-Form--normal"
novalidate=""
>
<div
class="a-Form-item a-Form-item--normal is-required"
>
<label
class="a-Form-label"
>
<span>
Label
<span
class="a-Form-star"
>
*
</span>
</span>
</label>
<div
class="a-Form-control a-TextControl"
>
<div
class="a-TextControl-input"
>
<input
autocomplete="off"
name="a"
placeholder=""
type="text"
value="123"
/>
</div>
</div>
</div>
</form>
</div>
<div>
<div
class="a-Panel-btnToolbar a-Panel-footer"
>
<button
class="a-Button a-Button--default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:Form:valdiate 3`] = `
Object {
"a": "123",
}
`;

View File

@ -0,0 +1,456 @@
import React = require('react');
import PageRenderer from '../../../src/renderers/Form';
import * as renderer from 'react-test-renderer';
import {render, fireEvent, cleanup, getByText} from 'react-testing-library';
import '../../../src/themes/default';
import {
render as amisRender
} from '../../../src/index';
import { wait, makeEnv } from '../../helper';
import { clearStoresCache } from '../../../src/factory';
import {
createMemoryHistory
} from 'history';
// mock getComputedStyle
Object.defineProperty(window, 'getComputedStyle', {
value: () => ({
getPropertyValue: (prop) => {
return '';
}
})
});
afterEach(() => {
cleanup();
clearStoresCache();
});
test('Renderer:Form', async () => {
const resultPromise = Promise.resolve({
data: {
status: 0,
msg: 'ok'
}
});
const fetcher = jest.fn().mockImplementation(() => resultPromise);
const {
container,
getByText
} = render(amisRender({
type: 'form',
api: '/api/xxx',
controls: [
{
type: 'text',
name: 'a',
label: 'Label',
value: '123'
}
],
title: 'The form',
actions: [
{
type: 'submit',
label: 'Submit'
}
]
}, {}, makeEnv({
fetcher
})));
expect(container).toMatchSnapshot();
fireEvent.click(getByText('Submit'));
await resultPromise;
await wait(100);
expect(fetcher).toHaveBeenCalled();
expect(fetcher.mock.calls[0][0]).toMatchSnapshot();
});
test('Renderer:Form:valdiate', async () => {
const notify = jest.fn();
const onSubmit = jest.fn();
const {
container,
getByText,
} = render(amisRender({
type: 'form',
controls: [
{
type: 'text',
name: 'a',
required: true,
label: 'Label'
}
],
title: 'The form',
actions: [
{
type: 'submit',
label: 'Submit'
}
]
}, {
onSubmit
}, makeEnv({
notify
})));
fireEvent.click(getByText('Submit'));
expect(container).toMatchSnapshot();
expect(onSubmit).not.toHaveBeenCalled();
await wait(100);
expect(notify).toHaveBeenCalledWith('error', '表单验证失败,请仔细检查');
const input = container.querySelector('input[name=a]');
expect(input).toBeTruthy();
fireEvent.change(input, {
target: {
value: '123'
}
});
await wait(300); // 有 250 秒左右的节流
fireEvent.click(getByText('Submit'));
expect(container).toMatchSnapshot();
await wait(100);
expect(onSubmit).toHaveBeenCalled();
expect(onSubmit.mock.calls[0][0]).toMatchSnapshot();
});
test('Renderer:Form:remoteValidate', async () => {
const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() => Promise.resolve({
data: {
status: 422,
msg: '服务端验证失败',
errors: {
a: '这个字段服务端验证失败'
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
api: '/api/xxx',
controls: [
{
type: 'text',
name: 'a',
label: 'Label'
}
],
title: 'The form',
actions: [
{
type: 'submit',
label: 'Submit'
}
]
}, {
}, makeEnv({
notify,
fetcher
})));
fireEvent.click(getByText('Submit'));
await wait(100);
expect(container).toMatchSnapshot();
});
test('Renderer:Form:onValidate', async () => {
const notify = jest.fn();
const onSubmit = jest.fn();
const onValidate = jest.fn()
.mockImplementationOnce(() => ({
a: 'a is wrong',
b: [
'b is wrong',
'b is wrong 2'
]
}))
.mockImplementationOnce(() => ({
a: '',
b: ''
}))
const {
container,
getByText,
} = render(amisRender({
type: 'form',
controls: [
{
type: 'text',
name: 'a',
label: 'A',
value: 1,
},
{
type: 'text',
name: 'b',
label: 'B',
value: 2
}
],
title: 'The form',
actions: [
{
type: 'submit',
label: 'Submit'
}
]
}, {
onSubmit,
onValidate
}, makeEnv({
notify
})));
fireEvent.click(getByText('Submit'));
await wait(100);
expect(container).toMatchSnapshot();
expect(onSubmit).not.toHaveBeenCalled();
expect(onValidate).toHaveBeenCalled();
expect(onValidate.mock.calls[0][0]).toMatchSnapshot();
await wait(100);
expect(notify).toHaveBeenCalledWith('error', '表单验证失败,请仔细检查');
fireEvent.click(getByText('Submit'));
await wait(100);
expect(container).toMatchSnapshot();
expect(onSubmit).toHaveBeenCalled();
expect(onSubmit.mock.calls[0][0]).toMatchSnapshot();
});
test('Renderer:Form initApi', async () => {
const notify = jest.fn();
let p0;
const fetcher = jest.fn().mockImplementation(() => p0 = Promise.resolve({
data: {
status: 0,
data: {
a: 1,
b: 2
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
initApi: '/api/xxx',
controls: [
{
type: 'text',
name: 'a',
label: 'A'
},
{
type: 'text',
name: 'b',
label: 'B'
}
],
title: 'The form'
}, {
}, makeEnv({
notify,
fetcher
})));
// fetch 调用了,所有 initApi 接口调用了
expect(fetcher).toHaveBeenCalled();
await p0;
// 通过 snapshot 可断定 initApi 返回值已经作用到了表单项上。
expect(container).toMatchSnapshot();
});
test('Renderer:Form initFetch:false', async () => {
const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() => Promise.resolve({
data: {
status: 0,
data: {
a: 1,
b: 2
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
initApi: '/api/xxx',
initFetch: false,
controls: [
{
type: 'text',
name: 'a',
label: 'A'
},
{
type: 'text',
name: 'b',
label: 'B'
}
],
title: 'The form'
}, {
}, makeEnv({
notify,
fetcher
})));
expect(fetcher).not.toHaveBeenCalled();
});
test('Renderer:Form initFetchOn:false', async () => {
const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() => Promise.resolve({
data: {
status: 0,
data: {
a: 1,
b: 2
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
initApi: '/api/xxx',
initFetchOn: 'this.goFetch',
controls: [
{
type: 'text',
name: 'a',
label: 'A'
},
{
type: 'text',
name: 'b',
label: 'B'
}
],
title: 'The form'
}, {
data: {
goFetch: false
}
}, makeEnv({
notify,
fetcher
})));
expect(fetcher).not.toHaveBeenCalled();
});
test('Renderer:Form sendOn:false', async () => {
const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() => Promise.resolve({
data: {
status: 0,
data: {
a: 1,
b: 2
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
initApi: {
method: 'get',
url: '/api/xxx',
sendOn: 'this.goFetch'
},
controls: [
{
type: 'text',
name: 'a',
label: 'A'
},
{
type: 'text',
name: 'b',
label: 'B'
}
],
title: 'The form'
}, {
data: {
goFetch: false
}
}, makeEnv({
notify,
fetcher
})));
expect(fetcher).not.toHaveBeenCalled();
});
test('Renderer:Form sendOn:true', async () => {
const notify = jest.fn();
let p0;
const fetcher = jest.fn().mockImplementation(() => p0 = Promise.resolve({
data: {
status: 0,
data: {
a: 1,
b: 2
}
}
}));
const {
container,
getByText,
} = render(amisRender({
type: 'form',
initApi: {
method: 'get',
url: '/api/xxx',
sendOn: 'this.goFetch'
},
controls: [
{
type: 'text',
name: 'a',
label: 'A'
},
{
type: 'text',
name: 'b',
label: 'B'
}
],
title: 'The form'
}, {
data: {
goFetch: true
}
}, makeEnv({
notify,
fetcher
})));
expect(fetcher).toHaveBeenCalled();
await p0;
expect(container).toMatchSnapshot();
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Renderers:Action [actionType = "link"] show active class 1`] = `
<button
className="a-Button a-Button--default a"
onClick={[Function]}
type="button"
>
<span>
123
</span>
</button>
`;
exports[`Renderers:Action [actionType = "link"] show active class 2`] = `
<button
className="a-Button a-Button--default a is-active"
onClick={[Function]}
type="button"
>
<span>
123
</span>
</button>
`;
exports[`Renderers:Action MenuItem changes class when actived & disabled 1`] = `
<a
className="a"
onClick={[Function]}
>
123
</a>
`;
exports[`Renderers:Action MenuItem changes class when actived & disabled 2`] = `
<a
className="a is-active"
onClick={[Function]}
>
233
</a>
`;
exports[`Renderers:Action MenuItem changes class when actived & disabled 3`] = `
<a
className="a is-active is-disabled"
onClick={[Function]}
>
233
</a>
`;
exports[`Renderers:Action MenuItem display icon 1`] = `
<a
className="a"
onClick={[Function]}
>
123
</a>
`;
exports[`Renderers:Action MenuItem display icon 2`] = `
<a
className="a"
onClick={[Function]}
>
123
<i
className="a-Button-icon fa fa-cloud"
/>
</a>
`;
exports[`Renderers:Action custom activeClass 1`] = `
<button
className="a-Button a-Button--default a"
onClick={[Function]}
type="button"
>
<span>
123
</span>
</button>
`;
exports[`Renderers:Action custom activeClass 2`] = `
<button
className="a-Button a-Button--default a custom-active"
onClick={[Function]}
type="button"
>
<span>
123
</span>
</button>
`;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,128 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`store:index 1`] = `
Object {
"storeType": "RendererStore",
"stores": Object {},
}
`;
exports[`store:index 2`] = `
Object {
"storeType": "RendererStore",
"stores": Object {
"1": Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "/xxx",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
},
}
`;
exports[`store:index 3`] = `
Object {
"storeType": "RendererStore",
"stores": Object {
"1": Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "/xxx",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
"2": Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "2",
"initializing": false,
"msg": "",
"parentId": "1",
"path": "/yyy",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
},
}
`;
exports[`store:index 4`] = `
Object {
"storeType": "RendererStore",
"stores": Object {
"1": Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "/xxx",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
},
}
`;

View File

@ -0,0 +1,138 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`store:ServiceStore 1`] = `
Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
}
`;
exports[`store:ServiceStore fetchInitData failed 1`] = `
Array [
Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": true,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"updatedAt": 0,
},
]
`;
exports[`store:ServiceStore fetchInitData success 1`] = `
Array [
Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": true,
"hasRemoteData": false,
"id": "1",
"initializing": false,
"msg": "",
"parentId": "",
"path": "",
"pristine": Object {},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
},
Object {
"action": undefined,
"busying": false,
"checking": false,
"data": Object {
"a": 1,
"b": 2,
},
"dialogData": undefined,
"dialogOpen": false,
"drawerData": undefined,
"drawerOpen": false,
"error": false,
"fetching": false,
"hasRemoteData": true,
"id": "1",
"initializing": false,
"msg": "undefined",
"parentId": "",
"path": "",
"pristine": Object {
"a": 1,
"b": 2,
},
"saving": false,
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
},
]
`;

View File

@ -0,0 +1,54 @@
import {
RendererStore
} from '../../src/store/index';
import { getSnapshot, getEnv } from 'mobx-state-tree';
import { ServiceStore } from '../../src/store/service';
test('store:index', () => {
const store = RendererStore.create({});
expect(getSnapshot(store)).toMatchSnapshot();
const serviceStore = ServiceStore.create({
path: '/xxx',
storeType: ServiceStore.name,
id: '1'
});
store.addStore(serviceStore);
expect(getSnapshot(store)).toMatchSnapshot();
const serviceStore2 = ServiceStore.create({
path: '/yyy',
storeType: ServiceStore.name,
id: '2',
parentId: '1'
});
store.addStore(serviceStore2);
expect(getSnapshot(store)).toMatchSnapshot();
expect(serviceStore2.parentStore).toEqual(serviceStore);
store.removeStore(serviceStore2);
expect(getSnapshot(store)).toMatchSnapshot();
});
test('store:index env', () => {
const fetcher = jest.fn();
const notify = jest.fn();
const isCancel = jest.fn(() => false);
const store = RendererStore.create({}, {
fetcher,
notify,
isCancel
});
expect(store.fetcher).toBe(fetcher);
expect(store.notify).toBe(notify);
expect(store.isCancel).toBe(isCancel);
});

View File

@ -0,0 +1,73 @@
import { getSnapshot, getEnv, onSnapshot } from 'mobx-state-tree';
import { ServiceStore } from '../../src/store/service';
import { RendererStore } from '../../src/store';
import omit = require('lodash/omit');
test('store:ServiceStore', () => {
const store = ServiceStore.create({
id: '1',
storeType: ServiceStore.name
});
expect(getSnapshot(store)).toMatchSnapshot();
});
test('store:ServiceStore fetchInitData success', async () => {
const fetcher = jest.fn().mockImplementationOnce(() => Promise.resolve({
ok: true,
data: {
a: 1,
b: 2
}
}));
const mainStore = RendererStore.create({}, {
fetcher
});
const states:Array<any> = [];
const store = ServiceStore.create({
id: '1',
storeType: ServiceStore.name
});
mainStore.addStore(store);
onSnapshot(store, (snapshot) => states.push(snapshot));
await store.fetchInitData('/api/xxx');
const ignoreUdatedAt = states.map(snapshot => omit(snapshot, ['updatedAt']));
expect(ignoreUdatedAt).toMatchSnapshot();
expect(states.length).toBe(2);
expect(states[1].updatedAt).not.toEqual(states[0].updatedAt);
});
test('store:ServiceStore fetchInitData failed', async () => {
const fetcher = jest.fn().mockImplementationOnce(() => Promise.reject('Network Error'));
const notify = jest.fn();
const isCancel = jest.fn(() => false);
const mainStore = RendererStore.create({}, {
fetcher,
notify,
isCancel
});
const states:Array<any> = [];
const store = ServiceStore.create({
id: '1',
storeType: ServiceStore.name
});
mainStore.addStore(store);
onSnapshot(store, (snapshot) => states.push(snapshot));
await store.fetchInitData('/api/xxx');
expect(states).toMatchSnapshot();
expect(notify).toHaveBeenCalled();
expect(notify).toHaveBeenLastCalledWith("error", "Network Error");
expect(isCancel).toHaveBeenCalled();
});

146
__tests__/utils/api.test.ts Normal file
View File

@ -0,0 +1,146 @@
import {
buildApi,
isApiOutdated
} from '../../src/utils/api';
test('api:buildApi', () => {
expect(buildApi('/api/xxx')).toMatchObject({
method: 'get',
url: '/api/xxx'
});
expect(buildApi('get:/api/xxx')).toMatchObject({
method: 'get',
url: '/api/xxx'
});
expect(buildApi('delete:/api/xxx')).toMatchObject({
method: 'delete',
url: '/api/xxx'
});
expect(buildApi('/api/xxx?a=${a}&b=${b}', {
a: 1,
b: 2
})).toMatchObject({
method: 'get',
url: '/api/xxx?a=1&b=2'
});
expect(buildApi({
method: 'get',
url: '/api/xxx?a=${a}&b=${b}'
}, {
a: 1,
b: 2
})).toMatchObject({
method: 'get',
url: '/api/xxx?a=1&b=2'
});
expect(buildApi('/api/xxx?a=${a}', {
a: '&'
})).toMatchObject({
method: 'get',
url: '/api/xxx?a=' + encodeURIComponent('&')
});
});
test('api:buildApi:dataMapping', () => {
expect(buildApi({
method: 'post',
url: '/api/xxx',
data: {
a: 1,
b: '${b}'
}
}, {
b: 2
})).toMatchObject({
method: 'post',
url: '/api/xxx',
data: {
a: 1,
b: 2
}
});
expect(buildApi({
method: 'post',
url: '/api/xxx',
headers: {
a: 1,
b: '${b}'
}
}, {
b: 2
})).toMatchObject({
method: 'post',
url: '/api/xxx',
headers: {
a: 1,
b: 2
}
});
});
test('api:buildApi:autoAppend', () => {
expect(buildApi({
method: 'get',
url: '/api/xxx',
}, {
a: 1,
b: 2
}, {
autoAppend: true
})).toMatchObject({
method: 'get',
url: '/api/xxx?a=1&b=2'
});
});
test('api:isApiOutdated', () => {
expect(isApiOutdated('/api/xxx?a=${a}', '/api/xxx?a=${a}', {
a: 1,
b: 0
}, {
a: 1,
b: 2,
})).toBeFalsy();
expect(isApiOutdated('/api/xxx?a=${a}', '/api/xxx?a=${a}', {
a: 1,
b: 0
}, {
a: 2,
b: 2,
})).toBeTruthy();
expect(isApiOutdated('/api/xxx', '/api/xxx', {
a: 1,
b: 0
}, {
a: 2,
b: 2,
})).toBeFalsy();
expect(isApiOutdated({
method: 'get',
url: '/api/xxx?a=${a}'
}, {
method: 'get',
url: '/api/xxx?a=${a}',
sendOn: 'this.b === 0'
}, {
a: 1,
b: 0
}, {
a: 2,
b: 2,
})).toBeFalsy();
});

View File

@ -0,0 +1,22 @@
import {
prettyBytes,
escapeHtml,
formatDuration
} from '../../src/utils/tpl-builtin';
test('tpl-builtin:prettyBytes', () => {
expect(prettyBytes(1024)).toEqual('1.02 kB');
expect(prettyBytes(1024000)).toEqual('1.02 MB');
});
test('tpl-builtin:escapeHtml', () => {
expect(escapeHtml('<div id="1">Hello&world</div>')).toEqual('&lt;div id=&quot;1&quot;&gt;Hello&amp;world&lt;&#x2F;div&gt;');
});
test('tpl-builtin:formatDuration', () => {
expect(formatDuration(1)).toEqual('1秒');
expect(formatDuration(61)).toEqual('1分1秒');
expect(formatDuration(233233)).toEqual('3天17时47分13秒');
})

View File

@ -0,0 +1,17 @@
import {
filter
} from '../../src/utils/tpl';
import '../../src/utils/tpl-builtin';
import '../../src/utils/tpl-lodash';
test('filter', () => {
expect(filter('xxx_a=${a}&b=${b}', {
a: 1,
b: 2
})).toEqual('xxx_a=1&b=2');
expect(filter('xxx_a=<%= data.a%>&b=<%= data.b%>', {
a: 1,
b: 2
})).toEqual('xxx_a=1&b=2');
})

View File

@ -0,0 +1,429 @@
import {
validate,
str2rules
} from '../../src/utils/validations';
test('validation:isRequired valid', () => {
expect(validate('somestring', {}, {
isRequired: true
}, {
isRequired: 'This is required!'
})).toMatchObject([]);
});
test('validation:isRequired invalid', () => {
expect(validate('', {}, {
isRequired: true
}, {
isRequired: 'This is required!'
})).toMatchObject(['This is required!']);
});
test('validation:isEmail valid', () => {
expect(validate('abc@gmail.com', {}, {
isEmail: true
}, {
isEmail: 'Email 格式不正确'
})).toMatchObject([]);
});
test('validation:isEmail invalid', () => {
expect(validate('somestring', {}, {
isEmail: true
}, {
isEmail: 'Email 格式不正确'
})).toMatchObject(['Email 格式不正确']);
});
test('validation:isUrl valid', () => {
expect(validate('http://www.baidu.com', {}, {
isUrl: true
}, {
isUrl: 'Url 格式不正确'
})).toMatchObject([]);
});
test('validation:isUrl invalid', () => {
expect(validate('somestring', {}, {
isUrl: true
}, {
isUrl: 'Url 格式不正确'
})).toMatchObject(['Url 格式不正确']);
});
test('validation:isInt valid', () => {
expect(validate(1, {}, {
isInt: true
}, {
isInt: '请输入整形数字'
})).toMatchObject([]);
});
test('validation:isInt invalid', () => {
expect(validate(1.1, {}, {
isInt: true
}, {
isInt: '请输入整形数字'
})).toMatchObject(['请输入整形数字']);
});
test('validation:isAlpha valid', () => {
expect(validate('a', {}, {
isAlpha: true
}, {
isAlpha: '请输入字母'
})).toMatchObject([]);
});
test('validation:isAlpha invalid', () => {
expect(validate('%', {}, {
isAlpha: true
}, {
isAlpha: '请输入字母'
})).toMatchObject(['请输入字母']);
});
test('validation:isNumeric valid', () => {
expect(validate(1.1, {}, {
isNumeric: true
}, {
isNumeric: '请输入数字'
})).toMatchObject([]);
});
test('validation:isNumeric invalid', () => {
expect(validate('a', {}, {
isNumeric: true
}, {
isNumeric: '请输入数字'
})).toMatchObject(['请输入数字']);
});
test('validation:isAlphanumeric Alpha valid', () => {
expect(validate('a', {}, {
isAlphanumeric: true
}, {
isAlphanumeric: '请输入数字'
})).toMatchObject([]);
});
test('validation:isAlphanumeric numeric valid', () => {
expect(validate(1, {}, {
isAlphanumeric: true
}, {
isAlphanumeric: '请输入字母或者数字'
})).toMatchObject([]);
});
test('validation:isAlphanumeric invalid', () => {
expect(validate('%', {}, {
isAlphanumeric: true
}, {
isAlphanumeric: '请输入字母或者数字'
})).toMatchObject(['请输入字母或者数字']);
});
test('validation:isFloat valid', () => {
expect(validate(1.1, {}, {
isFloat: true
}, {
isFloat: '请输入浮点型数值'
})).toMatchObject([]);
});
test('validation:isFloat invalid', () => {
expect(validate('a', {}, {
isFloat: true
}, {
isFloat: '请输入浮点型数值'
})).toMatchObject(['请输入浮点型数值']);
});
test('validation:isWords valid', () => {
expect(validate('baidu', {}, {
isWords: true
}, {
isWords: '请输入字母'
})).toMatchObject([]);
});
test('validation:isWords invalid', () => {
expect(validate('%', {}, {
isWords: true
}, {
isWords: '请输入字母'
})).toMatchObject(['请输入字母']);
});
test('validation:isUrlPath valid', () => {
expect(validate('baidu-fex_team', {}, {
isUrlPath: true
}, {
isUrlPath: '只能输入字母、数字、`-` 和 `_`'
})).toMatchObject([]);
});
test('validation:isUrlPath invalid', () => {
expect(validate('baidu&fex%team', {}, {
isUrlPath: true
}, {
isUrlPath: '只能输入字母、数字、`-` 和 `_`'
})).toMatchObject(['只能输入字母、数字、`-` 和 `_`']);
});
test('validation:minLength valid', () => {
expect(validate('abcdef', {}, {
minLength: 5
}, {
minLength: '请至少输入 5 个字符。'
})).toMatchObject([]);
});
test('validation:minLength invalid', () => {
expect(validate('abcd', {}, {
minLength: 5
}, {
minLength: '至少输入 5 个字符。'
})).toMatchObject(['至少输入 5 个字符。']);
});
test('validation:maxLength valid', () => {
expect(validate('abcde', {}, {
maxLength: 5
}, {
maxLength: '请不要输入 5 个字符以上'
})).toMatchObject([]);
});
test('validation:maxLength invalid', () => {
expect(validate('abcded', {}, {
maxLength: 5
}, {
maxLength: '请不要输入 5 个字符以上'
})).toMatchObject(['请不要输入 5 个字符以上']);
});
test('validation:minimum valid', () => {
expect(validate(6, {}, {
minimum: 5
}, {
minimum: '当前输入值低于最小值 5请检查'
})).toMatchObject([]);
});
test('validation:minimum invalid', () => {
expect(validate(4, {}, {
minimum: 5
}, {
minimum: '当前输入值低于最小值 5请检查'
})).toMatchObject(['当前输入值低于最小值 5请检查']);
});
test('validation:maximum valid', () => {
expect(validate(5, {}, {
maximum: 5
}, {
maximum: '当前输入值超出最大值 5请检查'
})).toMatchObject([]);
});
test('validation:maximum invalid', () => {
expect(validate(6, {}, {
maximum: 5
}, {
maximum: '当前输入值超出最大值 5请检查'
})).toMatchObject(['当前输入值超出最大值 5请检查']);
});
test('validation:isJson valid', () => {
expect(validate('{ "type": "select", "options": [ { "label": "A", "value": "a" } ] }', {}, {
isJson: true
}, {
isJson: '请检查 Json 格式'
})).toMatchObject([]);
});
test('validation:isJson invalid', () => {
expect(validate('string', {}, {
isJson: true
}, {
isJson: '请检查 Json 格式'
})).toMatchObject(['请检查 Json 格式']);
});
test('validation:isLength valid', () => {
expect(validate('abcde', {}, {
isLength: 5
}, {
isLength: '请输入长度为 5 的内容'
})).toMatchObject([]);
});
test('validation:isLength invalid', () => {
expect(validate('abc', {}, {
isLength: 5
}, {
isLength: '请输入长度为 5 的内容'
})).toMatchObject(['请输入长度为 5 的内容']);
});
test('validation:notEmptyString valid', () => {
expect(validate('abc', {}, {
notEmptyString: true
}, {
notEmptyString: '请不要全输入空白字符'
})).toMatchObject([]);
});
test('validation:notEmptyString invalid', () => {
expect(validate(' ', {}, {
notEmptyString: true
}, {
notEmptyString: '请不要全输入空白字符'
})).toMatchObject(['请不要全输入空白字符']);
});
test('validation:equalsField valid', () => {
expect(validate('a', {
a: 'a'
}, {
equalsField: 'a'
}, {
equalsField: '输入的数据与 a 值不一致'
})).toMatchObject([]);
});
test('validation:equalsField invalid', () => {
expect(validate('b', {
a: 'a'
}, {
equalsField: 'a'
}, {
equalsField: '输入的数据与 a 值不一致'
})).toMatchObject(['输入的数据与 a 值不一致']);
});
test('validation:equals valid', () => {
expect(validate('a', {}, {
equals: 'a'
}, {
equals: '输入的数据与 a 不一致'
})).toMatchObject([]);
});
test('validation:equals invalid', () => {
expect(validate('b', {}, {
equals: 'a'
}, {
equals: '输入的数据与 a 不一致'
})).toMatchObject(['输入的数据与 a 不一致']);
});
test('validation:multipleRules invalid', () => {
expect(validate('abc', {}, {
isUrl: true,
isInt: true
})).toMatchObject(['Url 格式不正确', '请输入整形数字']);
});
test('validation:matchRegexp valid', () => {
expect(validate('abcd', {}, {
matchRegexp: '/^abc/'
}, {
matchRegexp: '请输入abc开头的好么'
})).toMatchObject([]);
});
test('validation:matchRegexp invalid', () => {
expect(validate('cba', {}, {
matchRegexp: '/^abc/'
}, {
matchRegexp: '请输入abc开头的好么'
})).toMatchObject(['请输入abc开头的好么']);
});
test('validation:matchRegexp:noSlash valid', () => {
expect(validate('abcd', {}, {
matchRegexp: '^abc'
}, {
matchRegexp: '请输入abc开头的好么'
})).toMatchObject([]);
});
test('validation:matchRegexp:noSlash invalid', () => {
expect(validate('cba', {}, {
matchRegexp: '^abc'
}, {
matchRegexp: '请输入abc开头的好么'
})).toMatchObject(['请输入abc开头的好么']);
});
test('validation:multipleMatchRegexp valid', () => {
expect(validate('abcd123', {}, {
matchRegexp1: '/^abc/',
matchRegexp2: '/123$/',
}, {
matchRegexp1: '请输入abc开头的好么',
matchRegexp2: '请输入123结尾的好么',
})).toMatchObject([]);
});
test('validation:multipleMatchRegexp invalid', () => {
expect(validate('cba', {}, {
matchRegexp1: '/^abc/',
matchRegexp2: '/123$/',
}, {
matchRegexp1: '请输入abc开头的好么',
matchRegexp2: '请输入123结尾的好么',
})).toMatchObject(['请输入abc开头的好么', '请输入123结尾的好么']);
});
test('validation:multipleMatchRegexp:noSlash valid', () => {
expect(validate('abcd123', {}, {
matchRegexp1: '^abc',
matchRegexp2: '123$',
}, {
matchRegexp1: '请输入abc开头的好么',
matchRegexp2: '请输入123结尾的好么',
})).toMatchObject([]);
});
test('validation:multipleMatchRegexp:noSlash invalid', () => {
expect(validate('cba', {}, {
matchRegexp1: '^abc',
matchRegexp2: '123$',
}, {
matchRegexp1: '请输入abc开头的好么',
matchRegexp2: '请输入123结尾的好么',
})).toMatchObject(['请输入abc开头的好么', '请输入123结尾的好么']);
});
test('validation:str2rules', () => {
expect(str2rules('matchRegexp:/^abc/'))
.toMatchObject({
matchRegexp: ['/^abc/']
});
});
test('validation:multiplestr2rules', () => {
expect(str2rules('matchRegexp1:/^abc/,matchRegexp2:/123$/'))
.toMatchObject({
matchRegexp1: ['/^abc/'],
matchRegexp2: ['/123$/']
});
});
test('validation:str2rules:noSlash', () => {
expect(str2rules('matchRegexp:^abc'))
.toMatchObject({
matchRegexp: ['^abc']
});
});
test('validation:multiplestr2rules:noSlash', () => {
expect(str2rules('matchRegexp1:^abc,matchRegexp2:123$'))
.toMatchObject({
matchRegexp1: ['^abc'],
matchRegexp2: ['123$']
});
});

4
build.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
set -e
echo "Do nothing"

181
build/md-parser.js Normal file
View File

@ -0,0 +1,181 @@
/* eslint-disable */
var marked = require("marked");
var yaml = (yaml = require("js-yaml"));
var rYml = /^\s*---([\s\S]*?)---\s/;
var renderer = new marked.Renderer();
marked.setOptions({
renderer: renderer,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false
});
// Synchronous highlighting with highlight.js
marked.setOptions({
highlight: function(code) {
return require("highlight.js").highlightAuto(code).value;
}
});
// renderer.table = function(header, body) {
// return '<table class="table table-striped">\n'
// + '<thead>\n'
// + header
// + '</thead>\n'
// + '<tbody>\n'
// + body
// + '</tbody>\n'
// + '</table>\n';
// };
renderer.link = function(href, title, text) {
if (this.options.sanitize) {
try {
var prot = decodeURIComponent(unescape(href))
.replace(/[^\w:]/g, "")
.toLowerCase();
} catch (e) {
return "";
}
if (
prot.indexOf("javascript:") === 0 ||
prot.indexOf("vbscript:") === 0
) {
return "";
}
}
if (href && href[0] === "#") {
href = "#" +
encodeURIComponent(
href
.substring(1)
.toLowerCase()
.replace(/[^\u4e00-\u9fa5_a-zA-Z0-9]+/g, "-")
);
}
var out = '<a href="' + href + '"';
if (title) {
out += ' title="' + title + '"';
}
out += ">" + text + "</a>";
return out;
};
module.exports = function(content, file) {
var m = rYml.exec(content);
var info = {};
if (m && m[1]) {
info = yaml.safeLoad(m[1]);
content = content.substring(m[0].length);
}
var toc = {
label: "目录",
type: "toc",
children: [],
level: 0
};
var stack = [toc];
renderer.heading = function(text, level) {
var escapedText = encodeURIComponent(
text.toLowerCase().replace(/[^\u4e00-\u9fa5_a-zA-Z0-9]+/g, "-")
);
if (level < 5) {
var menu = {
label: text,
fragment: escapedText,
fullPath: "#" + escapedText,
level: level
};
while (stack.length && stack[0].level >= level) {
stack.shift();
}
stack[0].children = stack[0].children || [];
stack[0].children.push(menu);
stack.unshift(menu);
}
var anchor =
'<a class="anchor" name="' +
escapedText +
'" href="#' +
escapedText +
'" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>';
return "<h" + level + ">" + anchor + text + "</h" + level + ">";
// return '<h' + level + '><a name="' +
// escapedText +
// '" class="anchor" href="#' +
// escapedText +
// '"><span class="header-link"></span></a>' +
// text + '</h' + level + '>';
};
const placeholder = {};
let index = 1;
content = content.replace(
/```(schema|html)(?::(.*?))?\n([\s\S]*?)```/g,
function(_, lang, attr, code) {
const setting = {};
attr && attr.split(/\s+/).forEach(function(item) {
var parts = item.split("=");
if (parts[1] && /^('|").*\1/.test(parts[1])) {
parts[1] = parts[1].substring(1, parts[1].length - 1);
}
setting[parts[0]] = parts[1] ? decodeURIComponent(parts[1]) : "";
if (parts[0] === 'height') {
setting.height = parseInt(setting.height, 10) + 200/*编辑器的高度*/;
attr = attr.replace(item, `height="${setting.height}"`);
}
});
// placeholder[index] = `<iframe class="doc-iframe" width="100%" height="${setting.height || 200}px" frameBorder="0" src="/play?code=${encodeURIComponent(code)}&scope=${encodeURIComponent(setting.scope)}"></iframe>`;
if (lang === "html") {
placeholder[
index
] = `<div class="amis-doc"><div class="preview">${code}</div><pre><code class="lang-html">${
require("highlight.js").highlightAuto(code).value
}</code></pre></div>`;
} else {
placeholder[
index
] = `<div class="amis-preview" style="height: ${
setting.height
}px"><script type="text/schema" ${attr}>${code}</script></div>`;
}
return `[[${index++}]]`;
}
);
content = marked(content).replace(/<p>\[\[(\d+)\]\]<\/p>/g, function(
_,
id
) {
return placeholder[id] || "";
});
content = fis.compile.partial(content, file, "html");
info.html = content;
info.toc = toc;
return "module.exports = " + JSON.stringify(info, null, 2) + ";";
};

289
docs/advanced.md Normal file
View File

@ -0,0 +1,289 @@
---
title: 高级用法
shortname: advanced
---
在开始阅读之前,希望你已经对阅读 [快速开始文档](/docs/getting-started) 。
## 数据作用域
配置中很多地方都可以用变量如: [tpl](#/docs/renderers#tpl) 类型的渲染器、API 中的 Url、FormItem 中的 source 配置、visibleOn、disabledOn 以及 Form 中的 `redirect` 配置等等。
那么都有哪些数据可以用?这取决于在哪个容器,关于容器中的数据说明如下:
* `page` 等价于全局变量,因为顶级渲染器就是它,所以下面的所有组件都能用到这个里面的数据。
* `amisPage` 当前页面的数据信息包含标题idkey 之类的信息。
* `amisUser` 当前用户信息,包含邮箱和用户名信息。
* `params 中的数据` 如果地址栏中也携带了参数也会merge到该层的数据中。
* `initApi 返回的数据` 如果 page 设置了 `initApi` 那么初始化的时候会从 API 中拉取数据,拉取到的数据可以用于整个页面。
* `crud`
* 父级 容器中的数据可以直接使用,如 page 容器
* `api` 返回的数据crud 的 api 除了可以返回 `rows``count` 数据外,其他的数据会被 merge 到数据中,供容器使用。
* `form`
* 父级 容器中的数据可以直接使用,如 page 容器
* `initApi` 返回的数据。
* FormItem 的数据直接会存入到数据中,而且每次修改都会及时更新。通过 FormItem 设置的 `name` 值获取。
* `formItem` 表单项中,所在的表单中的数据都能用。
* `wizard` 同 form
* `dialog` dialog 由 button 触发弹出,携带的数据根据按钮所在的位置来决定。
* form 中弹出则会把 form 中的数据复制份传给 dialog。
* crud 中的批量操作按钮。把整个列表数据复制给 dialog。
* crud 中的某一项中的按钮,则只会把对应的那一条数据拷贝给 dialog。
* `servcie`
* 父级 容器中的数据可以直接使用,如 page 容器
* 如果配置了 `api`, `api` 返回的数据可以用。
取值过程,也跟 JS 作用域中取值一样,当前作用域中有,则直接返回当前作用域中,如果没有当前作用域没有,会一直往上找,直到找到了为止。如果存在同名变量,则返回就近作用域中数据。
需要注意的是要取到值一定是在自己所在的作用域或者上级作用域里面同级的是取不到的如果需要怎么办可以往下看联动比如FormA 的数据发送给 formB, 另外一种方式,可以把接口拉取换到父级组件去操作,没有可拉取数据的组件,就一起包在一个 service 控件里面。
## 联动
### 简单的显隐联动
主要通过 `visibleOn`、`hiddenOn` 和 `disabledOn` 来配置。
```schema:height="300" scope="form"
[
{
"type": "radios",
"name": "foo",
"inline": true,
"label": " ",
"options": [
{
"label": "类型1",
"value": 1
},
{
"label": "类型2",
"value": 2
},
{
"label": "类型3",
"value": 3
}
]
},
{
"type": "text",
"name": "text",
"placeholder": "类型1 可见",
"visibleOn": "data.foo == 1"
},
{
"type": "text",
"name": "text2",
"placeholder": "类型2 不可点",
"disabledOn": "data.foo == 2"
},
{
"type": "button",
"label": "类型三不能提交",
"level": "primary",
"disabledOn": "data.foo == 3"
}
]
```
### 选项联动
比如 select 中 options 可能根据某个值不同而不同。
```schema:height="300" scope="form"
[
{
"label": "选项1",
"type": "radios",
"labelClassName": "text-muted",
"name": "a",
"inline": true,
"options": [
{
"label": "选项1",
"value": 1
},
{
"label": "选项2",
"value": 2
},
{
"label": "选项3",
"value": 3
}
]
},
{
"label": "选项2",
"type": "select",
"labelClassName": "text-muted",
"name": "b",
"inline": true,
"source": "/api/mock2/options/level2?a=${a}",
"initFetchOn": "data.a"
}
]
```
他们是怎么关联的呢?注意看 select 的 source 配置 `"/api/mock/getOptions?waitSeconds=1&type=$foo"` 这里用了变量 `$foo` 这个 foo 正好是第一个表单的 name 值。只要这个值发生变化source 就会重新获取一次。
这里有个问题就是,数据一旦变化就会出发重新拉取,如果是输入框岂不是拉取得很频繁?没关系,也可以主动拉取如:
```schema:height="300" scope="body"
{
"type": "form",
"name": "lidong",
"controls": [
{
"type": "text",
"name": "foo",
"addOn": {
"label": "搜索",
"className": "btn-info",
"type": "button",
"actionType": "reload",
"disabledOn": "!data.foo",
"target": "lidong.select"
}
},
{
"type": "select",
"name": "select",
"label": "Select",
"source": {
"method": "get",
"url": "/api/mock2/options/level2?waitSeconds=1",
"data": {
"a": "$foo"
}
},
"desc": "这里只是演示刷新不会真正的过滤。"
}
]
}
```
注意source中的传参是通过 source 中的 data 关联的,不能写在 source 的 url 中,因为如果写了,就会自动监控值的变化而自动刷新,写在 data 里面关联则不会。如果对 source 中的配置规则不了解,请前往 [API 说明](/docs/renderers#api)
另外注意 button 的 target 值,正好是这个 form 的 name 值 `lidong` 的 formItem 的 name 值 `select`。当按钮的对象是一个 formItem 时,会出发 formItem 的数据重新拉取。
### 数据联动
Form 和 CRUD, CRUD 有个 filter 配置项,里面可以配置表单项,当他提交时 CRUD 自动就会携带接受到的表单数据然后重新获取数据。有个限制,就是 CRUD 和 filter 必须放在一起,不能分开,实际上完全可以分开,只要 Form 的 target 是 CRUD 的 name 值即可。
```schema:height="300"
{
"type": "page",
"aside": {
"type": "form",
"target": "doc-crud",
"wrapWithPanel": false,
"className": "wrapper-xs",
"controls": [
{
"type": "text",
"name": "keywords",
"placeholder": "请输入关键字",
"clearable": true,
"addOn": {
"label": "搜索",
"className": "btn-info",
"type": "submit"
}
}
]
},
"body": {
"name": "doc-crud",
"type": "crud",
"api": "/api/sample",
"syncLocation": false,
"title": null,
"perPageField":"rn",
"defaultParams":{
"rn": 10
},
"columns": [
{
"name": "id",
"label": "ID",
"width": 20,
"sortable": true
},
{
"name": "engine",
"label": "Rendering engine",
"sortable": true,
"toggled": false
},
{
"name": "browser",
"label": "Browser",
"sortable": true
},
{
"name": "platform",
"label": "Platform(s)",
"sortable": true
},
{
"name": "version",
"label": "Engine version"
}
]
}
}
```
Form 的 target 还可以是另外一个 Form当 A Form 把自己的数据提交给 B Form 时A 的数据会被合并到 B Form 中同时B Form 会再次初始化,如:拉取 initApi, 重新拉取 formItem 上的 source 等等。 比如用户管理中的[加入用户](/group/test/admin/users?perPage=12)操作就是用这种方式实现的。
```schema:height="300"
{
"type": "page",
"aside": {
"type": "form",
"target": "doc-form",
"wrapWithPanel": false,
"className": "wrapper-xs",
"controls": [
{
"type": "text",
"name": "keywords",
"clearable": true,
"placeholder": "请输入关键字",
"addOn": {
"label": "提交",
"className": "btn-info",
"type": "submit"
}
}
]
},
"body": {
"name": "doc-form",
"type": "form",
"api": "/api/sample",
"submitText": null,
"controls": [
{
"type": "static",
"name": "keywords",
"label": "你刚刚输入的是:"
}
]
}
}
```

181
docs/dev.md Normal file
View File

@ -0,0 +1,181 @@
---
title: 自定义组件
shortname: dev
---
AMis 平台中,支持了大部分的[通用组件](/docs/renderers),基本需求都能满足。针对比较定制的需求,则需要通过在群组内添加自定义组件来实现。
## 如何添加
1. 自定义组件是群级别的,先进入你想要添加的自定义组件的群组。
2. 然后进入【组件管理】页面。(你需要拥有管理权限,一般管理员自带这个权限)
3. 然后点击右上角添加【新建】
![图片](http://bos.nj.bpc.baidu.com/v1/agroup/e3619506735bbec17ea83da71944fc447d709de4)
* `组件名` 随意。
* `组件代码` React Component 代码。
## 如何开发?
AMis 中自定义组件主要分两类。表单类和非表单类。
### FormItem
即表单类,它主要用来扩充表单项。先看个例子。
```jsx
import * as React from 'react';
import {FormItem} from 'amis';
import * as cx from 'classnames';
@FormItem({
type:'custom-checkbox'
})
export default class CustomCheckbox extends React.PureComponent{
toggle = () => {
const {
value,
onChange
} = this.props;
onChange(!value);
};
render() {
const {
value
} = this.props;
const checked = !!value;
return (
<div>
<a
className={cx('btn btn-default', {
'btn-success': checked,
})}
onClick={this.toggle}
>{checked ? '已勾选' : '请勾选'}</a>
<div className="inline m-l-xs">{checked ? '已勾选' : '请勾选'}</div>
</div>
)
}
}
```
有了这个代码后,页面配置 form 的 controls 里面就可以通过这样的配置启动了。[在线 Demo](http://amis.baidu.com/group/demo/custom)。
```js
{
// 其他信息省略了。。
type: 'form',
controls: [
{
type: 'custom-checkbox',
name: '变量名',
label: '自定义组件。'
}
]
}
```
表单项开发主要关心两件事。
1. 呈现当前值。如以上例子,勾选了则显示`已勾选`,否则显示`请勾选`。
2. 接收用户交互,修改表单项值。如以上例子,当用户点击按钮时,切换当前选中的值。
至于其他功能如label/description 的展示、表单验证功能、表单布局(常规、左右或者内联)等等,只要是通过 FormItem 注册进去的都无需自己实现。
### Renderer
非表单类的组件自定义,主要通过 `Renderer` 实现。在开始阅读之前,请先阅读 [AMis 工作原理](/docs/sdk#工作原理)。
```jsx
import * as React from 'react';
import {
Renderer
} from 'amis';
@Renderer({
test: /(^|\/)my\-renderer$/,
})
class CustomRenderer extends React.Component {
render() {
const {
tip,
body,
render
} = this.props;
return (
<div>
<p>这是自定义组件:{tip}</p>
{body ? (
<div className="container">
{render('body', body, {
// 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个
})}
</div>
) : null}
</div>
);
}
}
```
这里注册一个 React 组件,当节点的 path 信息是 `my-renderer` 结尾时,交给当前组件来完成渲染。
请注意 `this.props` 中的 `render` 方法,它用来实现容器功能,通过它可以让使用者动态的配置其他渲染模型。
## 工具
目前主要提供以下工具。
### fetch
```jsx
import {
fetch
} from 'amis/utils';
```
用来做 ajax 请求。参数说明
* `api` 字符串或者 api 对象,如: {url: 'http://www.baidu.com', method: 'get'}, api 地址支持变量。
* `data` 数据体
返回一个 Promise。
如:
```js
import {
fetch
} from 'amis/utils';
fetch('http://www.baidu.com/api/xxx?a=${a}&b=${b}', {
a: 'aa',
b: 'bb'
}).then(function(result) {
console.log(result);
});
```
### filter
```jsx
import {
filter
} from 'amis/utils';
```
主要用来做字符替换,如:
```js
import {
filter
} from 'amis/utils';
filter('blabla?a={a}', {a: 123}); // => 'blabla?a=123'
```

409
docs/getting_started.md Normal file
View File

@ -0,0 +1,409 @@
---
title: 快速开始
---
为了简化前端开发AMis Renderer 能够直接用配置就能将页面渲染出来。
先来看个简单的例子。
```schema:height="300"
{
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"type": "page",
"title": "这是标题部分",
"subTitle": "这是子标题",
"remark": "这是小提示信息",
"aside": "这是侧边栏部分",
"body": "这是内容区",
"toolbar": "这是工具栏部分"
}
```
> PS: 可以通过编辑器实时修改预览
从上面的内容可以看出,一个简单页面框架已经基本出来了,这是 AMis 渲染器配置的入口。从 `page` 渲染器开始出发,通过在容器中放置不同的渲染器来配置不同性质的页面。
简单说明以上配置信息。
* `$schema` 这个字段可以忽略,他是指定当前 JSON 配置是符合指定路径 http://amis.baidu.com/v2/schemas/page.json 的 JSON SCHEMA 文件描述的。PS: 编辑器就是靠这个描述文件提示的,可以 hover 到字段上看效果。
* `type` 指定渲染器类型,这里指定的类型为 `page`。 更多渲染器类型可以去[这里面查看](/v2/docs/renderers)。
* `title` 从 title 开始就是对应的渲染模型上的属性了。这里用来指定标题内容。
* `subTitle` 副标题.
* `remark` 标题上面的提示信息
* `aside` 边栏区域内容
* `body` 内容区域的内容
* `toolbar` 工具栏部分的内容
这里有三个配置都是容器类型的。`aside`、`body` 和 `toolbar`。什么是容器类型?容器类型表示,他能够把其他渲染类型放进来。以上的例子为了简单,直接放了个字符串。字符串类型内部是把他当成了 [tpl](/v2/docs/renderers#tpl) 渲染器来处理,在这里也可以通过对象的形式指定,如以下的例子的 body 区域是完全等价的。
```schema:height="100"
{
"$schema": "http://amis.baidu.com/v2/schemas/page.json",
"type": "page",
"body": {
"type": "tpl",
"tpl": "这是内容区"
}
}
```
容器内可以直接放一个渲染器,也可以放多个,用数组包起来即可如:
```schema:height="130"
{
"$schema": "http://amis.baidu.com/v2/schemas/page.json",
"type": "page",
"body": [
{
"type": "tpl",
"tpl": "<p>段落1</p>"
},
{
"type": "tpl",
"tpl": "<p>段落2</p>"
},
"<p>段落3</p>"
]
}
```
再来看一个表单页面的列子
```schema:height="440"
{
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"type": "page",
"body": {
"api": "/api/mock2/form/saveForm",
"type": "form",
"title": "联系我们",
"controls": [
{
"type": "text",
"label": "姓名",
"name": "name"
},
{
"type": "email",
"label": "邮箱",
"name": "email",
"required": true
},
{
"type": "textarea",
"label": "内容",
"name": "content",
"required": true
}
],
"actions": [
{
"label": "发送",
"type": "submit",
"primary": true
}
]
}
}
```
这个例子就是在 body 容器内,放置一个 `form` 类型的渲染它就成了一个简单的表单提交页面了controls 中可以决定放哪些表单项目actions 中可以放置操作按钮。
如果 body 区域放置一个 `crud` 渲染器,它就是列表页面了,再来看个栗子:
```schema:height="600"
{
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"type": "page",
"title": "增删改查示例",
"toolbar": [
{
"type": "button",
"actionType": "dialog",
"label": "新增",
"icon": "fa fa-plus pull-left",
"primary": true,
"dialog": {
"title": "新增",
"body": {
"type": "form",
"name": "sample-edit-form",
"api": "",
"controls": [
{
"type": "alert",
"level": "info",
"body": "因为没有配置 api 接口,不能真正的提交哈!"
},
{
"type": "text",
"name": "text",
"label": "文本",
"required": true
},
{
"type": "divider"
},
{
"type": "image",
"name": "image",
"label": "图片",
"required": true
},
{
"type": "divider"
},
{
"type": "date",
"name": "date",
"label": "日期",
"required": true
},
{
"type": "divider"
},
{
"type": "select",
"name": "type",
"label": "选项",
"options": [
{
"label": "漂亮",
"value": "1"
},
{
"label": "开心",
"value": "2"
},
{
"label": "惊吓",
"value": "3"
},
{
"label": "漂亮",
"value": "紧张"
}
]
}
]
}
}
}
],
"body": [
{
"type": "crud",
"api": "/api/mock2/crud/list",
"defaultParams": {
"perPage": 5
},
"columns": [
{
"name": "id",
"label": "ID",
"type": "text"
},
{
"name": "text",
"label": "文本",
"type": "text"
},
{
"type": "image",
"label": "图片",
"multiple": false,
"name": "image",
"popOver": {
"title": "查看大图",
"body": "<div class=\"w-xxl\"><img class=\"w-full\" src=\"${image}\"/></div>"
}
},
{
"name": "date",
"type": "date",
"label": "日期"
},
{
"name": "type",
"label": "映射",
"type": "mapping",
"map": {
"1": "<span class='label label-info'>漂亮</span>",
"2": "<span class='label label-success'>开心</span>",
"3": "<span class='label label-danger'>惊吓</span>",
"4": "<span class='label label-warning'>紧张</span>",
"*": "其他:${type}"
}
},
{
"type": "container",
"label": "操作",
"body": [
{
"type": "button",
"icon": "fa fa-eye",
"level": "link",
"actionType": "dialog",
"tooltip": "查看",
"dialog": {
"title": "查看",
"body": {
"type": "form",
"controls": [
{
"type": "static",
"name": "id",
"label": "ID"
},
{
"type": "divider"
},
{
"type": "static",
"name": "text",
"label": "文本"
},
{
"type": "divider"
},
{
"type": "static-image",
"label": "图片",
"name": "image",
"popOver": {
"title": "查看大图",
"body": "<div class=\"w-xxl\"><img class=\"w-full\" src=\"${image}\"/></div>"
}
},
{
"type": "divider"
},
{
"name": "date",
"type": "static-date",
"label": "日期"
},
{
"type": "divider"
},
{
"name": "type",
"label": "映射",
"type": "static-mapping",
"map": {
"1": "<span class='label label-info'>漂亮</span>",
"2": "<span class='label label-success'>开心</span>",
"3": "<span class='label label-danger'>惊吓</span>",
"4": "<span class='label label-warning'>紧张</span>",
"*": "其他:${type}"
}
}
]
}
}
},
{
"type": "button",
"icon": "fa fa-pencil",
"tooltip": "编辑",
"level": "link",
"actionType": "drawer",
"drawer": {
"position": "left",
"size": "lg",
"title": "编辑",
"body": {
"type": "form",
"name": "sample-edit-form",
"controls": [
{
"type": "alert",
"level": "info",
"body": "因为没有配置 api 接口,不能真正的提交哈!"
},
{
"type": "hidden",
"name": "id"
},
{
"type": "text",
"name": "text",
"label": "文本",
"required": true
},
{
"type": "divider"
},
{
"type": "image",
"name": "image",
"multiple": false,
"label": "图片",
"required": true
},
{
"type": "divider"
},
{
"type": "date",
"name": "date",
"label": "日期",
"required": true
},
{
"type": "divider"
},
{
"type": "select",
"name": "type",
"label": "选项",
"options": [
{
"label": "漂亮",
"value": "1"
},
{
"label": "开心",
"value": "2"
},
{
"label": "惊吓",
"value": "3"
},
{
"label": "漂亮",
"value": "紧张"
}
]
}
]
}
}
},
{
"type": "button",
"level": "link",
"icon": "fa fa-times text-danger",
"actionType": "ajax",
"tooltip": "删除",
"confirmText": "您确认要删除?",
"api": ""
}
]
}
]
}
]
}
```
这个栗子最主要的渲染器就是 CRUD 渲染器了,他的作用是配置了个 API把数据拉取过来后根据配置 columns 信息完成列表展示,列类型可以是静态文本、图片、映射或者日期等等。 `columns` 通过 `name` 与行数据关联。除了展示外还可以放置操作按钮。
这里相对复杂一点配置就是按钮了,按钮主要是通过 `actionType`来决定用户点下的行为。可以配置成 弹框、发送 ajax、页面跳转、复制内容到剪切板、刷新目标组件等等。具体请参考[Action 渲染器说明](/v2/docs/renderers#action)
更多用法请参考[渲染器手册](/v2/docs/renderers)和示例。

4541
docs/renderers.md Normal file

File diff suppressed because it is too large Load Diff

404
docs/sdk.md Normal file
View File

@ -0,0 +1,404 @@
---
title: 如何使用
---
## 如何使用
```bash
npm i amis
```
安装完后可以在 React Component 这么使用。
```tsx
import * as React from 'react';
import {
render as renderAmis
} from 'amis';
class MyComponent extends React.Component<any, any> {
render() {
return (
<div>
<p>通过 AMis 渲染页面</p>
{renderAmis({
// schema
// 这里是 AMis 的 Json 配置。
type: 'page',
title: '简单页面',
body: '内容'
}, {
// props
}, {
// env
// 这些是 AMis 需要的一些接口实现
// 可以参考本项目里面的 Demo 部分代码。
updateLocation: (location:string/*目标地址*/, replace:boolean/*是replace还是push*/) => {
// 用来更新地址栏
},
jumpTo: (location:string/*目标地址*/) => {
// 页面跳转, actionType: link、url 都会进来。
},
fetcher: ({
url,
method,
data,
config
}:{
url:string/*目标地址*/,
method:'get' | 'post' | 'put' | 'delete'/*发送方式*/,
data: object | void/*数据*/,
config: object/*其他配置*/
}) => {
// 用来发送 Ajax 请求,建议使用 axios
},
notify: (type:'error'|'success'/**/, msg:string/*提示内容*/) => {
// 用来提示用户
},
alert: (content:string/*提示信息*/) => {
// 另外一种提示,可以直接用系统框
},
confirm: (content:string/*提示信息*/) => {
// 确认框。
}
});}
</div>
);
}
}
```
## 工作原理
AMis 的渲染过程就是将 `json` 转成对应的 React 组件。先通过 `json` 的 type 找到对应的 `Component` 然后,然后把其他属性作为 `props` 传递过去完成渲染。
拿一个表单页面来说如果用React组件调用大概是这样。
```jsx
<Page
title="页面标题"
subTitle="副标题"
>
<Form
title="用户登录"
controls={[
{
type: 'text',
name: 'username',
label: '用户名'
}
]}
/>
</Page>
```
把以上配置方式换成 AMis JSON, 则是:
```json
{
"type": "page",
"title": "页面标题",
"subTitle": "副标题",
"body": {
"type": "form",
"title": "用户登录",
"controls": [
{
"type": "text",
"name": "username",
"label": "用户名"
}
]
}
}
```
那么AMis 是如何将 JSON 转成组件的呢?直接根据节点的 type 去跟组件一一对应?似乎很可能会重名比如在表格里面展示的类型 `text` 跟表单里面的 `text`是完全不一样的,一个负责展示,一个却负责输入。所以说一个节点要被什么组件渲染,还需要携带上下文(context)信息。
如何去携带上下文context信息AMis 中直接是用节点的路径path来作为上下文信息。从上面的例子来看一共有三个节点path 信息分别是。
* `page` 页面节点
* `page/body/form` 表单节点
* `page/body/form/controls/0/text` 文本框节点。
根据 path 的信息就能很容易注册组件跟节点对应了。
Page 组件的示例代码
```jsx
@Renderer({
test: /^page$/,
// ... 其他信息隐藏了
})
export class PageRenderer extends React.Component {
// ... 其他信息隐藏了
render() {
const {
title,
body,
render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。
} = this.props;
return (
<div className="page">
<h1>{title}</h1>
<div className="body-container">
{render('body', body)/*渲染孩子节点*/}
</div>
</div>
);
}
}
```
Form 组件的示例代码
```jsx
@Renderer({
test: /(^|\/)form$/,
// ... 其他信息隐藏了
})
export class FormRenderer extends React.Component {
// ... 其他信息隐藏了
render() {
const {
title,
controls,
render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。
} = this.props;
return (
<form className="form">
{controls.map((control, index) => (
<div className="form-item" key={index}>
{render(`${index}/control`, control)}
</div>
))}
</form>
);
}
}
```
Text 组件的示例代码
```jsx
@Renderer({
test: /(^|\/)form(?:\/\d+)?\/control(?\/\d+)?\/text$/
// ... 其他信息隐藏了
})
export class FormItemTextRenderer extends React.Component {
// ... 其他信息隐藏了
render() {
const {
label,
name,
onChange
} = this.props;
return (
<div className="form-group">
<label>{label}<label>
<input type="text" onChange={(e) => onChange(e.currentTarget.value)} />
</div>
);
}
}
```
那么渲染过程就是根据节点 path 信息,跟组件池中的组件 `test` (检测) 信息做匹配,如果命中,则把当前节点转给对应组件渲染,节点中其他属性将作为目标组件的 props。需要注意的是如果是容器组件比如以上例子中的 `page` 组件,从 props 中拿到的 `body` 是一个子节点,由于节点类型是不固定,由使用者决定,所以不能直接完成渲染,所以交给属性中下发的 `render` 方法去完成渲染,`{render('body', body)}`,他的工作就是拿子节点的 path 信息去组件池里面找到对应的渲染器,然后交给对应组件去完成渲染。
## 自定义组件
如果 AMis 中组件不能满足你的需求,同时你又会 React 组件开发,那么就自己定制一个吧。
先来看个简单的例子
```jsx
import * as React from 'react';
import {
Renderer
} from 'amis';
@Renderer({
test: /(^|\/)my\-renderer$/,
})
class CustomRenderer extends React.Component {
render() {
const {tip} = this.props;
return (
<div>这是自定义组件:{tip}</div>
);
}
}
```
有了以上这段代码后,就可以这样使用了。
```json
{
"type": "page",
"title": "自定义组件示例",
"body": {
"type": "my-renderer",
"tip": "简单示例"
}
}
```
如果你看了[AMis工作原理](#工作原理)应该不难理解,这里注册一个 React 组件,当节点的 path 信息是 `my-renderer` 结尾时,交给当前组件来完成渲染。
如果你只写叶子节点的渲染器,已经可以不用看了,如果你的渲染器中有容器需要可以放置其他节点,那么接着看以下这段代码。
```jsx
import * as React from 'react';
import {
Renderer
} from 'amis';
@Renderer({
test: /(^|\/)my\-renderer2$/,
})
class CustomRenderer extends React.Component {
render() {
const {
tip,
body,
render
} = this.props;
return (
<div>
<p>这是自定义组件:{tip}</p>
{body ? (
<div className="container">
{render('body', body, {
// 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个
})}
</div>
) : null}
</div>
);
}
}
```
有了以上这段代码后,就可以这样使用了。
```json
{
"type": "page",
"title": "自定义组件示例",
"body": {
"type": "my-renderer2",
"tip": "简单示例",
"body": {
"type": "form",
"controls": [
{
"type": "text",
"label": "用户名",
"name": "usename"
}
]
}
}
}
```
跟第一个列子不同的地方是,这里多了个 `render` 方法,这个方法就是专门用来渲染子节点的。来看下参数说明:
* `region` 区域名称,你有可能有多个区域可以作为容器,请不要重复。
* `node` 子节点。
* `props` 可选,可以通过此对象跟子节点通信等。
以上是普通渲染器的注册方式,如果是表单项,为了更简单的扩充,请使用 `FormItem` 注解,而不是 `Renderer`。 原因是如果用 `FormItem` 是不用关心label怎么摆表单验证器怎么实现如何适配表单的3中展现方式水平、上下和内联模式而只用关心有了值后如何回显响应用户交互设置新值。
```jsx
import * as React from 'react';
import {
FormItem
} from 'amis';
@FormItem({
type: 'custom'
})
class MyFormItem extends React.Component {
render() {
const {
value,
onChange
} = this.props;
return (
<div>
<p>这个是个自定义组件</p>
<p>当前值:{value}</p>
<a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>
</div>
);
}
}
```
有了以上这段代码后,就可以这样使用了。
```json
{
"type": "page",
"title": "自定义组件示例",
"body": {
"type": "form",
"controls": [
{
"type": "text",
"label": "用户名",
"name": "usename"
},
{
"type": "custom",
"label": "随机值",
"name": "randon"
}
]
}
}
```
> 注意: 使用 FormItem 默认是严格模式,即只有必要的属性变化才会重新渲染,有可能满足不了你的需求,如果忽略性能问题,可以传入 `strictMode`: `false` 来关闭。
以上的例子都是需要先注册组件,然后再使用的,如果你在自己项目中使用,还有更简单的用法,以下示例直接无需注册。
```jsx
{
"type": "page",
"title": "自定义组件示例",
"body": {
"type": "form",
"controls": [
{
"type": "text",
"label": "用户名",
"name": "usename"
},
{
"name": "a",
"children": ({
value,
onChange
}) => (
<div>
<p>这个是个自定义组件</p>
<p>当前值:{value}</p>
<a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>
</div>
)
}
]
}
}
```
即:通过 `children` 传递一个React组件这个示例是一个React Stateless Functional Component也可以是传统的 React 组件。
任何节点如果包含 `children` 这个属性,则都会把当前节点交给 `children` 来处理,跳过了从 AMis 渲染器池子中选择渲染器的过程。

291
docs/style.md Normal file
View File

@ -0,0 +1,291 @@
---
title: 样式表说明
shortname: style
---
AMis 中有大量的功能类 class 可以使用,即可以用在 schema 中,也可以用在自定义组件开发中,掌握这些 class, 几乎可以不用写样式。
AMis 中的样式基于 [BootStrap V3](http://getbootstrap.com/css/), 这里主要讲 Bootstrap 中没有涉及到的。
## 图标
AMis 集成了 [fontawesome](http://fontawesome.io/icons/),所以关于图标部分,请前往 [fontawesome](http://fontawesome.io/icons/) 查看。
## 布局
水平布局可以考虑用 Bootstrap 的 [Grids](http://getbootstrap.com/css/#grid) 或者用 `hobx``col`
```html
<div class="hbox b-a">
<div class="col wrapper-sm bg-success">Col A</div>
<div class="col wrapper-sm bg-info">Col B</div>
<div class="col wrapper-sm bg-danger">Col C</div>
</div>
```
## 宽高
```css
.w-1x { width: 1em; }
.w-2x { width: 2em; }
.w-3x { width: 3em; }
.w-xxs { width: 60px; }
.w-xs { width: 90px; }
.w-sm { width: 150px; }
.w { width: 200px; }
.w-md { width: 240px; }
.w-lg { width: 280px; }
.w-xl { width: 320px; }
.w-xxl { width: 360px; }
.w-full { width: 100%; }
.w-auto { width: auto; }
.h-auto { height: auto; }
.h-full { height: 100% !important; max-height: none !important; }
```
```html
<div class="hbox b-a bg-primary">
<div class="col wrapper-sm b-r w-1x">w-1x</div>
<div class="col wrapper-sm b-r w-2x">w-2x</div>
<div class="col wrapper-sm b-r w-3x">w-3x</div>
<div class="col wrapper-sm b-r w-xxs">w-xxs</div>
<div class="col wrapper-sm b-r w-xs">w-xs</div>
<div class="col wrapper-sm b-r w-sm">w-sm</div>
<div class="col wrapper-sm b-r w">w</div>
<div class="col wrapper-sm lter">...</div>
</div>
<div class="hbox b-a bg-primary m-t">
<div class="col wrapper-sm b-r w-md">w-md</div>
<div class="col wrapper-sm b-r w-lg">w-lg</div>
<div class="col wrapper-sm b-r w-xl">w-xl</div>
<div class="col wrapper-sm lter">...</div>
</div>
<div class="hbox b-a bg-primary m-t">
<div class="col wrapper-sm b-r w-xxl">w-xxl</div>
<div class="col wrapper-sm lter">...</div>
</div>
```
## 外边距
```css
.m-xxs { margin: 2px 4px }
.m-xs { margin: 5px; }
.m-sm { margin: 10px; }
.m { margin: 15px; }
.m-md { margin: 20px; }
.m-lg { margin: 30px; }
.m-xl { margin: 50px; }
.m-n { margin: 0 !important }
.m-l-none { margin-left: 0 !important }
.m-l-xs { margin-left: 5px; }
.m-l-sm { margin-left: 10px; }
.m-l { margin-left: 15px }
.m-l-md { margin-left: 20px; }
.m-l-lg { margin-left: 30px; }
.m-l-xl { margin-left: 40px; }
.m-l-xxl { margin-left: 50px; }
.m-l-n-xxs { margin-left: -1px }
.m-l-n-xs { margin-left: -5px }
.m-l-n-sm { margin-left: -10px }
.m-l-n { margin-left: -15px }
.m-l-n-md { margin-left: -20px }
.m-l-n-lg { margin-left: -30px }
.m-l-n-xl { margin-left: -40px }
.m-l-n-xxl { margin-left: -50px }
.m-t-none { margin-top: 0 !important }
.m-t-xxs { margin-top: 1px; }
.m-t-xs { margin-top: 5px; }
.m-t-sm { margin-top: 10px; }
.m-t { margin-top: 15px }
.m-t-md { margin-top: 20px; }
.m-t-lg { margin-top: 30px; }
.m-t-xl { margin-top: 40px; }
.m-t-xxl { margin-top: 50px; }
.m-t-n-xxs { margin-top: -1px }
.m-t-n-xs { margin-top: -5px }
.m-t-n-sm { margin-top: -10px }
.m-t-n { margin-top: -15px }
.m-t-n-md { margin-top: -20px }
.m-t-n-lg { margin-top: -30px }
.m-t-n-xl { margin-top: -40px }
.m-t-n-xxl { margin-top: -50px }
.m-r-none { margin-right: 0 !important }
.m-r-xxs { margin-right: 1px }
.m-r-xs { margin-right: 5px }
.m-r-sm { margin-right: 10px }
.m-r { margin-right: 15px }
.m-r-md { margin-right: 20px }
.m-r-lg { margin-right: 30px }
.m-r-xl { margin-right: 40px }
.m-r-xxl { margin-right: 50px }
.m-r-n-xxs { margin-right: -1px }
.m-r-n-xs { margin-right: -5px }
.m-r-n-sm { margin-right: -10px }
.m-r-n { margin-right: -15px }
.m-r-n-md { margin-right: -20px }
.m-r-n-lg { margin-right: -30px }
.m-r-n-xl { margin-right: -40px }
.m-r-n-xxl { margin-right: -50px }
.m-b-none { margin-bottom: 0 !important }
.m-b-xxs { margin-bottom: 1px; }
.m-b-xs { margin-bottom: 5px; }
.m-b-sm { margin-bottom: 10px; }
.m-b { margin-bottom: 15px; }
.m-b-md { margin-bottom: 20px; }
.m-b-lg { margin-bottom: 30px; }
.m-b-xl { margin-bottom: 40px; }
.m-b-xxl { margin-bottom: 50px; }
.m-b-n-xxs { margin-bottom: -1px }
.m-b-n-xs { margin-bottom: -5px }
.m-b-n-sm { margin-bottom: -10px }
.m-b-n { margin-bottom: -15px }
.m-b-n-md { margin-bottom: -20px }
.m-b-n-lg { margin-bottom: -30px }
.m-b-n-xl { margin-bottom: -40px }
.m-b-n-xxl { margin-bottom: -50px }
```
## 内边距
```css
.wrapper-xs { padding: 5px; }
.wrapper-sm { padding: 10px; }
.wrapper { padding: 15px; }
.wrapper-md { padding: 20px; }
.wrapper-lg { padding: 30px; }
.wrapper-xl { padding: 50px; }
.padder-xs { padding-left: 5px; padding-right: 5px }
.padder-sm { padding-left: 10px; padding-right: 10px }
.padder-lg { padding-left: 30px; padding-right: 30px }
.padder-md { padding-left: 20px; padding-right: 20px }
.padder { padding-left: 15px; padding-right: 15px }
.padder-v-xs { padding-top: 5px; padding-bottom: 5px }
.padder-v-sm { padding-top: 10px; padding-bottom: 10px }
.padder-v-lg { padding-top: 30px; padding-bottom: 30px }
.padder-v-md { padding-top: 20px; padding-bottom: 20px }
.padder-v { padding-top: 15px; padding-bottom: 15px }
.no-padder { padding: 0 !important; }
.pull-in { margin-left: -15px; margin-right: -15px; }
.pull-out { margin: -10px -15px; }
```
## 边框
```css
.b { border: 1px solid rgba(0, 0, 0, 0.05) }
.b-a { border: 1px solid @border-color }
.b-t { border-top: 1px solid @border-color }
.b-r { border-right: 1px solid @border-color }
.b-b { border-bottom: 1px solid @border-color }
.b-l { border-left: 1px solid @border-color }
.b-light { border-color: @brand-light }
.b-dark { border-color: @brand-dark }
.b-black { border-color: @brand-dark }
.b-primary { border-color: @brand-primary }
.b-success { border-color: @brand-success }
.b-info { border-color: @brand-info }
.b-warning { border-color: @brand-warning }
.b-danger { border-color: @brand-danger }
.b-white { border-color: #fff }
.b-dashed { border-style: dashed !important; }
.b-l-light { border-left-color: @brand-light }
.b-l-dark { border-left-color: @brand-dark }
.b-l-black { border-left-color: @brand-dark }
.b-l-primary { border-left-color: @brand-primary }
.b-l-success { border-left-color: @brand-success }
.b-l-info { border-left-color: @brand-info }
.b-l-warning { border-left-color: @brand-warning }
.b-l-danger { border-left-color: @brand-danger }
.b-l-white { border-left-color: #fff }
.b-l-2x { border-left-width: 2px }
.b-l-3x { border-left-width: 3px }
.b-l-4x { border-left-width: 4px }
.b-l-5x { border-left-width: 5px }
.b-2x { border-width: 2px }
.b-3x { border-width: 3px }
.b-4x { border-width: 4px }
.b-5x { border-width: 5px }
```
## 圆角
```css
.r { border-radius: @border-radius-base @border-radius-base @border-radius-base @border-radius-base; }
.r-2x { border-radius: @border-radius-base * 2; }
.r-3x { border-radius: @border-radius-base * 3; }
.r-l { border-radius: @border-radius-base 0 0 @border-radius-base; }
.r-r { border-radius: 0 @border-radius-base @border-radius-base 0; }
.r-t { border-radius: @border-radius-base @border-radius-base 0 0; }
.r-b { border-radius: 0 0 @border-radius-base @border-radius-base; }
```
## 字体相关
```css
.font-normal { font-weight: normal; }
.font-thin { font-weight: 300; }
.font-bold { font-weight: 700; }
.text-3x { font-size: 3em; }
.text-2x { font-size: 2em; }
.text-lg { font-size: @font-size-lg; }
.text-md { font-size: @font-size-md; }
.text-base { font-size: @font-size-base; }
.text-sm { font-size: @font-size-sm; }
.text-xs { font-size: @font-size-xs; }
.text-xxs { text-indent: -9999px }
.text-ellipsis { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.text-u-c { text-transform: uppercase; }
.text-l-t { text-decoration: line-through; }
.text-u-l { text-decoration: underline; }
```
## 定位
```css
.pos-rlt { position: relative; }
.pos-stc { position: static !important; }
.pos-abt { position: absolute; }
.pos-fix { position: fixed; }
```
## 背景
```html
<div class="hbox b-a bg-light">
<div class="col wrapper-sm b-r bg-white">bg-white</div>
<div class="col wrapper-sm b-r bg-dark">bg-dark</div>
<div class="col wrapper-sm b-r bg-info">bg-info</div>
<div class="col wrapper-sm b-r bg-success">bg-sucess</div>
<div class="col wrapper-sm b-r bg-warning">bg-warning</div>
<div class="col wrapper-sm b-r bg-danger">bg-danger</div>
<div class="col wrapper-sm bg-primary">bg-primary</div>
</div>
```
## 其他
```css
.show { visibility: visible; }
.line { *width: 100%; height: 2px; margin: 10px 0; font-size: 0; overflow: hidden; background-color: transparent; border-width: 0; border-top: 1px solid @border-color; }
.line-xs { margin: 0 }
.line-lg { margin-top: 15px; margin-bottom: 15px }
.line-dashed { border-style: dashed; background: transparent; }
.no-line { border-width: 0 }
.no-border, .no-borders { border-color: transparent; border-width: 0 }
.no-radius { border-radius: 0 }
.block { display: block; }
.block.hide { display: none; }
.inline { display: inline-block !important; }
.none { display: none; }
.pull-none { float: none; }
.rounded { border-radius: 500px; }
.clear { display: block; overflow: hidden; }
.no-bg { background-color: transparent; color: inherit; }
.no-select { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
```

777
examples/components/App.jsx Normal file
View File

@ -0,0 +1,777 @@
import * as React from 'react';
import NotFound from '../../src/components/404';
import Layout from '../../src/components/Layout';
import AsideNav from '../../src/components/AsideNav';
import {AlertComponent, ToastComponent} from '../../src/components/index';
import {
mapTree
} from '../../src/utils/helper';
import { Router, Route, IndexRoute, browserHistory, Link, Redirect } from 'react-router';
import * as cx from 'classnames';
import makeSchemaRenderer from './SchemaRender';
import makeMarkdownRenderer from './MdRenderer';
import SimplePageSchema from './Page/Simple';
import ErrorPageSchema from './Page/Error';
import FormPageSchema from './Page/Form';
import ModeFormSchema from './Form/Mode';
import FieldSetFormSchema from './Form/FieldSet';
import TabsFormSchema from './Form/Tabs';
import RemoteFormSchema from './Form/Remote';
import ReactionFormSchema from './Form/Reaction';
import ValidationFormSchema from './Form/Validation';
import FullFormSchema from './Form/Full';
import StaticFormSchema from './Form/Static';
import HintFormSchema from './Form/Hint';
import FieldSetInTabsFormSchema from './Form/FieldSetInTabs';
import ComboFormSchema from './Form/Combo';
import SubFormSchema from './Form/SubForm';
import RichTextSchema from './Form/RichText';
import EditorSchema from './Form/Editor';
import TestFormSchema from './Form/Test';
import TableFormSchema from './Form/Table';
import PickerFormSchema from './Form/Picker';
import FormulaFormSchema from './Form/Formula';
import CustomFormSchema from './Form/Custom';
import FormLayoutTestSchema from './Form/layoutTest';
import TableCrudSchema from './CRUD/Table';
import ItemActionsSchema from './CRUD/ItemActions';
import GridCrudSchema from './CRUD/Grid';
import ListCrudSchema from './CRUD/List';
import LoadMoreSchema from './CRUD/LoadMore';
import TestCrudSchema from './CRUD/test';
import FixedCrudSchema from './CRUD/Fix';
import AsideCrudSchema from './CRUD/Aside';
import FieldsCrudSchema from './CRUD/Fields';
import JumpNextCrudSchema from './CRUD/JumpNext';
import KeyboardsCrudSchema from './CRUD/Keyboards';
import FootableCrudSchema from './CRUD/Footable';
import NestedCrudSchema from './CRUD/Nested';
import MergeCellSchema from './CRUD/MergeCell';
import SdkTest from './Sdk/Test';
import JSONSchemaForm from './Form/Schem';
import SimpleDialogSchema from './Dialog/Simple';
import DrwaerSchema from './Dialog/Drawer';
import PageLinkPageSchema from './Linkage/Page';
import FormLinkPageSchema from './Linkage/Form';
import Form2LinkPageSchema from './Linkage/Form2';
import CRUDLinkPageSchema from './Linkage/CRUD';
import OptionsPageSchema from './Linkage/Options';
import OptionsLocalPageSchema from './Linkage/OptionsLocal';
import WizardSchema from './Wizard';
import ChartSchema from './Chart';
import HorizontalSchema from './Horizontal';
import VideoSchema from './Video';
import TasksSchema from './Tasks';
import ServicesDataSchema from './Services/Data';
import ServicesSchemaSchema from './Services/Schema';
import ServicesFormSchema from './Services/Form';
import IFrameSchema from './IFrame';
import NormalTabSchema from './Tabs/Normal';
import FormTabSchema from './Tabs/Form';
import Tab1Schema from './Tabs/Tab1';
import Tab2Schema from './Tabs/Tab2';
import Tab3Schema from './Tabs/Tab3';
import TestComponent from './Test';
import Select from '../../src/components/Select';
import Button from '../../src/components/Button';
let PathPrefix = '/examples';
const navigations = [
{
label: '示例',
children: [
{
label: '页面',
icon: 'glyphicon glyphicon-th',
badge: 3,
badgeClassName: 'bg-info',
children: [
{
label: '简单页面',
path: 'pages/simple',
component: makeSchemaRenderer(SimplePageSchema)
},
{
label: '初始化出错',
path: 'pages/error',
component: makeSchemaRenderer(ErrorPageSchema)
},
{
label: '表单页面',
path: 'pages/form',
component: makeSchemaRenderer(FormPageSchema)
}
]
},
{
label: '表单',
icon: 'fa fa-list-alt',
children: [
{
label: '表单展示模式',
path: 'form/mode',
component: makeSchemaRenderer(ModeFormSchema)
},
{
label: '所有类型汇总',
path: 'form/full',
component: makeSchemaRenderer(FullFormSchema)
},
{
label: '静态展示',
path: 'form/static',
component: makeSchemaRenderer(StaticFormSchema)
},
{
label: '输入提示',
path: 'form/hint',
component: makeSchemaRenderer(HintFormSchema)
},
{
label: 'FieldSet',
path: 'form/fieldset',
component: makeSchemaRenderer(FieldSetFormSchema)
},
{
label: 'Tabs',
path: 'form/tabs',
component: makeSchemaRenderer(TabsFormSchema)
},
{
label: 'FieldSet Tabs 组合',
path: 'form/fields-tabs',
component: makeSchemaRenderer(FieldSetInTabsFormSchema)
},
{
label: '动态数据',
path: 'form/remote',
component: makeSchemaRenderer(RemoteFormSchema)
},
{
label: '显隐状态联动',
path: 'form/reaction',
component: makeSchemaRenderer(ReactionFormSchema)
},
{
label: '表单验证',
path: 'form/validation',
component: makeSchemaRenderer(ValidationFormSchema)
},
{
label: '组合类型',
path: 'form/combo',
component: makeSchemaRenderer(ComboFormSchema)
},
{
label: '多功能选择器',
path: 'form/picker',
component: makeSchemaRenderer(PickerFormSchema)
},
{
label: '子表单',
path: 'form/sub-form',
component: makeSchemaRenderer(SubFormSchema)
},
{
label: 'JSon Schema表单',
path: 'form/json-schema',
component: JSONSchemaForm
},
{
label: '富文本',
path: 'form/rich-text',
component: makeSchemaRenderer(RichTextSchema)
},
{
label: '代码编辑器',
path: 'form/ide',
component: makeSchemaRenderer(EditorSchema)
},
{
label: '自定义组件',
path: 'form/custom',
component: makeSchemaRenderer(CustomFormSchema)
},
{
label: '表格编辑',
path: 'form/table',
component: makeSchemaRenderer(TableFormSchema)
},
{
label: '公式示例',
path: 'form/formula',
component: makeSchemaRenderer(FormulaFormSchema)
},
{
label: '布局测试',
path: 'form/layout-test',
component: makeSchemaRenderer(FormLayoutTestSchema)
},
{
label: '测试',
path: 'form/test',
component: makeSchemaRenderer(TestFormSchema)
},
]
},
{
label: '增删改查',
icon: 'fa fa-table',
children: [
{
label: '表格模式',
path: 'crud/table',
component: makeSchemaRenderer(TableCrudSchema)
},
{
label: '卡片模式',
path: 'crud/grid',
component: makeSchemaRenderer(GridCrudSchema)
},
{
label: '列表模式',
path: 'crud/list',
component: makeSchemaRenderer(ListCrudSchema)
},
{
label: '加载更多模式',
path: 'crud/load-more',
component: makeSchemaRenderer(LoadMoreSchema)
},
{
label: '操作交互显示',
path: 'crud/item-actions',
component: makeSchemaRenderer(ItemActionsSchema)
},
{
label: '列类型汇总',
path: 'crud/columns',
component: makeSchemaRenderer(FieldsCrudSchema)
},
{
label: '可折叠',
path: 'crud/footable',
component: makeSchemaRenderer(FootableCrudSchema)
},
{
label: '嵌套',
path: 'crud/nested',
component: makeSchemaRenderer(NestedCrudSchema)
},
{
label: '合并单元格',
path: 'crud/merge-cell',
component: makeSchemaRenderer(MergeCellSchema)
},
{
label: '带边栏',
path: 'crud/aside',
component: makeSchemaRenderer(AsideCrudSchema)
},
{
label: '固定表头/列',
path: 'crud/fixed',
component: makeSchemaRenderer(FixedCrudSchema)
},
{
label: '键盘操作编辑',
path: 'crud/keyboards',
component: makeSchemaRenderer(KeyboardsCrudSchema)
},
{
label: '操作并下一个',
path: 'crud/jump-next',
component: makeSchemaRenderer(JumpNextCrudSchema)
},
{
label: '测试',
path: 'crud/test',
component: makeSchemaRenderer(TestCrudSchema)
}
]
},
{
label: '弹框',
icon: 'fa fa-bomb',
children: [
{
label: '对话框',
path: 'dialog/simple',
component: makeSchemaRenderer(SimpleDialogSchema)
},
{
label: '侧边弹出',
path: 'dialog/drawer',
component: makeSchemaRenderer(DrwaerSchema)
}
]
},
{
label: '选项卡',
icon: 'fa fa-clone',
children: [
{
label: '常规选项卡',
path: 'tabs/normal',
component: makeSchemaRenderer(NormalTabSchema)
},
{
label: '表单中选项卡分组',
path: 'tabs/form',
component: makeSchemaRenderer(FormTabSchema)
},
{
label: '选项卡页面1',
path: 'tabs/tab1',
component: makeSchemaRenderer(Tab1Schema)
},
{
label: '选项卡页面2',
path: 'tabs/tab2',
component: makeSchemaRenderer(Tab2Schema)
},
{
label: '选项卡页面3',
path: 'tabs/tab3',
component: makeSchemaRenderer(Tab3Schema)
},
]
},
{
label: '联动',
icon: 'fa fa-bolt',
children: [
{
label: '地址栏变化自动更新',
path: 'linkpage/page',
component: makeSchemaRenderer(PageLinkPageSchema)
},
{
label: '选项联动',
path: 'linkpage/options-local',
component: makeSchemaRenderer(OptionsLocalPageSchema)
},
{
label: '选项远程联动',
path: 'linkpage/options',
component: makeSchemaRenderer(OptionsPageSchema)
},
{
label: '表单和表单联动',
path: 'linkpage/form',
component: makeSchemaRenderer(FormLinkPageSchema)
},
{
label: '表单自动更新',
path: 'linkpage/form2',
component: makeSchemaRenderer(Form2LinkPageSchema)
},
{
label: '表单和列表联动',
path: 'linkpage/crud',
component: makeSchemaRenderer(CRUDLinkPageSchema)
}
]
},
{
label: '动态加载',
icon: 'fa fa-magic',
children: [
{
label: '动态加载数据',
path: 'services/data',
component: makeSchemaRenderer(ServicesDataSchema)
},
{
label: '动态加载页面',
path: 'services/schema',
component: makeSchemaRenderer(ServicesSchemaSchema)
},
{
label: '动态加载部分表单',
path: 'services/form',
component: makeSchemaRenderer(ServicesFormSchema)
}
]
},
{
label: '向导',
icon: 'fa fa-desktop',
path: 'wizard',
component: makeSchemaRenderer(WizardSchema)
},
{
label: '排版',
icon: 'fa fa-columns',
path: 'horizontal',
component: makeSchemaRenderer(HorizontalSchema)
},
{
label: '图表',
icon: 'fa fa-bar-chart',
path: 'chart',
component: makeSchemaRenderer(ChartSchema)
},
{
label: '视频',
icon: 'fa fa-video-camera',
path: 'video',
component: makeSchemaRenderer(VideoSchema)
},
{
label: '异步任务',
icon: 'fa fa-tasks',
path: 'task',
component: makeSchemaRenderer(TasksSchema)
},
{
label: 'IFrame',
icon: 'fa fa-cloud',
path: 'iframe',
component: makeSchemaRenderer(IFrameSchema)
},
{
label: 'SDK',
icon: 'fa fa-rocket',
path: 'sdk',
component: SdkTest
},
{
label: 'Test',
icon: 'fa fa-code',
path: 'test',
component: TestComponent
}
]
},
{
prefix: ({classnames: cx}) => (<li className={cx('AsideNav-divider')}></li>),
label: '文档',
children: [
{
label: '快速开始',
icon: 'fa fa-flash',
path: '/v2/docs/getting-started',
getComponent: (location, cb) => require(['../../docs/getting_started.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
},
{
label: '高级用法',
icon: 'fa fa-rocket',
path: '/v2/docs/advanced',
getComponent: (location, cb) => require(['../../docs/advanced.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
},
{
label: '渲染器手册',
icon: 'fa fa-diamond',
path: '/v2/docs/renderers',
getComponent: (location, cb) => require(['../../docs/renderers.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
},
{
label: '开源渲染器',
path: '/v2/docs/sdk',
icon: 'fa fa-cubes',
getComponent: (location, cb) => require(['../../docs/sdk.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
},
{
label: '自定义组件',
path: '/v2/docs/dev',
icon: 'fa fa-code',
getComponent: (location, cb) => require(['../../docs/dev.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
},
{
label: '样式说明',
path: '/v2/docs/style',
icon: 'fa fa-laptop',
getComponent: (location, cb) => require(['../../docs/style.md'], (doc) => {
cb(null, makeMarkdownRenderer(doc));
})
}
]
}
];
function isActive(link, location) {
return !!(link && link === location.pathname);
}
const themes = [
{
label: '默认主题',
ns: 'a-',
value: 'default'
},
{
label: '百度云舍',
ns: 'cxd-',
value: 'cxd'
}
];
export class App extends React.PureComponent {
state = {
asideFolded: localStorage.getItem('asideFolded') === 'true',
offScreen: false,
headerVisible: true,
themeIndex: 0,
themes: themes,
theme: themes[localStorage.getItem('themeIndex') || 0]
};
constructor(props) {
super(props);
this.toggleAside = this.toggleAside.bind(this);
this.setAsideFolded = this.setAsideFolded.bind(this);
this.setHeaderVisible = this.setHeaderVisible.bind(this);
}
componentDidMount() {
if (this.state.theme.value !== "default") {
document.querySelectorAll('link[title]').forEach(item => {
item.disabled = true
});
document.querySelector(`link[title=${this.state.theme.value}]`).disabled = false;
}
}
componentDidUpdate(preProps, preState) {
if (preState.theme.value !== this.state.theme.value) {
document.querySelector(`link[title=${preState.theme.value}]`).disabled = true;
document.querySelector(`link[title=${this.state.theme.value}]`).disabled = false;
}
}
toggleAside() {
this.setAsideFolded(!this.state.asideFolded);
}
setAsideFolded(folded = false) {
localStorage.setItem('asideFolded', JSON.stringify(folded));
this.setState({
asideFolded: folded
});
}
setHeaderVisible(visible = false) {
this.setState({
headerVisible: visible
});
}
renderAside() {
const location = this.props.location;
if (location.pathname === '/edit') {
return null;
}
const theme = this.state.theme;
return (
<AsideNav
theme={theme.value}
navigations={navigations}
renderLink={({link, active, toggleExpand, classnames: cx}) => {
let children = [];
if (link.children) {
children.push(
<span
key="expand-toggle"
className={cx('AsideNav-itemArrow')}
></span>
);
}
link.badge && children.push(
<b key="badge" className={cx(`AsideNav-itemBadge`, link.badgeClassName || 'bg-info')}>{link.badge}</b>
);
link.icon && children.push(
<i key="icon" className={cx(`AsideNav-itemIcon`, link.icon)} />
);
children.push(
<span className={cx(`AsideNav-itemLabel`)} key="label">{link.label}</span>
);
return link.path ? (<Link to={link.path[0] === '/' ? link.path : `${PathPrefix}/${link.path}`}>{children}</Link>) : (<a onClick={link.children ? () => toggleExpand(link) : null}>{children}</a>);
}}
isActive={link => isActive(link.path && link.path[0] === '/' ? link.path : `${PathPrefix}/${link.path}`, location)}
/>
);
}
renderHeader() {
const location = this.props.location;
const theme = this.state.theme;
if (location.pathname === '/edit') {
return (
<div id="headerBar" className="box-shadow bg-dark">
<div className={`${theme.ns}Layout-brand`}>AMis 可视化编辑器</div>
</div>
);
}
return (
<div>
<div className={`${theme.ns}Layout-brandBar`}>
<button
onClick={() => this.setState({offScreen: !this.state.offScreen})}
className="pull-right visible-xs"
>
<i className="glyphicon glyphicon-align-justify"></i>
</button>
<div className={`${theme.ns}Layout-brand`}>
<i className="fa fa-paw"></i>
<span className="hidden-folded m-l-sm">AMis Renderer</span>
</div>
</div>
<div className={`${theme.ns}Layout-headerBar`}>
<div className="nav navbar-nav hidden-xs">
<Button
theme={this.state.theme.value}
level="link"
className="no-shadow navbar-btn"
onClick={this.toggleAside}
tooltip="展开或收起侧边栏"
placement="bottom"
iconOnly
>
<i className={this.state.asideFolded ? 'fa fa-indent' : 'fa fa-dedent'} />
</Button>
</div>
<div className="hidden-xs p-t-sm pull-right">
主题{(
<Select
theme={this.state.theme.value}
value={this.state.theme}
options={this.state.themes}
onChange={(theme) => {
this.setState({theme});
localStorage.setItem('themeIndex', this.state.themes.indexOf(theme));
}}
/>
)}
</div>
</div>
</div>
);
}
render() {
// const pathname = this.props.location.pathname;
const theme = this.state.theme;
return (
<Layout
theme={theme.value}
offScreen={this.state.offScreen}
header={this.state.headerVisible ? this.renderHeader() : null}
folded={this.state.asideFolded}
aside={this.renderAside()}
>
<ToastComponent theme={theme.value} />
<AlertComponent theme={theme.value} />
{React.cloneElement(this.props.children, {
...this.props.children.props,
setAsideFolded: this.setAsideFolded,
setHeaderVisible: this.setHeaderVisible,
theme: theme.value,
classPrefix: theme.ns
})}
</Layout>
);
}
}
function navigations2route(pathPrefix = '/examples') {
let routes = [];
navigations.forEach(root => {
root.children && mapTree(root.children, item => {
if (item.path && item.component) {
routes.push(
<Route key={routes.length + 1} path={item.path[0] === '/' ? item.path : `${pathPrefix}/${item.path}`} component={item.component} />
)
} else if (item.path && item.getComponent) {
routes.push(
<Route key={routes.length + 1} path={item.path[0] === '/' ? item.path : `${pathPrefix}/${item.path}`} getComponent={item.getComponent} />
)
}
});
});
return routes;
}
export default function entry({pathPrefix}) {
PathPrefix = pathPrefix || '/examples';
return (
<Router history={ browserHistory }>
<Route component={App}>
<Redirect from={`${PathPrefix}/`} to={`${PathPrefix}/pages/simple`} />
{navigations2route(PathPrefix)}
<Route path="*" component={NotFound} />
</Route>
</Router>
);
}

View File

@ -0,0 +1,332 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "带边栏联动",
aside: {
type: 'form',
wrapWithPanel: false,
target: 'window', // location
controls: [
{
type: 'tree',
name: 'cat',
inputClassName: 'no-border',
submitOnChange: true,
options: [
{
label: '分类1',
value: 'cat1'
},
{
label: '分类2',
value: 'cat2'
},
{
label: '分类3',
value: 'cat3'
},
{
label: '分类4',
value: 'cat4'
}
]
}
]
},
toolbar: [
{
type: "button",
actionType: "dialog",
label: "新增",
primary: true,
dialog: {
title: "新增",
body: {
type: "form",
name: "sample-edit-form",
api: "post:/api/sample",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
}
],
body: {
type: "crud",
draggable: true,
api: "/api/sample",
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这里的表单项可以配置多个"
}
]
},
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/$ids",
confirmText: "确定要批量删除?"
},
{
label: "批量修改",
actionType: "dialog",
dialog: {
title: "批量编辑",
name: "sample-bulk-edit",
body: {
type: "form",
api: "/api/sample/bulkUpdate2",
controls: [
{
type: 'hidden',
name: 'ids'
},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
}
}
],
quickSaveApi: "/api/sample/bulkUpdate",
quickSaveItemApi: "/api/sample/$id",
columns: [
{
name: "id",
label: "ID",
width: 20,
sortable: true,
type: "text",
toggled: true
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "grade",
label: "CSS grade",
quickEdit: {
mode: "inline",
type: "select",
options: ["A", "B", "C", "D", "X"],
saveImmediately: true
},
type: "text",
toggled: true
},
{
type: "operation",
label: "操作",
width: 130,
buttons: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "dialog",
dialog: {
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
toggled: true
}
]
}
};

View File

@ -0,0 +1,80 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查列类型汇总",
body: {
type: "crud",
api: "/api/mock2/crud/list",
columns: [
{
name: "id",
label: "ID",
type: "text"
},
{
name: "text",
label: "文本",
type: "text"
},
{
type: 'image',
label: '图片',
name: 'image',
popOver: {
title: '查看大图',
body: '<div class="w-xxl"><img class="w-full" src="${image}"/></div>'
}
},
{
name: 'date',
type: 'date',
label: '日期'
},
{
name: "progress",
label: "进度",
type: "progress"
},
{
name: "boolean",
label: "状态",
type: "status"
},
{
name: "boolean",
label: "开关",
type: "switch",
// readOnly: false //
},
{
name: "type",
label: "映射",
type: "mapping",
map: {
"*": "其他:${type}",
"1": "<span class='label label-info'>漂亮</span>",
"2": "<span class='label label-success'>开心</span>",
"3": "<span class='label label-danger'>惊吓</span>",
"4": "<span class='label label-warning'>紧张</span>"
}
},
{
name: 'list',
type: 'list',
label: 'List',
placeholder: '-',
size: "sm",
listItem: {
title: '${title}',
subTitle: '${description}'
}
},
{
name: 'json',
type: 'json',
label: 'Json'
}
]
}
};

View File

@ -0,0 +1,301 @@
const table = {
type: "table",
data: [
{
engine: "Other browsers",
browser: "All others",
platform: "-",
version: "-",
grade: "U",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 56,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Other browsers",
browser: "All others",
platform: "-",
version: "-",
grade: "U",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 56,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Other browsers",
browser: "All others",
platform: "-",
version: "-",
grade: "U",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 56,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Misc",
browser: "PSP browser",
platform: "PSP",
version: "-",
grade: "C",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 55,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
},
{
engine: "Other browsers",
browser: "All others",
platform: "-",
version: "-",
grade: "U",
progress: 50,
status: true,
image:
"http://hiphotos.baidu.com/fex/%70%69%63/item/0d338744ebf81a4cff2f4cd6de2a6059252da694.jpg",
weight: 56,
others: null,
createdAt: "2017-11-17T08:47:50.000Z",
updatedAt: "2017-11-17T08:47:50.000Z"
}
].map((item, key) => ({
...item,
id: key + 1
})),
columns: [
{
name: "id",
label: "ID",
width: 20,
sortable: true,
type: "text",
toggled: true,
fixed: 'left'
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true,
fixed: 'left'
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "grade",
label: "CSS grade",
quickEdit: {
mode: "inline",
type: "select",
options: ["A", "B", "C", "D", "X"],
saveImmediately: true
},
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true,
fixed: 'right'
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true,
fixed: 'right'
},
]
};
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "固顶和列固定示例",
remark: "bla bla bla",
body: [
table,
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
'<div>分割</div>',
table
]
};

View File

@ -0,0 +1,197 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "开启单条底部展示功能",
body: {
type: "crud",
draggable: true,
api: "/api/sample",
footable: {
expand: 'first'
},
columns: [
{
name: "id",
label: "ID",
width: 20,
sortable: true,
type: "text",
toggled: true
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "grade",
label: "CSS grade",
breakpoint: '*',
quickEdit: {
mode: "inline",
type: "select",
options: ["A", "B", "C", "D", "X"],
inputClassName: 'w-xs',
saveImmediately: true
},
type: "text",
toggled: true
},
{
type: "operation",
label: "操作",
width: 100,
breakpoint: '*',
buttons: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "drawer",
drawer: {
position: 'left',
size: 'lg',
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "select",
name: "grade",
label: "CSS grade",
options: ["A", "B", "C", "D", "X"],
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
toggled: true
}
]
}
};

View File

@ -0,0 +1,221 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查示例",
remark: "bla bla bla",
body: {
type: "crud",
api: "/api/sample",
// api: "/api/mock2/crud/table?waitSeconds=100000",
mode: "cards",
defaultParams: {
perPage: 12,
},
// fixAlignment: true,
masonryLayout: true,
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这只是个示例, 目前搜索对查询结果无效."
}
]
},
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/${ids|raw}",
confirmText: "确定要批量删除?"
},
{
label: "批量修改",
actionType: "dialog",
dialog: {
title: "批量编辑",
name: "sample-bulk-edit",
body: {
type: "form",
api: "/api/sample/bulkUpdate2",
controls: [
{
type: "hidden",
name: "ids"
},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
}
}
],
quickSaveApi: "/api/sample/bulkUpdate",
quickSaveItemApi: "/api/sample/$id",
draggable: true,
card: {
header: {
title: "$engine",
subTitle: "$platform",
subTitlePlaceholder: "暂无说明",
avatar:
'<%= data.avatar || "http://bos.nj.bpc.baidu.com/showx/146bc2ce1b30f3824838f4208ad2663c" %>',
avatarClassName: "pull-left thumb b-3x m-r"
},
actions: [
{
type: "button",
label: "查看",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
label: "编辑",
actionType: "dialog",
dialog: {
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
label: "删除",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
body: [
{
name: "engine",
label: "engine",
sortable: true,
quickEdit: true
},
{
name: "browser",
label: "Browser"
},
{
name: "platform",
label: "Platform"
},
{
name: "version",
label: "version"
}
]
}
}
};

View File

@ -0,0 +1,223 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查示例",
remark: "bla bla bla",
body: {
type: "crud",
api: "/api/sample",
headerToolbar: ['bulkActions', {
type: 'columns-toggler',
className: 'pull-right',
align: 'right'
}, {
type: 'drag-toggler',
className: 'pull-right'
}, {
type: 'pagination',
className: 'pull-right'
}],
itemActions: [
{
type: "button",
label: "查看",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
label: "编辑",
actionType: "drawer",
drawer: {
position: 'left',
size: 'lg',
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "select",
name: "grade",
label: "CSS grade",
options: ["A", "B", "C", "D", "X"],
}
]
}
}
},
{
type: "button",
label: "删除",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/${ids|raw}",
confirmText: "确定要批量删除?",
type: "button"
},
{
label: "批量修改",
actionType: "dialog",
dialog: {
title: "批量编辑",
name: "sample-bulk-edit",
body: {
type: "form",
api: "/api/sample/bulkUpdate2",
controls: [
{
type: 'hidden',
name: 'ids'
},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
},
type: "button"
}
],
columns: [
{
name: "id",
label: "ID",
width: 20,
sortable: true,
type: "text",
toggled: true,
remark: 'Bla bla Bla'
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: false
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
type: "text",
toggled: true
},
{
name: "grade",
label: "CSS grade",
type: "text",
toggled: true
}
]
}
};

View File

@ -0,0 +1,131 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "操作并下一个",
"remark": "当存在下一条时,支持直接打开下一条操作。",
"body": {
"type": "crud",
"title": "",
"api": "/api/sample/list",
"columnsTogglable": false,
"columns": [
{
"name": "id",
"label": "ID",
"width": 20,
"type": "text",
"toggled": true
},
{
"name": "engine",
"label": "Rendering engine",
"type": "text",
"toggled": true
},
{
"name": "browser",
"label": "Browser",
"type": "text",
"toggled": true
},
{
"type": "operation",
"label": "操作",
"width": 130,
"buttons": [
{
"type": "button",
"icon": "fa fa-pencil",
"actionType": "dialog",
"nextCondition": "true",
"_nextCondition": "可以设置条件比如: data.grade == \"B\"",
"dialog": {
"title": "编辑",
"actions": [
{
"type": "button",
"actionType": "prev",
"level": "info",
"visibleOn": "data.hasPrev",
"label": "上一个"
},
{
"type": "button",
"actionType": "cancel",
"label": "关闭"
},
{
"type": "submit",
"actionType": "next",
"visibleOn": "data.hasNext",
"label": "保存并下一个",
"level": "primary"
},
{
"type": "submit",
"visibleOn": "!data.hasNext",
"label": "保存",
"level": "primary"
},
{
"type": "button",
"actionType": "next",
"level": "info",
"visibleOn": "data.hasNext",
"label": "下一个"
}
],
"body": {
"type": "form",
"name": "sample-edit-form",
"api": "/api/sample/$id",
"controls": [
{
"type": "text",
"name": "engine",
"label": "Engine",
"required": true
},
{
"type": "divider"
},
{
"type": "text",
"name": "browser",
"label": "Browser",
"required": true
},
{
"type": "divider"
},
{
"type": "text",
"name": "platform",
"label": "Platform(s)",
"required": true
},
{
"type": "divider"
},
{
"type": "text",
"name": "version",
"label": "Engine version"
},
{
"type": "divider"
},
{
"type": "text",
"name": "grade",
"label": "CSS grade"
}
]
}
}
}
],
"toggled": true
}
]
}
}

View File

@ -0,0 +1,76 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "Table 全键盘操作示例",
"remark": "bla bla bla",
"body": [
{
"type": "plain",
"className": "text-danger",
"text": "请通过上下左右键切换单元格,按 `Space` 键进入编辑模式,按 `Enter` 提交编辑,并最后点左上角的全部保存完成操作。"
},
{
"type": "crud",
"className": "m-t",
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"quickSaveItemApi": "/api/sample/$id",
"columns": [
{
"name": "id",
"label": "ID",
"width": 20,
"sortable": true,
"type": "text",
"toggled": true
},
{
"name": "engine",
"label": "Rendering engine",
"sortable": true,
"quickEdit": true,
"type": "text",
"toggled": true
},
{
"name": "browser",
"label": "Browser",
"sortable": true,
"quickEdit": true,
"type": "text",
"toggled": true
},
{
"name": "platform",
"label": "Platform(s)",
"sortable": true,
"quickEdit": true,
"type": "text",
"toggled": true
},
{
"name": "version",
"label": "Engine version",
"quickEdit": true,
"type": "text",
"toggled": true
},
{
"name": "grade",
"label": "CSS grade",
"quickEdit": {
"type": "select",
"options": [
"A",
"B",
"C",
"D",
"X"
]
},
"type": "text",
"toggled": true
}
]
}
]
}

View File

@ -0,0 +1,214 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查示例",
remark: "bla bla bla",
body: {
type: "crud",
api: "/api/sample",
mode: "list",
draggable: true,
saveOrderApi: {
url: "/api/sample/saveOrder"
},
orderField: "weight",
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这只是个示例, 目前搜索对查询结果无效."
}
]
},
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/${ids|raw}",
confirmText: "确定要批量删除?",
type: "button",
level: "danger"
},
{
label: "批量修改",
actionType: "dialog",
level: "info",
type: "button",
dialog: {
title: "批量编辑",
body: {
type: 'form',
api: "/api/sample/bulkUpdate2",
controls: [
{type: 'hidden', name: 'ids'},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
}
}
],
quickSaveApi: "/api/sample/bulkUpdate",
quickSaveItemApi: "/api/sample/$id",
listItem: {
actions: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "dialog",
dialog: {
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
body: [
{
name: "engine",
label: "Rendering engine",
sortable: true,
quickEdit: true
},
[
{
name: "browser",
label: "Browser"
},
{
name: "platform",
label: "Platform(s)"
}
],
{
name: "version",
label: "Engine version"
}
]
}
}
};

View File

@ -0,0 +1,223 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查示例",
remark: "bla bla bla",
body: {
type: "crud",
api: "/api/sample",
mode: "list",
draggable: true,
saveOrderApi: {
url: "/api/sample/saveOrder"
},
orderField: "weight",
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这只是个示例, 目前搜索对查询结果无效."
}
]
},
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/${ids|raw}",
confirmText: "确定要批量删除?",
type: "button",
level: "danger"
},
{
label: "批量修改",
actionType: "dialog",
level: "info",
type: "button",
dialog: {
title: "批量编辑",
body: {
type: 'form',
api: "/api/sample/bulkUpdate2",
controls: [
{type: 'hidden', name: 'ids'},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
}
}
],
quickSaveApi: "/api/sample/bulkUpdate",
quickSaveItemApi: "/api/sample/$id",
headerToolbar: [
"bulkActions"
],
footerToolbar: [
"load-more"
],
listItem: {
actions: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "dialog",
dialog: {
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
body: [
{
name: "engine",
label: "Rendering engine",
sortable: true,
quickEdit: true,
labelClassName: "w-sm pull-left text-muted"
},
[
{
name: "browser",
label: "Browser",
labelClassName: "w-sm pull-left text-muted"
},
{
name: "platform",
label: "Platform(s)",
labelClassName: "w-sm pull-left text-muted"
}
],
{
name: "version",
label: "Engine version",
labelClassName: "w-sm pull-left text-muted"
}
]
}
}
};

View File

@ -0,0 +1,104 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "支持自动合并单元格,从左到右,可配置从左侧起多少列内启动自动合并单元格,当前配置 3",
body: {
type: "table",
data: {
items: [
{
"engine": "Trident",
"browser": "Internet Explorer 4.2",
"platform": "Win 95+",
"version": "4",
"grade": "A"
},
{
"engine": "Trident",
"browser": "Internet Explorer 4.2",
"platform": "Win 95+",
"version": "4",
"grade": "B"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win 95+",
"version": "4",
"grade": "C"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win 98",
"version": "3",
"grade": "A"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win 98",
"version": "4",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "4",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "5",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "5",
"grade": "B"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "5",
"grade": "C"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "5",
"grade": "D"
}
]
},
combineNum: 3, //
columns: [
{
name: "engine",
label: "Rendering engine"
},
{
name: "browser",
label: "Browser"
},
{
name: "platform",
label: "Platform(s)"
},
{
name: "version",
label: "Engine version"
},
{
name: "grade",
label: "CSS grade",
}
]
}
};

View File

@ -0,0 +1,190 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "支持多层嵌套,列数据中有 children 字段即可。建议不超过10层",
body: {
type: "crud",
api: "/api/mock2/crud/table2",
columns: [
{
name: "id",
label: "ID",
sortable: true,
type: "text",
toggled: true
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: true
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true
},
{
name: "grade",
label: "CSS grade",
quickEdit: {
mode: "inline",
type: "select",
options: ["A", "B", "C", "D", "X"],
inputClassName: 'w-xs',
saveImmediately: true
},
type: "text",
toggled: true
},
{
type: "operation",
label: "操作",
width: 100,
buttons: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "drawer",
drawer: {
position: 'left',
size: 'lg',
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "select",
name: "grade",
label: "CSS grade",
options: ["A", "B", "C", "D", "X"],
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
toggled: true
}
]
}
};

View File

@ -0,0 +1,342 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "增删改查示例",
remark: "bla bla bla",
toolbar: [
{
type: "button",
actionType: "dialog",
label: "新增",
icon: 'fa fa-plus pull-left',
primary: true,
dialog: {
title: "新增",
body: {
type: "form",
name: "sample-edit-form",
api: "post:/api/sample",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
}
],
body: {
type: "crud",
draggable: true,
api: "/api/sample?waitSeconds=1",
keepItemSelectionOnPageChange: true,
labelTpl: '${id} ${engine}',
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这里的表单项可以配置多个"
}
]
},
bulkActions: [
{
label: "批量删除",
actionType: "ajax",
api: "delete:/api/sample/${ids|raw}",
confirmText: "确定要批量删除?"
},
{
label: "批量修改",
actionType: "dialog",
dialog: {
title: "批量编辑",
name: "sample-bulk-edit",
body: {
type: "form",
api: "/api/sample/bulkUpdate2",
controls: [
{
type: 'hidden',
name: 'ids'
},
{
type: "text",
name: "engine",
label: "Engine"
}
]
}
}
}
],
quickSaveApi: "/api/sample/bulkUpdate",
quickSaveItemApi: "/api/sample/$id",
filterTogglable: true,
headerToolbar: ['filter-toggler', 'bulkActions', {
type: 'tpl',
tpl: '定制内容示例:当前有 ${count} 条数据。',
className: 'v-middle'
}, {
type: 'columns-toggler',
align: 'right'
}, {
type: 'drag-toggler',
align: 'right'
}, {
type: 'pagination',
align: 'right'
}],
footerToolbar: ['statistics', 'switch-per-page', 'pagination'],
columns: [
{
name: "id",
label: "ID",
width: 20,
sortable: true,
type: "text",
toggled: true,
remark: 'Bla bla Bla'
},
{
name: "engine",
label: "Rendering engine",
sortable: true,
searchable: true,
type: "text",
toggled: true
},
{
name: "browser",
label: "Browser",
sortable: true,
type: "text",
toggled: false
},
{
name: "platform",
label: "Platform(s)",
sortable: true,
type: "text",
toggled: true
},
{
name: "version",
label: "Engine version",
quickEdit: true,
type: "text",
toggled: true,
filterable:{
options:[
{
label:'4',
value:'4'
},
{
label:'5',
value:'5'
},
{
label:'6',
value:'6'
},
]
}
},
{
name: "grade",
label: "CSS grade",
quickEdit: {
mode: "inline",
type: "select",
inputClassName: 'w-xs',
options: ["A", "B", "C", "D", "X"],
saveImmediately: true
},
type: "text",
toggled: true
},
{
type: "operation",
label: "操作",
width: 100,
buttons: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
tooltip: "查看",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
},
{
type: "divider"
},
{
type: "html",
html:
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换todo.</p>"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
tooltip: "编辑",
actionType: "drawer",
drawer: {
position: 'left',
size: 'lg',
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "select",
name: "grade",
label: "CSS grade",
options: ["A", "B", "C", "D", "X"],
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
tooltip: "删除",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
toggled: true
}
]
}
};

View File

@ -0,0 +1,92 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "Test 信息:${page}",
body: {
"type": "crud",
"api": "/api/sample",
"syncLocation": false,
"title": null,
"columns": [
{
"name": "id",
"label": "ID",
"width": 20
},
{
"name": "engine",
"label": "Rendering engine",
"sortable": true
},
{
"name": "grade",
"type": "map",
"label": "Rendering engine",
"map": {
"A": "<span class='label label-info'>A</span>",
"B": "<span class='label label-success'>B</span>",
"C": "<span class='label label-primary'>C</span>",
"X": "<span class='label label-danger'>X</span>",
"*": "Unkown"
}
},
{
"type": "operation",
"label": "操作",
"width": 200,
"buttons": [
{
"type": "button-group",
"buttons": [
{
"type": "button",
"label": "查看",
"actionType": "dialog",
"dialog": {
"disabled": true,
"body": {
"type": "form",
"controls": [
{
"name": "engine",
"label": "Rendering engine",
"type": "static"
}
]
}
}
},
{
"type": "button",
"label": "编辑",
"actionType": "dialog",
"dialog": {
"body": {
"api": "/api/sample/$id",
"type": "form",
"controls": [
{
"name": "engine",
"label": "Rendering engine",
"type": "text"
}
]
}
}
},
{
"type": "button",
"label": "删除",
"level": "danger",
"actionType": "ajax",
"confirmText": "确定?",
"api": "delete:/api/sample/$id"
}
]
}
]
}
]
}
};

View File

@ -0,0 +1,197 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "图表示例",
"body": [
{
"type": "grid",
"columns": [
{
"type": "panel",
"title": "本地配置示例 支持交互",
"name": "chart-local",
"body": [
{
"type": "chart",
"config": {
"title": {
"text": "极坐标双数值轴"
},
"legend": {
"data": [
"line"
]
},
"polar": {
"center": [
"50%",
"54%"
]
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "cross"
}
},
"angleAxis": {
"type": "value",
"startAngle": 0
},
"radiusAxis": {
"min": 0
},
"series": [
{
"coordinateSystem": "polar",
"name": "line",
"type": "line",
"showSymbol": false,
"data": [
[
0,
0
],
[
0.03487823687206265,
1
],
[
0.06958655048003272,
2
],
[
0.10395584540887964,
3
],
[
0.13781867790849958,
4
],
[
0.17101007166283433,
5
],
[
0.2033683215379001,
6
],
[
0.2347357813929454,
7
],
[
0.26495963211660245,
8
],
[
0.2938926261462365,
9
],
[
0.3213938048432697,
10
]
]
}
],
"animationDuration": 2000
},
clickAction: {
actionType: 'dialog',
dialog: {
title: '详情',
body: [
{
type: 'tpl',
tpl: '<span>当前选中值 ${value|json}<span>'
},
{
"type": "chart",
"api": "/api/mock2/chart/chart1"
}
]
}
}
}
]
},
{
"type": "panel",
"title": "远程图表示例(返回值带function)",
"name": "chart-remote",
"body": [
{
"type": "chart",
"api": "/api/mock2/chart/chart1"
}
]
}
]
},
{
"type": "panel",
"title": "Form+chart组合",
"body": [
{
"type": "form",
"title": "过滤条件",
"target": "chart1,chart2",
"submitOnInit":true,
"className": "m-b",
"wrapWithPanel": false,
"mode": "inline",
"controls": [
{
"type": "date",
"label": "开始日期",
"name": "starttime",
"value": "-8days",
"maxDate": "${endtime}"
},
{
"type": "date",
"label": "结束日期",
"name": "endtime",
"value": "-1days",
"minDate": "${starttime}"
},
{
"type": "text",
"label": "条件",
"name": "name",
"addOn": {
"type": "submit",
"label": "搜索",
"level": "primary"
}
}
],
"actions": []
},
{
type: 'divider'
},
{
"type": "grid",
"className": "m-t-lg",
"columns": [
{
"type": "chart",
"name": "chart1",
"initFetch": false,
"api": "/api/mock2/chart/chart?name=$name&starttime=${starttime}&endtime=${endtime}"
},
{
"type": "chart",
"name": "chart2",
"initFetch": false,
"api": "/api/mock2/chart/chart2?name=$name"
}
]
}
]
}
]
}

View File

@ -0,0 +1,401 @@
export default {
type: 'page',
title: 'Drawer',
body: [
{
type: 'button-toolbar',
className: "block",
buttons: [
{
type: 'button',
label: '左侧弹出-极小框',
actionType: 'drawer',
drawer: {
position: 'left',
size: 'xs',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '左侧弹出-小框',
actionType: 'drawer',
drawer: {
position: 'left',
size: 'sm',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '左侧弹出-中框',
actionType: 'drawer',
drawer: {
position: 'left',
size: 'md',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '左侧弹出-大框',
actionType: 'drawer',
drawer: {
position: 'left',
size: 'lg',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '左侧弹出-超大',
actionType: 'drawer',
level: 'danger',
drawer: {
position: 'left',
size: 'xl',
title: '提示',
body: '这是个简单的弹框'
}
},
]
},
{
type: 'button-toolbar',
className: 'block m-t',
buttons: [
{
type: 'button',
label: '右侧弹出-极小框',
level: 'success',
actionType: 'drawer',
drawer: {
position: 'right',
size: 'xs',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '右侧弹出-小框',
level: 'success',
actionType: 'drawer',
drawer: {
position: 'right',
size: 'sm',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '右侧弹出-中框',
level: 'success',
actionType: 'drawer',
drawer: {
position: 'right',
size: 'md',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '右侧弹出-大框',
level: 'success',
actionType: 'drawer',
drawer: {
position: 'right',
size: 'lg',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '右侧弹出-超大',
level: 'danger',
actionType: 'drawer',
drawer: {
size: 'xl',
position: 'right',
title: '提示',
body: '这是个简单的弹框'
}
}
]
},
{
type: 'button-toolbar',
className: 'block m-t',
buttons: [
{
type: 'button',
label: '顶部弹出-极小框',
actionType: 'drawer',
level: 'info',
drawer: {
position: 'top',
size: 'xs',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '顶部弹出-小框',
level: 'info',
actionType: 'drawer',
drawer: {
position: 'top',
size: 'sm',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '顶部弹出-中框',
actionType: 'drawer',
level: 'info',
drawer: {
position: 'top',
size: 'md',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '顶部弹出-大框',
actionType: 'drawer',
level: 'info',
drawer: {
position: 'top',
size: 'lg',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '顶部弹出 - 超大',
level: 'danger',
actionType: 'drawer',
drawer: {
position: 'top',
size: 'xl',
title: '提示',
body: '这是个简单的弹框'
}
}
]
},
{
type: 'button-toolbar',
className: 'block m-t',
buttons: [
{
type: 'button',
label: '底部弹出-极小框',
actionType: 'drawer',
level: 'primary',
drawer: {
position: 'bottom',
size: 'xs',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '底部弹出-小框',
level: 'primary',
actionType: 'drawer',
drawer: {
position: 'bottom',
size: 'sm',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '底部弹出-中框',
actionType: 'drawer',
level: 'primary',
drawer: {
position: 'bottom',
size: 'md',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '底部弹出-大框',
actionType: 'drawer',
level: 'primary',
drawer: {
position: 'bottom',
size: 'lg',
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '底部弹出-超大',
level: 'danger',
actionType: 'drawer',
drawer: {
position: 'bottom',
size: 'xl',
title: '提示',
body: '这是个简单的弹框'
}
}
]
},
{
type: 'button-toolbar',
className: 'block m-t',
buttons: [
{
type: 'button',
label: '多级弹框',
actionType: 'drawer',
level: 'danger',
drawer: {
title: '提示',
body: '这是个简单的弹框',
closeOnEsc: true,
actions: [
{
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true
},
{
type: 'button',
actionType: 'drawer',
label: '再弹一个',
drawer: {
position: 'left',
title: '弹框中的弹框',
closeOnEsc: true,
body: '如果你想,可以无限弹下去',
actions: [
{
type: 'button',
actionType: 'drawer',
label: '来吧',
level: 'info',
drawer: {
position: 'right',
title: '弹框中的弹框',
closeOnEsc: true,
body: '如果你想,可以无限弹下去',
actions: [
{
type: 'button',
actionType: 'confirm',
label: '可以了',
primary: true
}
]
}
}
]
}
}
]
}
},
{
type: 'button',
label: '交叉测试',
actionType: 'drawer',
className: 'm-l-xs',
level: 'danger',
drawer: {
title: '提示',
closeOnEsc: true,
body: '这是个简单的弹框',
actions: [
{
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true
},
{
type: 'button',
actionType: 'dialog',
closeOnEsc: true,
label: '再弹一个',
dialog: {
position: 'left',
title: '弹框中的弹框',
closeOnEsc: true,
body: '如果你想,可以无限弹下去',
actions: [
{
type: 'button',
actionType: 'drawer',
label: '来吧',
level: 'info',
drawer: {
position: 'right',
title: '弹框中的弹框',
body: '如果你想,可以无限弹下去',
closeOnEsc: true,
actions: [
{
type: 'button',
actionType: 'confirm',
label: '可以了',
primary: true
}
]
}
}
]
}
}
]
}
}
]
}
],
}

View File

@ -0,0 +1,266 @@
export default {
type: 'page',
title: 'Dialog',
body: [
{
type: 'button-toolbar',
className: 'm-b',
buttons: [
{
type: 'button',
label: '打开弹框',
actionType: 'dialog',
dialog: {
title: '提示',
closeOnEsc: true,
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '多级弹框',
actionType: 'dialog',
dialog: {
title: '提示',
closeOnEsc: true,
body: '这是个简单的弹框',
actions: [
{
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true
},
{
type: 'button',
actionType: 'dialog',
label: '再弹一个',
dialog: {
title: '弹框中的弹框',
closeOnEsc: true,
body: '如果你想,可以无限弹下去',
actions: [
{
type: 'button',
actionType: 'dialog',
label: '来吧',
level: 'info',
dialog: {
title: '弹框中的弹框',
closeOnEsc: true,
body: '如果你想,可以无限弹下去',
actions: [
{
type: 'button',
actionType: 'confirm',
label: '不弹了',
primary: true
}
]
}
}
]
}
}
]
}
},
{
type: 'button',
label: '弹个表单',
actionType: 'dialog',
dialog: {
title: '在弹框中的表单',
closeOnEsc: true,
actions: [
{
label: '取消',
actionType: 'close',
type: 'button'
},
{
label: '确认',
actionType: 'confirm',
type: 'button',
level: 'primary'
},
{
label: '提交不关闭',
actionType: 'submit',
close: false,
type: 'button',
api: '/api/mock2/form/saveForm?waitSeconds=2',
level: 'primary'
},
{
label: '保存不关闭',
actionType: 'ajax',
type: 'button',
api: '/api/mock2/form/saveForm?waitSeconds=4',
level: 'info'
},
{
type: 'button',
label: 'Feedback',
close: true,
actionType: 'ajax',
api: '/api/mock2/form/initData?waitSeconds=2',
tooltip: '点击我后会发送一个请求,请求回来后,弹出一个框。',
feedback: {
title: '操作成功',
body: 'xxx 已操作成功'
}
}
],
body: {
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "常规模式",
mode: "normal",
controls: [
{
type: "email",
name: "email",
required: true,
placeholder: "请输入邮箱",
label: "邮箱"
},
{
type: "password",
name: "password",
label: "密码",
required: true,
placeholder: "请输入密码"
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录"
}
]
}
}
},
{
type: 'button',
label: '再弹个表单',
actionType: 'dialog',
dialog: {
title: '在弹框中的表单',
actions: [
{
label: '取消',
actionType: 'close',
type: 'button'
},
{
label: '确认',
actionType: 'confirm',
type: 'button',
level: 'primary',
disabledOn: '!data.rememberMe'
}
],
body: {
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "常规模式",
mode: "normal",
controls: [
{
type: "checkbox",
name: "rememberMe",
label: "勾上我才可以确认"
}
]
}
}
},
{
type: 'button',
label: 'Feedback',
actionType: 'ajax',
api: '/api/mock2/form/initData?waitSeconds=2',
tooltip: '点击我后会发送一个请求,请求回来后,弹出一个框。',
feedback: {
title: '操作成功',
closeOnEsc: true,
body: 'xxx 已操作成功'
}
},
{
type: 'button',
label: 'Feedback2',
actionType: 'ajax',
api: '/api/mock2/form/initData?waitSeconds=2',
tooltip: '可以根据条件弹出比如这个栗子看当前时间戳是否可以整除3',
feedback: {
visibleOn: '!(this.date % 3)',
title: '操作成功',
body: '当前时间戳: <code>${date}</code>'
}
},
]
},
{
type: "button-toolbar",
className: 'm-l-none',
buttons: [
{
type: 'button',
label: 'sm 弹框',
actionType: 'dialog',
dialog: {
size: "sm",
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: '标准 弹框',
actionType: 'dialog',
dialog: {
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: 'lg 弹框',
actionType: 'dialog',
dialog: {
size: "lg",
title: '提示',
body: '这是个简单的弹框'
}
},
{
type: 'button',
label: 'xl 弹框',
actionType: 'dialog',
dialog: {
size: "xl",
title: '提示',
body: '这是个简单的弹框'
}
}
]
}
],
}

View File

@ -0,0 +1,51 @@
import * as React from 'react';
import Editor from '../../src/editor/Editor';
import Switch from '../../src/components/Switch';
import Button from '../../src/components/Button';
import schema from './Form/Test';
import { Portal } from 'react-overlays';
export default class AMisSchemaEditor extends React.Component {
state = {
preview: localStorage.getItem('editting_preview') ? true : false,
schema: localStorage.getItem('editting_schema') ? JSON.parse(localStorage.getItem('editting_schema')) : schema
};
handleChange = (value) => {
localStorage.setItem('editting_schema', JSON.stringify(value));
this.setState({
schema: value
});
}
handlePreviewChange = (preview) => {
localStorage.setItem('editting_preview', preview ? 'true' : '');
this.setState({
preview: !!preview
});
}
clearCache = () => {
localStorage.removeItem('editting_schema');
this.setState({
schema: schema
});
}
render() {
return (
<div className="h-full">
<Portal container={() => document.querySelector('#headerBar')}>
<div className="inline m-l" >
预览 <Switch value={this.state.preview} onChange={this.handlePreviewChange} className="v-middle" inline />
<Button size="sm" className="m-l" onClick={this.clearCache}>清除缓存</Button>
</div>
</Portal>
<Editor preview={this.state.preview} value={this.state.schema} onChange={this.handleChange} className="fix-settings" />
</div>
);
}
}

View File

@ -0,0 +1,339 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "Combo 示例",
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "",
mode: "horizontal",
// debug: true,
controls: [
{
type: 'text',
label: '文本',
name: 'a'
},
{
type: 'divider'
},
{
type: "combo",
name: "combo1",
label: "组合多条多行",
multiple: true,
multiLine: true,
value: [{}],
controls: [
{
name: "a",
label: "文本",
type: "text",
placeholder: "文本",
value: '',
size: 'full'
},
{
name: "b",
label: "选项",
type: "select",
options: ["a", "b", "c"],
size: 'full'
}
]
},
{
type: "button",
label: "独立排序",
level: "dark",
className: "m-t-n-xs",
size: "sm",
actionType: "dialog",
visibleOn: "data.combo1.length > 1",
dialog: {
title: "对 Combo 进行 拖拽排序",
body: {
type: "form",
controls: [
{
type: "combo",
name: "combo1",
label: false,
multiple: true,
draggable: true,
addable: false,
removable: false,
value: [{}],
controls: [
{
name: "a",
type: "static",
tpl: "${a} - ${b}"
}
]
}
]
},
actions: [
{
type: "submit",
mergeData: true,
label: "确认",
level: "primary"
},
{
type: "button",
actionType: "close",
label: "取消"
}
]
}
},
{
type: "combo",
name: "combo2",
label: "组合多条单行",
multiple: true,
value: [{}],
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
value: ''
},
{
name: "b",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo3",
label: "组合单条多行",
multiLine: true,
controls: [
{
name: "a",
label: "文本",
type: "text",
placeholder: "文本",
value: '',
size: 'full'
},
{
name: "b",
label: "选项",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: "combo",
name: "combo4",
label: "组合单条单行",
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
value: '',
size: 'full'
},
{
name: "b",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo11",
label: "组合多条多行内联",
multiple: true,
multiLine: true,
inline: true,
value: [{}],
controls: [
{
name: "a",
label: "文本",
type: "text",
placeholder: "文本",
value: ''
},
{
name: "b",
label: "选项",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: "combo",
name: "combo22",
label: "组合多条单行内联",
multiple: true,
inline: true,
value: [{}],
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
value: ''
},
{
name: "b",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo33",
label: "组合单条多行内联",
multiLine: true,
inline: true,
controls: [
{
name: "a",
label: "文本",
type: "text",
placeholder: "文本",
value: ''
},
{
name: "b",
label: "选项",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: "combo",
name: "combo44",
label: "组合单条单行内联",
inline: true,
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
value: ''
},
{
name: "b",
type: "select",
options: ["a", "b", "c"]
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo666",
label: "组合多条唯一",
multiple: true,
value: [{}],
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
value: '',
unique: true
},
{
name: "b",
type: "select",
options: ["a", "b", "c"],
unique: true
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo777",
label: "可拖拽排序",
multiple: true,
value: [{a: '1', b: "a"}, {a: '2', b: "b"}],
draggable: true,
controls: [
{
name: "a",
type: "text",
placeholder: "文本",
unique: true
},
{
name: "b",
type: "select",
options: ["a", "b", "c"],
unique: true
}
]
},
{
type: 'divider'
},
{
type: "combo",
name: "combo888",
label: "可打平只存储值",
multiple: true,
flat: true,
value: ["red", "pink"],
draggable: true,
controls: [
{
name: "a",
type: "color",
placeholder: "选取颜色"
}
]
},
{
type: "static",
name: "combo888",
label: "当前值",
tpl: "<pre>${combo888|json}</pre>"
},
]
}
]
};

View File

@ -0,0 +1,95 @@
import * as React from 'react';
import {
FormItem,
Renderer
} from '../../../src/index';
@FormItem({
type: 'custom'
})
class MyFormItem extends React.Component {
render() {
const {
value,
onChange
} = this.props;
return (
<div>
<p>这个是个自定义组件通过注册渲染器的方式实现</p>
<p>当前值{value}</p>
<a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>
</div>
);
}
}
@Renderer({
test: /(^|\/)my\-renderer$/,
})
class CustomRenderer extends React.Component {
render() {
const {tip} = this.props;
return (
<div>{tip || '非 FormItem 类型的渲染器注册, 这种不能修改 form'}</div>
);
}
}
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "自定义组件示例",
body: [
{
type: "form",
mode: "horizontal",
api: "/api/mock2/form/saveForm?waitSeconds=2",
actions: [
{
type: "submit",
label: "提交",
primary: true
}
],
controls: [
{
name: 'a',
children: ({value, onChange}) => (
<div>
<p>这个是个自定义组件最简单直接的方式不用注册直接使用</p>
<p>当前值{value}</p>
<a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>
</div>
)
},
{
type: 'divider'
},
{
name: 'b',
type: 'custom',
label: '自定义FormItem'
},
{
type: 'divider'
},
{
type: 'my-renderer'
}
]
},
{
type: 'my-renderer',
tip: '他能放 controls 里面,也能放外面。'
}
]
};

View File

@ -0,0 +1,35 @@
import * as React from 'react';
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "Editor",
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "",
controls: [
{
name: "javascript",
type: "javascript-editor",
label: "Javascript",
value: "console.log(1, 2, 3);"
},
{
name: "html",
type: "html-editor",
label: "Html",
value: "<html><head><title>Hello</title></head><body><p>world</p></body></html>"
},
{
name: "css",
type: "css-editor",
label: "CSS",
value: "body {color: red;}"
}
]
}
]
};

View File

@ -0,0 +1,147 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "FieldSet 示例",
body: [
{
type: "form",
mode: "horizontal",
api: "/api/mock2/saveForm?waitSeconds=2",
actions: [
{
type: "submit",
label: "提交",
primary: true
}
],
collapsable: true,
title: 'fieldSet 可以对表单元素做个分组',
controls: [
{
type: 'fieldSet',
title: '基本信息',
controls: [
{
type: 'group',
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "password",
name: "password",
label: false,
placeholder: "Password"
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "checkbox",
name: "rememberMe",
label: false,
option: "Remember me"
}
]
}
]
},
{
title: '其他信息',
type: 'fieldSet',
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "divider"
},
{
type: "checkbox",
name: "rememberMe",
option: "记住我"
}
]
}
]
},
{
title: "FieldSet 样式集",
type: "form",
controls: [
{
title: "超级小",
type: 'fieldSet',
className: "fieldset-xs",
controls: [
{
type: "plain",
text: "文本 ..."
}
]
},
{
title: "小尺寸",
type: 'fieldSet',
className: "fieldset-sm",
controls: [
{
type: "plain",
text: "文本 ..."
}
]
},
{
title: "正常尺寸",
type: 'fieldSet',
className: "fieldset",
controls: [
{
type: "plain",
text: "文本 ..."
}
]
},
{
title: "中大尺寸",
type: 'fieldSet',
className: "fieldset-md",
controls: [
{
type: "plain",
text: "文本 ..."
}
]
},
{
title: "超大尺寸",
type: 'fieldSet',
className: "fieldset-lg",
controls: [
{
type: "plain",
text: "文本 ..."
}
]
}
]
}
]
};

View File

@ -0,0 +1,101 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "FieldSet In Tabs",
remark: "",
body: {
type: "form",
collapsable: true,
tabs: [
{
title: "Tab A",
fieldSet: [
{
title: "Group A",
tabs: [
{
title: "SubTab A",
controls: [
{
name: "a",
type: "text",
label: "Text"
},
{
name: "a",
type: "text",
label: "Text"
}
]
},
{
title: "SubTab B",
controls: [
{
name: "a",
type: "text",
label: "Text"
},
{
name: "a",
type: "text",
label: "Text"
}
]
}
]
},
{
title: "Group B",
controls: [
{
name: "a",
type: "text",
label: "Text"
},
{
name: "a",
type: "text",
label: "Text"
}
]
}
]
},
{
title: "Tab B",
fieldSet: [
{
title: "Group A",
controls: [
{
name: "a",
type: "text",
label: "Text"
},
{
name: "a",
type: "text",
label: "Text"
}
]
},
{
title: "Group B",
controls: [
{
name: "a",
type: "text",
label: "Text"
},
{
name: "a",
type: "text",
label: "Text"
}
]
}
]
}
]
}
};

View File

@ -0,0 +1,127 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "公式示例",
"body": [
"<p>通过公式,可以动态的设置目标值。</p>",
{
"type": "form",
title: "自动应用",
"api": "/api/mock2/form/saveForm",
"controls": [
{
"type": "number",
"name": "a",
"label": "A"
},
{
"type": "number",
"name": "b",
"label": "B"
},
{
"type": "number",
"name": "sum",
"label": "和",
"disabled": true,
description: '自动计算 A + B'
},
{
"type": "formula",
"name": "sum",
"value": 0,
"formula": "a + b"
}
]
},
{
"type": "form",
title: "手动应用",
"api": "/api/mock2/form/saveForm",
"controls": [
{
"type": "number",
"name": "a",
"label": "A"
},
{
"type": "number",
"name": "b",
"label": "B"
},
{
type: "group",
controls: [
{
"type": "number",
"name": "sum",
"label": "和",
"disabled": true,
"columnClassName": "col-sm-11",
},
{
"type": "button",
"label": "计算",
"columnClassName": "col-sm-1 v-bottom",
"target": "theFormula"
}
]
},
{
"type": "formula",
"name": "sum",
"id": "theFormula",
"value": 0,
"formula": "a + b",
"initSet": false,
"autoSet": false
}
]
},
{
"type": "form",
title: "条件应用",
"api": "/api/mock2/form/saveForm",
"controls": [
{
"type": "radios",
"name": "radios",
"inline": true,
"label": "radios",
"options": [
{
"label": 'a',
"value": 'a'
},
{
"label": 'b',
"value": 'b'
},
],
"description": 'radios 变化会自动清空 B'
},
{
"type": "text",
"name": "b",
"label": "B"
},
{
"type": "formula",
"name": "b",
"value": 'some string',
"formula": "''",
"condition": '${radios}',
"initSet": false,
}
]
},
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "其他类型演示",
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "Hint demo",
mode: "horizontal",
horizontal: {
leftFixed: true
},
controls: [
{
name: "button",
type: "button",
label: "ID",
value: "",
size: 'xs',
hint: '比如输入 a-xxxx-xxx'
},
{
"type": "input-group",
"size": "md",
"label": "Icon 组合",
"controls": [
{
"type": "icon",
"addOnclassName": "no-bg",
className: "text-sm",
"icon": "search",
"vendor": "iconfont"
},
{
"type": "text",
"placeholder": "搜索作业ID/名称",
"inputClassName": "b-l-none p-l-none",
"name": "jobName"
}
]
},
{
name: "a",
type: "text",
label: "ID",
value: "",
size: 'xs',
hint: '比如输入 a-xxxx-xxx'
},
{
name: "b",
type: "text",
label: "ID",
value: "",
size: 'sm',
hint: '比如输入 a-xxxx-xxx'
},
{
name: "c",
type: "text",
label: "ID",
value: "",
size: 'md',
hint: '比如输入 a-xxxx-xxx'
},
{
name: "d",
type: "text",
label: "ID",
value: "",
size: 'lg',
hint: '比如输入 a-xxxx-xxx'
},
{
name: "tag",
type: "tag",
label: "Tag",
size: 'md',
clearable: true,
placeholder: "多个标签以逗号分隔",
options: [
"周小度",
"杜小度"
]
}
]
}
]
};

View File

@ -0,0 +1,561 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "表单各种展示模式汇总",
remark: "展示各种模式的 Form",
body: [
{
type: "grid",
columns: [
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "常规模式",
mode: "normal",
controls: [
{
type: "email",
name: "email",
required: true,
placeholder: "请输入邮箱",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password",
label: "密码",
required: true,
placeholder: "请输入密码",
size: 'full'
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录"
},
{
type: "submit",
btnClassName: "btn-default",
label: "登录"
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "常规模式 input md 尺寸",
mode: "normal",
controls: [
{
type: "email",
name: "email",
required: true,
placeholder: "请输入邮箱",
label: "邮箱",
size: 'md',
remark: 'xxxx',
hint: 'bla bla bla'
},
{
type: "password",
name: "password",
label: "密码",
required: true,
placeholder: "请输入密码",
size: 'md'
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录"
},
{
type: "submit",
btnClassName: "btn-default",
label: "登录"
}
]
}
]
},
{
type: "grid",
columns: [
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "水平模式,左右摆放 左右比率分配 ",
mode: "horizontal",
autoFocus: false,
horizontal: {
left: 'col-sm-2',
right: 'col-sm-10',
offset: 'col-sm-offset-2'
},
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱",
required: true,
desc: "表单描述文字",
},
{
type: "password",
name: "password",
label: "密码",
placeholder: "输入密码",
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录"
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "水平模式,左右摆放 左侧固定宽度 input md 尺寸",
mode: "horizontal",
autoFocus: false,
horizontal: {
leftFixed: 'xs'
},
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱",
required: true,
desc: "表单描述文字",
size: 'md',
remark: 'xxxx',
hint: 'bla bla bla'
},
{
type: "password",
name: "password",
label: "密码",
placeholder: "输入密码",
size: 'md'
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录"
}
]
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "内联模式",
mode: "inline",
autoFocus: false,
controls: [
{
type: "email",
name: "email",
placeholder: "Enter Email",
label: "邮箱",
size: 'auto'
},
{
type: "password",
name: "password",
placeholder: "密码",
size: 'auto',
remark: 'Bla bla bla'
},
{
type: "checkbox",
name: "rememberMe",
label: "记住登录",
size: 'auto'
},
{
type: "submit",
label: "登录"
},
{
type: "button",
label: "导出",
url: "http://www.baidu.com/",
level: "success"
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "常规模式下用数组包起来还能控制一行显示多个",
mode: "normal",
autoFocus: false,
controls: [
{
type: "text",
name: "name",
placeholder: "请输入...",
label: "名字",
size: 'full'
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email",
placeholder: "输入邮箱",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password",
label: "密码",
placeholder: "请输入密码",
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email2",
mode: 'inline',
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password2",
label: "密码",
mode: 'inline',
placeholder: "请输入密码",
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email3",
mode: 'inline',
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password3",
label: "密码",
placeholder: "请输入密码",
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email4",
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password4",
label: "密码",
placeholder: "请输入密码",
mode: 'inline',
size: 'full'
}
]
},
{
type: "divider"
},
{
type: "checkbox",
name: "rememberMe",
label: "记住我"
},
{
type: "submit",
btnClassName: "btn-default",
label: "提交"
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "水平模式用数组包起来也能控制一行显示多个",
mode: "horizontal",
autoFocus: false,
controls: [
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email2",
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password2",
label: "密码",
placeholder: "请输入密码",
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email3",
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password3",
label: "密码",
placeholder: "请输入密码",
size: 'full'
},
{
type: "password",
name: "password3",
label: "密码",
placeholder: "请输入密码",
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
controls: [
{
type: "email",
name: "email4",
placeholder: "请输入邮箱地址",
label: "邮箱",
size: 'full',
columnClassName: 'col-sm-6',
horizontal: {
left: 'col-sm-4',
right: 'col-sm-8'
}
},
{
type: "password",
name: "password4",
label: "密码",
placeholder: "请输入密码",
mode: 'inline',
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
label: "邮箱",
gap: 'xs',
controls: [
{
label: false,
type: "email",
name: "email5",
placeholder: "请输入邮箱地址",
size: 'full'
},
{
type: "password",
name: "password5",
label: "密码",
placeholder: "请输入密码",
mode: 'inline',
size: 'full'
}
]
},
{
type: "divider"
},
{
type: 'group',
label: "邮箱",
description: 'bla bla',
gap: 'xs',
controls: [
{
type: "email",
name: "email6",
placeholder: "请输入邮箱地址",
mode: 'inline'
},
{
type: "password",
name: "password6",
// label: "",
placeholder: "请输入密码",
labelClassName: "w-auto p-r-none",
mode: 'inline'
}
]
},
{
type: "divider"
},
{
type: 'group',
label: "邮箱",
description: 'bla bla',
direction: "vertical",
controls: [
{
type: "email",
name: "email9",
mode: 'normal',
placeholder: "请输入邮箱地址",
inline: true,
description: 'Bla blamfejkf fdjk',
},
{
type: "password",
name: "password9",
mode: 'normal',
placeholder: "请输入密码",
labelClassName: "w-auto p-r-none",
}
]
},
{
type: "divider"
},
{
type: "checkbox",
name: "rememberMe",
label: "记住我"
},
{
type: "submit",
btnClassName: "btn-default",
label: "Submit"
}
]
},
{
type: "form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "Inline form 用数组包起来还能控制一行显示多个",
mode: "inline",
submitText: null,
autoFocus: false,
controls: [
[
{
type: "email",
name: "email",
placeholder: "Enter Email",
label: "邮箱",
size: 'full'
},
{
type: "password",
name: "password",
placeholder: "Password",
size: 'full'
}
],
{
type: "divider"
},
[
{
type: "email",
name: "email",
placeholder: "Enter Email",
label: "邮箱",
size: 'full'
},
{
type: "checkbox",
name: "rememberMe",
label: "记住我",
size: 'full'
},
{
type: 'button-toolbar',
buttons: [
{
type: "submit",
label: "登录"
},
{
type: "button",
label: "导出",
url: "http://www.baidu.com/",
level: "success"
}
]
}
]
]
}
]
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "显隐切换示例",
body: [
{
name: "hiddenOn",
type: "form",
mode: "horizontal",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "Hide On 和 disabledOn 示例",
controls: [
{
type: "radios",
name: "type",
label: "类型选择",
inline: true,
value: "1",
options: [
{
label: "类型 1",
value: "1"
},
{
label: "类型 2",
value: "2"
},
{
label: "类型 3",
value: "3"
}
],
description: '<span class="text-danger">请切换类型来看效果</span>'
},
{
type: "text",
label: "所有可见",
name: "text1"
},
{
type: "text",
label: "类型2 可见",
hiddenOn: "data.type != 2",
name: "text2"
},
{
type: "text",
label: "类型3 不可点",
disabledOn: "data.type == 3",
name: "text3"
},
{
type: "text",
required: true,
label: "必填字段",
name: "test4"
},
{
type: "button-toolbar",
buttons: [
{
type: "submit",
disabledOn: "data.type == 1",
label: "类型1不可点"
},
{
type: "reset",
label: "类型3出现且不可点",
visibleOn: "data.type == 3",
disabledOn: "data.type == 3",
},
{
type: "button",
label: "Baidu",
href: "http://www.baidu.com?a=1&b=$test4"
},
{
type: "button",
actionType: "ajax",
label: "No Submit",
action: "/api/mock2/saveForm?waitSeconds=5"
},
{
type: "submit",
actionType: "ajax",
label: "Submit",
action: "/api/mock2/saveForm?waitSeconds=5"
}
]
}
]
}
]
};

View File

@ -0,0 +1,90 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "动态拉取选项",
name: "page-form-remote",
body: [
{
type: "form",
title: "动态表单元素示例",
name: "demo-form",
api: "/api/mock2/form/saveForm?waitSeconds=2",
mode: "horizontal",
actions: [
{
type: "submit",
label: "提交"
}
],
controls: [
{
name: "select",
type: "select",
label: "动态选项",
source: "/api/mock2/form/getOptions?waitSeconds=1",
description: '通过接口一口气拉取选项',
clearable: true,
searchable: true
},
{
type: "divider"
},
{
name: "select2",
type: "select",
label: "选项自动补全",
autoComplete: "/api/mock2/options/autoComplete?term=$term",
placeholder: "请输入",
description: '通过接口自动补全'
},
{
type: "divider"
},
{
type: "text",
name: "text",
label: "文本提示",
source: "/api/mock2/form/getOptions?waitSeconds=1",
placeholder: '请选择'
},
{
type: "divider"
},
{
name: "text2",
type: "text",
label: "文本自动补全",
clearable: true,
autoComplete: "/api/mock2/options/autoComplete2?term=$term",
description: '通过接口自动补全'
},
{
name: "chained",
type: "chained-select",
label: "级联选项",
source:
"/api/mock2/options/chainedOptions?waitSeconds=1&parentId=$parentId&level=$level&maxLevel=4&waiSeconds=1",
desc: "无限级别, 只要 api 返回数据就能继续往下选择. 当没有下级时请返回 null.",
value: "a,b"
},
{
type: "divider"
},
{
name: "tree",
type: "tree",
label: "动态树",
source: "/api/mock2/options/tree?waitSeconds=1"
},
{
type: "divider"
},
{
name: "matrix",
type: "matrix",
label: "动态矩阵开关",
source: "/api/mock2/options/matrix?waitSeconds=1"
}
]
}
]
};

View File

@ -0,0 +1,21 @@
import * as React from 'react';
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "富文本编辑器",
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "Form elements",
controls: [
{
name: "html",
type: "rich-text",
label: "富文本",
value: "<p>Just do <code>IT</code></p>"
}
]
}
]
};

View File

@ -0,0 +1,156 @@
import * as React from 'react';
import TitleBar from '../../../src/components/TitleBar';
import {render} from '../../../src/index';
const Schema = {
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"title": "First Name",
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"tag": {
"type": "array",
"description": "Tags",
"default": ["IT"],
"items": {
"type": "text"
}
},
"clients": {
"type": "array",
"description": "Tags",
"items": {
"type": "object",
"properties": {
"firstName": {
"title": "First Name",
"type": "string"
},
"lastName": {
"type": "string"
}
}
}
}
},
"required": ["firstName", "lastName"]
};
function property2control(property, key, schema) {
const requiredList= schema.required || [];
const rest = {};
const validations = {};
let type = 'text';
if (property.type === 'integer') {
type = 'number';
typeof property.minimum === 'number' && (rest.min = property.minimum);
// property.max
} else if (property.type === 'array') {
type = 'combo';
const items = property.items;
if (items.type === 'object') {
rest.controls = makeControls(items.properties, items);
rest.multiLine = true;
} else {
type = 'array';
rest.inline = true;
rest.items = property2control(items, 'item', property);
}
}
if (typeof property.minimum === 'number') {
validations.minimum = property.minimum;
}
return {
name: key,
type,
required: !!~requiredList.indexOf(key),
label: property.title || property.description,
desc: property.title && property.description,
value: property.default,
validations,
...rest
};
}
function makeControls(properties, schema) {
const keys = Object.keys(properties);
return keys.map(key => property2control(properties[key], key, schema));
}
function JSONSchme2AMisSchema(schema) {
if (schema.type !== 'object') {
throw new Error('JSONSchme2AMisSchema 只支持 object 转换');
}
return {
title: schema.title,
type: 'form',
mode: "horizontal",
controls: makeControls(schema.properties, schema)
}
}
const amisFormSchema = JSONSchme2AMisSchema(Schema);
export default class JSONSchemaForm extends React.Component {
state = {
data: {}
};
renderForm() {
return render({
type: 'page',
title: '',
body: {
...amisFormSchema,
onChange: values => this.setState({
data: {
...values
}
})
}
});
}
render() {
return (
<div>
<TitleBar title="JSON Schema Form" />
<div className="wrapper">
<div>
<h3>Schema</h3>
<pre><code>{JSON.stringify(Schema, null, 2)}</code></pre>
</div>
<div>
<h3>Form</h3>
{this.renderForm()}
</div>
<div>
<h3>Data</h3>
<pre><code>{JSON.stringify(this.state.data, null, 2)}</code></pre>
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,130 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "所有 Form 元素列举",
data: {
id: 1,
image: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
},
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "表单项静态展示",
mode: "horizontal",
controls: [
{
type: "static",
label: "文本",
value: "文本"
},
{
type: 'divider'
},
{
type: "static-tpl",
label: "模板",
tpl: "自己拼接 HTML 取变量 \\${id}: ${id}"
},
{
type: 'divider'
},
{
type: "static-date",
label: "日期",
value: Math.round(Date.now()/1000)
},
{
type: 'divider'
},
{
type: "static-datetime",
label: "日期时间",
value: Math.round(Date.now()/1000)
},
{
type: 'divider'
},
{
type: "static-mapping",
label: "映射",
value: Math.floor(Math.random() * 5),
map: {
'*': "<span class='label label-default'>-</span>",
'0': "<span class='label label-info'>一</span>",
'1': "<span class='label label-success'>二</span>",
'2': "<span class='label label-danger'>三</span>",
'3': "<span class='label label-warning'>四</span>",
'4': "<span class='label label-primary'>五</span>",
}
},
{
type: 'divider'
},
{
type: "static-progress",
label: "进度",
value: 66.66
},
{
type: 'divider'
},
{
type: "static-image",
label: "图片",
name: "image",
popOver: {
title: "查看大图",
body: '<div class="w-xxl"><img class="w-full" src="${image}"/></div>'
}
},
{
type: 'divider'
},
{
type: 'static-json',
label: 'JSON',
value: {a: 1, b: 2, c: {d: 3}}
},
{
type: 'divider'
},
{
type: "static",
label: "可复制",
value: "文本",
copyable: {
content: "内容,支持变量 ${id}"
}
},
{
type: 'divider'
},
{
type: "static",
name: "text",
label: "可快速编辑",
value: "文本",
quickEdit: true
},
]
}
]
};

View File

@ -0,0 +1,60 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "SubForm 示例",
body: [
{
type: "form",
api: "/api/mock2/saveForm?waitSeconds=2",
title: "Form elements",
mode: "horizontal",
// debug: true,
controls: [
{
type: "form",
label: "子表单单条",
name: "subForm1",
btnLabel: "点击设置",
form: {
title: "子表单",
controls: [
{
name: "a",
type: "text",
label: "Foo"
},
{
name: "b",
type: "switch",
label: "Boo"
}
]
}
},
{
type: "form",
label: "子表单多条",
name: "subForm2",
labelField: 'a',
btnLabel: "点击设置",
multiple: true,
form: {
title: "子表单",
controls: [
{
name: "a",
type: "text",
label: "Foo"
},
{
name: "b",
type: "switch",
label: "Boo"
}
]
}
},
]
}
]
};

View File

@ -0,0 +1,146 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "表格编辑",
body: {
type: "form",
mode: "horizontal",
api: "/api/mock2/form/saveForm?waitSeconds=2",
actions: [
{
type: "submit",
label: "提交",
primary: true
}
],
controls: [
{
type: 'combo',
name: 'colors',
label: 'Combo',
multiple: true,
draggable: true,
multiLine: true,
value: [
{
color: 'green',
name: '颜色'
}
],
controls: [
{
type: 'color',
name: 'color'
},
{
type: 'text',
name: 'name',
placeholder: '说明文字'
}
]
},
{
type: "static",
label: "当前值",
tpl: "<pre>${colors|json}</pre>"
},
{
type: 'table',
name: 'colors',
label: 'Table',
draggable: true,
columns: [
{
label: 'Color',
name: 'color',
quickEdit: {
type: 'color',
saveImmediately: true
}
},
{
label: '说明文字',
name: 'name',
quickEdit: {
type: 'text',
mode: 'inline',
saveImmediately: true
}
}
]
},
{
type: 'button',
label: 'Table2新增一行',
target: 'table2',
actionType: 'add'
},
{
"type": "table",
"name": "table2",
"label": "Table2",
"editable": true,
"addable": true,
"removable": true,
"draggable": true,
"columns": [
{
"name": "a",
"label": "A"
},
{
"name": "b",
"label": "B",
"quickEdit": {
"type": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
}
]
}
}
]
},
{
"type": "table",
"name": "table3",
"label": "Table3(指定第2列只有update时能编辑)",
"editable": true,
"addable": true,
"removable": true,
"draggable": true,
"columns": [
{
"name": "a",
"label": "A",
"quickEdit": true
},
{
"name": "b",
"label": "B",
"quickEdit": false,
"quickEditOnUpdate": {
"type": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
}
]
},
}
]
}
]
}
};

View File

@ -0,0 +1,169 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "Tabs 示例",
body: [
{
type: "form",
mode: "horizontal",
api: "/api/mock2/form/saveForm?waitSeconds=2",
title: "",
actions: [
{
type: "button",
actionType: "dialog",
label: "弹框中的 Tabs",
level: "info",
dialog: {
title: "",
// size: "md",
body: {
type: "form",
mode: "horizontal",
horizontal: {
leftFixed: 'xs'
},
api: "/api/mock2/form/saveForm?waitSeconds=2",
actions: [
{
type: "submit",
label: "提交",
primary: true
}
],
controls: [{
type: 'tabs',
tabs: [
{
title: "基本信息",
controls: [
[
{
type: "email",
name: "email1",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "password",
name: "password",
placeholder: "密码",
label: false
}
],
{
type: "divider"
},
[
{
type: "email",
name: "email2",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "checkbox",
name: "rememberMe",
label: false,
option: "记住我"
}
]
]
},
{
title: "其他信息",
controls: [
{
type: "email",
name: "email3",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "divider"
},
{
type: "checkbox",
name: "rememberMe2",
option: "记住我"
}
]
}
]
}]
}
}
},
{
type: "submit",
label: "提交",
primary: true
}
],
controls: [
{
type: 'tabs',
tabs: [
{
title: "基本信息",
hash: 'tab1',
controls: [
[
{
type: "email",
name: "email",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "password",
name: "password",
placeholder: "密码",
label: false
}
],
{
type: "divider"
},
[
{
type: "email",
name: "email2",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "checkbox",
name: "rememberMe",
label: false,
option: "记住我"
}
]
]
},
{
title: "其他信息",
hash: 'tab2',
controls: [
{
type: "email",
name: "email3",
placeholder: "请输入邮箱地址",
label: "邮箱"
},
{
type: "divider"
},
{
type: "checkbox",
name: "rememberMe4",
label: "记住我"
}
]
}
]
}
]
}
]
};

View File

@ -0,0 +1,126 @@
export default {
"type": "page",
"body": {
"type": "form",
"title": "详情",
"name": "scene_detail",
"mode": "horizontal",
"submitText": "",
"submitOnChange": false,
"actions": [{
"type": "button",
"label": "修改",
"actionType": "drawer",
"drawer": {
"type": "form",
"position": "left",
"title": "修改内容",
"controls": [{
"type": "text",
"label": "标题",
"name": "name",
"required": true
}, {
"label": "描述",
"type": "text",
"name": "typeDesc",
"required": true
}, {
"label": "内容",
"type": "textarea",
"name": "contents",
"required": true
}]
}
}],
"controls": [
{
type: "tree",
name: "tree",
label: "树",
options: [
{
label: "Folder A",
value: 1,
children: [
{
label: "file A",
value: 2
},
{
label: "file B",
value: 3
}
]
},
{
label: "file C",
value: 4
},
{
label: "file D",
value: 5
}
]
},
{
type: "divider"
},
{
type: "tree",
name: "trees",
label: "树多选",
multiple: true,
options: [
{
label: "Folder A",
value: 1,
children: [
{
label: "file A",
value: 2
},
{
label: "file B",
value: 3
}
]
},
{
label: "file C",
value: 4
},
{
label: "file D",
value: 5
}
]
},
{
type: "divider"
},
{
name: "select",
type: "tree-select",
label: "动态选项",
source: "/api/mock2/form/getTreeOptions?waitSeconds=1",
description: '通过接口一口气拉取选项',
clearable: true,
searchable: true
},
{
type: "divider"
},
{
name: "select2",
type: "tree-select",
label: "选项自动补全",
autoComplete: "/api/mock2/tree/autoComplete?term=$term",
placeholder: "请输入",
description: '通过接口自动补全',
multiple: true
},
]
}
}

View File

@ -0,0 +1,109 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "表单验证示例",
toolbar: "<a target='_blank' href='/docs/renderers#formitem'>文档</a>",
body: [
{
type: "form",
autoFocus: false,
title: "表单",
actions: [
{
type: "submit",
label: "提交"
}
],
api: "/api/mock2/form/saveFormFailed?waitSeconds=2",
mode: "horizontal",
controls: [
{
type: "text",
name: "test",
label: "必填",
required: true
},
{
type: "divider"
},
{
name: "test1",
type: "email",
label: "Email"
},
{
type: "divider"
},
{
name: "url",
type: "url",
label: "Url"
},
{
type: "divider"
},
{
name: "num",
type: "text",
label: "数字",
validations: "isNumeric"
},
{
type: "divider"
},
{
name: "alpha",
type: "text",
label: "字母或数字",
validations: "isAlphanumeric"
},
{
type: "divider"
},
{
name: "int",
type: "text",
label: "整形",
validations: "isInt"
},
{
type: "divider"
},
{
name: "minLength",
type: "text",
label: "长度限制",
validations: "minLength:2,maxLength:10"
},
{
type: "divider"
},
{
name: "min",
type: "text",
label: "数值限制",
validations: "maximum:10,minimum:2"
},
{
type: "divider"
},
{
name: "reg",
type: "text",
label: "正则",
validations: "matchRegexp:/^abc/",
validationErrors: {
matchRegexp: "请输入abc开头的好么?"
}
},
{
type: "divider"
},
{
name: "test2",
type: "text",
label: "服务端验证"
}
]
}
]
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,137 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "HBox & Grid",
"type": "page",
"body": [
{
"type": "plain",
"tpl": "Grid 请参考 bootstrap 的 grid 布局",
"inline": false,
"className": "h3 m-b-xs"
},
{
"type": "grid",
"columns": [
{
"type": "tpl",
"tpl": "sm-2",
"sm": 2,
"className": "bg-info",
"inline": false
},
{
"type": "tpl",
"tpl": "sm-4",
"sm": 4,
"className": "bg-success",
"inline": false
},
{
"type": "tpl",
"tpl": "sm-6",
"sm": 6,
"className": "bg-primary",
"inline": false
}
]
},
{
"type": "plain",
"tpl": "Hbox",
"inline": false,
"className": "h3 m-t m-b-xs"
},
{
"type": "hbox",
"columns": [
{
"type": "tpl",
"tpl": "平均分配",
"className": "bg-info",
"inline": false
},
{
"type": "tpl",
"tpl": "平均分配",
"className": "bg-success",
"inline": false
},
{
"type": "tpl",
"tpl": "平均分配",
"className": "bg-primary",
"inline": false
}
]
},
{
"type": "plain",
"tpl": "Hbox 部分定宽",
"inline": false,
"className": "h3 m-t m-b-xs"
},
{
"type": "hbox",
"columns": [
{
"type": "tpl",
"tpl": "w-xs",
"className": "bg-info",
"inline": false,
"columnClassName": "w-xs"
},
{
"type": "tpl",
"tpl": "w-sm",
"className": "bg-info lter",
"inline": false,
"columnClassName": "w-sm"
},
{
"type": "tpl",
"tpl": "w",
"className": "bg-info dk",
"inline": false,
"columnClassName": "w"
},
{
"type": "tpl",
"tpl": "平均分配",
"className": "bg-success",
"inline": false
},
{
"type": "tpl",
"tpl": "平均分配",
"className": "bg-primary",
"inline": false
}
]
},
{
"type": "plain",
"tpl": "示例",
"inline": false,
"className": "h3 m-t m-b-xs"
},
{
"type": "grid",
"columns": [
{
"type": "panel",
"title": "面板1",
"className": "Panel--danger",
"body": "内容",
"sm": 4
},
{
"type": "panel",
"title": "面板2",
"className": "Panel--primary",
"body": "内容",
"sm": 8
}
]
},
]
}

View File

@ -0,0 +1,31 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "IFrame 可以用来嵌入其他网站",
"body": [
{
type: 'form',
mode: 'inline',
target: 'window',
title: '',
controls: [
{
type: 'text',
name: 'keywords',
addOn: {
type: 'submit',
label: '搜索',
level: 'info',
icon: 'fa fa-search pull-left'
}
}
]
},
{
type: 'iframe',
className: 'b-a',
src: "https://www.baidu.com/s?wd=${keywords|url_encode}",
height: 500
}
]
}

View File

@ -0,0 +1,174 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
title: "表单与列表之间的联动",
body: [
{
title: "",
type: 'form',
mode: 'inline',
target: 'list',
wrapWithPanel: false,
className: 'm-b',
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
clearable: true,
addOn: {
type: 'submit',
icon: 'fa fa-search',
level: 'primary'
}
},
]
},
{
type: "crud",
name: 'list',
api: "/api/sample",
mode: "list",
listItem: {
actions: [
{
type: "button",
icon: "fa fa-eye",
actionType: "dialog",
dialog: {
title: "查看",
body: {
type: "form",
controls: [
{
type: "static",
name: "engine",
label: "Engine"
},
{
type: "divider"
},
{
type: "static",
name: "browser",
label: "Browser"
},
{
type: "divider"
},
{
type: "static",
name: "platform",
label: "Platform(s)"
},
{
type: "divider"
},
{
type: "static",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "static",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
icon: "fa fa-pencil",
actionType: "dialog",
dialog: {
title: "编辑",
body: {
type: "form",
name: "sample-edit-form",
api: "/api/sample/$id",
controls: [
{
type: "text",
name: "engine",
label: "Engine",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "browser",
label: "Browser",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "platform",
label: "Platform(s)",
required: true
},
{
type: "divider"
},
{
type: "text",
name: "version",
label: "Engine version"
},
{
type: "divider"
},
{
type: "text",
name: "grade",
label: "CSS grade"
}
]
}
}
},
{
type: "button",
icon: "fa fa-times text-danger",
actionType: "ajax",
confirmText: "您确认要删除?",
api: "delete:/api/sample/$id"
}
],
body: [
{
name: "engine",
label: "Rendering engine",
labelClassName: "w-sm"
},
[
{
name: "browser",
label: "Browser",
labelClassName: "w-sm"
},
{
name: "platform",
label: "Platform(s)",
labelClassName: "w-sm"
}
],
{
name: "version",
label: "Engine version",
labelClassName: "w-sm"
}
]
}
}]
};

View File

@ -0,0 +1,67 @@
export default {
type: 'page',
title: '表单与表单之间的联动',
aside: {
type: 'form',
target: 'detailForm',
className: 'wrapper-sm', //
wrapWithPanel: false, // panel
controls: [
{
type: 'text',
placeholder: '关键字',
name: 'keywords',
addOn: {
type: 'submit',
label: '搜索',
primary: true
}
},
'<span class="text-danger">请在此输入内容后点击搜索</sapn>'
]
},
body: {
name: 'detailForm',
type: 'form',
mode: 'horizontal',
title: '',
initApi: '/api/mock2/form/initData?keywords=${keywords}',
actions: [],
controls: [
'Form 模型除了用来提交数据外,还比较适合用来做详情数据的展示',
{
type: 'divider'
},
{
label: '名称',
type: 'static',
labelClassName: 'text-muted',
name: 'name'
},
{
label: '作者',
type: 'static',
labelClassName: 'text-muted',
name: 'author'
},
{
label: '输入信息',
type: 'static',
labelClassName: 'text-muted',
name: 'info'
},
{
label: '请求时间',
type: 'static-datetime',
labelClassName: 'text-muted',
format: 'YYYY-MM-DD HH:mm:ss',
name: 'date'
}
]
}
}

View File

@ -0,0 +1,306 @@
export default {
type: 'page',
title: '表单初始数据自动重新拉取',
body: [
{
type: 'form',
mode: 'horizontal',
title: '监听表单内部的修改',
initApi: '/api/mock2/form/initData?tpl=${tpl}',
actions: [],
controls: [
'<span class="text-danger">当 <code>initApi</code> 中有变量,且变量的值发生了变化了,则该表单就会重新初始数据。</span>',
{
type: 'divider'
},
{
label: '数据模板',
type: 'select',
labelClassName: 'text-muted',
name: 'tpl',
value: 'tpl1',
inline: true,
options: [
{
label: '模板1',
value: 'tpl1'
},
{
label: '模板2',
value: 'tpl2'
},
{
label: '模板3',
value: 'tpl3'
}
],
description: '<span class="text-danger">请修改这里看效果</span>'
},
{
label: '名称',
type: 'static',
labelClassName: 'text-muted',
name: 'name'
},
{
label: '作者',
type: 'static',
labelClassName: 'text-muted',
name: 'author'
},
{
label: '请求时间',
type: 'static-datetime',
labelClassName: 'text-muted',
format: 'YYYY-MM-DD HH:mm:ss',
name: 'date'
}
]
},
{
type: 'grid',
columns: [
{
type: 'form',
mode: 'horizontal',
title: '自动填充',
actions: [],
controls: [
{
label: '数据模板',
type: 'select',
labelClassName: 'text-muted',
name: 'tpl',
value: 'tpl1',
inline: true,
options: [
{
label: '模板1',
value: 'tpl1'
},
{
label: '模板2',
value: 'tpl2'
},
{
label: '模板3',
value: 'tpl3'
}
],
description: '<span class="text-danger">请修改这里看效果</span>'
},
'<div class="text-danger m-b">如果 <code>initApi</code> 已经暂用,用 <code>service</code>一样可以拉取值填充,同样以下 api 值发生变化时会自动填充。</div>',
{
type: 'service',
api: '/api/mock2/form/initData?tpl=${tpl}',
body: {
controls: [
{
label: '名称',
type: 'text',
labelClassName: 'text-muted',
name: 'name'
},
{
label: '作者',
type: 'text',
labelClassName: 'text-muted',
name: 'author'
},
{
label: '请求时间',
type: 'datetime',
labelClassName: 'text-muted',
inputFormat: 'YYYY-MM-DD HH:mm:ss',
name: 'date'
}
]
}
}
]
},
{
type: 'form',
mode: 'horizontal',
title: '手动填充',
actions: [],
controls: [
{
type: 'group',
label: '数据模板',
labelClassName: 'text-muted',
controls: [
{
type: 'select',
name: 'tpl',
value: 'tpl1',
mode: 'inline',
options: [
{
label: '模板1',
value: 'tpl1'
},
{
label: '模板2',
value: 'tpl2'
},
{
label: '模板3',
value: 'tpl3'
}
]
},
{
mode: 'inline',
type: 'button',
label: '获取',
level: 'dark',
actionType: 'reload',
target: 'theService'
}
]
},
'<div class="text-danger m-b">如果不想自动填充,自动填充,则把参数放在 data 里面,就不会监控变化自动拉取了,同时把 <code>servcie</code> 的初始拉取关掉,然后来个刷新目标组件的按钮。</div>',
{
type: 'service',
name: 'theService',
api: {
method: 'get',
url: '/api/mock2/form/initData',
data: {
tpl: '${tpl}'
}
},
body: {
controls: [
{
label: '名称',
type: 'text',
labelClassName: 'text-muted',
name: 'name'
},
{
label: '作者',
type: 'text',
labelClassName: 'text-muted',
name: 'author'
},
{
label: '请求时间',
type: 'datetime',
labelClassName: 'text-muted',
inputFormat: 'YYYY-MM-DD HH:mm:ss',
name: 'date'
}
]
}
}
]
}
]
},
{
type: 'divider'
},
{
type: 'form',
title: '条件表单',
target: 'detailForm',
submitOnInit: true,
mode: 'inline',
controls: [
{
label: '数据模板',
type: 'select',
labelClassName: 'text-muted',
name: 'tpl',
value: 'tpl1',
options: [
{
label: '模板1',
value: 'tpl1'
},
{
label: '模板2',
value: 'tpl2'
},
{
label: '模板3',
value: 'tpl3'
}
]
},
{
type: 'submit',
label: '提交',
primary: true
}
]
},
{
name: 'detailForm',
type: 'form',
mode: 'horizontal',
title: '响应表单',
initApi: '/api/mock2/form/initData?tpl=${tpl}',
initFetchOn: 'data.tpl',
actions: [],
controls: [
'<span class="text-danger">当 <code>initApi</code> 中有变量,且变量的值发生了变化了,则该表单就会重新初始数据。</span>',
{
type: 'divider'
},
{
label: '名称',
type: 'static',
labelClassName: 'text-muted',
name: 'name'
},
{
label: '作者',
type: 'static',
labelClassName: 'text-muted',
name: 'author'
},
{
label: '请求时间',
type: 'static-datetime',
labelClassName: 'text-muted',
format: 'YYYY-MM-DD HH:mm:ss',
name: 'date'
}
]
}
]
}

View File

@ -0,0 +1,60 @@
export default {
type: 'page',
title: '表单选线之间的远程联动',
body: {
type: 'form',
mode: 'horizontal',
title: '',
actions: [],
controls: [
'<p class="text-danger">表单选项可以设置 source 通过 API 远程拉取,同时如果 source 中有变量的话,变量值发生变化就会重新拉取,达到联动效果。</p>',
{
type: 'divider'
},
{
label: '选项1',
type: 'select',
labelClassName: 'text-muted',
name: 'a',
inline: true,
options: [
{
label: '选项1',
value: 1
},
{
label: '选项2',
value: 2
},
{
label: '选项3',
value: 3
}
]
},
{
label: '选项2',
type: 'select',
labelClassName: 'text-muted',
name: 'b',
inline: true,
source: '/api/mock2/options/level2?a=${a}',
initFetchOn: 'data.a'
},
{
label: '选项3',
type: 'select',
labelClassName: 'text-muted',
name: 'c',
inline: true,
visibleOn: 'data.b',
source: '/api/mock2/options/level3?b=${b}'
},
]
}
}

View File

@ -0,0 +1,68 @@
export default {
type: 'page',
title: '表单选线的联动',
body: {
type: 'form',
mode: 'horizontal',
title: '',
actions: [],
controls: [
'<p class="text-danger">表单选项内也能联动,通过配置 visibleOn、hiddenOn或者disabledOn</p>',
{
type: 'divider'
},
{
label: '选项1',
type: 'list',
multiple: false,
labelClassName: 'text-muted',
name: 'a',
inline: true,
options: [
{
label: '选项1',
value: 1
},
{
label: '选项2',
value: 2
},
{
label: '选项3',
value: 3
}
]
},
{
label: '选项2',
type: 'radios',
labelClassName: 'text-muted',
name: 'b',
inline: true,
options: [
{
label: '选项1',
value: 1,
disabledOn: 'data.a == 1'
},
{
label: '选项2',
value: 2,
hiddenOn: 'data.a == 2'
},
{
label: '选项3',
value: 3,
visibleOn: 'data.a == 3'
}
]
}
]
}
}

View File

@ -0,0 +1,49 @@
export default {
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
type: 'page',
title: "地址栏变化自动更新",
initApi: '/api/mock2/form/initData?id=${id}',
aside: {
type: 'wrapper',
size: 'xs',
className: '',
body: {
type: 'nav',
stacked: true,
links: [
{
label: '页面1',
to: '?id=1'
},
{
label: '页面2',
children: [
{
label: '页面2-1',
to: '?id=2-1',
},
{
label: '页面2-2',
to: '?id=2-2',
},
{
label: '页面2-3disabled',
disabled: true,
to: '?id=2-3',
},
]
},
{
label: '页面3',
to: '?id=3'
}
]
}
},
body: [
'<p><span class="text-danger">注意 page 渲染器的 `initApi` 中有变量跟地址栏中变量关联,只要值发生了变化,就会重新拉取一次 initApi。</sapn></p>',
"<p>这些数据是通过 initApi 拉取到的数据。 `\\$infoId`: <span class=\"text-danger\">${infoId|default:空}</span></p>"
]
};

View File

@ -0,0 +1,190 @@
/* eslint-disable no-unused-vars */
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {render} from '../../src/index';
import * as 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 {Portal} from 'react-overlays';
class CodePreview extends React.Component {
state = {
PlayGround: null
};
componentDidMount() {
require(['./Play'], (component) => this.setState({
PlayGround: component.default
}))
}
render() {
const {
container,
height,
setAsideFolded,
setHeaderVisible,
...rest
} = this.props;
const PlayGround = this.state.PlayGround;
// .markdown-body Overlay
return (
<div>
<span style={{display: 'block', height: height}} ref="span"></span>
{PlayGround ? <Overlay
container={container}
target={() => this.refs.span}
placement="bottom"
show
>
<PopOver offset={{x: 0, y: -height}} style={{height}} className="doc-shcema-preview-popover">
<div className="doc-schema-preview">
<PlayGround {...rest} vertical />
</div>
</PopOver>
</Overlay> : null }
</div>
);
}
}
function isActive(link, location) {
return !!(link.fullPath && link.fullPath === location.hash);
}
export default function(doc) {
return class extends React.Component {
static displayName = 'MarkdownRenderer';
ref = null;
doms = [];
constructor(props) {
super(props);
this.divRef = this.divRef.bind(this);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
this.renderSchema();
if (location.hash && location.hash.length > 1) {
//
if (window.history && 'scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual';
}
const dom = document.querySelector(`[name="${location.hash.substring(1)}"]`);
dom && dom.scrollIntoView();
}
}
componentDidUpdate() {
this.renderSchema();
}
componentWillUnmount() {
this.doms.forEach(dom => ReactDOM.unmountComponentAtNode(dom));
}
handleClick(e) {
const href = e.target.getAttribute('href');
if (href && href[0] !== '#' && !/^http/.test(href)) {
e.preventDefault();
this.props.push(href);
}
}
divRef(ref) {
this.ref = ref;
if (ref) {
ref.innerHTML = doc.html;
}
}
renderSchema() {
const scripts = document.querySelectorAll('script[type="text/schema"]');
if (!scripts && !scripts.length) {
return;
}
for (let i = 0, len = scripts.length; i < len; i++) {
let script = scripts[i];
let props = {};
[].slice.apply(script.attributes).forEach(item => {
props[item.name] = item.value
});
let dom = document.createElement('div');
let height = props.height ? parseInt(props.height, 10) : 200;
dom.setAttribute("class", "doc-play-ground")
dom.setAttribute("style", `height: ${height}px;`);
script.parentNode.replaceChild(dom, script);
this.doms.push(dom);
ReactDOM.unstable_renderSubtreeIntoContainer(this, (
<LazyComponent
{...this.props}
container={() => ReactDOM.findDOMNode(this)}
height={height}
component={CodePreview}
code={script.innerText}
scope={props.scope}
unMountOnHidden
placeholder="加载中,请稍后。。。"
/>
), dom);
}
}
render() {
const {location} = this.props;
return (
<div className="pos-rlt">
<TitleBar title={doc.title} />
<div className="markdown-body" ref={this.divRef}>Doc</div>
{doc.toc && doc.toc.children && doc.toc.children.length ? (
<Portal
container={() => document.querySelector('#asideInner')}
>
<NestedLinks
navigations={[doc.toc]}
renderLink={({link, active, toggleExpand, depth, classnames: cx}) => {
let children = [];
if (link.children) {
children.push(
<span
key="expand-toggle"
className={cx(`AsideNav-itemArrow`)}
></span>
);
}
link.badge && children.push(
<b key="badge" className={cx('AsideNav-itemBadge', link.badgeClassName || 'bg-info')}>{link.badge}</b>
);
depth === 1 && children.push(
<i key="icon" className={cx("AsideNav-itemIcon fa fa-flag")} />
);
children.push(
<span key="label" className={cx("AsideNav-itemLabel")}>{link.label}</span>
);
return link.fragment ? (<a href={`#${link.fragment}`}>{children}</a>) : (<a onClick={link.children ? () => toggleExpand(link) : null}>{children}</a>);
}}
isActive={link => isActive(link, location)}
/>
</Portal>
) : null}
</div>
);
}
}
}

View File

@ -0,0 +1,27 @@
export default {
type: 'page',
title: '标题',
remark: '提示 Tip',
body: [
`
<p>\`initApi\` 拉取失败时,页面内容区会显示对应的错误信息。</p>
<p>其他提示示例</p>
`,
{
type: 'alert',
level: 'success',
body: `温馨提示:对页面功能的提示说明,绿色为正向类的消息提示`
},
{
type: 'alert',
level: 'warning',
body: `您的私有网络已达到配额,如需更多私有网络,可以通过<a>工单</a>申请`
}
],
aside: '边栏',
toolbar: '工具栏',
initApi: '/api/mock2/page/initDataError'
}

View File

@ -0,0 +1,23 @@
export default {
type: 'page',
title: '表单页面',
body: {
type: 'form',
mode: 'horizontal',
title: '',
api: '/api/mock2/form/saveForm',
controls: [
{
label: 'Name',
type: 'text',
name: 'name'
},
{
label: 'Email',
type: 'email',
name: 'email'
}
]
}
}

View File

@ -0,0 +1,9 @@
export default {
type: 'page',
title: '标题',
remark: '提示 Tip',
body: "内容部分. 可以使用 \\${var} 获取变量。如: `\\$date`: ${date}",
aside: '边栏部分',
toolbar: '工具栏',
initApi: '/api/mock2/page/initData'
}

View File

@ -0,0 +1,290 @@
import * as React from 'react';
import {
toast
} from '../../src/components/Toast';
import {render} from '../../src/index';
import * as axios from 'axios';
import Frame from 'react-frame-component';
import * as stripJsonComments from 'strip-json-comments';
import CodeEditor from '../../src/components/Editor';
const DEFAULT_CONTENT = `{
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"type": "page",
"title": "Title",
"body": "Body",
"aside": "Aside",
"toolbar": "Toolbar"
}`;
const scopes = {
'none': ``,
'body': `{
"type": "page",
"body": SCHEMA_PLACEHOLDER
}`,
'form': `{
"type": "page",
"body": {
"title": "",
"type": "form",
"autoFocus": false,
"api": "/api/mock/saveForm?waitSeconds=1",
"mode": "horizontal",
"controls": SCHEMA_PLACEHOLDER,
"submitText": null,
"actions": []
}
}`,
'form-item': `{
"type": "page",
"body": {
"title": "",
"type": "form",
"mode": "horizontal",
"autoFocus": false,
"controls": [
SCHEMA_PLACEHOLDER
],
"submitText": null,
"actions": []
}
}`
};
export default class PlayGround extends React.Component {
state = null;
startX = 0;
oldContents = '';
frameTemplate;
static defaultProps = {
useIFrame: false,
vertical: false
};
constructor(props) {
super(props);
const schema = this.buildSchema(props.code || DEFAULT_CONTENT, props);
this.state = {
asideWidth: props.asideWidth || Math.max(300, window.innerWidth * 0.3),
schema: schema,
schemaCode: JSON.stringify(schema, null, 2)
};
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.removeWindowEvents = this.removeWindowEvents.bind(this);
this.handleChange = this.handleChange.bind(this);
this.schemaProps = {
style: {
height: '100%'
}
};
this.env = {
updateLocation: () => {
},
fetcher: config => {
config = {
dataType: 'json',
...config
};
if (config.dataType === 'json' && config.data) {
config.data = JSON.stringify(config.data)
config.headers = config.headers || {};
config.headers['Content-Type'] = 'application/json';
}
return axios[config.method](config.url, config.data, config);
},
notify: (type, msg) => toast[type] ? toast[type](msg, type === 'error' ? '系统错误' : '系统消息') : console.warn('[Notify]', type, msg)
}
const links = [].slice.call(document.head.querySelectorAll('link,style')).map(item => item.outerHTML);
this.frameTemplate = `<!DOCTYPE html><html><head>${links.join('')}</head><body><div></div></body></html>`;
this.handleEditorMount = this.handleEditorMount.bind(this);
}
componentWillReceiveProps(nextprops) {
const props = this.props;
if (props.code !== nextprops.code) {
const schema = this.buildSchema(nextprops.code || DEFAULT_CONTENT, nextprops);
this.setState({
schema: schema,
schemaCode: JSON.stringify(schema, null, 2)
});
}
}
componentDidMount() {
this.props.setAsideFolded && this.props.setAsideFolded(true);
}
componentWillUnmount() {
this.props.setAsideFolded && this.props.setAsideFolded(false);
}
buildSchema(schemaContent, props = this.props) {
const query = props.location.query;
try {
const scope = query.scope || props.scope;
if (scope && scopes[scope]) {
schemaContent = scopes[scope].replace('SCHEMA_PLACEHOLDER', schemaContent);
}
schemaContent = stripJsonComments(schemaContent).replace(/('|")raw:/g, '$1'); //
return JSON.parse(schemaContent);
} catch (e) {
console.error(this.formatMessage(e.message, schemaContent));
}
return {};
}
formatMessage(message, input) {
if (/position\s?(\d+)$/.test(message)) {
const lines = input.substring(0, parseInt(RegExp.$1, 10)).split(/\n|\r\n|\r/);
message = `Json 语法错误,请检测。出错位置:${lines.length},列:${lines[lines.length -1].length}`;
}
return message;
}
renderPreview() {
const schema = this.state.schema;
if (!this.props.useIFrame) {
return render(schema, this.schemaProps, this.env);
}
return (
<Frame
width="100%"
height="100%"
frameBorder={0}
initialContent={this.frameTemplate}
>
{render(schema, this.schemaProps, this.env)}
</Frame>
);
}
handleEditorMount(editor, monaco) {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
enableSchemaRequest: true,
validate: true
});
}
handleChange(value) {
this.setState({
schemaCode: value
});
try {
const schema = JSON.parse(value);
this.setState({
schema
});
} catch (e) {
//ignore
}
}
handleMouseDown(e) {
this.startX = e.clientX;
this.startWidth = this.state.asideWidth;
// this.startPosition.y = e.clientY;
window.addEventListener('mouseup', this.handleMouseUp);
window.addEventListener('mousemove', this.handleMouseMove);
return false;
}
handleMouseMove(e) {
const diff = this.startX - e.clientX;
e.preventDefault();
this.setState({
asideWidth: Math.min(800, Math.max(200, this.startWidth + diff))
});
}
handleMouseUp() {
this.removeWindowEvents();
}
removeWindowEvents() {
window.removeEventListener('mouseup', this.handleMouseUp);
window.removeEventListener('mousemove', this.handleMouseMove);
}
renderEditor() {
return (
<CodeEditor
value={this.state.schemaCode}
onChange={this.handleChange}
language="json"
editorDidMount={this.handleEditorMount}
/>
);
}
render() {
const {
vertical
} = this.props;
if (vertical) {
return (
<div className="vbox">
<div className="row-row">
<div className="cell pos-rlt">
<div className="scroll-y h-full pos-abt w-full">
{this.renderPreview()}
</div>
</div>
</div>
<div className="row-row" style={{height: 200}}>
<div className="cell">
{this.renderEditor()}
</div>
</div>
</div>
);
}
return (
<div style={{
position: "absolute",
top: 50,
bottom: 0
}}>
<div className="hbox">
<div className="col pos-rlt">
<div className="scroll-y h-full pos-abt w-full">
{this.renderPreview()}
</div>
</div>
<div className="col bg-light lter b-l bg-auto pos-rlt" style={{width: this.state.asideWidth}}>
<div className="resizer" onMouseDown={this.handleMouseDown}></div>
{this.renderEditor()}
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,171 @@
import * as React from 'react';
import {render} from '../../src/index';
import * as axios from 'axios';
import {toast} from '../../src/components/Toast';
import {alert, confirm} from '../../src/components/Alert';
import Button from '../../src/components/Button'
import LazyComponent from '../../src/components/LazyComponent'
import {default as DrawerContainer} from '../../src/components/Drawer'
import { Portal } from 'react-overlays';
function loadEditor() {
return new Promise((resolve) => require(['../../src/components/Editor'], (component) => resolve(component.default)));
}
export default function(schema) {
if (!schema['$schema']) {
schema = {
'$schema': 'http://amis.baidu.com/v2/schemas/page.json',
...schema
};
}
return class extends React.Component {
static displayName = 'SchemaRenderer';
state = {open: false};
toggleCode = () => this.setState({
open: !this.state.open
});
close = () => this.setState({
open: false
});
constructor(props) {
super(props);
const {router} = props;
const normalizeLink = (to) => {
to = to || '';
const location = router.getCurrentLocation();
if (to && to[0] === '#') {
to = location.pathname + location.search + to;
} else if (to && to[0] === '?') {
to = location.pathname + to;
}
const idx = to.indexOf('?');
const idx2 = to.indexOf('#');
let pathname = ~idx ? to.substring(0, idx) : ~idx2 ? to.substring(0, idx2) : to;
let search = ~idx ? to.substring(idx, ~idx2 ? idx2 : undefined) : '';
let hash = ~idx2 ? to.substring(idx2) : '';
if (!pathname) {
pathname = location.pathname;
} else if (pathname[0] != '/' && !/^https?:\/\//.test(pathname)) {
// todo
}
return pathname + search + hash;
}
this.env = {
updateLocation: (location, replace) => {
router[replace ? 'replace' : 'push'](normalizeLink(location));
},
isCurrentUrl: (to) => {
const link = normalizeLink(to);
return router.isActive(link);
},
jumpTo: (to) => {
to = normalizeLink(to);
if (/^https?:\/\//.test(to)) {
window.location.replace(to);
} else {
router.push(to);
}
},
fetcher: ({
url,
method,
data,
config
}) => {
if (data && data instanceof FormData) {
// config.headers = config.headers || {};
// config.headers['Content-Type'] = 'multipart/form-data';
} else if (data
&& typeof data !== 'string'
&& !(data instanceof Blob)
&& !(data instanceof ArrayBuffer)
) {
data = JSON.stringify(data);
config = config || {};
config.headers = config.headers || {};
config.headers['Content-Type'] = 'application/json';
}
if (method !== 'post' && method !== 'put' && method !== 'patch') {
if (data) {
if (method === 'delete') {
config.data = data;
} else {
config.params = data;
}
}
return axios[method](url, config);
}
return axios[method](url, data, config);
},
notify: (type, msg) => toast[type] ? toast[type](msg, type === 'error' ? '系统错误' : '系统消息') : console.warn('[Notify]', type, msg),
alert,
confirm,
copy: (content) => console.log('Copy', content)
};
this.handleEditorMount = this.handleEditorMount.bind(this);
}
handleEditorMount(editor, monaco) {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
enableSchemaRequest: true,
validate: true
});
}
renderCode() {
return (
<LazyComponent
getComponent={loadEditor}
editorDidMount={this.handleEditorMount}
language="json"
value={schema}
placeholder="加载中,请稍后。。。"
disabled
/>
);
}
renderSchema() {
const {
router,
location,
theme
} = this.props;
return render(schema, {
location,
theme
}, this.env);
}
render() {
const ns = this.props.classPrefix;
return (
<div className="schema-wrapper">
<DrawerContainer
classPrefix={ns}
size="lg"
onHide={this.close}
show={this.state.open}
position="left"
>
{this.state.open ? this.renderCode() : null}
</DrawerContainer>
{this.renderSchema()}
<Portal container={() => document.querySelector('.navbar-nav')}>
<Button classPrefix={ns} onClick={this.toggleCode} active={this.state.open} iconOnly tooltip="查看源码" level="link" placement="bottom" className="view-code"><i className="fa fa-code" /></Button>
</Portal>
</div>
);
}
}
}

View File

@ -0,0 +1,90 @@
import * as React from 'react';
import TitleBar from '../../../src/components/TitleBar';
import {render} from '../../../src/index';
export default class SdkTest extends React.Component {
state = {
data: {
name: 'Amis Renderer',
id: 1,
email: 'xxx@xxx.com'
}
};
renderForm() {
return render({
title: '',
type: 'form',
controls: [
{
type: 'text',
name: 'name',
label: 'Name'
},
{
type: 'text',
name: 'id',
label: 'Id'
},
{
type: 'email',
name: 'email',
label: 'Email'
},
{
type: 'static',
label: '最后更新时间',
name: 'lastModified'
}
]
}, {
data: this.state.data,
onFailed: (reason, errors) => {
console.log('Submit Failed', errors, '\n', reason);
},
onSubmit: (values) => {
console.log('Submit', values);
},
onChange: (values, diff) => {
this.setState({
data: {
...values,
lastModified: new Date()
}
});
console.log('Diff', diff);
},
});
}
handleClick = () => {
this.setState({
data: {
name: 'Amis Renderer',
id: Math.round(Math.random() * 1000),
email: 'xxx@xxx.com'
}
})
};
render() {
return (
<div>
<TitleBar title="API 调用 集成在你的 React 应用中" />
<div className="wrapper">
{this.renderForm()}
<button onClick={this.handleClick}>随机修改</button>
<h3>当前值</h3>
<pre><code>{JSON.stringify(this.state.data, null, 2)}</code></pre>
</div>
</div>
);
}
}

View File

@ -0,0 +1,118 @@
export default {
type: 'page',
title: '动态加载数据',
body: [
'<span class="text-danger">除了用 Page、CRUD、Form 或者 Wizard 能拉取数据外,还可以通过 Service 专门拉取数据,然后丢给其他类型的渲染器渲染。</span>',
{
type: 'form',
title: '条件输入',
className: 'm-t',
wrapWithPanel: false,
target: 'service1',
mode: 'inline',
controls: [
{
type: 'text',
name: 'keywords',
placeholder: '关键字',
addOn: {
type: 'button',
icon: 'fa fa-search',
actionType: 'submit',
level: 'primary'
}
}
]
},
{
name: 'service1',
type: 'service',
className: 'm-t',
api: '/api/mock2/service/data?keywords=${keywords}',
body: [
'当前关键字是 ${keywords},当前时间是: ${date|date:YYYY-MM-DD HH\\:mm}',
{
type: 'table',
className: 'm-t',
source: '${table1}',
columns: [
{
name: "id",
label: "ID",
type: "text"
},
{
name: "text",
label: "文本",
type: "text"
},
{
type: 'image',
label: '图片',
name: 'image',
popOver: {
title: '查看大图',
body: '<div class="w-xxl"><img class="w-full" src="${image}"/></div>'
}
},
{
name: 'date',
type: 'date',
label: '日期'
}
]
},
{
type: 'table',
source: '${table2}',
columns: [
{
name: "progress",
label: "进度",
type: "progress"
},
{
name: "boolean",
label: "状态",
type: "status"
},
{
name: "boolean",
label: "开关",
type: "switch",
// readOnly: false //
},
{
name: "type",
label: "映射",
type: "mapping",
map: {
"*": "其他:${type}",
"1": "<span class='label label-info'>漂亮</span>",
"2": "<span class='label label-success'>开心</span>",
"3": "<span class='label label-danger'>惊吓</span>",
"4": "<span class='label label-warning'>紧张</span>"
}
},
{
name: 'list',
type: 'list',
label: 'List',
placeholder: '-',
listItem: {
title: '${title}',
subTitle: '${description}'
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,68 @@
export default {
type: 'page',
title: '动态加载表单中的部分',
body: [
'<span class="text-danger">同样通过 <code>service</code>的<code>schemaApi</code> 来加载部分内容,当然也可以全部由它来加载</span>',
{
type: 'form',
panelClassName: 'Panel--info m-t',
target: 'service1',
mode: 'horizontal',
api: '/api/mock2/form/saveForm?waitSeconds=1',
fieldSet: [
{
title: '基本信息',
controls: [
{
type: 'text',
label: '字段一',
name: 'filed1',
},
{
type: 'text',
label: '字段二',
name: 'filed2'
}
]
},
{
title: '其他信息',
controls: [
{
name: 'tpl',
type: 'select',
label: '模板',
inline: true,
required: true,
value: 'tpl1',
options: [
{
label: '模板1',
value: 'tpl1'
},
{
label: '模板2',
value: 'tpl2'
},
{
label: '模板3',
value: 'tpl3'
}
]
},
{
type: 'service',
className: 'm-t',
initFetchSchemaOn: 'data.tpl',
schemaApi: '/api/mock2/service/form?tpl=$tpl',
}
]
}
]
}
]
}

View File

@ -0,0 +1,50 @@
export default {
type: 'page',
title: '动态加载页面',
body: [
'<span class="text-danger">可以通过 <code>service</code>的<code>schemaApi</code> 动态控制内容。</span>',
{
type: 'form',
title: '条件输入',
panelClassName: 'panel-info m-t',
target: 'service1',
mode: 'inline',
submitOnInit: true,
controls: [
{
label: '加载页面类型',
required: true,
type: 'button-group',
submitOnChange: true,
value: 'crud',
name: 'type',
options: [
{
label: 'Crud',
value: 'crud'
},
{
label: 'Form',
value: 'form'
},
{
label: 'Tabs',
value: 'tabs'
}
]
}
]
},
{
name: 'service1',
type: 'service',
className: 'm-t',
initFetchSchema: false,
schemaApi: '/api/mock2/service/schema?type=$type',
}
]
}

View File

@ -0,0 +1,47 @@
export default {
type: 'page',
title: '表单中选项卡分组',
subTitle: '',
body: [
'<p>多个 controls 可以通过 tabs 来分组展示,表单将作为一个整体提交。</p>',
{
type: 'form',
title: '',
tabs: [
{
title: '选项卡1',
hash: 'tab1',
controls: [
{
type: 'text',
label: '文本1',
name: 'a'
}
]
},
{
title: '选项卡2',
hash: 'tab2',
controls: [
{
type: 'text',
label: '文本2',
name: 'b'
}
]
},
{
title: '选项卡3',
hash: 'tab3',
controls: [
{
type: 'text',
label: '文本3',
name: 'c'
}
]
}
]
}]
}

View File

@ -0,0 +1,165 @@
export default {
type: 'page',
title: '选项卡示例',
subTitle: '所有选项卡都在当前页面中包括默认、line、card以及radio模式',
body: [
{
type: 'tabs',
tabs: [
{
title: '选项卡1',
hash: 'tab1',
body: '选项卡内容1'
},
{
title: '选项卡2',
hash: 'tab2',
body: {
type: 'form',
panelClassName: 'panel-primary',
controls: [
{
type: 'text',
name: 'a',
label: '文本'
}
]
}
},
{
title: '选项卡3',
body: {
type: "crud",
api: "/api/sample",
filter: {
title: "条件搜索",
submitText: "",
controls: [
{
type: "text",
name: "keywords",
placeholder: "通过关键字搜索",
clearable: true,
addOn: {
label: "搜索",
type: "submit"
}
},
{
type: "plain",
text: "这里的表单项可以配置多个"
}
]
},
columns: [
{
name: "id",
label: "ID",
width: 20
},
{
name: "engine",
label: "Rendering engine"
},
{
name: "browser",
label: "Browser"
},
{
name: "platform",
label: "Platform(s)"
},
{
name: "version",
label: "Engine version"
},
{
name: "grade",
label: "CSS grade"
},
{
type: "operation",
label: "操作",
width: 100,
buttons: [
]
}
]
}
}
]
},
{
type: 'divider'
},
{
type: 'tabs',
mode: 'line',
tabs: [
{
title: '选项卡1',
body: '选项卡内容1'
},
{
title: '选项卡2',
body: '选项卡内容2'
},
{
title: '选项卡3',
body: '选项卡内容3'
}
]
},
{
type: 'divider'
},
{
type: 'tabs',
mode: 'card',
tabs: [
{
title: '选项卡1',
body: '选项卡内容1'
},
{
title: '选项卡2',
body: '选项卡内容2'
},
{
title: '选项卡3',
body: '选项卡内容3'
}
]
},
{
type: 'divider'
},
{
type: 'tabs',
mode: 'radio',
tabs: [
{
title: '选项卡1',
body: '选项卡内容1'
},
{
title: '选项卡2',
body: '选项卡内容2'
},
{
title: '选项卡3',
body: '选项卡内容3'
}
]
},
]
}

View File

@ -0,0 +1,35 @@
export default {
type: 'page',
title: '选项卡1页面',
body: [
'<p>也可以多个页面,利用导航<code>nav</code>渲染期模拟 tabs 的效果。这样可以让 url 更加友好,而不是只能用 hash。</p>',
{
type: 'nav',
links: [
{
label: '选项卡1',
icon: 'fa fa-cloud',
to: '/examples/tabs/tab1'
},
{
label: '选项卡2',
to: '/examples/tabs/tab2'
},
{
label: '选项卡3',
icon: 'fa fa-youtube',
to: '/examples/tabs/tab3'
}
]
},
{
type: 'wrapper',
className: 'wrapper bg-white b-l b-b b-r',
body: '选项卡1的内容'
}
]
}

View File

@ -0,0 +1,35 @@
export default {
type: 'page',
title: '选项卡2页面',
body: [
'<p>也可以多个页面,利用导航<code>nav</code>渲染期模拟 tabs 的效果。</p>',
{
type: 'nav',
links: [
{
label: '选项卡1',
icon: 'fa fa-cloud',
to: '/examples/tabs/tab1'
},
{
label: '选项卡2',
to: '/examples/tabs/tab2'
},
{
label: '选项卡3',
icon: 'fa fa-youtube',
to: '/examples/tabs/tab3'
}
]
},
{
type: 'wrapper',
className: 'wrapper bg-white b-l b-b b-r',
body: '选项卡2的内容'
}
]
}

View File

@ -0,0 +1,121 @@
export default {
type: 'page',
title: '选项卡3页面',
body: [
'<p>也可以多个页面,利用导航<code>nav</code>渲染期模拟 tabs 的效果。</p>',
{
type: 'nav',
links: [
{
label: '选项卡1',
icon: 'fa fa-cloud',
to: '/examples/tabs/tab1'
},
{
label: '选项卡2',
to: '/examples/tabs/tab2'
},
{
label: '选项卡3',
icon: 'fa fa-youtube',
to: '/examples/tabs/tab3'
}
]
},
{
type: 'wrapper',
className: 'wrapper bg-white b-l b-b b-r',
body: {
"type": "chart",
"config": {
"title": {
"text": "极坐标双数值轴"
},
"legend": {
"data": [
"line"
]
},
"polar": {
"center": [
"50%",
"54%"
]
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "cross"
}
},
"angleAxis": {
"type": "value",
"startAngle": 0
},
"radiusAxis": {
"min": 0
},
"series": [
{
"coordinateSystem": "polar",
"name": "line",
"type": "line",
"showSymbol": false,
"data": [
[
0,
0
],
[
0.03487823687206265,
1
],
[
0.06958655048003272,
2
],
[
0.10395584540887964,
3
],
[
0.13781867790849958,
4
],
[
0.17101007166283433,
5
],
[
0.2033683215379001,
6
],
[
0.2347357813929454,
7
],
[
0.26495963211660245,
8
],
[
0.2938926261462365,
9
],
[
0.3213938048432697,
10
]
]
}
],
"animationDuration": 2000
}
}
}
]
}

View File

@ -0,0 +1,36 @@
export default {
"$schema": "http://amis.baidu.com/v2/schemas/page.json#",
"title": "异步任务",
"body": [
'<p class="text-danger"></p>',
{
"type": "tasks",
"name": "tasks",
"items": [
{
"label": "hive 任务",
"key": "hive",
"status": 4,
"remark": "查看详情<a target=\"_blank\" href=\"http://www.baidu.com\">日志</a>。"
},
{
"label": "小流量",
"key": "partial",
"status": 4
},
{
"label": "全量",
"key": "full",
"status": 4
}
]
},
{
"type": "tasks",
"name": "tasks",
"className": "b-a bg-white table-responsive m-t",
"checkApi": "/api/mock2/task"
}
]
}

Some files were not shown because too many files have changed in this diff Show More