门户定制
企业门户提供大量配置,以满足客户需求,参考《桌面端门户配置》和《移动端门户配置》
在配置不能满足需求时,可以采用门户定制实现个性化需求
门户定制后,如果平台更新了“企业门户应用模板”,则定制的门户,需要
- 升级 IDE,以得到最新的代码
- 如果修改了原应用模板中的代码,还需手工合并代码
- 发布门户应用
上面的过程,在每一次平台更新“企业门户应用模板”后,都需要操作一遍
为解决企业门户应用更新过程繁琐的问题,企业门户 V2 提供“门户自定义”组件,实现除定制门户的开发租户外,其余生产租户的门户无需定制。使用方法如下
- 使用一个开发租户用来定制门户,添加企业门户应用,门户应用中自带“门户自定义”组件
- 将所有的定制内容都写到“门户自定义”组件内
- 定制后,将定制应用中的“门户自定义”组件导出,上传到市场,更新市场中原来的“门户自定义”组件
- 使用 system 登录控制台,打开“我的开发-企业应用-企业门户 V2”的云 IDE,发布应用到市场(模板设置时提示“当前服务名为空”,请等待几分钟)
- 发布时,企业门户应用自动更新“门户自定义”组件,发布后,其余租户的门户重启即可
当平台升级版本,更新“企业门户应用模板”后,只需执行上面第4步和第5步即可
定制过程
开启定制
开启定制及门户调试方法,参考《门户调试》
开启组件定制
开发环境中的组件,一般都是自动升级的,在 IDE 打开时和应用发布时,会从市场下载最新的组件资源。对于需要定制的组件,需要开启组件定制,对于定制状态的组件,系统不进行自动更新操作。
在组件列表中,找到“门户自定义”组件,点击“定制”开启组件定制,如下图所示
配置定制文件列表
开启定制的应用,使用的应用模板如果升级了,定制的应用可以通过“高级-立即升级”,执行代码升级
升级代码时,系统只对开发者未修改的文件进行代码更新,对于开发者修改过的文件,不进行处理。这就意味着修改过的文件,需要进行手工代码合并。
系统提供两种方法记录文件是否被修改过
- 自动记录:对文件进行 md5 比较,如果不同,表示是被修改过的文件
- 手工记录:将增删改的目录或文件,记录在 patchFiles.json 文件中
系统根据有无 patchFiles.json 文件,采用不同的方式判断文件是否修改过。
特别说明
- 鉴于打开过文件,可能会影响文件 md5 的结果,建议使用手工记录的方式记录修改过的文件
- 使用文件记录后,对于修改的文件,务必记录在文件中,否则 IDE 池重启后,修改的文件就会被还原
手工记录,参考《配置定制文件》
门户定制,一切定制写在“门户自定义”组件内,将组件的前端目录和后端目录,写入配置定制文件 model/patchFiles.json 即可,如下图所示,在 model 目录中添加 patchFiles.json 文件,内容如下
{
"update": ["UI2/comp/portalConfig",
"service/comp/portalConfig"]
}
门户自定义组件
门户自定义组件 portalConfig,用于开发者在企业门户应用中,实现一些定制能力
- 前端目录为 /model/UI2/comp/portalConfig
- 后端目录为 /model/service/comp/portalConfig
- 依赖门户调整开发,需要新增 springboot.embed 标识文件,main 模块会自动将该项目依赖加入到 pom.xml 中
- 建议使用本地 ide,利用开发工具完成后端代码调试后再进行发布
- 企业门户应用自带门户自定义组件,所有定制的文件都应在组件目录下
- 门户自定义组件支持添加页面、数据和服务
- 门户自定义组件提供扩展机制
- 支持通过动作拦截,扩展现有的方法
- 支持预留扩展点
添加页面、数据和服务
门户自定义组件和其他组件一样,支持在组件内添加页面、数据和服务
切换到组件端
IDE 顶部显示“端”列表,默认显示桌面端和移动端,还有在多项目多端中扩展的端。开启组件开发后,“端”列表中会显示出已开启定制的组件,如下图所示
切换到“门户自定义”组件端,页面、数据和服务中显示的是组件内部定义的页面、数据和服务
添加页面
切换到门户自定义组件端,打开“页面”设计区,这里显示的是“门户自定义”组件中的目录和文件,在组件目录上添加页面,如下图所示
文件类型选择页面,页面类型选择页面,会在组件的 dialog 目录下创建页面,如果没有 dialog 目录会创建 dialog 目录,如下图所示
添加数据
切换到门户自定义组件端,打开“数据”设计区,这里显示的是在“门户自定义”组件中定义的数据集,如下图所示
添加服务
切换到门户自定义组件端,打开“服务”设计区,这里显示的是在“门户自定义”组件中定义的服务,如下图所示
组件扩展
在 /UI2/pcx/portal.meta.json 描述文件中,列举了企业门户的所有配置项。包括门户页 index 和登录页 login 中的动作、方法和变量
- 动作(类型 type 是 action):支持拦截处理,3个拦截时机:执行前 @before、执行后 @after、执行中 @replace。原则上所有页面实例上的函数,都可以作为 action 进行拦截,在描述中的函数是尽量保证兼容性的稳定接口,其他没有声明的接口,不能保证版本迭代过程的兼容性
- 方法(类型 type 是 funciton):扩展点,通过定义一个函数实现扩展
- 变量(类型 type 是 string):扩展点,通过定义一个变量实现扩展
门户页扩展
门户页 /entry/pcxapp/pcx/index.w 除了描述文件中,列举的方法,还提供了整体布局、顶部导航栏、左侧功能树的渲染方法,见下表
方法 | 用途 | 案例 |
---|---|---|
headerActionsRender | 顶部导航栏渲染 | after-添加下拉组织选择 |
headerAvatarRender | 头像渲染 | |
headerTitleRender | LOGO、标题渲染 | |
menuRender | 功能树渲染 | replace-双层功能树 |
subMenuItemRender | 子菜单渲染 | |
menuItemRender | 菜单项渲染 | |
menuFooterRender | 功能树底部区域渲染 | |
layoutRender | 整体布局渲染 | after-顶部显示第三方系统入口 |
layoutContentRender | 布局内容渲染 | after-自定义渲染内容,默认展示初始tab内容 |
门户页还包括一个订阅组件,订阅组件的相关方法,也支持扩展,见下表
方法 | 用途 | 案例 |
---|---|---|
onWxSubscribeMessage | 订阅组件接收消息方法 | after-右下角消息弹出提醒 |
setNotice | 设置消息 | after-显示待办任务数 |
系统使用 this.getConfig('xxx') 定义扩展点,例如在获取所有菜单数据 fetchMenu 方法中,定义 onProcessMenu 扩展点,代码如下
//自定义处理菜单数据
let defineMenus = await this.getConfig('onProcessMenu')?.({ menus });
扩展点 | 用途 | 案例 |
---|---|---|
onProcessMenu({ menus }) | 处理菜单数据 | 自定义登录 URL |
onProcessOrg({ orgsArr, userInfo }) | 处理组织信息 | |
onProcessGroup({ groupArr, userInfo }) | 处理组信息 | |
onProcessUser({ user, userInfo }) | 处理用户信息 | |
onProcessUserName({ currentOrgName,userInfo }) | 处理用户名 | |
onProcessUserMenu({ menuItems,userInfo }) | 处理右上角下拉菜单 |
登录页扩展
登录页 /entry/pcxapp/pcx/user/login.w 提供初始化页面状态、登录框的渲染方法,见下表
方法 | 用途 | 案例 |
---|---|---|
onInitState | 初始化页面状态 | before-第三方密码登录 |
loginRender | 登录框渲染 | before-自定义登录页 |
其他预留扩展点
在 config.components.uixContainer.UserImplement 中包括如下扩展点
扩展点 | 用途 | 案例 |
---|---|---|
loginUrl | 登录 URL | 自定义登录 URL |
loginResultProcess(res) | 登录结果处理 | 自定义登录 URL |
使用动作拦截
针对页面已有的方法,根据页面路径、方法名,定义相应拦截时机的方法
- @before:原方法执行前执行,参数:(...args),其中 args 是原方法自带参数
- @after:原方法执行后执行,参数:(result,...args),其中 result 是原方法返回数据,args 是原方法自带参数
- @replace:替换原方法,参数:(...args),其中 args 是原方法自带参数
特别说明
- 上述3种方法,async 标识必须与原方法同步,即原方法是异步,自定义方法才能使用异步,否则不能使用
- 上述3种方法,可同时使用
- 自定义方法必须写在 w 页面路径下,写在 js 路径下无效
定义门户页 menuRender 方法的3个拦截时机的示例代码如下
let portalConfig = {
"config": {
"/entry": {
"/pcxapp": {
"/pcx": {
"/index.w": {
"menuRender":{//方法
/**
* 原方法执行前执行
* @param {...any} args 原方法自带参数
*/
"@before":(...args)=>{
},
/**
* 原方法执行后执行
* @param {*} result 原方法返回数据
* @param {...any} args 原方法自带参数
*/
"@after":(result,...args)=>{
},
/**
* 替换原方法
* @param {...any} args 原方法自带参数
*/
"@replace":(...args)=>{
}
}
}
}
}
}
}
};
使用预留扩展点
根据扩展点所在的路径,定义相应的方法或变量。方法是同步还是异步,根据实际需求进行定制即可。使用扩展点示例如下
let portalConfig = {
"config": {
"components": {
"uixContainer": {
"UserImplement": {
"loginUrl": "xxx",
}
}
},
"/entry": {
"/pcxapp": {
"/pcx": {
"/index.w": {
"onProcessMenu": ({ menus }) => {
}
}
}
}
}
}
};
配置文件
桌面端门户的扩展点方法写在 /UI2/comp/portalConfig/components/portalConfig/portalConfig.config.pc.js 文件中 移动端门户的扩展点方法写在 /UI2/comp/portalConfig/components/portalConfig/portalConfig.config.mobile.js 文件中 门户的公共扩展点方法写在 /UI2/comp/portalConfig/components/portalConfig/portalConfig.config.js 文件中
特别说明
- 配置文件识别顺序为:先找 portalConfig.config.(pc/mobile).js,再找 portalConfig.config.js
- portalConfig.config.js 文件是“门户自定义”组件的基础脚本,该文件“不能删除,必须保留”,在运行时不区分端类型,即无论桌面端还是移动端都会加载执行
- 如果定制内容是“桌面端专属”,需要复制 portalConfig.config.js 为 portalConfig.config.pc.js 文件,并在其脚本内进行内容定制
- 如果定制内容是“移动端专属”,需要复制 portalConfig.config.js 为 portalConfig.config.mobile.js 文件,并在其脚本内进行移动内容定制
配置文件,示例代码如下
import { merge } from "lodash";
import ConfigContextProcessor from 'core/framework/ConfigContextProcessor';
export default {
processConfigContext(configContext) {
},
//自定义
async onConfigContextInit(configContextProcessor) {
//获取当前页
let _this = configContextProcessor.page;
let portalConfig = {
"config": {
"/entry": {
"/pcxapp": {
"/pcx": {
"/index.w": {
"menuRender":{//方法
/**
* 原方法执行前执行
* @param {...any} args 原方法自带参数
*/
"@before":(...args)=>{
},
/**
* 原方法执行后执行
* @param {*} result 原方法返回数据
* @param {...any} args 原方法自带参数
*/
"@after":(result,...args)=>{
},
/**
* 替换原方法
* @param {...any} args 原方法自带参数
*/
"@replace":(...args)=>{
}
},
/**
* 定义动态方法
* @param {*} param0 动态方法对应的参数,根据业务需要自行传参
*/
"onProcessMenu": function ({ menus }) {
}
}
}
}
}
}
};
//处理自定义内容,固定内容请勿随意删除
merge(_this.configContext, portalConfig);
ConfigContextProcessor.enhancePageAdvice(_this);
}
}