mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 02:48:55 +08:00
官网文档及示例支持切换到移动端下显示
This commit is contained in:
parent
7dabf46bc0
commit
771557224e
@ -8,9 +8,9 @@ amis 有两种使用方法:
|
||||
- [JS SDK](#SDK)
|
||||
- [React](#react)
|
||||
|
||||
React 可以完整使用 amis 的所有功能,方便扩展。
|
||||
React 版本可以完整使用 amis 的所有功能,方便扩展。
|
||||
|
||||
SDK 适合对前端或 React 不了解的开发者,它不依赖 npm 及 webpack,直接引入代码就能使用,但需要注意这种方式难以支持 [自定义组件](./custom),只能使用 amis 内置的组件。
|
||||
SDK 版本适合对前端或 React 不了解的开发者,它不依赖 npm 及 webpack,可以像 Vue/jQuery 那样外链代码就能使用,但需要注意这种方式难以支持 [自定义组件](./custom),只能使用 amis 内置的组件。
|
||||
|
||||
## SDK
|
||||
|
||||
|
@ -66,6 +66,18 @@ const locales = [
|
||||
}
|
||||
];
|
||||
|
||||
const viewModes = [
|
||||
{
|
||||
label: '桌面端',
|
||||
value: 'pc'
|
||||
},
|
||||
|
||||
{
|
||||
label: '移动端',
|
||||
value: 'mobile'
|
||||
}
|
||||
];
|
||||
|
||||
function getPath(path) {
|
||||
return path
|
||||
? path[0] === '/'
|
||||
@ -108,6 +120,7 @@ class BackTop extends React.PureComponent {
|
||||
@withRouter
|
||||
export class App extends React.PureComponent {
|
||||
state = {
|
||||
viewMode: localStorage.getItem('viewMode') || 'pc',
|
||||
offScreen: false,
|
||||
headerVisible: true,
|
||||
themeIndex: 0,
|
||||
@ -226,11 +239,12 @@ export class App extends React.PureComponent {
|
||||
onChange={locale => {
|
||||
this.setState({locale: locale.value});
|
||||
localStorage.setItem('locale', locale.value);
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="hidden-xs p-t pull-right">
|
||||
<div className="hidden-xs p-t pull-right m-l-sm">
|
||||
<Select
|
||||
clearable={false}
|
||||
theme={this.state.theme.value}
|
||||
@ -248,6 +262,20 @@ export class App extends React.PureComponent {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="hidden-xs p-t pull-right">
|
||||
<Select
|
||||
clearable={false}
|
||||
theme={this.state.theme.value}
|
||||
value={this.state.viewMode || 'pc'}
|
||||
options={viewModes}
|
||||
onChange={viewMode => {
|
||||
this.setState({viewMode: viewMode.value});
|
||||
localStorage.setItem('viewMode', viewMode.value);
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${theme.ns}Layout-searchBar hidden-xs hidden-sm`}>
|
||||
@ -368,6 +396,7 @@ export class App extends React.PureComponent {
|
||||
setNavigations: this.setNavigations,
|
||||
theme: theme.value,
|
||||
classPrefix: theme.ns,
|
||||
viewMode: this.state.viewMode,
|
||||
locale: this.state.locale,
|
||||
offScreen: this.state.offScreen,
|
||||
ContextPath
|
||||
|
@ -1045,6 +1045,7 @@ export default class Doc extends React.PureComponent {
|
||||
theme: this.props.theme,
|
||||
classPrefix: this.props.classPrefix,
|
||||
locale: this.props.locale,
|
||||
viewMode: this.props.viewMode,
|
||||
offScreen: this.props.offScreen,
|
||||
ContextPath: this.props.ContextPath,
|
||||
prevDoc: this.state.prevDoc,
|
||||
|
@ -560,6 +560,7 @@ export default class Example extends React.PureComponent {
|
||||
theme: this.props.theme,
|
||||
classPrefix: this.props.classPrefix,
|
||||
locale: this.props.locale,
|
||||
viewMode: this.props.viewMode,
|
||||
offScreen: this.props.offScreen
|
||||
})}
|
||||
</>
|
||||
|
@ -24,17 +24,19 @@ class CodePreview extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
container,
|
||||
height,
|
||||
setAsideFolded,
|
||||
setHeaderVisible,
|
||||
...rest
|
||||
} = this.props;
|
||||
const {container, setAsideFolded, setHeaderVisible, ...rest} = this.props;
|
||||
|
||||
let height = this.props.height;
|
||||
|
||||
const PlayGround = this.state.PlayGround;
|
||||
// 不要放在 .markdown-body 下面,因为样式会干扰,复写又麻烦,所以通过 Overlay 渲染到同级
|
||||
|
||||
if (this.props.viewMode === 'mobile') {
|
||||
// 移动端下高度不能太低
|
||||
if (height < 500) {
|
||||
height = 500;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<span style={{display: 'block', height: height}} ref="span" />
|
||||
|
@ -61,15 +61,15 @@ export default class PlayGround extends React.Component {
|
||||
startX = 0;
|
||||
oldContents = '';
|
||||
frameTemplate;
|
||||
iframeRef;
|
||||
|
||||
static defaultProps = {
|
||||
useIFrame: false,
|
||||
vertical: false
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.iframeRef = React.createRef();
|
||||
const {router} = props;
|
||||
|
||||
const schema = this.buildSchema(props.code || DEFAULT_CONTENT, props);
|
||||
@ -139,12 +139,27 @@ export default class PlayGround extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
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.watchIframeReady = this.watchIframeReady.bind(this);
|
||||
window.addEventListener('message', this.watchIframeReady, false);
|
||||
}
|
||||
|
||||
watchIframeReady(event) {
|
||||
// iframe 里面的 amis 初始化了就可以发数据
|
||||
if (event.data && event.data === 'amisReady') {
|
||||
this.updateIframe();
|
||||
}
|
||||
}
|
||||
|
||||
updateIframe() {
|
||||
if (this.iframeRef && this.iframeRef.current) {
|
||||
this.iframeRef.current.contentWindow.postMessage(
|
||||
{
|
||||
schema: this.state.schema,
|
||||
props: {theme: this.props.theme, locale: this.props.locale}
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextprops) {
|
||||
@ -168,6 +183,7 @@ export default class PlayGround extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.setAsideFolded && this.props.setAsideFolded(false);
|
||||
window.removeEventListener('message', this.watchIframeReady, false);
|
||||
}
|
||||
|
||||
buildSchema(schemaContent, props = this.props) {
|
||||
@ -224,33 +240,37 @@ export default class PlayGround extends React.Component {
|
||||
affixFooter: false
|
||||
};
|
||||
|
||||
if (!this.props.useIFrame) {
|
||||
return render(schema, props, this.env);
|
||||
if (this.props.viewMode === 'mobile') {
|
||||
return (
|
||||
<iframe
|
||||
width="375"
|
||||
height="100%"
|
||||
frameBorder={0}
|
||||
className="mobile-frame"
|
||||
ref={this.iframeRef}
|
||||
// @ts-ignore
|
||||
src={__uri('../index.html#mobileView')}
|
||||
></iframe>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Frame
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameBorder={0}
|
||||
initialContent={this.frameTemplate}
|
||||
>
|
||||
{render(schema, props, this.env)}
|
||||
</Frame>
|
||||
);
|
||||
return render(schema, props, this.env);
|
||||
}
|
||||
|
||||
handleChange(value) {
|
||||
this.setState({
|
||||
schemaCode: value
|
||||
});
|
||||
|
||||
try {
|
||||
const schema = JSON.parse(value);
|
||||
|
||||
this.setState({
|
||||
schema
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
schema
|
||||
},
|
||||
() => {
|
||||
this.updateIframe();
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
//ignore
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ function loadEditor() {
|
||||
resolve(component.default))
|
||||
);
|
||||
}
|
||||
|
||||
const viewMode = localStorage.getItem('viewMode') || 'pc';
|
||||
|
||||
export default function (schema) {
|
||||
if (!schema['$schema']) {
|
||||
schema = {
|
||||
@ -25,7 +28,8 @@ export default function (schema) {
|
||||
return withRouter(
|
||||
class extends React.Component {
|
||||
static displayName = 'SchemaRenderer';
|
||||
state = {open: false};
|
||||
iframeRef;
|
||||
state = {open: false, schema: {}};
|
||||
toggleCode = () =>
|
||||
this.setState({
|
||||
open: !this.state.open
|
||||
@ -40,6 +44,7 @@ export default function (schema) {
|
||||
});
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {router} = props;
|
||||
this.env = {
|
||||
updateLocation: (location, replace) => {
|
||||
@ -92,6 +97,10 @@ export default function (schema) {
|
||||
};
|
||||
|
||||
this.handleEditorMount = this.handleEditorMount.bind(this);
|
||||
|
||||
this.iframeRef = React.createRef();
|
||||
this.watchIframeReady = this.watchIframeReady.bind(this);
|
||||
window.addEventListener('message', this.watchIframeReady, false);
|
||||
}
|
||||
|
||||
handleEditorMount(editor, monaco) {
|
||||
@ -130,9 +139,51 @@ export default function (schema) {
|
||||
);
|
||||
}
|
||||
|
||||
watchIframeReady(event) {
|
||||
// iframe 里面的 amis 初始化了就可以发数据
|
||||
if (event.data && event.data === 'amisReady') {
|
||||
this.updateIframe();
|
||||
}
|
||||
}
|
||||
|
||||
updateIframe() {
|
||||
if (this.iframeRef && this.iframeRef.current) {
|
||||
this.iframeRef.current.contentWindow.postMessage(
|
||||
{
|
||||
schema: schema,
|
||||
props: {
|
||||
location: this.props.location,
|
||||
theme: this.props.theme,
|
||||
locale: this.props.locale
|
||||
}
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.setAsideFolded && this.props.setAsideFolded(false);
|
||||
window.removeEventListener('message', this.watchIframeReady, false);
|
||||
}
|
||||
|
||||
renderSchema() {
|
||||
const {router, location, theme, locale} = this.props;
|
||||
|
||||
if (viewMode === 'mobile') {
|
||||
return (
|
||||
<iframe
|
||||
width="375"
|
||||
height="100%"
|
||||
frameBorder={0}
|
||||
className="mobile-frame"
|
||||
ref={this.iframeRef}
|
||||
// @ts-ignore
|
||||
src={__uri('../index.html#mobileView')}
|
||||
></iframe>
|
||||
);
|
||||
}
|
||||
|
||||
return render(
|
||||
schema,
|
||||
{
|
||||
|
@ -44,21 +44,53 @@
|
||||
<div id="root" class="app-wrapper"></div>
|
||||
<script src="./mod.js"></script>
|
||||
<script type="text/javascript">
|
||||
var _hmt = _hmt || [];
|
||||
if (location.hash === '#mobileView') {
|
||||
// TODO: 本来应该使用 mobile.html 的,但 gh-pages 编译时 themes 下的 scss hash 不一致导致出错,所以先和 index.html 放一起
|
||||
const themes = [
|
||||
{
|
||||
label: '默认主题',
|
||||
value: 'default'
|
||||
},
|
||||
{
|
||||
label: '百度云舍',
|
||||
value: 'cxd'
|
||||
},
|
||||
{
|
||||
label: 'Dark',
|
||||
value: 'dark'
|
||||
}
|
||||
];
|
||||
const theme = themes[localStorage.getItem('themeIndex') || 0];
|
||||
// mobile 下先禁掉所有外链避免影响
|
||||
document.querySelectorAll('link').forEach(item => {
|
||||
item.disabled = true;
|
||||
});
|
||||
document.querySelector(`link[title=${theme.value}]`).disabled = false;
|
||||
if (theme.value === 'dark') {
|
||||
document.querySelector('body').classList.add('dark');
|
||||
}
|
||||
|
||||
// 百度统计
|
||||
(function () {
|
||||
var hm = document.createElement('script');
|
||||
hm.src = 'https://hm.baidu.com/hm.js?1f80f2c9dbe21dc3af239cf9eee90f1f';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
amis.require(['./mobile.tsx'], function (app) {
|
||||
app.bootstrap(document.getElementById('root'), {});
|
||||
});
|
||||
} else {
|
||||
var _hmt = _hmt || [];
|
||||
|
||||
/* @require ./index.jsx 标记为同步依赖,提前加载 */
|
||||
amis.require(['./index.jsx'], function (app) {
|
||||
var initialState = {};
|
||||
app.bootstrap(document.getElementById('root'), initialState);
|
||||
});
|
||||
// 百度统计
|
||||
(function () {
|
||||
var hm = document.createElement('script');
|
||||
hm.src =
|
||||
'https://hm.baidu.com/hm.js?1f80f2c9dbe21dc3af239cf9eee90f1f';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
|
||||
/* @require ./index.jsx 标记为同步依赖,提前加载 */
|
||||
amis.require(['./index.jsx'], function (app) {
|
||||
var initialState = {};
|
||||
app.bootstrap(document.getElementById('root'), initialState);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
75
examples/mobile.html
Normal file
75
examples/mobile.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>MobileView</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link type="image/x-icon" rel="shortcut icon" href="./static/favicon.png" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||
<link rel="stylesheet" href="font-awesome/css/font-awesome.css" />
|
||||
<link rel="stylesheet" href="animate.css/animate.css" />
|
||||
<link rel="stylesheet" title="default" href="../scss/themes/default.scss" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
title="cxd"
|
||||
disabled
|
||||
href="../scss/themes/cxd.scss"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
title="dark"
|
||||
disabled
|
||||
href="../scss/themes/dark.scss"
|
||||
/>
|
||||
<style>
|
||||
.app-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root" class="app-wrapper"></div>
|
||||
<script src="./mod.js"></script>
|
||||
<script type="text/javascript">
|
||||
const themes = [
|
||||
{
|
||||
label: '默认主题',
|
||||
ns: 'a-',
|
||||
value: 'default'
|
||||
},
|
||||
|
||||
{
|
||||
label: '百度云舍',
|
||||
ns: 'cxd-',
|
||||
value: 'cxd'
|
||||
},
|
||||
{
|
||||
label: 'Dark',
|
||||
ns: 'dark-',
|
||||
value: 'dark'
|
||||
}
|
||||
];
|
||||
const theme = themes[localStorage.getItem('themeIndex') || 0];
|
||||
if (theme.value !== 'default') {
|
||||
document.querySelectorAll('link[title]').forEach(item => {
|
||||
item.disabled = true;
|
||||
});
|
||||
document.querySelector(`link[title=${theme.value}]`).disabled = false;
|
||||
if (theme.value === 'dark') {
|
||||
document.querySelector('body').classList.add('dark');
|
||||
}
|
||||
}
|
||||
amis.require(['./mobile.tsx'], function (app) {
|
||||
var initialState = {};
|
||||
app.bootstrap(document.getElementById('root'), initialState);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
90
examples/mobile.tsx
Normal file
90
examples/mobile.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @file 用于浏览移动端下的效果,通过 postMessage 来被父容器控制
|
||||
*/
|
||||
|
||||
import './polyfills/index';
|
||||
|
||||
import React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import axios from 'axios';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {toast} from '../src/components/Toast';
|
||||
import '../src/locale/en';
|
||||
|
||||
import {render as renderAmis} from '../src/index';
|
||||
|
||||
class AMISComponent extends React.Component {
|
||||
state = {
|
||||
schema: null,
|
||||
props: {}
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
window.addEventListener('message', event => {
|
||||
const data = event.data;
|
||||
if (data && data.schema) {
|
||||
this.setState({schema: data.schema, props: data.props});
|
||||
}
|
||||
});
|
||||
window.parent.postMessage('amisReady', '*');
|
||||
}
|
||||
render() {
|
||||
return this.state.schema ? (
|
||||
<div>
|
||||
{renderAmis(this.state.schema, this.state.props, {
|
||||
fetcher: ({
|
||||
url, // 接口地址
|
||||
method, // 请求方法 get、post、put、delete
|
||||
data, // 请求数据
|
||||
responseType,
|
||||
config, // 其他配置
|
||||
headers // 请求头
|
||||
}: any) => {
|
||||
config = config || {};
|
||||
config.withCredentials = true;
|
||||
responseType && (config.responseType = responseType);
|
||||
|
||||
if (config.cancelExecutor) {
|
||||
config.cancelToken = new (axios as any).CancelToken(
|
||||
config.cancelExecutor
|
||||
);
|
||||
}
|
||||
|
||||
config.headers = headers || {};
|
||||
|
||||
if (method !== 'post' && method !== 'put' && method !== 'patch') {
|
||||
if (data) {
|
||||
config.params = data;
|
||||
}
|
||||
|
||||
return (axios as any)[method](url, config);
|
||||
} else 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.headers = config.headers || {};
|
||||
config.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
return (axios as any)[method](url, data, config);
|
||||
},
|
||||
isCancel: (value: any) => (axios as any).isCancel(value),
|
||||
copy: content => {
|
||||
copy(content);
|
||||
toast.success('内容已复制到粘贴板');
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
}
|
||||
|
||||
export function bootstrap(mountTo, initalState) {
|
||||
render(<AMISComponent />, mountTo);
|
||||
}
|
@ -928,3 +928,10 @@ a {
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mobile-frame {
|
||||
border: 5px solid #565656;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user