Compare commits

..

5 Commits

Author SHA1 Message Date
clay 72fd575dde Merge pull request 'master' (#33) from master into pro
continuous-integration/drone/push Build is passing Details
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/33
2023-03-03 18:06:35 +00:00
clay 74fbae3095 clay : 用户组件完成 2023-03-04 01:52:18 +08:00
邓洁 b271979372 dengjie : 用户选择器暂时能用,待优化样式 2023-03-04 01:18:43 +08:00
邓洁 dabdc0551e dengjie : 用户选择器暂时能用,待优化样式 2023-03-03 23:07:03 +08:00
邓洁 8df27644fb dengjie : 用户选择器占时能用,待优化样式 2023-03-03 22:51:19 +08:00
18 changed files with 792 additions and 282 deletions

View File

@ -34,6 +34,14 @@ export function getDepartmentTree() {
}) })
} }
export default { //根据角色或者部门获取到对应的数据
getOrgTree, getUserByName, getRole,getDepartmentTree export function getUserTree(type,chooseId){
return request({
url:`/admin/user/choose/${type}/${chooseId}`,
method:'get'
})
}
export default {
getOrgTree, getUserByName, getRole,getDepartmentTree,getUserTree
} }

View File

@ -11,6 +11,7 @@ Vue.prototype.$axios = axios;
export function getBaseUrl(){ export function getBaseUrl(){
return "http://gateway.mytwins.top" return "http://gateway.mytwins.top"
// return "http://192.168.101.7:8000"
// return "http://localhost:8000" // return "http://localhost:8000"
} }

View File

@ -5,25 +5,40 @@
<div class="candidate" v-loading="loading"> <div class="candidate" v-loading="loading">
<div style="padding: 5px 8px;"> <div style="padding: 5px 8px;">
<el-input v-model="filterText" style="width: 100%;" size="small" <el-input v-model="filterText" style="width: 100%;" size="small"
clearable placeholder="输入关键字进行过滤" prefix-icon="el-icon-search" /> clearable placeholder="输入关键字进行过滤" prefix-icon="el-icon-search"/>
<div style="margin-top: 5px"> <div style="margin-top: 5px">
<el-button size="mini" type="primary" plain style="padding:4px 6px;">人员</el-button> <el-radio-group v-model="radio" size="mini" @input="radioChange">
<el-button size="mini" type="primary" plain style="padding:4px 6px;">部门</el-button> <el-radio-button :label="0">角色</el-radio-button>
<el-radio-button :label="1">部门</el-radio-button>
</el-radio-group>
</div> </div>
</div> </div>
<!-- 人员选择 --> <!-- 人员选择 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="deptList.length === 0"/> <el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/>
<el-scrollbar style="height:350px"> <el-scrollbar style="height:317px">
<el-tree :data="deptList" ref="tree" :props="deptProps" empty-text="" node-key="value" default-expand-all <el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value"
:show-checkbox="showCheckbox" highlight-current :check-strictly="multiple===false" :default-expanded-keys="expandedKeys"
@check-change="handleCheckChange" @node-click="(node,check)=>handle(node,check)" @node-click="handleChange"
:filter-node-method="filterNode"> :filter-node-method="filterNode"
<div class="custom-tree-node" slot-scope="{ node }" style="width: 100%"> >
<i class="el-icon-folder-opened" style="margin-right: 5px"></i>{{ node.label }} <div class="tree-node" slot-scope="{ node,data }">
<div v-if="data.type === 0" style="display: flex;align-items: center">
<el-avatar :src="data.avatar"></el-avatar>
{{ node.label }}
</div>
<div v-else-if="data.type === 1">
<el-icon class="el-icon-user-solid"/>
{{ node.label }}
</div>
<div v-else>
<el-icon class="el-icon-folder-opened"/>
{{ node.label }}
</div>
</div> </div>
</el-tree> </el-tree>
</el-scrollbar> </el-scrollbar>
</div> </div>
<div class="selected"> <div class="selected">
<div class="count"> <div class="count">
<span>已选 {{ selectList.length }} </span> <span>已选 {{ selectList.length }} </span>
@ -32,9 +47,9 @@
<div class="org-items" style="height: 350px;"> <div class="org-items" style="height: 350px;">
<el-empty :image-size="100" description="请点击左侧列表选择数据" v-show="selectList.length === 0"/> <el-empty :image-size="100" description="请点击左侧列表选择数据" v-show="selectList.length === 0"/>
<div v-for="(selectItem, selectIndex) in selectList" :key="selectIndex" class="org-item"> <div v-for="(selectItem, selectIndex) in selectList" :key="selectIndex" class="org-item">
<i class="el-icon-folder-opened"></i> <el-avatar :src="selectItem.avatar" style="margin-right: 5px;"></el-avatar>
<span>{{ selectItem.label }}</span> {{ selectItem.name }}
<i class="el-icon-close" @click="noSelected(selectItem)" v-if="showCheckbox===false"></i> <i class="el-icon-close" @click="noSelected(selectItem)"></i>
</div> </div>
</div> </div>
</div> </div>
@ -44,10 +59,10 @@
</template> </template>
<script> <script>
import {getDepartmentTree} from "@/api/org"; import {getUserTree} from "@/api/org";
export default { export default {
name: "departmentPicker", name: "Test",
props: { props: {
value: { value: {
type: Array, type: Array,
@ -66,16 +81,24 @@ export default {
}, },
data() { data() {
return { return {
radio: 0,
chooseId: 0,
selectItem: {
type: -1,
value: '0',
},
activeNames: ['1'],
visible: false, visible: false,
loading: false, loading: false,
title: "请选择", title: "请选择",
selectList: [], selectList: [],
filterText: "", filterText: "",
deptList: [], dataList: [],
deptProps: { expandedKeys: [],
defaultProps: {
value: 'value', value: 'value',
label: 'label', label: 'name',
children: 'children' children: 'children',
} }
}; };
}, },
@ -84,9 +107,6 @@ export default {
this.$refs.tree.filter(val); this.$refs.tree.filter(val);
} }
}, },
created() {
this.getDepartmentTree();
},
computed: { computed: {
_value: { _value: {
get() { get() {
@ -98,111 +118,77 @@ export default {
} }
}, },
methods: { methods: {
// radioChange(e) {
getDepartmentTree() { this.selectItem.type = -2
getDepartmentTree().then(res => { this.chooseId = 0
// const jsona = JSON.stringify(res.data) // res.data this.radio = e
// const jsonb = jsona.replace(/"value"/g, '"deptId"') // this.expandedKeys = []
// const jsonc = jsonb.replace(/"label"/g, '"deptName"') this.getList();
this.deptList = res.data },
// console.log("===========", this.deptList); getList() {
getUserTree(this.radio, this.chooseId).then(res => {
if (res.data) {
if (this.selectItem.type === -1 || this.selectItem.type === -2) {
this.dataList = this.setData(res.data)
} else if (this.selectItem.type === 1) {
this.selectItem.children = res.data
} else if (this.selectItem.type === 2) {
this.selectItem.children = this.setData(res.data)
}
}
}); });
}, },
setData(source) {
for (let item of source) {
this.$set(item, "value", this.selectItem.value + "-" + item.id)
this.$set(item, "children", [])
}
return source;
},
// //
filterNode(value, data) { filterNode(value, data) {
if (!value) return true; if (!value) return true;
return data.deptName.indexOf(value) !== -1; return data.name.indexOf(value) !== -1;
}, },
// //
showUserPicker() { showUserPicker() {
this.getList();
this.visible = true; this.visible = true;
}, },
/** //
* 选中部门 handleChange(item, check) {
* @param data 选择的每个节点item this.selectItem = item
* @param checked 是否选中 this.expandedKeys.push(item.value)
*/ if (item.type !== 0) {
handleCheckChange(data, checked) { this.chooseId = item.id
// this.getList()
if (this.showCheckbox) {
// +
if (this.multiple ) {
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === data.value) {
this.selectList.splice(i, 1);
break;
}
}
if (checked) {
if(data.children === undefined){
this.selectList.push(data);
}
} else if (data === '1') {
this.$refs.tree.setCheckedKeys([]);
this.selectList = [];
}
} else {// +
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === data.value) {
this.selectList.splice(i, 1);
break;
}
}
if (checked) {
this.$refs.tree.setCheckedNodes([data]);
// this.$refs.tree.setCheckedKeys([]);
this.selectList = [data];
} else if (data === '1') {
this.selectList = [];
this.$refs.tree.setCheckedKeys([]);
}
}
} }
this._value = this.selectList if (!item.children) {
},
//,tree-item
/**
* 可以点击树节点deptName,进行选择
* @param node 选择的每个节点item
* @param check checked(checkbox选择框)是否选中
*/
handle(node, check) {
if (check.isLeaf !== false) {
if (this.multiple) { if (this.multiple) {
// //
for (let i = 0; i < this.selectList.length; i++) { for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === node.value) { if (this.selectList[i].id === item.id) {
this.selectList.splice(i, 1); this.selectList.splice(i, 1);
break; break;
} }
} }
check.checked = true this.selectList.push(item);
this.selectList.push(node); console.log('多选this.selectList', this.selectList);
} else { } else {
check.checked = true this.selectList = [item];
this.selectList = [node];
} }
} }
this._value = this.selectList this._value = this.selectList
}, },
//,× //×
noSelected(selectItem) { noSelected(selectItem) {
for (let i = 0; i < this.selectList.length; i++) { for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === selectItem.value) { if (this.selectList[i].value === selectItem.value) {
this.selectList.splice(i, 1); this.selectList.splice(i, 1);
this.$refs.tree.setCheckedKeys(i);
break; break;
} }
} }
if (this.showCheckbox) { selectItem.selected = false;
// +
if (this.multiple === false) {
this.$refs.tree.setCheckedKeys([]);
}
}
}, },
// //
clearSelected() { clearSelected() {
@ -211,11 +197,7 @@ export default {
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}).then(() => { }).then(() => {
if (!this.showCheckbox) { this.selectList = []
this.selectList = []
}else {
this.handleCheckChange("1");
}
}); });
}, },
// //
@ -229,16 +211,33 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
@containWidth: 278px; @containWidth: 278px;
/deep/ .el-tree-node {
.el-tree-node__children {
.el-tree-node__content {
height: 42px;
}
}
}
///deep/ .el-tree-node { .tree-node {
// .is-leaf + .el-checkbox .el-checkbox__inner { div {
// display: inline-block; .el-avatar {
// } width: 40px;
// height: 40px;
// .el-checkbox .el-checkbox__inner { margin-right: 5px;
// display: none; }
// } }
//} }
/deep/ .el-tree-node {
.is-leaf + .el-checkbox .el-checkbox__inner {
display: inline-block;
}
.el-checkbox .el-checkbox__inner {
display: none;
}
}
/deep/ .el-dialog__body { /deep/ .el-dialog__body {
padding: 10px 20px; padding: 10px 20px;

View File

@ -0,0 +1,331 @@
<template>
<w-dialog :border="false" closeFree width="600px" @ok="selectConfirm" :title="title" v-model="visible">
<div>
<div class="picker">
<div class="candidate" v-loading="loading">
<div style="padding: 5px 8px;">
<el-input v-model="filterText" style="width: 100%;" size="small"
clearable placeholder="输入关键字进行过滤" prefix-icon="el-icon-search"/>
<div style="margin-top: 5px">
<el-radio-group v-model="radio" size="mini" @input="radioChange">
<el-radio-button :label="0">角色</el-radio-button>
<el-radio-button :label="1">部门</el-radio-button>
</el-radio-group>
</div>
</div>
<!-- 人员选择 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/>
<el-scrollbar style="height:317px">
<el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value"
:default-expanded-keys="expandedKeys"
@node-click="handleChange"
:filter-node-method="filterNode"
>
<div class="tree-node" slot-scope="{ node,data }">
<div v-if="data.type === 0" style="display: flex;align-items: center">
<el-avatar :src="data.avatar"></el-avatar>
{{ node.label }}
</div>
<div v-else-if="data.type === 1">
<el-icon class="el-icon-user-solid"/>
{{ node.label }}
</div>
<div v-else>
<el-icon class="el-icon-folder-opened"/>
{{ node.label }}
</div>
</div>
</el-tree>
</el-scrollbar>
</div>
<div class="selected">
<div class="count">
<span>已选 {{ selectList.length }} </span>
<span @click="clearSelected">清空</span>
</div>
<div class="org-items" style="height: 350px;">
<el-empty :image-size="100" description="请点击左侧列表选择数据" v-show="selectList.length === 0"/>
<div v-for="(selectItem, selectIndex) in selectList" :key="selectIndex" class="org-item">
<el-avatar :src="selectItem.avatar" style="margin-right: 5px;"></el-avatar>
{{ selectItem.name }}
<i class="el-icon-close" @click="noSelected(selectItem)"></i>
</div>
</div>
</div>
</div>
</div>
</w-dialog>
</template>
<script>
import {getUserTree} from "@/api/org";
export default {
name: "UserPicker",
props: {
value: {
type: Array,
default: () => {
return [];
}
},
multiple: { //
default: true,
type: Boolean
},
showCheckbox: { //
default: true,
type: Boolean
}
},
data() {
return {
radio: 0,
chooseId: 0,
selectItem: {
type: -1,
value: '0',
},
activeNames: ['1'],
visible: false,
loading: false,
title: "请选择",
selectList: [],
filterText: "",
dataList: [],
expandedKeys: [],
defaultProps: {
value: 'value',
label: 'name',
children: 'children',
}
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
computed: {
_value: {
get() {
return this.value
},
set(val) {
this.$emit("input", val)
}
}
},
methods: {
radioChange(e) {
this.selectItem.type = -2
this.chooseId = 0
this.radio = e
this.expandedKeys = []
this.getList();
},
getList() {
getUserTree(this.radio, this.chooseId).then(res => {
if (res.data) {
if (this.selectItem.type === -1 || this.selectItem.type === -2) {
this.dataList = this.setData(res.data)
} else if (this.selectItem.type === 1) {
this.selectItem.children = res.data
} else if (this.selectItem.type === 2) {
this.selectItem.children = this.setData(res.data)
}
}
});
},
setData(source) {
for (let item of source) {
this.$set(item, "value", this.selectItem.value + "-" + item.id)
this.$set(item, "children", [])
}
return source;
},
//
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
//
showUserPicker() {
this.getList();
this.visible = true;
},
//
handleChange(item, check) {
this.selectItem = item
this.expandedKeys.push(item.value)
if (item.type !== 0) {
this.chooseId = item.id
this.getList()
}
if (!item.children) {
if (this.multiple) {
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].id === item.id) {
this.selectList.splice(i, 1);
break;
}
}
this.selectList.push(item);
} else {
this.selectList = [item];
}
}
// this._value = this.selectList
},
//×
noSelected(selectItem) {
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === selectItem.value) {
this.selectList.splice(i, 1);
break;
}
}
selectItem.selected = false;
},
//
clearSelected() {
this.$confirm("您确定要清空已选中的项?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.selectList = []
});
},
//
selectConfirm() {
this.$emit("ok", this.selectList);
this.visible = false;
}
}
};
</script>
<style lang="less" scoped>
@containWidth: 278px;
/deep/ .el-tree-node {
.el-tree-node__children {
.el-tree-node__content {
height: 42px;
}
}
}
.tree-node {
div {
.el-avatar {
width: 40px;
height: 40px;
margin-right: 5px;
}
}
}
/deep/ .el-tree-node {
.is-leaf + .el-checkbox .el-checkbox__inner {
display: inline-block;
}
.el-checkbox .el-checkbox__inner {
display: none;
}
}
/deep/ .el-dialog__body {
padding: 10px 20px;
}
.picker {
height: 402px;
position: relative;
text-align: left;
.candidate {
left: 0;
top: 0;
}
}
.candidate, .selected {
position: absolute;
display: inline-block;
width: @containWidth;
height: 400px;
border: 1px solid #e8e8e8;
}
.selected {
border-left: none;
right: 0;
top: 0;
.count {
width: calc(@containWidth - 20px);
padding: 10px;
display: inline-block;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 5px;
& > span:nth-child(2) {
float: right;
color: #c75450;
cursor: pointer;
}
}
}
.org-items {
overflow-y: auto;
height: 350px;
.el-icon-close {
position: absolute;
right: 5px;
cursor: pointer;
font-size: larger;
}
.org-item {
margin: 0 5px;
border-radius: 5px;
position: relative;
padding: 7px 5px;
display: flex;
align-items: center;
cursor: pointer;
&:hover {
background: #f1f1f1;
}
> span {
margin-left: 5px;
}
}
}
/deep/ .el-scrollbar .el-scrollbar__wrap {
overflow-x: hidden;
}
::-webkit-scrollbar {
float: right;
width: 4px;
height: 4px;
background-color: white;
}
::-webkit-scrollbar-thumb {
border-radius: 16px;
background-color: #efefef;
}
</style>

View File

@ -8,6 +8,7 @@ export default new Vuex.Store({
state: { state: {
nodeMap: new Map(), nodeMap: new Map(),
parentMap: new Map(), parentMap: new Map(),
selectUserMap: new Map(),
isEdit: null, isEdit: null,
selectedNode: {}, selectedNode: {},
selectFormItem: null, selectFormItem: null,
@ -29,7 +30,11 @@ export default new Vuex.Store({
state.isEdit = val state.isEdit = val
} }
}, },
getters: {}, getters: {
selectedNode(){
return state.selectedNode
}
},
actions: {}, actions: {},
modules: {} modules: {}
}) })

View File

@ -30,7 +30,6 @@
<script> <script>
import ProcessTree from './process/ProcessTree.vue' import ProcessTree from './process/ProcessTree.vue'
// import ProcessTree from './process/ProcessTreeBack.vue'
import NodeConfig from '../../common/process/config/NodeConfig' import NodeConfig from '../../common/process/config/NodeConfig'
// //
export default { export default {

View File

@ -9,7 +9,7 @@
</div> </div>
<el-main> <el-main>
<div class="design" :style="'transform: scale('+ scale / 100 +');'"> <div class="design" :style="'transform: scale('+ scale / 100 +');'">
<process-tree ref="process-tree-view"/> <process-tree ref="process-tree"/>
</div> </div>
</el-main> </el-main>
</div> </div>
@ -34,6 +34,21 @@ export default {
}, },
beforeDestroy() { beforeDestroy() {
this.$store.state.diagramMode = "design"; this.$store.state.diagramMode = "design";
},
methods:{
//
validate() {
return this.$refs["process-tree"].validateProcess()
},
validate_ASSIGN_USER(err) {
if (this.config.props.assignedUser.length > 0) {
return true;
} else {
this.errorInfo = '请指定审批人员'
err.push(`${this.config.name} 未指定审批人员`)
return false
}
},
} }
}; };
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<!--todo 预览时渲染的component--> <!--todo 预览时渲染的component-->
<component ref="form" :is="config.name" :mode="mode" :perm="perm" v-model="_value" v-bind="config.props"/> <component ref="form" :is="config.name" :config="config" :mode="mode" :perm="perm" v-model="_value" v-bind="config.props"/>
</template> </template>
<script> <script>

View File

@ -261,6 +261,7 @@ export default {
}, },
// //
selectNode(node) { selectNode(node) {
console.log(node)
this.$store.commit('selectedNode', node) this.$store.commit('selectedNode', node)
console.log(node, "node") console.log(node, "node")
if (!this.isConcurrentNode(node)) { if (!this.isConcurrentNode(node)) {

View File

@ -2,39 +2,50 @@
<div style="max-width: 350px"> <div style="max-width: 350px">
<template v-if="mode === 'DESIGN'"> <template v-if="mode === 'DESIGN'">
<el-button disabled icon="el-icon-user" type="primary" size="mini" round>选择人员</el-button> <el-button disabled icon="el-icon-user" type="primary" size="mini" round>选择人员</el-button>
<span class="placeholder"> {{placeholder}}</span> <span class="placeholder"> {{ placeholder }}</span>
</template> </template>
<template v-else> <template v-else>
<template v-if="perm === 'E'"> <template v-if="perm === 'E'">
<el-button icon="el-icon-user" type="primary" size="mini" round @click="$refs.userPicker.showUserPicker()">选择人员</el-button> <el-button icon="el-icon-user" type="primary" size="mini" round @click="$refs.userPicker.showUserPicker()">
<org-picker type="user" :multiple="multiple" ref="userPicker" :selected="_value" @ok="selected"/> 选择人员
<span class="placeholder"> {{placeholder}}</span> </el-button>
<div style="margin-top: 5px"> <user-select type="user" :multiple="multiple" ref="userPicker" :selected="_value" @ok="selected"/>
<el-tag size="mini" style="margin: 5px" closable v-for="(user, i) in _value" :key="i" @close="delDept(i)">{{user.label}}</el-tag> <span class="placeholder"> {{ placeholder }}</span>
<div style="display: flex;">
<div class="userStyle" v-for="(user, i) in _value" :key="i">
<span @click="delDept(i)">×</span>
<el-avatar :src="user.avatar"/>
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
<span>{{ user.name }}</span>
</el-tooltip>
</div>
</div> </div>
</template> </template>
<template v-else-if="perm === 'R'"> <template v-else-if="perm === 'R'">
<div v-for="(user, i) in _value" :key="i"> <div style="display: flex;">
{{user.label}} <div v-for="(user, i) in _value" :key="i" class="view_user">
<el-avatar :src="user.avatar"/>
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
<span>{{ user.name }}</span>
</el-tooltip>
</div>
</div> </div>
</template> </template>
</template> </template>
</div> </div>
</template> </template>
<script> <script>
import componentMinxins from '../ComponentMinxins' import componentMinxins from '../ComponentMinxins'
import OrgPicker from "@/components/common/Test"; import UserSelect from "@/components/common/UserPicker";
export default { export default {
mixins: [componentMinxins], mixins: [componentMinxins],
name: "UserPicker", name: "UserPicker",
components: {OrgPicker}, components: {UserSelect},
props: { props: {
value:{ value: {
type: Array, type: Array,
default: () => { default: () => {
return [] return []
@ -48,32 +59,75 @@ export default {
type: String, type: String,
default: '请选择人员' default: '请选择人员'
}, },
multiple:{ config: {
type: Object,
default: () => {
return {}
}
},
multiple: {
type: Boolean, type: Boolean,
default: false default: false
} }
}, },
data() { data() {
return { return {
showOrgSelect: false showPickerSelect: false
} }
}, },
methods: { methods: {
selected(values){ selected(values) {
this.showOrgSelect = false this.showPickerSelect = false
this._value = values this._value = values
}, },
delDept(i){ delDept(i) {
this._value.splice(i, 1) this._value.splice(i, 1)
} }
} }
} }
</script> </script>
<style scoped> <style scoped lang="less">
.placeholder{ .placeholder {
margin-left: 10px; margin-left: 10px;
color: #adabab; color: #adabab;
font-size: smaller; font-size: smaller;
} }
.userStyle {
width: 45px;
margin-top: 10px;
display: flex;
flex-direction: column;
position: relative;
margin-right: 10px;
span:first-child {
position: absolute;
right: -3px;
top: -11px;
cursor: pointer;
}
span:last-child {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden
}
}
.view_user {
width: 45px;
display: flex;
flex-direction: column;
margin-right: 10px;
span {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden
}
}
</style> </style>

View File

@ -7,7 +7,7 @@
</el-radio-group> </el-radio-group>
<div v-if="nodeProps.assignedType === 'ASSIGN_USER'"> <div v-if="nodeProps.assignedType === 'ASSIGN_USER'">
<el-button size="mini" icon="el-icon-plus" type="primary" @click="selectUser" round>选择人员</el-button> <el-button size="mini" icon="el-icon-plus" type="primary" @click="selectUser" round>选择人员</el-button>
<org-items v-model="nodeProps.assignedUser"/> <avatar-ellipsis :row="3" :user-info="nodeProps.assignedUser"/>
</div> </div>
<div v-else-if="nodeProps.assignedType === 'SELF_SELECT'"> <div v-else-if="nodeProps.assignedType === 'SELF_SELECT'">
<el-radio-group size="mini" v-model="nodeProps.selfSelect.multiple"> <el-radio-group size="mini" v-model="nodeProps.selfSelect.multiple">
@ -118,8 +118,8 @@
</el-form-item> </el-form-item>
<el-form-item label="🙅‍ 如果审批被驳回 👇"> <el-form-item label="🙅‍ 如果审批被驳回 👇">
<el-radio-group v-model="nodeProps.refuse.type"> <el-radio-group v-model="nodeProps.refuse.type">
<el-radio label="TO_END">直接结束流程</el-radio> <el-radio label="TO_INITIAL">直接结束流程</el-radio>
<!-- <el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>--> <el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>
<!-- <el-radio label="TO_NODE">驳回到指定节点</el-radio>--> <!-- <el-radio label="TO_NODE">驳回到指定节点</el-radio>-->
</el-radio-group> </el-radio-group>
<div v-if="nodeProps.refuse.type === 'TO_NODE'"> <div v-if="nodeProps.refuse.type === 'TO_NODE'">
@ -133,19 +133,22 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<role-picker :title="pickerTitle" multiple ref="rolePicker" :v-model="select" @ok="selectedRole"/> <role-picker title="请选择人员" multiple ref="rolePicker" :v-model="roleList" @ok="selectedRole"/>
<user-picker title="请选择系统角色" multiple ref="userPicker" :v-model="assignedUser" @ok="selectedUser"/>
</div> </div>
</template> </template>
<script> <script>
import RolePicker from "@/components/common/RolePicker"; import RolePicker from "@/components/common/RolePicker";
import UserPicker from "@/components/common/UserPicker";
import RoleItems from "../RoleItems"; import RoleItems from "../RoleItems";
import OrgItems from "../OrgItems"; import OrgItems from "../OrgItems";
export default { export default {
name: "ApprovalNodeConfig", name: "ApprovalNodeConfig",
components: {RoleItems, OrgItems, RolePicker}, components: {RoleItems, OrgItems, RolePicker, UserPicker},
props: { props: {
config: { config: {
type: Object, type: Object,
@ -158,7 +161,6 @@ export default {
return { return {
showOrgSelect: false, showOrgSelect: false,
orgPickerSelected: [], orgPickerSelected: [],
orgPickerType: "user",
approvalTypes: [ approvalTypes: [
{name: "指定人员", type: "ASSIGN_USER"}, {name: "指定人员", type: "ASSIGN_USER"},
{name: "发起人自选", type: "SELF_SELECT"}, {name: "发起人自选", type: "SELF_SELECT"},
@ -174,27 +176,27 @@ export default {
nodeProps() { nodeProps() {
return this.$store.state.selectedNode.props; return this.$store.state.selectedNode.props;
}, },
select() { assignedUser: {
return this.config.assignedUser || []; get() {
return this.config.assignedUser || [];
},
set(val) {
this.config.assignedUser = val
}
}, },
roleList() { roleList:{
return this.config.roleList || []; get() {
return this.config.roleList || [];
},
set(val) {
this.config.roleList = val
}
}, },
forms() { forms() {
return this.$store.state.design.formItems.filter(f => { return this.$store.state.design.formItems.filter(f => {
return f.name === "UserPicker"; return f.name === "UserPicker";
}); });
}, },
pickerTitle() {
switch (this.orgPickerType) {
case "user":
return "请选择人员";
case "role":
return "请选择系统角色";
default:
return null;
}
},
nodeOptions() { nodeOptions() {
let values = []; let values = [];
const excType = ["ROOT", "EMPTY", "CONDITION", "CONDITIONS", "CONCURRENT", "CONCURRENTS"]; const excType = ["ROOT", "EMPTY", "CONDITION", "CONDITIONS", "CONCURRENT", "CONCURRENTS"];
@ -224,34 +226,30 @@ export default {
}, },
methods: { methods: {
selectUser() { selectUser() {
this.orgPickerSelected = this.select; this.$refs.userPicker.showUserPicker()
this.orgPickerType = "user";
this.$refs.orgPicker.show();
}, },
selectNoSetUser() { selectNoSetUser() {
this.orgPickerSelected = this.config.nobody.assignedUser;
this.orgPickerType = "user";
this.$refs.orgPicker.show();
}, },
//, //,
selectRole() { selectRole() {
this.orgPickerType = "role";
this.$refs.rolePicker.showRolePicker(); this.$refs.rolePicker.showRolePicker();
}, },
selectedRole(select) { selectedRole(select) {
select.forEach(val => { this.roleList = select
for (let i = 0; i < this.roleList.length; i++) { },
if (this.roleList[i].roleId === val.roleId) { selectedUser(select) {
this.roleList.splice(i, 1); let userInfoList = []
break; for (let val of select) {
} let userInfo = {
} id: val.id,
this.roleList.push(val); name: val.name,
}); avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.assignedUser = userInfoList
}, },
removeOrgItem(index) {
this.select.splice(index, 1);
}
} }
}; };
</script> </script>

View File

@ -1,21 +1,21 @@
<template> <template>
<div> <div>
<el-button size="mini" icon="el-icon-plus" type="primary" @click="selectOrg" round>选择抄送人</el-button> <el-button size="mini" icon="el-icon-plus" type="primary" @click="selectUser" round>选择抄送人</el-button>
<div class="option"> <div class="option">
<el-checkbox label="允许发起人添加抄送人" v-model="config.shouldAdd"></el-checkbox> <el-checkbox label="允许发起人添加抄送人" v-model="shouldAdd"></el-checkbox>
</div> </div>
<org-items v-model="select"/> <!-- <org-items v-model="select"/>-->
<org-picker multiple ref="orgPicker" :selected="select" @ok="selected"/> <avatar-ellipsis :row="3" :user-info="assignedUser"/>
<user-picker title="请选择系统角色" multiple ref="userPicker" :v-model="assignedUser" @ok="selectedUser"/>
</div> </div>
</template> </template>
<script> <script>
import OrgPicker from "@/components/common/Test"; import UserPicker from "@/components/common/UserPicker";
import OrgItems from "../OrgItems";
export default { export default {
name: "CcNodeConfig.vue", name: "CcNodeConfig.vue",
components: {OrgPicker, OrgItems}, components: {UserPicker},
props:{ props:{
config:{ config:{
type: Object, type: Object,
@ -25,7 +25,16 @@ export default {
} }
}, },
computed:{ computed:{
select: { shouldAdd:{
get(){
return this.config.shouldAdd || false
},
set(val){
console.log("asdjhajdhasjdhasdhasjdad")
this.config.shouldAdd = val
}
},
assignedUser: {
get(){ get(){
return this.config.assignedUser || [] return this.config.assignedUser || []
}, },
@ -38,16 +47,21 @@ export default {
return {} return {}
}, },
methods: { methods: {
selectOrg() { selectUser() {
this.$refs.orgPicker.show() this.$refs.userPicker.showUserPicker()
}, },
selected(select) { selectedUser(select) {
console.log(select) let userInfoList = []
this.select = Object.assign([], select) for (let val of select) {
let userInfo = {
id: val.id,
name: val.name,
avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.assignedUser = userInfoList
}, },
removeOrgItem(index){
this.select.splice(index, 1)
}
} }
} }
</script> </script>

View File

@ -1,8 +1,10 @@
<template> <template>
<node :title="config.name" :show-error="showError" :content="content" :user-info="config.props.assignedUser" :error-info="errorInfo" <node :title="config.name" :show-error="showError" :content="content"
:show-avatar="config.props.assignedType === 'ASSIGN_USER'" :user-info="assignedUser"
:error-info="errorInfo"
:select-user="selectUser"
@selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)" @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
placeholder="请设置审批人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/> placeholder="请设置审批人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/>
</template> </template>
<script> <script>
@ -10,8 +12,8 @@ import Node from './Node'
export default { export default {
name: "ApprovalNode", name: "ApprovalNode",
props:{ props: {
config:{ config: {
type: Object, type: Object,
default: () => { default: () => {
return {} return {}
@ -25,120 +27,136 @@ export default {
errorInfo: '', errorInfo: '',
} }
}, },
computed:{ computed: {
viewer(){ selectUser() {
return {
show: this.config.props.assignedType === 'SELF_SELECT',
multiple: this.config.props.selfSelect.multiple
};
},
assignedUser() {
if (this.config.props.assignedType === 'SELF_SELECT') {
this.config.props.assignedUser = []
}
return this.config.props.assignedUser;
},
viewer() {
return this.$store.state.diagramMode === 'viewer' return this.$store.state.diagramMode === 'viewer'
}, },
preview(){ preview() {
return this.$store.state.preview; return this.$store.state.preview;
}, },
headerBgc() { headerBgc() {
if (this.preview || !this.viewer){ if (this.preview || !this.viewer) {
return '#ff943e' return '#ff943e'
}else { } else {
return this.config.props.headerBgc return this.config.props.headerBgc
} }
}, },
content(){ content() {
const config = this.config.props const config = this.config.props
switch (config.assignedType){ switch (config.assignedType) {
case "ASSIGN_USER": case "ASSIGN_USER":
if (config.assignedUser.length > 0){ if (config.assignedUser.length > 0) {
let texts = [] let texts = []
config.assignedUser.forEach(org => texts.push(org.name)) config.assignedUser.forEach(org => texts.push(org.name))
return String(texts).replaceAll(',', '、') return String(texts).replaceAll(',', '、')
}else { } else {
return '请指定审批人' return '请指定审批人'
} }
case "SELF": case "SELF":
return '发起人自己' return '发起人自己'
case "SELF_SELECT": case "SELF_SELECT":
return config.selfSelect.multiple ? '发起人自选多人':'发起人自选一人' return config.selfSelect.multiple ? '发起人自选多人' : '发起人自选一人'
case "LEADER_TOP": case "LEADER_TOP":
return '多级主管依次审批' return '多级主管依次审批'
case "LEADER": case "LEADER":
return config.leader.level > 1 ? '发起人的第 ' + config.leader.level + ' 级主管' : '发起人的直接主管' return config.leader.level > 1 ? '发起人的第 ' + config.leader.level + ' 级主管' : '发起人的直接主管'
case "FORM_USER": case "FORM_USER":
if (!config.formUser || config.formUser === ''){ if (!config.formUser || config.formUser === '') {
return '表单内联系人(未选择)' return '表单内联系人(未选择)'
}else { } else {
let text = this.getFormItemById(config.formUser) let text = this.getFormItemById(config.formUser)
if (text && text.title){ if (text && text.title) {
return `表单(${text.title})内的人员` return `表单(${text.title})内的人员`
}else { } else {
return '该表单已被移除😥' return '该表单已被移除😥'
} }
} }
case "ROLE": case "ROLE":
if (config.roleList.length > 0){ if (config.roleList.length > 0) {
return config.roleList.map(role=>{ return config.roleList.map(role => {
return role.roleName; return role.roleName;
}).join("、") }).join("、")
}else { } else {
return '指定角色(未设置)' return '指定角色(未设置)'
} }
default: return '未知设置项😥' default:
return '未知设置项😥'
} }
} }
}, },
methods: { methods: {
getFormItemById(id){ getFormItemById(id) {
return this.$store.state.design.formItems.find(item => item.id === id) return this.$store.state.design.formItems.find(item => item.id === id)
}, },
// //
validate(err){ validate(err) {
try { try {
this.showError = !this[`validate_${this.config.props.assignedType}`](err) this.showError = !this[`validate_${this.config.props.assignedType}`](err)
if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) { if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) {
this.errorInfo = '审批人为空时, 转交给指定人员:【请指定一个具体的人】' this.errorInfo = '审批人为空时, 转交给指定人员:【请指定一个具体的人】'
err.push('审批人为空时, 转交给指定人员:【请指定一个具体的人】') err.push('审批人为空时, 转交给指定人员:【请指定一个具体的人】')
this.showError = true this.showError = true
} else if (this.viewer) {
this.showError = !this.validate_ASSIGN_USER(err)
console.log(this.showError)
} }
return this.showError return this.showError
} catch (e) { } catch (e) {
console.log(e)
return true; return true;
} }
}, },
validate_ASSIGN_USER(err){ validate_ASSIGN_USER(err) {
if(this.config.props.assignedUser.length > 0){ if (this.config.props.assignedUser.length > 0) {
return true; return true;
}else { } else {
this.errorInfo = '请指定审批人员' this.errorInfo = '请指定审批人员'
err.push(`${this.config.name} 未指定审批人员`) err.push(`${this.config.name} 未指定审批人员`)
return false return false
} }
}, },
validate_SELF_SELECT(err){ validate_SELF_SELECT(err) {
return true; return true;
}, },
validate_LEADER_TOP(err){ validate_LEADER_TOP(err) {
return true; return true;
}, },
validate_LEADER(err){ validate_LEADER(err) {
return true; return true;
}, },
validate_ROLE(err){ validate_ROLE(err) {
if (this.config.props.roleList.length <= 0){ if (this.config.props.roleList.length <= 0) {
this.errorInfo = '请指定负责审批的系统角色' this.errorInfo = '请指定负责审批的系统角色'
err.push(`${this.config.name} 未指定审批角色`) err.push(`${this.config.name} 未指定审批角色`)
return false return false
} }
return true; return true;
}, },
validate_SELF(err){ validate_SELF(err) {
return true; return true;
}, },
validate_FORM_USER(err){ validate_FORM_USER(err) {
if (this.config.props.formUser === ''){ if (this.config.props.formUser === '') {
this.errorInfo = '请指定表单中的人员组件' this.errorInfo = '请指定表单中的人员组件'
err.push(`${this.config.name} 审批人为表单中人员,但未指定`) err.push(`${this.config.name} 审批人为表单中人员,但未指定`)
return false return false
} }
return true; return true;
}, },
validate_REFUSE(err){ validate_REFUSE(err) {
return true; return true;
}, },
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo" :user-info="config.props.assignedUser" <node :title="config.name" :show-error="showError" :select-user="selectUser" :content="content" :error-info="errorInfo" :show-avatar="true" :user-info="config.props.assignedUser"
@selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)" @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
placeholder="请设置抄送人" :header-bgc="headerBgc" header-icon="el-icon-s-promotion"/> placeholder="请设置抄送人" :header-bgc="headerBgc" header-icon="el-icon-s-promotion"/>
</template> </template>
@ -25,6 +25,12 @@ export default {
} }
}, },
computed:{ computed:{
selectUser(){
return {
show: this.config.props.shouldAdd,
multiple: true
};
},
viewer(){ viewer(){
return this.$store.state.diagramMode === 'viewer' return this.$store.state.diagramMode === 'viewer'
}, },

View File

@ -1,27 +1,26 @@
<template> <template>
<div :class="{'node': true, 'root': isRoot || !show, 'node-error-state': showError}"> <div :class="{'node': true, 'root': isRoot || !show, 'node-error-state': showError}">
<div v-if="show" @click="$emit('selected')" :class="{'node-body': true, 'error': showError}" > <div v-if="show" @click="$emit('selected')" :class="{'node-body': true, 'error': showError}">
<div> <div>
<div class="node-body-header" :style="{'background-color': headerBgc}"> <div class="node-body-header" :style="{'background-color': headerBgc}">
<i :class="headerIcon" style="margin-right: 5px" v-if="(headerIcon || '') !== ''"></i> <i :class="headerIcon" style="margin-right: 5px" v-if="(headerIcon || '') !== ''"></i>
<ellipsis class="name" hover-tip :content="title"/> <ellipsis class="name" hover-tip :content="title"/>
<i class="el-icon-close" v-if="!isRoot && $store.state.diagramMode !== 'viewer'" style="float:right;" @click="$emit('delNode')"></i> <i class="el-icon-close" v-if="!isRoot && $store.state.diagramMode !== 'viewer'" style="float:right;"
@click="$emit('delNode')"></i>
</div> </div>
<div class="node-body-content"> <div class="node-body-content">
<i :class="leftIcon" v-if="leftIcon"></i> <i :class="leftIcon" v-if="leftIcon"></i>
<template v-if="$store.state.diagramMode !== 'viewer'"> <template v-if="viewer && selectUser.show">
<span class="placeholder" v-if="(content || '').trim() === ''">{{placeholder}}</span> <el-button type="primary" icon="el-icon-plus" circle @click="$refs.userPicker.showUserPicker()"/>
<ellipsis :row="3" :content="content" v-else/> <avatar-ellipsis :row="3" v-if="userInfo.length > 0" :user-info="userInfo"/>
<i class="el-icon-arrow-right"></i> </template>
<template v-else-if="showAvatar">
<span class="placeholder" v-if="userInfo.length === 0">{{ placeholder }}</span>
<avatar-ellipsis :row="3" :user-info="userInfo" v-else/>
</template> </template>
<template v-else> <template v-else>
<template v-if="userInfo.length > 0"> <span class="placeholder" v-if="(content || '').trim() === ''">{{ placeholder }}</span>
<avatar-ellipsis :row="3" :user-info="userInfo"/> <ellipsis :row="3" :content="content" v-else/>
</template>
<template v-else>
<span class="placeholder" v-if="(content || '').trim() === ''">{{placeholder}}</span>
<ellipsis :row="3" :content="content" v-else/>
</template>
</template> </template>
</div> </div>
<div class="node-error" v-if="showError"> <div class="node-error" v-if="showError">
@ -32,21 +31,29 @@
</div> </div>
</div> </div>
<div class="node-footer"> <div class="node-footer">
<div v-if="merge" class="branch-merge"><img data-v-1e7b1da5="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg==" alt=""></div> <div v-if="merge" class="branch-merge">
<img data-v-1e7b1da5=""
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg=="
alt=""></div>
<div class="btn"> <div class="btn">
<insert-button v-show="$store.state.diagramMode !== 'viewer'" @insertNode="type => $emit('insertNode', type)"></insert-button> <insert-button v-show="$store.state.diagramMode !== 'viewer'"
@insertNode="type => $emit('insertNode', type)"></insert-button>
</div> </div>
</div> </div>
<user-picker v-if="selectUser.show" title="请选择系统角色" :multiple="selectUser.multiple" ref="userPicker"
:v-model="userInfo"
@ok="selectedUser"/>
</div> </div>
</template> </template>
<script> <script>
import UserPicker from "@/components/common/UserPicker";
import InsertButton from '@/views/common/InsertButton.vue' import InsertButton from '@/views/common/InsertButton.vue'
export default { export default {
name: "Node", name: "Node",
components: {InsertButton}, components: {InsertButton, UserPicker},
props:{ props: {
// //
isRoot: { isRoot: {
type: Boolean, type: Boolean,
@ -70,13 +77,29 @@ export default {
// //
userInfo: { userInfo: {
type: Array, type: Array,
default: [] default(){
return []
}
}, },
title:{ //
showAvatar: {
type: Boolean,
default: false
},
selectUser: {
type: Object,
default() {
return {
show: false,
multiple: false,
}
}
},
title: {
type: String, type: String,
default: "标题" default: "标题"
}, },
placeholder:{ placeholder: {
type: String, type: String,
default: "请设置" default: "请设置"
}, },
@ -86,49 +109,71 @@ export default {
default: undefined default: undefined
}, },
// //
headerIcon:{ headerIcon: {
type: String, type: String,
default: '' default: ''
}, },
// //
headerBgc:{ headerBgc: {
type: String, type: String,
default: '#576a95' default: '#576a95'
}, },
// //
showError:{ showError: {
type: Boolean, type: Boolean,
default: false default: false
}, },
errorInfo:{ errorInfo: {
type: String, type: String,
default: '无信息' default: '无信息'
}, },
}, },
data() { data() {
return {} return{}
}, },
methods: {} computed: {
viewer() {
return this.$store.state.diagramMode === 'viewer'
},
},
methods: {
selectedUser(select) {
let userInfoList = []
for (let val of select) {
let userInfo = {
id: val.id,
name: val.name,
avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.userInfo = userInfoList
this.$store.state.selectUserMap.set(this.$store.state.selectedNode.id,userInfoList)
},
}
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.root{ .root {
&:before{ &:before {
display: none !important; display: none !important;
} }
} }
.node-error-state{
.node-body{ .node-error-state {
.node-body {
box-shadow: 0px 0px 5px 0px #F56C6C !important; box-shadow: 0px 0px 5px 0px #F56C6C !important;
} }
} }
.node{
.node {
padding: 0 50px; padding: 0 50px;
width: 220px; width: 220px;
position: relative; position: relative;
&:before{
&:before {
content: ''; content: '';
position: absolute; position: absolute;
top: -12px; top: -12px;
@ -141,15 +186,18 @@ export default {
border-color: #CACACA transparent transparent; border-color: #CACACA transparent transparent;
background: #F5F5F7; background: #F5F5F7;
} }
.node-body{
.node-body {
cursor: pointer; cursor: pointer;
max-height: 120px; max-height: 120px;
position: relative; position: relative;
border-radius: 5px; border-radius: 5px;
background-color: white; background-color: white;
box-shadow: 0px 0px 5px 0px #d8d8d8; box-shadow: 0px 0px 5px 0px #d8d8d8;
&:hover{
&:hover {
box-shadow: 0px 0px 3px 0px @theme-primary; box-shadow: 0px 0px 3px 0px @theme-primary;
.node-body-header { .node-body-header {
.el-icon-close { .el-icon-close {
display: inline; display: inline;
@ -157,36 +205,43 @@ export default {
} }
} }
} }
.node-body-header{
.node-body-header {
border-top-left-radius: 5px; border-top-left-radius: 5px;
border-top-right-radius: 5px; border-top-right-radius: 5px;
padding: 5px 15px; padding: 5px 15px;
color: white; color: white;
font-size: xx-small; font-size: xx-small;
.el-icon-close{
.el-icon-close {
display: none; display: none;
} }
.name{
.name {
height: 14px; height: 14px;
width: 150px; width: 150px;
display: inline-block display: inline-block
} }
} }
.node-body-content{
.node-body-content {
padding: 18px; padding: 18px;
color: #656363; color: #656363;
font-size: 14px; font-size: 14px;
i{
i {
position: absolute; position: absolute;
top: 55%; top: 55%;
right: 5px; right: 5px;
font-size: medium; font-size: medium;
} }
.placeholder{
.placeholder {
color: #8c8c8c; color: #8c8c8c;
} }
} }
.node-error{
.node-error {
position: absolute; position: absolute;
right: -40px; right: -40px;
top: 20px; top: 20px;
@ -195,9 +250,10 @@ export default {
} }
} }
.node-footer{ .node-footer {
position: relative; position: relative;
.branch-merge{
.branch-merge {
font-size: 12px; font-size: 12px;
display: flex; display: flex;
width: 38px; width: 38px;
@ -210,17 +266,20 @@ export default {
flex-direction: column; flex-direction: column;
box-shadow: 0 0 5px 0 #d8d8d8; box-shadow: 0 0 5px 0 #d8d8d8;
} }
.btn{
.btn {
width: 100%; width: 100%;
display: flex; display: flex;
padding: 20px 0 32px; padding: 20px 0 32px;
justify-content: center; justify-content: center;
z-index: 10; z-index: 10;
} }
/deep/ .el-button{
/deep/ .el-button {
height: 32px; height: 32px;
} }
&::before{
&::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -1,5 +1,5 @@
<template> <template>
<node title="发起人" :is-root="true" :content="content" :user-info="config.props.assignedUser" <node title="发起人" :is-root="true" :content="content" show-avatar :user-info="config.props.assignedUser"
@selected="$emit('selected')" @insertNode="type => $emit('insertNode', type)" @selected="$emit('selected')" @insertNode="type => $emit('insertNode', type)"
placeholder="所有人" :header-bgc="config.props.headerBgc" header-icon="el-icon-user-solid"/> placeholder="所有人" :header-bgc="config.props.headerBgc" header-icon="el-icon-user-solid"/>
</template> </template>

View File

@ -6,7 +6,7 @@
</div> </div>
<div style="display: flex;justify-content: center;flex-direction: column;min-width: 1036px;"> <div style="display: flex;justify-content: center;flex-direction: column;min-width: 1036px;">
<span style="font-size: 18px;text-align: center;padding-top: 20px;">审批流程</span> <span style="font-size: 18px;text-align: center;padding-top: 20px;">审批流程</span>
<process-diagram-viewer/> <process-diagram-viewer ref="processDiagramViewer"/>
<!--渲染流程执行过程--> <!--渲染流程执行过程-->
<el-timeline :reverse="false"> <el-timeline :reverse="false">
<!-- <el-timeline-item v-for="(activity, index) in activities" :key="index">--> <!-- <el-timeline-item v-for="(activity, index) in activities" :key="index">-->
@ -75,6 +75,7 @@ export default {
}, },
validate(call) { validate(call) {
this.$refs.initiateForm.validate(call); this.$refs.initiateForm.validate(call);
this.$refs.processDiagramViewer.validate(call);
} }
} }
}; };

View File

@ -122,7 +122,8 @@ export default {
let processInstance = this.$refs.processInstance; let processInstance = this.$refs.processInstance;
let paramsData = { let paramsData = {
processDefinitionId: this.selectForm.processDefinitionId, processDefinitionId: this.selectForm.processDefinitionId,
formData: JSON.stringify(processInstance.formData) formData: JSON.stringify(processInstance.formData),
selectUserMap: this.$store.state.selectUserMap
} }
processInstance.validate(valid => { processInstance.validate(valid => {
if (valid) { if (valid) {