mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:58:07 +08:00
Merge pull request #2104 from 2betop/my-1.1.x
jssdk 支持 hash路由改造 to 1.1.x
This commit is contained in:
commit
7292c9f400
@ -83,6 +83,18 @@ SDK 版本适合对前端或 React 不了解的开发者,它不依赖 npm 及
|
|||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 更新属性
|
||||||
|
|
||||||
|
可以通过 amisScoped 对象的 updateProps 方法来更新下发到 amis 的属性。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
amisScoped.updateProps(
|
||||||
|
{
|
||||||
|
// 新的属性对象
|
||||||
|
} /*, () => {} 更新回调 */
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
### 切换主题
|
### 切换主题
|
||||||
|
|
||||||
jssdk 版本默认使用 `sdk.css` 即默认主题,如果你想用使用云舍,请改成引用 `cxd.css`。同时 js 渲染地方第四个参数传入 `theme` 属性。如:
|
jssdk 版本默认使用 `sdk.css` 即默认主题,如果你想用使用云舍,请改成引用 `cxd.css`。同时 js 渲染地方第四个参数传入 `theme` 属性。如:
|
||||||
@ -100,6 +112,11 @@ amis.embed(
|
|||||||
theme: 'cxd'
|
theme: 'cxd'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 或者
|
||||||
|
amisScoped.updateProps({
|
||||||
|
theme: 'cxd'
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
暗黑主题同理,改成引用 'dark.css' 同时主题设置成 `dark`。
|
暗黑主题同理,改成引用 'dark.css' 同时主题设置成 `dark`。
|
||||||
@ -179,27 +196,6 @@ let amisScoped = amis.embed(
|
|||||||
|
|
||||||
还可以通过 `amisScoped.getComponentByName('page1.form1').setValues({'name1': 'othername'})` 来修改表单中的值。
|
还可以通过 `amisScoped.getComponentByName('page1.form1').setValues({'name1': 'othername'})` 来修改表单中的值。
|
||||||
|
|
||||||
### 切换主题
|
|
||||||
|
|
||||||
jssdk 版本默认使用 `sdk.css` 即默认主题,如果你想用使用云舍,请改成引用 `cxd.css`。同时 js 渲染地方第四个参数传入 `theme` 属性。如:
|
|
||||||
|
|
||||||
```js
|
|
||||||
amis.embed(
|
|
||||||
'#root',
|
|
||||||
{
|
|
||||||
// amis schema
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 默认数据
|
|
||||||
},
|
|
||||||
{
|
|
||||||
theme: 'cxd'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
暗黑主题同理,改成引用 'dark.css' 同时主题设置成 `dark`。
|
|
||||||
|
|
||||||
### 多页模式
|
### 多页模式
|
||||||
|
|
||||||
默认 amis 渲染是单页模式,如果想实现多页应用,请使用 [app 渲染器](../../components/app)。
|
默认 amis 渲染是单页模式,如果想实现多页应用,请使用 [app 渲染器](../../components/app)。
|
||||||
@ -210,6 +206,14 @@ amis.embed(
|
|||||||
|
|
||||||
参考:https://github.com/baidu/amis/blob/master/examples/components/Example.tsx#L551-L575
|
参考:https://github.com/baidu/amis/blob/master/examples/components/Example.tsx#L551-L575
|
||||||
|
|
||||||
|
### 销毁
|
||||||
|
|
||||||
|
如果是单页应用,在离开当前页面的时候通常需要销毁实例,可以通过 unmount 方法来完成。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
amisScoped.unmount();
|
||||||
|
```
|
||||||
|
|
||||||
## react
|
## react
|
||||||
|
|
||||||
初始项目请参考 <https://github.com/aisuda/amis-react-starter>。
|
初始项目请参考 <https://github.com/aisuda/amis-react-starter>。
|
||||||
@ -290,8 +294,8 @@ import {toast} from 'amis/lib/components/Toast';
|
|||||||
|
|
||||||
class MyComponent extends React.Component<any, any> {
|
class MyComponent extends React.Component<any, any> {
|
||||||
render() {
|
render() {
|
||||||
let amisScoped;
|
let amisScoped;
|
||||||
let theme = 'default';
|
let theme = 'default';
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>通过 amis 渲染页面</p>
|
<p>通过 amis 渲染页面</p>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import './polyfills/index';
|
import './polyfills/index';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {render as renderReact} from 'react-dom';
|
import {render as renderReact, unmountComponentAtNode} from 'react-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import {match} from 'path-to-regexp';
|
import {match} from 'path-to-regexp';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
@ -46,11 +46,15 @@ export function embed(
|
|||||||
) {
|
) {
|
||||||
const disposition = response.headers['content-disposition'];
|
const disposition = response.headers['content-disposition'];
|
||||||
let filename = '';
|
let filename = '';
|
||||||
|
|
||||||
if (disposition && disposition.indexOf('attachment') !== -1) {
|
if (disposition && disposition.indexOf('attachment') !== -1) {
|
||||||
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i;
|
// disposition 有可能是 attachment; filename="??.xlsx"; filename*=UTF-8''%E4%B8%AD%E6%96%87.xlsx
|
||||||
let matches = filenameRegex.exec(disposition);
|
// 这种情况下最后一个才是正确的文件名
|
||||||
if (matches != null && matches[1]) {
|
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)$/;
|
||||||
filename = matches[1].replace(/['"]/g, '');
|
|
||||||
|
let matches = disposition.match(filenameRegex);
|
||||||
|
if (matches && matches.length) {
|
||||||
|
filename = matches[1].replace(`UTF-8''`, '').replace(/['"]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 很可能是中文被 url-encode 了
|
// 很可能是中文被 url-encode 了
|
||||||
@ -123,7 +127,7 @@ export function embed(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const responseAdaptor = (api: any) => (value: any) => {
|
const responseAdaptor = (api: any) => (value: any) => {
|
||||||
let response = value.data;
|
let response = value.data || {}; // blob 下可能会返回内容为空?
|
||||||
// 之前拼写错了,需要兼容
|
// 之前拼写错了,需要兼容
|
||||||
if (env && env.responseAdpater) {
|
if (env && env.responseAdpater) {
|
||||||
env.responseAdaptor = env.responseAdpater;
|
env.responseAdaptor = env.responseAdpater;
|
||||||
@ -155,178 +159,190 @@ export function embed(
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderReact(
|
const amisEnv = {
|
||||||
<div className="amis-routes-wrapper">
|
getModalContainer: () =>
|
||||||
<ToastComponent
|
env?.getModalContainer?.() || document.querySelector('.amis-scope'),
|
||||||
position={(env && env.toastPosition) || 'top-right'}
|
notify: (type: string, msg: string) =>
|
||||||
closeButton={false}
|
toast[type]
|
||||||
timeout={5000}
|
? toast[type](msg, type === 'error' ? '系统错误' : '系统消息')
|
||||||
theme={env?.theme}
|
: console.warn('[Notify]', type, msg),
|
||||||
/>
|
alert,
|
||||||
<AlertComponent
|
confirm,
|
||||||
theme={env?.theme}
|
updateLocation: (to: any, replace: boolean) => {
|
||||||
container={() => env?.getModalContainer?.() || container}
|
if (to === 'goBack') {
|
||||||
/>
|
return window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
{renderAmis(
|
if (replace && window.history.replaceState) {
|
||||||
schema,
|
window.history.replaceState('', document.title, to);
|
||||||
{
|
return;
|
||||||
...props,
|
}
|
||||||
scopeRef: (ref: any) => (scoped = ref)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
getModalContainer: () =>
|
|
||||||
env?.getModalContainer?.() || document.querySelector('.amis-scope'),
|
|
||||||
notify: (type: string, msg: string) =>
|
|
||||||
toast[type]
|
|
||||||
? toast[type](msg, type === 'error' ? '系统错误' : '系统消息')
|
|
||||||
: console.warn('[Notify]', type, msg),
|
|
||||||
alert,
|
|
||||||
confirm,
|
|
||||||
updateLocation: (to: any, replace: boolean) => {
|
|
||||||
if (to === 'goBack') {
|
|
||||||
return window.history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replace && window.history.replaceState) {
|
location.href = normalizeLink(to);
|
||||||
window.history.replaceState('', document.title, to);
|
},
|
||||||
return;
|
isCurrentUrl: (to: string, ctx?: any) => {
|
||||||
}
|
const link = normalizeLink(to);
|
||||||
|
const location = window.location;
|
||||||
|
let pathname = link;
|
||||||
|
let search = '';
|
||||||
|
const idx = link.indexOf('?');
|
||||||
|
if (~idx) {
|
||||||
|
pathname = link.substring(0, idx);
|
||||||
|
search = link.substring(idx);
|
||||||
|
}
|
||||||
|
|
||||||
location.href = normalizeLink(to);
|
if (search) {
|
||||||
},
|
if (pathname !== location.pathname || !location.search) {
|
||||||
isCurrentUrl: (to: string, ctx?: any) => {
|
return false;
|
||||||
const link = normalizeLink(to);
|
|
||||||
const location = window.location;
|
|
||||||
let pathname = link;
|
|
||||||
let search = '';
|
|
||||||
const idx = link.indexOf('?');
|
|
||||||
if (~idx) {
|
|
||||||
pathname = link.substring(0, idx);
|
|
||||||
search = link.substring(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
if (pathname !== location.pathname || !location.search) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = qs.parse(search.substring(1));
|
|
||||||
const currentQuery = qs.parse(location.search.substring(1));
|
|
||||||
|
|
||||||
return Object.keys(query).every(
|
|
||||||
key => query[key] === currentQuery[key]
|
|
||||||
);
|
|
||||||
} else if (pathname === location.pathname) {
|
|
||||||
return true;
|
|
||||||
} else if (!~pathname.indexOf('http') && ~pathname.indexOf(':')) {
|
|
||||||
return match(link, {
|
|
||||||
decode: decodeURIComponent,
|
|
||||||
strict: ctx?.strict ?? true
|
|
||||||
})(location.pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
jumpTo: (to: string, action?: any) => {
|
|
||||||
if (to === 'goBack') {
|
|
||||||
return window.history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
to = normalizeLink(to);
|
|
||||||
|
|
||||||
if (action && action.actionType === 'url') {
|
|
||||||
action.blank === false
|
|
||||||
? (window.location.href = to)
|
|
||||||
: window.open(to);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^https?:\/\//.test(to)) {
|
|
||||||
window.location.replace(to);
|
|
||||||
} else {
|
|
||||||
location.href = to;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fetcher: async (api: any) => {
|
|
||||||
let {url, method, data, responseType, config, headers} = api;
|
|
||||||
config = config || {};
|
|
||||||
config.withCredentials = true;
|
|
||||||
responseType && (config.responseType = responseType);
|
|
||||||
|
|
||||||
if (config.cancelExecutor) {
|
|
||||||
config.cancelToken = new (axios as any).CancelToken(
|
|
||||||
config.cancelExecutor
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.headers = headers || {};
|
|
||||||
config.method = method;
|
|
||||||
|
|
||||||
if (method === 'get' && data) {
|
|
||||||
config.params = data;
|
|
||||||
} else if (data && data instanceof FormData) {
|
|
||||||
// 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['Content-Type'] = 'application/json';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 支持返回各种报错信息
|
|
||||||
config.validateStatus = function (status) {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
data && (config.data = data);
|
|
||||||
let response = await axios(url, config);
|
|
||||||
response = attachmentAdpator(response);
|
|
||||||
response = responseAdaptor(api)(response);
|
|
||||||
|
|
||||||
if (response.status >= 400) {
|
|
||||||
if (response.data) {
|
|
||||||
// 主要用于 raw: 模式下,后端自己校验登录,
|
|
||||||
if (
|
|
||||||
response.status === 401 &&
|
|
||||||
response.data.location &&
|
|
||||||
response.data.location.startsWith('http')
|
|
||||||
) {
|
|
||||||
location.href = response.data.location.replace(
|
|
||||||
'{{redirect}}',
|
|
||||||
encodeURIComponent(location.href)
|
|
||||||
);
|
|
||||||
return new Promise(() => {});
|
|
||||||
} else if (response.data.msg) {
|
|
||||||
throw new Error(response.data.msg);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'接口报错:' + JSON.stringify(response.data, null, 2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(`接口出错,状态码是 ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
isCancel: (value: any) => (axios as any).isCancel(value),
|
|
||||||
copy: (contents: string, options: any = {}) => {
|
|
||||||
const ret = copy(contents, options);
|
|
||||||
ret && options.shutup !== true && toast.info('内容已拷贝到剪切板');
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
richTextToken: '',
|
|
||||||
affixOffsetBottom: 0,
|
|
||||||
...env
|
|
||||||
}
|
}
|
||||||
)}
|
|
||||||
</div>,
|
const query = qs.parse(search.substring(1));
|
||||||
container
|
const currentQuery = qs.parse(location.search.substring(1));
|
||||||
);
|
|
||||||
return scoped;
|
return Object.keys(query).every(
|
||||||
|
key => query[key] === currentQuery[key]
|
||||||
|
);
|
||||||
|
} else if (pathname === location.pathname) {
|
||||||
|
return true;
|
||||||
|
} else if (!~pathname.indexOf('http') && ~pathname.indexOf(':')) {
|
||||||
|
return match(link, {
|
||||||
|
decode: decodeURIComponent,
|
||||||
|
strict: ctx?.strict ?? true
|
||||||
|
})(location.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
jumpTo: (to: string, action?: any) => {
|
||||||
|
if (to === 'goBack') {
|
||||||
|
return window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
to = normalizeLink(to);
|
||||||
|
|
||||||
|
if (action && action.actionType === 'url') {
|
||||||
|
action.blank === false ? (window.location.href = to) : window.open(to);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^https?:\/\//.test(to)) {
|
||||||
|
window.location.replace(to);
|
||||||
|
} else {
|
||||||
|
location.href = to;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetcher: async (api: any) => {
|
||||||
|
let {url, method, data, responseType, config, headers} = api;
|
||||||
|
config = config || {};
|
||||||
|
config.withCredentials = true;
|
||||||
|
responseType && (config.responseType = responseType);
|
||||||
|
|
||||||
|
if (config.cancelExecutor) {
|
||||||
|
config.cancelToken = new (axios as any).CancelToken(
|
||||||
|
config.cancelExecutor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.headers = headers || {};
|
||||||
|
config.method = method;
|
||||||
|
|
||||||
|
if (method === 'get' && data) {
|
||||||
|
config.params = data;
|
||||||
|
} else if (data && data instanceof FormData) {
|
||||||
|
// 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['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持返回各种报错信息
|
||||||
|
config.validateStatus = function (status) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
data && (config.data = data);
|
||||||
|
let response = await axios(url, config);
|
||||||
|
response = await attachmentAdpator(response);
|
||||||
|
response = responseAdaptor(api)(response);
|
||||||
|
|
||||||
|
if (response.status >= 400) {
|
||||||
|
if (response.data) {
|
||||||
|
// 主要用于 raw: 模式下,后端自己校验登录,
|
||||||
|
if (
|
||||||
|
response.status === 401 &&
|
||||||
|
response.data.location &&
|
||||||
|
response.data.location.startsWith('http')
|
||||||
|
) {
|
||||||
|
location.href = response.data.location.replace(
|
||||||
|
'{{redirect}}',
|
||||||
|
encodeURIComponent(location.href)
|
||||||
|
);
|
||||||
|
return new Promise(() => {});
|
||||||
|
} else if (response.data.msg) {
|
||||||
|
throw new Error(response.data.msg);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'接口报错:' + JSON.stringify(response.data, null, 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`接口出错,状态码是 ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
isCancel: (value: any) => (axios as any).isCancel(value),
|
||||||
|
copy: (contents: string, options: any = {}) => {
|
||||||
|
const ret = copy(contents, options);
|
||||||
|
ret && options.shutup !== true && toast.info('内容已拷贝到剪切板');
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
richTextToken: '',
|
||||||
|
affixOffsetBottom: 0,
|
||||||
|
...env
|
||||||
|
};
|
||||||
|
|
||||||
|
let amisProps: any = {};
|
||||||
|
function createElements(props: any): any {
|
||||||
|
amisProps = {
|
||||||
|
...amisProps,
|
||||||
|
...props,
|
||||||
|
scopeRef: (ref: any) => (scoped = ref)
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="amis-routes-wrapper">
|
||||||
|
<ToastComponent
|
||||||
|
position={(env && env.toastPosition) || 'top-right'}
|
||||||
|
closeButton={false}
|
||||||
|
timeout={5000}
|
||||||
|
theme={env?.theme}
|
||||||
|
/>
|
||||||
|
<AlertComponent
|
||||||
|
theme={env?.theme}
|
||||||
|
container={() => env?.getModalContainer?.() || container}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{renderAmis(schema, amisProps, amisEnv)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderReact(createElements(props), container);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...scoped,
|
||||||
|
updateProps: (props: any, callback?: () => void) => {
|
||||||
|
renderReact(createElements(props), container as HTMLElement, callback);
|
||||||
|
},
|
||||||
|
unmount: () => {
|
||||||
|
unmountComponentAtNode(container as HTMLElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,11 @@ export const AppStore = ServiceStore.named('AppStore')
|
|||||||
},
|
},
|
||||||
|
|
||||||
setActivePage(page: any, env: RendererEnv, params?: any) {
|
setActivePage(page: any, env: RendererEnv, params?: any) {
|
||||||
|
// 同一个页面直接返回。
|
||||||
|
if (self.activePage?.id === page.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let bcn: Array<any> = [];
|
let bcn: Array<any> = [];
|
||||||
|
|
||||||
findTree(self.pages, (item, index, level, paths) => {
|
findTree(self.pages, (item, index, level, paths) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user