amis2/examples/embed.tsx

319 lines
8.9 KiB
TypeScript
Raw Permalink Normal View History

2019-08-05 11:11:08 +08:00
import './polyfills/index';
import React from 'react';
import {createRoot} from 'react-dom/client';
2019-08-05 11:11:08 +08:00
import axios from 'axios';
import {match} from 'path-to-regexp';
2019-08-05 11:11:08 +08:00
import copy from 'copy-to-clipboard';
2024-07-05 16:06:54 +08:00
import {normalizeLink, supportsMjs} from 'amis-core';
2019-08-05 11:11:08 +08:00
import qs from 'qs';
import {alert, confirm} from 'amis-ui/lib/components/Alert';
import {toast, default as ToastComponent} from 'amis-ui/lib/components/Toast';
import AlertComponent from 'amis-ui/lib/components/Alert';
import {render as renderAmis, makeTranslator} from 'amis-core';
import 'amis/lib/minimal';
2019-08-05 11:11:08 +08:00
2022-06-01 21:35:49 +08:00
import 'amis-ui/lib/locale/en-US';
import 'amis-ui/lib/locale/zh-CN';
import 'amis-ui/lib/locale/en-US';
import 'amis-ui/lib/locale/de-DE';
import 'amis-ui/lib/themes/cxd';
import 'amis-ui/lib/themes/ang';
import 'amis-ui/lib/themes/antd';
import 'amis-ui/lib/themes/dark';
2021-06-17 21:22:31 +08:00
import 'history';
import {attachmentAdpator, setGlobalOptions} from 'amis-core';
2024-07-05 16:06:54 +08:00
import {pdfUrlLoad} from './loadPdfjsWorker';
2022-06-01 21:35:49 +08:00
import type {ToastLevel, ToastConf} from 'amis-ui/lib/components/Toast';
setGlobalOptions({
pdfjsWorkerSrc: supportsMjs() ? pdfUrlLoad() : ''
});
2019-11-07 10:41:14 +08:00
export function embed(
container: string | HTMLElement,
schema: any,
props: any = {},
env?: any,
callback?: () => void
2019-11-07 10:41:14 +08:00
) {
const __ = makeTranslator(env?.locale || props?.locale);
2019-11-07 10:41:14 +08:00
if (typeof container === 'string') {
container = document.querySelector(container) as HTMLElement;
}
if (!container) {
console.error(__('Embed.invalidRoot'));
2019-11-07 10:41:14 +08:00
return;
} else if (container.tagName === 'BODY') {
let div = document.createElement('div');
container.appendChild(div);
container = div;
}
container.classList.add('amis-scope');
let scoped = {};
2019-11-07 10:41:14 +08:00
const requestAdaptor = async (config: any) => {
const fn =
env && typeof env.requestAdaptor === 'function'
? env.requestAdaptor.bind()
: async (config: any) => config;
const request = (await fn(config)) || config;
return request;
};
const responseAdaptor = (api: any) => (response: any) => {
let payload = response.data || {}; // blob 下可能会返回内容为空?
// 之前拼写错了,需要兼容
if (env && env.responseAdpater) {
env.responseAdaptor = env.responseAdpater;
}
if (env && env.responseAdaptor) {
2019-11-07 10:41:14 +08:00
const url = api.url;
const idx = url.indexOf('?');
const query = ~idx ? qs.parse(url.substring(idx)) : {};
2019-11-07 10:41:14 +08:00
const request = {
...api,
query: query,
body: api.data
};
payload = env.responseAdaptor(api, payload, query, request, response);
2019-11-07 10:41:14 +08:00
} else {
if (payload.hasOwnProperty('errno')) {
payload.status = payload.errno;
payload.msg = payload.errmsg;
} else if (payload.hasOwnProperty('no')) {
payload.status = payload.no;
payload.msg = payload.error;
2019-11-07 10:41:14 +08:00
}
2019-08-05 11:11:08 +08:00
}
2019-11-07 10:41:14 +08:00
const result = {
...response,
data: payload
2019-11-07 10:41:14 +08:00
};
return result;
};
2021-06-17 01:06:34 +08:00
const amisEnv = {
getModalContainer: () =>
env?.getModalContainer?.() || document.querySelector('.amis-scope'),
notify: (type: ToastLevel, msg: string, conf?: ToastConf) =>
toast[type]
? toast[type](msg, conf)
: console.warn('[Notify]', type, msg),
2021-06-17 01:06:34 +08:00
alert,
confirm,
updateLocation: (to: any, replace: boolean) => {
if (to === 'goBack') {
return window.history.back();
}
2019-08-05 11:11:08 +08:00
2021-06-17 01:06:34 +08:00
if (replace && window.history.replaceState) {
window.history.replaceState('', document.title, to);
return;
}
2020-12-07 14:56:16 +08:00
2021-06-17 01:06:34 +08:00
location.href = normalizeLink(to);
},
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);
}
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
if (search) {
if (pathname !== location.pathname || !location.search) {
return false;
}
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
const query = qs.parse(search.substring(1));
const currentQuery = qs.parse(location.search.substring(1));
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
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);
}
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
return false;
},
jumpTo: (to: string, action?: any) => {
if (to === 'goBack') {
return window.history.back();
}
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
to = normalizeLink(to);
2019-08-05 11:11:08 +08:00
2021-06-17 01:06:34 +08:00
if (action && action.actionType === 'url') {
action.blank === false ? (window.location.href = to) : window.open(to);
return;
}
2019-11-07 10:41:14 +08:00
// 主要是支持 nav 中的跳转
if (action && to && action.target) {
window.open(to, action.target);
return;
}
2021-06-17 01:06:34 +08:00
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.url = url;
2021-06-17 01:06:34 +08:00
config.withCredentials = true;
responseType && (config.responseType = responseType);
2019-11-07 10:41:14 +08:00
2021-06-17 01:06:34 +08:00
if (config.cancelExecutor) {
config.cancelToken = new (axios as any).CancelToken(
config.cancelExecutor
);
}
config.headers = headers || {};
config.method = method;
config.data = data;
config = await requestAdaptor(config);
2021-06-17 01:06:34 +08:00
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';
}
// 支持返回各种报错信息
2021-10-13 11:41:43 +08:00
config.validateStatus = function () {
2021-06-17 01:06:34 +08:00
return true;
};
let response = config.mockResponse
? config.mockResponse
: await axios(config);
response = await attachmentAdpator(response, __, api);
2021-06-17 01:06:34 +08:00
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(
__('System.requestError') + JSON.stringify(response.data, null, 2)
2021-06-17 01:06:34 +08:00
);
}
} else {
throw new Error(
`${__('System.requestErrorStatus')} ${response.status}`
);
2019-11-07 10:41:14 +08:00
}
2021-06-17 01:06:34 +08:00
}
return response;
},
isCancel: (value: any) => (axios as any).isCancel(value),
copy: (contents: string, options: any = {}) => {
2021-10-13 11:41:43 +08:00
const ret = copy(contents);
ret && options.silent !== true && toast.info(__('System.copy'));
2021-06-17 01:06:34 +08:00
return ret;
},
richTextToken: '',
affixOffsetBottom: 0,
customStyleClassPrefix: '.amis-scope',
2021-06-17 01:06:34 +08:00
...env
};
let amisProps: any = {};
function createElements(props: any): any {
amisProps = {
...amisProps,
...props,
scopeRef: (ref: any) => {
if (ref) {
Object.keys(ref).forEach(key => {
let value = ref[key];
if (typeof value === 'function') {
value = value.bind(ref);
}
(scoped as any)[key] = value;
});
callback?.();
}
}
2021-06-17 01:06:34 +08:00
};
return (
<div className="amis-routes-wrapper">
<ToastComponent
position={(env && env.toastPosition) || 'top-center'}
2021-06-17 01:06:34 +08:00
closeButton={false}
timeout={5000}
2021-07-01 00:00:08 +08:00
locale={props?.locale}
2021-06-17 01:06:34 +08:00
theme={env?.theme}
/>
<AlertComponent
2021-07-01 00:00:08 +08:00
locale={props?.locale}
2021-06-17 01:06:34 +08:00
theme={env?.theme}
container={() => env?.getModalContainer?.() || container}
/>
{renderAmis(schema, amisProps, amisEnv)}
</div>
);
}
const root = createRoot(container);
root.render(createElements(props));
2021-06-17 01:06:34 +08:00
return Object.assign(scoped, {
2021-06-17 01:06:34 +08:00
updateProps: (props: any, callback?: () => void) => {
root.render(createElements(props));
2021-06-17 01:06:34 +08:00
},
updateSchema: (newSchema: any, props = {}) => {
schema = newSchema;
root.render(createElements(props));
},
2021-06-17 01:06:34 +08:00
unmount: () => {
root.unmount();
2021-06-17 01:06:34 +08:00
}
});
2019-08-05 11:11:08 +08:00
}