更多ruoyi-nbcio功能请看演示系统
gitee源代码地址
前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio
演示地址:RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/
更多nbcio-boot功能请看演示系统
gitee源代码地址
后端代码: https://gitee.com/nbacheng/nbcio-boot
前端代码:https://gitee.com/nbacheng/nbcio-vue.git
在线演示(包括H5) : http://122.227.135.243:9888
1、因为增加了一个dataType,所以要在flowableDescriptor.json下面增加dataType类型属性
"name": "Assignable",
"extends": [ "bpmn:UserTask" ],
"properties": [
{
"name": "dataType",
"isAttr": true,
"type": "String"
},
2、同时增加了一个text,所以要在要在flowableDescriptor.json下面增加text类型属性
{
"name": "Assignable",
"extends": ["bpmn:UserTask"],
"properties": [
{
"name": "dataType",
"isAttr": true,
"type": "String"
},
{
"name": "assignee",
"isAttr": true,
"type": "String"
},
{
"name": "candidateUsers",
"isAttr": true,
"type": "String"
},
{
"name": "candidateGroups",
"isAttr": true,
"type": "String"
},
{
"name": "text",
"isAttr": true,
"type": "String"
},
{
"name": "dueDate",
"isAttr": true,
"type": "String"
},
{
"name": "followUpDate",
"isAttr": true,
"type": "String"
},
{
"name": "priority",
"isAttr": true,
"type": "String"
}
]
},
3、原先vue2代码如下:
<template>
<div>
<el-row>
<h4><b>审批人设置</b></h4>
<el-radio-group v-model="dataType" @change="changeDataType">
<el-radio label="USERS">指定用户</el-radio>
<el-radio label="ROLES">角色</el-radio>
<el-radio label="DEPTS">部门</el-radio>
<el-radio label="INITIATOR">发起人</el-radio>
<el-radio label="MANAGER">部门经理</el-radio>
</el-radio-group>
</el-row>
<el-row>
<div v-if="dataType === 'USERS'">
<el-tag v-for="userText in selectedUser.text" :key="userText" effect="plain">
{{userText}}
</el-tag>
<div class="element-drawer__button">
<el-button size="mini" type="primary" icon="el-icon-plus" @click="onSelectUsers()">添加用户</el-button>
</div>
</div>
<div v-if="dataType === 'ROLES'">
<el-select v-model="roleIds" multiple size="mini" placeholder="请选择 角色" @change="changeSelectRoles">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="`ROLE${item.roleId}`"
:disabled="item.status === 1">
</el-option>
</el-select>
</div>
<div v-if="dataType === 'DEPTS'">
<tree-select
:width="320"
:height="400"
size="mini"
:data="deptTreeData"
:defaultProps="deptProps"
multiple
clearable
checkStrictly
nodeKey="id"
:checkedKeys="deptIds"
@change="checkedDeptChange">
</tree-select>
</div>
</el-row>
<el-row>
<div v-show="showMultiFlog">
<el-divider />
<h4><b>多实例审批方式</b></h4>
<el-row>
<el-radio-group v-model="multiLoopType" @change="changeMultiLoopType()">
<el-row><el-radio label="Null">无</el-radio></el-row>
<el-row><el-radio label="SequentialMultiInstance">会签(需所有审批人同意)</el-radio></el-row>
<el-row><el-radio label="ParallelMultiInstance">或签(一名审批人同意即可)</el-radio></el-row>
<el-row><el-radio label="CustomMultiInstance">自定义会签条件</el-radio></el-row>
</el-radio-group>
</el-row>
<el-row v-if="multiLoopType === 'CustomMultiInstance'">
<el-input v-model="CustomCompletionCondition" clearable @change="updateLoopCondition" />
</el-row>
<el-row v-if="multiLoopType !== 'Null'">
<el-tooltip content="开启后,实例需按顺序轮流审批" placement="top-start" @click.stop.prevent>
<i class="header-icon el-icon-info"></i>
</el-tooltip>
<span class="custom-label">顺序审批:</span>
<el-switch v-model="isSequential" @change="changeMultiLoopType()" />
</el-row>
</div>
</el-row>
<!-- 候选用户弹窗 -->
<el-dialog title="候选用户" :visible.sync="userOpen" width="60%" append-to-body>
<el-row type="flex" :gutter="20">
<!--部门数据-->
<el-col :span="7">
<el-card shadow="never" style="height: 100%">
<div slot="header">
<span>部门列表</span>
</div>
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
<el-tree
:data="deptOptions"
:props="deptProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-card>
</el-col>
<el-col :span="17">
<el-table ref="multipleTable" height="600" :data="userTableList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户名" align="center" prop="nickName" />
<el-table-column label="部门" align="center" prop="dept.deptName" />
</el-table>
<pagination
:total="userTotal"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getUserList"
/>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleTaskUserComplete">确 定</el-button>
<el-button @click="userOpen = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listUser, deptTreeSelect } from "@/api/system/user";
import { listRole } from "@/api/system/role";
import TreeSelect from "@/components/TreeSelect";
const userTaskForm = {
dataType: '',
assignee: '',
candidateUsers: '',
candidateGroups: '',
text: '',
// dueDate: '',
// followUpDate: '',
// priority: ''
}
export default {
name: "UserTask",
props: {
id: String,
type: String
},
components: { TreeSelect },
data() {
return {
loading: false,
dataType: 'USERS',
selectedUser: {
ids: [],
text: []
},
userOpen: false,
deptName: undefined,
deptOptions: [],
deptProps: {
children: "children",
label: "label"
},
deptTempOptions: [],
userTableList: [],
userTotal: 0,
selectedUserDate: [],
roleOptions: [],
roleIds: [],
deptTreeData: [],
deptIds: [],
// 查询参数
queryParams: {
deptId: undefined
},
showMultiFlog: false,
isSequential: false,
multiLoopType: 'Null',
CustomCompletionCondition: '${nrOfCompletedInstances/nrOfInstances>=1}',
};
},
watch: {
id: {
immediate: true,
handler() {
this.bpmnElement = window.bpmnInstances.bpmnElement;
this.$nextTick(() => this.resetTaskForm());
}
},
// 根据名称筛选部门树
deptName(val) {
this.$refs.tree.filter(val);
}
},
methods: {
resetTaskForm() {
const bpmnElementObj = this.bpmnElement?.businessObject;
console.log("resetTaskForm bpmnElementObj",bpmnElementObj)
if (!bpmnElementObj) {
return;
}
this.clearOptionsData()
this.dataType = bpmnElementObj['dataType'];
console.log("resetTaskForm this.dataType",this.dataType)
if (this.dataType === 'USERS') {
let userIdData = bpmnElementObj['candidateUsers'] || bpmnElementObj['assignee'];
let userText = bpmnElementObj['text'] || [];
if (userIdData && userIdData.toString().length > 0 && userText && userText.length > 0) {
this.selectedUser.ids = userIdData?.toString().split(',');
this.selectedUser.text = userText?.split(',');
}
if (this.selectedUser.ids.length > 1) {
this.showMultiFlog = true;
}
} else if (this.dataType === 'ROLES') {
this.getRoleOptions();
let roleIdData = bpmnElementObj['candidateGroups'] || [];
if (roleIdData && roleIdData.length > 0) {
this.roleIds = roleIdData.split(',')
}
this.showMultiFlog = true;
} else if (this.dataType === 'DEPTS') {
this.getDeptTreeData();
let deptIdData = bpmnElementObj['candidateGroups'] || [];
if (deptIdData && deptIdData.length > 0) {
this.deptIds = deptIdData.split(',');
}
this.showMultiFlog = true;
}
this.getElementLoop(bpmnElementObj);
},
/**
* 清空选项数据
*/
clearOptionsData() {
this.selectedUser.ids = [];
this.selectedUser.text = [];
this.roleIds = [];
this.deptIds = [];
},
// 完成条件
updateLoopCondition(condition) {
},
/**
* 更新节点数据
*/
updateElementTask() {
const taskAttr = Object.create(null);
for (let key in userTaskForm) {
taskAttr[key] = userTaskForm[key];
}
console.log("updateElementTask taskAttr",taskAttr)
window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskAttr);
},
/**
* 查询部门下拉树结构
*/
getDeptOptions() {
return new Promise((resolve, reject) => {
if (!this.deptOptions || this.deptOptions.length <= 0) {
deptTreeSelect().then(response => {
this.deptTempOptions = response.data;
this.deptOptions = response.data;
resolve()
})
} else {
reject()
}
});
},
/**
* 查询部门下拉树结构(含部门前缀)
*/
getDeptTreeData() {
function refactorTree(data) {
return data.map(node => {
let treeData = { id: `DEPT${node.id}`, label: node.label, parentId: node.parentId, weight: node.weight };
if (node.children && node.children.length > 0) {
treeData.children = refactorTree(node.children);
}
return treeData;
});
}
return new Promise((resolve, reject) => {
if (!this.deptTreeData || this.deptTreeData.length <= 0) {
this.getDeptOptions().then(() => {
this.deptTreeData = refactorTree(this.deptOptions);
resolve()
}).catch(() => {
reject()
})
} else {
resolve()
}
})
},
/**
* 查询部门下拉树结构
*/
getRoleOptions() {
if (!this.roleOptions || this.roleOptions.length <= 0) {
listRole().then(response => this.roleOptions = response.rows);
}
},
/** 查询用户列表 */
getUserList() {
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.userTableList = response.rows;
this.userTotal = response.total;
});
},
// 筛选节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
// 节点单击事件
handleNodeClick(data) {
this.queryParams.deptId = data.id;
this.getUserList();
},
// 关闭标签
handleClose(tag) {
this.selectedUserDate.splice(this.selectedUserDate.indexOf(tag), 1);
this.$refs.multipleTable.toggleRowSelection(tag);
},
// 多选框选中数据
handleSelectionChange(selection) {
this.selectedUserDate = selection;
},
onSelectUsers() {
this.selectedUserDate = []
this.$refs.multipleTable?.clearSelection();
this.getDeptOptions();
this.userOpen = true;
},
handleTaskUserComplete() {
if (!this.selectedUserDate || this.selectedUserDate.length <= 0) {
this.$modal.msgError('请选择用户');
return;
}
userTaskForm.dataType = 'USERS';
this.selectedUser.text = this.selectedUserDate.map(k => k.nickName) || [];
if (this.selectedUserDate.length === 1) {
let data = this.selectedUserDate[0];
userTaskForm.assignee = data.userName;
userTaskForm.text = data.nickName;
userTaskForm.candidateUsers = null;
this.showMultiFlog = false;
this.multiLoopType = 'Null';
this.changeMultiLoopType();
} else {
userTaskForm.candidateUsers = this.selectedUserDate.map(k => k.userName).join() || null;
userTaskForm.text = this.selectedUserDate.map(k => k.nickName).join() || null;
userTaskForm.assignee = null;
this.showMultiFlog = true;
}
this.updateElementTask()
this.userOpen = false;
},
changeSelectRoles(val) {
let groups = null;
let text = null;
if (val && val.length > 0) {
userTaskForm.dataType = 'ROLES';
groups = val.join() || null;
let textArr = this.roleOptions.filter(k => val.indexOf(`ROLE${k.roleId}`) >= 0);
text = textArr?.map(k => k.roleName).join() || null;
} else {
userTaskForm.dataType = null;
this.multiLoopType = 'Null';
}
userTaskForm.candidateGroups = groups;
userTaskForm.text = text;
this.updateElementTask();
this.changeMultiLoopType();
},
checkedDeptChange(checkedIds) {
let groups = null;
let text = null;
this.deptIds = checkedIds;
if (checkedIds && checkedIds.length > 0) {
userTaskForm.dataType = 'DEPTS';
groups = checkedIds.join() || null;
let textArr = []
let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData));
checkedIds.forEach(id => {
let stark = []
stark = stark.concat(treeStarkData);
while(stark.length) {
let temp = stark.shift();
if(temp.children) {
stark = temp.children.concat(stark);
}
if(id === temp.id) {
textArr.push(temp);
}
}
})
text = textArr?.map(k => k.label).join() || null;
} else {
userTaskForm.dataType = null;
this.multiLoopType = 'Null';
}
userTaskForm.candidateGroups = groups;
userTaskForm.text = text;
this.updateElementTask();
this.changeMultiLoopType();
},
changeDataType(val) {
if (val === 'ROLES' || val === 'DEPTS' || (val === 'USERS' && this.selectedUser.ids.length > 1)) {
this.showMultiFlog = true;
} else {
this.showMultiFlog = false;
}
this.multiLoopType = 'Null';
this.changeMultiLoopType();
// 清空 userTaskForm 所有属性值
Object.keys(userTaskForm).forEach(key => userTaskForm[key] = null);
userTaskForm.dataType = val;
console.log("changeDataType this.selectedUser",this.selectedUser);
console.log("changeDataType val",val);
if (val === 'USERS') {
if (this.selectedUser && this.selectedUser.ids && this.selectedUser.ids.length > 0) {
if (this.selectedUser.ids.length === 1) {
userTaskForm.assignee = this.selectedUser.ids[0];
} else {
userTaskForm.candidateUsers = this.selectedUser.ids.join()
}
userTaskForm.text = this.selectedUser.text?.join() || null
}
} else if (val === 'ROLES') {
this.getRoleOptions();
if (this.roleIds && this.roleIds.length > 0) {
userTaskForm.candidateGroups = this.roleIds.join() || null;
let textArr = this.roleOptions.filter(k => this.roleIds.indexOf(`ROLE${k.roleId}`) >= 0);
userTaskForm.text = textArr?.map(k => k.roleName).join() || null;
}
} else if (val === 'DEPTS') {
this.getDeptTreeData();
if (this.deptIds && this.deptIds.length > 0) {
userTaskForm.candidateGroups = this.deptIds.join() || null;
let textArr = []
let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData));
this.deptIds.forEach(id => {
let stark = []
stark = stark.concat(treeStarkData);
while(stark.length) {
let temp = stark.shift();
if(temp.children) {
stark = temp.children.concat(stark);
}
if(id === temp.id) {
textArr.push(temp);
}
}
})
userTaskForm.text = textArr?.map(k => k.label).join() || null;
}
} else if (val === 'MANAGER') {
userTaskForm.assignee = "${DepManagerHandler.getUser(execution)}";
userTaskForm.text = "部门经理";
} else if (val === 'INITIATOR') {
userTaskForm.assignee = "${initiator}";
userTaskForm.text = "流程发起人";
}
this.updateElementTask();
},
getElementLoop(businessObject) {
if (!businessObject.loopCharacteristics) {
this.multiLoopType = "Null";
return;
}
this.isSequential = businessObject.loopCharacteristics.isSequential;
if (businessObject.loopCharacteristics.completionCondition) {
if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= nrOfInstances}") {
this.multiLoopType = "SequentialMultiInstance";
} else if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances > 0}") {
this.multiLoopType = "ParallelMultiInstance";
} else {
this.multiLoopType = "CustomMultiInstance";
}
}
},
changeMultiLoopType() {
// 取消多实例配置
if (this.multiLoopType === "Null") {
window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { loopCharacteristics: null, assignee: null });
return;
}
this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: this.isSequential });
// 更新多实例配置
window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
loopCharacteristics: this.multiLoopInstance,
assignee: '${assignee}'
});
// 完成条件
let completionCondition = null;
// 会签
if (this.multiLoopType === "SequentialMultiInstance") {
completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances >= nrOfInstances}" });
}
// 或签
if (this.multiLoopType === "ParallelMultiInstance") {
completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances > 0}" });
}
// 自定义会签
if (this.multiLoopType === "CustomMultiInstance") {
completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: this.CustomCompletionCondition });
}
// 更新模块属性信息
window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.multiLoopInstance, {
collection: '${multiInstanceHandler.getUserNames(execution)}',
elementVariable: 'assignee',
completionCondition
});
},
},
beforeUnmount() {
this.bpmnElement = null;
}
};
</script>
<style scoped lang="scss">
.el-row .el-radio-group {
margin-bottom: 15px;
.el-radio {
line-height: 28px;
}
}
.el-tag {
margin-bottom: 10px;
+ .el-tag {
margin-left: 10px;
}
}
.custom-label {
padding-left: 5px;
font-weight: 500;
font-size: 14px;
color: #606266;
}
</style>
3、修改成vue3代码如下:
<template>
<div>
<el-row>
<h4><b>审批人设置</b></h4>
<el-radio-group v-model="dataType" @change="changeDataType">
<el-radio label="USERS">指定用户</el-radio>
<el-radio label="ROLES">角色</el-radio>
<el-radio label="DEPTS">部门</el-radio>
<el-radio label="INITIATOR">发起人</el-radio>
<el-radio label="MANAGER">部门经理</el-radio>
</el-radio-group>
</el-row>
<el-row>
<div v-if="dataType === 'USERS'">
<el-tag v-for="userText in selectedUser.text" :key="userText" effect="plain">
{{userText}}
</el-tag>
<div class="element-drawer__button">
<el-button size="small" type="primary" icon="el-icon-plus" @click="onSelectUsers()">添加用户</el-button>
</div>
</div>
<div v-if="dataType === 'ROLES'">
<el-select v-model="roleIds" multiple size="small" placeholder="请选择 角色" @change="changeSelectRoles">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="`ROLE${item.roleId}`"
:disabled="item.status === 1">
</el-option>
</el-select>
</div>
<div v-if="dataType === 'DEPTS'">
<tree-select
:width="320"
:height="400"
size="small"
:data="deptTreeData"
:props="deptProps"
multiple
clearable
checkStrictly
nodeKey="id"
:checkedKeys="deptIds"
@change="checkedDeptChange">
</tree-select>
</div>
</el-row>
<el-row>
<div v-show="showMultiFlog">
<el-divider />
<h4><b>多实例审批方式</b></h4>
<el-row>
<el-radio-group v-model="multiLoopType" @change="changeMultiLoopType()">
<el-row><el-radio label="Null">无</el-radio></el-row>
<el-row><el-radio label="SequentialMultiInstance">会签(需所有审批人同意)</el-radio></el-row>
<el-row><el-radio label="ParallelMultiInstance">或签(一名审批人同意即可)</el-radio></el-row>
<el-row><el-radio label="CustomMultiInstance">自定义会签条件</el-radio></el-row>
</el-radio-group>
</el-row>
<el-row v-if="multiLoopType === 'CustomMultiInstance'">
<el-input v-model="CustomCompletionCondition" clearable @change="updateLoopCondition" />
</el-row>
<el-row v-if="multiLoopType !== 'Null'">
<el-tooltip content="开启后,实例需按顺序轮流审批" placement="top-start" @click.stop.prevent>
<i class="header-icon el-icon-info"></i>
</el-tooltip>
<span class="custom-label">顺序审批:</span>
<el-switch v-model="isSequential" @change="changeMultiLoopType()" />
</el-row>
</div>
</el-row>
<!-- 候选用户弹窗 -->
<el-dialog title="候选用户" v-model="userOpen" width="60%" append-to-body>
<el-row type="flex" :gutter="20">
<!--部门数据-->
<el-col :span="7">
<el-card shadow="never" style="height: 100%">
<template #header>
<span>部门列表</span>
</template>
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
<el-tree
:data="deptOptions"
:props="deptProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-card>
</el-col>
<el-col :span="17">
<el-table ref="multipleTable" height="600" :data="userTableList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户名" align="center" prop="nickName" />
<el-table-column label="部门" align="center" prop="dept.deptName" />
</el-table>
<pagination
:total="userTotal"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getUserList"
/>
</el-col>
</el-row>
<template #footer class="dialog-footer">
<el-button type="primary" @click="handleTaskUserComplete">确 定</el-button>
<el-button @click="userOpen = false">取 消</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { listUser, deptTreeSelect } from "@/api/system/user";
import { listRole } from "@/api/system/role";
defineOptions({ name: 'UserTask' })
const props = defineProps({
id: String,
type: String
})
const userTaskForm = ref({
dataType: '',
assignee: '',
candidateUsers: '',
candidateGroups: '',
text: '',
// dueDate: '',
// followUpDate: '',
// priority: ''
})
const { proxy } = getCurrentInstance() as ComponentInternalInstance
const bpmnElement = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const loading = ref(false)
const dataType = ref('USERS')
const selectedUser = ref({
ids: [],
text: []
})
const userOpen = ref(false)
const deptName = ref(undefined)
const deptOptions = ref<any[]>([])
const deptProps = ref({
label: "label",
children: "children"
})
const deptTempOptions = ref<any[]>([])
const userTableList = ref<any[]>([])
const userTotal = ref(0)
const selectedUserDate = ref<any[]>([])
const roleOptions = ref<any[]>([])
const roleIds = ref<any[]>([])
const deptTreeData = ref<any[]>([])
const deptIds = ref<any[]>([])
// 查询参数
const queryParams = ref({
pageNum: 1,
pageSize: 10,
deptId: ''
})
const showMultiFlog = ref(false)
const isSequential = ref(false)
const multiLoopType = ref('Null')
const CustomCompletionCondition = ref('${nrOfCompletedInstances/nrOfInstances>=1}')
const multipleTable = ref(null)
const tree = ref(null)
const resetTaskForm = () => {
bpmnElement.value = bpmnInstances().bpmnElement
const bpmnElementObj = toRaw(bpmnElement.value?.businessObject);
console.log("resetTaskForm bpmnElementObj",bpmnElementObj)
if (!bpmnElementObj) {
return;
}
clearOptionsData()
dataType.value = bpmnElementObj['dataType'];
console.log("resetTaskForm dataType",dataType)
console.log("resetTaskForm bpmnElementObj",bpmnElementObj)
if (dataType.value === 'USERS') {
let userIdData = bpmnElementObj['candidateUsers'] || bpmnElementObj['assignee'];
console.log("resetTaskForm userIdData",userIdData)
let userText = bpmnElementObj['text'] || [];
console.log("resetTaskForm userText",userText)
if (userIdData && userIdData.toString().length > 0 && userText && userText.length > 0) {
selectedUser.value.ids = userIdData?.toString().split(',');
selectedUser.value.text = userText?.split(',');
}
if (selectedUser.value.ids.length > 1) {
showMultiFlog.value = true;
}
} else if (dataType.value === 'ROLES') {
getRoleOptions();
let roleIdData = bpmnElementObj['candidateGroups'] || [];
if (roleIdData && roleIdData.length > 0) {
roleIds.value = roleIdData.split(',')
}
showMultiFlog.value = true;
} else if (dataType.value === 'DEPTS') {
getDeptTreeData();
let deptIdData = bpmnElementObj['candidateGroups'] || [];
if (deptIdData && deptIdData.length > 0) {
deptIds.value = deptIdData.split(',');
}
showMultiFlog.value = true;
}
getElementLoop(bpmnElementObj);
}
/**
* 清空选项数据
*/
const clearOptionsData = () => {
selectedUser.value.ids = [];
selectedUser.value.text = [];
roleIds.value = [];
deptIds.value = [];
}
// 完成条件
const updateLoopCondition = (condition) => {
}
/**
* 更新节点数据
*/
const updateElementTask = () => {
const taskAttr = Object.create(null);
for (let key in userTaskForm.value) {
taskAttr[key] = userTaskForm.value[key] || null;
}
console.log("updateElementTask taskAttr",taskAttr)
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
}
/**
* 查询部门下拉树结构
*/
const getDeptOptions = () => {
return new Promise((resolve, reject) => {
if (!deptOptions.value || deptOptions.value.length <= 0) {
deptTreeSelect().then(response => {
deptTempOptions.value = response.data;
deptOptions.value = response.data;
resolve()
})
} else {
reject()
}
});
}
/**
* 查询部门下拉树结构(含部门前缀)
*/
const getDeptTreeData = () => {
return new Promise((resolve, reject) => {
if (!deptTreeData.value || deptTreeData.value.length <= 0) {
getDeptOptions().then(() => {
deptTreeData.value = refactorTree(deptOptions.value);
console.log("getDeptTreeData deptTreeData.value",deptTreeData.value)
resolve()
}).catch(() => {
reject()
})
} else {
resolve()
}
})
}
const refactorTree = (data) => {
return data.map(node => {
let treeData = { id: `DEPT${node.id}`, label: node.label, parentId: node.parentId, weight: node.weight };
if (node.children && node.children.length > 0) {
treeData.children = refactorTree(node.children);
}
return treeData;
});
}
/**
* 查询部门下拉树结构
*/
const getRoleOptions = () => {
if (!roleOptions.value || roleOptions.value.length <= 0) {
listRole().then(response => roleOptions.value = response.rows);
}
}
/** 查询用户列表 */
const getUserList = () => {
listUser(proxy?.addDateRange(queryParams.value, dateRange.value)).then(response => {
userTableList.value = response.rows;
userTotal.value = response.total;
});
}
// 筛选节点
const filterNode = (value, data) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
}
// 节点单击事件
const handleNodeClick = (data) => {
queryParams.value.deptId = data.id;
getUserList();
}
// 关闭标签
const handleClose = (tag) => {
selectedUserDate.value.splice(selectedUserDate.value?.indexOf(tag), 1);
multipleTable.value?.toggleRowSelection(tag);
}
// 多选框选中数据
const handleSelectionChange = (selection) => {
selectedUserDate.value = selection;
}
const onSelectUsers = () => {
selectedUserDate.value = []
multipleTable.value?.clearSelection();
getDeptOptions();
console.log("onSelectUsers deptOptions",deptOptions.value)
console.log("onSelectUsers deptProps",deptProps.value)
userOpen.value = true;
}
const handleTaskUserComplete = () => {
if (!selectedUserDate.value || selectedUserDate.value.length <= 0) {
proxy?.$modal.msgError('请选择用户');
return;
}
userTaskForm.value.dataType = 'USERS';
selectedUser.value.text = selectedUserDate.value.map(k => k.nickName) || [];
if (selectedUserDate.value.length === 1) {
let data = selectedUserDate.value[0];
userTaskForm.value.assignee = data.userName;
userTaskForm.value.text = data.nickName;
userTaskForm.value.candidateUsers = '';
showMultiFlog.value = false;
multiLoopType.value = 'Null';
changeMultiLoopType();
} else {
userTaskForm.value.candidateUsers = selectedUserDate.value.map(k => k.userName).join() || '';
userTaskForm.value.text = selectedUserDate.value.map(k => k.nickName).join() || '';
userTaskForm.value.assignee = '';
showMultiFlog.value = true;
}
updateElementTask()
userOpen.value = false;
}
const changeSelectRoles = (val) => {
let groups = null;
let text = null;
if (val && val.length > 0) {
userTaskForm.value.dataType = 'ROLES';
groups = val.join() || null;
let textArr = roleOptions.value.filter(k => val.indexOf(`ROLE${k.roleId}`) >= 0);
text = textArr?.map(k => k.roleName).join() || null;
} else {
userTaskForm.value.dataType = '';
multiLoopType.value = 'Null';
}
userTaskForm.value.candidateGroups = groups;
userTaskForm.value.text = text;
updateElementTask();
changeMultiLoopType();
}
const checkedDeptChange = (checkedIds) => {
let groups = null;
let text = null;
deptIds.value = checkedIds;
if (checkedIds && checkedIds.length > 0) {
userTaskForm.value.dataType = 'DEPTS';
groups = checkedIds.join() || null;
let textArr = []
let treeStarkData = JSON.parse(JSON.stringify(deptTreeData.value));
checkedIds.forEach(id => {
let stark = []
stark = stark.concat(treeStarkData);
while(stark.length) {
let temp = stark.shift();
if(temp.children) {
stark = temp.children.concat(stark);
}
if(id === temp.id) {
textArr.push(temp);
}
}
})
text = textArr?.map(k => k.label).join() || '';
} else {
userTaskForm.value.dataType = '';
multiLoopType.value = 'Null';
}
userTaskForm.value.candidateGroups = groups;
userTaskForm.value.text = text || '';
updateElementTask();
changeMultiLoopType();
}
const changeDataType = (val) => {
if (val === 'ROLES' || val === 'DEPTS' || (val === 'USERS' && selectedUser.value.ids.length > 1)) {
showMultiFlog.value = true;
} else {
showMultiFlog.value = false;
}
multiLoopType.value = 'Null';
changeMultiLoopType();
// 清空 userTaskForm 所有属性值
Object.keys(userTaskForm.value).forEach(key => userTaskForm.value[key] = null);
userTaskForm.value.dataType = val;
console.log("changeDataType selectedUser.value",selectedUser.value);
console.log("changeDataType val",val);
if (val === 'USERS') {
if (selectedUser.value && selectedUser.value.ids && selectedUser.value.ids.length > 0) {
if (selectedUser.value.ids.length === 1) {
userTaskForm.value.assignee = selectedUser.value.ids[0];
} else {
userTaskForm.value.candidateUsers = selectedUser.value.ids.join()
}
userTaskForm.value.text = selectedUser.value.text?.join() || ''
}
} else if (val === 'ROLES') {
getRoleOptions();
if (roleIds.value&& roleIds.value.length > 0) {
userTaskForm.value.candidateGroups = roleIds.value.join() || '';
let textArr = roleOptions.value.filter(k => roleIds.value.indexOf(`ROLE${k.roleId}`) >= 0);
userTaskForm.value.text = textArr?.map(k => k.roleName).join() || '';
}
} else if (val === 'DEPTS') {
getDeptTreeData();
if (deptIds.value && deptIds.value.length > 0) {
userTaskForm.value.candidateGroups = deptIds.value.join() || '';
let textArr = []
let treeStarkData = JSON.parse(JSON.stringify(deptTreeData.value));
deptIds.value.forEach(id => {
let stark = []
stark = stark.concat(treeStarkData);
while(stark.length) {
let temp = stark.shift();
if(temp.children) {
stark = temp.children.concat(stark);
}
if(id === temp.id) {
textArr.push(temp);
}
}
})
userTaskForm.value.text = textArr?.map(k => k.label).join() || '';
}
} else if (val === 'MANAGER') {
userTaskForm.value.assignee = "${DepManagerHandler.getUser(execution)}";
userTaskForm.value.text = "部门经理";
} else if (val === 'INITIATOR') {
userTaskForm.value.assignee = "${initiator}";
userTaskForm.value.text = "流程发起人";
}
updateElementTask();
}
const getElementLoop = (businessObject) => {
if (!businessObject.loopCharacteristics) {
multiLoopType.value = "Null";
return;
}
isSequential.value = businessObject.loopCharacteristics.isSequential;
if (businessObject.loopCharacteristics.completionCondition) {
if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= nrOfInstances}") {
multiLoopType.value = "SequentialMultiInstance";
} else if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances > 0}") {
multiLoopType.value = "ParallelMultiInstance";
} else {
multiLoopType.value = "CustomMultiInstance";
}
}
}
const changeMultiLoopType = () => {
// 取消多实例配置
if (multiLoopType.value === "Null") {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { loopCharacteristics: null, assignee: null });
return;
}
const multiLoopInstance = bpmnInstances().moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: isSequential.value });
// 更新多实例配置
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: multiLoopInstance,
assignee: '${assignee}'
});
// 完成条件
let completionCondition = null;
// 会签
if (multiLoopType.value === "SequentialMultiInstance") {
completionCondition = bpmnInstances().moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances >= nrOfInstances}" });
}
// 或签
if (multiLoopType.value === "ParallelMultiInstance") {
completionCondition = bpmnInstances().moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances > 0}" });
}
// 自定义会签
if (multiLoopType.value === "CustomMultiInstance") {
completionCondition = bpmnInstances().moddle.create("bpmn:FormalExpression", { body: CustomCompletionCondition.value });
}
// 更新模块属性信息
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), multiLoopInstance, {
collection: '${multiInstanceHandler.getUserNames(execution)}',
elementVariable: 'assignee',
completionCondition
});
}
watch(
() => props.id,
() => {
bpmnElement.value = bpmnInstances().bpmnElement
nextTick(() => {
resetTaskForm()
})
},
{ immediate: true }
)
watch(
// 根据名称筛选部门树
() => deptName.value,
(val) => {
console.log("watch deptName",deptName)
console.log("watch tree",tree)
tree.value.filter(val);
}
)
onBeforeUnmount(() => {
bpmnElement.value = null;
})
</script>
<style scoped lang="scss">
.el-row .el-radio-group {
margin-bottom: 15px;
.el-radio {
line-height: 28px;
}
}
.el-tag {
margin-bottom: 10px;
+ .el-tag {
margin-left: 10px;
}
}
.custom-label {
padding-left: 5px;
font-weight: 500;
font-size: 14px;
color: #606266;
}
</style>
4、效果图