fineui是帆软报表和BI产品线所使用的前端框架。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

607 lines
17 KiB

import {
isFunction,
isNull,
isNotNull,
isArray,
each,
isWidget,
extend,
init,
isEmpty,
remove,
isString
} from "./2.base";
import { OB } from "./3.ob";
import { Widget } from "./4.widget";
import { Plugin } from "./6.plugin";
import { aspect } from "./structure";
import { Events } from "./constant";
import { _global } from "./0.foundation";
import { SystemProvider } from "./system";
import { loadResources } from "./platform";
const moduleInjection = {}, moduleInjectionMap = {
components: {},
constants: {},
stores: {},
services: {},
models: {},
providers: {},
};
export function module(xtype, cls) {
if (isNotNull(moduleInjection[xtype])) {
_global.console && console.error(`module: [${xtype}] already registered`);
} else {
if (isFunction(cls)) {
cls = cls();
}
for (const k in moduleInjectionMap) {
if (cls[k]) {
for (const key in cls[k]) {
if (!moduleInjectionMap[k]) {
continue;
}
if (!moduleInjectionMap[k][key]) {
moduleInjectionMap[k][key] = [];
}
moduleInjectionMap[k][key].push({
version: cls[k][key],
moduleId: xtype,
});
}
}
}
moduleInjection[xtype] = cls;
}
return () => Modules.getModule(xtype);
}
const constantInjection = {};
export function constant(xtype, cls) {
if (isNotNull(constantInjection[xtype])) {
_global.console && console.error(`constant: [${xtype}]already registered`);
} else {
constantInjection[xtype] = cls;
}
return () => Constants.getConstant(xtype);
}
const modelInjection = {};
export function model(xtype, cls) {
if (isNotNull(modelInjection[xtype])) {
_global.console && console.error(`model: [${xtype}] already registered`);
} else {
modelInjection[xtype] = cls;
}
return config => Models.getModel(xtype, config);
}
const storeInjection = {};
export function store(xtype, cls) {
if (isNotNull(storeInjection[xtype])) {
_global.console && console.error(`store: [${xtype}] already registered`);
} else {
storeInjection[xtype] = cls;
}
return config => Stores.getStore(xtype, config);
}
const serviceInjection = {};
export function service(xtype, cls) {
if ((serviceInjection[xtype])) {
_global.console && console.error(`service: [${xtype}] already registered`);
}
serviceInjection[xtype] = cls;
return config => Services.getService(xtype, config);
}
const providerInjection = {};
export function provider(xtype, cls) {
if ((providerInjection[xtype])) {
_global.console && console.error(`provider: [${xtype}] already registered`);
} else {
providerInjection[xtype] = cls;
}
return config => Providers.getProvider(xtype, config);
}
const configFunctions = OB.configFunctions = {};
const runConfigFunction = (type, configFn) => {
if (!type || !configFunctions[type]) {
return false;
}
let queue = [];
if (configFn) {
queue = configFunctions[type].filter(conf => conf.fn === configFn);
configFunctions[type] = configFunctions[type].filter(conf => conf.fn !== configFn);
} else {
queue = configFunctions[type];
delete configFunctions[type];
}
const dependencies = Providers.getProvider(SystemProvider.xtype).getDependencies();
const modules = moduleInjectionMap.components[type]
|| moduleInjectionMap.constants[type]
|| moduleInjectionMap.services[type]
|| moduleInjectionMap.stores[type]
|| moduleInjectionMap.models[type]
|| moduleInjectionMap.providers[type];
for (let i = 0; i < queue.length; i++) {
const conf = queue[i];
const version = conf.opt.version;
const fn = conf.fn;
if (modules && version) {
let findVersion = false;
let module;
for (let j = 0; j < modules.length; j++) {
module = modules[j];
if (module && dependencies[module.moduleId] && module.version === version) {
const minVersion = dependencies[module.moduleId].minVersion,
maxVersion = dependencies[module.moduleId].maxVersion;
if (minVersion && (moduleInjection[module.moduleId].version || version) < minVersion) {
findVersion = true;
break;
}
if (maxVersion && (moduleInjection[module.moduleId].version || version) > maxVersion) {
findVersion = true;
break;
}
}
}
if (findVersion === true) {
_global.console && console.error(`moduleId: [${module.moduleId}] interface: [${type}] version: [${version}] has expired. The version requirement is: `, dependencies[module.moduleId], "=>", moduleInjection[module.moduleId]);
continue;
}
}
if (constantInjection[type]) {
constantInjection[type] = fn(constantInjection[type]);
continue;
}
if (providerInjection[type]) {
if (!providers[type]) {
providers[type] = new providerInjection[type]();
}
if (providerInstance[type]) {
delete providerInstance[type];
}
fn(providers[type]);
continue;
}
Plugin.configWidget(type, fn, conf.opt);
}
};
/**
* 配置组件依赖
* @param deps
*/
function configWidgetDeps(deps) {
each(deps, (key, dep) => {
const deps = (isArray(dep) ? dep : [dep]).map(d => {
if (isString(d)) {
return {
src: d,
async: false
};
} else {
return d;
}
});
config(key, props => {
const asyncLoad = deps.some(dep => dep.async);
// 异步加载资源
if (asyncLoad && !props._depsLoaded) {
return {
type: Widget,
beforeInit: () => {
return loadResources(deps);
},
render: () => {
return {
...props,
_depsLoaded: true
};
},
};
}
// 同步加载资源
loadResources(deps);
return props;
});
});
}
export function config(type, configFn, opt) {
if (isFunction(type)) {
opt = configFn;
configFn = type;
type = SystemProvider.xtype;
}
opt = opt || {};
// 系统配置直接执行
if (SystemProvider.xtype === type) {
if (!providers[type]) {
providers[type] = new providerInjection[type]();
}
// 如果config被重新配置的话,需要删除掉之前的实例
if (providerInstance[type]) {
delete providerInstance[type];
}
return configFn(providers[type]);
}
if (!configFunctions[type]) {
configFunctions[type] = [];
}
configFunctions[type].push({
fn: configFn,
opt,
});
if (opt.deps) {
configWidgetDeps(opt.deps);
}
if (opt.immediately) {
return runConfigFunction(type, configFn);
}
}
export function getReference(type, fn) {
return Plugin.registerObject(type, fn);
}
const actions = {};
const globalAction = [];
export function action(type, actionFn) {
if (isFunction(type)) {
globalAction.push(type);
return () => {
remove(globalAction, idx => globalAction.indexOf(actionFn) === idx);
};
}
if (!actions[type]) {
actions[type] = [];
}
actions[type].push(actionFn);
return () => {
remove(actions[type], idx => actions[type].indexOf(actionFn) === idx);
if (actions[type].length === 0) {
delete actions[type];
}
};
}
const points = {};
export function point(type, action, pointFn, after) {
if (!points[type]) {
points[type] = {};
}
if (!points[type][action]) {
points[type][action] = {};
}
if (!points[type][action][after ? "after" : "before"]) {
points[type][action][after ? "after" : "before"] = [];
}
points[type][action][after ? "after" : "before"].push(pointFn);
}
export const Modules = {
getModule(type) {
if (!moduleInjection[type]) {
_global.console && console.error(`module: [${type}] undefined`);
}
return moduleInjection[type];
},
getAllModules() {
return moduleInjection;
},
};
export const Constants = {
getConstant(type) {
if (isNull(constantInjection[type])) {
_global.console && console.error(`constant: [${type}] undefined`);
}
runConfigFunction(type);
return isFunction(constantInjection[type]) ? constantInjection[type]() : constantInjection[type];
},
};
function callPoint(inst, types) {
types = isArray(types) ? types : [types];
each(types, (idx, type) => {
if (points[type]) {
for (const action in points[type]) {
const bfns = points[type][action].before;
if (bfns) {
aspect.before(inst, action, (function(bfns) {
return function() {
for (let i = 0, len = bfns.length; i < len; i++) {
try {
bfns[i].apply(inst, arguments);
} catch (e) {
_global.console && console.error(e);
}
}
};
}(bfns)));
}
const afns = points[type][action].after;
if (afns) {
aspect.after(inst, action, (function(afns) {
return function() {
for (let i = 0, len = afns.length; i < len; i++) {
try {
afns[i].apply(inst, arguments);
} catch (e) {
_global.console && console.error(e);
}
}
};
}(afns)));
}
}
}
});
}
export const Models = {
getModel(type, config) {
if (!modelInjection[type]) {
_global.console && console.error(`model: [${type}] undefined`);
}
runConfigFunction(type);
const inst = new modelInjection[type](config);
inst._constructor && inst._constructor(config);
inst.mixins && callPoint(inst, inst.mixins);
callPoint(inst, type);
return inst;
},
};
const stores = {};
export const Stores = {
getStore(type, config) {
if (!storeInjection[type]) {
_global.console && console.error(`store: [${type}] undefined`);
}
if (stores[type]) {
return stores[type];
}
const inst = stores[type] = new storeInjection[type](config);
inst._constructor && inst._constructor(config, () => {
delete stores[type];
});
callPoint(inst, type);
return inst;
},
};
const services = {};
export const Services = {
getService: (type, config) => {
if (!serviceInjection[type]) {
_global.console && console.error(`service: [${type}] undefined`);
}
if (services[type]) {
return services[type];
}
services[type] = new serviceInjection[type](config);
callPoint(services[type], type);
return services[type];
},
};
const providers = {},
providerInstance = {};
export const Providers = {
getProvider: (type, config) => {
if (!providerInjection[type]) {
_global.console && console.error(`provider: [${type}] undefined`);
}
runConfigFunction(type);
if (!providers[type]) {
providers[type] = new providerInjection[type]();
}
if (!providerInstance[type] && providers[type].$get) {
providerInstance[type] = new (providers[type].$get())(config);
}
return providerInstance[type];
},
};
export const Actions = {
runAction(type, event, config) {
each(actions[type], (i, act) => {
try {
act(event, config);
} catch (e) {
_global.console && console.error(e);
}
});
},
runGlobalAction() {
const args = [].slice.call(arguments);
each(globalAction, (i, act) => {
try {
act(...args);
} catch (e) {
_global.console && console.error(e);
}
});
},
};
const kv = {};
export function shortcut(xtype, cls) {
if (isNotNull(kv[xtype])) {
_global.console && console.error(`widget: [${xtype}] already registered`);
}
if (cls) {
cls.xtype = xtype;
}
// 兼容性
if (!cls.hasOwnProperty("superclass")) {
cls.superclass = Object.getPrototypeOf(cls.prototype);
}
kv[xtype] = cls;
}
export const component = shortcut;
// 根据配置属性生成widget
const createRealWidget = (config, context, lazy) => {
const Cls = isFunction(config.type) ? config.type : kv[config.type];
if (!Cls) {
throw new Error(`widget: [${config.type}] undefined`);
}
let pushed = false;
const widget = new Cls();
widget._context = Widget.context || context;
if (!Widget.context && context) {
pushed = true;
Widget.pushContext(context);
}
callPoint(widget, config.type);
widget._initProps(config);
widget._initRoot();
widget._constructed();
// if (!lazy || config.element || config.root) {
widget._lazyConstructor();
// }
pushed && Widget.popContext();
return widget;
};
export function createWidget(item, options, context, lazy) {
item || (item = {});
if (isWidget(options)) {
context = options;
options = {};
} else {
options || (options = {});
}
let el, w;
if (item.type || options.type) {
el = extend({}, options, item);
} else if (item.el && (item.el.type || options.type)) {
el = extend({}, options, item.el);
}
let elType;
if (el) {
elType = (el.type && el.type.xtype) || el.type;
runConfigFunction(elType);
}
// 先把准备环境准备好
init();
if (isEmpty(item) && isEmpty(options)) {
return createWidget({
type: "bi.layout",
});
}
if (isWidget(item)) {
return item;
}
if (el) {
w = Plugin.getWidget(elType, el);
const wType = (w.type && w.type.xtype) || w.type;
if (wType === elType) {
if (Plugin.hasObject(elType)) {
if (!w.listeners || isArray(w.listeners)) {
w.listeners = (w.listeners || []).concat([{
eventName: Events.MOUNT,
action: function() {
Plugin.getObject(elType, this);
},
}]);
} else {
w.listeners[Events.MOUNT] = [
function() {
Plugin.getObject(elType, this);
}
].concat(w.listeners[Events.MOUNT] || []);
}
}
return createRealWidget(w, context, lazy);
}
return createWidget(w, options, context, lazy);
}
if (isWidget(item.el)) {
return item.el;
}
throw new Error("widget: Unable to create widget from item ", item);
}
export function _lazyCreateWidget(item, options, context) {
return createWidget(item, options, context, true);
}
export function createElement() {
const widget = createWidget.apply(this, arguments);
return widget.element;
}
export function getResource(type, config) {
if (isNotNull(constantInjection[type])) {
return Constants.getConstant(type);
}
if (modelInjection[type]) {
return Models.getModel(type, config);
}
if (storeInjection[type]) {
return Stores.getStore(type, config);
}
if (serviceInjection[type]) {
return Services.getService(type, config);
}
if (providerInjection[type]) {
return Providers.getProvider(type, config);
}
throw new Error("unknown type: [" + type + "] undefined");
}
export function getClass(xtype) {
return kv[xtype] || serviceInjection[xtype];
}