# 1、前言
本文前端部分仅提供React示例且使用TypeScript做类型定义。在开始之前,推荐先学习 React (opens new window)和TypeScript (opens new window),并正确安装和配置了 Node.js (opens new window) v18 或以上。官方指南假设你已了解关于 HTML、CSS 和 JavaScript 的中级知识,并且已经完全掌握了 React 全家桶的正确开发方式。
客开接入扩展指南:https://www.yuque.com/teamdocs/v8-client-kb/zngrgup351qls4gv#%20
# 1.1、前端客开扩展前置条件:
为了保证页签之间的事件互不干扰,使用bpmEventId做事件隔离。某些扩展支持postMessage通信,某些扩展支持eventBus通信。
# 1.1.1、通过事件获取bpmEventId
//通过该事件可以得到bpmEventId
window.postMessage({ type: 'SUB_PAGE_MESSAGE', data: msgData }, '*');

# 1.1.2、eventBus 事件
发起、监听、销毁方法从@seeyon/global中获取,window上已挂载该组件(window.SeeyonGlobal)。
使用方式可以从组件包引入:
import { eventEmit, eventOn, eventOff } from '@seeyon/global';
也可以使用全局方法:
const fn = (params: any) => {
console.log(params);
};
SeeyonGlobal.eventEmit(`xxx_${bpmEventId}`, fn);
SeeyonGlobal.eventOn(`xxx_${bpmEventId}`, fn);
SeeyonGlobal.eventOff(`xxx_${bpmEventId}`, fn);
# 2、可扩展组件
# 2.1、意见列表
事件需要保证多页签下不同的URL页面互相不干扰,事件类型带上bpmEventId,BPM初始化会抛出事件,参考前端客开扩展前置条件
# 2.1.1、列表节点栏点击事件扩展
增加点击事件(postMessage通信)
# 2.1.1.1、IOpinionInfo:意见信息,部分流程信息
export interface IOpinionNodeInfo {
activityStateEnum?: string; //节点状态
batchId?: string; //批号
dynamicChildNode?: string[]; //动态节点内部子节点拆分出来的节点
hasMore?: boolean; //弃用字段-忽略
hasNotice?: boolean; //弃用字段-忽略
nodeId: string; //节点id
nodeName: string; //节点名称
nodeType?: string; //节点类型
opinionDtoList: IOpinionDto[]; //意见列表
sourceNodeId?: string; //模板源节点id
noticeUsers: INoticeUser[];
processingUsers: IProcessingUser[];
noticeOpinionDtoList?: IOpinionDto[]; //知会意见
activityFirstTime: number;
hasOpinion: boolean;
hasProcessing: boolean;
isDynamic: boolean;
reRendered?: boolean; //三方系统自定义节点内意见内容和排序规则
}
# 2.1.1.2、调用示例
postMessage事件定义:
BPM_OPINION_NODE_CLICK_${bpmEventId}:意见列表节点栏点击时,标品发起该事件并传递意见信息及部分流程信息;
REWRITE_BPM_OPINION_NODE_CLICK_${bpmEventId}:标品监听该事件,标品需要重写意见列表节点栏点击事件时主动发起该事件。
//处理message事件
const handleMessageEvent = (event: Record<string, any>) => {
//重写意见列表节点名称点击事件
if (event.data?.type === `BPM_OPINION_NODE_CLICK_${bpmEventId}`) {
//发起事件告知标品需要复写意见列表节点栏点击事件
window.postMessage({type: `REWRITE_BPM_OPINION_NODE_CLICK_${bpmEventId}`}, '*');
//获取意见信息及部分流程信息
console.log(event.data?.data)
}
};
useEffect(() => {
//消息监听
window.addEventListener('message', handleMessageEvent);
return () => {
window.removeEventListener('message', handleMessageEvent);
};
}, []);
# 2.1.2、PC端支持自定义更新意见内容
eventBus 事件定义:
OPINION_CUSTOM_OPERATION_CONTROL_ASK_${bpmEventId}:询问是否要自定义控制编辑、删除按钮;
OPINION_CUSTOM_OPERATION_CONTROL_${bpmEventId}:告知标品要展示编辑、删除按钮、以及编辑删除调用的服务;
# 2.1.2.1 标品代码示例
// 处理自定义操作
const handleCustomOp = (
data: {
//是否展示编辑按钮
customEdit?: boolean;
//是否展示删除按钮
customDelete?: boolean;
//编辑、删除按钮提交到哪个服务
serviceName?: string;
}
) => {
console.log(data);
};
useEffect(() => {
if (!bpmEventId) {
return;
}
// 监听自定义操作
eventOn(`OPINION_CUSTOM_OPERATION_CONTROL_${bpmEventId}`, handleCustomOp);
// 询问是否要自定义编辑、删除
eventEmit(`OPINION_CUSTOM_OPERATION_CONTROL_ASK_${bpmEventId}`);
return () => eventOff(`OPINION_CUSTOM_OPERATION_CONTROL_${bpmEventId}`, handleCustomOp);
}, [bpmEventId]);
# 2.1.2.1 客开调用示例
// 自定义操作
const controlCustomOp = () => {
//发送自定义操作事件
eventEmit(
`OPINION_CUSTOM_OPERATION_CONTROL_${bpmEventId}`,
{ customEdit: true, customDelete: true, serviceName: 'appName' }
);
};
useEffect(() => {
if (!bpmEventId) {
return;
}
// 监听意见列表自定义操作询问事件
eventOn(`OPINION_CUSTOM_OPERATION_CONTROL_ASK_${bpmEventId}`, controlCustomOp);
return () => eventOff(`OPINION_CUSTOM_OPERATION_CONTROL_ASK_${bpmEventId}`, controlCustomOp);
}, [bpmEventId]);
# 2.1.3 阻止弹出知会人员、处理中人员已读/未读弹框
postMessage事件定义:
BPM_OPINION_LIST_USERS_LABEL_INITIALIZED_${bpmEventId}:意见列表处理中人员或者知会人展示组件初始化完成事件;
BPM_OPINION_CANCEL_SHOW_ALL_USERS_${bpmEventId}:处理中人员、知会是按节点合并展示时,点击人员名称文案是否需要弹出完整人员列表弹窗
# 2.1.3.1 标品代码示例
//处理message事件 -支持客开能力扩展
const handleMessageEvent = (event: Record<string, any>) => {
// 当处理中人员、知会是按节点合并展示时,点击人员名称文案是否需要弹出完整人员列表弹窗
if (get(event, 'data.type') === `BPM_OPINION_CANCEL_SHOW_ALL_USERS_${bpmEventId}`) {
console.log(data.data);
}
};
useEffect(() => {
window.addEventListener('message', handleMessageEvent);
return () => {
window.removeEventListener('message', handleMessageEvent);
};
}, [bpmEventId]);
useEffect(() => {
if (!bpmEventId) {
return;
}
// 广播意见列表处理中人员或者知会人初始化完成事件
window.postMessage(
{
type: `BPM_OPINION_LIST_USERS_LABEL_INITIALIZED_${bpmEventId}`,
data: {
opinionInfo, //当前意见信息
serviceName, //服务名称
userType, //'noticeUser':知会人; 'processingUser':处理中人员
caseId,
},
},
'*',
);
}, [bpmEventId]);
# 2.1.3.2 客开调用示例
//处理message事件
const handleMessageEvent = (event: Record<string, any>) => {
// 取消知会人、处理中人员已读/未读弹框
if (get(event, 'data.type') === `BPM_OPINION_LIST_USERS_LABEL_INITIALIZED_${bpmEventId}`) {
console.log(data.data);
window.postMessage({
type: `BPM_OPINION_CANCEL_SHOW_ALL_USERS_${bpmEventId}`,
data: {noticeUsers: true, processingUser: true}
})
}
};
useEffect(() => {
window.addEventListener('message', handleMessageEvent);
return () => {
window.removeEventListener('message', handleMessageEvent);
};
}, [bpmEventId]);
# 2.2、意见输入框
# 2.2.1、@功能扩展(包含意见列表回复):
1、点击意见输入框的@按钮的时候,触发postMessage事件,BPM侧向客户方询问是否要使用自定义的@功能。 2、如果需要自定义@功能则通知BPM侧,需要自定义并且返回相应的参数。 3、BPM将客开的回调参数传给意见输入框组件方。
事件定义:
BPM_OPINION_CUSTOM_@_${bpmEventId}:标品询问是否需要重写@人员;
REWRITE_BPM_OPINION_CUSTOM_@_${bpmEventId}:客开回答:重写@人员;
BASE_OPINION_CUSTOM_@_CLICK_${bpmEventId}:@图标点击发起该事件;
BASE_OPINION_CUSTOM_@_SUBMIT_${bpmEventId}:@客开弹窗人员提交事件。
// 是否需要重写@人员 事件key
const rewriteCustomAtKey = `BPM_OPINION_CUSTOM_@_${bpmEventId}`
// 回答:重写@人员 事件key
const isCustomAtKey = `REWRITE_BPM_OPINION_CUSTOM_@_${bpmEventId}`
// @图标点击 事件key
const atCustomClickKey = `BASE_OPINION_CUSTOM_@_CLICK_${bpmEventId}`;
// @客开弹窗人员提交 事件key
const atCustomSubmitKey = `BASE_OPINION_CUSTOM_@_SUBMIT_${bpmEventId}`;
# 2.2.1.1 代码示例
标品BPM示例代码(BPM向客开询问是否重写@功能,BPM接收客开返回的参数):
// 重写@需要的回调
const [isCustomAt, setIsCustomAt] = useState<boolean>();
// 处理message事件
const handleMessageEvent = (event: Record<string, any>) => {
// 重写@功能
if (get(event, 'data.type') === isCustomAtKey) {
setIsCustomAt(true);
}
};
useEffect(() => {
window.addEventListener('message', handleMessageEvent);
return () => {
window.removeEventListener('message', handleMessageEvent);
};
}, []);
// Bpm调用Opinion组件
<Opinion
id={bpmEventId} // 输入框唯一标识ID,bpm可用bpmEventId
extraConfig={{
mentionConfig: {
custom: isCustomAt, // 是否是自定义
}
}}
/>
// bpm主动询问是否需要客开
window.postMessage({
type: rewriteCustomAtKey,
data: { bpmEventId },
});
客开@人员组件示例(例如@流程外的人员张三):
// 流程内部@人员列表接口的入参
interface Params {
appName: string; // 应用服务名
extendImplFlag: string; // 应用服务的扩展常量
objectId: string; // 详情数据id,bpm即caseId
userName: string; // @人员查询值(可模糊搜索)
}
// @人员的数据格式
interface Atuser {
id: string; // 人员id
name: string; // 人员名称
}
---------------------------------------- 客开@人员列表 ----------------------------------------
// atCustomClickKey的data入参
const [submitFnInfo, setSubmitFnInfo] = useState<{atCustomSubmitKey: string, params
:Params}>({});
// 客开发送通知给bpm
window.postMessage({
type: rewriteCustomAtKey,
data: {
atCustomClickKey: atCustomClickKey
},
});
// 监听postMessage事件
useEffect(() => {
const handleMessageEvent = (event: Record<string, any>) => {
// 接受是否需要重写@
if (get(event, 'data.type') === rewriteCustomAtKey) {
window.postMessage({
type: isCustomAtKey,
data: true,
});
}
// 监听@按钮的点击事件
if (get(event, 'data.type') === atCustomClickKey) {
// 打开客开弹窗事件
setSubmitFnInfo(event.data.data);
}
};
window.addEventListener('message', handleMessageEvent);
return () => {
window.removeEventListener('message', handleMessageEvent);
};
}, []);
// 客开@弹窗选择数据的确定提交
const handleSubmitByCustom =() => {
window.postMessage({
type: submitFnInfo.atCustomSubmitKey,
data: [{ id:123, name:"张三" }],
});
}
<Modal>
<Button onClick={handleSubmitByCustom}>提交选择的人员</Button>
</Modal>
标品平台输入框组件代码示例:
// 监听客开@弹窗选择数据的确定提交
const handleAtCustomSubmitEvent = (event: Record<string, any>) => {
if (get(event, 'data.type') === atCustomSubmitKey) {
const data = get(event, 'data.data');
// 输入框组件拿到@人员列表信息
doSomething(data);
}
};
useEffect(() => {
window.addEventListener('message', handleAtCustomSubmitEvent);
return () => {
window.removeEventListener('message', handleAtCustomSubmitEvent);
};
}, []);
// atClick触发postMessage,其中data入参两个(第一个是click的type,第二个atCustomSubmitKey作为客开@弹窗选择数据的确定提交所触发的postMessage的type key)
function atClick() {
window.postMessage({
type: atCustomClickKey,
data: {
params: {
appName: "",
extendImplFlag: "",
objectId: "",
userName: "",
},
atCustomSubmitKey: atCustomSubmitKey,
},
});
}
<div onClick={atClick}>@图标</div>
# 2.2.1.2、注意点:
前端: 1、handleSubmit参数的人员数据格式必须是 Atuser[] 格式 2、Atuser里面id为userId,name为username 3、@所有人时,含义是流程内的所有人,并且,@所有人对应的数据是 {id: 'all', name: '所有人'}(name可自定义,id不能改)
快速跳转
← 流程按钮扩展行为 客开自定义选分支功能 →