设计时组件开发案例

优质
小牛编辑
144浏览
2023-12-01

1.Button组件案例(有对应的运行时组件)

所在目录:/UI2/system/components/justep/button

1.1 创建组件目录

在运行时组件目录/UI2/system/ components/justep/button下创建designer目录,该目录放置设计时组件的相关代码和资源,如下图:

 

1.2 创建组件配置文件

在designer目录下创建button.xml配置文件,这个文件的名称要与运行时组件目录名相同,详细描述如下 :

 <!-- 创建根元素,可以同时定义多个element -->
<elements>
	<!-- 定义组件配置,这里的名称通常定义为运行时组件的名称
             tag-name 节点的标签名名称 
             icon  组件显示在工具箱上面的图标, 默认存放在designer/img下 
	     text 组件文本描述信息 
             binding-component 跟运行时组件绑定 
             component-type 组件类型,可自定义, 详细可参见设计时组件开发文档 -->
	<element name="$UI/system/components/justep/button/button"
		tag-name="a" icon="button.gif" binding-component="$UI/system/components/justep/button/button"
		component-type="formControl" design-view="web-designer">
		<!-- 定义要可视化编辑的属性 -->
		<properties>
			<!-- 定义xid属性,所有组件都有这个属性 -->
			<property name="xid" text="编号" required="true" />

			<!-- 定义class样式属性,默认情况下会继承公共配置中的定义的 class公共属性,
			继承后公共属性上定义的配置直接体现到当前属性上,比如这里没有editor-ref,
			但会继承了公共属性中的editor-ref,该属性就自动默认带有class属性编辑器 -->
			<property name="class" text="class" editor-ref="classSelector">
				<editor-parameter><![CDATA[				{
				"0base": {
					label: "基础样式",
					value: "btn"
				},	 
				"1color":{
					label: "颜色样式",
					value: "btn-default|btn-primary|btn-success "
				}, 
				"2size": {
					label: "尺寸样式",
					value: "btn-lg|btn-sm|btn-xs",
					require: false
				},
				"3icon": {
					label: "图标位置",
					value: "btn-icon-left|btn-icon-top|btn-icon-right|btn-icon-bottom",
					require: false
				}
			}
			]]></editor-parameter>
			</property>
			......

			<!-- 包含数据绑定属性 ,$UI/system/components/designerCommon/commonConfig.xml
			为为目标文件路径#号后面的//common-properties/group[@name='bind']为标准的xpath表达式 -->
			<include
				path="$UI/system/components/designerCommon/commonConfig.xml#//common-properties/group[@name='bind']" />
		</properties>


		<!-- 定义当前组件的事件 -->
		<events>
			<event name="onClick" type="event" text="点击事件" />

			<!-- 包含公共事件 -->
			<include
				path="$UI/system/components/designerCommon/commonConfig.xml#//html-evens/*" />
		</events>

		<!-- 定义模板 -->
		<templates>
			<!-- 默认模板,创建当前组件时会生成模板中的节点代码 -->
			<template name="default"><![CDATA[ 
			<a component="$UI/system/components/justep/button/button" class="btn btn-default" 
			     label="button">
				<i/><span></span>
			</a>		   ]]></template>
		</templates>
	</element>
</elements>

补充说明:1)classSelector 编辑器传入的参数讲解

编辑器的参数值放到editor-parameter节点中,是一个json格式的数据,json数据的第一层列出了class的的分类,每个分类下面都有一个或多个class,如:

"1color":{
	 label: "颜色样式",
	 value: "btn-default|btn-primary|btn-success”,
         require: false
	}

label表示分组的名称

value 枚举了可选的class列表,多个用 | 号分隔

require 表示该组的class是不是必须要选一个,默认为false

 

1.3 创建组件js类

Js类主要负责绘制组件的展现,大多数情况下直接继承运行时的组件,在继承的类里加上设计时的相关的一些设置,以及去掉影响设计时设计的一些功能。设计时组件的的代码结构与运行时组件的一样,但在返回组件类的时候有区别,设计时允许返回多个组件类,这样方便多个设计时组件可以写到一个js文件里面,下面是button组件的js类的说明。

在designer目录下创建button.js文件,代码为:

define(function(require) {
	var $ = require("jquery");
	var Util = require("$UI/system/components/justep/common/designer/common");
	
	/*引入设计时w页面 模型操作服务类*/
	var xuiService = require("$UI/system/components/designerCommon/js/xuiService");
	var xuiDoc = xuiService.getXuiDoc();
	
	/*引入运行时的相关组件类*/
	var Button = require("../button");
	var ButtonGroup = require("../buttonGroup");
	var Radio = require("../radio");
	var Checkbox = require("../checkbox");
	var Toggle = require("../toggle");
	
	/*引入css样式*/
	require('css!./css/button').load();

	/*创建类*/
	var _Button = Button.extend({
		
		/*重载运行时的init方法,主要做一些设计时需要的额外处理*/
		init : function(value, bindingContext) {
			
			/*获取到当前组件的dom节点*/
			var $domNode = $(this.domNode);
			
			/*调用父的init方法*/
			this.callParent(value, bindingContext);
			var cfg = Util.attr2js($domNode, [ 'label', 'icon', 'image' ]);
			if (cfg)
				this.set(cfg);
			var onclick = $domNode.attr('onClick');
			if (onclick && 0 < onclick.indexOf('operation')) {
				// 操作感知
				this.on('onClick', onclick);
			}
			$domNode.removeAttr('onclick').off('click');
			this._d_inited_ = true;
			
			/*对dom节点加上各种控制属性,这些控制属性可以参考《设计时组件开发文档》*/
			this._getLabelNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false);
			this._getIconNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false);
			this._getImgNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false);
		},
		
		/*在事件编辑里绑定操作后调用的方法,主要是感知操作里的文字和图片*/
		bindOperation : function(data) {
			if (data && data.eventName)
				this.on(data.eventName, data.value);
		},
		
		/*重载运行时的属性改变后的处理方法,主要是做一些属性修改后动态更新界面的操作*/
		propertyChangedHandler : function(key, oldVal, value) {
			switch (key) {
			case "label":
				this.callParent(key, oldVal, value);
				if (this._d_inited_)
					xuiDoc.updateText(this._getLabelNode());
				break;
			case "icon":
				if (oldVal != value) {
					
				}
				break;
			default:
				this.callParent(key, oldVal, value);
			}
		}
	});

	/*定义按钮组类*/
	var _ButtonGroup = ButtonGroup.extend({
		init : function(value, bindingContext) {
			var $domNode = $(this.domNode);
			var cfg = Util.attr2js($domNode, [ 'selected' ]);
			this.callParent(value, bindingContext);
			if (cfg)
				this.set(cfg);
			this.$domNode.off('click');
		},
		/*右键菜单上的操作,添加按钮组件*/
		addButton : function() {
			xuiDoc.createComponent('$UI/system/components/justep/button/button', this, {
				paintComponent : true
			});
		}
	});

	/*定义单选按钮类*/
	var _Radio = Radio.extend({
		init : function(value, bindingContext) {
			createRadioButton(this, 'radio');
			this.callParent(value, bindingContext);
			this._getInput().off('click');
		}
	});

	/*定义多选按钮类*/
	var _Checkbox = Checkbox.extend({
		init : function(value, bindingContext) {
			createRadioButton(this, 'checkbox');
			this.callParent(value, bindingContext);
			this._getInput().off('click');
		}
	});

	/*返回多个组件类,这里与运行时组件有区别,组件类的key值对应注册文件里面的name属性*/
	return {
		'$UI/system/components/justep/button/button' : _Button,
		'$UI/system/components/justep/button/buttonGroup' : _ButtonGroup,
		'$UI/system/components/justep/button/radio' : _Radio,
		'$UI/system/components/justep/button/checkbox' : _Checkbox
	};
});

补充说明:以上代码只摘取了实际类中的主体内容,具体的可以直接到组件目录下查看

 

2.listGroup组件案例(没有对应的运行时组件)

所在目录:/UI2/system/components/bootstrap/listGroup

 

该组件不存在运行期的组件,只是以html标签的形式展现

 2.1 创建组件目录

/UI2/system/components/bootstrap/listGroup/designer

 

2.2 创建组件配置文件

配置文件与上面案例的格式基本上是一样的,因为这里没有对应运行时组件,所以通过加额外的鉴别条件来识别该组件:discriminate-condition=“hasClass(‘list-group’)”

意思就是当有list-group这个class的节点时,该节点就会被识别成listGroup组件。

<elements>
	<element name="$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)" tag-name="div" icon="element.gif" text="列表组"  
	    discriminate-condition="hasClass('list-group')" resizable="false"
		component-type="layout-container"  design-view="web-designer" >

		<!-- 右键菜单到工具栏的配置,addItem对应这js类中的方法 -->
		 <toolbar>
				<item text="添加条目" method="addItem"/>			
		</toolbar>
              …..
       </element>
</elements>

2.3创建js类

因为没有对应的运行时组件类,所以直接继承了一个组件基类,如:

define(function(require) {
	require("$UI/system/components/justep/common/res");
	
	var xuiService = require("$UI/system/components/designerCommon/js/xuiService");
	var xuiDoc = xuiService.getXuiDoc();
	
	/*引入组件基类*/
	var ViewComponent = require("$UI/system/lib/base/viewComponent");
 
	/*创建组件类*/
	var ListGroup = ViewComponent.extend({
		
		/*重载基类的init方法*/
		init : function() {  
			var $domNode = $(this.domNode);  
			$(">*", $domNode).attr("d_resizable", "false");
		},
		
		/*右键菜单的操作,添加一个子项*/
		addItem : function(){
			xuiDoc.createComponent("$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)#gruopItem", this, {
				paintComponent : true
			});
		}
	});
	
	/*返回组件类*/
	return {
	'$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)' : ListGroup
	};
});

 

3.特定功能点案例

3.1 右键菜单弹出编辑页面

$UI/system/components/justep/data/data 组件的右键菜单“编辑列”为例

执行效果图

 

代码片断:

var editColumn = function(data, config) {
		/*打开列编辑页面,页面 会以对话框的形式 打开  */
		xuiService.openPage("$UI/system/components/justep/data/designer/editColumn.w", 
		{
			title : "data列编辑", //对话框的标题
			width:800, //对话框的宽度
			height:600,//对话框的高度
			xml : config.nodeXml //当前节点的xml,改参数会传入到编辑页面中
		},
		function(result) {//点确定后的回掉方法
			
			//用返回来的节点替换到当前节点的子节点
			xuiDoc.replaceChild(data, result.def.join(""), {
				paintComponent : false, //是否要在设计区立即绘制这些新加的子节点,true 或者 false
				xpathCondition : "*[local-name()='column']"//目标子节点的查询条件
			});
			
			//设置idColumn属性值
			xuiDoc.set(data, {
				idColumn : result.idColumn
			});
		});
};

3.2 批量创建子组件

$UI/system/components/justep/grid/grid 组件添加列为例

appendColumn: function(config) {
	var self = this;
	//获取data的did
	var dataID = this.get('data');
	var data = this.getModel().comp(dataID);
	if(!data){
		alert('请先设置Grid的data属性');
		return;
	}
	var data_did = data.$domNode.attr('d_id');
	// 获取data列信息
	var cols = xuiDoc.getEditorDataSource("RuleRelationDatasource.getDatasource",null,data_did);
	//打开编辑器
	xuiService.openPage("$UI/system/components/justep/dataTables/designer/appendColumn.w", {
		xml : config.nodeXml,
		cols : cols
	}, function(result) {
		var cols = result.cols;
		if($.isArray(cols)){
			var configs = [];
			//构建要创建的列列表
			for(var i=0;i<cols.length;i++){
				configs.push({
					componentName : "$UI/system/components/justep/grid/grid#column",//注册的列组件名
					parentElementId : self.columns_dId,//父的xid
					templateContent : '<column width="100" name="'+cols[i]+'"/>'//模板内容
				});
			}
			//批量创建列
			xuiDoc.batchCreateComponent(configs, function() {
				self.repaintGrid();//重新绘制grid
			});
		}
		
	});
}

3.3 节点同步删除

当右键删除一个组件的同时还想同时在删除其他地方的节点时,可以参考以下例子来进行处理。

$UI/system/components/bootstrap/tabs/tabs(bootstrap) 组件右键删除页签为例,删除页签头时,同时删除页签的内容。

接管TabItem的onRemove 方法,如:

//删除时调用的方法 
onRemove : function(event) {
	if ($("li", this.domNode.parentNode.parentNode).length == 1) {
		event.cancel = true;
	} else {
		//取消默认的删除动作
		event.cancel = true;
		var $domNode = $(this.domNode);
		
		//找到内容节点
		var content = $domNode.attr("content");
		var contentDId = $(">.tab-content>*[xid='" + content + "']",
				$domNode.parent().parent().parent()).attr("d_id");
		
		//删除内容节点和当前节点
		xuiDoc.deleteComponent([ contentDId,
				$domNode.parent().attr("d_id") ]);
	}
}

结束语:以上是比较典型的案例开发讲解,设计时的组件有些更细的方面这里没有面面俱到,需要更深入了解的话可以去研究更多的组件代码[/fusion_text]