首先,我们来简单介绍一下门户系统。
我们的后台管理页面的研发本质上都是在门户系统上进行二次开发的过程。
首先,需要了解门户是什么?门户是一个页面访问入口的程序,在每个平台中,都会封装好一个门户系统作为管理页面访问的入口,我们要开发的新的功能未来会在门户中作为新的菜单来展示给页面。
那么,现在的门户主要做了哪些事情呢?
第一件事,门户封装了基本的开发工具。在我们开发页面的时候,很多准备数据的维护以及简化的开发工具都在门户中做了维护,基于这些工具,我们才能进行页面的维护与研发。
第二件事,门户实现实现了基本的权限管理系统。换句话讲,门户实现了人员管理的功能,我们只需要开发页面,门户实现好了人员、角色、菜单的管理,将来我们可以通过门户添加人员,定义角色以及分配菜单。这样实现不同人员对不同页面的访问。
第三件事,门户实现了单点登录的处理。要说明这点,首先我们需要了解什么是单点登录。在我们访问网络程序的时候,很多时候我们会有这种情况,我们在访问一个网站的时候,需要登录,登录的时候,我们可以使用一个共享账号登录(例如QQ号登录),这也就是说,我们本质上没有在这个系统本身注册账号,我们通过一个共享的账号可以访问这个系统的资源,这其实就是单点登录的本质。其实,单点登录解决的是跨系统访问而不需要重新注册的处理。
但是,与上面的例子略有不同的是,门户实现的是共享账号的管理,在门户中注册了账号后,我们可以通过这个门户访问其他系统的资源。
理解了上面的说明后,我们再来提一下门户的基本概念结构。
在门户中,有几个概念是必须了解的,在理解了这些概念后,才能够更容易的使用门户。
系统:系统也就是实际部署的项目,每个在域中部署的项目都是一个系统,每个系统会有绝对的IP地址、端口与系统标识(也就是工程路径)(PT_APP_INFO)
菜单:每个功能提供的页面入口定义为一个菜单,菜单是实际存在的页面,与系统存在直接的物理关系,也就是说,菜单一定是定义在某个系统下的,实际关系也一定是这样。(PW_APP_MENU_INFO)
应用:这个可以看做一个虚拟系统的概念,系统是实际部署的项目,但是系统不会直接展示给用户,在门户中,作为最上层呈现给用户的是应用,应用是由菜单组合起来的,一般我们开发一个项目的时候,都会单独定义一个应用,把项目相关的菜单都整合到这个应用下。正是因为应用的出现,使得不同系统的菜单可以实现自由组合的处理,让实际的开发可以更加功能化。(PW_APP_DETAIL)
应用在门户中是一个非常关键的概念,请一定注意。
模块:应用下的一级菜单。应用一般不直接关联菜单,应用的菜单一般指二级菜单,而应用下直接关联的是一级菜单,一般我们称为模块,而一级菜单下面关联实际菜单。(PW_APP_MODEL)
部门:系统中的机构管理,对于银行来说,一般的机构类似于 总行 > 管辖行 > 支行 的层次。(PW_DEPT)
人员:门户的中的人员一般是登录后台管理的人员管理,这类人员一般是管理员性质的人员。(PW_OPER)
角色:控制权限访问的定义,角色绑定应用下的菜单,可以分配到人员,控制人员登录可访问的页面。(PW_ROLE)
运维商:这个是门户中一个比较容易被忽略的概念,因为平时被使用的概率也不是非常高。运维商是一个最上级的概念定义。你可以认为是一个最高管理员的定义。一般有些比较庞大的项目,会涉及到比较复杂的数据分离,很多时候,我们会通过部门信息进行数据分离,但是当情况非常复杂的时候,就会非常麻烦。例如,某个项目需要分地区,比如分为青岛地区和烟台地区,如果项目是部署在互联网上的,数据在同一个数据库中,那么就会出现很麻烦的情况,比如青岛地址和烟台地区的角色控制可能完全是不同的定义,在这种情况下,要怎样处理?这时候就可以使用运维商,分别定义青岛和烟台的运维商,然后下面,门户中所有的数据都会维护到各自的运维商下(包括人员、角色、部门甚至应用)。
说到运维商,一般是在项目上线运维时候进行创建,创建后可以生成一个最高管理员账户,管理该运维商下所有数据。但是,上面说过这种情况不常用,那么,平常的项目大多怎样处理呢?现在的项目很多不涉及过于复杂的权限处理,所以,很多项目直接使用9999作为最高管理员使用,基于最简单的人员角色管理就可以进行数据分离与权限控制。(PW_MER_INFO)
理解了上面的概念后,我们来说明门户的基本使用。
下面的这张图来说明门户的基本结构:

在上面我们提过了运维商,这里我们来介绍一下门户的权限控制。
首先,在门户中存在2种权限控制,管理员模式和普通用户模式。通过用户类型(OPER_TYP)来区分用户采用哪种模式。
9999默认为最高管理员,不需要权限控制,直接可以访问全部的系统菜单。
我们可以通过人员管理页面添加人员,通过这个位置添加的人员为普通人员,走普通用户模式,通过授权角色来控制访问菜单。现在我们实际的项目大部分都是走了这种模式。
还有一种模式,在添加运维商的时候,默认会添加一个人员信息,这个人员属于管理员模式,这个人员可以访问所有授权到运维商的应用菜单(注意,这个地方授权的是可访问应用,授权后可以访问应用下面全部菜单,不是直接授权菜单)。这个模式在什么时候使用呢,一般在2种情况下会使用,第一种情况是不希望让9999作为最高管理员,希望在上线的时候生成一个新的最高管理员作为运维账户使用(9999实际上默认是初始运维商(999001)的管理员账户)。第二种情况是顶层存在大的数据分离,例如地区性质的数据分离,青岛地区与烟台地区访问的是同一个后台,需要不同的最高管理员账户与数据体系(菜单、角色、人员都需要分离),这时候就会分别添加2个运维商控制不同的数据体系。
关于门户的单点登录,使用的情况比较简单,以实际情况为例,比如后台部署了2个项目,积分与权益,我希望以权益作为门户登陆,登陆后可以访问权益的菜单,同时也可以访问积分的菜单,这种情况就需要使用单点登录。平台中的实现比较简单,下面来介绍一下具体的处理步骤。
首先区分开门户系统和对接系统,对接系统需要配置支持门户的访问配置,需要配置的部分如下
首先,config/esb/esbSetting.xml 文件中 配置 isAuth为true,代表开启门户访问支持
	<!-- 门户相关:会话是否门户认证 -->
	<property name="isAuth" value="true"/>
然后,修改资源配置,config/esb/driver/res/resPool.xml和resPoolApp.xml中找到portal的配置记录,
调整svrPort、commIP、commUrl为门户的通讯信息
----- resPool.xml ------
<record resID="portal" resName="门户" commMod="20" commID="platFormService" timeOut="30" commHandle="XXX" svrPorts="7002" svrPort="7001" monSvr="XXX"  proxyIp="" proxyPort="" appFile="resPoolApp"/>
----- resPoolApp.xml -----
<record resID="portal" resAppID="portalSys" appNam="门户系统" exeNum="10" maxNum="15" commIP="127.0.0.1" commUrl="lily/platFormService?commID=portalService" serviceFile="resPoolService" errorFile="resPoolError" />
最后,调整config/web/webSetting.xml中门户访问与退出路径,如下
<!--系统退出路径-->
<property name="exitUrl" value="/login.jsp"/>
	
<!-- 门户访问路径 -->
<property name="portalUrl" value="http://127.0.0.1:7001/lily"/>	
这样对接系统的调整完成。
门户系统需要配置对接系统的系统信息和菜单信息。
首先,我们要添加对接系统的系统信息,到 开发应用 > 开发运维 > 菜单共享系统管理 添加一条系统记录,系统的标识需要与对接系统的标识(部署路径)一致,访问地址需要配置为项目的完整地址,例如http://127.0.0.1:8090/aps。
配置完系统后,我们只需要将对接系统的菜单配置到当前系统下即可。
菜单第你定义后,可以当作普通菜单使用。
很多上线项目都会有扩充登陆流程的需求,例如,以商户身份登陆内管,登陆后希望把商户的相关信息也存储到当前登录人员中,在后续要使用的位置可以直接获取(而不是需要反复查询)。为了方便处理这种情况,平台支持重写登陆流程,写法非常简单。
首先,可以继承SystemAuth实现一个登陆扩展类。
public class InsLogin extends SystemAuth {
	
	@Override
	public void after(OperEntity operEntity) {
		
		String operNo = operEntity.getOperNo();
		String operKey = operEntity.getOperId();
		
		//查询人员角色信息
		sql = "select * from ${schema}.PW_OPER_ROLE where OPER_KEY='"+operKey+"'";
		DataTable roles = DBFactory.getActionDB().executeQueryTable(sql);
		
		List<String> roleList = new ArrayList<String>();
		while(roles.next()){			
			String roleID = roles.getRecord().getStringNotNull("ROLE_ID");
			roleList.add(roleID);						
		}
		
		operEntity.setFieldValue("roles", roleList);
		
	}
	
}
在类中,我们可以实现before(校验密码前)和after(获取用户后)2个回调方法,在after中,我们可以实现自己的业务处理,需要存储的信息只需要通过operEntity.setFieldValue存储即可,其他需要获取的地方可以使用operEntity.getFieldValue获取。
实现类后,我们需要将类配置到项目中,在项目config/esb/esbSetting.xml文件中,有一个userControl的参数,可以配置登陆类的完整路径,默认为SystemAuth,只需要把这个路径调整为我们自己的类路径即可
	<!-- 用户处理实例 -->
	<property name="userControl" value="app.ins.web.login.InsLogin"/>	
在最初我们部署项目进行研发的时候,导航栏上可以选择应用进行切换,这种展示模式称为多应用模式,这种模式下,将用户可以访问的应用全部展示出来,由登陆人员选择需要的应用查看菜单。
实际在生产上线的时候,大部分项目都会将全部放开的菜单打包到一个应用中,用户登陆后可以不用切换应用,只访问一个应用中的菜单,而登陆后,默认也是展示该应用的菜单,不再提供其他应用的切换(导航上没有切换应用的选项)。这种模式称为单应用模式。
单应用模式一方面可以方便实现上线后的使用,另一方面可以控制权限(例如可以不让开发使用的菜单暴露到外部)。
项目从多应用切换到单应用,处理实际非常简单。首先,我们需要找到我们将来要使用的应用的标识。
到开发应用 > 开发运维 > 菜单共享应用管理中,找到对应应用的记录,从页面上可以直接找到应用标识

找到标识后,我们到配置文件 config/web/webSetting.xml 中 single_id 配置,这条信息注释或者为空的情况,为多应用模式,配置为固定的应用标识的情况,为单应用模式,把记录放开修改标识为需要标识即可
<property name="single_id" value="000626"/>
重启项目即可看到效果。
在提前端处理前,我们先来简单介绍一下前端架构。
平台的前端使用的是独立控件封装的处理方式,每个后台的组件在前端一般会对应一个前端控件,每个前端控件有各自的属性与方法,一般使用前端控件的时候一定要确定控件类型没有问题的情况下调用方法,避免出现grid调用了form的情况。
前端页面在加载的时候,会针对页面封装一个顶层page对象,所有当前页面中的对象都可以通过page.find([对象标识])来进行定位,因为所有的回调方法都支持page参数,所以基本可以放放心使用。
使用page的时候,需要注意,page对象与后台类是对应关系,page中只会封装对应后台类中的页面组件,出现跨页面的情况,请一定小心。目前常见的页面的模式有3种,第一种是弹窗,弹窗页面与主页面是父子关系,子页面对象page可以通过parent属性直接获取父页面对象(page.parent)。第二种是标签页,标签页是通过嵌套模式将其他页面嵌套到子标签中,主页面中只有tab组件,标签页面也可以通过page.parent定位主页面对象。第三种是Frame模式,典型的页面类型是树加表,树的关联表页面是一个单独的页面,表页面通过Frame对象套入主页面中,这种情况,子页面也可以通过page.parent定位到主页面。实际说了这么多,要说明的只有一点,出现跨页面的情况,可以通过page.parent定位父页面对象,不要出现定位组件的错误。
page中封装了那些组件,我们可以通过console.log(page)输出page对象来进行查询,page.component就是封装组件的数组。在很多时候出错的情况经常会这样使用。
前端封装的普通控件基本没有通用的方法,通用的属性也只有elementID(组件标识)与elementType(组件类型)。需要注意一点的是,基本前端控件都会有一个view属性,这个属性封装这最初从后台获取的初始化的属性信息,在后期熟悉配置结构后,有时候会从这部分读取数据或者修正数据。
内管所有前端方法都是基于回调处理的,一般在前端定义的回调方法可以分为2种,一种是在后台直接声明的方法,从后台某些组件声明前端方法名,前端定义该方法进行调用。比如:按钮点击方法。另一种是通过组件标识+后缀声明定义的方法,一般这种方法会在某个固定的时间点执行,例如:[grid标识]_afterEvent在表格绑定所有动作后执行。
无论是什么样的回调方法,一般都遵循一个固定的准则,回调方法的参数至少有2个参数,且第二个参数一定是page对象。
关于回调方法的参数,第一个参数,一般是方法涉及的对象本身,如果是按钮点击方法,因为按钮对象本身意义不大,所以,第一个参数一般是按钮的上级对象。其他的回调方法,属于哪个对象,第一个参数就是哪个对象。第二个参数是page对象,用于联动页面中其他对象。有些方法还有其他参数,这些一般需要考虑具体情况处理。
在定义js的时候,对象支持的方法,请一定注意查询api手册(目前没有的情况可以参考《平台新主题手册》)。
在具体使用中,可能会出现需要获取某些信息但是API中没有的情况,建议使用console.log打印一下page或者控件对象。一般控件对象中的view属性可以向下读取到当前缓存数据。
下面是一份最新的前端API,大家可以参考使用
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回组件的完整HTML信息 | 
| refresh | 无 | 刷新组件数据 | 
| getDataSet | 无 | 获取当前单选形式数据 | 
| getCheckDataSet | 无 | 获取当前多选形式数据 | 
| getFullDataSet | 无 | 获取当前页全部数据 | 
| getFieldValue | String – 字段标示 | 获取字段值 | 
| getCheckFieldValue | String - 字段标识,msg(可选,未勾选记录的提示信息) | 获取复选字段值,自动逗号分隔 | 
| setFieldValue | String-字段标识,String-字段值 | 修改活动记录的显示信息 | 
| next | 无 | 获取多条数据的时候,移动到下一条数据 | 
| getRecordCount | 无 | 获取多条数据的时候,获取记录数 | 
| bindEditRow | JQuery表达式(tr对象) | 设置要修改的行信息(批量不弹窗修改使用) | 
| getJQueryTable | 无 | 返回JQuery对象table | 
| getJQueryHead | 无 | 返回JQuery对象thead | 
| getJQueryBody | 无 | 返回JQuery对象tbody | 
| getJQueryHeadTD | String - 字段标识 | 获取th对象集合 | 
| getJQueryBodyTD | String - 字段标识 | 获取td对象集合 | 
| getMaster | 无 | 返回主表grid对象 | 
| replaceHref | String - 列标识,String - 列样式,String-列内容 | 替换某列为链接 | 
| getTitleNames | 无 | 获取标题ID字符串拼接集合 | 
| resize | 无 | 重新调整布局宽度 | 
| colorFont | String - 列标识,String - 列值,String-颜色 | 通过值进行字体染色 | 
| colorBack | String - 列标识,String - 列值,String-颜色 | 通过值进行背景染色 | 
| 重要属性 | 属性说明 | 
|---|---|
| view | 参数配置信息 – Object | 
| page | 页面对象 – Object | 
| elementID | 对象标示 – String | 
| elementType | 对象类型 – String | 
| spinner | 蒙版对象 – TSpin | 
| dataSet | 页面数据集对象 – DataSet | 
| fields | 字段集合 – Array - Field | 
| title | 标题对象 - TSqlGridTitle | 
| body | 数据对象 – TSqlGridBody | 
| foot | 底部对象 – TSqlGridFoot | 
| menu | 右键菜单对象 - TMenu | 
| findform | 查询对象 - TForm | 
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _beforeEvent | 组件绑定动作前执行 | 
| _afterEvent | 组件绑定动作后执行 | 
| _onRowClickBefore | 行点击前执行 | 
| _onRowClickAfter | 行点击后执行 | 
| _onRowDbClickBefore | 行双击前执行 | 
| _onRowDbClickAfter | 行双击后执行 | 
| _beforeRefresh | 数据刷新前执行(注意该方法是异步执行) | 
| _afterRefresh | 数据刷新后执行(注意该方法是异步执行) | 
| _beforeInsert | 增加前执行,返回true或false,false时会阻断后续方法执行 | 
| _beforeEdit | 修改前执行,返回true或false,false时会阻断后续方法执行 | 
| _beforeDelete | 删除前执行,返回true或false,false时会阻断后续方法执行 | 
| _menu_show | 右键菜单显示前执行 | 
| _menu_shown | 右键菜单显示后执行 | 
| _menu_hide | 右键菜单关闭前执行 | 
| _menu_hiden | 右键菜单关闭后执行 | 
| _beforePastUrl | 获取明细地址弹窗前(参数1为地址,返回新地址信息) | 
| _beforeInsertUrl | 获取增加地址弹窗前(参数1为地址,返回新地址信息) | 
| _beforeEditUrl | 获取修改地址弹窗前(参数1为地址,返回新地址信息) | 
| _beforeSave | 保存前执行,返回true或false,false时会阻断后续方法执行 | 
| _afterSave | 保存后执行 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回组件的完整HTML信息 | 
| getHeadHtml | 无 | 获取组件头部HTML信息 | 
| getBodyHtml | 无 | 获取组件体部HTML信息 | 
| getFootHtml | 无 | 获取组件底部HTML信息 | 
| 重要属性 | 属性说明 | 
|---|---|
| view | 参数配置信息 – Object | 
| page | 页面对象 – Object | 
| elementID | 对象标示 – String | 
| elementType | 对象类型 – String | 
| panel | panel对象配置信息 | 
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _beforeEvent | 组件绑定动作前执行 | 
| _afterEvent | 组件绑定动作后执行 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回组件的完整HTML信息 | 
| instData | 无参数/dataSet | 加载数据到dataSet,不传入dataSet的情况从parent.dataSet获取 | 
| reset | 无 | 重置信息 | 
| value | 单参数/双参数 | 单参数的情况取值,双参数的情况赋值,对应HTML组件value值 | 
| text | 单参数/双参数 | 单参数的情况取值,双参数的情况赋值,对应HTML组件text值 | 
| html | 单参数/双参数 | 单参数的情况取值,双参数的情况赋值,对应HTML组件html信息 | 
| select | 单参数/双参数/三参数 | 单参数取值,双参数选择某个值,三参数添加某个选项,处理下拉框 | 
| multiSelect | 单参数/双参数 | 单参数取值,双参数选择值(多选模式) | 
| date | 双参数 | 单参数的情况取值,双参数的情况赋值,处理日期组件 | 
| disabled | 组件标示 | 设置组件不可用 | 
| enabled | 组件标示 | 设置组件可用 | 
| check | 单参数/双参数 | 单参数获取当前选中的值,双参数设置选中值(逗号分割),复选框使用 | 
| radio | 单参数/双参数 | 单参数的情况取值,双参数的情况赋值,单选框使用 | 
| progress | 单参数/双参数 | 单参数的情况取值,双参数的情况赋值,进度条使用 | 
| hide | 组件标示 | 隐藏某个组件 | 
| hideRow | 组件标识 | 隐藏组件所在行 | 
| show | 组件标识 | 显示某个组件 | 
| showRow | 组件标识 | 显示组件所在行 | 
| empty | 组件标识 | 彻底移除某个组件 | 
| clearSelect | 组件标识 | 移除某个下拉组件选择部分 | 
| getJQueryObject | 组件标识 | 获取内部组件的JQuery对象 | 
| getJQueryForm | 无 | 获取form的JQuery对象 | 
| cloneRow | 待复制组件标识,复制到组件标识(前) | 将 待复制组件 整个行复制到 另一个组件行前 | 
| getRow | 组件标识 | 获取组件所在行对应JQuery对象 | 
| nextRow | 组件标识 | 获取组件匹配行最后一行(搭配cloneRow使用) | 
| prevRow | 组件标识 | 获取组件匹配行第一行(搭配cloneRow使用) | 
| trigger | 组件标识/动作名称 | 触发内部组件的某个动作 | 
| fontColor | 组件标识/颜色信息 | 设置内部组件的字体颜色 | 
| backColor | 组件标识/颜色信息 | 设置内部组件的背景颜色 | 
| clearSelect | 组件标识 | 移除下拉组件中的全部选项 | 
| rela | 父标识/子标识/后台方法名 | 二级联动使用该方法,触发联动效果 | 
| change | 组件标识/动作方法 | 为组件绑定change方法 | 
| blur | 组件标识/动作方法 | 为组件绑定blur方法 | 
| click | 组件标识/动作方法 | 为组件绑定click方法 | 
| focus | 组件标识/动作方法 | 为组件绑定focus方法 | 
| unbind | 组件标识/动作类型 | 为组件移除已绑定的某个动作 | 
| bind | 组件标识/动作类型/动作方法 | 为组件绑定某个方法 | 
| 重要属性 | 属性说明 | 
|---|---|
| view | 参数配置信息 – Object | 
| page | 页面对象 – Object | 
| elementID | 对象标示 – String | 
| elementType | 对象类型 – String | 
| items | 所有成员组件 – Record | 
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _beforeEvent | 组件绑定动作前执行 | 
| _afterEvent | 组件绑定动作后执行 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| createRecord | 无 | 从this.dataset中创建记录并返回 – Record | 
| setFieldValue | 双参数String/Object | 为当前记录设置值 | 
| getJSON | 无 | 获取组装完成的JSON报文 | 
| getFieldValue | 单参数 | 获取当前记录中的值 | 
| getFirstRecord | 无 | 获取第一条记录信息 | 
| setAction | 双参数String/Object | 设置报文头信息 | 
| getAction | 单参数 | 获取报文头信息 | 
| setBody | 双参数String/Object | 设置报文体信息 | 
| getBody | 单参数 | 获取报文体信息 | 
| switchMessage | 单参数 | 传入后台方法名,前后台交互 | 
| switchMessageSyn | 后台方法名,after方法名,before方法名 | 传入后台方法名,异步前后台交互,不阻塞当前页面,可设置后续执行方法 | 
| getDataValue | 无 | 获取后台返回的字符串信息 | 
| switchWindow | 单参数 – Object | 弹窗 | 
| switchPage | 单参数 – URL | 地址访问 | 
| free | 无 | 释放缓存 | 
| setDataSet | 双参数/三参数 | 设置数据集当前记录字段信息 | 
| clearDataSet | 无 | 清空数据集 | 
| instData | 无 | 通过组件加载数据,需要组件实现加载方法(目前只有TTree、TSQLGrid支持) | 
| showData | 无 | 通过组件展示数据,需要组件实现展示方法(目前只有TTree、TSQLGrid支持) | 
| getDataSet | 无 | 获取页面选中的信息,以Table方式返回 | 
| getCheckDataSet | 无 | 获取页面多选的信息,以Table方式返回 | 
| getDataSetValue | 单参数 | 获取页面某个字段值 | 
| getDataSetOldValue | 单参数 | 获取页面某个字段原始值 | 
| refresh | 无参数 | 刷新父组件数据信息 | 
| setWhereField | 双参数 | 设置前后台交互需要值 | 
| getDataSetCount | 无参数 | 获取多选数据后,获取多选记录数 | 
| next | 无参数 | 遍历记录 | 
| getUrl | 无参数 | 获取完整URL信息 | 
| isEdit | 无参数 | 判断是否是修改状态 | 
| isInsert | 无参数 | 判断是否是增加状态 | 
| isDelete | 无参数 | 判断是否是删除状态 | 
| sql | 无参数 | 取消SQL与XSS过滤 | 
| isSuccess | 无参数 | 判断交易是否成功 | 
| createSpin | el匹配式 | 创建蒙版对象 | 
| spinIn | el匹配式 | 加载蒙版对象 | 
| spinOut | el匹配式 | 移除蒙版对象 | 
| spinShow | el匹配式 | 显示蒙版 | 
| spinHidden | el匹配式 | 隐藏蒙版 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回树的完整HTML信息 | 
| getZtree | 无参数 | 获取内部Tree对象 – 所有回调处理基本都来自这里 | 
| getCheckNodes | leaf/parent/all/层数 | 获取复选树节点集合 | 
| getCheckNodesIds | leaf/parent/all/层数 | 获取复选树节点标示集合 | 
| getCheckNodesCaptions | leaf/parent/all/层数 | 获取复选树节点标题集合 | 
| getCheckAllNodes | 无 | 获取所有选中节点 | 
| getUnCheckAllNodes | 无 | 获取所有未选中节点 | 
| getCheckLeafNodes | 无 | 获取所有选中叶节点 | 
| getCheckParentNodes | 无 | 获取所有选中父节点 | 
| getCheckLevelNode | 层数 | 获取所有选中某层节点 | 
| getRadioAllNodes | 无 | 获取所有单选节点 | 
| getUnRadioAllNodes | 无 | 获取所有未单选节点 | 
| getChildNodes | 无 | 获取当前节点所有子节点集合 Array - Node | 
| refresh | 树标识,节点对象 | 刷新节点下数据 | 
| refreshParent | 树标识,节点对象 | 刷新父节点下数据 | 
| resetParentNode | 无 | 重置父节点 | 
| getRootNode | 无 | 获取根节点对象 | 
| clickNode | 节点对象 | 触发点击节点事件 | 
| clickChildNode | 节点对象 | 触发点击第一个子节点事件 | 
| 重要属性 | 属性说明 | 
|---|---|
| view | 参数配置信息 – Object | 
| page | 页面对象 – Object | 
| elementID | 对象标示 – String | 
| elementType | 对象类型 – String | 
| dataSet | 树数据集合 - DataSet | 
| treeId | 树组合标示 | 
| treeNode | 树当前节点 – 基本所有的方法都依赖该对象 – treeNode | 
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _page | 树关联Edit页面回调 | 
| _show | 树按钮展示回调 | 
| _onclick | 树点击方法回调 | 
| _beforeRefresh | 刷新前之前 | 
| _afterRefresh | 刷新后执行 | 
| _beforeCheck | 复选前执行 | 
| _afterCheck | 复选后执行 | 
| _afterAdd | 增加后执行 | 
| _beforeEdit | 修改前执行,返回true或false,false时会阻断后续方法执行 | 
| _afterEdit | 修改后执行 | 
| _beforeDelete | 删除前执行,返回true或false,false时会阻断后续方法执行 | 
| _afterDelete | 删除后执行 | 
| treeNode | 属性说明 | 
|---|---|
| nodeID | 节点标示 | 
| nodeName | 节点名称 | 
| dataSet | dataSet[fieldName].fieldvalue / dataSet[fieldName].fieldoldvalue | 
| level | 层数 | 
| checked | 是否勾选 | 
| children | 子节点数组 | 
| chkDisabled | 复选与单选是否禁用 | 
| getIndex() | 获取同级节点索引 | 
| getNextNode() | 获取临近的后一个节点 | 
| getParentNode() | 获取父节点 | 
| getPath() | 获取 treeNode 节点的所有父节点(包括自己)。 | 
| getPreNode() | 获取与 treeNode 节点相邻的前一个节点。 | 
| halfCheck | 半勾选状态 | 
| icon | 图标路径 | 
| iconClose | 父节点自定义折叠时图标的 URL 路径。 | 
| iconOpen | 父节点自定义展开时图标的 URL 路径。 | 
| iconSkin | 节点自定义图标的 className | 
| isHidden | 判断 treeNode 节点是否被隐藏。 | 
| isParent | 记录 treeNode 节点是否为父节点 | 
| nocheck | 设置节点是否隐藏 checkbox / radio | 
| open | 记录 treeNode 节点的 展开 / 折叠 状态。 | 
| editNameFlag | 用于记录节点是否处于编辑名称状态。 | 
| isAjaxing | 记录 treeNode 节点是否正在进行异步加载。 | 
| isFirstNode | 记录 treeNode 节点是否为同级节点中的第一个节点。 | 
| isHover | 记录节点 的 hover 状态 | 
| isLastNode | 记录 treeNode 节点是否为同级节点中的最后一个节点。 | 
| parentTId | treeNode 节点的父节点唯一标识 tId。 | 
| tId | treeNode 节点的唯一标识 tId。 | 
| zAsync | 记录 treeNode 节点是否已经进行过异步加载,避免父节点反复异步加载数据。 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回组件的完整HTML信息 | 
| refresh | 无参数 | 数据刷新 | 
| getCheckCells | 无 | 获取所有选中单元格 Array - TMultiCell | 
| 重要属性 | 属性说明 | 
|---|---|
| view | 参数配置信息 – Object | 
| page | 页面对象 – Object | 
| elementID | 对象标示 – String | 
| elementType | 对象类型 – String | 
| dataSet | 数据集对象 – DataSet | 
| spinner | 蒙版对象 – Spin | 
| title | 标题对象 – TMultiGridNav | 
| body | 数据对象 - TMultiGridBody | 
| menu | 通用顶部菜单 - TMenu | 
| titleMenu | 标题部分右键菜单 - TMenu | 
| dataMenu | 数据部分右键菜单 - TMenu | 
| tableMenu | 全表格通用右键菜单 - TMenu | 
| rightMenu | 活动右键菜单 - TMenu | 
| foot | 表格底部分页组件 - TFoot | 
| findform | 表格查询组件 - TForm | 
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _beforeEvent | 组件绑定动作前执行 | 
| _afterEvent | 组件绑定动作后执行 | 
| _menu_show | 右键菜单显示前执行 | 
| _menu_shown | 右键菜单显示后执行 | 
| _menu_hide | 右键菜单关闭前执行 | 
| _menu_hiden | 右键菜单关闭后执行 | 
| TMultiCell | 方法说明 | 
|---|---|
| cell.getFieldName | 获取字段标示 | 
| cell.getFieldName | 获取字段标示 | 
| cell.getFieldValue | 获取字段值 | 
| cell.getColNo | 获取列号 | 
| cell.getRowNo | 获取行号 | 
| cell.getRowSpan | 获取跨行数 | 
| cell.getColSpan | 获取跨列数 | 
| cell.getActiveCell | 获取当前单元格的JQuery对象 | 
| cell.isTitle | 是否为标题 | 
| cell.isData | 是否为数据 | 
| cell.getRowCells | 获取所在行所有单元格,返回Array-Cell | 
| cell.getRowDataCells | 获取所在行所有数据单元格,返回Array-Cell | 
| cell.getRowTitleCells | 获取所在行所有标题单元格,返回Array-Cell | 
| cell.getColCells | 获取所在列所有单元格,返回Array-Cell | 
| cell.getColTitleCells | 获取所在列所有标题单元格,返回Array-Cell | 
| cell.getColDataCells | 获取所在列所有数据单元格,返回Array-Cell | 
| cell.getActiveCell | 获取活动单元格的JQuery对象 | 
| cell.getHidden | 获取隐藏信息 | 
该对象为新增对象,新上传组件使用该对象,因为文件上传组件支持的回调方法比较多而且较复杂,这里补充一下APi
| 回调方法 | 方法说明 | 
|---|---|
| _load | 组件加载完成后执行 | 
| _beforeEvent | 组件绑定动作前执行 | 
| _afterEvent | 组件绑定动作后执行 | 
| _select | 文件选中后执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _imageLoad | 图片加载后执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _imagesLoad | 全部图片加载后执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _batchselect | 批量选中后执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _remove | 文件移除前执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _removed | 文件移除后执行(5参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _clear | 文件清空前执行(3参数 - 第一参数为父对象,第二参数为page,第3参数为对象本身) | 
| _success | 上传成功后执行(4参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _error | 上传失败后执行(4参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _before | 上传调用后台前执行(4参数 - 第一参数为父对象,第二参数为page,其他建议console) | 
| _extraData | 补充额外传参信息(3参数 - 第一参数为父对象,第二参数为page,第3参数为config),方法需要返回{key1:val1,key2:val2....} 格式数据 | 
| _extra | 补充额外传参信息,与_extraData类似,但无参数,属于完全覆盖模式 | 
| _config | 调整config配置信息(4参数 - 第一参数为父对象,第二参数为page,第3参数为config) | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| getHtml | 无 | 返回整个页面完整HTML | 
| show | 无参数 | 展示页面 | 
| addComponet | Object | 绑定组件 | 
| find | 组件标示 | 查询组件 | 
| layout | 无参数 | 布局页面 | 
| resize | 无参数 | 重新按照窗口宽度适配适配组件宽度 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| clone | 对象 | 克隆对象 | 
| fireUserEvent | 方法名/参数数组 | 触发页面中的方法,传入参数信息 | 
| isfireuserEvent | 方法名 | 判断页面中是否存在某个JS方法 | 
| getInt | 字符参数 | 转换参数为Int值 | 
| getFloat | 字符参数 | 转换参数为Double值 | 
| isTrue | 参数值 | 转换参数为Boolean值 | 
| isEmpty | 对象 | 判断参数是否为空 | 
| isArray | 对象 | 判断参数是否为数组 | 
| isDate | 对象 | 判断参数是否为Date类型 | 
| isObject | 对象 | 判断参数是否为Object | 
| isFunction | 对象 | 判断参数是否为Function | 
| isNumber | 对象 | 判断参数是否为数字 | 
| isString | 对象 | 判断参数是否为String | 
| isBoolean | 对象 | 判断参数是否为布尔值 | 
| isElement | 对象 | 判断参数是否为DOM对象 | 
| isDefined | 对象 | 判断参数是否定义 | 
| isNull | 对象 | 判断参数是否为Null | 
| getContextPath | 无参数 | 获取项目工程路径。例如:/lily | 
| getRootPath | 无参数 | 获取项目访问地址。例如:http://127.0.0.1:7001/lily | 
| getLocationPath | 无参数 | 获取工程地址。例如:http://127.0.0.1:7001/ | 
| getRelaPath | 无参数 | 获取页面相对地址。例如:/UI/xxx.jsp | 
| verifySSL | 无参数 | 判断地址是否为https | 
| stripDefend | 字符参数 | 处理普通注入 | 
| stripSQL | 字符参数 | 处理SQL注入 | 
| splitUrl | 地址字符串 | 拆分地址参数,返回参数对象,{key:val.....} | 
| getUrlParam | 单参数或双参数 | 单参数,获取当前地址中的参数值,双参数,获取参数中地址的参数值 | 
| plusUrl | 地址字符串 | 将当前页面地址中参数信息补充到传入地址中并进行返回 | 
| showModal | 对象 | 弹窗处理,参考弹窗机制 | 
| addUrlParam | 参数名/参数值/地址 | 为地址添加参数,返回添加后的地址 | 
| callBack | 方法名/方法参数 | 回调调用某个方法 | 
| upload | 标识/回调 | 通用上传 | 
| download | 文件路径 | 通用下载 | 
| easyDownload | 对象 | 简单下载(参考入门手册-简单下载) | 
| sleep | 毫秒值 | 当前线程强制沉睡一定时间 | 
| getNowFormatDateTime | 无参数 | 格式化当前时间戳信息返回 | 
| getNowFormatDate | 无参数 | 格式化当前日期信息返回 | 
| getNowFormatTime | 无参数 | 格式化当前时间信息返回 | 
| addToken | url地址 | 在参数地址中补充token信息返回 | 
| throwException | 字符信息 | 抛出异常信息 | 
| addOKE | url地址 | 在参数地址中补充防交易重放信息返回 | 
| updateToken | 无参数 | 更新token信息 | 
| unCry | String | 解密信息返回 | 
| cry | String | 加密信息返回 | 
| randomNum | min,max | 生成从min到max的随机数 | 
| randKey | int | 生成n位随机信息(0-9A-Z) | 
| randHex | int | 生成n位随机Hex信息(0-F) | 
| getFormatDate | String | 根据日期格式获取当前格式化后日期(yyyy-mm-dd hh:ii:ss) | 
| getJson | url | 直接get调用地址获取返回String信息 | 
| getJsonStr | url | 直接get调用地址获取返回JsonObject信息 | 
| getMsTimes | String | 获取当前毫秒值信息 | 
| asciiToHex | String | ASCII 转换 Hex | 
| hexToAscii | String | Hex 转换 ASCII | 
| changeMenu | String(menuID) | 切换当前页面菜单(引导模式无效) | 
| cliceMenu | String(menuID) | 强行触发菜单点击事件 | 
| getRandomID | 无参数 | 获取唯一标识 | 
| clickAppHome | String(appID) | 强行切换应用 | 
| rekillEnter | el匹配 | 强行阻断el匹配区域的回车事件 | 
| rekillKeyDown | el匹配 | 强行阻断el匹配区域的键盘事件 | 
| pointBaiduMap | 对象 | 百度地图调用(参考入门手册-地图部分) | 
| pointTencentMap | 对象 | 腾讯地图调用(参考入门手册-地图部分) | 
| pointGeoMap | 对象 | 高德地图调用(参考入门手册-地图部分) | 
| pointMap | 对象 | 地图调用(参考入门手册-地图部分) | 
| layout | page | 重新布局page内组件 | 
| resize | page | 重新适配page内组件宽高 | 
| showCode | config对象 | 预览代码 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| contains | 字符信息 | 是否包含某个字符 | 
| startsWith | 字符信息 | 是否以某个字符开始 | 
| endWith | 字符信息 | 是否以某个字符结束 | 
| trim | 无 | 清理前后空格 | 
| ltrim | 无 | 清理左空格 | 
| rtrim | 无 | 清理右空格 | 
| isTrue | 无 | 转换Boolean值 | 
| replaceNewLineChars | 字符信息 | 替换换行符为参数字符 | 
| getReplace | 待替换字符,替换字符 | 替换匹配信息返回 | 
| getInt | 无 | 转换Int值 | 
| getFloat | 无 | 转换Float值 | 
| replaceAll | 双参数 | 替换选中信息 | 
| isEqual | 字符信息 | 比较是否相同 | 
| fillLZero | int | 左补0到参数长度 | 
| cut | String | 从第一匹配位置开始向后截取到最后 | 
| toHex | 无 | 转换为Hex格式 | 
| toAscii | 无 | 转换为Ascii格式 | 
| encode | 无 | 加码信息,回避中文 | 
| decode | 无 | 解码信息 | 
| cry | 无/boolean | 加密信息 | 
| unCry | 无/boolean | 解密信息 | 
| psignJson | 无 | 补充签名信息,JSON字符串 | 
| gsignJson | 无 | 获取签名信息,JSON字符串 | 
| vsignJson | 无 | 验证签名信息,JSON字符串 | 
| psignUrl | 无 | 补充签名信息,URL签名处理 | 
| gsignUrl | 无 | 获取签名信息,URL签名处理 | 
| vsignUrl | 无 | 验证签名信息,URL签名处理 | 
| b64ToHex | 无 | Base64转换Hex | 
| hexToB64 | 无 | Hex转换Base64 | 
| 方法名 | 参数 | 说明 | 
|---|---|---|
| max | 无 | 获取数组最大值 | 
| min | 无 | 获取数组最小值 | 
| add | 对象 | 添加对象到数组 | 
| get | 索引值 | 获取对象 | 
| indexOf | 对象 | 获取对象索引 | 
| isExit | 对象 | 判断对象是否存在 | 
| remove | 对象/索引值 | 移除对象 | 
| size | 无 | 获取数组长度 | 
| clear | 无 | 清空数组 | 
在平台中,地址传参是非常常见的参数传输方式,在页面中经常使用,传参的方式,只需要在地址中拼接需要的参数即可。需要注意,这种传参方式前台与后台都是可以取值的。
例如地址为:http://127.0.0.1:7001/lily/managerService?aa=111&bb=222
后台取值方式
this.getAttr("aa")                             //pageOnload中使用
this.getViewTable().getAttributeValue("aa")    //pageOnload之外的其他方法中使用
前台取值方式
page.element.parameter.[参数标识]
上面的处理为:page.element.parameter.aa
在平台中,自定义弹窗的时候往往会涉及参数传递的问题,这种情况的传参首先需要区分参数传输的来源。
主页面传值窗口页面
这种传参方式,一般使用的方式有2种,第一种就是地址传参,也是最常用的方式,地址传参与之前章节介绍的方式完全相同。
还有一种处理方式,是无参数处理模式,这个模式一般用于前端需要取值的情况,从前端,我们可以直接拿到父页面的对象进行取值,首先,默认的方法中page为当前窗口page对象,通过page.parent可以获取到父页面对象,后面就可以使用page.find来定位父页面对象,进而获取需要的值信息。这个模式在很多不希望参数保留在通讯中的情况也会使用。
例如这种情况,主页面列表弹窗,窗口中要获取主页面选中记录的某个值,可以使用类似下面的方式
page.parent.find([grid标识]).getFieldValue([字段标识]);
窗口页面返回值给主页面
需要从窗口页面返回值到主页面,一般我们会使用page.windows进行返回。
子页面中一般使用page.windows.returnValue=[需要返回的值]进行返回。
窗口示例
function save_click(panel,page){
	page.windows.returnValue='xxxxx';
	page.windows.closeWindow();  //关闭弹窗
}
主页面中,我们可以在关闭窗口的回调方法中获取该值。
主页面示例
function show_click(grid,page){
	Yacon.showModal({
		url:"xx.jsp",
		width:"80%",
		height:"90%",
		close:"show_window_close"  //弹窗关闭方法
	});
}
function show_window_close(windows,page){
	var ret = windows.returnValue;  //接收返回值
	
	//非空返回处理
	if(!Yacon.isEmpty(ret)){  
	
	}
	
}
关于前后台传参的处理,这里稍微复杂,需要区分多种情况。
前台传后台(普通字段)
首先介绍的是前台传后台,传输普通的字符信息。首先是前端写法。
var sendSet = new Yacon.data.DataSet(page);
sendSet.setWhereField("aa","测试值");
sendSet.setWhereField("bb","测试值");
sendSet.switchMessage("auth");
所有需要前后台交互的位置都需要使用DataSet对象,通过dataSet.switchMessage调用后台方法。前端可以通过调用setWhereField(key,value)的方式将需要的值送到后台。
后台取值的方式如下:
public void auth(){
	
	if(this.getViewTable().next()){
	
		String aa = this.getViewTable().getString("aa");
		String bb = this.getViewTable().getString("bb");
	
	}
}
需要注意必须在next判断中取值,取值的方式使用this.getViewTable.getString。
前台传后台(数组信息)
下面要介绍的是前台传后台,传数组的处理方式。也就是传输多条记录的处理。处理方式如下:
var sendSet = new Yacon.data.DataSet(page);
//第一条记录值
sendSet.createRecord();
sendSet.setWhereField("aa","测试值");
sendSet.setWhereField("bb","测试值");
//第二条记录值
sendSet.createRecord();
sendSet.setWhereField("aa","测试值2");
sendSet.setWhereField("bb","测试值2");
.....
sendSet.switchMessage("auth");
每次调用createRecord都会创建一个新的record对象,之后通过setWhereField可以直接赋值。
后台取值的方式如下:
public void auth(){
	
	while(this.getViewTable().next()){
	
		String aa = this.getViewTable().getString("aa");
		String bb = this.getViewTable().getString("bb");
	
	}
}
取值与普通取值只是判断的方式变成了while遍历的方式。
通过这种方式处理的时候,需要注意一点,前端向后台送值的情况,每条记录中的字段key数量与名称必须完全一致,不允许出现字段差异,这点非常关键。
后台返前台(普通字符)
后台返回前台的处理,有多种返回方式,通用的字符返回有下面几种方式
this.setSelfMessage(str); //返回固定字符串到前端
this.setSuccess(str);     //设置成功返回信息,前端会自动弹窗
this.setError(str);       //设置失败返回信息,前端自动弹窗(一般不用,因为不会阻断代码执行)
throw new YaconException(str);   //设置失败返回
前端获取的方式一般如下
if(sendSet.isSuccess()){       //判断通讯执行是否成功
	sendSet.getDataValue();    //获取到返回的字符信息
}
因为前端是基于DataSet对象调用后台的,可以通过isSuccess判断是否通讯成功,之后可以通过getDataValue()获取后台普通返回字符信息。
后台返前台(数组)
后台返回数据到前端,我们之前实际也已经接触过了,在Tree返回的时候就是这种方式。
我们可以通过this.setDataTable把一个数组返回到前端。
一般后台构造数组的方式有2种:
方式1:基于SQL定义
String sql = "select * from xxxx";
DataTable table = DBFactory.getActionDB().executeQueryTable(sql);
this.setDataTable(table);
方式2:自定义Table
DataTable table = new DataTable("table");
//使用DataTable在添加数据前必须通过createColumn声明列信息
//一旦开始添加数据,列结构不可改变
table.createColumn("COL1");
table.createColumn("COL2");
//添加数据
Record record = table.createRecord();
record.setFieldValue("COL1","val1");
record.setFieldValue("COL2","val2");
record = table.createRecord();
record.setFieldValue("COL1","vala");
record.setFieldValue("COL2","valb");
this.setDataTable(table);
自定义table返回,我们就必须说明一下table的结构,平台中Table-Record-Field是一个基础数据结构。Table定义为表格结构,Record是table中的子对象,类似于表格中的记录信息,Field是Record的子对象,类似与记录中的每一列字段信息,Field的内部结构类似Map,存储的是列的标识和列的值。
Table有很多重写对象,DataTable就是页面最常用的Table对象。DataTable与一般的Table,最大的不同是DataTable中有列定义的处理,在使用DataTable的时候,必须声明Table的列(也就是Table的结构),之后必须按照结构存储数据,在存储数据的过程中,不允许修改结构。
通过createColumn可以创建列,通过createRecord可以创建列对象,使用返回的record可以调用setFieldValue方法自动创建字段对象并赋值。
最后通过setDataTable返回DataTable对象。
前端遍历后台值处理的方式如下:
while(sendSet.next()){
	sendSet.getDataSetValue("COL1");
	sendSet.getDataSetValue("COL2");
}
前端通过dataSet.next遍历数据,通过getDataSetValue获取每列值。
对于默认增改删的路径处理,有时候,我们希望传参到子页面中,但是因为路径是在后台写死的,要处理参数处理就需要考虑处理方案。
下面是常见的几种情况。
传参当前地址中的值到子页面
将当前地址中的值传参到子页面中,这种方式实际非常简单,我只需要拼接子窗口的地址参数即可。
例如:
bookGrid.detailPage().setPagePath("table1Edit.jsp?aa="+this.getAttr("aa"));
之后,子页面中只需要使用this.getAttr取值即可。
当前表格选中记录中的信息
将当前表格中选中值传参到子页面,这个也非常简单,调用内置方法addParameter即可,例如
bookGrid.detailPage().setPagePath("table1Edit.jsp").addParameter("BOOK_ID", "BOOK_ID");
注意这个方法使用的时候,第一个参数是表格中的字段标识,第二个参数是地址中的参数标识,一般习惯同名,之后在子页面中使用this.getAttr取值即可。
主表选中记录值传递从表弹窗路径
处理方式也非常简单,只需要在调用内置方法的时候,补充固定前缀$即可,例如
bookGrid.detailPage().setPagePath("table1Edit.jsp").addParameter("BOOK_ID", "$BOOK_ID");
子页面中依然使用this.getAttr取值即可。
主表复选记录值传递从表路径(3+支持)
处理方式与之前类似,在调用内置方法的时候,补充固定前端@即可,例如
bookGrid.detailPage().setPagePath("table1Edit.jsp").addParameter("BOOK_ID", "@BOOK_ID");
子页面中依然使用this.getAttr取值即可。
回调个性化处理参数
关于弹窗内置地址参数,实际上还有2个回调方法,可以用于我们自己个性化处理参数。
[grid标识]_beforeInsertUrl(path,page)    //增加个性化参数处理,参数为path,需要返回最终path
[grid标识]_beforeEditUrl(path,page)      //修改个性化参数处理,参数为path,需要返回最终path
例如:
function bookGrid_beforeInsertUrl(path,page){
	path = Yacon.addUrlParam(path,"key","val");    //平台封装号的补充参数方法,回避&与?问题。
	return path;
}
关于主从表参数的处理,在之前我们已经接触过,下面进行一下整理。
从表定义处理
borrowGrid.masterTable().setMasterID("bookGrid").addForeignField("BOOK_ID", "BOOK_ID")
通过addForeignField定义关联字段,之后,可以在其他位置使用 this.getViewTable.getAttibuteValue取值。
表格取值
表格取值处理,在前端中非常常见,一般取值需要区分获取选中记录中的某个字段与获取复选记录信息2种处理方式。
获取当前选中记录中的信息比较简单,使用下面的方式即可
grid.getFieldValue([字段标识]);
如果要获取复选记录的信息,使用下面的方法即可
grid.getCheckFieldValue([字段标识]);
这个方法将会把获取的信息使用逗号分隔后组合成字符串返回。
需要注意的是,上面获取复选记录的方法,在3以下的平台中可能没有,对于旧版本,一般获取复选记录的方法如下:
grid.getCheckDataSet();   //打包复选记录
if(grid.getRecordCount() <1){    //获取复选记录总素
    alert("请至少选择一条小说信息!");
    return ;
}
var novID = "";
while(grid.next()){    //遍历记录
	novID += "," + grid.getFieldValue("NOV_ID");  //拼接字段
}
var novIDs = novID.substring(1);  //去掉开始逗号
前端赋值
目前表格赋值只支持修改表格中选中记录中的某个字段值,使用的方法如下
grid.setFieldValue([字段标识],[字段值]);
表单取值与赋值是页面处理经常的操作,从页面定位到form后,我们可以通过form.value进行通用取值与赋值处理。
form.value("CAN_IN")   //取值操作
form.value("CAN_IN","inval")  //赋值操作
form.value可以处理大部分表单组件的读取值处理,这个方法有很多变种方法,用于处理不同组件的内容。
form.text     //处理组件text值,一般TLabel组件使用该方法取值与赋值
form.html     //处理组件html信息,与innerHtml处理相同
form.select   //下拉组件专用,1个参数取值,2个参数选中某个值,3个参数追加下拉选项
form.multiSelect  //下拉多选组件使用,1个参数取值,2个参数选中值
form.check    //处理复选组件,1个参数取值,2个参数选中值
form.radio    //处理单选组件,1个参数取值,2个参数选中值
form.data     //处理下拉日期组件,1个参数取值,2个参数赋值
树的常见取值,一般都是基于树中节点对象treeNode对象进行处理。
常见的取值方法是:
tree.treeNode  //获取当前获取节点对象
tree.treeNode.nodeID   //获取选中节点标识
tree.treeNode.level    //获取当前节点层级
tree.treeNode.nodeName //获取选中节点展示名称
tree.treeNode.dataSet[字段标识].fieldvalue  //获取查询节点中的其他参数信息
在之前窗口传参的时候,实际已经提到过跨页面取值的处理,这里需要进一步强调一下。
一般我们跨页面取值的时候,大多数都会通过地址传参将需要的信息传递到子页面中,实际上,从平台2+版本后,跨页面取值的方式也可以直接通过由子页面定位父页面对象后取值。使用这种模式需要注意,如果是后台直接取值的情况不适用。
获取当前页面的父页面的方法是page.parent,在之前窗口传参的时候我们就已经使用过了,通过page.parent.find定义到需要的对象后进一步取值。但是窗口传参只是一个狭义范围的跨页面情况,实际上,跨页面取值的情况一般出现在下面几种情况
树+表页面:表页面为子页面,树页面为父页面
基于TFrame或TOutFrame嵌套页面的情况:嵌套的页面为子页面
弹窗页面:弹窗页面为子页面
标签页:定义标签的页面为父页面,标签关联页面为子页面
在上面几种情况中,都可以使用跨页面取值的方式。
使用跨页面取值的是,一定注意2点,
第一点是父页面的层级,根据层级定义parent调用的次数,例如page.parent.parent.find。最容易出问题的情况是弹窗树+表,从表页面定位主页面中间是2层parent(树单独一层parent),这点一定注意。
第二点是前端page对象对应后台某个基础BusinessPage的页面对象,这种对应关系请一定注意,在使用page的时候,在不确定page对应的情况下,尽量console.log先输出一下page对象进行确认后再取值,很多情况无法获取对象取值的问题都出在这里。
异步处理在内管中后期实际是比较常见的处理,因为内管页面的交互是有时间限制的,超过2min的交易会出现超时问题,在很多项目后期,操作较大数据量的情况很容易出现这种情况,为了避免,很多时候,会将耗时长的交易在后台另开一个线程异步处理,优先给前端返回一个过程中结果。
异步处理的模式之一是后台单开线程进行异步处理,这种处理模式比较常见,在新主题中单独封装了OwnerPageThread类来处理这种情况。下面是具体例子
	public void excute() {
		
		try {
			
			OwnerPageThread thread = new OwnerPageThread() {
				
				@Override
				public void excute() throws Exception {
					
					//具体业务处理。。。。
					
				}
			};
			
			thread.start();
			
			this.setSuccess("交易正在处理中,请稍等片刻....");
			
		} catch (Exception e) {
			
			EnvLog.error(e);
			
			this.setError("交易失败");
			
		}
		
	}
使用OwerPageThread做内部类处理,构造的时候会要求实现excute方法,方法中处理具体业务即可(上层已经封装了数据库的事务控制与日志环境处理,可以不需要自己处理)。
需要注意2点,第一点是由于这个位置使用的是内部类处理,所以,未来会编程成一个子类,名称为类名$XX.class的形式,子类中才是内部类的实现,单独部署的时候千万不要忘了。
第2点是如果需要想线程中传递参数,可以在类中添加属性与set方法,构造完成后set进去。如果需要的信息只是为了在线程中读取使用,可以将外部定义的变量设置为final常量后就可以在子类中直接使用(一般String的话,定义位final就可以直接使用了,Table、Record之类的对象就需要赋值处理了)。
强调:只读不改的参数一定设置final在内部调用(从jdk8开始可以不设置final进行读取,但是为了兼容之前的写法,请设置final防止出现问题)。
单独传参的处理在这里提供一个示例,请一定注意调用位置。
	public void excute() {
		
		if(this.getViewTable().next()) {
			
			Record record = new Record();
			
			OwnerPageThread thead = new OwnerPageThread() {
				
				private Record src;
				
				public OwnerPageThread setSrc(Record record) {
					this.src = record;
					return this;
				}
							
				@Override
				public void excute() throws Exception {
					
					
				}
				
			}.setSrc(record);
					
			thead.start();
			
			this.setSuccess("正在处理中,请稍后");
			
		}
通讯异步与之前的异步有些不同,一般我们使用ajax进行前后台交互,这种模式会锁住整个页面的渲染(也就是卡死页面),通讯结束才能正常操作页面。例如很多时候,我们添加一个单独过渡页面,希望在调用前触发显示,在调用后触发隐藏,但是因为页面渲染被阻断,实际就出现了调用后才整个触发显示和隐藏的情况。通讯异步只是让通讯不会锁屏页面,便于在通讯中仍然可以对页面进行渲染和动作类的操作,这种操作常见于单独做过渡页面的处理与定时刷新页面的情况(实际项目中使用不多,只在特定情况使用,这里相对一提)。
内管进行前后台交互的方法是dataSet.switchMessage方法,这个方法调用后台某个方法,默认模式是阻断执行的,而与之类似的一个方法是dataSet.switchMessageSyn方法,这个方法调用方法采用的是异步处理,不会阻断页面执行,但后续执行的操作需要在单独的方法中调用(因为不阻断方法执行,所以调用位置一般在某个方法最后)。类似下面的方式
	function bookGrid_afterEvent(grid,page){
		var sendSet = new Yacon.data.DataSet(page);
		sendSet.switchMessageSyn("xxxx","xxxxAfter");
	}
	
	function xxxxAfter(sendSet,page){
		alert("11111111");
	}
调用的时候,第一个参数为后台方法名,第二个参数为后续执行方法名,后续方法包含2个参数,参数1为dataSet对象本身,参数2为page对象。
这种调用的具体使用希望大家根据实际情况使用。
这部分我们开始介绍个性化数据表的处理,在使用TSqlGrid的时候,有时候,我们希望自己设置表格的数据内容,这个章节我们先介绍一种简单的实现方式,基于已经存在的数据进行设置。下面是处理示例。
    @Override
    public void page_onLoad() throws YaconException {
        
        TSqlGrid grid = sqlGrid("datasGrid");
        //处理方式
        //利用sql构造,补全全部字段,后续信息更新
        
        String sql = " select BOOK_NAM,BOOK_AUTHOR,BOOK_PUB,1 as PLUS_A,1 as PLUS_B "
                   + " from ${schema}.PK_BOOK_INFO "; 
        
        DataTable table = DBFactory.getActionDB().executePageTable(sql, this.getPageIndex(), this.getPageSize());
        
        int idx = 10;
        while(table.next()) {
            table.getRecord().setFieldText("PLUS_A", idx+"");
            table.getRecord().setFieldValue("PLUS_A", idx+"");
            table.getRecord().setFieldText("PLUS_B", idx+""+idx);
            table.getRecord().setFieldValue("PLUS_B", idx+""+idx);
            idx++;
        }
        table.beforeFirst(); //置会首行
        
        grid.setDataTable(table);
        
        grid.setEdit(true);
        //默认不设置的情况下使用 save 与 refresh 处理
        grid.setExcuteMethod("save");
        grid.setRefreshMethod("refresh");
        
        grid.createFieldTitle("BOOK_NAM").setFieldAlign("center").setDataAlign("center").setFieldText("图书名称");; //图书名称
        grid.createFieldTitle("BOOK_AUTHOR").setFieldAlign("center").setDataAlign("center").setFieldText("图书作者"); //图书作者
        grid.createFieldTitle("BOOK_PUB").setFieldAlign("center").setDataAlign("center").setFieldText("图书出版社"); //图书出版社
        grid.createFieldTitle("PLUS_A").setFieldAlign("center").setDataAlign("center").setFieldText("补充信息A");
        grid.createFieldTitle("PLUS_B").setFieldAlign("center").setDataAlign("center").setFieldText("补充信息B");
        
        grid.detailPage().setPagePath("table1Edit.jsp");
        
        TButtonPilot gridPilot = ButtonPilot("gridPilot");
        gridPilot.setPilotIco("glyphicon glyphicon-th");
        gridPilot.setPilotText("图书信息表");
        
        grid.setButtonPilot(gridPilot);
        
        grid.setTopButtonType("all");
        getPage().addElement(grid);
    }
    
    public void refresh() {
        
        TSqlGrid grid = sqlGrid("datasGrid");
        //处理方式
        //利用sql构造,补全全部字段,后续信息更新
        
        String sql = " select BOOK_NAM,BOOK_AUTHOR,BOOK_PUB,1 as PLUS_A,1 as PLUS_B "
                   + " from ${schema}.PK_BOOK_INFO "; 
        
        DataTable table = DBFactory.getActionDB().executePageTable(sql, this.getPageIndex(), this.getPageSize());
        
        int idx = 10;
        while(table.next()) {
            table.getRecord().setFieldText("PLUS_A", idx+"");
            table.getRecord().setFieldValue("PLUS_A", idx+"");
            table.getRecord().setFieldText("PLUS_B", idx+""+idx);
            table.getRecord().setFieldValue("PLUS_B", idx+""+idx);
            idx++;
        }
        table.beforeFirst(); //置会首行
        
        grid.setDataTable(table);
        
        this.setSelfMessage(grid.getDataElement());
        
    }
设置个性化数据,本质上是通过TSqlGrid的setDataTable设置自定义数据集合。
需要考虑的问题,是如何构造DataTable。
上面的实现方法比较简单,首先通过一个sql来构造table,缺少的字段在查询中进行占位处理,这样我们就可以查询出一个需要的集合。通过 getPageIndex 和 getPageSize 获取当前页号和每页记录数。通过 executePageTable自动进行分页查询。
下面我们根据需要,遍历table将之前占位的信息填充我们需要的结果即可。
更新的处理需要考虑 setFieldText 和 setFieldValue 分别设置值信息和展示信息(非常关键)
更新信息的方法可以使用多种方式,一般最常见的方式是基于Map先缓存数据,然后逐步更新处理。
上面的结果呈现如下

基于上面的实现方式比较简单,不需要自定义DataTable,只是在原有的sql基础上进行字段扩展,一般,用来处理拆分连接查询sql处理的时候经常使用这种方式。
这部分介绍完全自定义DataTable填充TSqlGrid的处理方式。
首先来看一下示例
    public void page_onLoad() throws YaconException {
        
        TSqlGrid grid = sqlGrid("datasGrid");
        //处理方式
        DataTable table = new DataTable("table");
        
        table.createColumn("COLUMN1");
        table.createColumn("COLUMN2");
        
        int pageNo = this.getPageIndex();
        int pageSize = this.getPageSize();
        
        for(int i=(pageNo-1)*pageSize;i<(pageNo-1)*pageSize+pageSize;i++) {
            
            Record record = table.createRecord();
            record.setFieldValue("COLUMN1", i+"");
            record.setFieldValue("COLUMN2", i+i+"");
        }
        
        grid.setDataTable(table);
        
        table.setDbRecordCount(500); //总记录数
        
        grid.setEdit(true);
        //默认不设置的情况下使用 save 与 refresh 处理
        grid.setExcuteMethod("save");
        grid.setRefreshMethod("refresh");
        
        grid.createFieldTitle("COLUMN1").setFieldAlign("center").setDataAlign("center").setFieldText("扩展列1"); 
        grid.createFieldTitle("COLUMN2").setFieldAlign("center").setDataAlign("center").setFieldText("扩展列2"); 
        
        grid.detailPage().setPagePath("table1Edit.jsp");
        
        TButtonPilot gridPilot = ButtonPilot("gridPilot");
        gridPilot.setPilotIco("glyphicon glyphicon-th");
        gridPilot.setPilotText("图书信息表");
        
        grid.setButtonPilot(gridPilot);
        
        grid.setTopButtonType("all");
        getPage().addElement(grid);
    }
    
    public void refresh() {
              
        TSqlGrid grid = sqlGrid("datasGrid");
        //处理方式  
        DataTable table = new DataTable("table");
        
        table.createColumn("COLUMN1");
        table.createColumn("COLUMN2");
        
        int pageNo = this.getPageIndex();
        int pageSize = this.getPageSize();
        
        
        
        for(int i=(pageNo-1)*pageSize;i<(pageNo-1)*pageSize+pageSize;i++) {
            
            Record record = table.createRecord();
            record.setFieldValue("COLUMN1", i+"");
            record.setFieldValue("COLUMN2", i+i+"");
        }
        
        grid.setDataTable(table);
        
        table.setDbRecordCount(500); //总记录数
        
        this.setSelfMessage(grid.getDataElement());
        
    }
在使用完全自定义DataTable的使用,需要注意的问题比较多。
第一个关键点,是DataTable中Column的使用。DataTable与普通的ListTable最大的不同点之一,就是DataTable是标准表结构,在使用的时候,必须在创建表后,定义表的字段结构,之后才能使用。在使用的过程中,严禁修改表结构。也就是说,new DataTable后,通过createColumn声明表中存在哪些字段。但是一旦调用createRecord,下面填充数据的时候,就必须按照Column的定义进行添加,在处理的过程中,不允许新建Column或修改Column,如果要调整,就必须整个Table重新创建。在这个原则上,new 构造与createColumn在最上方也就容易理解了。
下面在设置数据的时候,与直接查出的DataTable也有一个不同,createRecord后,只调用了setFieldValue方法,而没有使用setFieldText方法,这里就是另一个问题了。在直接createRecord后,当前字段中FieldText不存在,直接调用setFieldValue将会同时在FieldText中设置相同的值,但是,查询出来的DataTable中,FieldText已经存在值,setFieldValue就只会修改FieldValue,要修改展示信息,就必须调用setFieldText设置FieldText值。
最后,就是DataTable与普通Table的另一个最大的不同点,普通的Table,获取记录数的方法返回的是应该是内部存储的记录总数,但是DataTable是在页面中使用的,获取记录数返回的方法应该是未分页前的SQL能够查询的记录总数,这点非常关键。setDbRecordCount 可以给自定义的表格设置记录总数调用该方法设置总的记录数,这个是数据处理非常关键的部分。在之前通过普通sql查询扩展的处理时,实际上会通过普通sql自动设置该数值信息,让显示正常。这点请一定注意。
最终呈现如下

某些情况希望做HTML的支持,下面是一个简单的使用示例
    public void page_onLoad() throws YaconException {
        
        TDiv htmlDiv = new TDiv("htmlDiv");
        
        htmlDiv.addHtml(FilePath.getWebRootPath("/UI/ekbs/div/html/index.html"));
        
        this.getPage().addElement(htmlDiv);
        
    }
这个示例的实现比较简单,在平台中,TDiv是为了给完全个性化处理提供支持的组件,该组件可以作为TPanel或者页面中的组件使用。
通过addHtml可以将写好的html放入到div中,之后可以将div放到页面中。
最终呈现如下

html的处理比较简单,实际上,在TDiv使用中,还有一种常见的处理,那就是TDiv占位,前端js填充内容。例如下面的示例
后台
    public void page_onLoad() throws YaconException {
        
        
        TDiv callDiv = new TDiv("callDiv");
        
        callDiv.addStyle("width", "100%");
        callDiv.addStyle("height", "100%");
        
        this.getPage().addElement(callDiv);
        
    }
前端
<script>
	function callDiv_afterEvent(div,page){
	
        var myChart = echarts.init(document.getElementById('callDiv'));
 
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: '简单柱形图示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };
 
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
	}
</script>
在上面的处理中,后台定义TDiv,通过addStyle设置样式,目的只是位置占位,前端走回调,将需要的内容填充到区域中。实际上,未来的统计图处理更多的希望走这种方式。
最终呈现如下

在之前的处理中,我们都是基于TDiv来进行个性化处理,这种个性化处理,本质上是将页面的局部内容更新到当前页面的html中做最终呈现,换句话讲,这种形式使用的一般是局部页面处理。
如果我们要处理一个完整的其他页面或者其他的地址页面,使用这种方法,就会出现各种路径匹配的问题,一般,如果是html中,通常这种情况会使用iframe标签来进行引入。在平台中,针对该部分提供了支持。
下面来看一个示例代码
    public void page_onLoad() throws YaconException {
       
        TOutFrame frame = new TOutFrame("baiduFrame");
        
        frame.setUrl("https://www.baidu.com");
        
        frame.setCellHeight("12");
        
        frame.setStyle("width", "100%");
        frame.setStyle("height", "100%");
        
        this.getPage().addElement(frame,12);
        
    }
写法实际非常简单,基于TOutFrame,设置路径即可。
因为这个组件基于IFrame实现,所以一定小心跨域处理与边界问题。
前端跨域:当浏览器路径与内部访问路径的域名不同时,就会出现跨域不可访问的问题,这种情况一般是在前置或者交易代码中放开跨域支持设置。
边界问题:使用iframe引入其他地址页面时,frame内部元素的边界收iframe整体边界影响,不能跨界。
定义一个外部页面嵌入当前的平台中,这个也是个性化处理的常用模式,与上面的TOutFrame本质上是一样的,只是没有代码处理罢了。
我们到开发应用 > 开发运维 > 菜单共享系统管理中直接定义一个菜单,如下

注意地址写一个完整地址,类型选择外部菜单,定义后,我们就可以直接到项目中分配到应用看效果了

在某些纯做预览显示而且无动作处理的页面时,有时候我们可以直接基于md完成页面之后做呈现处理。这种模式对于一些图文混排性质的页面尤其好用。
首先,我们将md文件与资源文件assert都放到目录下,如下

然后,我们来实现jsp页面作为md的预览入口,代码如下
public class FlwHelp extends BusinessPage {
	@Override
	public void page_onLoad() throws YaconException {
		
		TMarkDown markDown = new TMarkDown("flwHelp");
		
		//设置文件路径
		markDown.setFilePath("/UI/doc/flw/FLW_HELP.md");
		
		//设置CSS样式
		markDown.setCss("lily-markdown-os");
		
		//设置背景色
		markDown.setBackground("#fff");
		
		//是否呈现大纲
		//markDown.setView(true);
		
		this.getPage().addElement(markDown);
		
	}
	
}
MD组件的上层是TMarkDown组件,这个组件可以直接在页面最外层调用。
通过setFilePath可以设置MD文件的访问路径。
通过setCss可以设置主题CSS文件。
setBackground设置背景颜色。
setOverFlow设置是否可以滚动。
setPadding设置边距
setView处理是否呈现标题大纲部分,默认false,不展示。
下面,只要配置地址指向jsp地址即可。