tfp
Version:
A Web UI framework for TaskBuilder
633 lines (576 loc) • 19.1 kB
JavaScript
const _parent = Symbol("parent");
/**
* TaskUI组件基类
*/
export class Component {
constructor(__tfp, typeName, dataModel, parent) {
if(!__tfp) {
throw new Error("请提供tfp对象!");
return;
}
if(!typeName) {
console.log(dataModel);
throw new Error("请提供组件类型!");
return;
}
this._tfp = __tfp;
this.level = 0; //组件相对页面组件的层次
this.index = 0; //组件在父组件中的索引,主要用来排序
//目前如果某个兄弟组件删除了,不会重置所有剩余的兄弟组件的索引
//如果没有提供数据模型,则进行模型初始化
if(!dataModel) {
let metadata = this._tfp.type(typeName);
if(!metadata) {
throw new Error("请提供正确的组件类型!");
return;
}
this.dataModel = {
type: typeName
};
this.dataModel.id = this.dataModel.type.substr(0, 1).toLowerCase()
+this.dataModel.type.substr(1)+this._tfp.getNewCptIndex(this.dataModel.type);
let attrs = this.attrTypes;
//设置组件默认属性
for(let i=0; i<attrs.length; i++) {
let attr = attrs[i];
if(attr.default || attr.default==0 || attr.default==false) {
if(attr.type.toLowerCase()=="string") {
this.dataModel[attr.name] = attr.default.replace("{id}", this.id);
} else {
this.dataModel[attr.name] = attr.default;
}
}
}
//设置可视组件数据模型的默认样式
if(metadata.defaultStyles) {
this.dataModel.styles = {};
for (let style in metadata.defaultStyles) {
this.dataModel.styles[style] = metadata.defaultStyles[style];
}
}
} else { //否则用传入的数据模型
this.dataModel = dataModel;
if(!this.dataModel.type) this.dataModel.type = typeName;
}
//设置组件数据模型的默认ID
if(!this.dataModel.id) {
this.dataModel.id = this.dataModel.type.substr(0, 1).toLowerCase()
+this.dataModel.type.substr(1)+this._tfp.getNewCptIndex(this.dataModel.type);
}
this._tfp.components[this.dataModel.id] = this;
if(this._tfp.isRuntime) window[this.dataModel.id] = this;
//设置组件的父组件
if(parent) {
this[_parent] = parent;
this.level = parent.level+1;
if(!this._tfp.isLoadingPage) {
if(!parent.dataModel.components) parent.dataModel.components = [];
let cptExists = false;
for(var i=0;i<parent.dataModel.components.length;i++) {
let cdmTmp = parent.dataModel.components[i];
if(cdmTmp.id == this.dataModel.id) {
cptExists = true;
continue;
}
if(cdmTmp.index>=this.index) this.index = cdmTmp.index + 1;
}
if(!cptExists) parent.dataModel.components.push(this.dataModel);
}
} else {
//TODO 如果创建组件时没有提供父组件,则表示是临时组件,后续可以设置父组件
}
}
//组件ID,每个组件的ID是唯一的,不能重复
get id() { return this.dataModel.id }
set id(value) {
if(!value) return;
let oldId = this.dataModel.id;
if(oldId==value) return;
for(let cptId in this._tfp.components) {
if(cptId==value) {
throw new Error("ID为的"+value+"组件已存在!");
return;
}
}
this.dataModel.id = value;
delete this._tfp.components[oldId];
this._tfp.components[this.dataModel.id] = this;
if(this._tfp.isRuntime) {
delete window[oldId];
window[this.dataModel.id] = this;
}
if($("#"+oldId).length>0) {
$("#"+oldId).attr("id", this.dataModel.id);
}
}
//组件类型,组件一旦创建,不允许再修改类型
get type() { return this.dataModel.type }
set type(value) {}
//组件类型元数据,组件一旦创建,不允许再修改
get metadata() { return this._tfp.type(this.dataModel.type) }
set metadata(value) {}
//父组件
get parent() { return this[_parent] }
set parent(value) {
//如果没有设置父组件
if(!this[_parent]) {
this[_parent] = value;
if(!this[_parent].dataModel.components) this[_parent].dataModel.components = [];
this[_parent].dataModel.components.push(this.dataModel);
} else {
if(this[_parent].id!=value.id) { //如果已经设置了父组件,但新设置的父组件和原来的父组件不一样
this[_parent].dataModel.components.remove(this.dataModel);
this[_parent] = value;
if(!this[_parent].dataModel.components) this[_parent].dataModel.components = [];
this[_parent].dataModel.components.push(this.dataModel);
} else {
//如果父组件没有变化,则不需要执行后续操作
return;
}
}
//设置当前组件的层级和索引
this.level = this[_parent].level+1;
for(var i=0;i<this[_parent].dataModel.components.length;i++) {
let cdmTmp = this[_parent].dataModel.components[i];
if(cdmTmp.index>=this.index) this.index = cdmTmp.index + 1;
}
}
get attrTypes() {
let attrs = [];
let metadata = this._tfp.type(this.type);
if(metadata.attrs) {
for(let i=0; i<metadata.attrs.length; i++) {
let attr = metadata.attrs[i];
if(attr.type=="group" || attr.items) {
for(let j=0;j<attr.items.length;j++) {
attrs.push(attr.items[j]);
}
} else {
attrs.push(attr);
}
}
}
return attrs;
}
set attrTypes(value) {}
attr(attrName, attrValue) {
if(arguments.length==0) return;
//获取样式值
if(arguments.length==1) {
return this[attrName];
}
this[attrName] = attrValue;
}
/**
* 获得属性定义信息
* @param {[type]} attrName [description]
* @return {[type]} [description]
*/
getAttrTypeInfo(attrName) {
if(!this.metadata) {
throw new Error("没有找到组件类型定义信息,请先引用类型信息!");
return;
}
if(!this.metadata.attrs) {
//throw new Error("没有找到组件的属性定义信息!");
return null;
}
for(var i=0;i<this.metadata.attrs.length;i++) {
let attrInfo = this.metadata.attrs[i];
if(attrInfo.type=="group" || attrInfo.items) {
for(var j=0;j<attrInfo.items.length;j++) {
if(attrInfo.items[j].name==attrName) {
return attrInfo.items[j];
}
}
} else {
if(this.metadata.attrs[i].name==attrName) {
return this.metadata.attrs[i];
}
}
}
return null;
}
/**
* 检查属性选项是否是组件类型定义中设置的选项
* @param {[type]} attrName [description]
* @param {[type]} attrVal [description]
* @return {[type]} [description]
*/
checkAttrOption(attrName, attrVal) {
let attr = this.getAttrTypeInfo(attrName);
if(!attr) return false;
let options = attr.options;
if(!options) return false;
for(var i=0;i<options.length;i++) {
if(options[i].value==attrVal) return true;
}
return false;
}
/**
* 执行事件处理函数
* @param {[type]} eventName [description]
* @return {[type]} [description]
*/
exeEventHandler(eventName) {
//设计时不执行任何事件处理函数
if(this._tfp.isDesigning) return;
let ret;
if (this[eventName] && typeof this[eventName] == "function") {
ret = this[eventName](arguments);
} else if (this.dataModel[eventName]) {
let funcStr = this.dataModel[eventName];
if(funcStr.indexOf("(")>0) {
let funcName = funcStr.substr(0, funcStr.indexOf("("));
if(typeof(window[funcName])=="function") {
let args = [];
if(arguments.length>1) {
for(var i=1;i<arguments.length;i++) {
args.push(arguments[i]);
}
}
let func = window[funcName];
ret = func.apply(window, args);
} else {
ret = eval(funcStr);
}
} else {
ret = eval(funcStr);
}
}
if(ret) return ret;
}
}
export class InvisibleComponent extends Component{
constructor(__tfp, typeName, dataModel, parent) {
super(__tfp, typeName, dataModel, parent);
}
get isInvisible() { return true }
set isInvisible(value) {}
render() {
//如果是不可视组件,只有在设计时需要渲染
if(this._tfp.isDesigning) {
window.parent.uiDesigner.addInvisibleComponent(this);
this._tfp.initCptDesignSetting(this);
}
}
}
export class VisibleComponent extends Component{
constructor(__tfp, typeName, dataModel, parent) {
super(__tfp, typeName, dataModel, parent);
this._jqObj = null;
this.el = null;
this.isRendered = false;
}
get isInvisible() { return false }
set isInvisible(value) {}
get isContainer() { return false }
set isContainer(value) {}
get styles() { return this.dataModel.styles; }
set styles(value) {}
get style() { return this.dataModel.style; }
set style(value) {
if(!value) value = "";
this.dataModel.style = value;
if(this._jqObj) {
let style = value.trim();
if(style!="" && !style.endsWith(";")) style += ";";
if(this.dataModel.styles) {
for(let styleTmp in this.dataModel.styles) {
style += styleTmp +": "+this.dataModel.styles[styleTmp]+";";
}
}
//要保留设计时的外边框
let outline = this._jqObj.css("outline");
this._jqObj.attr("style", style);
this._jqObj.css("outline", outline);
}
}
get class() { return this.dataModel.class; }
set class(value) {
if(!value) value = "";
this.dataModel.class = value;
if(this._jqObj) {
this._jqObj.attr("class", value);
}
}
get indent() {
var _indent = "";
for(var i=0;i<this.level;i++) {
_indent += "\t";
}
return _indent;
}
set indent(value) {}
css(styleName, styleValue) {
if(arguments.length==0) return;
//获取样式值
if(arguments.length==1) {
if(!this.dataModel.styles) return null;
return this.dataModel.styles[styleName];
}
if(this._jqObj) this._jqObj.css(styleName, styleValue);
if(!this.dataModel.styles) this.dataModel.styles = {};
if(styleValue=="" || styleValue==null) {
delete this.dataModel.styles[styleName];
} else {
this.dataModel.styles[styleName] = styleValue;
}
}
val(value) {
if(arguments.length==0) return this.value;
this.value = value;
}
show() {
if(this._jqObj) this._jqObj.show();
}
hide() {
if(this._jqObj) this._jqObj.hide();
}
toggle() {
if(this._jqObj) this._jqObj.toggle();
}
focus() {
if(this._jqObj) this._jqObj.focus();
}
getHtmlIndent() {
if(this.indent) return this.indent;
let indent = "";
for(let i=0;i<this.level;i++) {
indent += "\t";
}
return indent;
}
render() {
if((!this.parent || !this.parent.containerEl) && this.type!="Page") return;
let Render = this._tfp.renders[this.type];
let render = new Render(this._tfp, this.dataModel, this.level);
if(this.type=="Page") {
if(this.dataModel.pageElId && this._tfp.isRuntime) {
this._jqObj = $("#"+this.dataModel.pageElId);
} else {
this._jqObj = $("body");
}
this._jqObj.append(render.getHtml());
this._tfp.curPage = this;
} else {
$(this.parent.containerEl).append(render.getHtml());
this._jqObj = $("#"+this.id);
}
if(this._jqObj.length>0) this.el = this._jqObj.get(0);
if(!this.isRendered && this._tfp.isDesigning) {
this._tfp.initCptDesignSetting(this);
}
if (typeof window != "undefined" && !this._tfp.isDesigning) {
window[this.id] = this;
}
if(this.dataModel.components) {
for(var i=0;i<this.dataModel.components.length;i++) {
let cdmChild = this.dataModel.components[i];
let cptChild = this._tfp.render(cdmChild, this);
}
}
if(this["initDesigning"]) this.initDesigning();
this.isRendered = true;
}
clear() {
if(this.dataModel.components) {
for(var i=0;i<this.dataModel.components.length;i++) {
let childCdm = this.dataModel.components[i];
let child = this._tfp.get(childCdm.id);
child.clear();
if(child._jqObj) child._jqObj.remove();
delete this._tfp.components[child.id];
}
//this.dataModel.components = [];
}
}
}
//容器组件
export class ContainerComponent extends VisibleComponent{
constructor(__tfp, typeName, dataModel, parent) {
super(__tfp, typeName, dataModel, parent);
if(!this.dataModel.components) this.dataModel.components = [];
}
get isContainer() { return true }
set isContainer(value) {}
get containerEl() { return this.el }
set containerEl(value) {
this.el = value;
}
get components() { return this.dataModel.components }
set components(value) {}
/**
* 添加子组件
* @param {[type]} cptChild [description]
*/
addChild(cptChild) {
cptChild.parent = this;
}
/**
* 移除子组件
* @param {[type]} cptId [description]
* @return {[type]} [description]
*/
removeChild(cptId) {
for(var i=0;i<this.dataModel.components.length;i++) {
let childCdm = this.dataModel.components[i];
if(childCdm.id==cptId) {
let child = this._tfp.components[childCdm.id];
child.clear();
if(child._jqObj) child._jqObj.remove();
if(this._tfp.isRuntime) {
delete this._tfp.components[child.id];
delete window[child.id];
}
child = null;
childCdm = null;
return;
}
}
}
/**
* 清空子组件
* @return {[type]} [description]
*/
clearChildren() {
for(var i=0;i<this.dataModel.components.length;i++) {
let childCdm = this.dataModel.components[i];
let child = this._tfp.components[childCdm.id];
child.clear();
if(child._jqObj) child._jqObj.remove();
delete this._tfp.components[child.id];
if(this._tfp.isRuntime) delete window[child.id];
child = null;
childCdm = null;
}
this.dataModel.components = [];
}
}
//表单输入项组件
export class FormInput extends VisibleComponent{
constructor(__tfp, typeName, dataModel, parent) {
super(__tfp, typeName, dataModel, parent);
}
get isFormInput() { return true }
//组件备注
get comment() { return this.dataModel.comment }
set comment(value) {this.dataModel.comment = value}
//数据绑定格式
get dataBindingFormat() { return this.dataModel.dataBindingFormat }
set dataBindingFormat(value) {this.dataModel.dataBindingFormat = value}
//是否必填
get required() { return this.dataModel.required }
set required(value) {this.dataModel.required = value ? true : false}
//是否只读
get readonly() { return this.dataModel.readonly }
set readonly(value) {
this.dataModel.readonly = value ? true : false;
if(!this.dataModel.readonly) delete this.dataModel.readonly;
if(this._jqObj && this._jqObj.length>0 && !this._tfp.isDesigning) {
let el = this._jqObj.get(0);
if(el.tagName=="INPUT" || el.tagName=="SELECT" || el.tagName=="TEXTAREA") {
el.readOnly = this.dataModel.readonly;
} else if(el.tagName=="DIV") {
this._jqObj.find("input").each(function(){
$(this).get(0).readOnly = this.dataModel.readonly;
});
}
}
}
//是否禁用
get disabled() { return this.dataModel.disabled }
set disabled(value) {
this.dataModel.disabled = value ? true : false;
if(!this.dataModel.disabled) delete this.dataModel.disabled;
if(this._jqObj && this._jqObj.length>0 && !this._tfp.isDesigning) {
let el = this._jqObj.get(0);
if(el.tagName=="INPUT" || el.tagName=="SELECT" || el.tagName=="TEXTAREA") {
el.disabled = this.dataModel.disabled;
} else if(el.tagName=="DIV") {
this._jqObj.find("input").each(function(){
$(this).get(0).disabled = this.dataModel.disabled;
});
}
}
}
//自动计算表达式
get formula() { return this.dataModel.formula }
set formula(value) {
if(isNull(value)) {
delete this.dataModel["formula"];
return;
}
this.dataModel.formula = value;
}
/**
* 当值发生变化时
* @return {[type]} [description]
*/
valueOnChange() {
this._tfp.iptValueOnChange(this);
}
/**
* 执行计算表达式
* @return {[type]} [description]
*/
exeFormula() {
if(!this.dataModel.formula) return;
let val = this.dataModel.formula;
let ipts = this.dataModel.formula.match(/\$\{[\w]+\}/g);
for(let i=0;i<ipts.length;i++) {
let iptId = ipts[i].substr(2, ipts[i].length-3);
let ipt = this._tfp.components[iptId];
if(!ipt) continue;
let iptVal = ipt.value;
if(ipt.type=="Text" && isNull(iptVal)) {
//如果是单行输入框,且数据类型不是文本,则值为空时,将值设置为0,以便计算
//不判断类型了,用户经常不设置类型
//if(ipt.dataType=="int" || ipt.dataType=="float" || ipt.dataType=="money") {
iptVal = "0";
//}
} else if(ipt.type=="DataSet") {
iptVal = ipt.id;
}
val = val.replaceAll("\$\{"+iptId+"\}", iptVal);
}
try {
this.value = eval(val);
} catch(err) {
console.error("执行计算表达式出错:"+err.message);
}
}
/**
* 设置输入项的选项
* @param {[type]} value [description]
*/
setOptions(value) {
if(!value) return;
let options = [];
if(typeof(value)=="string") {
let arr = value.split(",");
for(var i=0;i<arr.length;i++) {
let str = arr[i];
if(str.trim()!="") {
options.push({
value: str,
text: str
});
}
}
} else if(Array.isArray(value)) {
for(var i=0;i<value.length;i++) {
let val = value[i];
if(Object.prototype.toString.call(val) === '[object Object]') {
options.push(val);
} else {
options.push({
value: val,
text: val
});
}
}
}
this.dataModel.options = options;
}
}