Files
2022-12-23 16:28:23 +08:00

644 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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-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="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'">
<!-- :width="320"
:height="400"
size="small"
:defaultProps="deptProps"
multiple
clearable
nodeKey="id"
:checkedKeys="deptIds"
checkStrictly -->
<!-- <tree-select :options="deptTreeData" @update:value="checkedDeptChange">
</tree-select> -->
<el-cascader
:options="deptTreeData"
:modelValue="deptCascaderData"
clearable
@change="checkedDeptChange"
:props="{
checkStrictly: true,
value: 'id',
multiple: true,
}"
></el-cascader>
</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
:style="{
width: '100%',
}"
>
<el-radio label="Null">无</el-radio>
</el-row>
<el-row
:style="{
width: '100%',
}"
><el-radio label="SequentialMultiInstance"
>会签(需所有审批人同意)</el-radio
></el-row
>
<el-row
:style="{
width: '100%',
}"
><el-radio label="ParallelMultiInstance"
>或签(一名审批人同意即可)</el-radio
></el-row
>
</el-radio-group>
</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%">
<div slot="header">
<span>部门列表</span>
</div>
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-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"
v-model:page="queryParams.pageNum"
v-model:limit="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 setup>
import { listUser, deptTreeSelect } from "@/api/system/user";
import { listRole } from "@/api/system/role";
import { addDateRange } from "@/utils/ruoyi";
import TreeSelect from "@/components/TreeSelect";
import { ref, reactive, watch } from "vue";
import { ElMessage } from "element-plus";
const testResult = ref([]);
const userTaskForm = {
dataType: "",
assignee: "",
candidateUsers: "",
candidateGroups: "",
text: "",
};
const props = defineProps({
id: String,
type: String,
});
const { id, type } = toRefs(props);
const loading = ref(false);
const dataType = ref("USERS");
const userOpen = ref(false);
const deptName = ref(undefined);
const deptOptions = ref([]);
const deptTempOptions = ref([]);
const userTableList = ref([]);
const userTotal = ref(0);
const selectedUserDate = ref([]);
const roleOptions = ref([]);
const roleIds = ref([]);
const deptTreeData = ref([]);
const deptIds = ref([]);
const showMultiFlog = ref(false);
const isSequential = ref(false);
const multiLoopType = ref("Null");
const deptCascaderData = ref([]); // 部门级联 modelValue
const data = reactive({
selectedUser: {
ids: [],
text: [],
},
deptProps: {
children: "children",
label: "label",
},
// 查询参数
queryParams: {
deptId: undefined,
},
});
const { selectedUser, deptProps, queryParams } = toRefs(data);
let bpmnElement;
let multiLoopInstance;
let dateRange;
watch(
id,
() => {
bpmnElement = window.bpmnInstances.bpmnElement;
nextTick(() => resetTaskForm());
},
{ immediate: true }
);
function resetTaskForm() {
const bpmnElementObj = bpmnElement?.businessObject;
if (!bpmnElementObj) {
return;
}
clearOptionsData();
dataType.value = bpmnElementObj["dataType"];
if (dataType.value === "USERS") {
let userIdData =
bpmnElementObj["candidateUsers"] || bpmnElementObj["assignee"];
let userText = bpmnElementObj["text"] || [];
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().then(() => {
let deptIdData = bpmnElementObj["candidateGroups"] || [];
if (deptIdData && deptIdData.length > 0) {
deptIds.value = deptIdData.split(",");
deptCascaderData.value = deptIds.value.map((id) => [
...getParentId(deptTreeData.value, id).reverse(),
]);
}
});
showMultiFlog.value = true;
}
getElementLoop(bpmnElementObj);
}
/**
* 清空选项数据
*/
function clearOptionsData() {
selectedUser.value.ids = [];
selectedUser.value.text = [];
roleIds.value = [];
deptIds.value = [];
}
/**
* 跟新节点数据
*/
function updateElementTask() {
const taskAttr = Object.create(null);
for (let key in userTaskForm) {
taskAttr[key] = userTaskForm[key];
}
window.bpmnInstances.modeling.updateProperties(bpmnElement, taskAttr);
}
/**
* 查询部门下拉树结构
*/
function 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();
}
});
}
/**
* 查询部门下拉树结构(含部门前缀)
*/
function 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 (!deptTreeData.value || deptTreeData.value.length <= 0) {
getDeptOptions()
.then(() => {
deptTreeData.value = refactorTree(deptOptions.value);
resolve();
})
.catch(() => {
reject();
});
} else {
resolve();
}
});
}
/**
* 查询部门下拉树结构
*/
function getRoleOptions() {
if (!roleOptions.value || roleOptions.value.length <= 0) {
listRole().then((response) => (roleOptions.value = response.rows));
}
}
/** 查询用户列表 */
function getUserList() {
listUser(addDateRange(queryParams.value, dateRange)).then((response) => {
userTableList.value = response.rows;
userTotal.value = response.total;
});
}
// 筛选节点
function filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
}
// 节点单击事件
function handleNodeClick(data) {
queryParams.value.deptId = data.id;
getUserList();
}
// 关闭标签
const multipleTable = ref();
function handleClose(tag) {
selectedUserDate.value.splice(selectedUserDate.value.indexOf(tag), 1);
multipleTable.value.toggleRowSelection(tag);
}
// 多选框选中数据
function handleSelectionChange(selection) {
selectedUserDate.value = selection;
}
function onSelectUsers() {
selectedUserDate.value = [];
multipleTable.value?.clearSelection();
getDeptOptions();
userOpen.value = true;
}
function handleTaskUserComplete() {
if (!selectedUserDate.value || selectedUserDate.value.length <= 0) {
ElMessage.error("请选择用户");
return;
}
userTaskForm.dataType = "USERS";
selectedUser.value.text = selectedUserDate.value.map((k) => k.nickName) || [];
if (selectedUserDate.value.length === 1) {
let data = selectedUserDate.value[0];
userTaskForm.assignee = data.userId;
userTaskForm.text = data.nickName;
userTaskForm.candidateUsers = null;
showMultiFlog.value = false;
multiLoopType.value = "Null";
changeMultiLoopType(multiLoopType.value);
} else {
userTaskForm.candidateUsers =
selectedUserDate.value.map((k) => k.userId).join() || null;
userTaskForm.text =
selectedUserDate.value.map((k) => k.nickName).join() || null;
userTaskForm.assignee = null;
showMultiFlog.value = true;
}
updateElementTask();
userOpen.value = false;
}
function changeSelectRoles(val) {
userTaskForm.dataType = "ROLES";
userTaskForm.candidateGroups = val.join() || null;
let textArr = roleOptions.value.filter(
(k) => val.indexOf(`ROLE${k.roleId}`) >= 0
);
userTaskForm.text = textArr?.map((k) => k.roleName).join() || null;
updateElementTask();
}
// function checkedDeptChange(checkedIds, checkedData) {
// userTaskForm.dataType = "DEPTS";
// if (checkedIds && checkedIds.length > 0) {
// deptIds.value = checkedIds;
// }
// if (checkedData && checkedData.length > 0) {
// userTaskForm.candidateGroups = checkedData.map((k) => k.id).join() || null;
// userTaskForm.text = checkedData.map((k) => k.label).join() || null;
// updateElementTask();
// }
// }
function findInDeptTree(id) {
let hasFound = false, // 表示是否有找到id值
result = null;
var fn = function (data) {
if (Array.isArray(data) && !hasFound) {
// 判断是否是数组并且没有的情况下,
data.forEach((item) => {
if (item.id === id) {
// 数据循环每个子项并且判断子项下边是否有id值
result = item; // 返回的结果等于每一项
hasFound = true; // 并且找到id值
} else if (item.children) {
fn(item.children); // 递归调用下边的子项
}
});
}
};
fn(deptTreeData.value); // 调用一下
return result;
}
// 获取所有父级
function getParentId(list, id) {
for (let i in list) {
if (list[i].id == id) {
return [list[i].id];
}
if (list[i].children) {
let node = getParentId(list[i].children, id);
if (node !== undefined) {
return node.concat(list[i].id);
}
}
}
}
function checkedDeptChange(checkedIds) {
const ids = checkedIds.map((el) => el[el.length - 1]);
const checkedData = ids.map((el) => findInDeptTree(el));
userTaskForm.dataType = "DEPTS";
if (ids && ids.length > 0) {
deptIds.value = ids;
}
if (checkedData && checkedData.length > 0) {
userTaskForm.candidateGroups = checkedData.map((k) => k.id).join() || null;
userTaskForm.text = checkedData.map((k) => k.label).join() || null;
updateElementTask();
}
}
function changeDataType(val) {
// 清空 userTaskForm 所有属性值
Object.keys(userTaskForm).forEach((key) => (userTaskForm[key] = null));
userTaskForm.dataType = val;
if (val === "USERS") {
if (
selectedUser.value &&
selectedUser.value.ids &&
selectedUser.value.ids.length > 0
) {
if (selectedUser.value.ids.length === 1) {
userTaskForm.assignee = selectedUser.value.ids[0];
} else {
userTaskForm.candidateUsers = selectedUser.value.ids.join();
}
userTaskForm.text = selectedUser.value.text?.join() || null;
}
} else if (val === "ROLES") {
getRoleOptions();
if (roleIds.value && roleIds.value.length > 0) {
userTaskForm.candidateGroups = roleIds.value.join() || null;
let textArr = roleOptions.value.filter(
(k) => roleIds.value.indexOf(`ROLE${k.roleId}`) >= 0
);
userTaskForm.text = textArr?.map((k) => k.roleName).join() || null;
}
} else if (val === "DEPTS") {
getDeptTreeData();
if (deptIds.value && deptIds.value.length > 0) {
userTaskForm.candidateGroups = deptIds.value.join() || null;
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.text = textArr?.map((k) => k.label).join() || null;
}
} else if (val === "INITIATOR") {
userTaskForm.assignee = "${initiator}";
userTaskForm.text = "流程发起人";
}
updateElementTask();
if (
val === "ROLES" ||
val === "DEPTS" ||
(val === "USERS" && selectedUser.value.ids.length > 1)
) {
showMultiFlog.value = true;
} else {
showMultiFlog.value = false;
}
multiLoopType.value = "Null";
changeMultiLoopType(multiLoopType.value);
}
function 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 {
multiLoopType.value = "ParallelMultiInstance";
}
}
}
function changeMultiLoopType(type) {
// 取消多实例配置
if (type === "Null") {
window.bpmnInstances.modeling.updateProperties(bpmnElement, {
loopCharacteristics: null,
});
return;
}
multiLoopInstance = window.bpmnInstances.moddle.create(
"bpmn:MultiInstanceLoopCharacteristics",
{ isSequential: isSequential.value }
);
// 更新多实例配置
window.bpmnInstances.modeling.updateProperties(bpmnElement, {
loopCharacteristics: multiLoopInstance,
assignee: "${assignee}",
});
// 完成条件
let completionCondition = null;
// 会签
if (type === "SequentialMultiInstance") {
completionCondition = window.bpmnInstances.moddle.create(
"bpmn:FormalExpression",
{ body: "${nrOfCompletedInstances >= nrOfInstances}" }
);
}
// 或签
if (type === "ParallelMultiInstance") {
completionCondition = window.bpmnInstances.moddle.create(
"bpmn:FormalExpression",
{ body: "${nrOfCompletedInstances > 0}" }
);
}
// 更新模块属性信息
window.bpmnInstances.modeling.updateModdleProperties(
bpmnElement,
multiLoopInstance,
{
collection: "${multiInstanceHandler.getUserIds(execution)}",
elementVariable: "assignee",
completionCondition,
}
);
}
onBeforeUnmount(() => {
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>