UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

471 lines (470 loc) 19.8 kB
"use strict"; 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