|
@@ -0,0 +1,538 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <el-dialog
|
|
|
+ class="modify-flow-detail page-dialog"
|
|
|
+ :visible.sync="modalIsShow"
|
|
|
+ title="流程图编辑"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ :show-close="false"
|
|
|
+ append-to-body
|
|
|
+ fullscreen
|
|
|
+ destroy-on-close
|
|
|
+ @open="visibleChange"
|
|
|
+ >
|
|
|
+ <div class="box-justify" slot="title">
|
|
|
+ <h4 class="el-dialog__title">流程图编辑:{{ instance.name }}</h4>
|
|
|
+ <div>
|
|
|
+ <el-button @click="cancel">取消</el-button>
|
|
|
+ <el-button type="primary" :disabled="isSubmit" @click="submit"
|
|
|
+ >发布</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flow-box">
|
|
|
+ <div class="flow-content">
|
|
|
+ <div class="flow-main">
|
|
|
+ <!-- nodes -->
|
|
|
+ <div
|
|
|
+ v-for="node in nodes"
|
|
|
+ :key="node.id"
|
|
|
+ :id="`node-${node.id}`"
|
|
|
+ :class="[
|
|
|
+ 'flow-node',
|
|
|
+ `node-${node.type.toLowerCase()}`,
|
|
|
+ { 'is-active': curNode.id === node.id }
|
|
|
+ ]"
|
|
|
+ @click="toSelectNode(node)"
|
|
|
+ >
|
|
|
+ <div v-if="node.type === 'PROCESS'" class="flow-node-title">
|
|
|
+ <i
|
|
|
+ v-if="node.type === 'PROCESS'"
|
|
|
+ :class="nodeIcons['PROCESS']"
|
|
|
+ ></i
|
|
|
+ >审批人
|
|
|
+ </div>
|
|
|
+ <div v-if="node.type === 'PROCESS'" class="flow-node-content">
|
|
|
+ <div v-if="node.property.approveUserType === 'USER'">
|
|
|
+ <p>
|
|
|
+ {{
|
|
|
+ node.property.approveUsers
|
|
|
+ .map(item => item.name)
|
|
|
+ .join(",")
|
|
|
+ }}
|
|
|
+ </p>
|
|
|
+ <p v-if="node.property.copyForUsers.length">
|
|
|
+ 抄送:{{
|
|
|
+ node.property.copyForUsers
|
|
|
+ .map(item => item.name)
|
|
|
+ .join(",")
|
|
|
+ }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <div v-else>
|
|
|
+ <p>
|
|
|
+ {{
|
|
|
+ node.property.approveRoles
|
|
|
+ .map(item => item.name)
|
|
|
+ .join(",")
|
|
|
+ }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="flow-node-content">
|
|
|
+ <span><i :class="nodeIcons[node.type]"></i></span>
|
|
|
+ <span>{{ node.content }}</span>
|
|
|
+ </div>
|
|
|
+ <div v-if="node.type !== 'END'" class="flow-link">
|
|
|
+ <div class="node-add" @click.stop="toAddNode(node.id)">
|
|
|
+ <i class="el-icon-plus"></i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="flow-property">
|
|
|
+ <div v-if="curNode.id" class="flow-property-main" :key="curNode.id">
|
|
|
+ <h3 class="flow-property-title">节点属性</h3>
|
|
|
+ <div class="property-part">
|
|
|
+ <h4 class="property-part-title">设置审批人</h4>
|
|
|
+ <div class="flow-radio">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="curNode.property.approveUserType"
|
|
|
+ size="small"
|
|
|
+ @change="curNodeChange"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-for="(val, key) in APPROVE_USER_TYPE"
|
|
|
+ :key="key"
|
|
|
+ :label="key"
|
|
|
+ >{{ val }}</el-radio
|
|
|
+ >
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="curNode.property.approveUserType === 'USER'"
|
|
|
+ class="flow-users"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ type="primary"
|
|
|
+ @click="toAddUser('approveUsers')"
|
|
|
+ >添加成员</el-button
|
|
|
+ >
|
|
|
+ <span class="tips-info">(最多添加5个)</span>
|
|
|
+ <div class="user-list">
|
|
|
+ <el-tag
|
|
|
+ v-for="user in curNode.property.approveUsers"
|
|
|
+ :key="user.id"
|
|
|
+ size="small"
|
|
|
+ closable
|
|
|
+ :disable-transitions="false"
|
|
|
+ @close="deleteApproveUser(user)"
|
|
|
+ >
|
|
|
+ {{ user.name }}
|
|
|
+ </el-tag>
|
|
|
+ <el-button
|
|
|
+ class="user-clear"
|
|
|
+ type="danger"
|
|
|
+ size="mini"
|
|
|
+ plain
|
|
|
+ @click="clearApproveUsers"
|
|
|
+ >清空</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="flow-users">
|
|
|
+ <el-button size="small" type="primary" @click="toAddApproveRole"
|
|
|
+ >添加角色</el-button
|
|
|
+ >
|
|
|
+ <div class="user-list">
|
|
|
+ <el-tag
|
|
|
+ v-for="role in curNode.property.approveRoles"
|
|
|
+ :key="role.id"
|
|
|
+ size="small"
|
|
|
+ closable
|
|
|
+ :disable-transitions="false"
|
|
|
+ @close="deleteApproveRole(role)"
|
|
|
+ >
|
|
|
+ {{ role.name }}
|
|
|
+ </el-tag>
|
|
|
+ <el-button
|
|
|
+ class="user-clear"
|
|
|
+ type="danger"
|
|
|
+ size="mini"
|
|
|
+ plain
|
|
|
+ @click="clearApproveRole"
|
|
|
+ >清空</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="curNode.property.approveUserType === 'USER'"
|
|
|
+ class="property-part"
|
|
|
+ >
|
|
|
+ <h4 class="property-part-title">设置抄送人</h4>
|
|
|
+ <div class="flow-users">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ type="primary"
|
|
|
+ @click="toAddUser('copyForUsers')"
|
|
|
+ >添加成员</el-button
|
|
|
+ >
|
|
|
+ <span class="tips-info">(最多添加5个)</span>
|
|
|
+ <div class="user-list">
|
|
|
+ <el-tag
|
|
|
+ v-for="user in curNode.property.copyForUsers"
|
|
|
+ :key="user.id"
|
|
|
+ closable
|
|
|
+ size="small"
|
|
|
+ :disable-transitions="false"
|
|
|
+ @close="deleteCopyForUser(user)"
|
|
|
+ >
|
|
|
+ {{ user.name }}
|
|
|
+ </el-tag>
|
|
|
+ <el-button
|
|
|
+ class="user-clear"
|
|
|
+ type="danger"
|
|
|
+ size="mini"
|
|
|
+ plain
|
|
|
+ @click="clearCopyForUsers"
|
|
|
+ >清空</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="property-part">
|
|
|
+ <h4 class="property-part-title">高级设置</h4>
|
|
|
+ <p class="property-desc">多人审批时采用的审批方式</p>
|
|
|
+ <div class="flow-radio-v">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="curNode.property.multipleUserApproveType"
|
|
|
+ size="small"
|
|
|
+ @change="curNodeChange"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-for="(val, key) in MULTIPLE_USER_APPROVE_TYPE"
|
|
|
+ :key="key"
|
|
|
+ :label="key"
|
|
|
+ >{{ val }}</el-radio
|
|
|
+ >
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="property-part">
|
|
|
+ <h4 class="property-part-title">驳回设置</h4>
|
|
|
+ <p class="property-desc">允许驳回节点</p>
|
|
|
+ <div class="flow-radio-v">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="curNode.property.rejectType"
|
|
|
+ size="small"
|
|
|
+ @change="curNodeChange"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-for="(val, key) in REJECT_TYPE"
|
|
|
+ :key="key"
|
|
|
+ :label="key"
|
|
|
+ >{{ val }}</el-radio
|
|
|
+ >
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <p class="property-desc">驳回后提交方式</p>
|
|
|
+ <div class="flow-radio-v">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="curNode.property.rejectResubmitType"
|
|
|
+ size="small"
|
|
|
+ @change="curNodeChange"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-for="(val, key) in REJECT_RESUBMIT_TYPE"
|
|
|
+ :key="key"
|
|
|
+ :label="key"
|
|
|
+ >{{ val }}</el-radio
|
|
|
+ >
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button size="mini" type="danger" plain @click="toDeleteNode"
|
|
|
+ >删除节点</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- SelectUserDialog -->
|
|
|
+ <select-user-dialog
|
|
|
+ ref="SelectUserDialog"
|
|
|
+ :users="curAddUsers"
|
|
|
+ @modified="userModified"
|
|
|
+ ></select-user-dialog>
|
|
|
+ <!-- SelectRoleDialog -->
|
|
|
+ <select-role-dialog
|
|
|
+ ref="SelectRoleDialog"
|
|
|
+ :data="curAddRoles"
|
|
|
+ @modified="roleModified"
|
|
|
+ ></select-role-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { deepCopy } from "../../../plugins/utils";
|
|
|
+import { flowNodeList, updateFlowDetail } from "../api";
|
|
|
+import SelectUserDialog from "./SelectUserDialog";
|
|
|
+import SelectRoleDialog from "./SelectRoleDialog";
|
|
|
+
|
|
|
+const DEFAULT_NODE = {
|
|
|
+ id: "",
|
|
|
+ type: "PROCESS",
|
|
|
+ content: "",
|
|
|
+ w: 150,
|
|
|
+ h: 40,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ property: {
|
|
|
+ approveUserType: "USER",
|
|
|
+ approveUsers: [],
|
|
|
+ approveRoles: [],
|
|
|
+ copyForUsers: [],
|
|
|
+ multipleUserApproveType: "ORDER",
|
|
|
+ rejectType: "PREV",
|
|
|
+ rejectResubmitType: "NORMAL"
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "modify-flow-detail",
|
|
|
+ components: { SelectUserDialog, SelectRoleDialog },
|
|
|
+ props: {
|
|
|
+ instance: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ modalIsShow: false,
|
|
|
+ isSubmit: false,
|
|
|
+ initNodes: [
|
|
|
+ {
|
|
|
+ id: "1",
|
|
|
+ type: "START",
|
|
|
+ content: "开始流程",
|
|
|
+ w: 150,
|
|
|
+ h: 40,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ property: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "2",
|
|
|
+ type: "END",
|
|
|
+ content: "结束流程",
|
|
|
+ w: 150,
|
|
|
+ h: 40,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ property: null
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ nodes: [],
|
|
|
+ curNode: {},
|
|
|
+ curAddUsers: [],
|
|
|
+ curAddRoles: [],
|
|
|
+ curAddUserType: "",
|
|
|
+ nodeIcons: {
|
|
|
+ START: "el-icon-video-play",
|
|
|
+ END: "el-icon-switch-button",
|
|
|
+ PROCESS: "el-icon-circle-plus-outline"
|
|
|
+ },
|
|
|
+ APPROVE_USER_TYPE: {
|
|
|
+ USER: "成员",
|
|
|
+ ROLE: "角色"
|
|
|
+ },
|
|
|
+ MULTIPLE_USER_APPROVE_TYPE: {
|
|
|
+ ORDER: "依次审批",
|
|
|
+ ALL: "会签(所有人必须审批)",
|
|
|
+ SOME: "或签(一名审批人同意或拒绝即可)"
|
|
|
+ },
|
|
|
+ REJECT_TYPE: {
|
|
|
+ PREV: "上一节点",
|
|
|
+ START: "发起人节点",
|
|
|
+ PREV_ALL: "该节点前全部节点"
|
|
|
+ },
|
|
|
+ REJECT_RESUBMIT_TYPE: {
|
|
|
+ NORMAL: "按正常流程提交",
|
|
|
+ PREV_STEP: "提交到驳回节点"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async initData(val) {
|
|
|
+ if (val.id) {
|
|
|
+ const data = await flowNodeList(val.id);
|
|
|
+ this.nodes = data;
|
|
|
+ this.toSelectNode(this.nodes[1]);
|
|
|
+ } else {
|
|
|
+ this.nodes = deepCopy(this.initNodes);
|
|
|
+ this.toSelectNode(this.nodes[1]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ visibleChange() {
|
|
|
+ this.initData(this.instance);
|
|
|
+ },
|
|
|
+ cancel() {
|
|
|
+ this.modalIsShow = false;
|
|
|
+ },
|
|
|
+ open() {
|
|
|
+ this.modalIsShow = true;
|
|
|
+ },
|
|
|
+ getDefaultNode() {
|
|
|
+ const nodeId = this.nodes.length
|
|
|
+ ? Math.max.apply(
|
|
|
+ null,
|
|
|
+ this.nodes.map(item => item.id)
|
|
|
+ ) + 1
|
|
|
+ : 1;
|
|
|
+ return { ...deepCopy(DEFAULT_NODE), id: nodeId };
|
|
|
+ },
|
|
|
+ toAddNode(curNodeId) {
|
|
|
+ const newNode = this.getDefaultNode();
|
|
|
+ const pos = this.nodes.findIndex(node => node.id === curNodeId);
|
|
|
+ this.nodes.splice(pos + 1, 0, newNode);
|
|
|
+ this.toSelectNode(newNode);
|
|
|
+ },
|
|
|
+ toDeleteNode() {
|
|
|
+ const pos = this.nodes.findIndex(node => node.id === this.curNode.id);
|
|
|
+ this.nodes.splice(pos, 1);
|
|
|
+ if (this.nodes[pos]) {
|
|
|
+ this.toSelectNode(this.nodes[pos]);
|
|
|
+ } else {
|
|
|
+ let prevPos = pos - 1;
|
|
|
+ prevPos = Math.max(prevPos, 0);
|
|
|
+ this.toSelectNode(this.nodes[prevPos]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ toSelectNode(node) {
|
|
|
+ if (node.type === "START" || node.type === "END") return;
|
|
|
+ this.curNode = node ? deepCopy(node) : {};
|
|
|
+ },
|
|
|
+ curNodeChange() {
|
|
|
+ const pos = this.nodes.findIndex(node => node.id === this.curNode.id);
|
|
|
+ this.nodes.splice(pos, 1, deepCopy(this.curNode));
|
|
|
+ },
|
|
|
+ toAddUser(type) {
|
|
|
+ this.curAddUserType = type;
|
|
|
+ this.curAddUsers = this.curNode.property[this.curAddUserType];
|
|
|
+ this.$refs.SelectUserDialog.open();
|
|
|
+ },
|
|
|
+ userModified(users) {
|
|
|
+ this.curNode.property[this.curAddUserType] = users;
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ deleteApproveUser(user) {
|
|
|
+ console.log(user);
|
|
|
+ const pos = this.curNode.property.approveUsers.findIndex(
|
|
|
+ item => item.id === user.id
|
|
|
+ );
|
|
|
+ this.curNode.property.approveUsers.splice(pos, 1);
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ clearApproveUsers() {
|
|
|
+ this.curNode.property.approveUsers = [];
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ deleteCopyForUser(user) {
|
|
|
+ const pos = this.curNode.property.copyForUsers.findIndex(
|
|
|
+ item => item.id === user.id
|
|
|
+ );
|
|
|
+ this.curNode.property.copyForUsers.splice(pos, 1);
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ clearCopyForUsers() {
|
|
|
+ this.curNode.property.copyForUsers = [];
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ toAddApproveRole() {
|
|
|
+ this.curAddRoles = this.curNode.property.approveRoles;
|
|
|
+ this.$refs.SelectRoleDialog.open();
|
|
|
+ },
|
|
|
+ roleModified(roles) {
|
|
|
+ this.curNode.property.approveRoles = roles;
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ deleteApproveRole(role) {
|
|
|
+ const pos = this.curNode.property.approveRoles.findIndex(
|
|
|
+ item => item.id === role.id
|
|
|
+ );
|
|
|
+ this.curNode.property.approveRoles.splice(pos, 1);
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ clearApproveRole() {
|
|
|
+ this.curNode.property.approveRoles = [];
|
|
|
+ this.curNodeChange();
|
|
|
+ },
|
|
|
+ checkData() {
|
|
|
+ if (!this.nodes.some(node => node.type === "PROCESS")) {
|
|
|
+ this.$message.error("请设置过程节点");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const nodeUserValid = !this.nodes
|
|
|
+ .filter(node => node.type === "PROCESS")
|
|
|
+ .some(node => {
|
|
|
+ if (node.property.approveUserType === "USER") {
|
|
|
+ return !(
|
|
|
+ node.property.approveUsers.length &&
|
|
|
+ node.property.copyForUsers.length
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ return !node.property.approveRoles.length;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (!nodeUserValid) {
|
|
|
+ this.$message.error("请完成节点设置");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ async submit() {
|
|
|
+ if (this.isSubmit) return;
|
|
|
+ if (!this.checkData()) return;
|
|
|
+
|
|
|
+ this.nodes.forEach(node => {
|
|
|
+ const dom = document.getElementById(`node-${node.id}`);
|
|
|
+ node.w = dom.clientWidth;
|
|
|
+ node.h = dom.clientHeight;
|
|
|
+ node.x = dom.offsetLeft;
|
|
|
+ node.y = dom.offsetTop;
|
|
|
+ });
|
|
|
+
|
|
|
+ const nodes = this.nodes.map((node, index) => {
|
|
|
+ let nnode = deepCopy(node);
|
|
|
+ nnode.id = index + 1;
|
|
|
+ if (node.property) {
|
|
|
+ if (node.property.approveUserType === "USER") {
|
|
|
+ nnode.approveRoles = [];
|
|
|
+ } else {
|
|
|
+ nnode.approveUsers = [];
|
|
|
+ nnode.copyForUsers = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nnode;
|
|
|
+ });
|
|
|
+
|
|
|
+ this.isSubmit = true;
|
|
|
+
|
|
|
+ const res = await updateFlowDetail({
|
|
|
+ id: this.instance.id,
|
|
|
+ name: this.instance.name,
|
|
|
+ type: this.instance.type,
|
|
|
+ nodes: nodes
|
|
|
+ }).catch(() => {});
|
|
|
+ this.isSubmit = false;
|
|
|
+ if (!res) return;
|
|
|
+
|
|
|
+ this.$message.success("编辑成功!");
|
|
|
+ this.$emit("modified");
|
|
|
+ this.cancel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|