init
This commit is contained in:
402
src/views/workflow/process/config/ApprovalNodeConfig.vue
Normal file
402
src/views/workflow/process/config/ApprovalNodeConfig.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<template>
|
||||
<el-form label-position="top" label-width="90px">
|
||||
<el-form-item label="⚙ 选择审批对象" prop="text" class="user-type">
|
||||
<el-radio-group v-model="nodeProps.assignedType">
|
||||
<el-radio v-for="item in approvalTypes" :label="item.type" :key="item.type">{{ item.name }}</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="nodeProps.assignedType === 'ASSIGN_USER'">
|
||||
<el-button size="mini" icon="Plus" type="primary" @click="showSysRolePicker" round>
|
||||
选择人员
|
||||
</el-button>
|
||||
<user-picker title="请选择系统角色" :multiple="false" ref="sysRolePicker" :v-model="assignedUser" @ok="selectedUser"/>
|
||||
<avatar-ellipsis :row="3" :user-info="assignedUser"/>
|
||||
</div>
|
||||
<div v-else-if="nodeProps.assignedType === 'SELF_SELECT'">
|
||||
<el-radio-group size="mini" v-model="nodeProps.selfSelect.multiple">
|
||||
<el-radio-button :label="false">自选一个人</el-radio-button>
|
||||
<el-radio-button :label="true">自选多个人</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-else-if="nodeProps.assignedType === 'LEADER_TOP'">
|
||||
<el-divider/>
|
||||
<el-form-item label="审批终点" prop="text" class="approve-end">
|
||||
<el-radio-group v-model="nodeProps.leaderTop.endCondition">
|
||||
<el-radio label="TOP">直到最上层主管</el-radio>
|
||||
<el-radio label="LEAVE">不超过发起人的</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="approve-end-leave" v-if="nodeProps.leaderTop.endCondition === 'LEAVE'">
|
||||
<span>第 </span>
|
||||
<el-input-number :min="1" :max="20" :step="1" size="mini" v-model="nodeProps.leaderTop.level"/>
|
||||
<span> 级主管</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else-if="nodeProps.assignedType === 'LEADER'">
|
||||
<el-divider/>
|
||||
<el-form-item label="指定主管" prop="text">
|
||||
<span>发起人的第 </span>
|
||||
<el-input-number :min="1" :max="20" :step="1" size="mini"
|
||||
v-model="nodeProps.leader.level"></el-input-number>
|
||||
<span> 级主管</span>
|
||||
<div style="color: #409EFF; font-size: small;">👉 直接主管为 第 1 级主管</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else-if="nodeProps.assignedType === 'ROLE'">
|
||||
<el-button size="mini" icon="Plus" type="primary" @click="showRolePicker()" round>
|
||||
选择系统角色
|
||||
</el-button>
|
||||
<role-picker title="请选择人员" :multiple="false" ref="rolePicker" :v-model="roleList" @ok="selectedRole"/>
|
||||
<role-items v-model="nodeProps.roleList"/>
|
||||
</div>
|
||||
<div v-else-if="nodeProps.assignedType === 'FORM_USER'">
|
||||
<el-form-item label="选择表单联系人项" prop="text" class="approve-end">
|
||||
<el-select style="width: 80%;" size="small" v-model="nodeProps.formUser" placeholder="请选择包含联系人的表单项" filterable>
|
||||
<el-option v-for="(op,i) in forms" :label="op.title" :value="op.id" :key="i"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="item-desc">发起人自己作为审批人进行审批</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-divider></el-divider>
|
||||
<el-form-item label="👤 审批人为空时" prop="text" class="line-mode">
|
||||
<el-radio-group v-model="nodeProps.nobody.handler">
|
||||
<el-radio label="TO_PASS">自动通过</el-radio>
|
||||
<el-radio label="TO_REFUSE">自动驳回</el-radio>
|
||||
<el-radio label="TO_ADMIN">转交审批管理员</el-radio>
|
||||
<el-radio label="TO_USER">转交到指定人员</el-radio>
|
||||
</el-radio-group>
|
||||
<div style="margin-top: 10px" v-if="nodeProps.nobody.handler === 'TO_USER'">
|
||||
<el-button size="mini" icon="Plus" type="primary" @click="showUserPicker()" round>
|
||||
选择人员
|
||||
</el-button>
|
||||
<user-picker title="请指定用户" :multiple="false" ref="toUserPicker" :v-model="nobodyAssignedUser"
|
||||
@ok="selectNoSetUser"/>
|
||||
<avatar-ellipsis :row="3" :user-info="nobodyAssignedUser"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="showMode">
|
||||
<el-divider/>
|
||||
<el-form-item label="👩👦👦 多人审批时审批方式" prop="text" class="approve-mode">
|
||||
<el-radio-group v-model="nodeProps.mode">
|
||||
<el-radio label="NEXT">会签 (按选择顺序审批,每个人必须同意)</el-radio>
|
||||
<el-radio label="AND">会签(可同时审批,每个人必须同意)</el-radio>
|
||||
<el-radio label="OR">或签(有一人同意即可)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-divider>高级设置</el-divider>
|
||||
<el-form-item label="✍ 审批同意时是否需要签字" prop="text">
|
||||
<el-switch inactive-text="不用" active-text="需要" v-model="nodeProps.sign"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" content="如果全局设置了需要签字,则此处不生效" placement="top-start">
|
||||
<i class="el-icon-question" style="margin-left: 10px; font-size: medium; color: #b0b0b1"></i>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="⏱ 审批期限(为 0 则不生效)" prop="timeLimit">
|
||||
<el-input style="width: 180px;" placeholder="时长" type="number"
|
||||
v-model="nodeProps.timeLimit.timeout.value">
|
||||
<el-select style="width: 75px;" v-model="nodeProps.timeLimit.timeout.unit" slot="append" placeholder="请选择" filterable>
|
||||
<el-option label="天" value="D"></el-option>
|
||||
<el-option label="小时" value="H"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批期限超时后执行" prop="level" v-if="nodeProps.timeLimit.timeout.value > 0">
|
||||
<el-radio-group v-model="nodeProps.timeLimit.handler.type">
|
||||
<el-radio label="PASS">自动通过</el-radio>
|
||||
<el-radio label="REFUSE">自动驳回</el-radio>
|
||||
<el-radio label="NOTIFY">发送提醒</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="nodeProps.timeLimit.handler.type === 'NOTIFY'">
|
||||
<div style="color:#409EEF; font-size: small">默认提醒当前审批人</div>
|
||||
<el-switch inactive-text="循环" active-text="一次" v-model="nodeProps.timeLimit.handler.notify.once"></el-switch>
|
||||
<span style="margin-left: 20px" v-if="!nodeProps.timeLimit.handler.notify.once">
|
||||
每隔
|
||||
<el-input-number :min="0" :max="10000" :step="1" size="mini"
|
||||
v-model="nodeProps.timeLimit.handler.notify.hour"/>
|
||||
小时提醒一次
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="🙅 如果审批被驳回 👇">
|
||||
<el-radio-group v-model="nodeProps.refuse.type">
|
||||
<el-radio label="TO_INITIAL">重新开始流程</el-radio>
|
||||
<el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>
|
||||
<el-radio label="TO_NODE">驳回到指定节点</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="nodeProps.refuse.type === 'TO_NODE'">
|
||||
<span>指定节点:</span>
|
||||
<el-select style="margin-left: 10px; width: 150px;" placeholder="选择跳转步骤" size="small"
|
||||
v-model="nodeProps.refuse.target" filterable>
|
||||
<el-option v-for="(node, index) in nodeOptions" :key="index" :label="node.name"
|
||||
:value="node.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="自定义监听器">
|
||||
<div slot="label">
|
||||
<span style="margin-left: 20px">使用自定义监听器: </span>
|
||||
<!-- <el-switch v-model="config.listener.state" @change="getListener"></el-switch>-->
|
||||
</div>
|
||||
<div v-if="config.listener.state">
|
||||
<div slot="label">
|
||||
<span style="margin-right: 10px">设置监听器</span>
|
||||
<el-button type="primary" @click="addListener(config.listener.list)" link> + 添加</el-button>
|
||||
</div>
|
||||
<div v-for="(listen, index) in config.listener.list" :key="index">
|
||||
<el-input v-if="listen.isSys" placeholder="监听器名称" :disabled="true" size="small" style="width: 100px;"
|
||||
v-model="listen.listenerName"/>
|
||||
<el-input v-if="!listen.isSys" placeholder="监听器名称" size="small" style="width: 100px;"
|
||||
v-model="listen.listenerName"/>
|
||||
<el-radio-group size="small" style="margin: 0 5px;" @change="typeChange(listen)" v-model="listen.isSys">
|
||||
<el-radio-button :label="true">内置</el-radio-button>
|
||||
<el-radio-button :label="false">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="listen.isSys" style="width: 180px;" v-model="listen.listenerValue" size="small"
|
||||
@change="listenerOptionChange(listen)"
|
||||
placeholder="请选择表单字段" filterable>
|
||||
<el-option v-for="option in listenerOption" :key="option.value" :label="option.label"
|
||||
:value="option.value"/>
|
||||
</el-select>
|
||||
<!-- <el-input v-if="listen.isSys" placeholder="请设置字段值" size="small" v-model="listen.listenerValue" style="width: 180px;"/>-->
|
||||
<el-button v-if="!listen.isSys" type="primary" size="small" @click="settingListener(listen)" link>设置</el-button>
|
||||
<el-button @click="delListener(config.listener.list, index)"
|
||||
class="el-icon-delete" type="primary"
|
||||
style="margin-left: 5px; color: #c75450;" link/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!--
|
||||
<el-dialog custom-class="custom-dialog" class="border" width="600px" title="定义监听器设置"
|
||||
append-to-body :close-on-click-modal="true"
|
||||
:destroy-on-close="true" :visible.sync="editListenShow">
|
||||
<el-form ref="listenerForm" label-position="left" label-width="100px" :rules="listenerRules">
|
||||
<el-form-item label="监听器名称" prop="listenerName" class="listen">
|
||||
<el-input placeholder="请设置监听器名称" size="small" v-model="selectListen.listenerName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="监听器类型" prop="eventType" class="listen">
|
||||
<el-select placeholder="请选择监听器类型" @change="selectListen.eventType = []"
|
||||
v-model="selectListen.listenerType" filterable>
|
||||
<el-option :value="'1'" label="任务监听"/>
|
||||
<el-option :value="'2'" label="执行监听"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="事件类型" prop="listenerType" class="listen">
|
||||
<el-select multiple placeholder="请选择事件类型" v-model="selectListen.eventType" filterable>
|
||||
<el-option value="create" label="create"/>
|
||||
<el-option v-if="selectListen.listenerType === '2'" value="end" label="end"/>
|
||||
<el-option v-if="selectListen.listenerType === '2'" value="take" label="take"/>
|
||||
<el-option v-if="selectListen.listenerType === '1'" value="assignment" label="assignment"/>
|
||||
<el-option v-if="selectListen.listenerType === '1'" value="complete" label="complete"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="值类型" prop="listenerValueType" class="listen">
|
||||
<el-radio-group size="small" style="margin: 0 5px;" @change="listenerValueTypeChange"
|
||||
v-model="selectListen.listenerValueType">
|
||||
<el-radio-button :label="'0'">Java类</el-radio-button>
|
||||
<el-radio-button :label="'1'">表达式</el-radio-button>
|
||||
<el-radio-button :label="'2'">代理表达式</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="值" prop="listenerValue" class="listen">
|
||||
<el-input :placeholder="listenerValuePlaceholder" size="small" v-model="selectListen.listenerValue"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button size="mini" @click="editListenShow = false">取消</el-button>
|
||||
<el-button size="mini" type="primary" @click="editListenShow = false">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>-->
|
||||
</template>
|
||||
<script setup>
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import UserPicker from '../common/UserPicker.vue'
|
||||
import RolePicker from '../common/RolePicker.vue'
|
||||
import AvatarEllipsis from '../common/AvatarEllipsis.vue'
|
||||
import RoleItems from "../common/RoleItems.vue";
|
||||
import {computed, defineProps} from 'vue'
|
||||
|
||||
const processStore = useProcessStore()
|
||||
|
||||
const toUserPicker = ref()
|
||||
const rolePicker = ref()
|
||||
const sysRolePicker = ref()
|
||||
const showOrgSelect = ref(false)
|
||||
const orgPickerSelected = ref([])
|
||||
const approvalTypes = reactive([
|
||||
{name: "指定人员", type: "ASSIGN_USER"},
|
||||
{name: "发起人自选", type: "SELF_SELECT"},
|
||||
{name: "连续多级主管", type: "LEADER_TOP"},
|
||||
{name: "主管", type: "LEADER"},
|
||||
{name: "角色", type: "ROLE"},
|
||||
{name: "发起人自己", type: "SELF"},
|
||||
{name: "表单内联系人", type: "FORM_USER"}
|
||||
])
|
||||
const listenerOption = ref([])
|
||||
const selectListen = ref({})
|
||||
const editListenShow = ref(false)
|
||||
const listenerValuePlaceholder = ref('请输入类路径')
|
||||
const listenerRules = ref({
|
||||
listenerName: [
|
||||
{required: true, message: '监听器名称不能为空', trigger: 'blur'}
|
||||
],
|
||||
eventType: [
|
||||
{required: true, message: '监听器类型不能为空', trigger: 'blur'},
|
||||
// {validator: checkLength,trigger:'blur'}
|
||||
],
|
||||
listenerType: [
|
||||
{required: true, message: '监听器类型不能为空', trigger: 'blur'},
|
||||
],
|
||||
listenerValue: [
|
||||
{required: true, message: '值不能为空', trigger: 'blur'}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const nodeProps = computed(() => {
|
||||
return processStore.getSelectedNode().props;
|
||||
})
|
||||
|
||||
const assignedUser = computed({
|
||||
get() {
|
||||
return props.config.assignedUser || [];
|
||||
},
|
||||
set(val) {
|
||||
props.config.assignedUser = val
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const nobodyAssignedUser = computed({
|
||||
get() {
|
||||
return props.config.nobody.assignedUser || [];
|
||||
},
|
||||
set(val) {
|
||||
props.config.nobody.assignedUser = val
|
||||
}
|
||||
})
|
||||
|
||||
const roleList = computed({
|
||||
get() {
|
||||
return props.config.roleList || [];
|
||||
},
|
||||
set(val) {
|
||||
props.config.roleList = val
|
||||
}
|
||||
})
|
||||
watch(()=>props.config.roleList,(value)=>{
|
||||
roleList.value = value
|
||||
})
|
||||
|
||||
const forms = computed(() => {
|
||||
return processStore.getDesign().formItems.filter(f => {
|
||||
return f.name === "UserPicker";
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
const nodeOptions = computed(() => {
|
||||
let values = [];
|
||||
const excType = ["ROOT", "EMPTY", "CONDITION", "CONDITIONS", "CONCURRENT", "CONCURRENTS", "CC", "END", "TRIGGER"];
|
||||
processStore.nodeMap.forEach((v) => {
|
||||
if (excType.indexOf(v.type) === -1) {
|
||||
values.push({id: v.id, name: v.name});
|
||||
}
|
||||
});
|
||||
return values;
|
||||
})
|
||||
|
||||
|
||||
const showMode = computed(() => {
|
||||
let props = processStore.getSelectedNode().props;
|
||||
switch (props.assignedType) {
|
||||
case "ASSIGN_USER":
|
||||
return props.assignedUser.length > 0;
|
||||
case "SELF_SELECT":
|
||||
return props.selfSelect.multiple;
|
||||
case "LEADER_TOP":
|
||||
return props.formUser !== "";
|
||||
case "FORM_USER":
|
||||
case "ROLE":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})
|
||||
//指定人员
|
||||
const showSysRolePicker = () => {
|
||||
sysRolePicker.value.showUserPicker()
|
||||
}
|
||||
//点击转交给指定人员
|
||||
const showUserPicker = () => {
|
||||
toUserPicker.value.showUserPicker()
|
||||
}
|
||||
//选择系统角色
|
||||
const showRolePicker = () => {
|
||||
rolePicker.value.showRolePicker()
|
||||
}
|
||||
const checkLength = (rule, value, callback) => {
|
||||
if (value.length === 0) {
|
||||
callback(new Error("事件类型不能为空!"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const selectNoSetUser = (select) => {
|
||||
let userInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
id: val.id,
|
||||
name: val.name,
|
||||
avatar: val.avatar,
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
nobodyAssignedUser.value = userInfoList
|
||||
}
|
||||
|
||||
|
||||
const selectedRole = (select) => {
|
||||
let roleInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
roleId: val.roleId,
|
||||
roleName: val.roleName
|
||||
}
|
||||
roleInfoList.push(userInfo)
|
||||
}
|
||||
roleList.value = roleInfoList
|
||||
}
|
||||
const selectedUser = (select) => {
|
||||
let userInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
id: val.id,
|
||||
name: val.name,
|
||||
avatar: val.avatar,
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
assignedUser.value = userInfoList
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
73
src/views/workflow/process/config/CcNodeConfig.vue
Normal file
73
src/views/workflow/process/config/CcNodeConfig.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<el-button size="mini" icon="Plus" type="primary" @click="selectUser" round>选择抄送人</el-button>
|
||||
<div class="option">
|
||||
<el-checkbox label="允许发起人添加抄送人" v-model="shouldAdd"></el-checkbox>
|
||||
</div>
|
||||
<!-- <org-items v-model="select"/>-->
|
||||
<avatar-ellipsis :row="3" :user-info="assignedUser"/>
|
||||
<user-picker title="请选择抄送人" multiple ref="userPicker" :v-model="assignedUser" @ok="selectedUser"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, defineProps} from 'vue'
|
||||
import UserPicker from "../common/UserPicker.vue";
|
||||
import AvatarEllipsis from "../common/AvatarEllipsis.vue";
|
||||
const userPicker=ref()
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const shouldAdd = computed({
|
||||
get() {
|
||||
return props.config.shouldAdd || false
|
||||
},
|
||||
set(val) {
|
||||
props.config.shouldAdd = val
|
||||
}
|
||||
})
|
||||
const assignedUser = computed({
|
||||
get() {
|
||||
return props.config.assignedUser || []
|
||||
},
|
||||
set(val) {
|
||||
props.config.assignedUser = val
|
||||
}
|
||||
})
|
||||
const selectUser = () => {
|
||||
userPicker.value.showUserPicker()
|
||||
}
|
||||
const selectedUser = (select) => {
|
||||
let userInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
id: val.id,
|
||||
name: val.name,
|
||||
avatar: val.avatar,
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
assignedUser.value = userInfoList
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.option {
|
||||
color: #606266;
|
||||
margin-top: 20px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: small;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.org-item {
|
||||
margin: 5px;
|
||||
}
|
||||
</style>
|
||||
308
src/views/workflow/process/config/ConditionGroupItemConfig.vue
Normal file
308
src/views/workflow/process/config/ConditionGroupItemConfig.vue
Normal file
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<div v-for="(group, index) in selectedNode.props.groups" :key="index + '_g'" class="group">
|
||||
<div class="group-header">
|
||||
<span class="group-name">条件组 {{ groupNames[index] }}</span>
|
||||
<div class="group-cp">
|
||||
<span>组内条件关系:</span>
|
||||
<el-switch v-model="group.groupType" active-color="#409EFF"
|
||||
inactive-color="#c1c1c1" active-value="AND" inactive-value="OR"
|
||||
active-text="且" inactive-text="或"/>
|
||||
</div>
|
||||
|
||||
<div class="group-operation">
|
||||
<el-popover placement="bottom" title="选择审批条件" width="300" trigger="click">
|
||||
<template #reference>
|
||||
<el-icon :size="18" class="group-icon">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</template>
|
||||
<div>以下条件将决定具体的审批流程</div>
|
||||
<el-checkbox-group v-model="group.cids" value-key="id">
|
||||
<el-checkbox :label="condition.id" v-for="(condition, cindex) in conditionList" :key="condition.id"
|
||||
@change="conditionChange(cindex, group)">
|
||||
{{ condition.title }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-popover>
|
||||
<el-icon :size="18" class="group-icon" @click="delGroup(index)">
|
||||
<Close/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-content">
|
||||
<p v-if="group.conditions.length === 0">点击右上角 + 为本条件组添加条件</p>
|
||||
<div v-else>
|
||||
<el-form ref="condition-form">
|
||||
<!--构建表达式-->
|
||||
<el-form-item v-for="(condition, cindex) in group.conditions" :key="condition.id + '_' + cindex">
|
||||
<ellipsis slot="label" hover-tip :content="condition.title"/>
|
||||
<span v-if="condition.valueType === ValueType.string">
|
||||
<el-select placeholder="判断符" style="width: 120px;" v-model="condition.compare"
|
||||
@change="condition.value = []" filterable>
|
||||
<el-option label="等于" value="="></el-option>
|
||||
<el-option label="包含在" value="IN"></el-option>
|
||||
</el-select>
|
||||
<span v-if="isSelect(condition.id)" style="margin-left: 10px">
|
||||
<el-select v-if="condition.compare === 'IN'" style="width: 280px;" clearable multiple size="small"
|
||||
v-model="condition.value" placeholder="选择值" filterable>
|
||||
<el-option v-for="(option, oi) in getOptions(condition.id)" :key="oi" :label="option"
|
||||
:value="option"></el-option>
|
||||
</el-select>
|
||||
<el-select v-else style="width: 280px;" clearable size="small" v-model="condition.value[0]"
|
||||
placeholder="选择值" filterable>
|
||||
<el-option v-for="(option, oi) in getOptions(condition.id)" :key="oi" :label="option"
|
||||
:value="option"></el-option>
|
||||
</el-select>
|
||||
</span>
|
||||
<span v-else style="margin-left: 10px">
|
||||
<el-input v-if="condition.compare === '='" style="width: 280px;" placeholder="输入比较值"
|
||||
v-model="condition.value[0]"/>
|
||||
<el-select v-else style="width: 280px;" multiple clearable filterable allow-create size="small"
|
||||
v-model="condition.value" placeholder="输入可能包含的值"></el-select>
|
||||
</span>
|
||||
</span>
|
||||
<span v-else-if="condition.valueType === ValueType.number">
|
||||
<el-select size="small" placeholder="判断符" style="width: 120px;" v-model="condition.compare" filterable>
|
||||
<el-option :label="exp.label" :value="exp.value" :key="exp.value" v-for="exp in explains"></el-option>
|
||||
</el-select>
|
||||
<span style="margin-left: 10px">
|
||||
<el-input style="width: 280px;" v-if="conditionValType(condition.compare) === 0"
|
||||
placeholder="输入比较值" type="number" v-model="condition.value[0]"/>
|
||||
<el-select style="width: 280px;" multiple filterable allow-create
|
||||
v-else-if="conditionValType(condition.compare) === 1"
|
||||
v-model="condition.value" placeholder="输入可能包含的值"></el-select>
|
||||
<span v-else>
|
||||
<el-input style="width: 130px;" type="number" placeholder="输入比较值"
|
||||
v-model="condition.value[0]"/>
|
||||
<span> ~
|
||||
<el-input style="width: 130px;" type="number" placeholder="输入比较值"
|
||||
v-model="condition.value[1]"/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span v-else-if="condition.valueType === ValueType.user">
|
||||
<span class="item-desc" style="margin-right: 20px">属于某部门 / 为某些人其中之一</span>
|
||||
<el-button size="mini" :icon="Plus" type="primary" @click="selectUser(condition.value, 'user')"
|
||||
round>选择人员/部门</el-button>
|
||||
<org-items :modelValue="users"/>
|
||||
</span>
|
||||
<span v-else-if="condition.valueType === ValueType.dept">
|
||||
<span class="item-desc" style="margin-right: 20px">为某部门 / 某部门下的部门</span>
|
||||
<el-button size="mini" :icon="Plus" type="primary" @click="selectUser(condition.value, 'dept')"
|
||||
round>选择部门</el-button>
|
||||
<org-items :modelValue="users"/>
|
||||
</span>
|
||||
<span v-else-if="condition.valueType === ValueType.date"></span>
|
||||
<el-icon class="delete-icon" @click="delSubCondition(group, cindex)"><Minus /></el-icon>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<org-picker title="请选择人员/部门" multiple ref="orgPicker" :v-model="users" @ok="selected"></org-picker>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import OrgPicker from "../common/UserPicker.vue";
|
||||
import OrgItems from "../common/RoleItems.vue";
|
||||
import {ValueType} from '@/views/workflow/form/ComponentsConfigExport.js'
|
||||
import {computed, ref} from 'vue'
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import Ellipsis from '../common/Ellipsis.vue'
|
||||
import {Plus, Minus} from '@element-plus/icons-vue'
|
||||
|
||||
const processStore = useProcessStore()
|
||||
|
||||
const orgPicker = ref()
|
||||
const users = ref([])
|
||||
// const orgType = ref('user')
|
||||
const showOrgSelect = ref(false)
|
||||
const groupNames = ref(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'])
|
||||
const supportTypes = ref([ValueType.number, ValueType.string, ValueType.date, ValueType.dateRange, ValueType.dept, ValueType.user])
|
||||
const explains = ref(
|
||||
[
|
||||
{label: '等于', value: '='},
|
||||
{label: '大于', value: '>'},
|
||||
{label: '大于等于', value: '>='},
|
||||
{label: '小于', value: '<'},
|
||||
{label: '小于等于', value: '<='},
|
||||
{label: '包含在', value: 'IN'},
|
||||
{label: 'x < 值 < x', value: 'B'},
|
||||
{label: 'x ≤ 值 < x', value: 'AB'},
|
||||
{label: 'x < 值 ≤ x', value: 'BA'},
|
||||
{label: 'x ≤ 值 ≤ x', value: 'ABA'},
|
||||
]
|
||||
)
|
||||
|
||||
const selectedNode = computed(() => {
|
||||
//当前选择的节点
|
||||
return processStore.getSelectedNode()
|
||||
})
|
||||
|
||||
const conditionList = computed(() => {
|
||||
//条件数组
|
||||
//构造可用条件选项
|
||||
const conditionItems = []
|
||||
processStore.getDesign().formItems.forEach(item => filterCondition(item, conditionItems))
|
||||
if (conditionItems.length === 0 || conditionItems[0].id !== 'root') {
|
||||
conditionItems.unshift({id: 'root', title: '发起人', valueType: 'User'})
|
||||
}
|
||||
return conditionItems
|
||||
})
|
||||
|
||||
const isSelect = (processDefinitionKey) => {
|
||||
//判断是否被选择
|
||||
let form = processStore.getFormMap().get(processDefinitionKey)
|
||||
return (form && (form.name === 'SelectInput' || form.name === 'MultipleSelect'))
|
||||
}
|
||||
|
||||
const getOptions = (processDefinitionKey) => {
|
||||
//获取到对应的option选项
|
||||
return processStore.getFormMap().get(processDefinitionKey).props.options || []
|
||||
}
|
||||
|
||||
const conditionValType = (type) => {
|
||||
//条件类型选择
|
||||
switch (type) {
|
||||
case '=':
|
||||
case '>':
|
||||
case '>=':
|
||||
case '<':
|
||||
case '<=':
|
||||
return 0;
|
||||
case 'IN':
|
||||
return 1;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const selectUser = (value, orgType) => {
|
||||
//选择用户,倒开组织选择器
|
||||
// orgType.value = orgType
|
||||
users.value = value
|
||||
orgPicker.value.showUserPicker()
|
||||
}
|
||||
|
||||
const filterCondition = (item, list) => {
|
||||
//从表单中过滤出可以选择的条件
|
||||
if (item.name === 'SpanLayout') {
|
||||
item.props.items.forEach(sub => filterCondition(sub, list))
|
||||
} else if (supportTypes.value.indexOf(item.valueType) > -1 && item.props.required) {
|
||||
list.push({title: item.title, id: item.id, valueType: item.valueType})
|
||||
}
|
||||
}
|
||||
|
||||
const selected = (selected) => {
|
||||
let userInfoList = []
|
||||
for (let val of selected) {
|
||||
let userInfo = {
|
||||
id: val.id,
|
||||
roleName: val.name
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
users.value = userInfoList
|
||||
//组织选择器的选中回调函数
|
||||
// users.value.length = 0
|
||||
// console.log('processStore.getAssignedUser()',processStore.getAssignedUser())
|
||||
processStore.getAssignedUser().forEach(u => users.value=userInfoList)
|
||||
}
|
||||
|
||||
const delGroup = (index) => {
|
||||
//删除条件组
|
||||
processStore.getSelectedNode().props.groups.splice(index, 1)
|
||||
}
|
||||
|
||||
const delSubCondition = (group, index) => {
|
||||
//删除
|
||||
group.cids.splice(index, 1)
|
||||
group.conditions.splice(index, 1)
|
||||
}
|
||||
|
||||
const conditionChange = (index, group) => {
|
||||
//条件组进行发生了改变
|
||||
//判断新增的
|
||||
group.cids.forEach(cid => {
|
||||
if (0 > group.conditions.findIndex(cd => cd.id === cid)) {
|
||||
//新增条件
|
||||
let condition = {...conditionList.value[index]}
|
||||
console.log('fs', condition, conditionList.value, index)
|
||||
condition.compare = '';
|
||||
condition.value = []
|
||||
group.conditions.push(condition)
|
||||
}
|
||||
})
|
||||
for (let i = 0; i < group.conditions.length; i++) {
|
||||
//去除没有选中的
|
||||
if (group.cids.indexOf(group.conditions[i].id) < 0) {
|
||||
group.conditions.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.group {
|
||||
margin-bottom: 20px;
|
||||
color: #5e5e5e;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e3e3e3;
|
||||
|
||||
.group-header {
|
||||
padding: 5px 10px;
|
||||
background: #e3e3e3;
|
||||
position: relative;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.group-name {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.group-cp {
|
||||
font-size: small;
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
display: flex;
|
||||
top: 5px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-operation {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
.group-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group-content {
|
||||
padding: 10px 5px;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 12px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-title {
|
||||
display: block;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
src/views/workflow/process/config/ConditionNodeConfig.vue
Normal file
141
src/views/workflow/process/config/ConditionNodeConfig.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<el-form inline label-width="100px">
|
||||
<el-form-item label="调整优先级" prop="level">
|
||||
<el-popover placement="right" title="拖拽条件调整优先级顺序" width="250" trigger="click">
|
||||
<draggable style="width: 100%; min-height:25px" :list="prioritySortList" group="from"
|
||||
@end="onEnd"
|
||||
:options="sortOption">
|
||||
<template #item="{ index,element }">
|
||||
<div :class="{'drag-no-choose': true, 'drag-hover': element.id === selectedNode.id}">
|
||||
<ellipsis style="width: 80px;" hover-tip :content="element.name"/>
|
||||
<div>优先级 {{ index + 1 }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
<template #reference>
|
||||
<el-button slot="reference">第{{ nowNodeLeave + 1 }}级</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="条件组关系" label-width="150px">
|
||||
<el-switch v-model="selectedNode.props.groupsType" active-color="#409EFF"
|
||||
inactive-color="#c1c1c1" active-value="AND" inactive-value="OR"
|
||||
active-text="且" inactive-text="或">
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="条件组表达式">
|
||||
<el-input v-model="config.expression" placeholder="输入条件组关系表达式 &为与,|为或"/>
|
||||
<span class="item-desc">使用表达式构建复杂逻辑,例如: (A & B) | C</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div>
|
||||
<el-button type="primary" icon="Plus" style="margin: 0 15px 15px 0" round
|
||||
@click="addConditionGroup">
|
||||
添加条件组
|
||||
</el-button>
|
||||
<span class="item-desc">只有必填选项才能作为审批条件</span>
|
||||
</div>
|
||||
<group-item/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable';
|
||||
import Ellipsis from '../common/Ellipsis.vue'
|
||||
import {computed, defineProps, ref,defineEmits} from 'vue'
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
const emit = defineEmits()
|
||||
|
||||
const processStore = useProcessStore()
|
||||
import GroupItem from "./ConditionGroupItemConfig.vue"
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
})
|
||||
|
||||
const sortOption = reactive({
|
||||
animation: 300,
|
||||
chosenClass: 'choose',
|
||||
scroll: true,
|
||||
sort: true
|
||||
})
|
||||
|
||||
const selectedNode = computed(() => {
|
||||
//当前选择的节点
|
||||
return processStore.getSelectedNode()
|
||||
})
|
||||
|
||||
|
||||
//条件节点
|
||||
const prioritySortList = computed(() => {
|
||||
let node = processStore.nodeMap.get(processStore.getSelectedNode().parentId)
|
||||
if (node) {
|
||||
return node.branchs || []
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const nowNodeLeave = computed(() => {
|
||||
let node = processStore.nodeMap.get(processStore.getSelectedNode().parentId)
|
||||
let list = []
|
||||
if (node) {
|
||||
list = node.branchs || []
|
||||
}
|
||||
return list.indexOf(processStore.getSelectedNode())
|
||||
})
|
||||
|
||||
|
||||
const onEnd = () => {
|
||||
emit('initRender')
|
||||
}
|
||||
const addConditionGroup = (select) => {
|
||||
props.config.groups.push({
|
||||
cids: [],
|
||||
groupType: "OR",
|
||||
conditions: []
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// const selected = (select) => {
|
||||
// select.forEach(val => processStore.addAssignedUser(val))
|
||||
// }
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.choose {
|
||||
border-radius: 5px;
|
||||
margin-top: 2px;
|
||||
background: #f4f4f4;
|
||||
border: 1px dashed #1890FF !important;
|
||||
}
|
||||
|
||||
.drag-hover {
|
||||
color: #1890FF
|
||||
}
|
||||
|
||||
.drag-no-choose {
|
||||
cursor: move;
|
||||
background: #f8f8f8;
|
||||
border-radius: 5px;
|
||||
margin: 5px 0;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #ffffff;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
font-size: small !important;
|
||||
}
|
||||
|
||||
div:nth-child(2) {
|
||||
float: right !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
41
src/views/workflow/process/config/DelayNodeConfig.vue
Normal file
41
src/views/workflow/process/config/DelayNodeConfig.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-bottom: 20px">
|
||||
<p class="item-desc">延时方式</p>
|
||||
<el-radio-group v-model="config.type">
|
||||
<el-radio-button label="FIXED">固定时长</el-radio-button>
|
||||
<el-radio-button label="AUTO">自动计算</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-if="config.type === 'FIXED'">
|
||||
<el-input style="width: 180px;" placeholder="时间单位" type="number" v-model="config.time">
|
||||
</el-input>
|
||||
<el-select style="width: 75px;" v-model="config.unit" slot="append" placeholder="请选择" filterable>
|
||||
<el-option label="天" value="D"></el-option>
|
||||
<el-option label="小时" value="H"></el-option>
|
||||
<el-option label="分钟" value="M"></el-option>
|
||||
</el-select>
|
||||
<span class="item-desc"> 后进入下一步</span>
|
||||
</div>
|
||||
<div class="item-desc" v-else>
|
||||
<el-time-picker value-format="HH:mm:ss" style="width: 150px;" v-model="config.dateTime" placeholder="任意时间点"></el-time-picker>
|
||||
<span class="item-desc"> 后进入下一步</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps} from 'vue'
|
||||
const props = defineProps({
|
||||
config:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
117
src/views/workflow/process/config/FormAuthorityConfig.vue
Normal file
117
src/views/workflow/process/config/FormAuthorityConfig.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<el-table :header-cell-style="{background:'#f5f6f6'}" :data="formPerms" border style="width: 100%" v-tabh>
|
||||
<el-table-column prop="title" show-overflow-tooltip label="表单字段">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.required" style="color: #c75450"> * </span>
|
||||
<span>{{ scope.row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="nodeType !== 'ROOT'" prop="readOnly" label="只读" width="80">
|
||||
<template slot="header" #header="scope">
|
||||
<el-radio label="R" v-model="permSelect" @change="allSelect('R')">只读</el-radio>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-radio v-model="scope.row.perm" label="R" :name="scope.row.id"></el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="editable" label="可编辑" width="90" v-if="nowNode.type !== 'CC'">
|
||||
<template slot="header" #header="scope">
|
||||
<el-radio label="E" v-model="permSelect" @change="allSelect('E')">可编辑</el-radio>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-radio v-model="scope.row.perm" label="E" :name="scope.row.id"></el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hide" label="隐藏" width="80">
|
||||
<template slot="header" #header="scope">
|
||||
<el-radio label="H" v-model="permSelect" @change="allSelect('H')">隐藏</el-radio>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-radio v-model="scope.row.perm" label="H" :name="scope.row.id"></el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps, watch,computed} from "vue";
|
||||
|
||||
import {useProcessStore} from "@/stores/processStore.js";
|
||||
|
||||
const props = defineProps({
|
||||
nodeType: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
const processStore = useProcessStore();
|
||||
|
||||
const tableData = ref([]);
|
||||
const isIndeterminate = ref(false);
|
||||
const permSelect = ref("");
|
||||
const checkStatus = reactive({
|
||||
readOnly: true,
|
||||
editable: false,
|
||||
hide: false
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const init = () => {
|
||||
let oldPermMap = new Map()
|
||||
if (Array.isArray(formPerms.value) && formPerms.value.length){
|
||||
for (let item of formPerms.value) {
|
||||
oldPermMap.set(item.id,item)
|
||||
}
|
||||
}
|
||||
processStore.getSelectedNode().props.formPerms = [];
|
||||
formPermsLoad(oldPermMap, processStore.getDesign().formItems);
|
||||
};
|
||||
|
||||
const formPerms = computed(() => {
|
||||
return processStore.getSelectedNode().props.formPerms;
|
||||
});
|
||||
|
||||
const nowNode = computed(() => {
|
||||
return processStore.getSelectedNode();
|
||||
});
|
||||
|
||||
const formItems = computed(() => {
|
||||
return processStore.getDesign().formItems;
|
||||
});
|
||||
|
||||
const allSelect = (type) => {
|
||||
permSelect.value = type;
|
||||
formPerms.value.forEach(f => f.perm = type);
|
||||
};
|
||||
|
||||
|
||||
const formPermsLoad = (oldPermMap, forms) => {
|
||||
forms.forEach(form => {
|
||||
if (form.name === "SpanLayout") {
|
||||
formPermsLoad(oldPermMap, form.props.items);
|
||||
} else {
|
||||
//刷新名称
|
||||
let old = oldPermMap.get(form.id)
|
||||
if (old) {
|
||||
old.title = form.title;
|
||||
old.required = form.props.required;
|
||||
formPerms.value.push(old);
|
||||
} else {
|
||||
formPerms.value.push({
|
||||
id: form.id,
|
||||
title: form.title,
|
||||
required: form.props.required,
|
||||
perm: nowNode.type === "ROOT" ? "E" : "R"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
92
src/views/workflow/process/config/NodeConfig.vue
Normal file
92
src/views/workflow/process/config/NodeConfig.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-tabs v-model="active" v-if="visible && name && formConfig.length > 0">
|
||||
<el-tab-pane :label="name" name="properties">
|
||||
<component :is="com" :config="selectNode.props" @initRender="emit('initRender')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="表单权限设置" name="permissions">
|
||||
<form-authority-config :node-type="selectNode.type"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div v-else>
|
||||
<component :is="com" :config="selectNode.props" @initRender="emit('initRender')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormAuthorityConfig from './FormAuthorityConfig.vue'
|
||||
import Approval from './ApprovalNodeConfig.vue'
|
||||
import Root from './RootConfig.vue'
|
||||
import Delay from './DelayNodeConfig.vue'
|
||||
import CcNode from './CcNodeConfig.vue'
|
||||
import Condition from './ConditionNodeConfig.vue'
|
||||
import Trigger from './TriggerNodeConfig.vue'
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import {computed,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const processStore = useProcessStore()
|
||||
|
||||
const selectNode = computed(() => {
|
||||
return processStore.getSelectedNode()
|
||||
})
|
||||
|
||||
|
||||
const formConfig = computed(() => {
|
||||
return processStore.getDesign().formItems
|
||||
})
|
||||
|
||||
|
||||
const com = ref()
|
||||
const active = ref('properties')
|
||||
const visible = ref(false)
|
||||
|
||||
|
||||
const name = computed(()=>{
|
||||
switch (processStore.getSelectedNode().type) {
|
||||
case 'ROOT':
|
||||
return '设置发起人';
|
||||
case 'APPROVAL':
|
||||
return '设置审批人';
|
||||
case 'CC':
|
||||
return '设置抄送人';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const init = () => {
|
||||
console.log(processStore.getSelectedNode().type)
|
||||
switch (processStore.getSelectedNode().type) {
|
||||
case 'APPROVAL' :
|
||||
com.value = Approval;
|
||||
break;
|
||||
case 'ROOT' :
|
||||
com.value = Root;
|
||||
break;
|
||||
case 'DELAY' :
|
||||
com.value = Delay;
|
||||
break;
|
||||
case 'CC' :
|
||||
com.value = CcNode;
|
||||
break;
|
||||
case 'CONDITION' :
|
||||
com.value = Condition;
|
||||
break;
|
||||
case 'TRIGGER' :
|
||||
com.value = Trigger;
|
||||
break;
|
||||
}
|
||||
visible.value = false
|
||||
nextTick(() => {
|
||||
visible.value = true
|
||||
})
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
44
src/views/workflow/process/config/RootConfig.vue
Normal file
44
src/views/workflow/process/config/RootConfig.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<p class="desc">选择能发起该审批的人员/部门,不选则默认开放给所有人</p>
|
||||
<el-button size="mini" @click="selectOrg" icon="el-icon-plus" type="primary" round>请选择</el-button>
|
||||
<org-items v-model="select"/>
|
||||
<org-picker title="请选择可发起本审批的人员/部门" multiple ref="orgPicker" :selected="select" @ok="selected"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import {computed, defineExpose} from 'vue'
|
||||
const props = defineProps({
|
||||
config:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const showOrgSelect = ref(false)
|
||||
const select = computed(()=>{
|
||||
return props.config.assignedUser
|
||||
|
||||
})
|
||||
|
||||
const selectOrg = () => {
|
||||
// this.$refs.orgPicker.show()
|
||||
}
|
||||
|
||||
const selected = (item) => {
|
||||
item.forEach(val => select.push(val))
|
||||
}
|
||||
|
||||
const removeOrgItem = (index) => {
|
||||
this.select.splice(index, 1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
342
src/views/workflow/process/config/TriggerNodeConfig.vue
Normal file
342
src/views/workflow/process/config/TriggerNodeConfig.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="top" label-width="90px">
|
||||
<el-form-item label="选择触发的动作" prop="text" class="user-type">
|
||||
<el-radio-group v-model="config.type">
|
||||
<el-radio label="WEBHOOK">发送网络请求</el-radio>
|
||||
<el-radio label="EMAIL">发送邮件</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="config.type === 'WEBHOOK'">
|
||||
<el-form-item label="请求地址" prop="text">
|
||||
<el-input placeholder="请输入URL地址" v-model="config.http.url">
|
||||
<el-select v-model="config.http.method" style="width: 85px;" slot="prepend" placeholder="URL" filterable>
|
||||
<el-option label="GET" value="GET"></el-option>
|
||||
<el-option label="POST" value="POST"></el-option>
|
||||
<el-option label="PUT" value="PUT"></el-option>
|
||||
<el-option label="DELETE" value="DELETE"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Header请求头" prop="text">
|
||||
<div slot="label">
|
||||
<span style="margin-right: 10px">Header请求头</span>
|
||||
<el-button type="primary" @click="addItem(config.http.headers)" link> + 添加</el-button>
|
||||
</div>
|
||||
<div v-for="(header, index) in config.http.headers" :key="index">
|
||||
-
|
||||
<el-input placeholder="参数名" style="width: 100px;" v-model="header.name"/>
|
||||
<el-radio-group v-model="header.isField">
|
||||
<el-radio-button :label="true">表单</el-radio-button>
|
||||
<el-radio-button :label="false">固定</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="header.isField" style="width: 180px;" v-model="header.value"
|
||||
placeholder="请选择表单字段" filterable>
|
||||
<el-option v-for="form in forms" :key="form.id" :label="form.title" :value="form.title"></el-option>
|
||||
</el-select>
|
||||
<el-input v-else placeholder="请设置字段值" v-model="header.value" style="width: 180px;"/>
|
||||
<el-icon class="el-icon-delete" @click="delItem(config.http.headers, index)"
|
||||
style="margin-left: 5px; color: #c75450; cursor: pointer"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="Header请求参数" prop="text">
|
||||
<div slot="label">
|
||||
<span style="margin-right: 10px">请求参数 </span>
|
||||
<el-button style="margin-right: 20px" type="primary" @click="addItem(config.http.params)" link> + 添加</el-button>
|
||||
<span>参数类型 - </span>
|
||||
<el-radio-group style="margin: 0 5px;" v-model="config.http.contentType">
|
||||
<el-radio-button label="JSON">json</el-radio-button>
|
||||
<el-radio-button label="FORM">form</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-for="(param, index) in config.http.params" :key="index">
|
||||
-
|
||||
<el-input placeholder="参数名" style="width: 100px;" v-model="param.name"/>
|
||||
<el-radio-group style="margin: 0 5px;" v-model="param.isField">
|
||||
<el-radio-button :label="true">表单</el-radio-button>
|
||||
<el-radio-button :label="false">固定</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="param.isField" style="width: 180px;" v-model="param.value"
|
||||
placeholder="请选择表单字段" filterable>
|
||||
<el-option v-for="form in forms" :key="form.id" :label="form.title" :value="form.id"></el-option>
|
||||
</el-select>
|
||||
<el-input v-else placeholder="请设置字段值" v-model="param.value" style="width: 180px;"/>
|
||||
<el-icon class="el-icon-delete" @click="delItem(config.http.params, index)"
|
||||
style="margin-left: 5px; color: #c75450; cursor: pointer"/>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="请求结果处理" prop="text">-->
|
||||
<div slot="label">
|
||||
<span>请求结果处理</span>
|
||||
<span style="margin-left: 20px">自定义脚本: </span>
|
||||
<el-switch v-model="config.http.handlerByScript"></el-switch>
|
||||
</div>
|
||||
<span class="item-desc" v-if="config.http.handlerByScript">
|
||||
<p>👉 返回值为 ture 则流程通过,为 false 则流程将被驳回</p>
|
||||
<p>👉 (上线注意)占不支持ES高级语法</p>
|
||||
<p>👉 (上线注意)不支持浏览器的内置函数</p>
|
||||
<!-- <div>支持函数-->
|
||||
<!-- <span style="color: dodgerblue">setFormByName(-->
|
||||
<!-- <span style="color: #939494">'表单字段名', '表单字段值'</span>-->
|
||||
<!-- )</span>-->
|
||||
<!-- 可改表单数据-->
|
||||
<!-- </div>-->
|
||||
</span>
|
||||
<span class="item-desc" v-else>👉 无论请求结果如何,均通过</span>
|
||||
<div v-if="config.http.handlerByScript">
|
||||
<div>
|
||||
<el-button @click="requestTestHandler">测试</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<span>请求成功😀:</span>
|
||||
<js-code-edit v-model="config.http.success" :editor-placeholder="'请输入js代码'"
|
||||
:editor-height="250" :tab-size="2"/>
|
||||
</div>
|
||||
<div>
|
||||
<span>请求失败😥:</span>
|
||||
<js-code-edit v-model="config.http.fail" :editor-placeholder="'请输入js代码'"
|
||||
:editor-height="250" :tab-size="2"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
<div v-else-if="config.type === 'EMAIL'">
|
||||
<el-form-item label="邮件主题" prop="text">
|
||||
<el-input placeholder="请输入邮件主题" v-model="config.email.subject"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="收件方" prop="text">
|
||||
<el-select style="width: 100%;" v-model="config.email.to" filterable multiple allow-create
|
||||
default-first-option placeholder="请输入收件人">
|
||||
<el-option v-for="sender in config.email.to" :key="sender" :label="sender" :value="sender"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送方" prop="text">
|
||||
<el-select style="width: 100%;" v-model="config.email.cc" filterable multiple allow-create
|
||||
default-first-option placeholder="请输入收件人">
|
||||
<el-option v-for="item in config.email.cc" :key="item" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮件正文" prop="text">
|
||||
<el-input type="textarea" v-model="config.email.content" :rows="4"
|
||||
placeholder="邮件内容,支持变量提取表单数据 ${表单字段名} "></el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, defineProps} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import axios from "axios";
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import JsCodeEdit from "@/components/codeEdit/JsCodeEdit.vue";
|
||||
|
||||
const processStore = useProcessStore()
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const forms = computed(() => {
|
||||
return processStore.getDesign().formItems || []
|
||||
})
|
||||
|
||||
const addItem = (items) => {
|
||||
if (items.length > 0 && (items[items.length - 1].name.trim() === ''
|
||||
|| items[items.length - 1].value.trim() === '')) {
|
||||
ElMessage.warning("请完善之前项后在添加")
|
||||
return;
|
||||
}
|
||||
items.push({name: '', value: '', isField: true})
|
||||
}
|
||||
|
||||
|
||||
const delItem = (items, index) => {
|
||||
items.splice(index, 1)
|
||||
}
|
||||
//url规范性检查
|
||||
const restfulCheck = (url) => {
|
||||
const httpProtocolPattern = /^http:/;
|
||||
const httpsProtocolPattern = /^https:/;
|
||||
const restfulUrlPattern = /\/\w+\/\w+(\/\{[^}]+\})*/;
|
||||
if (httpProtocolPattern.test(url) || httpsProtocolPattern.test(url)) {
|
||||
return restfulUrlPattern.test(url);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//是否含有动态参数
|
||||
const hasUrlParams = (url) => {
|
||||
const pattern = /{[^{}]+}/g;
|
||||
let match;
|
||||
while ((match = pattern.exec(url)) !== null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//获取到动态参数的名称
|
||||
const getDynamicParamNames = (url) => {
|
||||
const pattern = /{([^{}]+)}/g;
|
||||
let match;
|
||||
const paramNames = [];
|
||||
while ((match = pattern.exec(url)) !== null) {
|
||||
if (match[0].startsWith('{') && match[0].endsWith('}')) {
|
||||
const paramName = match[1];
|
||||
paramNames.push(paramName);
|
||||
}
|
||||
}
|
||||
return paramNames;
|
||||
}
|
||||
//替换rul动态参数
|
||||
const replaceDynamicParams = (url, params) => {
|
||||
const dynamicParamPattern = /{\s*(\w+)\s*}/g;
|
||||
return url.replace(dynamicParamPattern, (match, param) => {
|
||||
return params[param] || '0';
|
||||
});
|
||||
}
|
||||
//获取到参数值
|
||||
const getParamsValue = (params, paramName) => {
|
||||
for (let param of params) {
|
||||
if (param.name === paramName) {
|
||||
return param.value
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//设置头部
|
||||
const setHeaders = (http) => {
|
||||
let headers = {}
|
||||
for (let header of http.headers) {
|
||||
if (header.isField) {
|
||||
ElMessage.error("测试只支持固定参数")
|
||||
return
|
||||
}
|
||||
if (header.name !== "" && header.value !== "") {
|
||||
this.$set(headers, header.name, header.value);
|
||||
}
|
||||
}
|
||||
if (http.contentType === "FORM") {
|
||||
this.$set(headers, "Content-Type", "multipart/form-data")
|
||||
} else {
|
||||
this.$set(headers, "Content-Type", "application/json")
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
//设置post和put参数
|
||||
const setPostAndPutParams = (http) => {
|
||||
let params = {}
|
||||
for (let param of http.params) {
|
||||
params[param.name] = param.value
|
||||
}
|
||||
return params;
|
||||
}
|
||||
//设置get和delete的参数
|
||||
const setGetAndDeleteParams = (http) => {
|
||||
let dynamicParams = []
|
||||
let url = http.url
|
||||
let hasParams = hasUrlParams(url)
|
||||
if (hasParams) {
|
||||
dynamicParams = getDynamicParamNames(url);
|
||||
let replaceParams = {}
|
||||
for (let paramsName of dynamicParams) {
|
||||
let value = getParamsValue(http.params, paramsName)
|
||||
if (null == value) {
|
||||
ElMessage.error(paramsName + '参数未设置')
|
||||
return;
|
||||
}
|
||||
replaceParams[paramsName] = value
|
||||
}
|
||||
url = replaceDynamicParams(url, replaceParams);
|
||||
}
|
||||
if (http.method === "DELETE") {
|
||||
return url;
|
||||
}
|
||||
let getParams = []
|
||||
for (let param of http.params) {
|
||||
if (dynamicParams.indexOf(param.name) === -1 && param.name !== '' && param.value !== '') {
|
||||
getParams.push(param.name + "=" + param.value)
|
||||
}
|
||||
}
|
||||
if (getParams.length > 0) {
|
||||
url += "?"
|
||||
for (let i = 0; i < getParams.length; i++) {
|
||||
if (i !== getParams.length - 1) {
|
||||
url += getParams[i] + "&";
|
||||
} else {
|
||||
url += getParams[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
//请求测试
|
||||
|
||||
const requestTestHandler = () => {
|
||||
let http = props.config.http;
|
||||
if (http.url == null || http.url === '') {
|
||||
ElMessage.error("请填写请求路径!")
|
||||
return
|
||||
}
|
||||
if (!restfulCheck(http.url)) {
|
||||
ElMessage.error("当前只支持 RESTful URL!")
|
||||
return
|
||||
}
|
||||
let headers = setHeaders(http)
|
||||
let request
|
||||
switch (http.method) {
|
||||
case "GET":
|
||||
case "DELETE":
|
||||
let url = setGetAndDeleteParams(http)
|
||||
if (null == url) {
|
||||
return;
|
||||
}
|
||||
request = axios.request({
|
||||
method: http.method,
|
||||
url: url,
|
||||
headers: headers,
|
||||
});
|
||||
break;
|
||||
case "POST":
|
||||
case "PUT":
|
||||
request = axios.request({
|
||||
method: http.method,
|
||||
url: http.url,
|
||||
headers: headers,
|
||||
data: setPostAndPutParams(http)
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
console.log("==================[测试打印内容]==================")
|
||||
request.then(res => {
|
||||
console.log(res)
|
||||
if (res.status === 200) {
|
||||
let data = res.data
|
||||
console.log(data)
|
||||
let successFun = eval("(false ||" + http.success + ")");
|
||||
let result = successFun(data);
|
||||
console.log(result, "成功函数执行的返回结果")
|
||||
} else {
|
||||
let failFun = eval("(false ||" + http.fail + ")");
|
||||
let result = failFun(res);
|
||||
console.log(result, "失败函数执行的返回结果")
|
||||
}
|
||||
}).finally(() => {
|
||||
console.log("==================[测试打印结束]==================")
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-desc {
|
||||
color: #939494;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user