fastlion-amis
Version:
一种MIS页面生成工具
471 lines (470 loc) • 19.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.RendererEnv = exports.getRendererByName = exports.getRenderers = exports.resolveRenderer = exports.updateEnv = exports.clearStoresCache = exports.render = exports.loadRenderer = exports.unRegisterRenderer = exports.registerRenderer = exports.Renderer = exports.filterSchema = exports.addSchemaFilter = void 0;
var tslib_1 = require("tslib");
var index_1 = require("./store/index");
var mobx_state_tree_1 = require("mobx-state-tree");
var api_1 = require("./utils/api");
var normalizeLink_1 = require("./utils/normalizeLink");
var mobx_react_1 = require("mobx-react");
var react_1 = (0, tslib_1.__importDefault)(require("react"));
var helper_1 = require("./utils/helper");
var envOverwrite_1 = require("./envOverwrite");
var Action_1 = require("./actions/Action");
var Root_1 = (0, tslib_1.__importDefault)(require("./Root"));
var WithStore_1 = require("./WithStore");
var env_1 = require("./env");
Object.defineProperty(exports, "RendererEnv", { enumerable: true, get: function () { return env_1.RendererEnv; } });
var renderer_event_1 = require("./utils/renderer-event");
var Scoped_1 = (0, tslib_1.__importDefault)(require("./Scoped"));
var theme_1 = require("./theme");
var find_1 = (0, tslib_1.__importDefault)(require("lodash/find"));
var Alert2_1 = (0, tslib_1.__importDefault)(require("./components/Alert2"));
var Toast_1 = require("./components/Toast");
var Alert_1 = require("./components/Alert");
var locale_1 = require("./locale");
var renderers = [];
var rendererNames = [];
var schemaFilters = [];
var anonymousIndex = 1;
function addSchemaFilter(fn) {
schemaFilters.push(fn);
}
exports.addSchemaFilter = addSchemaFilter;
function filterSchema(schema, render, props) {
return schemaFilters.reduce(function (schema, filter) { return filter(schema, render, props); }, schema);
}
exports.filterSchema = filterSchema;
function Renderer(config) {
return function (component) {
var renderer = registerRenderer((0, tslib_1.__assign)((0, tslib_1.__assign)({}, config), { component: component }));
return renderer.component;
};
}
exports.Renderer = Renderer;
function registerRenderer(config) {
if (!config.test && !config.type) {
throw new TypeError('please set config.test or config.type');
}
else if (!config.component) {
throw new TypeError('config.component is required');
}
if (typeof config.type === 'string' && config.type) {
config.type = config.type.toLowerCase();
config.test =
config.test || new RegExp("(^|/)" + (0, helper_1.string2regExp)(config.type) + "$", 'i');
}
config.weight = config.weight || 0;
config.Renderer = config.component;
config.name = config.name || config.type || "anonymous-" + anonymousIndex++;
if (~rendererNames.indexOf(config.name)) {
throw new Error("The renderer with name \"" + config.name + "\" has already exists, please try another name!");
}
if (config.storeType && config.component) {
config.component = (0, WithStore_1.HocStoreFactory)({
storeType: config.storeType,
extendsData: config.storeExtendsData,
shouldSyncSuperStore: config.shouldSyncSuperStore
})((0, mobx_react_1.observer)(config.component));
}
if (config.isolateScope) {
config.component = (0, Scoped_1.default)(config.component);
}
var idx = (0, helper_1.findIndex)(renderers, function (item) { return config.weight < item.weight; });
~idx ? renderers.splice(idx, 0, config) : renderers.push(config);
rendererNames.push(config.name);
return config;
}
exports.registerRenderer = registerRenderer;
function unRegisterRenderer(config) {
var idx = typeof config === 'string'
? (0, helper_1.findIndex)(renderers, function (item) { return item.name === config; })
: renderers.indexOf(config);
~idx && renderers.splice(idx, 1);
var idx2 = typeof config === 'string'
? (0, helper_1.findIndex)(rendererNames, function (item) { return item === config; })
: rendererNames.indexOf(config.name || '');
~idx2 && rendererNames.splice(idx2, 1);
// 清空渲染器定位缓存
cache = {};
}
exports.unRegisterRenderer = unRegisterRenderer;
function loadRenderer(schema, path) {
return (react_1.default.createElement(Alert2_1.default, { level: "danger" },
react_1.default.createElement("p", null, "Error: \u627E\u4E0D\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u5668"),
react_1.default.createElement("p", null,
"Path: ",
path),
react_1.default.createElement("pre", null,
react_1.default.createElement("code", null, JSON.stringify(schema, null, 2)))));
}
exports.loadRenderer = loadRenderer;
var defaultOptions = {
session: 'global',
affixOffsetTop: 0,
affixOffsetBottom: 0,
richTextToken: '',
useMobileUI: true,
loadRenderer: loadRenderer,
rendererEventListeners: [],
fetcher: function () {
return Promise.reject('fetcher is required');
},
// 使用 WebSocket 来实时获取数据
wsFetcher: function (ws, onMessage, onError) {
if (ws) {
var socket_1 = new WebSocket(ws.url);
socket_1.onopen = function (event) {
if (ws.body) {
socket_1.send(JSON.stringify(ws.body));
}
};
socket_1.onmessage = function (event) {
if (event.data) {
onMessage(JSON.parse(event.data));
}
};
socket_1.onerror = onError;
return {
close: socket_1.close
};
}
else {
return {
close: function () { }
};
}
},
isCancel: function () {
console.error('Please implement isCancel. see https://baidu.gitee.io/amis/docs/start/getting-started#%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97');
return false;
},
updateLocation: function () {
console.error('Please implement updateLocation. see https://baidu.gitee.io/amis/docs/start/getting-started#%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97');
},
alert: Alert_1.alert,
confirm: Alert_1.confirm,
prompt: Alert_1.prompt,
notify: function (type, msg, conf) {
return Toast_1.toast[type] ? Toast_1.toast[type](msg, conf) : console.warn('[Notify]', type, msg);
},
jumpTo: function (to, action) {
if (to === 'goBack') {
return window.history.back();
}
to = (0, normalizeLink_1.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;
}
},
isCurrentUrl: function (to) {
if (!to) {
return false;
}
var link = (0, normalizeLink_1.normalizeLink)(to);
var location = window.location;
var pathname = link;
var search = '';
var idx = link.indexOf('?');
if (~idx) {
pathname = link.substring(0, idx);
search = link.substring(idx);
}
if (search) {
if (pathname !== location.pathname || !location.search) {
return false;
}
var query_1 = (0, helper_1.qsparse)(search.substring(1));
var currentQuery_1 = (0, helper_1.qsparse)(location.search.substring(1));
return Object.keys(query_1).every(function (key) { return query_1[key] === currentQuery_1[key]; });
}
else if (pathname === location.pathname) {
return true;
}
return false;
},
copy: function (contents) {
console.error('copy contents', contents);
},
// 用于跟踪用户在界面中的各种操作
tracker: function (eventTrack, props) { },
// 返回解绑函数
bindEvent: function (renderer) {
var _this = this;
if (!renderer) {
return undefined;
}
var listeners = renderer.props.$schema.onEvent;
if (listeners) {
// 暂存
for (var _i = 0, _a = Object.keys(listeners); _i < _a.length; _i++) {
var key = _a[_i];
this.rendererEventListeners.push({
renderer: renderer,
type: key,
weight: listeners[key].weight || 0,
actions: listeners[key].actions
});
}
return function () {
_this.rendererEventListeners = _this.rendererEventListeners.filter(function (item) { return item.renderer === renderer; });
};
}
return undefined;
},
dispatchEvent: function (e, renderer, scoped, data, broadcast) {
var _a, _b;
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var eventName, eventConfig, rendererEvent, listeners, _i, listeners_1, listener;
return (0, tslib_1.__generator)(this, function (_c) {
switch (_c.label) {
case 0:
eventName = typeof e === 'string' ? e : e.type;
if (!broadcast) {
eventConfig = (_b = (_a = renderer === null || renderer === void 0 ? void 0 : renderer.props) === null || _a === void 0 ? void 0 : _a.onEvent) === null || _b === void 0 ? void 0 : _b[eventName];
if (!eventConfig) {
// 没命中也没关系
return [2 /*return*/, Promise.resolve(undefined)];
}
}
// 没有可处理的监听
if (!this.rendererEventListeners.length) {
return [2 /*return*/, Promise.resolve()];
}
rendererEvent = broadcast ||
(0, renderer_event_1.createRendererEvent)(eventName, {
env: this,
nativeEvent: e,
data: data,
scoped: scoped
});
listeners = this.rendererEventListeners
.filter(function (item) {
return item.type === eventName &&
(broadcast ? true : item.renderer === renderer);
})
.sort(function (prev, next) {
return next.weight - prev.weight;
});
_i = 0, listeners_1 = listeners;
_c.label = 1;
case 1:
if (!(_i < listeners_1.length)) return [3 /*break*/, 4];
listener = listeners_1[_i];
return [4 /*yield*/, (0, Action_1.runActions)(listener.actions, listener.renderer, rendererEvent)];
case 2:
_c.sent();
// 停止后续监听器执行
if (rendererEvent.stoped) {
return [3 /*break*/, 4];
}
_c.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, rendererEvent];
}
});
});
},
rendererResolver: resolveRenderer,
replaceTextIgnoreKeys: [
'type',
'name',
'mode',
'target',
'reload',
'persistData'
],
/**
* 过滤 html 标签,可用来添加 xss 保护逻辑
*/
filterHtml: function (input) { return input; }
};
var stores = {};
function render(schema, props, options, pathPrefix) {
if (props === void 0) { props = {}; }
if (options === void 0) { options = {}; }
if (pathPrefix === void 0) { pathPrefix = ''; }
var locale = props.locale || (0, locale_1.getDefaultLocale)();
// 兼容 locale 的不同写法
locale = locale.replace('_', '-');
locale = locale === 'en' ? 'en-US' : locale;
locale = locale === 'zh' ? 'zh-CN' : locale;
locale = locale === 'cn' ? 'zh-CN' : locale;
var translate = props.translate || (0, locale_1.makeTranslator)(locale);
var store = stores[options.session || 'global'];
// 根据环境覆盖 schema,这个要在最前面做,不然就无法覆盖 validations
(0, envOverwrite_1.envOverwrite)(schema, locale);
if (!store) {
options = (0, tslib_1.__assign)((0, tslib_1.__assign)((0, tslib_1.__assign)({}, defaultOptions), options), { fetcher: options.fetcher
? (0, api_1.wrapFetcher)(options.fetcher, options.tracker)
: defaultOptions.fetcher, confirm: (0, helper_1.promisify)(options.confirm || defaultOptions.confirm || window.confirm), locale: locale, translate: translate });
store = index_1.RendererStore.create({}, options);
stores[options.session || 'global'] = store;
}
else {
// 走更新逻辑
updateEnv(options, options.session);
}
// (window as any).amisStore = store; // 为了方便 debug.
var env = (0, mobx_state_tree_1.getEnv)(store);
var theme = props.theme || options.theme || 'antd';
if (theme === 'default') {
theme = 'antd';
}
env.theme = (0, theme_1.getTheme)(theme);
if (props.locale !== undefined) {
env.translate = translate;
env.locale = locale;
}
// 默认将开启移动端原生 UI
if (typeof options.useMobileUI) {
props.useMobileUI = true;
}
// 进行文本替换
if (env.replaceText && (0, helper_1.isObject)(env.replaceText)) {
var replaceKeys_1 = Object.keys(env.replaceText);
replaceKeys_1.sort().reverse(); // 避免用户将短的放前面
var replaceTextIgnoreKeys_1 = new Set(env.replaceTextIgnoreKeys || []);
(0, helper_1.JSONTraverse)(schema, function (value, key, object) {
if (typeof value === 'string' && !replaceTextIgnoreKeys_1.has(key)) {
for (var _i = 0, replaceKeys_2 = replaceKeys_1; _i < replaceKeys_2.length; _i++) {
var replaceKey = replaceKeys_2[_i];
if (~value.indexOf(replaceKey)) {
object[key] = value.replaceAll(replaceKey, env.replaceText[replaceKey]);
}
}
}
});
}
// console.log(env, 'env')
return (react_1.default.createElement(env_1.EnvContext.Provider, { value: env },
react_1.default.createElement(Root_1.default, (0, tslib_1.__assign)({}, props, { schema: schema, pathPrefix: pathPrefix, rootStore: store, env: env, theme: theme, locale: locale, translate: translate }))));
}
exports.render = render;
// 默认 env 会被缓存,所以新传入的 env 不会替换旧的。
// 除非先删了旧的,新的才会生效。
function clearStoresCache(sessions) {
if (sessions === void 0) { sessions = Object.keys(stores); }
if (!Array.isArray(sessions)) {
sessions = [sessions];
}
sessions.forEach(function (key) {
var store = stores[key];
// @ts-ignore
delete stores[key];
store && (0, mobx_state_tree_1.destroy)(store);
});
}
exports.clearStoresCache = clearStoresCache;
// 当然也可以直接这样更新。
// 主要是有时候第一次创建的时候并没有准备多少接口,
// 可以后续补充点,比如 amis 自己实现的,prompt 里面的表单。
function updateEnv(options, session) {
if (session === void 0) { session = 'global'; }
options = (0, tslib_1.__assign)({}, options);
var store = stores[options.session || session];
if (!store.fetcher && options.fetcher) {
options.fetcher = (0, api_1.wrapFetcher)(options.fetcher, options.tracker);
}
else if (store.fetcher) {
options.fetcher = store.fetcher;
}
if (options.confirm) {
options.confirm = (0, helper_1.promisify)(options.confirm);
}
if (!store) {
store = index_1.RendererStore.create({}, options);
stores[options.session || session] = store;
}
else {
var env = (0, mobx_state_tree_1.getEnv)(store);
Object.assign(env, options);
}
}
exports.updateEnv = updateEnv;
var cache = {};
function resolveRenderer(path, schema) {
var type = typeof (schema === null || schema === void 0 ? void 0 : schema.type) == 'string' ? schema.type.toLowerCase() : '';
if (type && cache[type]) {
return cache[type];
}
else if (cache[path]) {
return cache[path];
}
else if (path && path.length > 1024) {
throw new Error('Path太长是不是死循环了?');
}
var renderer = null;
renderers.some(function (item) {
var matched = false;
// 直接匹配类型,后续注册渲染都应该用这个方式而不是之前的判断路径。
if (item.type && type) {
matched = item.type === type;
// 如果是type来命中的,那么cache的key直接用 type 即可。
if (matched) {
cache[type] = item;
}
}
else if (typeof item.test === 'function') {
// 不应该搞得这么复杂的,让每个渲染器唯一 id,自己不晕别人用起来也不晕。
matched = item.test(path, schema, resolveRenderer);
}
else if (item.test instanceof RegExp) {
matched = item.test.test(path);
}
if (matched) {
renderer = item;
}
return matched;
});
// 只能缓存纯正则表达式的后者方法中没有用到第二个参数的,
// 因为自定义 test 函数的有可能依赖 schema 的结果
if (renderer !== null &&
(renderer.type ||
renderer.test instanceof RegExp ||
(typeof renderer.test === 'function' &&
renderer.test.length < 2))) {
cache[path] = renderer;
}
return renderer;
}
exports.resolveRenderer = resolveRenderer;
function getRenderers() {
return renderers.concat();
}
exports.getRenderers = getRenderers;
function getRendererByName(name) {
return (0, find_1.default)(renderers, function (item) { return item.name === name; });
}
exports.getRendererByName = getRendererByName;
(0, Alert_1.setRenderSchemaFn)(function (controls, value, callback, scopeRef, theme, inModal, autoFocus) {
return render({
name: 'form',
type: 'form',
wrapWithPanel: false,
mode: 'horizontal',
controls: controls,
inModal: inModal,
messages: {
validateFailed: ''
},
autoFocus: autoFocus
}, {
data: value,
onFinished: callback,
scopeRef: scopeRef,
theme: theme
}, {
session: 'prompt'
});
});
//# sourceMappingURL=./factory.js.map
;