模型基础信息
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
|
"less": "^4.2.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"luckyexcel": "^1.0.1",
|
"luckyexcel": "^1.0.1",
|
||||||
"luckysheet": "^2.1.13",
|
"luckysheet": "^2.1.13",
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ export const listWFForm = (data: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有流程表单
|
||||||
|
*/
|
||||||
|
export const listAllWFForm = () => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/listAll',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据id查询表单详细信息
|
* 根据id查询表单详细信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
export function getAreaTree(data: any) {
|
export function getAreaTree(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/user-boot/dept/getAreaTree',
|
url: '/user-boot/dept/getAreaTree',
|
||||||
@@ -22,6 +23,7 @@ export function addDept(data: any) {
|
|||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateDept(data: any) {
|
export function updateDept(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/user-boot/dept/update',
|
url: '/user-boot/dept/update',
|
||||||
@@ -46,3 +48,14 @@ export function selectPid(data: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门信息树
|
||||||
|
*/
|
||||||
|
export function deptTreeSelector() {
|
||||||
|
return request({
|
||||||
|
url: '/user-boot/dept/deptTreeSelector',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -153,3 +153,12 @@ export function getUserByRoleType(data:any) {
|
|||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取部门下所有用户
|
||||||
|
export function listAllUserByDeptId(data:any) {
|
||||||
|
return request({
|
||||||
|
url: '/user-boot/user/listAllUserByDeptId?deptId=' + data,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
47
src/api/workflow-boot/model.ts
Normal file
47
src/api/workflow-boot/model.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import createAxios from '@/utils/request'
|
||||||
|
import { WORKFLOW_BOOT } from '@/utils/constantRequest'
|
||||||
|
|
||||||
|
const MAPPING_PATH = WORKFLOW_BOOT + '/flw/model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增流程
|
||||||
|
*/
|
||||||
|
export const addModel = (data:any) => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/add',
|
||||||
|
method: 'POST',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取执行监听器选择器
|
||||||
|
*/
|
||||||
|
export const executionListenerSelector = () => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/executionListenerSelector',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取自定义事件执行监听器选择器
|
||||||
|
*/
|
||||||
|
export const executionListenerSelectorForCustomEvent = () => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/executionListenerSelectorForCustomEvent',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务监听器选择器
|
||||||
|
*/
|
||||||
|
export const taskListenerSelector = () => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/taskListenerSelector',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
41
src/api/workflow-boot/template.ts
Normal file
41
src/api/workflow-boot/template.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import createAxios from '@/utils/request'
|
||||||
|
import { WORKFLOW_BOOT } from '@/utils/constantRequest'
|
||||||
|
|
||||||
|
const MAPPING_PATH = WORKFLOW_BOOT + '/flw/templateSn'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增流程
|
||||||
|
*/
|
||||||
|
export const addModel = (data:any) => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/add',
|
||||||
|
method: 'POST',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取流水号模板列表选择器
|
||||||
|
*/
|
||||||
|
export const flwTemplateSnSelector = (data:any) => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/flwTemplateSnSelector',
|
||||||
|
method: 'POST',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取打印模板列表选择器
|
||||||
|
*/
|
||||||
|
export const flwTemplatePrintSelector = (data:any) => {
|
||||||
|
return createAxios({
|
||||||
|
url: MAPPING_PATH + '/flwTemplatePrintSelector',
|
||||||
|
method: 'POST',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
142
src/components/Selector/iconMobileSelector.vue
Normal file
142
src/components/Selector/iconMobileSelector.vue
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="移动端图标选择"
|
||||||
|
:mask-closable="false"
|
||||||
|
:width="800"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="onCancel"
|
||||||
|
>
|
||||||
|
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="paneChange">
|
||||||
|
<a-tab-pane v-for="item in iconData" :key="item.key" :tab="item.name">
|
||||||
|
<div v-if="item.iconItem.length > 1" class="xn-icon-select-radio">
|
||||||
|
<a-radio-group v-model:value="iconItemDefault" @change="radioGroupChange">
|
||||||
|
<a-radio-button v-for="iconItem in item.iconItem" :key="iconItem.key" :value="iconItem.key">{{
|
||||||
|
iconItem.name
|
||||||
|
}}</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :key="iconItemIns" v-for="iconItemIns in item.iconItem">
|
||||||
|
<div v-if="iconItemIns.key === iconItemDefault" class="xn-icon-select-list">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="icon in iconItemIns.item"
|
||||||
|
:key="icon"
|
||||||
|
:class="icon === modelValue ? 'active' : ''"
|
||||||
|
@click="selectIcon(icon.font_class)"
|
||||||
|
>
|
||||||
|
<span class="snowy xn-icons" :class="icon.font_class"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import config from '@/assets/icons/mobile'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
iconData: [],
|
||||||
|
modelValue: '',
|
||||||
|
activeKey: 'default',
|
||||||
|
iconItemDefault: 'default'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.iconData.push(...config.icons)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 打开
|
||||||
|
showIconModal(value) {
|
||||||
|
this.visible = true
|
||||||
|
this.defaultSetting(value)
|
||||||
|
},
|
||||||
|
// 默认配置
|
||||||
|
defaultSetting(value) {
|
||||||
|
if (value) {
|
||||||
|
this.modelValue = value
|
||||||
|
// 判断展开哪个
|
||||||
|
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
|
||||||
|
this.activeKey = 'default'
|
||||||
|
if (value.indexOf('-two-tone') > -1) {
|
||||||
|
this.iconItemDefault = 'twotone'
|
||||||
|
} else if (value.indexOf('-filled') > -1) {
|
||||||
|
this.iconItemDefault = 'filled'
|
||||||
|
}
|
||||||
|
} else if (value.indexOf('-extend') > -1) {
|
||||||
|
// 扩展列表
|
||||||
|
this.activeKey = 'extend'
|
||||||
|
// 如扩展其他顶部单选的情况,默认选中在这里配置,同时这里需要做判断
|
||||||
|
// this.iconItemDefault = '您的json中配置的'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 切换标签页,如果是切换到了没用额外的标签页的地方,我们将其置为默认
|
||||||
|
paneChange(e) {
|
||||||
|
if (e.indexOf('default') === -1) {
|
||||||
|
this.iconItemDefault = 'default'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 切换icon风格
|
||||||
|
radioGroupChange(e) {
|
||||||
|
this.iconItemDefault = e.target.value
|
||||||
|
},
|
||||||
|
// 选择图标后关闭并返回
|
||||||
|
selectIcon(value) {
|
||||||
|
this.defaultValue = value
|
||||||
|
this.visible = false
|
||||||
|
// eslint-disable-next-line vue/require-explicit-emits
|
||||||
|
this.$emit('iconCallBack', this.defaultValue)
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
this.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.xn-icon-select-radio {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.xn-icons {
|
||||||
|
font-size: 26px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.xn-icon-select-list {
|
||||||
|
height: 360px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.xn-icon-select-list ul {
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
padding: 18px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
vertical-align: top;
|
||||||
|
box-shadow: 0 0 0 1px var(--border-color-split);
|
||||||
|
transition: all 0.1s;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
143
src/components/Selector/iconSelector.vue
Normal file
143
src/components/Selector/iconSelector.vue
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="图标选择"
|
||||||
|
:mask-closable="false"
|
||||||
|
:width="800"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="onCancel"
|
||||||
|
>
|
||||||
|
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="paneChange">
|
||||||
|
<a-tab-pane v-for="item in iconData" :key="item.key" :tab="item.name">
|
||||||
|
<div v-if="item.iconItem.length > 1" class="xn-icon-select-radio">
|
||||||
|
<a-radio-group v-model:value="iconItemDefault" @change="radioGroupChange">
|
||||||
|
<a-radio-button v-for="iconItem in item.iconItem" :key="iconItem.key" :value="iconItem.key">{{
|
||||||
|
iconItem.name
|
||||||
|
}}</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :key="iconItemIns" v-for="iconItemIns in item.iconItem">
|
||||||
|
<div v-if="iconItemIns.key === iconItemDefault" class="xn-icon-select-list">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="icon in iconItemIns.item"
|
||||||
|
:key="icon"
|
||||||
|
:class="icon === modelValue ? 'active' : ''"
|
||||||
|
@click="selectIcon(icon)"
|
||||||
|
>
|
||||||
|
<component :is="icon" class="xn-icons" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import config from '@/config/iconSelect'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
iconData: [],
|
||||||
|
modelValue: '',
|
||||||
|
activeKey: 'default',
|
||||||
|
iconItemDefault: 'default'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.iconData.push(...config.icons)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 打开
|
||||||
|
showIconModal(value) {
|
||||||
|
this.visible = true
|
||||||
|
this.defaultSetting(value)
|
||||||
|
},
|
||||||
|
// 默认配置
|
||||||
|
defaultSetting(value) {
|
||||||
|
if (value) {
|
||||||
|
this.modelValue = value
|
||||||
|
// 判断展开哪个
|
||||||
|
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
|
||||||
|
this.activeKey = 'default'
|
||||||
|
if (value.indexOf('-two-tone') > -1) {
|
||||||
|
this.iconItemDefault = 'twotone'
|
||||||
|
} else if (value.indexOf('-filled') > -1) {
|
||||||
|
this.iconItemDefault = 'filled'
|
||||||
|
}
|
||||||
|
} else if (value.indexOf('-extend') > -1) {
|
||||||
|
// 扩展列表
|
||||||
|
this.activeKey = 'extend'
|
||||||
|
// 如扩展其他顶部单选的情况,默认选中在这里配置,同时这里需要做判断
|
||||||
|
// this.iconItemDefault = '您的json中配置的'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 切换标签页,如果是切换到了没用额外的标签页的地方,我们将其置为默认
|
||||||
|
paneChange(e) {
|
||||||
|
if (e.indexOf('default') === -1) {
|
||||||
|
this.iconItemDefault = 'default'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 切换icon风格
|
||||||
|
radioGroupChange(e) {
|
||||||
|
this.iconItemDefault = e.target.value
|
||||||
|
},
|
||||||
|
// 选择图标后关闭并返回
|
||||||
|
selectIcon(value) {
|
||||||
|
this.defaultValue = value
|
||||||
|
this.visible = false
|
||||||
|
// eslint-disable-next-line vue/require-explicit-emits
|
||||||
|
this.$emit('iconCallBack', this.defaultValue)
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
this.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.xn-icon-select-radio {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.xn-icons {
|
||||||
|
font-size: 26px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.xn-icon-select-list {
|
||||||
|
height: 360px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.xn-icon-select-list ul {
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
padding: 18px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
vertical-align: top;
|
||||||
|
box-shadow: 0 0 0 1px var(--border-color-split);
|
||||||
|
transition: all 0.1s;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
397
src/components/Selector/orgSelectorPlus.vue
Normal file
397
src/components/Selector/orgSelectorPlus.vue
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="机构选择"
|
||||||
|
:width="1000"
|
||||||
|
:mask-closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="7">
|
||||||
|
<a-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData"
|
||||||
|
v-model:expandedKeys="defaultExpandedKeys"
|
||||||
|
:tree-data="treeData"
|
||||||
|
:field-names="treeFieldNames"
|
||||||
|
@select="treeSelect"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<div class="table-operator" style="margin-bottom: 10px">
|
||||||
|
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item name="searchKey">
|
||||||
|
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入机构名" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-button type="primary" class="primarySele" @click="loadData()"> 查询 </a-button>
|
||||||
|
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="org-table">
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
size="small"
|
||||||
|
:columns="commons"
|
||||||
|
:data-source="tableData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="pageLoading"
|
||||||
|
bordered
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'category'">
|
||||||
|
{{ $TOOL.dictTypeData('ORG_CATEGORY', record.category) }}
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" size="small" @click="addRecord(record)">添加</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a-pagination
|
||||||
|
v-if="!isEmpty(tableData)"
|
||||||
|
v-model:current="current"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
size="small"
|
||||||
|
showSizeChanger
|
||||||
|
@change="paginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<div class="org-table">
|
||||||
|
<a-table
|
||||||
|
ref="selectedTable"
|
||||||
|
size="small"
|
||||||
|
:columns="selectedCommons"
|
||||||
|
:data-source="selectedData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="selectedTableListLoading"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>已选择: {{ selectedData.length }}</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delAllRecord">全部移除</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delRecord(record)">移除</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="orgSelectorPlus">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { remove, isEmpty } from 'lodash-es'
|
||||||
|
// 弹窗是否打开
|
||||||
|
const visible = ref(false)
|
||||||
|
// 主表格common
|
||||||
|
const commons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '机构名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类',
|
||||||
|
dataIndex: 'category'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 选中表格的表格common
|
||||||
|
const selectedCommons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '机构名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 主表格的ref 名称
|
||||||
|
const table = ref()
|
||||||
|
// 选中表格的ref 名称
|
||||||
|
const selectedTable = ref()
|
||||||
|
const tableRecordNum = ref()
|
||||||
|
const searchFormState = ref({})
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const cardLoading = ref(true)
|
||||||
|
const pageLoading = ref(false)
|
||||||
|
const selectedTableListLoading = ref(false)
|
||||||
|
// 替换treeNode 中 title,key,children
|
||||||
|
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||||
|
// 获取机构树数据
|
||||||
|
const treeData = ref()
|
||||||
|
// 默认展开二级树的节点id
|
||||||
|
const defaultExpandedKeys = ref([])
|
||||||
|
const emit = defineEmits({ onBack: null })
|
||||||
|
const tableData = ref([])
|
||||||
|
const selectedData = ref([])
|
||||||
|
const recordIds = ref()
|
||||||
|
const props = defineProps(['orgPageApi', 'orgTreeApi', 'radioModel', 'dataIsConverterFlw', 'checkedOrgListApi'])
|
||||||
|
// 是否是单选
|
||||||
|
const radioModel = props.radioModel || false
|
||||||
|
// 数据是否转换成工作流格式
|
||||||
|
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||||
|
// 分页相关
|
||||||
|
const current = ref(0) // 当前页数
|
||||||
|
const pageSize = ref(20) // 每页条数
|
||||||
|
const total = ref(0) // 数据总数
|
||||||
|
|
||||||
|
// 打开弹框
|
||||||
|
const showOrgPlusModal = (ids) => {
|
||||||
|
visible.value = true
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
ids = goDataConverter(ids)
|
||||||
|
}
|
||||||
|
recordIds.value = ids
|
||||||
|
if (props.orgTreeApi) {
|
||||||
|
// 获取机构树
|
||||||
|
props.orgTreeApi().then((data) => {
|
||||||
|
cardLoading.value = false
|
||||||
|
if (data !== null) {
|
||||||
|
treeData.value = data
|
||||||
|
// 默认展开2级
|
||||||
|
treeData.value.forEach((item) => {
|
||||||
|
// 因为0的顶级
|
||||||
|
if (item.parentId === '0') {
|
||||||
|
defaultExpandedKeys.value.push(item.id)
|
||||||
|
// 取到下级ID
|
||||||
|
if (item.children) {
|
||||||
|
item.children.forEach((items) => {
|
||||||
|
defaultExpandedKeys.value.push(items.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
searchFormState.value.size = pageSize.value
|
||||||
|
loadData()
|
||||||
|
if (props.checkedOrgListApi) {
|
||||||
|
if (isEmpty(recordIds.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const param = {
|
||||||
|
idList: recordIds.value
|
||||||
|
}
|
||||||
|
selectedTableListLoading.value = true
|
||||||
|
props
|
||||||
|
.checkedOrgListApi(param)
|
||||||
|
.then((data) => {
|
||||||
|
selectedData.value = data
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
selectedTableListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 查询主表格数据
|
||||||
|
const loadData = () => {
|
||||||
|
pageLoading.value = true
|
||||||
|
props
|
||||||
|
.orgPageApi(searchFormState.value)
|
||||||
|
.then((data) => {
|
||||||
|
current.value = data.current
|
||||||
|
total.value = data.total
|
||||||
|
// 重置、赋值
|
||||||
|
tableData.value = []
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = data.records
|
||||||
|
if (data.records) {
|
||||||
|
tableRecordNum.value = data.records.length
|
||||||
|
} else {
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pageLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// pageSize改变回调分页事件
|
||||||
|
const paginationChange = (page, pageSize) => {
|
||||||
|
searchFormState.value.current = page
|
||||||
|
searchFormState.value.size = pageSize
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const judge = () => {
|
||||||
|
if (radioModel && selectedData.value.length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 添加记录
|
||||||
|
const addRecord = (record) => {
|
||||||
|
if (!judge()) {
|
||||||
|
message.warning('只可选择一条')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||||
|
if (selectedRecord.length === 0) {
|
||||||
|
selectedData.value.push(record)
|
||||||
|
} else {
|
||||||
|
message.warning('该记录已存在')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加全部
|
||||||
|
const addAllPageRecord = () => {
|
||||||
|
let newArray = selectedData.value.concat(tableData.value)
|
||||||
|
let list = []
|
||||||
|
for (let item1 of newArray) {
|
||||||
|
let flag = true
|
||||||
|
for (let item2 of list) {
|
||||||
|
if (item1.id === item2.id) {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
list.push(item1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedData.value = list
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delRecord = (record) => {
|
||||||
|
remove(selectedData.value, (item) => item.id === record.id)
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delAllRecord = () => {
|
||||||
|
selectedData.value = []
|
||||||
|
}
|
||||||
|
// 点击树查询
|
||||||
|
const treeSelect = (selectedKeys) => {
|
||||||
|
searchFormState.value.current = 0
|
||||||
|
if (selectedKeys.length > 0) {
|
||||||
|
searchFormState.value.orgId = selectedKeys.toString()
|
||||||
|
} else {
|
||||||
|
delete searchFormState.value.orgId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
const handleOk = () => {
|
||||||
|
const value = []
|
||||||
|
selectedData.value.forEach((item) => {
|
||||||
|
const obj = {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
category: item.category
|
||||||
|
}
|
||||||
|
value.push(obj)
|
||||||
|
})
|
||||||
|
// 判断是否做数据的转换为工作流需要的
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
emit('onBack', outDataConverter(value))
|
||||||
|
} else {
|
||||||
|
emit('onBack', value)
|
||||||
|
}
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
delete searchFormState.value.searchKey
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
searchFormState.value = {}
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = []
|
||||||
|
current.value = 0
|
||||||
|
pageSize.value = 20
|
||||||
|
total.value = 0
|
||||||
|
selectedData.value = []
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据进入后转换
|
||||||
|
const goDataConverter = (data) => {
|
||||||
|
const resultData = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
const values = data[0].value.split(',')
|
||||||
|
if (JSON.stringify(values) !== '[""]') {
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
resultData.push(values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 数据出口转换器
|
||||||
|
const outDataConverter = (data) => {
|
||||||
|
const obj = {}
|
||||||
|
let label = ''
|
||||||
|
let value = ''
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data.length === i + 1) {
|
||||||
|
label = label + data[i].name
|
||||||
|
value = value + data[i].id
|
||||||
|
} else {
|
||||||
|
label = label + data[i].name + ','
|
||||||
|
value = value + data[i].id + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.key = 'ORG'
|
||||||
|
obj.label = label
|
||||||
|
obj.value = value
|
||||||
|
obj.extJson = ''
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
showOrgPlusModal
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.selectorTreeDiv {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.cardTag {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.primarySele {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.org-table {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
398
src/components/Selector/posSelectorPlus.vue
Normal file
398
src/components/Selector/posSelectorPlus.vue
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="职位选择"
|
||||||
|
:width="1000"
|
||||||
|
:mask-closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="7">
|
||||||
|
<a-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData"
|
||||||
|
v-model:expandedKeys="defaultExpandedKeys"
|
||||||
|
:tree-data="treeData"
|
||||||
|
:field-names="treeFieldNames"
|
||||||
|
@select="treeSelect"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<div class="table-operator" style="margin-bottom: 10px">
|
||||||
|
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item name="searchKey">
|
||||||
|
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入职位名" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-button type="primary" class="primarySele" @click="loadData()"> 查询 </a-button>
|
||||||
|
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="pos-table">
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
size="small"
|
||||||
|
:columns="commons"
|
||||||
|
:data-source="tableData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="pageLoading"
|
||||||
|
bordered
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" size="small" @click="addRecord(record)">添加</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'category'">
|
||||||
|
{{ $TOOL.dictTypeData('POSITION_CATEGORY', record.category) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a-pagination
|
||||||
|
v-if="!isEmpty(tableData)"
|
||||||
|
v-model:current="current"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
size="small"
|
||||||
|
showSizeChanger
|
||||||
|
@change="paginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<div class="pos-table">
|
||||||
|
<a-table
|
||||||
|
ref="selectedTable"
|
||||||
|
size="small"
|
||||||
|
:columns="selectedCommons"
|
||||||
|
:data-source="selectedData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="selectedTableListLoading"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>已选择: {{ selectedData.length }}</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delAllRecord">全部移除</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delRecord(record)">移除</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="posSelectorPlus">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { remove, isEmpty } from 'lodash-es'
|
||||||
|
// 弹窗是否打开
|
||||||
|
const visible = ref(false)
|
||||||
|
// 主表格common
|
||||||
|
const commons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '职位名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类',
|
||||||
|
dataIndex: 'category'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 选中表格的表格common
|
||||||
|
const selectedCommons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '职位名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 主表格的ref 名称
|
||||||
|
const table = ref()
|
||||||
|
// 选中表格的ref 名称
|
||||||
|
const selectedTable = ref()
|
||||||
|
const tableRecordNum = ref()
|
||||||
|
const searchFormState = ref({})
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const cardLoading = ref(true)
|
||||||
|
const pageLoading = ref(false)
|
||||||
|
const selectedTableListLoading = ref(false)
|
||||||
|
// 替换treeNode 中 title,key,children
|
||||||
|
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||||
|
// 获取机构树数据
|
||||||
|
const treeData = ref()
|
||||||
|
// 默认展开二级树的节点id
|
||||||
|
const defaultExpandedKeys = ref([])
|
||||||
|
const emit = defineEmits({ onBack: null })
|
||||||
|
const tableData = ref([])
|
||||||
|
const selectedData = ref([])
|
||||||
|
const recordIds = ref()
|
||||||
|
const props = defineProps(['posPageApi', 'orgTreeApi', 'radioModel', 'dataIsConverterFlw', 'checkedPosListApi'])
|
||||||
|
// 是否是单选
|
||||||
|
const radioModel = props.radioModel || false
|
||||||
|
// 数据是否转换成工作流格式
|
||||||
|
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||||
|
// 分页相关
|
||||||
|
const current = ref(0) // 当前页数
|
||||||
|
const pageSize = ref(20) // 每页条数
|
||||||
|
const total = ref(0) // 数据总数
|
||||||
|
|
||||||
|
// 打开弹框
|
||||||
|
const showPosPlusModal = (ids) => {
|
||||||
|
visible.value = true
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
ids = goDataConverter(ids)
|
||||||
|
}
|
||||||
|
recordIds.value = ids
|
||||||
|
// 获取机构树
|
||||||
|
if (props.orgTreeApi) {
|
||||||
|
// 获取机构树
|
||||||
|
props.orgTreeApi().then((data) => {
|
||||||
|
cardLoading.value = false
|
||||||
|
if (data !== null) {
|
||||||
|
treeData.value = data
|
||||||
|
// 默认展开2级
|
||||||
|
treeData.value.forEach((item) => {
|
||||||
|
// 因为0的顶级
|
||||||
|
if (item.parentId === '0') {
|
||||||
|
defaultExpandedKeys.value.push(item.id)
|
||||||
|
// 取到下级ID
|
||||||
|
if (item.children) {
|
||||||
|
item.children.forEach((items) => {
|
||||||
|
defaultExpandedKeys.value.push(items.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
searchFormState.value.size = pageSize.value
|
||||||
|
loadData()
|
||||||
|
if (props.checkedPosListApi) {
|
||||||
|
if (isEmpty(recordIds.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const param = {
|
||||||
|
idList: recordIds.value
|
||||||
|
}
|
||||||
|
selectedTableListLoading.value = true
|
||||||
|
props
|
||||||
|
.checkedPosListApi(param)
|
||||||
|
.then((data) => {
|
||||||
|
selectedData.value = data
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
selectedTableListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 查询主表格数据
|
||||||
|
const loadData = () => {
|
||||||
|
pageLoading.value = true
|
||||||
|
props
|
||||||
|
.posPageApi(searchFormState.value)
|
||||||
|
.then((data) => {
|
||||||
|
current.value = data.current
|
||||||
|
total.value = data.total
|
||||||
|
// 重置、赋值
|
||||||
|
tableData.value = []
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = data.records
|
||||||
|
if (data.records) {
|
||||||
|
tableRecordNum.value = data.records.length
|
||||||
|
} else {
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pageLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// pageSize改变回调分页事件
|
||||||
|
const paginationChange = (page, pageSize) => {
|
||||||
|
searchFormState.value.current = page
|
||||||
|
searchFormState.value.size = pageSize
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const judge = () => {
|
||||||
|
if (radioModel && selectedData.value.length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 添加记录
|
||||||
|
const addRecord = (record) => {
|
||||||
|
if (!judge()) {
|
||||||
|
message.warning('只可选择一条')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||||
|
if (selectedRecord.length === 0) {
|
||||||
|
selectedData.value.push(record)
|
||||||
|
} else {
|
||||||
|
message.warning('该记录已存在')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加全部
|
||||||
|
const addAllPageRecord = () => {
|
||||||
|
let newArray = selectedData.value.concat(tableData.value)
|
||||||
|
let list = []
|
||||||
|
for (let item1 of newArray) {
|
||||||
|
let flag = true
|
||||||
|
for (let item2 of list) {
|
||||||
|
if (item1.id === item2.id) {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
list.push(item1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedData.value = list
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delRecord = (record) => {
|
||||||
|
remove(selectedData.value, (item) => item.id === record.id)
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delAllRecord = () => {
|
||||||
|
selectedData.value = []
|
||||||
|
}
|
||||||
|
// 点击树查询
|
||||||
|
const treeSelect = (selectedKeys) => {
|
||||||
|
searchFormState.value.current = 0
|
||||||
|
if (selectedKeys.length > 0) {
|
||||||
|
searchFormState.value.orgId = selectedKeys.toString()
|
||||||
|
} else {
|
||||||
|
delete searchFormState.value.orgId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
const handleOk = () => {
|
||||||
|
const value = []
|
||||||
|
selectedData.value.forEach((item) => {
|
||||||
|
const obj = {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
account: item.account
|
||||||
|
}
|
||||||
|
value.push(obj)
|
||||||
|
})
|
||||||
|
// 判断是否做数据的转换为工作流需要的
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
emit('onBack', outDataConverter(value))
|
||||||
|
} else {
|
||||||
|
emit('onBack', value)
|
||||||
|
}
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
delete searchFormState.value.searchKey
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
searchFormState.value = {}
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = []
|
||||||
|
current.value = 0
|
||||||
|
pageSize.value = 20
|
||||||
|
total.value = 0
|
||||||
|
selectedData.value = []
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据进入后转换
|
||||||
|
const goDataConverter = (data) => {
|
||||||
|
const resultData = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
const values = data[0].value.split(',')
|
||||||
|
if (JSON.stringify(values) !== '[""]') {
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
resultData.push(values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 数据出口转换器
|
||||||
|
const outDataConverter = (data) => {
|
||||||
|
const obj = {}
|
||||||
|
let label = ''
|
||||||
|
let value = ''
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data.length === i + 1) {
|
||||||
|
label = label + data[i].name
|
||||||
|
value = value + data[i].id
|
||||||
|
} else {
|
||||||
|
label = label + data[i].name + ','
|
||||||
|
value = value + data[i].id + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.key = 'POSITION'
|
||||||
|
obj.label = label
|
||||||
|
obj.value = value
|
||||||
|
obj.extJson = ''
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
showPosPlusModal
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.selectorTreeDiv {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.cardTag {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.primarySele {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.pos-table {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
448
src/components/Selector/roleSelectorPlus.vue
Normal file
448
src/components/Selector/roleSelectorPlus.vue
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="角色选择"
|
||||||
|
:width="1000"
|
||||||
|
:mask-closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="7">
|
||||||
|
<a-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData"
|
||||||
|
v-model:expandedKeys="defaultExpandedKeys"
|
||||||
|
:tree-data="treeData"
|
||||||
|
:field-names="treeFieldNames"
|
||||||
|
@select="treeSelect"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<div class="table-operator" style="margin-bottom: 10px">
|
||||||
|
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item name="searchKey">
|
||||||
|
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入角色名" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-button type="primary" class="primarySele" @click="loadData()"> 查询 </a-button>
|
||||||
|
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="role-table">
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
size="small"
|
||||||
|
:columns="commons"
|
||||||
|
:data-source="tableData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="pageLoading"
|
||||||
|
bordered
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" size="small" @click="addRecord(record)">添加</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'category'">
|
||||||
|
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a-pagination
|
||||||
|
v-if="!isEmpty(tableData)"
|
||||||
|
v-model:current="current"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
size="small"
|
||||||
|
showSizeChanger
|
||||||
|
@change="paginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<div class="role-table">
|
||||||
|
<a-table
|
||||||
|
ref="selectedTable"
|
||||||
|
size="small"
|
||||||
|
:columns="selectedCommons"
|
||||||
|
:data-source="selectedData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="selectedTableListLoading"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>已选择: {{ selectedData.length }}</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delAllRecord">全部移除</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delRecord(record)">移除</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="roleSelectorPlus">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { remove, isEmpty } from 'lodash-es'
|
||||||
|
// 弹窗是否打开
|
||||||
|
const visible = ref(false)
|
||||||
|
// 主表格common
|
||||||
|
const commons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类',
|
||||||
|
dataIndex: 'category'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 选中表格的表格common
|
||||||
|
const selectedCommons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 主表格的ref 名称
|
||||||
|
const table = ref()
|
||||||
|
// 选中表格的ref 名称
|
||||||
|
const selectedTable = ref()
|
||||||
|
const tableRecordNum = ref()
|
||||||
|
const searchFormState = ref({})
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const cardLoading = ref(true)
|
||||||
|
const pageLoading = ref(false)
|
||||||
|
const selectedTableListLoading = ref(false)
|
||||||
|
// 替换treeNode 中 title,key,children
|
||||||
|
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||||
|
// 获取机构树数据
|
||||||
|
const treeData = ref()
|
||||||
|
// 默认展开二级树的节点id
|
||||||
|
const defaultExpandedKeys = ref([])
|
||||||
|
const emit = defineEmits({ onBack: null })
|
||||||
|
const tableData = ref([])
|
||||||
|
const selectedData = ref([])
|
||||||
|
const recordIds = ref()
|
||||||
|
const props = defineProps({
|
||||||
|
rolePageApi: {
|
||||||
|
type: Function
|
||||||
|
},
|
||||||
|
orgTreeApi: {
|
||||||
|
type: Function
|
||||||
|
},
|
||||||
|
// 是否是单选
|
||||||
|
radioModel: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
// 数据是否转换成工作流格式
|
||||||
|
dataIsConverterFlw: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
// 是否展示‘全局’这个节点
|
||||||
|
roleGlobal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
checkedRoleListApi: {
|
||||||
|
type: Function
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 是否是单选
|
||||||
|
const radioModel = props.radioModel
|
||||||
|
// 数据是否转换成工作流格式
|
||||||
|
const dataIsConverterFlw = props.dataIsConverterFlw
|
||||||
|
// 是否展示‘全局’这个节点
|
||||||
|
const roleGlobal = props.roleGlobal
|
||||||
|
// 分页相关
|
||||||
|
const current = ref(0) // 当前页数
|
||||||
|
const pageSize = ref(20) // 每页条数
|
||||||
|
const total = ref(0) // 数据总数
|
||||||
|
|
||||||
|
// 打开弹框
|
||||||
|
const showRolePlusModal = (ids) => {
|
||||||
|
visible.value = true
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
ids = goDataConverter(ids)
|
||||||
|
}
|
||||||
|
recordIds.value = ids
|
||||||
|
if (props.orgTreeApi) {
|
||||||
|
// 获取机构树
|
||||||
|
props.orgTreeApi().then((data) => {
|
||||||
|
cardLoading.value = false
|
||||||
|
if (data !== null) {
|
||||||
|
treeData.value = data
|
||||||
|
// 树中插入全局角色类型
|
||||||
|
if (roleGlobal) {
|
||||||
|
const globalRoleType = [
|
||||||
|
{
|
||||||
|
id: 'GLOBAL',
|
||||||
|
parentId: '-1',
|
||||||
|
name: '全局'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
treeData.value = globalRoleType.concat(data)
|
||||||
|
}
|
||||||
|
// 默认展开2级
|
||||||
|
treeData.value.forEach((item) => {
|
||||||
|
// 因为0的顶级
|
||||||
|
if (item.parentId === '0') {
|
||||||
|
defaultExpandedKeys.value.push(item.id)
|
||||||
|
// 取到下级ID
|
||||||
|
if (item.children) {
|
||||||
|
item.children.forEach((items) => {
|
||||||
|
defaultExpandedKeys.value.push(items.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
searchFormState.value.size = pageSize.value
|
||||||
|
loadData()
|
||||||
|
if (props.checkedRoleListApi) {
|
||||||
|
if (isEmpty(recordIds.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const param = {
|
||||||
|
idList: recordIds.value
|
||||||
|
}
|
||||||
|
selectedTableListLoading.value = true
|
||||||
|
props
|
||||||
|
.checkedRoleListApi(param)
|
||||||
|
.then((data) => {
|
||||||
|
selectedData.value = data
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
selectedTableListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 查询主表格数据
|
||||||
|
const loadData = () => {
|
||||||
|
// 如果不是用全局的,我们每次查询加上这个条件
|
||||||
|
if (!roleGlobal) {
|
||||||
|
searchFormState.value.category = 'ORG'
|
||||||
|
}
|
||||||
|
pageLoading.value = true
|
||||||
|
props
|
||||||
|
.rolePageApi(searchFormState.value)
|
||||||
|
.then((data) => {
|
||||||
|
current.value = data.current
|
||||||
|
total.value = data.total
|
||||||
|
// 重置、赋值
|
||||||
|
tableData.value = []
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = data.records
|
||||||
|
if (data.records) {
|
||||||
|
tableRecordNum.value = data.records.length
|
||||||
|
} else {
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pageLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// pageSize改变回调分页事件
|
||||||
|
const paginationChange = (page, pageSize) => {
|
||||||
|
searchFormState.value.current = page
|
||||||
|
searchFormState.value.size = pageSize
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const judge = () => {
|
||||||
|
if (radioModel && selectedData.value.length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 添加记录
|
||||||
|
const addRecord = (record) => {
|
||||||
|
if (!judge()) {
|
||||||
|
message.warning('只可选择一条')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||||
|
if (selectedRecord.length === 0) {
|
||||||
|
selectedData.value.push(record)
|
||||||
|
} else {
|
||||||
|
message.warning('该记录已存在')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加全部
|
||||||
|
const addAllPageRecord = () => {
|
||||||
|
let newArray = selectedData.value.concat(tableData.value)
|
||||||
|
let list = []
|
||||||
|
for (let item1 of newArray) {
|
||||||
|
let flag = true
|
||||||
|
for (let item2 of list) {
|
||||||
|
if (item1.id === item2.id) {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
list.push(item1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedData.value = list
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delRecord = (record) => {
|
||||||
|
remove(selectedData.value, (item) => item.id === record.id)
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delAllRecord = () => {
|
||||||
|
selectedData.value = []
|
||||||
|
}
|
||||||
|
// 点击树查询
|
||||||
|
const treeSelect = (selectedKeys) => {
|
||||||
|
searchFormState.value.current = 0
|
||||||
|
if (selectedKeys.length > 0) {
|
||||||
|
if (selectedKeys[0] === 'GLOBAL') {
|
||||||
|
searchFormState.value.category = selectedKeys[0]
|
||||||
|
delete searchFormState.value.orgId
|
||||||
|
} else {
|
||||||
|
searchFormState.value.orgId = selectedKeys.toString()
|
||||||
|
delete searchFormState.value.category
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete searchFormState.value.category
|
||||||
|
delete searchFormState.value.orgId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
const handleOk = () => {
|
||||||
|
const value = []
|
||||||
|
selectedData.value.forEach((item) => {
|
||||||
|
const obj = {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name
|
||||||
|
}
|
||||||
|
value.push(obj)
|
||||||
|
})
|
||||||
|
// 判断是否做数据的转换为工作流需要的
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
emit('onBack', outDataConverter(value))
|
||||||
|
} else {
|
||||||
|
emit('onBack', value)
|
||||||
|
}
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
delete searchFormState.value.searchKey
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
searchFormState.value = {}
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = []
|
||||||
|
current.value = 0
|
||||||
|
pageSize.value = 20
|
||||||
|
total.value = 0
|
||||||
|
selectedData.value = []
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据进入后转换
|
||||||
|
const goDataConverter = (data) => {
|
||||||
|
const resultData = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
const values = data[0].value.split(',')
|
||||||
|
if (JSON.stringify(values) !== '[""]') {
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
resultData.push(values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 数据出口转换器
|
||||||
|
const outDataConverter = (data) => {
|
||||||
|
const obj = {}
|
||||||
|
let label = ''
|
||||||
|
let value = ''
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data.length === i + 1) {
|
||||||
|
label = label + data[i].name
|
||||||
|
value = value + data[i].id
|
||||||
|
} else {
|
||||||
|
label = label + data[i].name + ','
|
||||||
|
value = value + data[i].id + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.key = 'ROLE'
|
||||||
|
obj.label = label
|
||||||
|
obj.value = value
|
||||||
|
obj.extJson = ''
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
showRolePlusModal
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.selectorTreeDiv {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.cardTag {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.primarySele {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.role-table {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
399
src/components/Selector/userSelectorPlus.vue
Normal file
399
src/components/Selector/userSelectorPlus.vue
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="用户选择"
|
||||||
|
:width="1000"
|
||||||
|
:mask-closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="7">
|
||||||
|
<a-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData"
|
||||||
|
v-model:expandedKeys="defaultExpandedKeys"
|
||||||
|
:tree-data="treeData"
|
||||||
|
:field-names="treeFieldNames"
|
||||||
|
@select="treeSelect"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<div class="table-operator" style="margin-bottom: 10px">
|
||||||
|
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item name="searchKey">
|
||||||
|
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入用户名" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-button type="primary" class="primarySele" @click="loadData()"> 查询 </a-button>
|
||||||
|
<a-button class="snowy-buttom-left" @click="reset()"> 重置 </a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="user-table">
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
size="small"
|
||||||
|
:columns="commons"
|
||||||
|
:data-source="tableData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="pageLoading"
|
||||||
|
bordered
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" size="small" @click="addRecord(record)">添加</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'category'">
|
||||||
|
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a-pagination
|
||||||
|
v-if="!isEmpty(tableData)"
|
||||||
|
v-model:current="current"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
size="small"
|
||||||
|
showSizeChanger
|
||||||
|
@change="paginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<div class="user-table">
|
||||||
|
<a-table
|
||||||
|
ref="selectedTable"
|
||||||
|
size="small"
|
||||||
|
:columns="selectedCommons"
|
||||||
|
:data-source="selectedData"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:loading="selectedTableListLoading"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>已选择: {{ selectedData.length }}</span>
|
||||||
|
<div v-if="!radioModel" style="float: right">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delAllRecord">全部移除</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-button type="dashed" danger size="small" @click="delRecord(record)">移除</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="userSelectorPlus">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { remove, isEmpty } from 'lodash-es'
|
||||||
|
// 弹窗是否打开
|
||||||
|
const visible = ref(false)
|
||||||
|
// 主表格common
|
||||||
|
const commons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '用户名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '账号',
|
||||||
|
dataIndex: 'account'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 选中表格的表格common
|
||||||
|
const selectedCommons = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '用户名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 主表格的ref 名称
|
||||||
|
const table = ref()
|
||||||
|
// 选中表格的ref 名称
|
||||||
|
const selectedTable = ref()
|
||||||
|
const tableRecordNum = ref()
|
||||||
|
const searchFormState = ref({})
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const cardLoading = ref(true)
|
||||||
|
const pageLoading = ref(false)
|
||||||
|
const selectedTableListLoading = ref(false)
|
||||||
|
// 替换treeNode 中 title,key,children
|
||||||
|
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||||
|
// 获取机构树数据
|
||||||
|
const treeData = ref()
|
||||||
|
// 默认展开二级树的节点id
|
||||||
|
const defaultExpandedKeys = ref([])
|
||||||
|
const emit = defineEmits({ onBack: null })
|
||||||
|
const tableData = ref([])
|
||||||
|
const selectedData = ref([])
|
||||||
|
const recordIds = ref()
|
||||||
|
const props = defineProps(['radioModel', 'dataIsConverterFlw', 'orgTreeApi', 'userPageApi', 'checkedUserListApi'])
|
||||||
|
// 是否是单选
|
||||||
|
const radioModel = props.radioModel || false
|
||||||
|
// 数据是否转换成工作流格式
|
||||||
|
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||||
|
// 分页相关
|
||||||
|
const current = ref(0) // 当前页数
|
||||||
|
const pageSize = ref(20) // 每页条数
|
||||||
|
const total = ref(0) // 数据总数
|
||||||
|
|
||||||
|
// 打开弹框
|
||||||
|
const showUserPlusModal = (ids = []) => {
|
||||||
|
visible.value = true
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
ids = goDataConverter(ids)
|
||||||
|
}
|
||||||
|
recordIds.value = ids
|
||||||
|
// 加载机构树
|
||||||
|
if (props.orgTreeApi) {
|
||||||
|
// 获取机构树
|
||||||
|
props.orgTreeApi().then((data) => {
|
||||||
|
cardLoading.value = false
|
||||||
|
if (data !== null) {
|
||||||
|
treeData.value = data
|
||||||
|
// 默认展开2级
|
||||||
|
treeData.value.forEach((item) => {
|
||||||
|
// 因为0的顶级
|
||||||
|
if (item.parentId === '0') {
|
||||||
|
defaultExpandedKeys.value.push(item.id)
|
||||||
|
// 取到下级ID
|
||||||
|
if (item.children) {
|
||||||
|
item.children.forEach((items) => {
|
||||||
|
defaultExpandedKeys.value.push(items.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
searchFormState.value.size = pageSize.value
|
||||||
|
loadData()
|
||||||
|
if (props.checkedUserListApi) {
|
||||||
|
if (isEmpty(recordIds.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const param = {
|
||||||
|
idList: recordIds.value
|
||||||
|
}
|
||||||
|
selectedTableListLoading.value = true
|
||||||
|
props
|
||||||
|
.checkedUserListApi(param)
|
||||||
|
.then((data) => {
|
||||||
|
selectedData.value = data
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
selectedTableListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 查询主表格数据
|
||||||
|
const loadData = () => {
|
||||||
|
pageLoading.value = true
|
||||||
|
props
|
||||||
|
.userPageApi(searchFormState.value)
|
||||||
|
.then((data) => {
|
||||||
|
current.value = data.current
|
||||||
|
// pageSize.value = data.size
|
||||||
|
total.value = data.total
|
||||||
|
// 重置、赋值
|
||||||
|
tableData.value = []
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = data.records
|
||||||
|
if (data.records) {
|
||||||
|
tableRecordNum.value = data.records.length
|
||||||
|
} else {
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pageLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// pageSize改变回调分页事件
|
||||||
|
const paginationChange = (page, pageSize) => {
|
||||||
|
searchFormState.value.current = page
|
||||||
|
searchFormState.value.size = pageSize
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const judge = () => {
|
||||||
|
if (radioModel && selectedData.value.length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 添加记录
|
||||||
|
const addRecord = (record) => {
|
||||||
|
if (!judge()) {
|
||||||
|
message.warning('只可选择一条')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||||
|
if (selectedRecord.length === 0) {
|
||||||
|
selectedData.value.push(record)
|
||||||
|
} else {
|
||||||
|
message.warning('该记录已存在')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加全部
|
||||||
|
const addAllPageRecord = () => {
|
||||||
|
let newArray = selectedData.value.concat(tableData.value)
|
||||||
|
let list = []
|
||||||
|
for (let item1 of newArray) {
|
||||||
|
let flag = true
|
||||||
|
for (let item2 of list) {
|
||||||
|
if (item1.id === item2.id) {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
list.push(item1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedData.value = list
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delRecord = (record) => {
|
||||||
|
remove(selectedData.value, (item) => item.id === record.id)
|
||||||
|
}
|
||||||
|
// 删减记录
|
||||||
|
const delAllRecord = () => {
|
||||||
|
selectedData.value = []
|
||||||
|
}
|
||||||
|
// 点击树查询
|
||||||
|
const treeSelect = (selectedKeys) => {
|
||||||
|
searchFormState.value.current = 0
|
||||||
|
if (selectedKeys.length > 0) {
|
||||||
|
searchFormState.value.orgId = selectedKeys.toString()
|
||||||
|
} else {
|
||||||
|
delete searchFormState.value.orgId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
const handleOk = () => {
|
||||||
|
const value = []
|
||||||
|
selectedData.value.forEach((item) => {
|
||||||
|
const obj = {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
account: item.account
|
||||||
|
}
|
||||||
|
value.push(obj)
|
||||||
|
})
|
||||||
|
// 判断是否做数据的转换为工作流需要的
|
||||||
|
if (dataIsConverterFlw) {
|
||||||
|
emit('onBack', outDataConverter(value))
|
||||||
|
} else {
|
||||||
|
emit('onBack', value)
|
||||||
|
}
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
delete searchFormState.value.searchKey
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
searchFormState.value = {}
|
||||||
|
tableRecordNum.value = 0
|
||||||
|
tableData.value = []
|
||||||
|
current.value = 0
|
||||||
|
pageSize.value = 20
|
||||||
|
total.value = 0
|
||||||
|
selectedData.value = []
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据进入后转换
|
||||||
|
const goDataConverter = (data) => {
|
||||||
|
const resultData = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
const values = data[0].value.split(',')
|
||||||
|
if (JSON.stringify(values) !== '[""]') {
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
resultData.push(values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 数据出口转换器
|
||||||
|
const outDataConverter = (data) => {
|
||||||
|
const obj = {}
|
||||||
|
let label = ''
|
||||||
|
let value = ''
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data.length === i + 1) {
|
||||||
|
label = label + data[i].name
|
||||||
|
value = value + data[i].id
|
||||||
|
} else {
|
||||||
|
label = label + data[i].name + ','
|
||||||
|
value = value + data[i].id + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.key = 'USER'
|
||||||
|
obj.label = label
|
||||||
|
obj.value = value
|
||||||
|
obj.extJson = ''
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
showUserPlusModal
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.selectorTreeDiv {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.cardTag {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.primarySele {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.user-table {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
47
src/components/XnFormContainer/index.vue
Normal file
47
src/components/XnFormContainer/index.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-if="isModal" :visible="visible" @cancel="cancel" v-bind="$attrs">
|
||||||
|
<template v-for="slotKey in slotKeys" #[slotKey]>
|
||||||
|
<slot :name="slotKey" />
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<el-drawer v-else :visible="visible" v-bind="$attrs" :footer-style="{ textAlign: 'right' }">
|
||||||
|
<template v-for="slotKey in slotKeys" #[slotKey]>
|
||||||
|
<slot :name="slotKey" />
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
import { globalStore } from '@/store'
|
||||||
|
|
||||||
|
const FormContainerTypeEnum = {
|
||||||
|
DRAWER: 'drawer',
|
||||||
|
MODAL: 'modal'
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'XnFormContainer',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(globalStore, ['formStyle']),
|
||||||
|
slotKeys() {
|
||||||
|
return Object.keys(this.$slots)
|
||||||
|
},
|
||||||
|
isModal() {
|
||||||
|
return FormContainerTypeEnum.MODAL === this.formStyle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
57
src/components/XnWorkflow/chart/cNodeWrap.vue
Normal file
57
src/components/XnWorkflow/chart/cNodeWrap.vue
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<start-events v-if="childNode.type === 'startEvent'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||||
|
<start-tasks v-if="childNode.type === 'startTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||||
|
<user-tasks v-if="childNode.type === 'userTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||||
|
<service-tasks v-if="childNode.type === 'serviceTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||||
|
<exclusive-gateways v-if="childNode.type === 'exclusiveGateway'" v-model="childNode" :currentActivityId="currentActivityId">
|
||||||
|
<template #default="slot">
|
||||||
|
<c-node-wrap v-if="slot.node" v-model="slot.node.childNode" :currentActivityId="currentActivityId" />
|
||||||
|
</template>
|
||||||
|
</exclusive-gateways>
|
||||||
|
<parallel-gateways v-if="childNode.type === 'parallelGateway'" v-model="childNode" :currentActivityId="currentActivityId">
|
||||||
|
<template #default="slot">
|
||||||
|
<c-node-wrap v-if="slot.node" v-model="slot.node.childNode" :currentActivityId="currentActivityId" />
|
||||||
|
</template>
|
||||||
|
</parallel-gateways>
|
||||||
|
<c-node-wrap v-if="childNode.childNode" v-model="childNode.childNode" :currentActivityId="currentActivityId" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import startEvents from './nodes/cStartEvent.vue'
|
||||||
|
import startTasks from './nodes/cStartTask.vue'
|
||||||
|
import userTasks from './nodes/cUserTask.vue'
|
||||||
|
import exclusiveGateways from './nodes/cExclusiveGateway.vue'
|
||||||
|
import parallelGateways from './nodes/cParallelGateway.vue'
|
||||||
|
import serviceTasks from './nodes/cServiceTask.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
startEvents,
|
||||||
|
startTasks,
|
||||||
|
userTasks,
|
||||||
|
exclusiveGateways,
|
||||||
|
parallelGateways,
|
||||||
|
serviceTasks
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.childNode = val
|
||||||
|
},
|
||||||
|
childNode(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
80
src/components/XnWorkflow/chart/index.vue
Normal file
80
src/components/XnWorkflow/chart/index.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workflow-design">
|
||||||
|
<div class="workflow-design-btns">
|
||||||
|
<a-space>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>放大</template>
|
||||||
|
<a-button @click="handleZoom(true)" :disabled="zoom > 2">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>缩小</template>
|
||||||
|
<a-button @click="handleZoom(false)" :disabled="zoom < 1">
|
||||||
|
<template #icon><minus-outlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div id="div1">
|
||||||
|
<div class="box-scale" :style="style">
|
||||||
|
<node-wrap-chart v-if="childNode" v-model="childNode.childNode" :currentActivityId="currentActivityId" />
|
||||||
|
<div class="end-node">
|
||||||
|
<div class="end-node-circle"></div>
|
||||||
|
<div class="end-node-text">流程结束</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import nodeWrapChart from '@/components/XnWorkflow/chart/cNodeWrap.vue'
|
||||||
|
import FullscreenPreviewHelper from './zoom_helper'
|
||||||
|
|
||||||
|
const FullScreenRightSpace = 20
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
nodeWrapChart
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: this.modelValue,
|
||||||
|
style: {},
|
||||||
|
zoom: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.childNode = val
|
||||||
|
},
|
||||||
|
childNode(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleGetStyle(zoom) {
|
||||||
|
return FullscreenPreviewHelper.getZoomStyles(zoom, 1, 0, FullScreenRightSpace)
|
||||||
|
},
|
||||||
|
handleZoom(zoomIn) {
|
||||||
|
const zoom = this.zoom
|
||||||
|
const zoomData = FullscreenPreviewHelper.getZoomData(zoomIn, zoom)
|
||||||
|
const style = this.handleGetStyle(zoomData.zoom)
|
||||||
|
this.style = style
|
||||||
|
this.zoom = zoomData.zoom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import '../flowIndex.less';
|
||||||
|
.workflow-design-btns {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
5
src/components/XnWorkflow/chart/nodes/cAddNode.vue
Normal file
5
src/components/XnWorkflow/chart/nodes/cAddNode.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div class="add-node-btn-box">
|
||||||
|
<div class="add-node-btn"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
141
src/components/XnWorkflow/chart/nodes/cExclusiveGateway.vue
Normal file
141
src/components/XnWorkflow/chart/nodes/cExclusiveGateway.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<div class="branch-wrap">
|
||||||
|
<div class="branch-box-wrap">
|
||||||
|
<div class="branch-box" style="margin-top: 0px">
|
||||||
|
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||||
|
<div class="condition-node">
|
||||||
|
<div class="condition-node-box">
|
||||||
|
<div class="auto-judge">
|
||||||
|
<div class="title">
|
||||||
|
<span class="node-title">{{ item.title }}</span>
|
||||||
|
<span class="priority-title">优先级{{ item.properties.configInfo.priorityLevel }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode, index)">{{ toText(childNode, index) }}</span>
|
||||||
|
<span v-else class="placeholder">请设置条件</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-nodes v-model="item.childNode" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot v-if="item.childNode" :node="item"></slot>
|
||||||
|
<div v-if="index == 0" class="top-left-cover-line"></div>
|
||||||
|
<div v-if="index == 0" class="bottom-left-cover-line"></div>
|
||||||
|
<div v-if="index == childNode.conditionNodeList.length - 1" class="top-right-cover-line"></div>
|
||||||
|
<div v-if="index == childNode.conditionNodeList.length - 1" class="bottom-right-cover-line"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-nodes v-model="childNode.childNode" :parent-data="childNode" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNodes from './cAddNode.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNodes
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
index: 0,
|
||||||
|
operatorList: [
|
||||||
|
{
|
||||||
|
label: '等于',
|
||||||
|
value: '=='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不等于',
|
||||||
|
value: '!='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '大于',
|
||||||
|
value: '>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '大于等于',
|
||||||
|
value: '>='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '小于',
|
||||||
|
value: '<'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '小于等于',
|
||||||
|
value: '<='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '包含',
|
||||||
|
value: 'include'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不包含',
|
||||||
|
value: 'notInclude'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toText(childNode, index) {
|
||||||
|
const conditionList = childNode.conditionNodeList[index].properties.conditionInfo
|
||||||
|
const priorityLevel = childNode.conditionNodeList[index].properties.configInfo.priorityLevel
|
||||||
|
const len = this.childNode.conditionNodeList.length
|
||||||
|
const priorityLevelMax = this.childNode.conditionNodeList[len - 1].properties.configInfo.priorityLevel
|
||||||
|
|
||||||
|
if (JSON.stringify(conditionList) !== undefined && conditionList.length > 0) {
|
||||||
|
let text = ''
|
||||||
|
for (let i = 0; i < conditionList.length; i++) {
|
||||||
|
for (let j = 0; j < conditionList[i].length; j++) {
|
||||||
|
if (j + 1 !== conditionList[i].length) {
|
||||||
|
text =
|
||||||
|
text +
|
||||||
|
conditionList[i][j].label +
|
||||||
|
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||||
|
conditionList[i][j].value +
|
||||||
|
' 且 '
|
||||||
|
} else {
|
||||||
|
text =
|
||||||
|
text +
|
||||||
|
conditionList[i][j].label +
|
||||||
|
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||||
|
conditionList[i][j].value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i + 1 !== conditionList.length) {
|
||||||
|
text = text + ' 或 '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
} else if (conditionList.length === 0 && priorityLevel < priorityLevelMax) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return '其他条件进入此流程'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 通过value 获取界面显示的label汉字
|
||||||
|
getOperatorLabel(value) {
|
||||||
|
return this.operatorList.find((item) => item.value === value).label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped type="less">
|
||||||
|
.workflow-design .condition-node {
|
||||||
|
min-height: 200px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
src/components/XnWorkflow/chart/nodes/cParallelGateway.vue
Normal file
50
src/components/XnWorkflow/chart/nodes/cParallelGateway.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="branch-wrap">
|
||||||
|
<div class="branch-box-wrap">
|
||||||
|
<div class="branch-box">
|
||||||
|
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||||
|
<div class="condition-node">
|
||||||
|
<div class="condition-node-box">
|
||||||
|
<user-tasks v-model="childNode.conditionNodeList[index]" :currentActivityId="currentActivityId" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot v-if="item.childNode" :node="item"></slot>
|
||||||
|
<div v-if="index == 0" class="top-left-cover-line"></div>
|
||||||
|
<div v-if="index == 0" class="bottom-left-cover-line"></div>
|
||||||
|
<div v-if="index == childNode.conditionNodeList.length - 1" class="top-right-cover-line"></div>
|
||||||
|
<div v-if="index == childNode.conditionNodeList.length - 1" class="bottom-right-cover-line"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-nodes v-model="childNode.childNode" :parent-data="childNode" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNodes from './cAddNode.vue'
|
||||||
|
import userTasks from './cUserTask.vue'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNodes,
|
||||||
|
userTasks
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
80
src/components/XnWorkflow/chart/nodes/cServiceTask.vue
Normal file
80
src/components/XnWorkflow/chart/nodes/cServiceTask.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<a-tooltip placement="left">
|
||||||
|
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||||
|
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||||
|
{{ comment.userName }}于{{ comment.approveTime }}
|
||||||
|
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||||
|
>,意见:{{ comment.comment }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :class="childNode.properties && childNode.properties.commentList.length > 0 ? 'node-state-label' : ''">
|
||||||
|
<div class="node-wrap-box">
|
||||||
|
<div class="title" style="background: #3296fa">
|
||||||
|
<send-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||||
|
<span v-else class="placeholder">未选择人员</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<add-nodes v-model="childNode.childNode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNodes from './cAddNode.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNodes
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toText(childNode) {
|
||||||
|
if (JSON.stringify(childNode) !== '{}') {
|
||||||
|
const participateInfo = childNode.properties.participateInfo
|
||||||
|
if (participateInfo.length > 0) {
|
||||||
|
let resultArray = []
|
||||||
|
if (participateInfo[0].label.indexOf(',') !== -1) {
|
||||||
|
resultArray = participateInfo[0].label.split(',')
|
||||||
|
} else {
|
||||||
|
resultArray.push(participateInfo[0].label)
|
||||||
|
}
|
||||||
|
return resultArray.toString()
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.node-state-label {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid rgb(24, 144, 255);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
24
src/components/XnWorkflow/chart/nodes/cStartEvent.vue
Normal file
24
src/components/XnWorkflow/chart/nodes/cStartEvent.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
76
src/components/XnWorkflow/chart/nodes/cStartTask.vue
Normal file
76
src/components/XnWorkflow/chart/nodes/cStartTask.vue
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<a-tooltip placement="left">
|
||||||
|
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||||
|
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||||
|
{{ comment.userName }}于{{ comment.approveTime }}
|
||||||
|
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||||
|
>,意见:{{ comment.comment }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :class="getClassName()">
|
||||||
|
<div class="node-wrap-box start-node">
|
||||||
|
<div class="title" style="background: #576a95">
|
||||||
|
<user-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span>{{ toText(childNode) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<add-nodes v-model="childNode.childNode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNodes from './cAddNode.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNodes
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toText() {
|
||||||
|
return '系统自动配置参与人'
|
||||||
|
},
|
||||||
|
getClassName() {
|
||||||
|
if (this.childNode.id === this.currentActivityId) {
|
||||||
|
return 'node-state-label-activity'
|
||||||
|
} else {
|
||||||
|
return this.childNode.properties && this.childNode.properties.commentList.length > 0 ? 'node-state-label' : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.node-state-label {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid rgb(24, 144, 255);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-activity {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px dashed #00e97c;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
147
src/components/XnWorkflow/chart/nodes/cUserTask.vue
Normal file
147
src/components/XnWorkflow/chart/nodes/cUserTask.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<a-tooltip placement="left">
|
||||||
|
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||||
|
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||||
|
{{ comment.userName }}于{{ comment.approveTime }}
|
||||||
|
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||||
|
>,意见:{{ comment.comment }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :class="getClassName()">
|
||||||
|
<div class="node-wrap-box">
|
||||||
|
<div class="title" style="background: #ff943e">
|
||||||
|
<user-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||||
|
<span v-else class="placeholder">未选择审批人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<add-nodes v-model="childNode.childNode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNodes from './cAddNode.vue'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNodes
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
currentActivityId: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toText(childNode) {
|
||||||
|
if (JSON.stringify(childNode) !== '{}') {
|
||||||
|
const strArray = this.toTag(childNode.properties.participateInfo[0])
|
||||||
|
if (strArray.length > 0) {
|
||||||
|
let value = ''
|
||||||
|
// eslint-disable-next-line no-plusplus
|
||||||
|
for (let i = 0; i < strArray.length; i++) {
|
||||||
|
if (strArray.length === i + 1) {
|
||||||
|
value = value + strArray[i]
|
||||||
|
} else {
|
||||||
|
value = value + strArray[i] + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toTag(participateInfo) {
|
||||||
|
// eslint-disable-next-line no-undefined
|
||||||
|
if (participateInfo === undefined) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (participateInfo.label === '') {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
let resultArray = []
|
||||||
|
if (participateInfo.label.indexOf(',') !== -1) {
|
||||||
|
resultArray = participateInfo.label.split(',')
|
||||||
|
} else {
|
||||||
|
resultArray.push(participateInfo.label)
|
||||||
|
}
|
||||||
|
return resultArray
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getClassName() {
|
||||||
|
if (this.childNode.properties && this.childNode.properties.commentList.length > 0) {
|
||||||
|
if (this.childNode.properties.commentList.length === 1) {
|
||||||
|
if (this.childNode.properties.commentList[0].operateType === 'PASS') {
|
||||||
|
return 'node-state-label-pass'
|
||||||
|
}
|
||||||
|
if (this.childNode.properties.commentList[0].operateType === 'REJECT') {
|
||||||
|
return 'node-state-label-reject'
|
||||||
|
}
|
||||||
|
if (this.childNode.properties.commentList[0].operateType === 'JUMP') {
|
||||||
|
return 'node-state-label-jump'
|
||||||
|
}
|
||||||
|
if (this.childNode.properties.commentList[0].operateType === 'BACK') {
|
||||||
|
return 'node-state-label-back'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'node-state-label'
|
||||||
|
} else {
|
||||||
|
if (this.childNode.id === this.currentActivityId) {
|
||||||
|
return 'node-state-label-activity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.node-state-label {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid #1890ff;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-pass {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid #52c41a;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-reject {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid #ff4d4f;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-jump {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid #bfbfbf;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-back {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px solid #bfbfbf;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.node-state-label-activity {
|
||||||
|
padding: 1px;
|
||||||
|
border: 3px dashed #00e97c;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
39
src/components/XnWorkflow/chart/zoom_helper.js
Normal file
39
src/components/XnWorkflow/chart/zoom_helper.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const defaultZoomMargin = 360
|
||||||
|
const perZoom = 0.25 // 每次放大缩小0.25倍
|
||||||
|
|
||||||
|
const ZoomHelper = {
|
||||||
|
// 处理全屏放大的样式
|
||||||
|
getZoomStyles(zoom, MinZoom = 1, ZoomMargin = defaultZoomMargin, rightSpace = 0) {
|
||||||
|
const width = document.querySelector('.workflow-design').clientWidth
|
||||||
|
let style = {}
|
||||||
|
// 兼容Firefox浏览器的放大缩小
|
||||||
|
if (zoom !== MinZoom) {
|
||||||
|
style = {
|
||||||
|
transform: `scale(${zoom})`,
|
||||||
|
'transform-origin': '0 0',
|
||||||
|
width: (width - ZoomMargin - rightSpace) / zoom + 'px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
},
|
||||||
|
// 获取最大的放大倍数
|
||||||
|
getMaxZoom() {
|
||||||
|
const width = window.innerWidth
|
||||||
|
const mediumWidth = 1600
|
||||||
|
const smallScreenScale = 2.5 // 小屏幕下附件放大3倍会有样式问题, 所以取2.5
|
||||||
|
const bigScreenScale = 3 // 大于1600的最大倍数为3
|
||||||
|
const maxZoom = width > mediumWidth ? bigScreenScale : smallScreenScale
|
||||||
|
return maxZoom
|
||||||
|
},
|
||||||
|
// 获取点击放大缩小之后生成的最终的放大倍数和样式
|
||||||
|
getZoomData(zoomIn, zoom) {
|
||||||
|
const zoomResult = zoomIn ? zoom + perZoom : zoom - perZoom // 放大倍数加一次或者减少一次
|
||||||
|
const zoomData = {
|
||||||
|
style: this.getZoomStyles(zoomResult),
|
||||||
|
zoom: zoomResult
|
||||||
|
}
|
||||||
|
return zoomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ZoomHelper
|
||||||
9
src/components/XnWorkflow/color.text
Normal file
9
src/components/XnWorkflow/color.text
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
审批中 蓝色 #1890FF
|
||||||
|
已挂起 黄色 #FCC02E
|
||||||
|
已完成 绿色 #52C41A
|
||||||
|
已终止 黄色 #FF5A5A
|
||||||
|
已撤回 灰色 #BFBFBF
|
||||||
|
已拒绝 红色 #FF4D4F
|
||||||
|
|
||||||
|
同意 绿色 #52C41A
|
||||||
|
|
||||||
3
src/components/XnWorkflow/customForm/404.vue
Normal file
3
src/components/XnWorkflow/customForm/404.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<a-result status="404" title="未找到表单" sub-title="对不起,该节点配置的自定义表单不存在,请联系管理员。" />
|
||||||
|
</template>
|
||||||
28
src/components/XnWorkflow/customForm/index.js
Normal file
28
src/components/XnWorkflow/customForm/index.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 自定义表单导入
|
||||||
|
*
|
||||||
|
* @author yubaoshan
|
||||||
|
* @date 2023-05-11 00:12:22
|
||||||
|
*/
|
||||||
|
const modules = import.meta.glob('/src/views/flw/customform/**/**.vue')
|
||||||
|
const notFound = () => import(/* @vite-ignore */ `/src/components/XnWorkflow/customForm/404.vue`)
|
||||||
|
|
||||||
|
// 直接渲染组件
|
||||||
|
export const loadComponent = (component) => {
|
||||||
|
if (component) {
|
||||||
|
const link = modules[`/src/views/flw/customform/${component}.vue`]
|
||||||
|
return markRaw(defineAsyncComponent(link ? link : notFound))
|
||||||
|
} else {
|
||||||
|
return markRaw(defineAsyncComponent(notFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给出判断,如果使用的地方取到是404,那么他的下一步就不走了
|
||||||
|
export const verdictComponent = (component) => {
|
||||||
|
if (component) {
|
||||||
|
const link = modules[`/src/views/flw/customform/${component}.vue`]
|
||||||
|
return !!link
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
425
src/components/XnWorkflow/flowIndex.less
Normal file
425
src/components/XnWorkflow/flowIndex.less
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
.workflow-design {
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.workflow-design .box-scale {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 55px;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
min-width: min-content;
|
||||||
|
}
|
||||||
|
.nodeLegal {
|
||||||
|
border: 2px solid red;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
.workflow-design {
|
||||||
|
.node-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0px 50px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.node-wrap-box {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: 220px;
|
||||||
|
min-height: 72px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--node-wrap-box-color);
|
||||||
|
border-radius: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
.node-wrap-box::before {
|
||||||
|
background: var(--auto-judge-before-color);
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -12px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 0px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 8px 6px 4px;
|
||||||
|
border-color: #cacaca transparent transparent;
|
||||||
|
}
|
||||||
|
.node-wrap-box.start-node:before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
.node-wrap-box .title {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #fff;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 30px;
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.node-wrap-box .title .icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.node-wrap-box .title .close {
|
||||||
|
font-size: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
right: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.node-wrap-box .title .success {
|
||||||
|
font-size: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
right: -25px;
|
||||||
|
display: block;
|
||||||
|
color: #00bb00;
|
||||||
|
}
|
||||||
|
.node-wrap-box .content {
|
||||||
|
position: relative;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
.node-wrap-box .content .placeholder {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.node-wrap-box:hover .close {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.add-node-btn-box {
|
||||||
|
width: 240px;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.add-node-btn-box:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
z-index: -1;
|
||||||
|
margin: auto;
|
||||||
|
width: 2px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgb(202, 202, 202);
|
||||||
|
}
|
||||||
|
.add-node-btn {
|
||||||
|
user-select: none;
|
||||||
|
width: 240px;
|
||||||
|
padding: 20px 0px 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.add-node-btn span {
|
||||||
|
}
|
||||||
|
.add-branch {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0px 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: -16px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
transform-origin: center center;
|
||||||
|
z-index: 1;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.branch-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.branch-box-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 270px;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.col-box {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
background: var(--component-background);
|
||||||
|
}
|
||||||
|
// 分支 上面横线
|
||||||
|
.branch-box {
|
||||||
|
display: flex;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 180px;
|
||||||
|
height: auto;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
|
border-top: 2px solid #ccc;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.branch-box .col-box::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
z-index: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 2px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgb(202, 202, 202);
|
||||||
|
}
|
||||||
|
.condition-node {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 220px;
|
||||||
|
}
|
||||||
|
.condition-node-box {
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-right: 50px;
|
||||||
|
padding-left: 50px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.auto-judge {
|
||||||
|
position: relative;
|
||||||
|
width: 220px;
|
||||||
|
min-height: 72px;
|
||||||
|
background: var(--node-wrap-box-color);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 15px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
// 箭头框框
|
||||||
|
.auto-judge::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -12px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 0px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 8px 6px 4px;
|
||||||
|
border-color: #cacaca transparent transparent;
|
||||||
|
background: var(--auto-judge-before-color);
|
||||||
|
}
|
||||||
|
.auto-judge .title {
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
.auto-judge .title .node-title {
|
||||||
|
color: #15bc83;
|
||||||
|
}
|
||||||
|
.auto-judge .title .close {
|
||||||
|
font-size: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
color: #999;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.auto-judge .title .success {
|
||||||
|
font-size: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
color: #999;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.auto-judge .title .priority-title {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.auto-judge .content {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
.auto-judge .content .placeholder {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.auto-judge:hover {
|
||||||
|
.close {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.priority-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-left-cover-line,
|
||||||
|
.top-right-cover-line {
|
||||||
|
position: absolute;
|
||||||
|
height: 3px;
|
||||||
|
width: 50%;
|
||||||
|
background-color: var(--component-background);
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
.bottom-left-cover-line,
|
||||||
|
.bottom-right-cover-line {
|
||||||
|
position: absolute;
|
||||||
|
height: 3px;
|
||||||
|
width: 50%;
|
||||||
|
background-color: var(--component-background);
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
.top-left-cover-line {
|
||||||
|
left: -1px;
|
||||||
|
}
|
||||||
|
.top-right-cover-line {
|
||||||
|
right: -1px;
|
||||||
|
}
|
||||||
|
.bottom-left-cover-line {
|
||||||
|
left: -1px;
|
||||||
|
}
|
||||||
|
.bottom-right-cover-line {
|
||||||
|
right: -1px;
|
||||||
|
}
|
||||||
|
.end-node {
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(25, 31, 37, 0.4);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
// 结束的小点点
|
||||||
|
.end-node-circle {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #dbdcdc;
|
||||||
|
}
|
||||||
|
.end-node-text {
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.auto-judge:hover {
|
||||||
|
.sort-left {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.sort-right {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.auto-judge .sort-left {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
left: 0;
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.auto-judge .sort-right {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
right: 0;
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.auto-judge .sort-left:hover,
|
||||||
|
.auto-judge .sort-right:hover {
|
||||||
|
background: var(--auto-judge-before-color);
|
||||||
|
}
|
||||||
|
.auto-judge:after {
|
||||||
|
pointer-events: none;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
.auto-judge:hover:after {
|
||||||
|
border: 1px solid #3296fa;
|
||||||
|
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
|
||||||
|
}
|
||||||
|
.node-wrap-box:after {
|
||||||
|
pointer-events: none;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
.node-wrap-box:hover:after {
|
||||||
|
border: 1px solid #3296fa;
|
||||||
|
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tags-list {
|
||||||
|
margin-top: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.add-node-popover-body {
|
||||||
|
height: 81px;
|
||||||
|
}
|
||||||
|
.add-node-popover-body li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.add-node-popover-body li i {
|
||||||
|
border: 1px solid var(--el-border-color-light);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 38px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.add-node-popover-body li i:hover {
|
||||||
|
border: 1px solid #3296fa;
|
||||||
|
background: #3296fa;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.add-node-popover-body li p {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.node-wrap-drawer__title {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
.node-wrap-drawer__title label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.node-wrap-drawer__title label:hover {
|
||||||
|
border-bottom: 1px dashed #409eff;
|
||||||
|
}
|
||||||
|
.node-wrap-drawer__title .node-wrap-drawer-title-edit {
|
||||||
|
color: #409eff;
|
||||||
|
margin-left: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
139
src/components/XnWorkflow/index.vue
Normal file
139
src/components/XnWorkflow/index.vue
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<!--
|
||||||
|
* @Descripttion: 仿钉钉流程设计器
|
||||||
|
* @version: 1.2
|
||||||
|
* @Author: sakuya
|
||||||
|
* @Date: 2021年9月14日08:38:35
|
||||||
|
* @LastEditors: yubaoshan
|
||||||
|
* @LastEditTime: 2022年2月9日16:48:49
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="workflow-design">
|
||||||
|
<!-- 配置流程全局属性 -->
|
||||||
|
<div style="float: right; padding-right: 10px">
|
||||||
|
<span v-if="!toDataLegal(childNode)" style="padding-right: 5px">
|
||||||
|
<!-- <exclamation-circle-outlined style="color: red; font-size: 18px" />-->
|
||||||
|
<!-- <Warning style="color: red; font-size: 18px"/>-->
|
||||||
|
</span>
|
||||||
|
<!-- <el-tooltip>-->
|
||||||
|
<!-- <template #title></template>-->
|
||||||
|
<!-- <el-button @click="$refs.process.showDrawer()">-->
|
||||||
|
<!-- <template #icon>-->
|
||||||
|
<!--<!– <setting-outlined />–>-->
|
||||||
|
<!-- <Setting />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </el-button>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="dark"
|
||||||
|
content="配置流程全局属性"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-button :icon='Setting'>
|
||||||
|
全局配置
|
||||||
|
<template #icon>
|
||||||
|
<Setting />
|
||||||
|
</template>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="box-scale">
|
||||||
|
<node-wrap
|
||||||
|
v-if="childNode"
|
||||||
|
v-model="childNode.childNode"
|
||||||
|
:form-field-list-value="childFormFieldListValue"
|
||||||
|
:record-data="childRecordData"
|
||||||
|
:process-config-info="childNode.properties.configInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
<div class="end-node">
|
||||||
|
<div class="end-node-circle"></div>
|
||||||
|
<div class="end-node-text">流程结束</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<process
|
||||||
|
ref="process"
|
||||||
|
v-model="childNode"
|
||||||
|
:form-field-list-value="childFormFieldListValue"
|
||||||
|
:record-data="childRecordData"
|
||||||
|
:sn-template-array="snTemplateArray"
|
||||||
|
:print-template-array="printTemplateArray"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:execution-listener-selector-for-custom-event-array="executionListenerSelectorForCustomEventArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import nodeWrap from './nodeWrap.vue'
|
||||||
|
import { Warning, Setting, Plus } from '@element-plus/icons-vue'
|
||||||
|
import process from './process.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
Plus() {
|
||||||
|
return Plus
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
nodeWrap,
|
||||||
|
Warning,
|
||||||
|
Setting,
|
||||||
|
process
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
snTemplateArray: { type: Array, default: () => [] },
|
||||||
|
printTemplateArray: { type: Array, default: () => [] },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
executionListenerSelectorForCustomEventArray: { type: Array, default: () => [] },
|
||||||
|
listenerType: { type: String, default: () => 'default' },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: this.modelValue,
|
||||||
|
childFormFieldListValue: this.formFieldListValue,
|
||||||
|
childRecordData: this.recordData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.childNode = val
|
||||||
|
},
|
||||||
|
// 监听字段列表传输的相关动静
|
||||||
|
formFieldListValue(val) {
|
||||||
|
this.childFormFieldListValue = val
|
||||||
|
},
|
||||||
|
recordData(val) {
|
||||||
|
this.childRecordData = val
|
||||||
|
},
|
||||||
|
childNode(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toDataLegal(childNode) {
|
||||||
|
if (childNode === undefined) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return childNode.dataLegal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import './flowIndex.less';
|
||||||
|
</style>
|
||||||
131
src/components/XnWorkflow/nodeWrap.vue
Normal file
131
src/components/XnWorkflow/nodeWrap.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<start-event v-if="childNode.type === 'startEvent'" v-model="childNode" />
|
||||||
|
<start-task
|
||||||
|
v-if="childNode.type === 'startTask'"
|
||||||
|
v-model="childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
<user-task
|
||||||
|
v-if="childNode.type === 'userTask'"
|
||||||
|
v-model="childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
<service-task
|
||||||
|
v-if="childNode.type === 'serviceTask'"
|
||||||
|
v-model="childNode"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
:form-field-list-value="formFieldListValue"
|
||||||
|
:record-data="recordData"
|
||||||
|
/>
|
||||||
|
<exclusive-gateway
|
||||||
|
v-if="childNode.type === 'exclusiveGateway'"
|
||||||
|
v-model="childNode"
|
||||||
|
:form-field-list-value="formFieldListValue"
|
||||||
|
:record-data="recordData"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
>
|
||||||
|
<template #default="slot">
|
||||||
|
<node-wrap
|
||||||
|
v-if="slot.node"
|
||||||
|
v-model="slot.node.childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</exclusive-gateway>
|
||||||
|
<parallel-gateway
|
||||||
|
v-if="childNode.type === 'parallelGateway'"
|
||||||
|
v-model="childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
>
|
||||||
|
<template #default="slot">
|
||||||
|
<node-wrap
|
||||||
|
v-if="slot.node"
|
||||||
|
v-model="slot.node.childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</parallel-gateway>
|
||||||
|
<node-wrap
|
||||||
|
v-if="childNode.childNode"
|
||||||
|
v-model="childNode.childNode"
|
||||||
|
:formFieldListValue="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import startEvent from './nodes/startEvent.vue'
|
||||||
|
import startTask from './nodes/startTask.vue'
|
||||||
|
import userTask from './nodes/userTask.vue'
|
||||||
|
import exclusiveGateway from './nodes/exclusiveGateway.vue'
|
||||||
|
import parallelGateway from './nodes/parallelGateway.vue'
|
||||||
|
import serviceTask from './nodes/serviceTask.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
startEvent,
|
||||||
|
startTask,
|
||||||
|
userTask,
|
||||||
|
exclusiveGateway,
|
||||||
|
parallelGateway,
|
||||||
|
serviceTask
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
processConfigInfo: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.childNode = val
|
||||||
|
},
|
||||||
|
childNode(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
152
src/components/XnWorkflow/nodes/addNode.vue
Normal file
152
src/components/XnWorkflow/nodes/addNode.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<div class="add-node-btn-box">
|
||||||
|
<div class="add-node-btn">
|
||||||
|
<a-popover v-model:visible="visible" placement="rightTop" trigger="click" :width="270">
|
||||||
|
<template #content>
|
||||||
|
<div class="add-node-popover-body">
|
||||||
|
<ul style="height: 80px">
|
||||||
|
<li>
|
||||||
|
<a-button shape="circle" size="large" @click="addType('userTask')">
|
||||||
|
<template #icon>
|
||||||
|
<user-outlined style="color: #ff943e; font-size: 18px" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<p>审批节点</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a-button shape="circle" size="large" @click="addType('serviceTask')">
|
||||||
|
<template #icon>
|
||||||
|
<send-outlined style="color: #3296fa; font-size: 18px" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<p>抄送节点</p>
|
||||||
|
</li>
|
||||||
|
<li v-if="addExclusiveGateway">
|
||||||
|
<a-button shape="circle" size="large" @click="addType('exclusiveGateway')">
|
||||||
|
<template #icon>
|
||||||
|
<share-alt-outlined style="color: #15bc83; font-size: 18px" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<p>条件分支</p>
|
||||||
|
</li>
|
||||||
|
<li v-if="addParallelGateway">
|
||||||
|
<a-button shape="circle" size="large" @click="addType('parallelGateway')">
|
||||||
|
<template #icon>
|
||||||
|
<partition-outlined :rotate="180" style="color: #ac28f5; font-size: 18px" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<p>并行分支</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-button type="primary" shape="circle">
|
||||||
|
<!-- @click="addNodeButton" -->
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
const NodeTitleMap = {
|
||||||
|
userTask: '审核人',
|
||||||
|
serviceTask: '抄送人',
|
||||||
|
exclusiveGateway: '条件路由',
|
||||||
|
parallelGateway: '并行路由'
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
parentData: { type: Object, default: () => {} },
|
||||||
|
nodeItem: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
addExclusiveGateway: true,
|
||||||
|
addParallelGateway: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {},
|
||||||
|
methods: {
|
||||||
|
addNodeButton() {
|
||||||
|
// 他的上级是条件分支或并行分支,将其不在添加 // 控制节点下面
|
||||||
|
if (!this.parentData) {
|
||||||
|
this.disabledChildren()
|
||||||
|
} else {
|
||||||
|
if (this.parentData.type === 'exclusiveGateway' || this.parentData.type === 'parallelGateway') {
|
||||||
|
this.addExclusiveGateway = false
|
||||||
|
this.addParallelGateway = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disabledChildren() {
|
||||||
|
// 如果下级是条件分支或并行分支,将其不在添加 // 控制节点上面
|
||||||
|
if (this.modelValue && this.modelValue.type) {
|
||||||
|
if (this.modelValue.type === 'exclusiveGateway' || this.modelValue.type === 'parallelGateway') {
|
||||||
|
this.addExclusiveGateway = false
|
||||||
|
this.addParallelGateway = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 不管其他的,如果是条件分支的项,那么他的下面无法添加条件
|
||||||
|
if (this.nodeItem) {
|
||||||
|
this.addExclusiveGateway = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getBaseCondition(type, title) {
|
||||||
|
const condition = cloneDeep(config.nodeModel.node)
|
||||||
|
condition.id = this.$TOOL.snowyUuid()
|
||||||
|
condition.type = type
|
||||||
|
condition.title = title
|
||||||
|
return condition
|
||||||
|
},
|
||||||
|
addType(type) {
|
||||||
|
const nodeModel = this.getBaseCondition(type, NodeTitleMap[type]) || {}
|
||||||
|
nodeModel.childNode = this.modelValue
|
||||||
|
if (type === 'userTask') {
|
||||||
|
// 创建 configInfo
|
||||||
|
const configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
|
||||||
|
nodeModel.properties.configInfo = configInfo
|
||||||
|
} else if (type === 'exclusiveGateway') {
|
||||||
|
nodeModel.dataLegal = true
|
||||||
|
// 创建分支节点1
|
||||||
|
const condition1 = this.getBaseCondition('sequenceFlow', '条件1')
|
||||||
|
// 创建分支节点1 configInfo
|
||||||
|
const condition1ConfigInfo1 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
|
||||||
|
condition1ConfigInfo1.priorityLevel = 1
|
||||||
|
condition1.properties.configInfo = condition1ConfigInfo1
|
||||||
|
// 创建分支节点2
|
||||||
|
const condition2 = this.getBaseCondition('sequenceFlow', '条件2')
|
||||||
|
// 创建分支节点2 configInfo
|
||||||
|
const condition1ConfigInfo2 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
|
||||||
|
condition1ConfigInfo2.priorityLevel = 2
|
||||||
|
condition2.properties.configInfo = condition1ConfigInfo2
|
||||||
|
// 装进去
|
||||||
|
nodeModel.conditionNodeList.push(condition1)
|
||||||
|
nodeModel.conditionNodeList.push(condition2)
|
||||||
|
} else if (type === 'parallelGateway') {
|
||||||
|
// 创建主节点
|
||||||
|
nodeModel.dataLegal = true
|
||||||
|
// 创建分支节点1
|
||||||
|
const condition1 = this.getBaseCondition('userTask', '审批人1')
|
||||||
|
condition1.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
|
||||||
|
condition1.dataLegal = true
|
||||||
|
// 创建分支节点2
|
||||||
|
const condition2 = this.getBaseCondition('userTask', '审批人2')
|
||||||
|
condition2.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
|
||||||
|
condition2.dataLegal = true
|
||||||
|
// 装进去
|
||||||
|
nodeModel.conditionNodeList.push(condition1)
|
||||||
|
nodeModel.conditionNodeList.push(condition2)
|
||||||
|
}
|
||||||
|
this.visible = false
|
||||||
|
this.$emit('update:modelValue', nodeModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
5
src/components/XnWorkflow/nodes/addNodeNoButton.vue
Normal file
5
src/components/XnWorkflow/nodes/addNodeNoButton.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div class="add-node-btn-box">
|
||||||
|
<div class="add-node-btn"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
41
src/components/XnWorkflow/nodes/common/previewCustomForm.vue
Normal file
41
src/components/XnWorkflow/nodes/common/previewCustomForm.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
title="预览"
|
||||||
|
:width="700"
|
||||||
|
:visible="visible"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:footer-style="{ textAlign: 'right' }"
|
||||||
|
:mask="false"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
@ok="onSubmit"
|
||||||
|
@cancel="onClose"
|
||||||
|
>
|
||||||
|
<component ref="customFormRef" :is="customFormsLayouts" />
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="previewCustomForm">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { loadComponent } from '../../customForm'
|
||||||
|
const visible = ref(false)
|
||||||
|
const confirmLoading = ref(false)
|
||||||
|
const customFormRef = ref()
|
||||||
|
const customFormsLayouts = ref()
|
||||||
|
const onOpen = (url) => {
|
||||||
|
if (url) {
|
||||||
|
visible.value = true
|
||||||
|
customFormsLayouts.value = loadComponent(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onSubmit = () => {
|
||||||
|
customFormRef.value.getData().then((value) => {
|
||||||
|
message.info(JSON.stringify(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
onOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
581
src/components/XnWorkflow/nodes/config/config.js
Normal file
581
src/components/XnWorkflow/nodes/config/config.js
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
export default {
|
||||||
|
// 模型结构,就是每个节点,都有的
|
||||||
|
nodeModel: {
|
||||||
|
// 节点
|
||||||
|
node: {
|
||||||
|
id: '', // 节点id
|
||||||
|
title: '', // 节点名称
|
||||||
|
type: '', // 节点类型
|
||||||
|
dataLegal: false, // 信息是否完整
|
||||||
|
// content:'', // 内容、备注
|
||||||
|
properties: {
|
||||||
|
configInfo: {}, // 除条件路由和分合流节点外均有
|
||||||
|
conditionInfo: [], // 条件信息, 条件节点特有
|
||||||
|
participateInfo: [], // 参与人信息,开始节点、审批节点、抄送节点才有
|
||||||
|
buttonInfo: [], // 按钮信息, 开始节点、审批节点、抄送节点才有
|
||||||
|
fieldInfo: [], // 字段信息, 开始节点、审批节点、抄送节点才有
|
||||||
|
commentList: [], // 流转记录内容
|
||||||
|
formInfo: [], // 自定义表单 发起、审批节点都有
|
||||||
|
executionListenerInfo: [], // 执行监听器信息,所有节点都有
|
||||||
|
taskListenerInfo: [] // 任务监听器信息(审批节点特有)
|
||||||
|
// noticeInfo: [], // 通知信息, 开始节点、审批节点、抄送节点才有
|
||||||
|
},
|
||||||
|
childNode: {}, // 子节点
|
||||||
|
conditionNodeList: [] // 条件子节点
|
||||||
|
},
|
||||||
|
// 通用按钮默认配置,不需要改动,除非自定义开发的时候,再加什么按钮
|
||||||
|
buttonInfo: [
|
||||||
|
{
|
||||||
|
key: 'SAVE',
|
||||||
|
label: '保存',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'save-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SUBMIT',
|
||||||
|
label: '提交',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'check-circle-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'PASS',
|
||||||
|
label: '同意',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'check-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REJECT',
|
||||||
|
label: '拒绝',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'danger',
|
||||||
|
icon: 'close-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'BACK',
|
||||||
|
label: '退回',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'rollback-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'JUMP',
|
||||||
|
label: '跳转',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'ungroup-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ADD_SIGN',
|
||||||
|
label: '加签',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'tags-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'PRINT',
|
||||||
|
label: '打印',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'printer-outlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'TURN',
|
||||||
|
label: '转办',
|
||||||
|
value: 'HIDE',
|
||||||
|
type: 'default',
|
||||||
|
icon: 'user-switch-outlined'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
fieldInfo: [],
|
||||||
|
formInfo: [
|
||||||
|
{
|
||||||
|
key: 'FORM_URL',
|
||||||
|
label: 'PC端表单组件',
|
||||||
|
value: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'MOBILE_FORM_URL',
|
||||||
|
label: '移动端表单组件',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
executionListenerInfo: [],
|
||||||
|
taskListenerInfo: [],
|
||||||
|
noticeInfo: []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 各个节点的configInfo,他们长的都不一样
|
||||||
|
nodeConfigInfo: {
|
||||||
|
// 全局配置
|
||||||
|
processConfigInfo: {
|
||||||
|
// 基础配置
|
||||||
|
processSnTemplateId: undefined, // 流水号模板 id
|
||||||
|
processPrintTemplateId: undefined, // 打印模板 id
|
||||||
|
processTitleTemplate: 'initiator 的 processName - startTime', // 标题模板
|
||||||
|
processAbstractTemplate: '', // 摘要模板
|
||||||
|
processEnableAutoDistinct: false, // 开启自动去重
|
||||||
|
processAutoDistinctType: 'SAMPLE', // 自动去重类型
|
||||||
|
processEnableRevoke: true, // 开启审批撤销
|
||||||
|
processEnableCommentRequired: false, // 开启意见必填
|
||||||
|
// 通知配置
|
||||||
|
processEnableBackNotice: false, // 开启退回通知
|
||||||
|
processEnableTodoNotice: false, // 开启待办通知
|
||||||
|
processEnableCopyNotice: false, // 开启抄送通知
|
||||||
|
processEnableCompleteNotice: false, // 开启完成通知
|
||||||
|
// 通知的方式
|
||||||
|
processBackNoticeChannel: ['MSG'], // 退回通知渠道
|
||||||
|
processTodoNoticeChannel: ['MSG'], // 待办通知渠道
|
||||||
|
processCopyNoticeChannel: ['MSG'], // 抄送通知渠道
|
||||||
|
processCompleteNoticeChannel: ['MSG'], // 完成通知渠道
|
||||||
|
// 通知配置对应的模板
|
||||||
|
processBackNoticeTemplate: '您于 startTime 发起的 processName 被退回', // 退回通知模板
|
||||||
|
processTodoNoticeTemplate: '由 initiator 发起的 processName 需要您审批', // 待办通知模板
|
||||||
|
processCopyNoticeTemplate: '您收到一条由 initiator 发起的 processName 的抄送', // 抄送通知模板
|
||||||
|
processCompleteNoticeTemplate: '您于 startTime 发起的 processName 审批通过', // 完成通知模板
|
||||||
|
// 全局自定义表单
|
||||||
|
processStartTaskFormUrl: '', // 全局PC申请单
|
||||||
|
processStartTaskMobileFormUrl: '', // 全局移动端申请单
|
||||||
|
processUserTaskFormUrl: '', // 全局PC审批单
|
||||||
|
processUserTaskMobileFormUrl: '' // 全局移动端审批单
|
||||||
|
},
|
||||||
|
// 审核节点配置
|
||||||
|
userTaskConfigInfo: {
|
||||||
|
userTaskType: 'ARTIFICIAL', // 任务节点类型
|
||||||
|
userTaskRejectType: 'TO_START', // 审批退回类型
|
||||||
|
userTaskMulApproveType: 'SEQUENTIAL', // 多人审批时类型
|
||||||
|
userTaskEmptyApproveType: 'AUTO_COMPLETE', // 审批人为空时类型
|
||||||
|
userTaskEmptyApproveUser: '', // 审批人为空时转交人id
|
||||||
|
userTaskEmptyApproveUserArray: [] // 审批人为空时转交人包含name,用来前端回显,后端不解析
|
||||||
|
},
|
||||||
|
// 条件中默认的配置
|
||||||
|
conditionConfigInfo: {
|
||||||
|
priorityLevel: 1 // 优先级 默认1
|
||||||
|
},
|
||||||
|
// 抄送节点配置
|
||||||
|
serviceTaseConfigInfo: {}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 按钮相关配置
|
||||||
|
button: {
|
||||||
|
// 发起人节点 默认选中按钮
|
||||||
|
startTaskDefaultButtonkey: ['SAVE', 'SUBMIT'],
|
||||||
|
// 发起人节点 默认不让选的按钮 // 这个节点屏蔽下面配置的
|
||||||
|
startTaskNoCheckedButtonkey: ['PASS', 'PRINT', 'REJECT', 'BACK', 'ADD_SIGN', 'TURN', 'JUMP'],
|
||||||
|
// 审批人节点 默认选中按钮
|
||||||
|
userTaskDefaultButtonkey: ['PASS', 'REJECT'],
|
||||||
|
// 审批节点 默认不让选的
|
||||||
|
userTaskNoCheckedButtonkey: ['SUBMIT', 'SAVE'],
|
||||||
|
// 抄送人节点 默认选中按钮
|
||||||
|
serviceTaskDefaultButtonkey: ['SUBMIT']
|
||||||
|
},
|
||||||
|
// 字段相关配置
|
||||||
|
field: {
|
||||||
|
// 其他节点中字段对象数据模型
|
||||||
|
fieldModel: {
|
||||||
|
key: '',
|
||||||
|
label: '',
|
||||||
|
value: 'WRITE', // 默认
|
||||||
|
required: false, // 必填
|
||||||
|
extJson: '' // 额外扩展,暂无
|
||||||
|
},
|
||||||
|
// 审批节点中字段对象数据模型
|
||||||
|
userTaskFieldModel: {
|
||||||
|
key: '',
|
||||||
|
label: '',
|
||||||
|
value: 'READ', // 默认设为只读
|
||||||
|
required: false, // 必填
|
||||||
|
extJson: '' // 额外扩展,暂无
|
||||||
|
},
|
||||||
|
// 字段列表中的字典
|
||||||
|
fieldRadioList: [
|
||||||
|
{
|
||||||
|
label: '可编辑',
|
||||||
|
value: 'WRITE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '只读',
|
||||||
|
value: 'READ'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '隐藏',
|
||||||
|
value: 'HIDE'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 监听器配置,不同的地方不同的选项
|
||||||
|
listener: {
|
||||||
|
// 全局执行监听可以选择的
|
||||||
|
processExecutionListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'START',
|
||||||
|
label: '开始',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'END',
|
||||||
|
label: '完成',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REJECT',
|
||||||
|
label: '拒绝',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'CLOSE',
|
||||||
|
label: '终止',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REVOKE',
|
||||||
|
label: '撤回',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DELETE',
|
||||||
|
label: '删除',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 条件执行监听可以选择的
|
||||||
|
exclusiveGatewayExecutionListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'TAKE',
|
||||||
|
label: '到达',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 开始节点执行监听可选择的
|
||||||
|
startTaskExecutionListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'START',
|
||||||
|
label: '开始',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'END',
|
||||||
|
label: '结束',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 审批节点执行监听可以选择的
|
||||||
|
userTaskExecutionListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'START',
|
||||||
|
label: '开始',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'END',
|
||||||
|
label: '结束',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 抄送节点执行监听可以选择的
|
||||||
|
serviceTaskExecutionListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'START',
|
||||||
|
label: '开始',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'END',
|
||||||
|
label: '结束',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 审批节点任务监听
|
||||||
|
userTaskTaskListenerInfo: [
|
||||||
|
{
|
||||||
|
key: 'CREATE',
|
||||||
|
label: '创建',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ASSIGNMENT',
|
||||||
|
label: '分配',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'COMPLETE',
|
||||||
|
label: '完成',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DELETE',
|
||||||
|
label: '删除',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'UPDATE',
|
||||||
|
label: '更新',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'TIMEOUT',
|
||||||
|
label: '超时',
|
||||||
|
value: '',
|
||||||
|
extJson: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 通知方式字典
|
||||||
|
noticeInfoList: [
|
||||||
|
{
|
||||||
|
label: '短信',
|
||||||
|
value: 'SMS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '邮件',
|
||||||
|
value: 'EMAIL'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '站内信',
|
||||||
|
value: 'MSG'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 模板默认自带字段
|
||||||
|
templateDefaultFields: [
|
||||||
|
{
|
||||||
|
label: '发起人',
|
||||||
|
value: 'initiator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '流程名称',
|
||||||
|
value: 'processName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '发起时间',
|
||||||
|
value: 'startTime'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单字段',
|
||||||
|
value: 'disabled' // 这里表示在组件显示的时候就截至了,是下一梭子数组的标题哦
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 审批节点
|
||||||
|
userTaskConfig: {
|
||||||
|
// 审批人员类型
|
||||||
|
userSelectionTypeList: [
|
||||||
|
{
|
||||||
|
label: '机构',
|
||||||
|
value: 'ORG'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '角色',
|
||||||
|
value: 'ROLE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '职位',
|
||||||
|
value: 'POSITION'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '部门主管',
|
||||||
|
value: 'ORG_LEADER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '上级主管',
|
||||||
|
value: 'SUPERVISOR'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单内的人',
|
||||||
|
value: 'FORM_USER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单内的人上级主管',
|
||||||
|
value: 'FORM_USER_SUPERVISOR'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '连续多级主管',
|
||||||
|
value: 'MUL_LEVEL_SUPERVISOR'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单内的人连续多级主管',
|
||||||
|
value: 'FORM_USER_MUL_LEVEL_SUPERVISOR'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '用户',
|
||||||
|
value: 'USER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '发起人',
|
||||||
|
value: 'INITIATOR'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 任务节点类型
|
||||||
|
userTaskTypeList: [
|
||||||
|
{
|
||||||
|
label: '人工审批',
|
||||||
|
value: 'ARTIFICIAL'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '自动通过',
|
||||||
|
value: 'COMPLETE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '自动拒绝',
|
||||||
|
value: 'REJECT'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 审批退回类型
|
||||||
|
userTaskRejectTypeList: [
|
||||||
|
{
|
||||||
|
label: '开始节点',
|
||||||
|
value: 'TO_START'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '自选节点',
|
||||||
|
value: 'USER_SELECT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '自动结束',
|
||||||
|
value: 'AUTO_END'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 多人审批时类型
|
||||||
|
userTaskMulApproveTypeList: [
|
||||||
|
{
|
||||||
|
label: '依次审批',
|
||||||
|
value: 'SEQUENTIAL'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '会签(须所有审批人同意)',
|
||||||
|
value: 'COUNTERSIGN'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '或签(一名审批人同意或拒绝即可)',
|
||||||
|
value: 'ORSIGN'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 审批人为空时类型
|
||||||
|
userTaskEmptyApproveTypeList: [
|
||||||
|
{
|
||||||
|
label: '自动通过',
|
||||||
|
value: 'AUTO_COMPLETE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '自动转交给某个人',
|
||||||
|
value: 'AUTO_TURN'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 主管层级
|
||||||
|
levelSupervisorList: [
|
||||||
|
{
|
||||||
|
label: '最高层主管',
|
||||||
|
value: '-1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '直接主管',
|
||||||
|
value: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第2级主管',
|
||||||
|
value: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第3级主管',
|
||||||
|
value: '3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第4级主管',
|
||||||
|
value: '4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第5级主管',
|
||||||
|
value: '5'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第6级主管',
|
||||||
|
value: '6'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第7级主管',
|
||||||
|
value: '7'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第8级主管',
|
||||||
|
value: '8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第9级主管',
|
||||||
|
value: '9'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第10级主管',
|
||||||
|
value: '10'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 条件节点
|
||||||
|
exclusiveGatewayConfig: {
|
||||||
|
operatorList: [
|
||||||
|
{
|
||||||
|
label: '等于',
|
||||||
|
value: '=='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不等于',
|
||||||
|
value: '!='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '大于',
|
||||||
|
value: '>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '大于等于',
|
||||||
|
value: '>='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '小于',
|
||||||
|
value: '<'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '小于等于',
|
||||||
|
value: '<='
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '包含',
|
||||||
|
value: 'include'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不包含',
|
||||||
|
value: 'notInclude'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 抄送节点
|
||||||
|
serviceTaskConfig: {
|
||||||
|
// 抄送人员类型
|
||||||
|
userSelectionTypeList: [
|
||||||
|
{
|
||||||
|
label: '用户',
|
||||||
|
value: 'USER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单内的人',
|
||||||
|
value: 'FORM_USER'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
487
src/components/XnWorkflow/nodes/exclusiveGateway.vue
Normal file
487
src/components/XnWorkflow/nodes/exclusiveGateway.vue
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
<template>
|
||||||
|
<div class="branch-wrap">
|
||||||
|
<div class="branch-box-wrap">
|
||||||
|
<div class="branch-box">
|
||||||
|
<a-button class="add-branch" type="primary" shape="round" @click="addTerm"> 添加条件 </a-button>
|
||||||
|
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||||
|
<div class="condition-node">
|
||||||
|
<div class="condition-node-box">
|
||||||
|
<div class="auto-judge" @click="show(index)">
|
||||||
|
<div v-if="index != 0" class="sort-left" @click.stop="arrTransfer(index, -1)">
|
||||||
|
<left-outlined />
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<span class="node-title">{{ item.title }}</span>
|
||||||
|
<span class="priority-title">优先级{{ item.properties.configInfo.priorityLevel }}</span>
|
||||||
|
<close-outlined class="close" @click.stop="delTerm(index)" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode, index)">{{ toText(childNode, index) }}</span>
|
||||||
|
<span v-else class="placeholder">请设置条件</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="index !== childNode.conditionNodeList.length - 1"
|
||||||
|
class="sort-right"
|
||||||
|
@click.stop="arrTransfer(index)"
|
||||||
|
>
|
||||||
|
<right-outlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="item.childNode" :node-item="item" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot v-if="item.childNode" :node="item" />
|
||||||
|
<div v-if="index === 0" class="top-left-cover-line" />
|
||||||
|
<div v-if="index === 0" class="bottom-left-cover-line" />
|
||||||
|
<div v-if="index === childNode.conditionNodeList.length - 1" class="top-right-cover-line" />
|
||||||
|
<div v-if="index === childNode.conditionNodeList.length - 1" class="bottom-right-cover-line" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="childNode.childNode" :parent-data="childNode" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<xn-form-container
|
||||||
|
v-model:visible="drawer"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:width="700"
|
||||||
|
:body-style="{ 'padding-top': '0px' }"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="node-wrap-drawer__title">
|
||||||
|
<label v-if="!isEditTitle" @click="editTitle">
|
||||||
|
{{ form.title }}
|
||||||
|
<edit-outlined class="node-wrap-drawer-title-edit" />
|
||||||
|
</label>
|
||||||
|
<a-input
|
||||||
|
v-if="isEditTitle"
|
||||||
|
ref="nodeTitle"
|
||||||
|
v-model:value="form.title"
|
||||||
|
allow-clear
|
||||||
|
@blur="saveTitle"
|
||||||
|
@keyup.enter="saveTitle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-layout-content>
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="1" tab="条件配置" force-render>
|
||||||
|
<a-form layout="vertical">
|
||||||
|
<div v-show="!isNodeLegal(form)" style="margin-bottom: 10px">
|
||||||
|
<a-alert message="请填写完成所有项!" type="error" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">配置要执行的条件</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-bottom: 2px">
|
||||||
|
<a-button type="primary" round @click="addDynamicValidateForm" size="small">
|
||||||
|
<plus-outlined />
|
||||||
|
增加条件组
|
||||||
|
</a-button>
|
||||||
|
</p>
|
||||||
|
<a-form-item v-for="(domain, index) in dynamicValidateForm" :key="index">
|
||||||
|
<a-divider style="margin: 10px 0" />
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="22">
|
||||||
|
<a-table :data-source="domain" size="small" :pagination="false">
|
||||||
|
<a-table-column data-index="field" title="条件字段" width="130">
|
||||||
|
<template #default="{ record }">
|
||||||
|
<a-select
|
||||||
|
v-model:value="record.field"
|
||||||
|
placeholder="请选择"
|
||||||
|
v-if="recordData.formType === 'DESIGN'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="formField in fieldList"
|
||||||
|
:key="formField.model"
|
||||||
|
:value="formField.model"
|
||||||
|
@click="record.label = formField.label"
|
||||||
|
>{{ formField.label }}</a-select-option
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
<a-input v-model:value="record.field" placeholder="条件" v-else />
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column data-index="label" title="描述">
|
||||||
|
<template #default="{ record }">
|
||||||
|
<a-input v-model:value="record.label" placeholder="描述" />
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column data-index="operator" width="140">
|
||||||
|
<template #title>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title
|
||||||
|
>注:自定义表单模式下条件选择完全放开,中文字段不可以使用大于、大于等于、小于、小于等于!</template
|
||||||
|
>
|
||||||
|
<question-circle-outlined />
|
||||||
|
运算符
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #default="{ record }">
|
||||||
|
<a-select v-model:value="record.operator" placeholder="请选择">
|
||||||
|
<a-select-option value="==">等于</a-select-option>
|
||||||
|
<a-select-option value="!=">不等于</a-select-option>
|
||||||
|
<a-select-option value=">" v-if="isSelectOption(record)">大于</a-select-option>
|
||||||
|
<a-select-option value=">=" v-if="isSelectOption(record)">大于等于</a-select-option>
|
||||||
|
<a-select-option value="<" v-if="isSelectOption(record)">小于</a-select-option>
|
||||||
|
<a-select-option value="<=" v-if="isSelectOption(record)">小于等于</a-select-option>
|
||||||
|
<a-select-option value="include" v-if="!isSelectOption(record, 'include')"
|
||||||
|
>包含</a-select-option
|
||||||
|
>
|
||||||
|
<a-select-option value="notInclude" v-if="!isSelectOption(record, 'notInclude')"
|
||||||
|
>不包含</a-select-option
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column data-index="value" width="100">
|
||||||
|
<template #title>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>中文字段需判断等于,值必须加入英文双引号</template>
|
||||||
|
<question-circle-outlined />
|
||||||
|
值
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #default="{ record }">
|
||||||
|
<a-input v-model:value="record.value" placeholder="值" />
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column data-index="value" title="移除" width="55">
|
||||||
|
<template #default="{ index }">
|
||||||
|
<a-button size="small" type="primary" danger ghost @click="deleteConditionList(index, domain)"
|
||||||
|
>移除</a-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
</a-table>
|
||||||
|
<a-button type="dashed" class="dashedButton" @click="addConditionList(index)">
|
||||||
|
<PlusOutlined />
|
||||||
|
增加条件
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="2" class="deleteIcon">
|
||||||
|
<minus-circle-two-tone class="minusCircle" @click="delDomains(index)" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab="执行监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propExecutionListenerInfoRef"
|
||||||
|
:listenerValue="form.properties.executionListenerInfo"
|
||||||
|
:defaultListenerList="executionListenerInfo"
|
||||||
|
:listener-value-array="executionListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-layout-content>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="primary" style="margin-right: 8px" @click="save">保存</a-button>
|
||||||
|
<a-button @click="drawer = false">取消</a-button>
|
||||||
|
</template>
|
||||||
|
</xn-form-container>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep, isEmpty } from 'lodash-es'
|
||||||
|
import addNode from './addNode.vue'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import workFlowUtils from '@/components/XnWorkflow/nodes/utils/index'
|
||||||
|
import PropListenerInfo from './prop/propListenerInfo.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNode,
|
||||||
|
PropListenerInfo
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
index: 0,
|
||||||
|
form: {},
|
||||||
|
dynamicValidateForm: [],
|
||||||
|
fieldList: [],
|
||||||
|
activeKey: '1',
|
||||||
|
executionListenerInfo: cloneDeep(config.listener.exclusiveGatewayExecutionListenerInfo),
|
||||||
|
operatorList: cloneDeep(config.exclusiveGatewayConfig.operatorList)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
// 把字段给掏出来
|
||||||
|
this.fieldList = workFlowUtils.getListField(this.formFieldListValue).map((m) => {
|
||||||
|
let type = m.type //slider rate number
|
||||||
|
if (type === 'slider' || type === 'rate' || type === 'number') {
|
||||||
|
m.type = 'number'
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: m.label,
|
||||||
|
model: m.selectTable + '.' + m.model,
|
||||||
|
type: m.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show(index) {
|
||||||
|
this.index = index
|
||||||
|
this.form = {}
|
||||||
|
this.form = cloneDeep(this.childNode.conditionNodeList[index])
|
||||||
|
this.drawer = true
|
||||||
|
this.dynamicValidateForm = this.form.properties.conditionInfo
|
||||||
|
},
|
||||||
|
editTitle() {
|
||||||
|
this.isEditTitle = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.nodeTitle.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveTitle() {
|
||||||
|
this.isEditTitle = false
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
this.form.properties.conditionInfo = this.dynamicValidateForm
|
||||||
|
this.form.properties.executionListenerInfo = this.$refs.propExecutionListenerInfoRef.selectedListenerList()
|
||||||
|
if (this.isNodeLegal(this.form)) {
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.childNode.conditionNodeList[this.index] = this.form
|
||||||
|
this.setCalibration()
|
||||||
|
this.$emit('update:modelValue', this.childNode)
|
||||||
|
this.drawer = false
|
||||||
|
} else {
|
||||||
|
this.form.dataLegal = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isSelectOption(record, value) {
|
||||||
|
if (this.recordData.formType === 'DESIGN') {
|
||||||
|
if (record.field) {
|
||||||
|
return this.fieldList.find((f) => f.model === record.field).type === 'number'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value === 'include' || value === 'notInclude') {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 校验此条件是否通过
|
||||||
|
isNodeLegal(data) {
|
||||||
|
const priorityLevel = data.properties.configInfo.priorityLevel
|
||||||
|
const len = this.childNode.conditionNodeList.length
|
||||||
|
const priorityLevelMax = this.childNode.conditionNodeList[len - 1].properties.configInfo.priorityLevel
|
||||||
|
// 如果往其他条件的分支中增加,那我们一视同仁
|
||||||
|
if (priorityLevelMax === priorityLevel) {
|
||||||
|
if (this.dynamicValidateForm.length > 0) {
|
||||||
|
for (let i = 0; i < this.dynamicValidateForm.length; i++) {
|
||||||
|
const obj = this.dynamicValidateForm[i]
|
||||||
|
if (obj.length > 0) {
|
||||||
|
return this.isNodeLegalItem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.isNodeLegalItem()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 设置校验
|
||||||
|
setCalibration() {
|
||||||
|
// 在数据返回更新之前,我要顺手吧优先级最后的条件校验设置为 true,管他设没设
|
||||||
|
for (let i = 0; i < this.childNode.conditionNodeList.length; i++) {
|
||||||
|
let conditionNode = this.childNode.conditionNodeList[i]
|
||||||
|
// 取到优先级
|
||||||
|
const priorityLevel = conditionNode.properties.configInfo.priorityLevel
|
||||||
|
// 如果是最高的
|
||||||
|
if (priorityLevel === this.childNode.conditionNodeList.length) {
|
||||||
|
// 给成通过,不管他的条件,本身优先级最后的就是其他条件进入,一般也不设
|
||||||
|
conditionNode.dataLegal = true
|
||||||
|
} else {
|
||||||
|
// 其他地方的,判断是否有条件,无条件的统统给 false
|
||||||
|
if (conditionNode.properties.conditionInfo.length === 0) {
|
||||||
|
conditionNode.dataLegal = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isNodeLegalItem() {
|
||||||
|
let objNum = 0
|
||||||
|
let successNum = 0
|
||||||
|
if (this.dynamicValidateForm.length > 0) {
|
||||||
|
for (let i = 0; i < this.dynamicValidateForm.length; i++) {
|
||||||
|
const obj = this.dynamicValidateForm[i]
|
||||||
|
let objNumItem = 0
|
||||||
|
if (!isEmpty(obj)) {
|
||||||
|
for (let a = 0; a < obj.length; a++) {
|
||||||
|
objNumItem++
|
||||||
|
if (this.isObjLegal(obj[a])) {
|
||||||
|
successNum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objNum = objNum + objNumItem
|
||||||
|
} else {
|
||||||
|
objNum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (successNum !== 0) {
|
||||||
|
if (objNum === successNum) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
// 校验对象中是否有空值
|
||||||
|
isObjLegal(obj) {
|
||||||
|
let a = 0
|
||||||
|
for (let b in obj) {
|
||||||
|
if (!obj[b]) {
|
||||||
|
a++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a === 1) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 增加条件组
|
||||||
|
addDynamicValidateForm() {
|
||||||
|
this.dynamicValidateForm.push([])
|
||||||
|
},
|
||||||
|
// 删除条件组
|
||||||
|
delDomains(index) {
|
||||||
|
this.dynamicValidateForm.splice(index, 1)
|
||||||
|
},
|
||||||
|
addTerm() {
|
||||||
|
const len = this.childNode.conditionNodeList.length
|
||||||
|
const priorityLevel = this.childNode.conditionNodeList[len - 1].properties.configInfo.priorityLevel
|
||||||
|
// 创建分支节点 n
|
||||||
|
const condition = cloneDeep(config.nodeModel.node)
|
||||||
|
condition.id = this.$TOOL.snowyUuid()
|
||||||
|
condition.type = 'sequenceFlow'
|
||||||
|
condition.title = `条件${priorityLevel + 1}`
|
||||||
|
// 创建分支节点2 configInfo
|
||||||
|
const condition1ConfigInfo = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
|
||||||
|
condition1ConfigInfo.priorityLevel = priorityLevel + 1
|
||||||
|
|
||||||
|
condition.properties.configInfo = condition1ConfigInfo
|
||||||
|
this.childNode.conditionNodeList.push(condition)
|
||||||
|
},
|
||||||
|
delTerm(index) {
|
||||||
|
this.childNode.conditionNodeList.splice(index, 1)
|
||||||
|
if (this.childNode.conditionNodeList.length === 1) {
|
||||||
|
if (this.childNode.childNode) {
|
||||||
|
if (JSON.stringify(this.childNode.conditionNodeList[0].childNode) !== '{}') {
|
||||||
|
this.reData(this.childNode.conditionNodeList[0].childNode, this.childNode.childNode)
|
||||||
|
} else {
|
||||||
|
this.childNode.conditionNodeList[0].childNode = this.childNode.childNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$emit('update:modelValue', this.childNode.conditionNodeList[0].childNode)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reData(data, addData) {
|
||||||
|
if (JSON.stringify(data) !== '{}') {
|
||||||
|
data.childNode = addData
|
||||||
|
} else {
|
||||||
|
this.reData(data.childNode, addData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arrTransfer(index, type = 1) {
|
||||||
|
this.childNode.conditionNodeList[index] = this.childNode.conditionNodeList.splice(
|
||||||
|
index + type,
|
||||||
|
1,
|
||||||
|
this.childNode.conditionNodeList[index]
|
||||||
|
)[0]
|
||||||
|
this.childNode.conditionNodeList.map((item, index) => {
|
||||||
|
item.properties.configInfo.priorityLevel = index + 1
|
||||||
|
})
|
||||||
|
this.setCalibration()
|
||||||
|
this.$emit('update:modelValue', this.childNode)
|
||||||
|
},
|
||||||
|
addConditionList(index) {
|
||||||
|
const domainsObj = {
|
||||||
|
label: '',
|
||||||
|
key: '',
|
||||||
|
operator: '==',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
this.dynamicValidateForm[index].push(domainsObj)
|
||||||
|
},
|
||||||
|
deleteConditionList(index, domain) {
|
||||||
|
domain.splice(index, 1)
|
||||||
|
},
|
||||||
|
toText(childNode, index) {
|
||||||
|
const conditionList = childNode.conditionNodeList[index].properties.conditionInfo
|
||||||
|
const priorityLevel = childNode.conditionNodeList[index].properties.configInfo.priorityLevel
|
||||||
|
const len = this.childNode.conditionNodeList.length
|
||||||
|
const priorityLevelMax = this.childNode.conditionNodeList[len - 1].properties.configInfo.priorityLevel
|
||||||
|
|
||||||
|
if (JSON.stringify(conditionList) !== undefined && conditionList.length > 0) {
|
||||||
|
let text = ''
|
||||||
|
for (let i = 0; i < conditionList.length; i++) {
|
||||||
|
for (let j = 0; j < conditionList[i].length; j++) {
|
||||||
|
if (j + 1 !== conditionList[i].length) {
|
||||||
|
text =
|
||||||
|
text +
|
||||||
|
conditionList[i][j].label +
|
||||||
|
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||||
|
conditionList[i][j].value +
|
||||||
|
' 且 '
|
||||||
|
} else {
|
||||||
|
text =
|
||||||
|
text +
|
||||||
|
conditionList[i][j].label +
|
||||||
|
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||||
|
conditionList[i][j].value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i + 1 !== conditionList.length) {
|
||||||
|
text = text + ' 或 '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
} else if (conditionList.length === 0 && priorityLevel < priorityLevelMax) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return '其他条件进入此流程'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 通过value 获取界面显示的label汉字
|
||||||
|
getOperatorLabel(value) {
|
||||||
|
return this.operatorList.find((item) => item.value === value).label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.deleteIcon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.minusCircle {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
.dashedButton {
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
110
src/components/XnWorkflow/nodes/parallelGateway.vue
Normal file
110
src/components/XnWorkflow/nodes/parallelGateway.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="branch-wrap">
|
||||||
|
<div class="branch-box-wrap">
|
||||||
|
<div class="branch-box">
|
||||||
|
<a-button class="add-branch" type="primary" shape="round" @click="addTerm">添加并行</a-button>
|
||||||
|
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||||
|
<div class="condition-node">
|
||||||
|
<div class="condition-node-box">
|
||||||
|
<user-task
|
||||||
|
v-model="childNode.conditionNodeList[index]"
|
||||||
|
:form-field-list-value="formFieldListValue"
|
||||||
|
:recordData="recordData"
|
||||||
|
:processConfigInfo="processConfigInfo"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
@deleteParalle="delTerm(index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot v-if="item.childNode" :node="item" />
|
||||||
|
<div v-if="index === 0" class="top-left-cover-line" />
|
||||||
|
<div v-if="index === 0" class="bottom-left-cover-line" />
|
||||||
|
<div v-if="index === childNode.conditionNodeList.length - 1" class="top-right-cover-line" />
|
||||||
|
<div v-if="index === childNode.conditionNodeList.length - 1" class="bottom-right-cover-line" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="childNode.childNode" :parent-data="childNode" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import addNode from './addNode.vue'
|
||||||
|
import userTask from './userTask.vue'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNode,
|
||||||
|
userTask
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
processConfigInfo: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
index: 0,
|
||||||
|
form: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addTerm() {
|
||||||
|
const len = this.childNode.conditionNodeList.length + 1
|
||||||
|
// 创建主节点
|
||||||
|
const nodeModel = cloneDeep(config.nodeModel.node)
|
||||||
|
nodeModel.id = this.$TOOL.snowyUuid()
|
||||||
|
nodeModel.type = 'userTask'
|
||||||
|
;(nodeModel.title = `审批人${len}`),
|
||||||
|
(nodeModel.priorityLevel = len),
|
||||||
|
(nodeModel.conditionNodeList = []),
|
||||||
|
(nodeModel.childNode = {})
|
||||||
|
// 创建 configInfo
|
||||||
|
const configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
|
||||||
|
nodeModel.properties.configInfo = configInfo
|
||||||
|
|
||||||
|
this.childNode.conditionNodeList.push(nodeModel)
|
||||||
|
},
|
||||||
|
delTerm(index) {
|
||||||
|
this.childNode.conditionNodeList.splice(index, 1)
|
||||||
|
if (this.childNode.conditionNodeList.length == 1) {
|
||||||
|
if (this.childNode.childNode) {
|
||||||
|
// 这是{}
|
||||||
|
if (JSON.stringify(this.childNode.conditionNodeList[0].childNode) !== '{}') {
|
||||||
|
this.reData(this.childNode.conditionNodeList[0].childNode, this.childNode.childNode)
|
||||||
|
} else {
|
||||||
|
this.childNode.conditionNodeList[0].childNode = this.childNode.childNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$emit('update:modelValue', this.childNode.conditionNodeList[0].childNode)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reData(data, addData) {
|
||||||
|
if (JSON.stringify(data) !== '{}') {
|
||||||
|
data.childNode = addData
|
||||||
|
} else {
|
||||||
|
this.reData(data.childNode, addData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
107
src/components/XnWorkflow/nodes/prop/formUserSelector.vue
Normal file
107
src/components/XnWorkflow/nodes/prop/formUserSelector.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="表单人员选择"
|
||||||
|
:mask-closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:width="600"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<div class="form-user-table">
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:row-key="(record) => record.model"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange, type: 'radio' }"
|
||||||
|
:pagination="false"
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="formUserSelector">
|
||||||
|
import workFlowUtils from '@/components/XnWorkflow/nodes/utils/index'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '字段名',
|
||||||
|
dataIndex: 'label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '字段',
|
||||||
|
dataIndex: 'model'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 定义emit事件
|
||||||
|
const emit = defineEmits({ click: null })
|
||||||
|
const props = defineProps(['formFieldList'])
|
||||||
|
|
||||||
|
const dataSource = ref([])
|
||||||
|
dataSource.value = workFlowUtils.getListField(props.formFieldList).filter((f) => {
|
||||||
|
// 判断如果是人员类型的,就让列表显示
|
||||||
|
if (f.type === 'xnUserSelector') {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}).map((m) => {
|
||||||
|
return {
|
||||||
|
label: m.label,
|
||||||
|
model: m.model,
|
||||||
|
field: m.model,
|
||||||
|
type: m.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const selectedRowKeys = ref([])
|
||||||
|
// 设置默认选中的
|
||||||
|
const state = reactive({
|
||||||
|
selectedRowKeys: selectedRowKeys.value,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
const showFormUserModal = (data = '') => {
|
||||||
|
if (dataSource.value.length === 0) {
|
||||||
|
message.warning('表单内无人员可提供选择!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataSource.value.length === 1) {
|
||||||
|
emit('click', dataSource.value[0])
|
||||||
|
message.success('表单内仅有一个人员选择,默认选中!')
|
||||||
|
} else {
|
||||||
|
visible.value = true
|
||||||
|
state.selectedRowKeys[0] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 点击复选框回调
|
||||||
|
const onSelectChange = (selectedRowKeys) => {
|
||||||
|
state.selectedRowKeys = selectedRowKeys
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
const handleOk = () => {
|
||||||
|
const result = dataSource.value.filter((item) => state.selectedRowKeys[0] === item.model)
|
||||||
|
emit('click', result[0])
|
||||||
|
visible.value = false
|
||||||
|
state.selectedRowKeys = []
|
||||||
|
}
|
||||||
|
// 关闭
|
||||||
|
const handleCancel = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抛出方法,让上个界面使用
|
||||||
|
defineExpose({
|
||||||
|
showFormUserModal
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.form-user-table {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
157
src/components/XnWorkflow/nodes/prop/propButtonInfo.vue
Normal file
157
src/components/XnWorkflow/nodes/prop/propButtonInfo.vue
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">参与者可以看见或操作哪些按钮</span>
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:row-key="(record) => record.key"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:row-selection="{
|
||||||
|
selectedRowKeys: state.selectedRowKeys,
|
||||||
|
onChange: onSelectChange,
|
||||||
|
getCheckboxProps: checkboxProps
|
||||||
|
}"
|
||||||
|
:pagination="false"
|
||||||
|
:show-header="false"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'label'">
|
||||||
|
<a-popover trigger="hover" placement="left">
|
||||||
|
<template #content>
|
||||||
|
<a-button :type="record.type" size="small">
|
||||||
|
<template #icon>
|
||||||
|
<component :is="record.icon" />
|
||||||
|
</template>
|
||||||
|
{{ record.label }}
|
||||||
|
</a-button>
|
||||||
|
<div v-if="record.key === 'SAVE'" style="width: 300px">
|
||||||
|
”保存“按钮作用为发起节点保存操作,审批节点下无保存操作。
|
||||||
|
</div>
|
||||||
|
<div v-if="record.key === 'SUBMIT'" style="width: 300px">
|
||||||
|
”提交“按钮作用为发起节点填写完申请单,提交流程到下一步。
|
||||||
|
</div>
|
||||||
|
<div v-if="record.key === 'REVOKE'" style="width: 300px">”撤回“按钮作用为发起节点发错内容,将其撤回。</div>
|
||||||
|
<div v-if="record.key === 'PASS'" style="width: 300px">按钮作用为审批节点同意该审核之操作。</div>
|
||||||
|
<div v-if="record.key === 'REJECT'" style="width: 300px">按钮作用为审批节点进行拒绝之操作。</div>
|
||||||
|
<div v-if="record.key === 'BACK'" style="width: 300px">按钮作用为审批节点退回操作,可退回至任意节点。</div>
|
||||||
|
<div v-if="record.key === 'ADD_SIGN'" style="width: 300px">按钮作用为审批节点加签操作。</div>
|
||||||
|
<div v-if="record.key === 'TURN'" style="width: 300px">按钮作用为审批节点转给他人办理操作。</div>
|
||||||
|
<div v-if="record.key === 'JUMP'" style="width: 300px">按钮作用为审批节点可跳转至任意节点操作。</div>
|
||||||
|
<div v-if="record.key === 'PRINT'" style="width: 300px">
|
||||||
|
按钮配置后,操作人到该节点可以进行管理员配置的全局打印模板,进行打印此流程进展的内容。
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-button size="small" :type="record.type">
|
||||||
|
<template #icon>
|
||||||
|
<component :is="record.icon" />
|
||||||
|
</template>
|
||||||
|
{{ record.label }}
|
||||||
|
</a-button>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'newLabel'">
|
||||||
|
<a-input v-model:value="record.newLabel" show-count :maxlength="10" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="propButtonInfo">
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'label',
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'newLabel'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const buttonInfo = cloneDeep(config.nodeModel.buttonInfo)
|
||||||
|
|
||||||
|
const props = defineProps(['buttonInfo', 'showButton', 'noChecked'])
|
||||||
|
|
||||||
|
const dataSource = ref([])
|
||||||
|
const selectedRowKeys = ref([])
|
||||||
|
// 将其回传的跟本组件的关联性切断
|
||||||
|
selectedRowKeys.value = cloneDeep(props.showButton)
|
||||||
|
|
||||||
|
// 设置按钮选中及显示的
|
||||||
|
const buttonInfoData = () => {
|
||||||
|
buttonInfo.forEach((button) => {
|
||||||
|
if (props.buttonInfo.length > 0) {
|
||||||
|
props.buttonInfo.forEach((item) => {
|
||||||
|
if (button.key === item.key) {
|
||||||
|
button.newLabel = item.label
|
||||||
|
button.value = item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
button.newLabel = button.label
|
||||||
|
}
|
||||||
|
// 匹配选中的
|
||||||
|
if (selectedRowKeys.value.length > 0) {
|
||||||
|
selectedRowKeys.value.forEach((selectedRowKey) => {
|
||||||
|
if (button.value === 'SHOW') {
|
||||||
|
// 已有的里面如果不包含这个已经被选中的
|
||||||
|
if (selectedRowKeys.value.indexOf(button.key) === -1) {
|
||||||
|
selectedRowKeys.value.push(button.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedRowKey === button.key) {
|
||||||
|
button.value = 'SHOW'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (button.value === 'SHOW') {
|
||||||
|
if (selectedRowKeys.value.indexOf(button.key) === -1) {
|
||||||
|
selectedRowKeys.value.push(button.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return buttonInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSource.value = buttonInfoData()
|
||||||
|
|
||||||
|
// 设置默认选中的
|
||||||
|
const state = reactive({
|
||||||
|
selectedRowKeys: selectedRowKeys.value,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
// 点击复选框回调
|
||||||
|
const onSelectChange = (selectedRowKeys) => {
|
||||||
|
state.selectedRowKeys = selectedRowKeys
|
||||||
|
}
|
||||||
|
// 设置选择框的默认属性配置,将其设置成不可取消,不可选中
|
||||||
|
const checkboxProps = (record) => ({
|
||||||
|
disabled:
|
||||||
|
(record.value === 'SHOW' && props.showButton.indexOf(record.key) > -1) || props.noChecked.indexOf(record.key) > -1
|
||||||
|
})
|
||||||
|
// 父界面需要调用获取参数
|
||||||
|
const selectedButtonKeyList = () => {
|
||||||
|
let resultData = cloneDeep(dataSource.value)
|
||||||
|
resultData.forEach((dataItem) => {
|
||||||
|
if (state.selectedRowKeys.indexOf(dataItem.key) > -1) {
|
||||||
|
dataItem.value = 'SHOW'
|
||||||
|
} else {
|
||||||
|
dataItem.value = 'HIDE'
|
||||||
|
}
|
||||||
|
if (dataItem.newLabel !== '') {
|
||||||
|
dataItem.label = dataItem.newLabel
|
||||||
|
}
|
||||||
|
delete dataItem.newLabel
|
||||||
|
})
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 抛出方法,让上个界面使用
|
||||||
|
defineExpose({
|
||||||
|
selectedButtonKeyList
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
propButtonInfo
|
||||||
|
====
|
||||||
|
|
||||||
|
> 工作流中配置按钮选择器
|
||||||
|
> eg: 发起人、审批人、通知人 这三个节点使用
|
||||||
|
|
||||||
|
该组件由 [俞宝山](https://www.xiaonuo.vip) 封装
|
||||||
|
|
||||||
|
|
||||||
|
### 使用方式
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<prop-button-info ref="propButtonInfo" :buttonInfo="selectedButtonInfo" :showButton="startTaskDefaultButtonkey"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 配置的一些数据
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config';
|
||||||
|
|
||||||
|
// 这个数据就是下面说的,回显数据,选中的数据对象中show值为true
|
||||||
|
const selectedButtonInfo = ref(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: 'SAVE',
|
||||||
|
label: '保存',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SUBMIT',
|
||||||
|
label: '提交',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REVOKE',
|
||||||
|
label: '撤回',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'COMPLETE',
|
||||||
|
label: '同意',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REJECT',
|
||||||
|
label: '驳回',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'PRINT',
|
||||||
|
label: '打印',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
// 某个节点调用此组件,不管三七二十一,这个是必须选中,而且不能取消的
|
||||||
|
const startTaskDefaultButtonkey = ref(
|
||||||
|
// 注意:使用静态配置中的数据,必须切断双向绑定,不能因为不切短改变原始数据
|
||||||
|
JSON.parse(JSON.stringify(config.button.startTaskDefaultButtonkey))
|
||||||
|
)
|
||||||
|
|
||||||
|
// 可以配置不让配置选择的按钮
|
||||||
|
const startTaskNoCheckedButtonkey = ref(
|
||||||
|
// 注意:使用静态配置中的数据,必须切断双向绑定,不能因为不切短改变原始数据
|
||||||
|
JSON.parse(JSON.stringify(config.button.startTaskNoCheckedButtonkey))
|
||||||
|
)
|
||||||
|
|
||||||
|
// 初始化我们调用这个组件的ref名称,Vue3 setup语法糖的写法,是这样的哦!
|
||||||
|
const propButtonInfo = ref(null);
|
||||||
|
|
||||||
|
// 使用的界面调用这个方法,获得组件中选好的数据
|
||||||
|
const getButtonInfoData = () => {
|
||||||
|
console.log('result data :' + JSON.stringify(propButtonInfo.selectedButtonKeyList()))
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 | 默认值 |
|
||||||
|
| --------------------- | ----------------------------------- | ------ | ------ |
|
||||||
|
| selectedButtonKeyList | 调用界面使用此方法获取组件中选中的数据 | Array | [] |
|
||||||
|
|
||||||
|
### 数据
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---------- | ------------------------------------------------------------------------------------ | ------ | ------ |
|
||||||
|
| buttonInfo | 传送数据为此模型节点中选中的按钮用show=true标识,示例:`selectedButtonInfo` | Array | [] |
|
||||||
|
| showButton | 哪个节点调用,哪个节点设置他必选的,不能改变的按钮数据,示例:`startTaskDefaultButtonkey ` | Array | [] |
|
||||||
|
| noChecked | 默认不让哪个按钮在本节点进行配置,示例:`startTaskNoCheckedButtonkey ` | Array | [] |
|
||||||
141
src/components/XnWorkflow/nodes/prop/propFieldInfo.vue
Normal file
141
src/components/XnWorkflow/nodes/prop/propFieldInfo.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">参与者可以看见或操作哪些字段</span>
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:row-key="(record) => record.key"
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:pagination="false"
|
||||||
|
:show-header="false"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'label'">
|
||||||
|
<span v-if="record.required" style="color: red">* </span>
|
||||||
|
<span v-else> </span>
|
||||||
|
{{ record.label }}
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'value'">
|
||||||
|
<a-radio-group v-model:value="record.value" :disabled="record.disabled">
|
||||||
|
<a-radio :key="radio.value" v-for="radio in fieldRadioList" :value="radio.value">{{ radio.label }}</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="propFieldInfo">
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '表单字段',
|
||||||
|
dataIndex: 'label',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'value'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 定义一个表单的已拖进去的组件列表,也就是我们要的字段列表
|
||||||
|
const listField = ref([])
|
||||||
|
|
||||||
|
let fieldRadioList = JSON.parse(JSON.stringify(config.field.fieldRadioList))
|
||||||
|
|
||||||
|
// 接受到值
|
||||||
|
const props = defineProps(['formFieldListValue', 'fieldInfo', 'defaultFieldModel'])
|
||||||
|
|
||||||
|
const dataSource = ref([])
|
||||||
|
|
||||||
|
const getListField = () => {
|
||||||
|
listField.value = []
|
||||||
|
// 递归遍历控件树
|
||||||
|
const traverse = (array) => {
|
||||||
|
array.forEach((element) => {
|
||||||
|
if (element.type === 'grid' || element.type === 'tabs') {
|
||||||
|
// 栅格布局 and 标签页
|
||||||
|
element.columns.forEach((item) => {
|
||||||
|
traverse(item.list)
|
||||||
|
})
|
||||||
|
} else if (element.type === 'card') {
|
||||||
|
// 卡片布局 and 动态表格
|
||||||
|
traverse(element.list)
|
||||||
|
} else if (element.type === 'table') {
|
||||||
|
// 表格布局
|
||||||
|
element.trs.forEach((item) => {
|
||||||
|
item.tds.forEach((val) => {
|
||||||
|
traverse(val.list)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const type = element.type
|
||||||
|
// 排除一些
|
||||||
|
if ((type !== 'alert') & (type !== 'text') & (type !== 'divider') & (type !== 'html')) {
|
||||||
|
// & (type !== 'batch')
|
||||||
|
listField.value.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(props.formFieldListValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.formFieldListValue.length > 0) {
|
||||||
|
getListField()
|
||||||
|
// 数据转换至模型中
|
||||||
|
listField.value.forEach((item) => {
|
||||||
|
// 创建数据模型
|
||||||
|
let dataModel = {}
|
||||||
|
// 如果调用此组件的节点没配置它默认的选中按钮,那么就用通用的
|
||||||
|
if (props.defaultFieldModel) {
|
||||||
|
dataModel = JSON.parse(JSON.stringify(props.defaultFieldModel))
|
||||||
|
} else {
|
||||||
|
dataModel = JSON.parse(JSON.stringify(config.field.fieldModel))
|
||||||
|
}
|
||||||
|
dataModel.key = item.model
|
||||||
|
// 假如有选中的值,我们将其回显
|
||||||
|
if (props.fieldInfo.length > 0) {
|
||||||
|
props.fieldInfo.forEach((field) => {
|
||||||
|
if (field.key === item.model) {
|
||||||
|
dataModel.value = field.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (item.rules) {
|
||||||
|
dataModel.required = item.rules[0].required
|
||||||
|
} else {
|
||||||
|
dataModel.required = false
|
||||||
|
}
|
||||||
|
// 判断有没有从表单那边勾选了隐藏跟禁用
|
||||||
|
if (item.options.hidden || item.options.disabled) {
|
||||||
|
dataModel.disabled = true
|
||||||
|
// 并且设置它选中的选项为隐藏活禁用
|
||||||
|
if (item.options.disabled) {
|
||||||
|
dataModel.value = 'READ' // 只读
|
||||||
|
}
|
||||||
|
// 如果设置了隐藏跟禁用,我们按隐藏来算
|
||||||
|
if (item.options.hidden) {
|
||||||
|
dataModel.value = 'HIDE' // 隐藏
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataModel.disabled = false
|
||||||
|
}
|
||||||
|
dataModel.label = item.label
|
||||||
|
dataSource.value.push(dataModel)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 父界面需要调用获取参数
|
||||||
|
const selectedFieldList = () => {
|
||||||
|
let resultData = JSON.parse(JSON.stringify(dataSource.value))
|
||||||
|
return resultData
|
||||||
|
}
|
||||||
|
// 抛出方法,让上个界面使用
|
||||||
|
defineExpose({
|
||||||
|
selectedFieldList
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
127
src/components/XnWorkflow/nodes/prop/propFieldInfo_README.md
Normal file
127
src/components/XnWorkflow/nodes/prop/propFieldInfo_README.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
propFieldInfo
|
||||||
|
====
|
||||||
|
|
||||||
|
> 工作流中配置字段选择器
|
||||||
|
> eg: 发起人、审批人这两个节点使用
|
||||||
|
|
||||||
|
该组件由 [俞宝山](https://www.xiaonuo.vip) 封装
|
||||||
|
|
||||||
|
|
||||||
|
### 使用方式
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<prop-field-info ref="propFieldInfo" :field-info="fieldInfo" :default-field-model="defaultFieldModel" :form-field-list-value="formFieldListValue"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
// 这个数据就是下面说的,回显数据,选中的数据对象中show值为true
|
||||||
|
const fieldInfo = ref(
|
||||||
|
{
|
||||||
|
"key":"input_1653160767462",
|
||||||
|
"label":"姓名",
|
||||||
|
"value":"WRITE",
|
||||||
|
"required":true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key":"input_1653160770827",
|
||||||
|
"label":"绰号",
|
||||||
|
"value":"WRITE",
|
||||||
|
"required":true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 这个是表单设计器选择后传过来的
|
||||||
|
const formFieldListValue = ref(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type":"input",
|
||||||
|
"label":"姓名",
|
||||||
|
"icon":"icon-write",
|
||||||
|
"options":{
|
||||||
|
"type":"text",
|
||||||
|
"width":"100%",
|
||||||
|
"defaultValue":"",
|
||||||
|
"placeholder":"请输入",
|
||||||
|
"clearable":false,
|
||||||
|
"maxLength":null,
|
||||||
|
"addonBefore":"",
|
||||||
|
"addonAfter":"",
|
||||||
|
"hidden":false,
|
||||||
|
"disabled":false
|
||||||
|
},
|
||||||
|
"model":"input_1653160767462",
|
||||||
|
"key":"input_1653160767462",
|
||||||
|
"help":"",
|
||||||
|
"rules":[
|
||||||
|
{
|
||||||
|
"required":true,
|
||||||
|
"message":"必填项"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type":"input",
|
||||||
|
"label":"绰号",
|
||||||
|
"icon":"icon-write",
|
||||||
|
"options":{
|
||||||
|
"type":"text",
|
||||||
|
"width":"100%",
|
||||||
|
"defaultValue":"",
|
||||||
|
"placeholder":"请输入",
|
||||||
|
"clearable":false,
|
||||||
|
"maxLength":null,
|
||||||
|
"addonBefore":"",
|
||||||
|
"addonAfter":"",
|
||||||
|
"hidden":false,
|
||||||
|
"disabled":false
|
||||||
|
},
|
||||||
|
"model":"input_1653160770827",
|
||||||
|
"key":"input_1653160770827",
|
||||||
|
"help":"",
|
||||||
|
"rules":[
|
||||||
|
{
|
||||||
|
"required":false,
|
||||||
|
"message":"必填项"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
// 默认的节点选中
|
||||||
|
const defaultFieldModel = ref({
|
||||||
|
key: '',
|
||||||
|
label: '',
|
||||||
|
value: 'READ', // 默认设为只读
|
||||||
|
required: false, // 必填
|
||||||
|
extJson: '' // 额外扩展,暂无
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化我们调用这个组件的ref名称,Vue3 setup语法糖的写法,是这样的哦!
|
||||||
|
const propFieldInfo = ref(null);
|
||||||
|
|
||||||
|
// 使用的界面调用这个方法,获得组件中选好的数据
|
||||||
|
const selectedFieldList = () => {
|
||||||
|
console.log('result data :' + JSON.stringify(propFieldInfo.selectedFieldList()))
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 | 默认值 |
|
||||||
|
| --------------------- | ----------------------------------- | ------ | ------ |
|
||||||
|
| selectedFieldList | 调用界面使用此方法获取组件中选中的数据 | Array | [] |
|
||||||
|
|
||||||
|
### 数据
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---------- | ------------------------------------------------------------------------------------ | ------ | ------ |
|
||||||
|
| form-field-list-value | 表单设计器中拖拉拽过后的字段数据,示例:`formFieldListValue` | Array | [] |
|
||||||
|
| field-info | 这个节点已经选好勾好的数据,回显的,示例:`fieldInfo ` | Array | [] |
|
||||||
|
| default-field-model | 默认这个节点勾选的字段配置,示例:`defaultFieldModel ` | Array | [] |
|
||||||
158
src/components/XnWorkflow/nodes/prop/propListenerInfo.vue
Normal file
158
src/components/XnWorkflow/nodes/prop/propListenerInfo.vue
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">配置要执行的监听</span>
|
||||||
|
</div>
|
||||||
|
<a-button type="primary" size="small" @click="addEditListener" class="mb-2">
|
||||||
|
<template #icon>
|
||||||
|
<plus-outlined />
|
||||||
|
</template>
|
||||||
|
新增
|
||||||
|
</a-button>
|
||||||
|
<a-table
|
||||||
|
ref="table"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:row-key="(record) => record.id"
|
||||||
|
bordered
|
||||||
|
:expand-row-by-click="true"
|
||||||
|
:pagination="false"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'listenerType'">
|
||||||
|
{{ listenerlabel(record.listenerType) }}
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-popconfirm title="确定要删除此监听吗?" @confirm="deleteListener(record)" placement="topRight">
|
||||||
|
<a-button type="link" danger size="small">删除</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<a-modal v-model:visible="modalVisible" title="新增" @ok="handleOk" @cancel="handleCancel">
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||||
|
<a-form-item label="监听类型:" name="listenerType">
|
||||||
|
<a-select v-model:value="formData.listenerType" placeholder="请选择类型" :options="listenerTypeOptions" @change="listenerType" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="JAVA监听器:" name="javaClass">
|
||||||
|
<a-select v-model:value="formData.javaClass" placeholder="请选择JAVA监听器" :options="listenerValueArray" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="PropListenerInfo">
|
||||||
|
import { required } from '@/utils/formRules'
|
||||||
|
import tool from '@/utils/tool'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const table = ref()
|
||||||
|
const listenerValueArray = ref([])
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'listenerType',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'JAVA监听器',
|
||||||
|
dataIndex: 'javaClass',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
width: 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const dataSource = ref([])
|
||||||
|
const props = defineProps(['listenerValue', 'defaultListenerList', 'listenerValueArray', 'executionListenerSelectorForCustomEventArray', 'listenerType'])
|
||||||
|
if (props.listenerValue && props.listenerValue.length > 0) {
|
||||||
|
// 转换到列表
|
||||||
|
props.listenerValue.forEach((item) => {
|
||||||
|
const obj = {
|
||||||
|
listenerType: item.key,
|
||||||
|
javaClass: item.value,
|
||||||
|
id: tool.snowyUuid()
|
||||||
|
}
|
||||||
|
dataSource.value.push(obj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 加载
|
||||||
|
if (props.listenerValueArray) {
|
||||||
|
listenerValueArray.value = props.listenerValueArray
|
||||||
|
}
|
||||||
|
// 新增或编辑
|
||||||
|
const addEditListener = () => {
|
||||||
|
modalVisible.value = true
|
||||||
|
}
|
||||||
|
// 删除
|
||||||
|
const deleteListener = (record) => {
|
||||||
|
// 算了,什么也不说了,留下能用的
|
||||||
|
dataSource.value = dataSource.value.filter((item) => item.id !== record.id)
|
||||||
|
}
|
||||||
|
// 转换监听数据
|
||||||
|
const selectedListenerList = () => {
|
||||||
|
return dataSource.value.map((item) => {
|
||||||
|
return {
|
||||||
|
key: item.listenerType,
|
||||||
|
value: item.javaClass,
|
||||||
|
label: listenerlabel(item.listenerType)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 通过key获得中文label
|
||||||
|
const listenerlabel = (key) => {
|
||||||
|
if (!props.defaultListenerList) {
|
||||||
|
return '请重新打开'
|
||||||
|
}
|
||||||
|
return props.defaultListenerList.find((f) => f.key === key).label
|
||||||
|
}
|
||||||
|
// 小对话框
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = ref({})
|
||||||
|
const formRules = {
|
||||||
|
listenerType: [required('请选择监听类型')],
|
||||||
|
javaClass: [required('请选择JAVA监听器')]
|
||||||
|
}
|
||||||
|
const listenerTypeOptions = props.defaultListenerList.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item.label,
|
||||||
|
value: item.key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 选择器类型监听
|
||||||
|
const listenerType = (value) => {
|
||||||
|
// 只有在全局情况下,才会去加载这些自定义的类
|
||||||
|
if (props.listenerType === 'global') {
|
||||||
|
formData.value.javaClass = undefined
|
||||||
|
listenerValueArray.value = []
|
||||||
|
// 拒绝、终止、撤回、删除 这些类型的时候,加载自定义的到选择列表
|
||||||
|
if (value === 'REJECT' || value === 'CLOSE' || value === 'REVOKE' || value === 'DELETE') {
|
||||||
|
if (props.executionListenerSelectorForCustomEventArray) {
|
||||||
|
listenerValueArray.value = props.executionListenerSelectorForCustomEventArray
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props.listenerValueArray) {
|
||||||
|
listenerValueArray.value = props.listenerValueArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleOk = () => {
|
||||||
|
formRef.value.validate().then(() => {
|
||||||
|
formData.value.id = tool.snowyUuid()
|
||||||
|
dataSource.value.push(formData.value)
|
||||||
|
handleCancel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleCancel = () => {
|
||||||
|
formData.value = {}
|
||||||
|
modalVisible.value = false
|
||||||
|
}
|
||||||
|
// 抛出方法,让上个界面使用
|
||||||
|
defineExpose({
|
||||||
|
selectedListenerList
|
||||||
|
})
|
||||||
|
</script>
|
||||||
7
src/components/XnWorkflow/nodes/prop/propNoticeInfo.vue
Normal file
7
src/components/XnWorkflow/nodes/prop/propNoticeInfo.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template></template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PropNoticeInfo'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
48
src/components/XnWorkflow/nodes/prop/propTag.vue
Normal file
48
src/components/XnWorkflow/nodes/prop/propTag.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<a-tag v-for="tag in tagList()">{{ tag }}</a-tag
|
||||||
|
><!-- 花里胡哨的颜色先去掉,用的时候拿到旁边就行 :color="randomColor()"-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="propTag">
|
||||||
|
// 颜色列表
|
||||||
|
const colorList = ['pink', 'red', 'orange', 'green', 'cyan', 'blue', 'purple']
|
||||||
|
// 获取随机颜色
|
||||||
|
const randomColor = () => {
|
||||||
|
return colorList[randomNum(0, colorList.length - 1)]
|
||||||
|
}
|
||||||
|
// 获取minNum到maxNum内的随机数
|
||||||
|
const randomNum = (minNum, maxNum) => {
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 1:
|
||||||
|
return parseInt(Math.random() * minNum + 1, 10)
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps(['tagList'])
|
||||||
|
// 将配置的参数转换为数组,显示tag标签
|
||||||
|
const tagList = () => {
|
||||||
|
const tag = props.tagList
|
||||||
|
if (tag === undefined) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
if (!tag.label) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const str = tag.label
|
||||||
|
let resultArray = []
|
||||||
|
if (str.indexOf(',') !== -1) {
|
||||||
|
resultArray = str.split(',')
|
||||||
|
} else {
|
||||||
|
resultArray.push(str)
|
||||||
|
}
|
||||||
|
return resultArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
220
src/components/XnWorkflow/nodes/prop/templateGenerator.vue
Normal file
220
src/components/XnWorkflow/nodes/prop/templateGenerator.vue
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-textarea
|
||||||
|
ref="smsInput"
|
||||||
|
v-model:value="smsContent"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
@input.native="smsInput"
|
||||||
|
@blur="inputBlur"
|
||||||
|
@focus="focusHandler"
|
||||||
|
@click.native="focusHandler"
|
||||||
|
@keydown.up.down.left.right.native="focusHandler"
|
||||||
|
@select.native="selectHandler"
|
||||||
|
></a-textarea>
|
||||||
|
<a-dropdown>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item
|
||||||
|
v-for="fields in fieldInfoLisNew"
|
||||||
|
:key="fields.value"
|
||||||
|
:value="fields.value"
|
||||||
|
:disabled="fields.value === 'disabled'"
|
||||||
|
@click="insertFields(fields)"
|
||||||
|
>{{ fields.label }}</a-menu-item
|
||||||
|
>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button size="small" type="primary" style="float: right; margin-top: -35px; margin-right: 10px">
|
||||||
|
置入字段
|
||||||
|
<DownOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
export default {
|
||||||
|
name: 'TemplateGenerator',
|
||||||
|
props: {
|
||||||
|
// 表单数据
|
||||||
|
fieldInfoLis: { type: Array, default: () => [] },
|
||||||
|
// v-model数据
|
||||||
|
inputValue: { type: String, default: () => '' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
smsContent: '',
|
||||||
|
inputFocus: null,
|
||||||
|
fieldInfoLisNew: [],
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 这里回显数据,并做转换
|
||||||
|
mounted() {
|
||||||
|
this.fieldInfoLisNew = [...JSON.parse(JSON.stringify(config.templateDefaultFields)), ...this.fieldInfoLis]
|
||||||
|
this.smsContent = this.setInput(this.inputValue)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 入数据转换
|
||||||
|
setInput(value) {
|
||||||
|
this.fieldInfoLisNew.forEach((field) => {
|
||||||
|
let temp = ''
|
||||||
|
if (value.indexOf(field.value) > -1) {
|
||||||
|
temp = value.replace(new RegExp(field.value, 'g'), '[' + field.label + ']')
|
||||||
|
}
|
||||||
|
if (temp !== '') {
|
||||||
|
value = temp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
// 选中置入的
|
||||||
|
insertFields(fields) {
|
||||||
|
this.insertStr('[' + fields.label + ']')
|
||||||
|
},
|
||||||
|
// 插入元素
|
||||||
|
insertStr(str) {
|
||||||
|
let before = this.smsContent.slice(0, this.inputFocus)
|
||||||
|
let after = this.smsContent.slice(this.inputFocus, this.smsContent.length)
|
||||||
|
this.inputFocus = this.inputFocus + str.length
|
||||||
|
this.smsContent = before + str + after
|
||||||
|
this.$emit('update:inputValue', this.outValue(this.smsContent))
|
||||||
|
},
|
||||||
|
// 保存光标位置
|
||||||
|
inputBlur(e) {
|
||||||
|
this.inputFocus = e.target.selectionStart
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
// 删除元素剩余部分
|
||||||
|
smsInput(e) {
|
||||||
|
//deleteContentBackward==退格键 deleteContentForward==del键
|
||||||
|
if (e.inputType === 'deleteContentBackward' || e.inputType === 'deleteContentForward') {
|
||||||
|
let beforeIndex = 0
|
||||||
|
let afterIndex = 0
|
||||||
|
// 光标位置往前
|
||||||
|
for (let i = e.target.selectionStart - 1; i >= 0; i--) {
|
||||||
|
if (this.smsContent[i] === '[') {
|
||||||
|
beforeIndex = i
|
||||||
|
afterIndex = e.target.selectionStart
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (this.smsContent[i] === ']') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 光标位置往后
|
||||||
|
for (let i = e.target.selectionStart; i < this.smsContent.length; i++) {
|
||||||
|
if (this.smsContent[i] === ']') {
|
||||||
|
afterIndex = i + 1
|
||||||
|
beforeIndex = e.target.selectionStart
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (this.smsContent[i] === '[') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (beforeIndex === 0 && afterIndex === 0) {
|
||||||
|
this.$emit('update:inputValue', this.outValue(this.smsContent))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let beforeStr = this.smsContent.slice(0, beforeIndex)
|
||||||
|
let afterStr = this.smsContent.slice(afterIndex)
|
||||||
|
this.smsContent = beforeStr + afterStr
|
||||||
|
this.inputFocus = beforeStr.length
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.changeFocus(e.target, this.inputFocus, this.inputFocus)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// this.$emit("smsText", this.smsContent);
|
||||||
|
|
||||||
|
this.$emit('update:inputValue', this.outValue(this.smsContent))
|
||||||
|
},
|
||||||
|
// 选择元素剩余部分
|
||||||
|
selectHandler(e) {
|
||||||
|
// 光标开始位置往前
|
||||||
|
for (let i = e.target.selectionStart - 1; i >= 0; i--) {
|
||||||
|
if (this.smsContent[i] === '[') {
|
||||||
|
this.changeFocus(e.target, i, e.target.selectionEnd)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (this.smsContent[i] === ']') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 光标结束位置往后
|
||||||
|
for (let i = e.target.selectionEnd; i < this.smsContent.length; i++) {
|
||||||
|
if (this.smsContent[i] === ']') {
|
||||||
|
this.changeFocus(e.target, e.target.selectionStart, i + 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (this.smsContent[i] === '[') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 焦点跳出元素内
|
||||||
|
focusHandler(e) {
|
||||||
|
setTimeout(() => {
|
||||||
|
let selStart = e.target.selectionStart
|
||||||
|
let beforeArrLength = this.smsContent.slice(0, selStart).split('[').length
|
||||||
|
let afterArrLength = this.smsContent.slice(0, selStart).split(']').length
|
||||||
|
//根据'['和']'生成两个数组 判断数组长度 是否相等 不相等就不成对就移动光标
|
||||||
|
if (beforeArrLength !== afterArrLength) {
|
||||||
|
let pos = this.smsContent.indexOf(']', selStart) + 1
|
||||||
|
if (beforeArrLength > afterArrLength && e.code == 'ArrowLeft') {
|
||||||
|
//按下按键左箭头
|
||||||
|
pos = this.smsContent.lastIndexOf('[', selStart)
|
||||||
|
}
|
||||||
|
this.changeFocus(e.target, pos, pos)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
// 修改光标位置
|
||||||
|
changeFocus(target, start, end) {
|
||||||
|
let range,
|
||||||
|
el = target
|
||||||
|
if (el.setSelectionRange) {
|
||||||
|
el.setSelectionRange(start, end)
|
||||||
|
} else {
|
||||||
|
range = el.createTextRange()
|
||||||
|
range.collapse(false)
|
||||||
|
range.select()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 出口数据转换
|
||||||
|
outValue(result) {
|
||||||
|
let index = result.indexOf('[')
|
||||||
|
let num = 0
|
||||||
|
while (index !== -1) {
|
||||||
|
num++
|
||||||
|
index = result.indexOf('[', index + 1)
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
for (let i = 1; i <= num; i++) {
|
||||||
|
let temp = ''
|
||||||
|
this.fieldInfoLisNew.forEach((field) => {
|
||||||
|
if (result.indexOf('[' + field.label + ']') > -1) {
|
||||||
|
temp = result.replace('[' + field.label + ']', field.value)
|
||||||
|
result = temp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.insert-list p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.insert-list div {
|
||||||
|
margin: 10px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
templateGenerator
|
||||||
|
====
|
||||||
|
|
||||||
|
> 模板编辑器
|
||||||
|
> eg: 主要用于工作流中的各种模板,也可作为通知的
|
||||||
|
|
||||||
|
该组件由 [俞宝山](https://www.xiaonuo.vip) 封装
|
||||||
|
|
||||||
|
|
||||||
|
### 使用方式
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<template-generator ref="templateGenerator" :fieldInfoLis="fieldInfoLis" v-model:inputValue="inputValue"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 初始化我们调用这个组件的dom名称,Vue3 setup语法糖的写法,是这样的哦!
|
||||||
|
const templateGenerator = ref(null)
|
||||||
|
|
||||||
|
// 这个数据就是下面说的,置入字段下拉中选择的参数
|
||||||
|
const fieldInfoLis = ref(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: '名称',
|
||||||
|
value: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '这有是啥',
|
||||||
|
value: 'what',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
// 组件回显数据
|
||||||
|
const inputValue = ref('发起人name在几点积分通知了,内容为:what')
|
||||||
|
|
||||||
|
// 使用的界面调用这个方法,获得组件中选好的数据
|
||||||
|
const selectedFieldList = () => {
|
||||||
|
console.log('result data :' + JSON.stringify(templateGenerator.getValue()))
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---------- | ------------------------------------------------------------------------------------ | ------ | ------ |
|
||||||
|
| fieldInfoLis | 表单数据,下拉框中要选择的,示例:`fieldInfoLis` | Array | [] |
|
||||||
|
| inputValue | 组件中要回显的数据,示例:`inputValue ` | String | '' |
|
||||||
304
src/components/XnWorkflow/nodes/serviceTask.vue
Normal file
304
src/components/XnWorkflow/nodes/serviceTask.vue
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<div class="node-wrap-box" @click="show">
|
||||||
|
<div class="title" style="background: #3296fa">
|
||||||
|
<send-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
<close-outlined class="close" @click.stop="delNode()" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||||
|
<span v-else class="placeholder">请选择人员</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="childNode.childNode"></add-node>
|
||||||
|
<xn-form-container
|
||||||
|
v-model:visible="drawer"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:width="700"
|
||||||
|
:body-style="{ 'padding-top': '0px' }"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="node-wrap-drawer__title">
|
||||||
|
<label v-if="!isEditTitle" @click="editTitle"
|
||||||
|
>{{ form.title }}<edit-outlined class="node-wrap-drawer-title-edit" />
|
||||||
|
</label>
|
||||||
|
<a-input
|
||||||
|
v-if="isEditTitle"
|
||||||
|
ref="nodeTitle"
|
||||||
|
v-model:value="form.title"
|
||||||
|
allow-clear
|
||||||
|
@blur="saveTitle"
|
||||||
|
@keyup.enter="saveTitle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-layout-content>
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="1" tab="抄送配置" force-render>
|
||||||
|
<a-form layout="vertical" :model="userTypeForm">
|
||||||
|
<div v-show="nodeLegal" style="margin-bottom: 10px">
|
||||||
|
<a-alert message="请选择抄送人员!" type="error" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">选择要抄送的人员</span>
|
||||||
|
</div>
|
||||||
|
<a-radio-group v-model:value="userSelectionType" style="width: 100%">
|
||||||
|
<a-row style="padding-bottom: 10px">
|
||||||
|
<a-col :span="8" v-for="userSelectionType in userSelectionTypeList" :key="userSelectionType.value">
|
||||||
|
<a-radio :value="userSelectionType.value" @click="selectionClick(userSelectionType.value)">
|
||||||
|
{{ userSelectionType.label }}
|
||||||
|
</a-radio>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-form-item v-if="userSelectionType === 'USER'">
|
||||||
|
<a-button class="mt-2" type="primary" size="small" round @click="openSelector">
|
||||||
|
<template #icon>
|
||||||
|
<search-outlined />
|
||||||
|
</template>
|
||||||
|
选择
|
||||||
|
</a-button>
|
||||||
|
<p />
|
||||||
|
<prop-tag :tag-list="getTagList('USER')" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="recordData.formType === 'DESIGN' && userSelectionType === 'FORM_USER'">
|
||||||
|
<div class="mt-2">
|
||||||
|
<a-button type="primary" @click="openSelector" size="small">
|
||||||
|
<template #icon>
|
||||||
|
<search-outlined />
|
||||||
|
</template>
|
||||||
|
选择
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<prop-tag
|
||||||
|
v-if="form.properties.participateInfo.length > 0 && form.properties.participateInfo[0].value !== ''"
|
||||||
|
:tag-list="form.properties.participateInfo[0]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="mt-2"
|
||||||
|
v-if="recordData.formType === 'DEFINE' && userSelectionType === 'FORM_USER'"
|
||||||
|
name="formUser"
|
||||||
|
:rules="[{ required: true, message: '请输入人员变量' }]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>单个字段可以采用:字段名,多个采用:字段名1,字段名2,字段名3</template>
|
||||||
|
<question-circle-outlined />
|
||||||
|
人员变量:
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
style="width: 50%"
|
||||||
|
v-model:value="userTypeForm.formUser"
|
||||||
|
allow-clear
|
||||||
|
@input="customFormUser"
|
||||||
|
@keyup.enter="customFormUser"
|
||||||
|
placeholder="请输入人员变量"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab="执行监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propExecutionListenerInfoRef"
|
||||||
|
:listener-value="form.properties.executionListenerInfo"
|
||||||
|
:default-listener-list="executionListenerInfo"
|
||||||
|
:listener-value-array="executionListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-layout-content>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="primary" style="margin-right: 8px" @click="save">保存</a-button>
|
||||||
|
<a-button @click="drawer = false">取消</a-button>
|
||||||
|
</template>
|
||||||
|
</xn-form-container>
|
||||||
|
<form-user-selector ref="formUserSelectorRef" :form-field-list="formFieldListValue" @click="userCallBack" />
|
||||||
|
<user-selector-plus
|
||||||
|
ref="userselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:user-page-api="selectorApiFunction.userPageApi"
|
||||||
|
:checked-user-list-api="selectorApiFunction.checkedUserListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="userCallBack"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import addNode from './addNode.vue'
|
||||||
|
import userSelectorPlus from '@/components/Selector/userSelectorPlus.vue'
|
||||||
|
import FormUserSelector from './prop/formUserSelector.vue'
|
||||||
|
import propTag from './prop/propTag.vue'
|
||||||
|
import { cloneDeep, isEmpty } from 'lodash-es'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import PropListenerInfo from './prop/propListenerInfo.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FormUserSelector,
|
||||||
|
addNode,
|
||||||
|
userSelectorPlus,
|
||||||
|
propTag,
|
||||||
|
PropListenerInfo
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
activeKey: '1',
|
||||||
|
form: {},
|
||||||
|
nodeLegal: false,
|
||||||
|
userTypeForm: {},
|
||||||
|
executionListenerInfo: cloneDeep(config.listener.serviceTaskExecutionListenerInfo),
|
||||||
|
// 用户选择类型
|
||||||
|
userSelectionType: '',
|
||||||
|
userSelectionTypeList: cloneDeep(config.serviceTaskConfig.userSelectionTypeList)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
this.form = {}
|
||||||
|
this.form = cloneDeep(this.childNode)
|
||||||
|
this.drawer = true
|
||||||
|
if (!isEmpty(this.form.properties.participateInfo)) {
|
||||||
|
this.userSelectionType = this.form.properties.participateInfo[0].key
|
||||||
|
// 如果包含表单内的人
|
||||||
|
const formUserObj = this.form.properties.participateInfo.find((f) => f.key === 'FORM_USER')
|
||||||
|
if (formUserObj) {
|
||||||
|
this.userTypeForm.formUser = formUserObj.value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.userSelectionType = 'USER'
|
||||||
|
this.userTypeForm = {}
|
||||||
|
this.nodeLegal = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editTitle() {
|
||||||
|
this.isEditTitle = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.nodeTitle.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveTitle() {
|
||||||
|
this.isEditTitle = false
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (isEmpty(this.nodeLegal)) {
|
||||||
|
this.form.properties.executionListenerInfo = this.$refs.propExecutionListenerInfoRef.selectedListenerList()
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delNode() {
|
||||||
|
this.$emit('update:modelValue', this.childNode.childNode)
|
||||||
|
},
|
||||||
|
selectHandle(data) {
|
||||||
|
this.$refs.userselectorPlus.showUserPlusModal(data)
|
||||||
|
},
|
||||||
|
// 选择器回调
|
||||||
|
userCallBack(value) {
|
||||||
|
if (isEmpty(value.label)) {
|
||||||
|
this.nodeLegal = true
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = false
|
||||||
|
}
|
||||||
|
if (this.userSelectionType === 'USER') {
|
||||||
|
this.form.properties.participateInfo[0] = value
|
||||||
|
} else if (this.userSelectionType === 'FORM_USER') {
|
||||||
|
this.form.properties.participateInfo = [
|
||||||
|
{
|
||||||
|
key: 'FORM_USER',
|
||||||
|
label: this.userSelectionTypeList.filter((item) => item.value === 'FORM_USER')[0].label,
|
||||||
|
value: value.model
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取tag标签的内容
|
||||||
|
getTagList(type) {
|
||||||
|
const participateInfo = this.form.properties.participateInfo
|
||||||
|
if (participateInfo.length > 0) {
|
||||||
|
const obj = participateInfo.find((item) => item.key === type)
|
||||||
|
if (isEmpty(obj.label)) {
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 选择抄送人员类型
|
||||||
|
selectionClick(value) {
|
||||||
|
this.form.properties.participateInfo = []
|
||||||
|
this.userSelectionType = value
|
||||||
|
this.nodeLegal = true
|
||||||
|
},
|
||||||
|
// 打开选择器
|
||||||
|
openSelector() {
|
||||||
|
let data = this.form.properties.participateInfo
|
||||||
|
if (this.userSelectionType === 'USER') {
|
||||||
|
this.$refs.userselectorPlus.showUserPlusModal(data)
|
||||||
|
} else if (this.userSelectionType === 'FORM_USER') {
|
||||||
|
this.$refs.formUserSelectorRef.showFormUserModal(data[0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 监听自定义表单人员输入
|
||||||
|
customFormUser() {
|
||||||
|
if (this.userTypeForm.formUser) {
|
||||||
|
this.form.properties.participateInfo = [
|
||||||
|
{
|
||||||
|
key: 'FORM_USER',
|
||||||
|
label: '表单内的人',
|
||||||
|
value: this.userTypeForm.formUser
|
||||||
|
}
|
||||||
|
]
|
||||||
|
this.nodeLegal = false
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toText(childNode) {
|
||||||
|
if (!isEmpty(childNode)) {
|
||||||
|
const participateInfo = childNode.properties.participateInfo
|
||||||
|
if (participateInfo.length > 0) {
|
||||||
|
let resultArray = []
|
||||||
|
if (participateInfo[0].label.indexOf(',') !== -1) {
|
||||||
|
resultArray = participateInfo[0].label.split(',')
|
||||||
|
} else {
|
||||||
|
resultArray.push(participateInfo[0].label)
|
||||||
|
}
|
||||||
|
return resultArray.toString()
|
||||||
|
} else {
|
||||||
|
// return '未选择抄送人';
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
48
src/components/XnWorkflow/nodes/startEvent.vue
Normal file
48
src/components/XnWorkflow/nodes/startEvent.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
// drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
form: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
this.form = JSON.parse(JSON.stringify(this.childNode))
|
||||||
|
this.isEditTitle = false
|
||||||
|
this.drawer = true
|
||||||
|
},
|
||||||
|
// editTitle() {
|
||||||
|
// this.isEditTitle = true;
|
||||||
|
// this.$nextTick(() => {
|
||||||
|
// this.$refs.nodeTitle.focus();
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// saveTitle() {
|
||||||
|
// this.isEditTitle = false;
|
||||||
|
// },
|
||||||
|
save() {
|
||||||
|
this.form.id = this.$TOOL.snowyUuid()
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
241
src/components/XnWorkflow/nodes/startTask.vue
Normal file
241
src/components/XnWorkflow/nodes/startTask.vue
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<div class="node-wrap-box start-node" @click="show">
|
||||||
|
<div class="title" style="background: #576a95">
|
||||||
|
<user-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||||
|
<span v-else class="placeholder">请配置字段与按钮</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="childNode.childNode" />
|
||||||
|
<xn-form-container
|
||||||
|
v-model:visible="drawer"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:width="700"
|
||||||
|
:body-style="{ 'padding-top': '0px' }"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="node-wrap-drawer__title">
|
||||||
|
<label v-if="!isEditTitle" @click="editTitle">
|
||||||
|
{{ form.title }}
|
||||||
|
<edit-outlined class="node-wrap-drawer-title-edit" />
|
||||||
|
</label>
|
||||||
|
<a-input
|
||||||
|
v-if="isEditTitle"
|
||||||
|
ref="nodeTitle"
|
||||||
|
v-model:value="form.title"
|
||||||
|
allow-clear
|
||||||
|
@blur="saveTitle"
|
||||||
|
@keyup.enter="saveTitle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-layout-content>
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="1" tab="按钮配置" force-render>
|
||||||
|
<prop-button-info
|
||||||
|
ref="propButtonInfo"
|
||||||
|
:button-info="selectedButtonInfo"
|
||||||
|
:show-button="startTaskDefaultButtonkey"
|
||||||
|
:no-checked="startTaskNoCheckedButtonkey"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab="字段配置" force-render v-if="recordData.formType === 'DESIGN'">
|
||||||
|
<prop-field-info ref="propFieldInfo" :field-info="fieldInfo" :form-field-list-value="formFieldListValue" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="3" tab="表单配置" force-render v-else>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">参与者可以填写的表单</span>
|
||||||
|
</div>
|
||||||
|
<a-form ref="startTaskFormRef" :model="formData" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
label="节点表单"
|
||||||
|
name="FORM_URL"
|
||||||
|
:rules="[{ required: true, message: '请输入节点表单组件地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.FORM_URL"
|
||||||
|
addon-before="src/views/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入节点表单组件地址"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-button
|
||||||
|
v-if="formData.FORM_URL"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="$refs.previewCustomFormRef.onOpen(formData.FORM_URL)"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="移动端节点表单"
|
||||||
|
name="MOBILE_FORM_URL"
|
||||||
|
:rules="[{ required: true, message: '请输入移动端节点表单组件地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.MOBILE_FORM_URL"
|
||||||
|
addon-before="pages/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入移动端节点表单组件地址"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="4" tab="执行监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propExecutionListenerInfoRef"
|
||||||
|
:listener-value="form.properties.executionListenerInfo"
|
||||||
|
:default-listener-list="executionListenerInfo"
|
||||||
|
:listener-value-array="executionListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="5" tab="节点监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propTaskListenerInfoRef"
|
||||||
|
:listener-value="form.properties.taskListenerInfo"
|
||||||
|
:default-listener-list="taskListenerInfo"
|
||||||
|
:listener-value-array="taskListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-layout-content>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="primary" style="margin-right: 8px" @click="save">保存</a-button>
|
||||||
|
<a-button @click="drawer = false">取消</a-button>
|
||||||
|
</template>
|
||||||
|
</xn-form-container>
|
||||||
|
<preview-custom-form ref="previewCustomFormRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import addNode from './addNode.vue'
|
||||||
|
import propButtonInfo from './prop/propButtonInfo.vue'
|
||||||
|
import propFieldInfo from './prop/propFieldInfo.vue'
|
||||||
|
import propListenerInfo from './prop/propListenerInfo.vue'
|
||||||
|
import PreviewCustomForm from '@/components/XnWorkflow/nodes/common/previewCustomForm.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNode,
|
||||||
|
propButtonInfo,
|
||||||
|
propFieldInfo,
|
||||||
|
propListenerInfo,
|
||||||
|
PreviewCustomForm
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
processConfigInfo: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
childNode: {},
|
||||||
|
drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
form: {},
|
||||||
|
activeKey: '1',
|
||||||
|
selectedButtonInfo: [],
|
||||||
|
executionListenerInfo: cloneDeep(config.listener.startTaskExecutionListenerInfo),
|
||||||
|
taskListenerInfo: cloneDeep(config.listener.userTaskTaskListenerInfo),
|
||||||
|
startTaskDefaultButtonkey: cloneDeep(config.button.startTaskDefaultButtonkey),
|
||||||
|
startTaskNoCheckedButtonkey: cloneDeep(config.button.startTaskNoCheckedButtonkey),
|
||||||
|
fieldInfo: [],
|
||||||
|
formData: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
this.form = cloneDeep(this.childNode)
|
||||||
|
this.isEditTitle = false
|
||||||
|
this.drawer = true
|
||||||
|
this.selectedButtonInfo = this.form.properties.buttonInfo
|
||||||
|
this.fieldInfo = this.form.properties.fieldInfo
|
||||||
|
// 初始化自定义表单字段值
|
||||||
|
this.initFormInfo()
|
||||||
|
},
|
||||||
|
initFormInfo() {
|
||||||
|
const processConfigInfo = cloneDeep(this.processConfigInfo)
|
||||||
|
if (!this.form.properties.formInfo.find((f) => f.key === 'FORM_URL')) {
|
||||||
|
this.formData.FORM_URL = processConfigInfo.processStartTaskFormUrl
|
||||||
|
} else {
|
||||||
|
this.formData.FORM_URL = this.form.properties.formInfo.find((f) => f.key === 'FORM_URL').value
|
||||||
|
}
|
||||||
|
if (!this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL')) {
|
||||||
|
this.formData.MOBILE_FORM_URL = processConfigInfo.processStartTaskMobileFormUrl
|
||||||
|
} else {
|
||||||
|
this.formData.MOBILE_FORM_URL = this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL').value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editTitle() {
|
||||||
|
this.isEditTitle = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.nodeTitle.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveTitle() {
|
||||||
|
this.isEditTitle = false
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
this.form.id = this.$TOOL.snowyUuid()
|
||||||
|
this.form.properties.buttonInfo = this.$refs.propButtonInfo.selectedButtonKeyList()
|
||||||
|
this.form.properties.executionListenerInfo = this.$refs.propExecutionListenerInfoRef.selectedListenerList()
|
||||||
|
this.form.properties.taskListenerInfo = this.$refs.propTaskListenerInfoRef.selectedListenerList()
|
||||||
|
if (this.recordData.formType === 'DESIGN') {
|
||||||
|
this.form.properties.fieldInfo = this.$refs.propFieldInfo.selectedFieldList()
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
} else {
|
||||||
|
this.$refs.startTaskFormRef
|
||||||
|
.validate()
|
||||||
|
.then((values) => {
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.form.properties.formInfo = cloneDeep(config.nodeModel.formInfo)
|
||||||
|
this.form.properties.formInfo.find((f) => f.key === 'FORM_URL').value = values.FORM_URL
|
||||||
|
this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL').value = values.MOBILE_FORM_URL
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
})
|
||||||
|
.catch((err) => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
toText(childNode) {
|
||||||
|
if (childNode.dataLegal) {
|
||||||
|
return '系统自动配置参与人'
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.font-span {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
788
src/components/XnWorkflow/nodes/userTask.vue
Normal file
788
src/components/XnWorkflow/nodes/userTask.vue
Normal file
@@ -0,0 +1,788 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrap">
|
||||||
|
<div class="node-wrap-box" @click="show">
|
||||||
|
<div class="title" style="background: #ff943e">
|
||||||
|
<user-outlined class="icon" />
|
||||||
|
<span>{{ childNode.title }}</span>
|
||||||
|
<close-outlined class="close" @click.stop="delNode()" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||||
|
<span v-else class="placeholder">请选择</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-node v-model="childNode.childNode" />
|
||||||
|
|
||||||
|
<!-- 抽屉 -->
|
||||||
|
<xn-form-container
|
||||||
|
v-model:visible="drawer"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:width="700"
|
||||||
|
:body-style="{ 'padding-top': '0px' }"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="node-wrap-drawer__title">
|
||||||
|
<label v-if="!isEditTitle" @click="editTitle"
|
||||||
|
>{{ form.title }}<edit-outlined class="node-wrap-drawer-title-edit"
|
||||||
|
/></label>
|
||||||
|
<a-input
|
||||||
|
v-if="isEditTitle"
|
||||||
|
ref="nodeTitle"
|
||||||
|
v-model:value="form.title"
|
||||||
|
allow-clear
|
||||||
|
@blur="saveTitle"
|
||||||
|
@keyup.enter="saveTitle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-layout-content>
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="1" tab="人员配置" force-render>
|
||||||
|
<div v-show="!nodeLegal" style="margin-bottom: 10px">
|
||||||
|
<a-alert message="请配置节点相关人员!" type="error" />
|
||||||
|
</div>
|
||||||
|
<a-form layout="vertical" :model="userTypeForm" ref="userTypeFormRef">
|
||||||
|
<div class="mb-3">
|
||||||
|
<span class="left-span-label">选择审批人员类型</span>
|
||||||
|
</div>
|
||||||
|
<a-radio-group v-model:value="userSelectionType" style="width: 100%">
|
||||||
|
<a-row style="padding-bottom: 10px">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="ORG" @click="selectionClick('ORG')">机构</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="ROLE" @click="selectionClick('ROLE')">角色</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="POSITION" @click="selectionClick('POSITION')">职位</a-radio>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="padding-bottom: 10px">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="ORG_LEADER" @click="selectionClick('ORG_LEADER')">部门主管</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="SUPERVISOR" @click="selectionClick('SUPERVISOR')">上级主管</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="MUL_LEVEL_SUPERVISOR" @click="selectionClick('MUL_LEVEL_SUPERVISOR')"
|
||||||
|
>连续多级主管</a-radio
|
||||||
|
>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="padding-bottom: 10px">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="USER" @click="selectionClick('USER')">用户</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="FORM_USER" @click="selectionClick('FORM_USER')">表单内的人</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="FORM_USER_SUPERVISOR" @click="selectionClick('FORM_USER_SUPERVISOR')"
|
||||||
|
>表单内的人上级主管</a-radio
|
||||||
|
>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="padding-bottom: 10px">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio
|
||||||
|
value="FORM_USER_MUL_LEVEL_SUPERVISOR"
|
||||||
|
@click="selectionClick('FORM_USER_MUL_LEVEL_SUPERVISOR')"
|
||||||
|
>表单内的人连续多级主管</a-radio
|
||||||
|
>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-radio value="INITIATOR" @click="selectionClick('INITIATOR')">发起人</a-radio>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8" />
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-form-item
|
||||||
|
class="mt-2"
|
||||||
|
v-if="
|
||||||
|
recordData.formType === 'DEFINE' &&
|
||||||
|
(userSelectionType === 'FORM_USER' ||
|
||||||
|
userSelectionType === 'FORM_USER_SUPERVISOR' ||
|
||||||
|
userSelectionType === 'FORM_USER_MUL_LEVEL_SUPERVISOR')
|
||||||
|
"
|
||||||
|
name="formUser"
|
||||||
|
:rules="[{ required: true, message: '请输入人员变量' }]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>单个字段可以采用:字段名,多个采用:字段名1,字段名2,字段名3</template>
|
||||||
|
<question-circle-outlined />
|
||||||
|
人员变量:
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
style="width: 50%"
|
||||||
|
v-model:value="userTypeForm.formUser"
|
||||||
|
allow-clear
|
||||||
|
@input="customFormUser"
|
||||||
|
@keyup.enter="customFormUser"
|
||||||
|
placeholder="请输入人员变量"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<div v-if="seleType" class="mt-2">
|
||||||
|
<a-button type="primary" @click="openSelector" size="small">
|
||||||
|
<template #icon>
|
||||||
|
<search-outlined />
|
||||||
|
</template>
|
||||||
|
选择
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<prop-tag
|
||||||
|
v-if="form.properties.participateInfo.length > 0 && form.properties.participateInfo[0].value !== ''"
|
||||||
|
:tag-list="form.properties.participateInfo[0]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<a-form-item
|
||||||
|
class="mt-2"
|
||||||
|
v-if="
|
||||||
|
userSelectionType === 'SUPERVISOR' ||
|
||||||
|
userSelectionType === 'FORM_USER_SUPERVISOR' ||
|
||||||
|
userSelectionType === 'MUL_LEVEL_SUPERVISOR' ||
|
||||||
|
userSelectionType === 'FORM_USER_MUL_LEVEL_SUPERVISOR'
|
||||||
|
"
|
||||||
|
:label="
|
||||||
|
userSelectionType === 'SUPERVISOR' || userSelectionType === 'FORM_USER_SUPERVISOR'
|
||||||
|
? '上级主管级别'
|
||||||
|
: '审批终点主管级别'
|
||||||
|
"
|
||||||
|
name="levelSupervisor"
|
||||||
|
:rules="[{ required: true, message: '请选择主管级别' }]"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
style="width: 50%"
|
||||||
|
v-model:value="userTypeForm.levelSupervisor"
|
||||||
|
placeholder="请选择主管级别"
|
||||||
|
@change="levelSupervisorChange"
|
||||||
|
:options="levelSupervisorList"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab="审批配置" force-render>
|
||||||
|
<span class="left-span-label">配置审批方式</span>
|
||||||
|
<a-form label-position="top" layout="vertical" class="mt-2">
|
||||||
|
<div v-show="!nodeLegal" style="margin-bottom: 10px">
|
||||||
|
<a-alert message="请配置节点相关人员!" type="error" />
|
||||||
|
</div>
|
||||||
|
<a-form-item label="任务节点类型">
|
||||||
|
<a-radio-group v-model:value="form.properties.configInfo.userTaskType">
|
||||||
|
<a-radio
|
||||||
|
v-for="userTaskType in userTaskTypeList"
|
||||||
|
:key="userTaskType.value"
|
||||||
|
:value="userTaskType.value"
|
||||||
|
>{{ userTaskType.label }}</a-radio
|
||||||
|
>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="审批退回类型">
|
||||||
|
<a-radio-group v-model:value="form.properties.configInfo.userTaskRejectType">
|
||||||
|
<a-radio
|
||||||
|
v-for="userTaskRejectType in userTaskRejectTypeList"
|
||||||
|
:key="userTaskRejectType.value"
|
||||||
|
:value="userTaskRejectType.value"
|
||||||
|
>{{ userTaskRejectType.label }}
|
||||||
|
</a-radio>
|
||||||
|
<br />
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="多人审批类型">
|
||||||
|
<a-radio-group v-model:value="form.properties.configInfo.userTaskMulApproveType">
|
||||||
|
<a-radio
|
||||||
|
v-for="userTaskMulApproveType in userTaskMulApproveTypeList"
|
||||||
|
:key="userTaskMulApproveType.value"
|
||||||
|
:value="userTaskMulApproveType.value"
|
||||||
|
>{{ userTaskMulApproveType.label }}
|
||||||
|
</a-radio>
|
||||||
|
<br />
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="审批人员为空时">
|
||||||
|
<a-radio-group v-model:value="form.properties.configInfo.userTaskEmptyApproveType">
|
||||||
|
<a-radio
|
||||||
|
v-for="userTaskEmptyApproveType in userTaskEmptyApproveTypeList"
|
||||||
|
:key="userTaskEmptyApproveType.value"
|
||||||
|
:value="userTaskEmptyApproveType.value"
|
||||||
|
@change="userTaskEmptyApproveTypeChange"
|
||||||
|
>{{ userTaskEmptyApproveType.label }}
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="form.properties.configInfo.userTaskEmptyApproveType === 'AUTO_TURN'"
|
||||||
|
label="审批人为空时转交人"
|
||||||
|
>
|
||||||
|
<p><a-button type="primary" size="small" @click="seleApproveUser">选择人员</a-button></p>
|
||||||
|
<a-tag
|
||||||
|
v-if="form.properties.configInfo.userTaskEmptyApproveUserArray.length > 0"
|
||||||
|
color="orange"
|
||||||
|
closable
|
||||||
|
@close="closeApproveUserTag"
|
||||||
|
>
|
||||||
|
{{ form.properties.configInfo.userTaskEmptyApproveUserArray[0].name }}</a-tag
|
||||||
|
>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="3" tab="按钮配置" force-render>
|
||||||
|
<prop-button-info
|
||||||
|
ref="propButtonInfo"
|
||||||
|
:button-info="selectedButtonInfo"
|
||||||
|
:show-button="defaultButtonkey"
|
||||||
|
:no-checked="userTaskNoCheckedButtonkey"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="4" tab="字段配置" force-render v-if="recordData.formType === 'DESIGN'">
|
||||||
|
<prop-field-info
|
||||||
|
ref="propFieldInfo"
|
||||||
|
:field-info="fieldInfo"
|
||||||
|
:default-field-model="defaultFieldModel"
|
||||||
|
:form-field-list-value="formFieldListValue"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="5" tab="表单配置" force-render v-else>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">参与者可以使用的表单</span>
|
||||||
|
</div>
|
||||||
|
<a-form ref="userTaskFormRef" :model="formData" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
label="节点表单"
|
||||||
|
name="FORM_URL"
|
||||||
|
:rules="[{ required: true, message: '请输入节点表单组件地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.FORM_URL"
|
||||||
|
addon-before="src/views/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入节点表单组件地址"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-button
|
||||||
|
v-if="formData.FORM_URL"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="$refs.previewCustomFormRef.onOpen(formData.FORM_URL)"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="移动端节点表单"
|
||||||
|
name="MOBILE_FORM_URL"
|
||||||
|
:rules="[{ required: true, message: '请输入移动端节点表单组件地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.MOBILE_FORM_URL"
|
||||||
|
addon-before="pages/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入移动端节点表单组件地址"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="6" tab="执行监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propExecutionListenerInfoRef"
|
||||||
|
:listener-value="form.properties.executionListenerInfo"
|
||||||
|
:default-listener-list="executionListenerInfo"
|
||||||
|
:listener-value-array="executionListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="7" tab="节点监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propTaskListenerInfoRef"
|
||||||
|
:listener-value="form.properties.taskListenerInfo"
|
||||||
|
:default-listener-list="taskListenerInfo"
|
||||||
|
:listener-value-array="taskListenerArray"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-layout-content>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="primary" style="margin-right: 8px" @click="save">保存</a-button>
|
||||||
|
<a-button @click="drawer = false">取消</a-button>
|
||||||
|
</template>
|
||||||
|
</xn-form-container>
|
||||||
|
<role-selector-plus
|
||||||
|
ref="roleselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:role-page-api="selectorApiFunction.rolePageApi"
|
||||||
|
:checked-role-list-api="selectorApiFunction.checkedRoleListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<user-selector-plus
|
||||||
|
ref="userselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:user-page-api="selectorApiFunction.userPageApi"
|
||||||
|
:checked-user-list-api="selectorApiFunction.checkedUserListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<pos-selector-plus
|
||||||
|
ref="posselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:pos-page-api="selectorApiFunction.posPageApi"
|
||||||
|
:checked-pos-list-api="selectorApiFunction.checkedPosListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<org-selector-plus
|
||||||
|
ref="orgselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:org-page-api="selectorApiFunction.orgPageApi"
|
||||||
|
:checked-org-list-api="selectorApiFunction.checkedOrgListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<form-user-selector ref="formuserselector" :form-field-list="formFieldListValue" @click="formuserClick" />
|
||||||
|
<user-selector-plus
|
||||||
|
ref="userselectorApprove"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:user-page-api="selectorApiFunction.userPageApi"
|
||||||
|
:checked-user-list-api="selectorApiFunction.checkedUserListApi"
|
||||||
|
:radio-model="true"
|
||||||
|
@onBack="callBackApprove"
|
||||||
|
/>
|
||||||
|
<preview-custom-form ref="previewCustomFormRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep, isEmpty } from 'lodash-es'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import addNode from './addNode.vue'
|
||||||
|
import propButtonInfo from './prop/propButtonInfo.vue'
|
||||||
|
import propFieldInfo from './prop/propFieldInfo.vue'
|
||||||
|
import propListenerInfo from './prop/propListenerInfo.vue'
|
||||||
|
import formUserSelector from './prop/formUserSelector.vue'
|
||||||
|
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
|
||||||
|
import userSelectorPlus from '@/components/Selector/userSelectorPlus.vue'
|
||||||
|
import posSelectorPlus from '@/components/Selector/posSelectorPlus.vue'
|
||||||
|
import orgSelectorPlus from '@/components/Selector/orgSelectorPlus.vue'
|
||||||
|
import propTag from './prop/propTag.vue'
|
||||||
|
import PreviewCustomForm from '@/components/XnWorkflow/nodes/common/previewCustomForm.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
addNode,
|
||||||
|
propButtonInfo,
|
||||||
|
propFieldInfo,
|
||||||
|
propListenerInfo,
|
||||||
|
formUserSelector,
|
||||||
|
roleSelectorPlus,
|
||||||
|
userSelectorPlus,
|
||||||
|
posSelectorPlus,
|
||||||
|
orgSelectorPlus,
|
||||||
|
propTag,
|
||||||
|
PreviewCustomForm
|
||||||
|
},
|
||||||
|
// inject: ['select'],
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
processConfigInfo: { type: Object, default: () => {} },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodeLegal: false,
|
||||||
|
childNode: {},
|
||||||
|
drawer: false,
|
||||||
|
isEditTitle: false,
|
||||||
|
form: {},
|
||||||
|
selectedButtonInfo: [],
|
||||||
|
executionListenerInfo: cloneDeep(config.listener.userTaskExecutionListenerInfo),
|
||||||
|
taskListenerInfo: cloneDeep(config.listener.userTaskTaskListenerInfo),
|
||||||
|
activeKey: '1',
|
||||||
|
defaultButtonkey: cloneDeep(config.button.userTaskDefaultButtonkey),
|
||||||
|
userTaskNoCheckedButtonkey: cloneDeep(config.button.userTaskNoCheckedButtonkey),
|
||||||
|
defaultFieldModel: cloneDeep(config.field.userTaskFieldModel),
|
||||||
|
fieldInfo: [],
|
||||||
|
tagList: [],
|
||||||
|
seleType: false,
|
||||||
|
// 新配置
|
||||||
|
// 用户选择类型
|
||||||
|
userSelectionType: '',
|
||||||
|
userSelectionTypeList: cloneDeep(config.userTaskConfig.userSelectionTypeList),
|
||||||
|
// 主管级别
|
||||||
|
levelSupervisorList: cloneDeep(config.userTaskConfig.levelSupervisorList),
|
||||||
|
// 任务节点类型
|
||||||
|
userTaskTypeList: cloneDeep(config.userTaskConfig.userTaskTypeList),
|
||||||
|
// 审批退回类型
|
||||||
|
userTaskRejectTypeList: cloneDeep(config.userTaskConfig.userTaskRejectTypeList),
|
||||||
|
// 多人审批时类型
|
||||||
|
userTaskMulApproveTypeList: cloneDeep(config.userTaskConfig.userTaskMulApproveTypeList),
|
||||||
|
// 审批人为空时类型
|
||||||
|
userTaskEmptyApproveTypeList: cloneDeep(config.userTaskConfig.userTaskEmptyApproveTypeList),
|
||||||
|
// 审批人为空时转交人
|
||||||
|
userTaskEmptyApproveUser: '',
|
||||||
|
formData: {},
|
||||||
|
userTypeForm: {
|
||||||
|
levelSupervisor: '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.childNode = this.modelValue
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
this.form = {}
|
||||||
|
this.form = cloneDeep(this.childNode)
|
||||||
|
this.selectedButtonInfo = this.form.properties.buttonInfo
|
||||||
|
this.fieldInfo = this.form.properties.fieldInfo
|
||||||
|
this.drawer = true
|
||||||
|
// 设置默认选中的用户类型单选 userSelectionType
|
||||||
|
if (this.form.properties.participateInfo.length > 0) {
|
||||||
|
this.userSelectionType = this.form.properties.participateInfo[0].key
|
||||||
|
// 如果包含表单内的人
|
||||||
|
const formUserObj = this.form.properties.participateInfo.find((f) => f.key === 'FORM_USER')
|
||||||
|
if (formUserObj) {
|
||||||
|
this.userTypeForm.formUser = formUserObj.value
|
||||||
|
}
|
||||||
|
// 如果是直属主管或者连续多级审批的
|
||||||
|
this.form.properties.participateInfo.forEach((item) => {
|
||||||
|
// 如果只是普通的主管以及级别
|
||||||
|
if (item.key === 'SUPERVISOR' || item.key === 'MUL_LEVEL_SUPERVISOR') {
|
||||||
|
this.userTypeForm.levelSupervisor = item.value
|
||||||
|
}
|
||||||
|
// 如果是表单内的人的主管以及级别
|
||||||
|
if (item.key === 'FORM_USER_SUPERVISOR' || item.key === 'FORM_USER_MUL_LEVEL_SUPERVISOR') {
|
||||||
|
this.userTypeForm.levelSupervisor = item.value
|
||||||
|
this.userTypeForm.formUser = JSON.parse(item.extJson).value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 设置发起人
|
||||||
|
this.userSelectionType = 'INITIATOR'
|
||||||
|
this.form.properties.participateInfo = [{ key: 'INITIATOR', label: '发起人', value: '发起人:${INITIATOR}' }]
|
||||||
|
}
|
||||||
|
// 校验状态
|
||||||
|
this.isNodeLegal()
|
||||||
|
// 设置第一个选项卡打开选择器的按钮是否展示
|
||||||
|
if (
|
||||||
|
this.userSelectionType === 'USER' ||
|
||||||
|
this.userSelectionType === 'ROLE' ||
|
||||||
|
this.userSelectionType === 'ORG' ||
|
||||||
|
this.userSelectionType === 'POSITION' ||
|
||||||
|
this.userSelectionType === 'FORM_USER' ||
|
||||||
|
this.userSelectionType === 'FORM_USER_SUPERVISOR' ||
|
||||||
|
this.userSelectionType === 'FORM_USER_MUL_LEVEL_SUPERVISOR'
|
||||||
|
) {
|
||||||
|
this.seleType = true
|
||||||
|
}
|
||||||
|
// 初始化自定义表单字段值
|
||||||
|
this.initFormInfo()
|
||||||
|
},
|
||||||
|
initFormInfo() {
|
||||||
|
const processConfigInfo = cloneDeep(this.processConfigInfo)
|
||||||
|
if (!this.form.properties.formInfo.find((f) => f.key === 'FORM_URL')) {
|
||||||
|
this.formData.FORM_URL = processConfigInfo.processUserTaskFormUrl
|
||||||
|
} else {
|
||||||
|
this.formData.FORM_URL = this.form.properties.formInfo.find((f) => f.key === 'FORM_URL').value
|
||||||
|
}
|
||||||
|
if (!this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL')) {
|
||||||
|
this.formData.MOBILE_FORM_URL = processConfigInfo.processUserTaskMobileFormUrl
|
||||||
|
} else {
|
||||||
|
this.formData.MOBILE_FORM_URL = this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL').value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editTitle() {
|
||||||
|
this.isEditTitle = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.nodeTitle.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveTitle() {
|
||||||
|
this.isEditTitle = false
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (isEmpty(this.form.id)) {
|
||||||
|
this.form.id = this.$TOOL.snowyUuid()
|
||||||
|
}
|
||||||
|
this.form.properties.buttonInfo = this.$refs.propButtonInfo.selectedButtonKeyList()
|
||||||
|
if (this.recordData.formType === 'DESIGN') {
|
||||||
|
this.form.properties.fieldInfo = this.$refs.propFieldInfo.selectedFieldList()
|
||||||
|
}
|
||||||
|
if (this.isNodeLegal()) {
|
||||||
|
this.form.properties.executionListenerInfo = this.$refs.propExecutionListenerInfoRef.selectedListenerList()
|
||||||
|
this.form.properties.taskListenerInfo = this.$refs.propTaskListenerInfoRef.selectedListenerList()
|
||||||
|
if (this.recordData.formType === 'DESIGN') {
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
} else {
|
||||||
|
this.$refs.userTaskFormRef
|
||||||
|
.validate()
|
||||||
|
.then((values) => {
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.form.properties.formInfo = cloneDeep(config.nodeModel.formInfo)
|
||||||
|
this.form.properties.formInfo.find((f) => f.key === 'FORM_URL').value = values.FORM_URL
|
||||||
|
this.form.properties.formInfo.find((f) => f.key === 'MOBILE_FORM_URL').value = values.MOBILE_FORM_URL
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
})
|
||||||
|
.catch((err) => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectionClick(value) {
|
||||||
|
const type = value
|
||||||
|
const result = []
|
||||||
|
this.form.properties.participateInfo = []
|
||||||
|
const obj = {}
|
||||||
|
obj.key = type
|
||||||
|
obj.label = ''
|
||||||
|
obj.value = ''
|
||||||
|
if (type === 'ORG_LEADER' || type === 'INITIATOR') {
|
||||||
|
// 部门主管 发起人
|
||||||
|
this.seleType = false
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === type)[0].label
|
||||||
|
obj.value = this.userSelectionTypeList.filter((item) => item.value === type)[0].label + ':${' + type + '}'
|
||||||
|
} else if (type === 'SUPERVISOR' || type === 'MUL_LEVEL_SUPERVISOR') {
|
||||||
|
this.seleType = false
|
||||||
|
// 直属主管 连续多级审批 默认选中直接主管
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === type)[0].label
|
||||||
|
obj.value = this.userTypeForm.levelSupervisor
|
||||||
|
} else if (type === 'FORM_USER') {
|
||||||
|
this.seleType = this.recordData.formType === 'DESIGN'
|
||||||
|
} else if (type === 'FORM_USER_SUPERVISOR' || type === 'FORM_USER_MUL_LEVEL_SUPERVISOR') {
|
||||||
|
this.seleType = this.recordData.formType === 'DESIGN'
|
||||||
|
} else {
|
||||||
|
this.seleType = true
|
||||||
|
}
|
||||||
|
result.push(obj)
|
||||||
|
this.form.properties.participateInfo = result
|
||||||
|
// 清空自定义表单内的人员输入框
|
||||||
|
this.userTypeForm.formUser = ''
|
||||||
|
this.isNodeLegal()
|
||||||
|
},
|
||||||
|
// 打开各种选择器
|
||||||
|
openSelector() {
|
||||||
|
let type = this.userSelectionType
|
||||||
|
let data = this.form.properties.participateInfo
|
||||||
|
if (type === 'ROLE') {
|
||||||
|
this.$refs.roleselectorPlus.showRolePlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'USER') {
|
||||||
|
this.$refs.userselectorPlus.showUserPlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'POSITION') {
|
||||||
|
this.$refs.posselectorPlus.showPosPlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'ORG') {
|
||||||
|
this.$refs.orgselectorPlus.showOrgPlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'FORM_USER' || type === 'FORM_USER_SUPERVISOR' || type === 'FORM_USER_MUL_LEVEL_SUPERVISOR') {
|
||||||
|
this.$refs.formuserselector.showFormUserModal(data[0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delNode() {
|
||||||
|
// eslint-disable-next-line vue/require-explicit-emits
|
||||||
|
this.$emit('update:modelValue', this.childNode.childNode)
|
||||||
|
// eslint-disable-next-line vue/require-explicit-emits
|
||||||
|
this.$emit('deleteParalle')
|
||||||
|
},
|
||||||
|
delUser(index) {
|
||||||
|
this.form.nodeUserList.splice(index, 1)
|
||||||
|
},
|
||||||
|
// 选择转交人
|
||||||
|
seleApproveUser() {
|
||||||
|
const data = [this.form.properties.configInfo.userTaskEmptyApproveUser]
|
||||||
|
this.$refs.userselectorApprove.showUserPlusModal(data)
|
||||||
|
},
|
||||||
|
// 选择转交人回调
|
||||||
|
callBackApprove(value) {
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUser = value[0].id
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUserArray = value
|
||||||
|
},
|
||||||
|
// 清除转交人
|
||||||
|
closeApproveUserTag() {
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUser = ''
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUserArray = []
|
||||||
|
},
|
||||||
|
// 点击自动转交给某人单选时
|
||||||
|
userTaskEmptyApproveTypeChange(value) {
|
||||||
|
const type = value.target.value
|
||||||
|
if (type === 'AUTO_TURN') {
|
||||||
|
// 赋值默认的管理员
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUser = JSON.parse(this.recordData.extJson)[0].id
|
||||||
|
this.form.properties.configInfo.userTaskEmptyApproveUserArray = JSON.parse(this.recordData.extJson)
|
||||||
|
} else {
|
||||||
|
// 置为空
|
||||||
|
this.closeApproveUserTag()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 校验节点是否合法
|
||||||
|
isNodeLegal() {
|
||||||
|
if (this.form.properties.participateInfo.length > 0) {
|
||||||
|
if (isEmpty(this.form.properties.participateInfo[0].label)) {
|
||||||
|
this.nodeLegal = false
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
// 再看看默认转交人是否OK
|
||||||
|
// eslint-disable-next-line no-lonely-if
|
||||||
|
if (this.form.properties.configInfo.userTaskEmptyApproveType === 'AUTO_TURN') {
|
||||||
|
if (!isEmpty(this.form.properties.configInfo.userTaskEmptyApproveUser)) {
|
||||||
|
this.nodeLegal = true
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectHandle(type, data) {
|
||||||
|
const value = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
data.forEach((item) => {
|
||||||
|
value.push(item.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (type === 1) {
|
||||||
|
this.$refs.userselector.showUserModal(value)
|
||||||
|
}
|
||||||
|
if (type === 2) {
|
||||||
|
this.$refs.roleselector.showRoleModal(value)
|
||||||
|
}
|
||||||
|
// this.select(type, data)
|
||||||
|
},
|
||||||
|
// 公共回调方法,因为它们返回的数据结构一致
|
||||||
|
callBack(value) {
|
||||||
|
if (value.label === []) {
|
||||||
|
this.nodeLegal = false
|
||||||
|
} else {
|
||||||
|
this.form.properties.participateInfo[0] = value
|
||||||
|
}
|
||||||
|
this.isNodeLegal()
|
||||||
|
},
|
||||||
|
// 表单内的人选择器回调
|
||||||
|
formuserClick(value) {
|
||||||
|
// eslint-disable-next-line no-undefined
|
||||||
|
if (value) {
|
||||||
|
const participateInfo = this.form.properties.participateInfo[0].key
|
||||||
|
const result = []
|
||||||
|
const obj = {}
|
||||||
|
if (participateInfo === 'FORM_USER_SUPERVISOR' || participateInfo === 'FORM_USER_MUL_LEVEL_SUPERVISOR') {
|
||||||
|
obj.key = participateInfo
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === participateInfo)[0].label
|
||||||
|
obj.value = this.userTypeForm.levelSupervisor
|
||||||
|
const extJson = {
|
||||||
|
key: 'FORM_USER',
|
||||||
|
label: '表单内的人',
|
||||||
|
value: value.model
|
||||||
|
}
|
||||||
|
obj.extJson = JSON.stringify(extJson)
|
||||||
|
} else {
|
||||||
|
obj.key = 'FORM_USER'
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === 'FORM_USER')[0].label
|
||||||
|
obj.value = value.model
|
||||||
|
}
|
||||||
|
result.push(obj)
|
||||||
|
this.form.properties.participateInfo = result
|
||||||
|
} else {
|
||||||
|
this.nodeLegal = false
|
||||||
|
}
|
||||||
|
this.isNodeLegal()
|
||||||
|
},
|
||||||
|
// 监听自定义表单人员输入
|
||||||
|
customFormUser() {
|
||||||
|
if (this.userTypeForm.formUser) {
|
||||||
|
const participateInfo = this.form.properties.participateInfo[0].key
|
||||||
|
const result = []
|
||||||
|
const obj = {}
|
||||||
|
if (participateInfo === 'FORM_USER_SUPERVISOR' || participateInfo === 'FORM_USER_MUL_LEVEL_SUPERVISOR') {
|
||||||
|
obj.key = participateInfo
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === participateInfo)[0].label
|
||||||
|
obj.value = this.userTypeForm.levelSupervisor
|
||||||
|
const extJson = {
|
||||||
|
key: 'FORM_USER',
|
||||||
|
label: '表单内的人',
|
||||||
|
value: this.userTypeForm.formUser
|
||||||
|
}
|
||||||
|
obj.extJson = JSON.stringify(extJson)
|
||||||
|
} else {
|
||||||
|
obj.key = 'FORM_USER'
|
||||||
|
obj.label = this.userSelectionTypeList.filter((item) => item.value === 'FORM_USER')[0].label
|
||||||
|
obj.value = this.userTypeForm.formUser
|
||||||
|
}
|
||||||
|
result.push(obj)
|
||||||
|
this.form.properties.participateInfo = result
|
||||||
|
this.nodeLegal = false
|
||||||
|
} else {
|
||||||
|
this.form.properties.participateInfo = []
|
||||||
|
this.nodeLegal = true
|
||||||
|
}
|
||||||
|
this.isNodeLegal()
|
||||||
|
},
|
||||||
|
// 选择主管层级
|
||||||
|
levelSupervisorChange(value) {
|
||||||
|
this.form.properties.participateInfo[0].value = value
|
||||||
|
},
|
||||||
|
toText(childNode) {
|
||||||
|
if (!isEmpty(childNode)) {
|
||||||
|
const strArray = this.toTag(childNode.properties.participateInfo[0])
|
||||||
|
if (strArray.length > 0) {
|
||||||
|
let value = ''
|
||||||
|
// eslint-disable-next-line no-plusplus
|
||||||
|
for (let i = 0; i < strArray.length; i++) {
|
||||||
|
if (strArray.length === i + 1) {
|
||||||
|
value = value + strArray[i]
|
||||||
|
} else {
|
||||||
|
value = value + strArray[i] + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toTag(participateInfo) {
|
||||||
|
// eslint-disable-next-line no-undefined
|
||||||
|
if (participateInfo === undefined) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (isEmpty(participateInfo.label)) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
let resultArray = []
|
||||||
|
if (participateInfo.label.indexOf(',') !== -1) {
|
||||||
|
resultArray = participateInfo.label.split(',')
|
||||||
|
} else {
|
||||||
|
resultArray.push(participateInfo.label)
|
||||||
|
}
|
||||||
|
return resultArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
180
src/components/XnWorkflow/nodes/utils/index.js
Normal file
180
src/components/XnWorkflow/nodes/utils/index.js
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
// 根据自定义的此节点定义的,转换表单的隐藏、必填、禁用
|
||||||
|
export default {
|
||||||
|
// 设置字段显示与否
|
||||||
|
convSettingsField(formJson, fieldInfo) {
|
||||||
|
// 递归遍历控件树
|
||||||
|
const traverse = (array) => {
|
||||||
|
array.forEach((element) => {
|
||||||
|
if (element.type === 'grid' || element.type === 'tabs') {
|
||||||
|
// 栅格布局 and 标签页
|
||||||
|
element.columns.forEach((item) => {
|
||||||
|
traverse(item.list)
|
||||||
|
})
|
||||||
|
} else if (element.type === 'card') {
|
||||||
|
// 卡片布局 and 动态表格
|
||||||
|
traverse(element.list)
|
||||||
|
} else if (element.type === 'table') {
|
||||||
|
// 表格布局
|
||||||
|
element.trs.forEach((item) => {
|
||||||
|
item.tds.forEach((val) => {
|
||||||
|
traverse(val.list)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const type = element.type
|
||||||
|
if ((type !== 'alert') & (type !== 'text') & (type !== 'divider') & (type !== 'html')) {
|
||||||
|
const obj = fieldInfo.find((i) => i.key === element.model)
|
||||||
|
if (obj) {
|
||||||
|
element.options.hidden = obj.value === 'HIDE' // ? true : false
|
||||||
|
element.options.disabled = obj.value === 'READ' // ? true : false
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('程序检测到功能字段配置发生了异常,依然能正常使用,请联系管理员进行流程重新配置部署!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(formJson.list)
|
||||||
|
return formJson
|
||||||
|
},
|
||||||
|
// 掏出所有字段,返回列表
|
||||||
|
getListField(data) {
|
||||||
|
let result = []
|
||||||
|
// 递归遍历控件树
|
||||||
|
const traverse = (array) => {
|
||||||
|
array.forEach((element) => {
|
||||||
|
if (element.type === 'grid' || element.type === 'tabs') {
|
||||||
|
// 栅格布局 and 标签页
|
||||||
|
element.columns.forEach((item) => {
|
||||||
|
traverse(item.list)
|
||||||
|
})
|
||||||
|
} else if (element.type === 'card') {
|
||||||
|
// 卡片布局 and 动态表格
|
||||||
|
traverse(element.list)
|
||||||
|
} else if (element.type === 'table') {
|
||||||
|
// 表格布局
|
||||||
|
element.trs.forEach((item) => {
|
||||||
|
item.tds.forEach((val) => {
|
||||||
|
traverse(val.list)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const type = element.type
|
||||||
|
// 排除一些
|
||||||
|
if ((type !== 'alert') & (type !== 'text') & (type !== 'divider') & (type !== 'batch') & (type !== 'html')) {
|
||||||
|
result.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(data)
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
// 取节点(用到按钮权限跟字段),并且给节点set一个json,也就是我们的审批记录
|
||||||
|
getChildNode(modelJson, activityId, dataList) {
|
||||||
|
let result = {}
|
||||||
|
let traverse = (obj) => {
|
||||||
|
// obj.properties.commentList = []
|
||||||
|
if (obj.type === 'exclusiveGateway' || obj.type === 'parallelGateway') {
|
||||||
|
// 网关下分2步走
|
||||||
|
if (obj.conditionNodeList) {
|
||||||
|
obj.conditionNodeList.forEach((item) => {
|
||||||
|
traverse(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (obj.childNode) {
|
||||||
|
traverse(obj.childNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (obj.id === activityId) {
|
||||||
|
result = obj
|
||||||
|
} else {
|
||||||
|
// 这里追加记录
|
||||||
|
// if (dataList) {
|
||||||
|
// dataList.forEach((item) => {
|
||||||
|
// // 给对应的节点
|
||||||
|
// if (item.activityId === obj.id) {
|
||||||
|
// obj.properties.commentList.push(item)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// 再穿下去
|
||||||
|
if (obj.childNode) {
|
||||||
|
traverse(obj.childNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 传入流程的这个
|
||||||
|
traverse(modelJson)
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
// 遍历表单,将组件设为禁用
|
||||||
|
convFormComponentsDisabled (formJson) {
|
||||||
|
// 递归遍历控件树
|
||||||
|
const traverse = (array) => {
|
||||||
|
array.forEach((element) => {
|
||||||
|
if (element.type === 'grid' || element.type === 'tabs') {
|
||||||
|
// 栅格布局 and 标签页
|
||||||
|
element.columns.forEach((item) => {
|
||||||
|
traverse(item.list)
|
||||||
|
})
|
||||||
|
} else if (element.type === 'card') {
|
||||||
|
// 卡片布局 and 动态表格
|
||||||
|
traverse(element.list)
|
||||||
|
} else if (element.type === 'table') {
|
||||||
|
// 表格布局
|
||||||
|
element.trs.forEach((item) => {
|
||||||
|
item.tds.forEach((val) => {
|
||||||
|
traverse(val.list)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const type = element.type
|
||||||
|
if ((type !== 'alert') & (type !== 'text') & (type !== 'divider') & (type !== 'html')) {
|
||||||
|
element.options.disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(formJson.list)
|
||||||
|
return formJson
|
||||||
|
},
|
||||||
|
// 将渲染图形的信息跟审批记录进行融合,每个节点的配置内添加commentList
|
||||||
|
coalesceDataListChildNode(modelJson, dataList) { // activityId,
|
||||||
|
// let result = {}
|
||||||
|
const traverse = (obj) => {
|
||||||
|
// obj.properties.commentList = []
|
||||||
|
if (obj.type === 'exclusiveGateway' || obj.type === 'parallelGateway') {
|
||||||
|
// 网关下分2步走
|
||||||
|
if (obj.conditionNodeList) {
|
||||||
|
obj.conditionNodeList.forEach((item) => {
|
||||||
|
traverse(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (obj.childNode) {
|
||||||
|
traverse(obj.childNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dataList) {
|
||||||
|
dataList.forEach((item) => {
|
||||||
|
// 给对应的节点
|
||||||
|
if (item.activityId === obj.id) {
|
||||||
|
// 增加多个对象
|
||||||
|
obj.properties.commentList.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 再穿下去
|
||||||
|
if (obj.childNode) {
|
||||||
|
traverse(obj.childNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 传入流程的这个
|
||||||
|
traverse(modelJson)
|
||||||
|
return modelJson
|
||||||
|
}
|
||||||
|
}
|
||||||
625
src/components/XnWorkflow/process.vue
Normal file
625
src/components/XnWorkflow/process.vue
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
<template>
|
||||||
|
<xn-form-container
|
||||||
|
v-model:visible="drawer"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:title="modelTitle"
|
||||||
|
:width="700"
|
||||||
|
:bodyStyle="{ 'padding-top': '0px' }"
|
||||||
|
>
|
||||||
|
<el-form ref="noticeFormRef" :model="formData" layout="vertical">
|
||||||
|
<el-tabs v-model:activeKey="activeKey">
|
||||||
|
<el-tab-pane key="1" tab="人员配置" force-render>
|
||||||
|
<div v-show="formVerify" style="margin-bottom: 10px">
|
||||||
|
<el-alert message="请切换标签查看,填写完必填项!" type="error" />
|
||||||
|
</div>
|
||||||
|
<div v-show="alertShow" style="margin-bottom: 10px">
|
||||||
|
<el-alert message="未选择任何类型的人员配置,默认所有人均可参与此流程。" type="warning" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">配置使用该流程的人员</span>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" round @click="selectionParticipants('ORG')" size="small">
|
||||||
|
<Plus/>
|
||||||
|
选择机构
|
||||||
|
</el-button>
|
||||||
|
<p />
|
||||||
|
<prop-tag :tagList="getTagList('ORG')" />
|
||||||
|
<el-divider />
|
||||||
|
<el-button type="primary" round @click="selectionParticipants('ROLE')" size="small">
|
||||||
|
<plus-outlined />
|
||||||
|
选择角色
|
||||||
|
</el-button>
|
||||||
|
<p />
|
||||||
|
<prop-tag :tagList="getTagList('ROLE')" />
|
||||||
|
<el-divider />
|
||||||
|
<el-button type="primary" round @click="selectionParticipants('POSITION')" size="small">
|
||||||
|
<plus-outlined />
|
||||||
|
选择职位
|
||||||
|
</el-button>
|
||||||
|
<p />
|
||||||
|
<prop-tag :tagList="getTagList('POSITION')" />
|
||||||
|
<el-divider />
|
||||||
|
<el-button type="primary" round @click="selectionParticipants('USER')" size="small">
|
||||||
|
<plus-outlined />
|
||||||
|
选择用户
|
||||||
|
</el-button>
|
||||||
|
<p />
|
||||||
|
<prop-tag :tagList="getTagList('USER')" />
|
||||||
|
<el-divider />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane key="2" tab="基础配置" force-render>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">流程基础全局配置</span>
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="[10, 0]">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item
|
||||||
|
label="流水号"
|
||||||
|
name="processSnTemplateId"
|
||||||
|
:rules="[{ required: true, message: '请选择流水号' }]"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model:value="form.properties.configInfo.processSnTemplateId"
|
||||||
|
placeholder="请选择流水号"
|
||||||
|
:options="snTemplateArray"
|
||||||
|
>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item
|
||||||
|
v-if="recordData.formType === 'DESIGN'"
|
||||||
|
label="打印模板"
|
||||||
|
name="processPrintTemplateId"
|
||||||
|
:rules="[{ required: true, message: '请选择打印模板' }]"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model:value="form.properties.configInfo.processPrintTemplateId"
|
||||||
|
placeholder="请选择打印模板"
|
||||||
|
:options="printTemplateArray"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<span v-else>
|
||||||
|
<p>打印模板</p>
|
||||||
|
<p>自定义表单内提供打印方法</p>
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item
|
||||||
|
label="标题模板"
|
||||||
|
name="processTitleTemplate"
|
||||||
|
:rules="[{ required: true, message: '请创造标题模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="processTitleGenerator"
|
||||||
|
v-model:inputValue="form.properties.configInfo.processTitleTemplate"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="摘要模板"
|
||||||
|
name="processAbstractTemplate"
|
||||||
|
:rules="[{ required: true, message: '请创造摘要模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="processAbstractGenerator"
|
||||||
|
v-model:inputValue="form.properties.configInfo.processAbstractTemplate"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="开启自动去重" name="processEnableAutoDistinct">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableAutoDistinct" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-show="form.properties.configInfo.processEnableAutoDistinct" name="processAutoDistinctType">
|
||||||
|
<el-radio-group v-model:value="form.properties.configInfo.processAutoDistinctType">
|
||||||
|
<el-radio
|
||||||
|
v-for="autoDistinctType in processAutoDistinctTypeList"
|
||||||
|
:key="autoDistinctType.value"
|
||||||
|
:value="autoDistinctType.value"
|
||||||
|
>{{ autoDistinctType.label }}</el-radio
|
||||||
|
>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-row :gutter="[10, 0]">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="开启审批撤销" name="processEnableRevoke">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableRevoke" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="开启意见必填" name="processEnableCommentRequired">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableCommentRequired" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane key="3" tab="通知配置" force-render>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">配置通知事项</span>
|
||||||
|
</div>
|
||||||
|
<el-form-item label="开启退回通知" name="processEnableBackNotice">
|
||||||
|
<el-switch v-model:checked="formData.processEnableBackNotice" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="退回通知方式"
|
||||||
|
v-show="formData.processEnableBackNotice"
|
||||||
|
name="processBackNoticeChannel"
|
||||||
|
:rules="[{ required: formData.processEnableBackNotice, message: '请选择通知方式' }]"
|
||||||
|
>
|
||||||
|
<el-checkbox-group v-model:value="formData.processBackNoticeChannel" :options="noticeInfoList" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="退回通知模板"
|
||||||
|
v-show="formData.processEnableBackNotice"
|
||||||
|
name="processBackNoticeTemplate"
|
||||||
|
:rules="[{ required: formData.processEnableBackNotice, message: '请创造通知模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="enableBackNoticeRef"
|
||||||
|
v-model:inputValue="formData.processBackNoticeTemplate"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="开启待办通知" name="processEnableTodoNotice">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableTodoNotice" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="待办通知方式"
|
||||||
|
v-show="form.properties.configInfo.processEnableTodoNotice"
|
||||||
|
name="processTodoNoticeChannel"
|
||||||
|
:rules="[{ required: formData.processEnableTodoNotice, message: '请选择通知方式' }]"
|
||||||
|
>
|
||||||
|
<el-checkbox-group
|
||||||
|
v-model:value="form.properties.configInfo.processTodoNoticeChannel"
|
||||||
|
:options="noticeInfoList"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="待办通知模板"
|
||||||
|
v-show="form.properties.configInfo.processEnableTodoNotice"
|
||||||
|
name="processTodoNoticeTemplate"
|
||||||
|
:rules="[{ required: formData.processEnableTodoNotice, message: '请创造通知模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="todoNoticeChannelRef"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
v-model:inputValue="form.properties.configInfo.processTodoNoticeTemplate"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="开启抄送通知" name="processEnableCopyNotice">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableCopyNotice" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="抄送通知方式"
|
||||||
|
v-show="form.properties.configInfo.processEnableCopyNotice"
|
||||||
|
name="processCopyNoticeChannel"
|
||||||
|
:rules="[{ required: formData.processEnableCopyNotice, message: '请选择通知方式' }]"
|
||||||
|
>
|
||||||
|
<el-checkbox-group
|
||||||
|
v-model:value="form.properties.configInfo.processCopyNoticeChannel"
|
||||||
|
:options="noticeInfoList"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="抄送通知模板"
|
||||||
|
v-show="form.properties.configInfo.processEnableCopyNotice"
|
||||||
|
name="processCopyNoticeTemplate"
|
||||||
|
:rules="[{ required: formData.processEnableCopyNotice, message: '请创造通知模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="enableCopyNoticeRef"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
v-model:inputValue="form.properties.configInfo.processCopyNoticeTemplate"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="开启完成通知" name="processEnableCompleteNotice">
|
||||||
|
<el-switch v-model:checked="form.properties.configInfo.processEnableCompleteNotice" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="完成通知方式"
|
||||||
|
v-show="form.properties.configInfo.processEnableCompleteNotice"
|
||||||
|
name="processCompleteNoticeChannel"
|
||||||
|
:rules="[{ required: formData.processEnableCompleteNotice, message: '请选择通知方式' }]"
|
||||||
|
>
|
||||||
|
<el-checkbox-group
|
||||||
|
v-model:value="form.properties.configInfo.processCompleteNoticeChannel"
|
||||||
|
:options="noticeInfoList"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="完成通知模板"
|
||||||
|
v-show="form.properties.configInfo.processEnableCompleteNotice"
|
||||||
|
name="processCompleteNoticeTemplate"
|
||||||
|
:rules="[{ required: formData.processEnableCompleteNotice, message: '请创造通知模板' }]"
|
||||||
|
>
|
||||||
|
<template-generator
|
||||||
|
ref="enableCompleteNoticeRef"
|
||||||
|
:fieldInfoLis="fieldInfoLis"
|
||||||
|
v-model:inputValue="form.properties.configInfo.processCompleteNoticeTemplate"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane key="4" tab="表单预设" force-render v-if="recordData.formType !== 'DESIGN'">
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="left-span-label">预设全局需要的表单</span>
|
||||||
|
</div>
|
||||||
|
<el-form-item
|
||||||
|
label="开始节点表单"
|
||||||
|
name="processStartTaskFormUrl"
|
||||||
|
:rules="[{ required: true, message: '请输入开始节点表单' }]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model:value="form.properties.configInfo.processStartTaskFormUrl"
|
||||||
|
addon-before="src/views/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入开始节点表单组件"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-button
|
||||||
|
v-if="form.properties.configInfo.processStartTaskFormUrl"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="$refs.previewCustomFormRef.onOpen(form.properties.configInfo.processStartTaskFormUrl)"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="移动端开始节点表单"
|
||||||
|
name="processStartTaskMobileFormUrl"
|
||||||
|
:rules="[{ required: true, message: '请输入移动端开始节点表单' }]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model:value="form.properties.configInfo.processStartTaskMobileFormUrl"
|
||||||
|
addon-before="pages/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入移动端开始节点表单组件"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="人员节点表单"
|
||||||
|
name="processUserTaskFormUrl"
|
||||||
|
:rules="[{ required: true, message: '请输入人员节点表单' }]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model:value="form.properties.configInfo.processUserTaskFormUrl"
|
||||||
|
addon-before="src/views/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入人员节点表单组件"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-button
|
||||||
|
v-if="form.properties.configInfo.processUserTaskFormUrl"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="$refs.previewCustomFormRef.onOpen(form.properties.configInfo.processUserTaskFormUrl)"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="移动端人员节点表单"
|
||||||
|
name="processUserTaskMobileFormUrl"
|
||||||
|
:rules="[{ required: true, message: '请输入移动端人员节点表单' }]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model:value="form.properties.configInfo.processUserTaskMobileFormUrl"
|
||||||
|
addon-before="pages/flw/customform/"
|
||||||
|
addon-after=".vue"
|
||||||
|
placeholder="请输入移动端人员节点表单组件"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane key="5" tab="执行监听" force-render>
|
||||||
|
<prop-listener-info
|
||||||
|
ref="propListenerInfoRef"
|
||||||
|
:listenerValue="form.properties.executionListenerInfo"
|
||||||
|
:defaultListenerList="executionListenerInfo"
|
||||||
|
:listener-value-array="executionListenerArray"
|
||||||
|
listener-type="global"
|
||||||
|
:execution-listener-selector-for-custom-event-array="executionListenerSelectorForCustomEventArray"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" style="margin-right: 8px" @click="onFinish">保存</el-button>
|
||||||
|
<el-button @click="drawer = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<role-selector-plus
|
||||||
|
ref="roleselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:role-page-api="selectorApiFunction.rolePageApi"
|
||||||
|
:checked-role-list-api="selectorApiFunction.checkedRoleListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<user-selector-plus
|
||||||
|
ref="userselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:user-page-api="selectorApiFunction.userPageApi"
|
||||||
|
:checked-user-list-api="selectorApiFunction.checkedUserListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<pos-selector-plus
|
||||||
|
ref="posselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:pos-page-api="selectorApiFunction.posPageApi"
|
||||||
|
:checked-pos-list-api="selectorApiFunction.checkedPosListApi"
|
||||||
|
:datel-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<org-selector-plus
|
||||||
|
ref="orgselectorPlus"
|
||||||
|
:org-tree-api="selectorApiFunction.orgTreeApi"
|
||||||
|
:org-page-api="selectorApiFunction.orgPageApi"
|
||||||
|
:checked-org-list-api="selectorApiFunction.checkedOrgListApi"
|
||||||
|
:data-is-converter-flw="true"
|
||||||
|
@onBack="callBack"
|
||||||
|
/>
|
||||||
|
<preview-custom-form ref="previewCustomFormRef" />
|
||||||
|
</xn-form-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import templateGenerator from './nodes/prop/templateGenerator.vue'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
|
||||||
|
import userSelectorPlus from '@/components/Selector/userSelectorPlus.vue'
|
||||||
|
import posSelectorPlus from '@/components/Selector/posSelectorPlus.vue'
|
||||||
|
import orgSelectorPlus from '@/components/Selector/orgSelectorPlus.vue'
|
||||||
|
import propTag from './nodes/prop/propTag.vue'
|
||||||
|
import PropListenerInfo from '@/components/XnWorkflow/nodes/prop/propListenerInfo.vue'
|
||||||
|
import PreviewCustomForm from '@/components/XnWorkflow/nodes/common/previewCustomForm.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PropListenerInfo,
|
||||||
|
templateGenerator,
|
||||||
|
roleSelectorPlus,
|
||||||
|
userSelectorPlus,
|
||||||
|
posSelectorPlus,
|
||||||
|
orgSelectorPlus,
|
||||||
|
propTag,
|
||||||
|
PreviewCustomForm
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: { type: Object, default: () => {} },
|
||||||
|
formFieldListValue: { type: Array, default: () => [] },
|
||||||
|
recordData: { type: Object, default: () => {} },
|
||||||
|
snTemplateArray: { type: Array, default: () => [] },
|
||||||
|
printTemplateArray: { type: Array, default: () => [] },
|
||||||
|
executionListenerArray: { type: Array, default: () => [] },
|
||||||
|
executionListenerSelectorForCustomEventArray: { type: Array, default: () => [] },
|
||||||
|
taskListenerArray: { type: Array, default: () => [] },
|
||||||
|
selectorApiFunction: { type: Object, default: () => {} }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
noticeInfoList: cloneDeep(config.noticeInfoList),
|
||||||
|
// 摘要模板,因为要从传来的字段中取
|
||||||
|
abstractStr: '',
|
||||||
|
executionListenerInfo: cloneDeep(config.listener.processExecutionListenerInfo),
|
||||||
|
// 自动去重类型
|
||||||
|
processAutoDistinctTypeList: [
|
||||||
|
{
|
||||||
|
label: '当审批人和发起人是同一个人,审批自动通过',
|
||||||
|
value: 'SAMPLE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '当同一审批人在流程中连续多次出现时,自动去重',
|
||||||
|
value: 'MULTIPLE'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
drawer: false,
|
||||||
|
modelTitle: '全局属性',
|
||||||
|
activeKey: '1',
|
||||||
|
childNode: this.modelValue,
|
||||||
|
form: {},
|
||||||
|
formData: {},
|
||||||
|
fieldInfoLis: [],
|
||||||
|
formVerify: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 监听内部数组,选了人员相关,我们就不提示
|
||||||
|
alertShow() {
|
||||||
|
return this.form.properties.participateInfo.length <= 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.childNode = val
|
||||||
|
},
|
||||||
|
childNode(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
},
|
||||||
|
formFieldListValue(val) {
|
||||||
|
// 获取主表名称
|
||||||
|
const parentTableName = JSON.parse(this.recordData.tableJson).filter((item) => item.tableType === 'parent')[0]
|
||||||
|
.tableName
|
||||||
|
// 监听到字段列表后,将其转至定义的变量中
|
||||||
|
this.fieldInfoLis = []
|
||||||
|
// 不仅表单中有字段,而且还要必须选择了主表
|
||||||
|
if (val.length > 0) {
|
||||||
|
const fildLists = this.getListField(val)
|
||||||
|
fildLists.forEach((item) => {
|
||||||
|
const obj = {}
|
||||||
|
// 判断是否是选择了表,并且选择了字段
|
||||||
|
if (item.selectTable && item.selectColumn) {
|
||||||
|
// 判断是否是主表的字段,并且是必填项
|
||||||
|
const requireds = item.rules[0].required
|
||||||
|
if ((item.selectTable === parentTableName) & requireds) {
|
||||||
|
obj.label = item.label
|
||||||
|
obj.value = item.model
|
||||||
|
this.fieldInfoLis.push(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showDrawer() {
|
||||||
|
this.form = {}
|
||||||
|
this.form = cloneDeep(this.childNode)
|
||||||
|
this.formData = this.form.properties.configInfo
|
||||||
|
this.drawer = true
|
||||||
|
// 制作默认的摘要字段
|
||||||
|
this.makeAbstractTemp()
|
||||||
|
},
|
||||||
|
// 制作默认显示的摘要信息
|
||||||
|
makeAbstractTemp() {
|
||||||
|
let temp = this.form.properties.configInfo.processAbstractTemplate
|
||||||
|
if (temp === '') {
|
||||||
|
if (this.fieldInfoLis.length > 0) {
|
||||||
|
let fieldInfoTemp = ''
|
||||||
|
for (let a = 0; a < this.fieldInfoLis.length; a++) {
|
||||||
|
// 最多3个摘要,按顺序而来
|
||||||
|
if (a < 3) {
|
||||||
|
const str = this.fieldInfoLis[a].label + ':' + this.fieldInfoLis[a].value
|
||||||
|
if (a === 3 - 1 || a === this.fieldInfoLis.length - 1) {
|
||||||
|
fieldInfoTemp = fieldInfoTemp + str
|
||||||
|
} else {
|
||||||
|
fieldInfoTemp = fieldInfoTemp + str + ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp = fieldInfoTemp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFinish() {
|
||||||
|
// 校验表单的正确性
|
||||||
|
this.$refs.noticeFormRef
|
||||||
|
.validate()
|
||||||
|
.then((values) => {
|
||||||
|
if (this.form.id === '') {
|
||||||
|
this.form.id = this.$TOOL.snowyUuid()
|
||||||
|
}
|
||||||
|
// 获取输入的监听
|
||||||
|
this.form.properties.executionListenerInfo = this.$refs.propListenerInfoRef.selectedListenerList()
|
||||||
|
this.form.dataLegal = true
|
||||||
|
this.form.properties.configInfo = values
|
||||||
|
this.$emit('update:modelValue', this.form)
|
||||||
|
this.drawer = false
|
||||||
|
})
|
||||||
|
.catch((info) => {
|
||||||
|
this.formVerify = true
|
||||||
|
setTimeout((e) => {
|
||||||
|
this.formVerify = false
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 选择参与人
|
||||||
|
selectionParticipants(type) {
|
||||||
|
let participateInfo = this.form.properties.participateInfo
|
||||||
|
let data = []
|
||||||
|
if (participateInfo.length > 0) {
|
||||||
|
participateInfo.forEach((item) => {
|
||||||
|
if (item.key === type) {
|
||||||
|
data.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (type === 'ORG') {
|
||||||
|
this.$refs.orgselectorPlus.showOrgPlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'ROLE') {
|
||||||
|
this.$refs.roleselectorPlus.showRolePlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'POSITION') {
|
||||||
|
this.$refs.posselectorPlus.showPosPlusModal(data)
|
||||||
|
}
|
||||||
|
if (type === 'USER') {
|
||||||
|
this.$refs.userselectorPlus.showUserPlusModal(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 回调函数,这几个选择人员相关的设计器,都是的
|
||||||
|
callBack(value) {
|
||||||
|
let participateInfo = this.form.properties.participateInfo
|
||||||
|
if (participateInfo.length > 0) {
|
||||||
|
let mark = 0
|
||||||
|
for (let a = 0; a < participateInfo.length; a++) {
|
||||||
|
if (value.key === participateInfo[a].key) {
|
||||||
|
if (value.label === '') {
|
||||||
|
participateInfo.splice(a, 1)
|
||||||
|
} else {
|
||||||
|
participateInfo[a] = value
|
||||||
|
}
|
||||||
|
mark = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mark === 0) {
|
||||||
|
participateInfo.push(value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.form.properties.participateInfo.push(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取tag标签的内容
|
||||||
|
getTagList(type) {
|
||||||
|
const participateInfo = this.form.properties.participateInfo
|
||||||
|
if (participateInfo.length < 0) {
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
return participateInfo.find((item) => item.key === type)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getListField(data) {
|
||||||
|
let result = []
|
||||||
|
// 递归遍历控件树
|
||||||
|
const traverse = (array) => {
|
||||||
|
array.forEach((element) => {
|
||||||
|
if (element.type === 'grid' || element.type === 'tabs') {
|
||||||
|
// 栅格布局 and 标签页
|
||||||
|
element.columns.forEach((item) => {
|
||||||
|
traverse(item.list)
|
||||||
|
})
|
||||||
|
} else if (element.type === 'card') {
|
||||||
|
// 卡片布局 and 动态表格
|
||||||
|
traverse(element.list)
|
||||||
|
} else if (element.type === 'table') {
|
||||||
|
// 表格布局
|
||||||
|
element.trs.forEach((item) => {
|
||||||
|
item.tds.forEach((val) => {
|
||||||
|
traverse(val.list)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const type = element.type
|
||||||
|
// 排除一些
|
||||||
|
if (
|
||||||
|
(type !== 'alert') &
|
||||||
|
(type !== 'text') &
|
||||||
|
(type !== 'divider') &
|
||||||
|
(type !== 'batch') &
|
||||||
|
(type !== 'html')
|
||||||
|
) {
|
||||||
|
result.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(data)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="process-panel__container"
|
class='process-panel__container'
|
||||||
:style="{ width: `${width}px`, height: `700px`, overflow: 'auto', background: '#fff' }"
|
:style="{ width: `${width}px`, height: `700px`, overflow: 'auto', background: '#fff' }"
|
||||||
>
|
>
|
||||||
<el-collapse v-model="activeTab">
|
<el-collapse v-model='activeTab'>
|
||||||
<el-collapse-item name="base">
|
<el-collapse-item name='base'>
|
||||||
<!-- class="panel-tab__title" -->
|
<!-- class="panel-tab__title" -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<Icon name="el-icon-InfoFilled" />
|
<Icon name='el-icon-InfoFilled' />
|
||||||
常规
|
常规
|
||||||
</template>
|
</template>
|
||||||
<ElementBaseInfo
|
<ElementBaseInfo
|
||||||
:id-edit-disabled="idEditDisabled"
|
:id-edit-disabled='idEditDisabled'
|
||||||
:business-object="elementBusinessObject"
|
:business-object='elementBusinessObject'
|
||||||
:type="elementType"
|
:type='elementType'
|
||||||
:model="model"
|
:model='model'
|
||||||
/>
|
/>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<!-- <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
|
<!-- <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
|
||||||
@@ -24,76 +24,76 @@
|
|||||||
</template>
|
</template>
|
||||||
<SignalAndMassage />
|
<SignalAndMassage />
|
||||||
</el-collapse-item>-->
|
</el-collapse-item>-->
|
||||||
<el-collapse-item name="condition" v-if="conditionFormVisible" key="condition">
|
<el-collapse-item name='condition' v-if='conditionFormVisible' key='condition'>
|
||||||
<template #title>
|
<template #title>
|
||||||
<Icon name="el-icon-Promotion" />
|
<Icon name='el-icon-Promotion' />
|
||||||
流转条件
|
流转条件
|
||||||
</template>
|
</template>
|
||||||
<FlowCondition :business-object="elementBusinessObject" :type="elementType" />
|
<FlowCondition :business-object='elementBusinessObject' :type='elementType' />
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<!-- <el-collapse-item name="condition" v-if="formVisible" key="form">
|
<el-collapse-item name='condition' v-if='formVisible' key='form'>
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-List" />
|
|
||||||
表单
|
|
||||||
</template>
|
|
||||||
<ElementForm :id="elementId" :type="elementType" />
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
|
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-Checked" />
|
|
||||||
任务(审批人)
|
|
||||||
</template>
|
|
||||||
<ElementTask :id="elementId" :type="elementType" />
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="multiInstance" v-if="elementType.indexOf('Task') !== -1" key="multiInstance">
|
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-HelpFilled" />
|
|
||||||
多实例(会签配置)
|
|
||||||
</template>
|
|
||||||
<ElementMultiInstance :business-object="elementBusinessObject" :type="elementType" />
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="listeners" key="listeners">
|
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-BellFilled" />
|
|
||||||
执行监听器
|
|
||||||
</template>
|
|
||||||
<ElementListeners :id="elementId" :type="elementType" />
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="taskListeners" v-if="elementType === 'UserTask'" key="taskListeners">
|
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-BellFilled" />
|
|
||||||
任务监听器
|
|
||||||
</template>
|
|
||||||
<UserTaskListeners :id="elementId" :type="elementType" />
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="extensions" key="extensions">
|
|
||||||
<template #title>
|
|
||||||
<Icon name="el-icon-CirclePlusFilled" />
|
|
||||||
扩展属性
|
|
||||||
</template>
|
|
||||||
<ElementProperties :id="elementId" :type="elementType" />
|
|
||||||
</el-collapse-item> -->
|
|
||||||
<el-collapse-item name="other" key="other">
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<Icon name="el-icon-Promotion" />
|
<Icon name='el-icon-List' />
|
||||||
|
表单
|
||||||
|
</template>
|
||||||
|
<ElementForm :id='elementId' :type='elementType' />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name='task' v-if="elementType.indexOf('Task') !== -1" key='task'>
|
||||||
|
<template #title>
|
||||||
|
<Icon name='el-icon-Checked' />
|
||||||
|
任务(审批人)
|
||||||
|
</template>
|
||||||
|
<ElementTask :id='elementId' :type='elementType' />
|
||||||
|
</el-collapse-item>
|
||||||
|
<!-- <el-collapse-item name='multiInstance' v-if="elementType.indexOf('Task') !== -1" key='multiInstance'>-->
|
||||||
|
<!-- <template #title>-->
|
||||||
|
<!-- <Icon name='el-icon-HelpFilled' />-->
|
||||||
|
<!-- 多实例(会签配置)-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <ElementMultiInstance :business-object='elementBusinessObject' :type='elementType' />-->
|
||||||
|
<!-- </el-collapse-item>-->
|
||||||
|
<!-- <el-collapse-item name='listeners' key='listeners'>-->
|
||||||
|
<!-- <template #title>-->
|
||||||
|
<!-- <Icon name='el-icon-BellFilled' />-->
|
||||||
|
<!-- 执行监听器-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <ElementListeners :id='elementId' :type='elementType' />-->
|
||||||
|
<!-- </el-collapse-item>-->
|
||||||
|
<!-- <el-collapse-item name='taskListeners' v-if="elementType === 'UserTask'" key='taskListeners'>-->
|
||||||
|
<!-- <template #title>-->
|
||||||
|
<!-- <Icon name='el-icon-BellFilled' />-->
|
||||||
|
<!-- 任务监听器-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <UserTaskListeners :id='elementId' :type='elementType' />-->
|
||||||
|
<!-- </el-collapse-item>-->
|
||||||
|
<!-- <el-collapse-item name='extensions' key='extensions'>-->
|
||||||
|
<!-- <template #title>-->
|
||||||
|
<!-- <Icon name='el-icon-CirclePlusFilled' />-->
|
||||||
|
<!-- 扩展属性-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <ElementProperties :id='elementId' :type='elementType' />-->
|
||||||
|
<!-- </el-collapse-item>-->
|
||||||
|
<el-collapse-item name='other' key='other'>
|
||||||
|
<template #title>
|
||||||
|
<Icon name='el-icon-Promotion' />
|
||||||
其他
|
其他
|
||||||
</template>
|
</template>
|
||||||
<ElementOtherConfig :id="elementId" />
|
<ElementOtherConfig :id='elementId' />
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
import { onMounted, provide, ref, watch, onBeforeUnmount } from 'vue'
|
import { onMounted, provide, ref, watch, onBeforeUnmount } from 'vue'
|
||||||
import ElementBaseInfo from './base/ElementBaseInfo.vue'
|
import ElementBaseInfo from './base/ElementBaseInfo.vue'
|
||||||
import ElementOtherConfig from './other/ElementOtherConfig.vue'
|
import ElementOtherConfig from './other/ElementOtherConfig.vue'
|
||||||
// // import ElementTask from './task/ElementTask.vue'
|
import ElementTask from './task/ElementTask.vue'
|
||||||
// import ElementMultiInstance from './multi-instance/ElementMultiInstance.vue'
|
// import ElementMultiInstance from './multi-instance/ElementMultiInstance.vue'
|
||||||
import FlowCondition from './flow-condition/FlowCondition.vue'
|
import FlowCondition from './flow-condition/FlowCondition.vue'
|
||||||
// import SignalAndMassage from './signal-message/SignalAndMessage.vue'
|
// import SignalAndMassage from './signal-message/SignalAndMessage.vue'
|
||||||
// import ElementListeners from './listeners/ElementListeners.vue'
|
// import ElementListeners from './listeners/ElementListeners.vue'
|
||||||
// import ElementProperties from './properties/ElementProperties.vue'
|
// import ElementProperties from './properties/ElementProperties.vue'
|
||||||
// // import ElementForm from './form/ElementForm.vue'
|
import ElementForm from './form/ElementForm.vue'
|
||||||
// import UserTaskListeners from './listeners/UserTaskListeners.vue'
|
// import UserTaskListeners from './listeners/UserTaskListeners.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'MyPropertiesPanel' })
|
defineOptions({ name: 'MyPropertiesPanel' })
|
||||||
@@ -107,7 +107,8 @@ defineOptions({ name: 'MyPropertiesPanel' })
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bpmnModeler: {
|
bpmnModeler: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {
|
||||||
|
}
|
||||||
},
|
},
|
||||||
prefix: {
|
prefix: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -1,239 +1,239 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class='panel-tab__content'>
|
||||||
<el-form label-width="80px">
|
<el-form label-width='80px'>
|
||||||
<el-form-item label="流程表单">
|
<el-form-item label='流程表单'>
|
||||||
<!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
|
<!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
|
||||||
<el-select v-model="formKey" clearable @change="updateElementFormKey">
|
<el-select v-model='formKey' clearable @change='updateElementFormKey'>
|
||||||
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
<el-option v-for='form in formList' :key='form.id' :label='form.name' :value='"key_"+form.id' />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="业务标识">-->
|
<!-- <el-form-item label="业务标识">-->
|
||||||
<!-- <el-select v-model="businessKey" @change="updateElementBusinessKey">-->
|
<!-- <el-select v-model="businessKey" @change="updateElementBusinessKey">-->
|
||||||
<!-- <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />-->
|
<!-- <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />-->
|
||||||
<!-- <el-option label="无" value="" />-->
|
<!-- <el-option label="无" value="" />-->
|
||||||
<!-- </el-select>-->
|
<!-- </el-select>-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!--字段列表-->
|
<!--字段列表-->
|
||||||
<!-- <div class="element-property list-property">-->
|
<!-- <div class="element-property list-property">-->
|
||||||
<!-- <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>-->
|
<!-- <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>-->
|
||||||
<!-- <el-table :data="fieldList" max-height="240" fit border>-->
|
<!-- <el-table :data="fieldList" max-height="240" fit border>-->
|
||||||
<!-- <el-table-column label="序号" type="index" width="50px" />-->
|
<!-- <el-table-column label="序号" type="index" width="50px" />-->
|
||||||
<!-- <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column-->
|
<!-- <el-table-column-->
|
||||||
<!-- label="字段类型"-->
|
<!-- label="字段类型"-->
|
||||||
<!-- prop="type"-->
|
<!-- prop="type"-->
|
||||||
<!-- min-width="80px"-->
|
<!-- min-width="80px"-->
|
||||||
<!-- :formatter="(row) => fieldType[row.type] || row.type"-->
|
<!-- :formatter="(row) => fieldType[row.type] || row.type"-->
|
||||||
<!-- show-overflow-tooltip-->
|
<!-- show-overflow-tooltip-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- <el-table-column-->
|
<!-- <el-table-column-->
|
||||||
<!-- label="默认值"-->
|
<!-- label="默认值"-->
|
||||||
<!-- prop="defaultValue"-->
|
<!-- prop="defaultValue"-->
|
||||||
<!-- min-width="80px"-->
|
<!-- min-width="80px"-->
|
||||||
<!-- show-overflow-tooltip-->
|
<!-- show-overflow-tooltip-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- <el-table-column label="操作" width="90px">-->
|
<!-- <el-table-column label="操作" width="90px">-->
|
||||||
<!-- <template #default="scope">-->
|
<!-- <template #default="scope">-->
|
||||||
<!-- <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"-->
|
<!-- <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"-->
|
||||||
<!-- >编辑</el-button-->
|
<!-- >编辑</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-divider direction="vertical" />-->
|
<!-- <el-divider direction="vertical" />-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- style="color: #ff4d4f"-->
|
<!-- style="color: #ff4d4f"-->
|
||||||
<!-- @click="removeField(scope, scope.$index)"-->
|
<!-- @click="removeField(scope, scope.$index)"-->
|
||||||
<!-- >移除</el-button-->
|
<!-- >移除</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-table-column>-->
|
<!-- </el-table-column>-->
|
||||||
<!-- </el-table>-->
|
<!-- </el-table>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="element-drawer__button">-->
|
<!-- <div class="element-drawer__button">-->
|
||||||
<!-- <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />-->
|
<!-- <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!--字段配置侧边栏-->
|
<!--字段配置侧边栏-->
|
||||||
<!-- <el-drawer-->
|
<!-- <el-drawer-->
|
||||||
<!-- v-model="fieldModelVisible"-->
|
<!-- v-model="fieldModelVisible"-->
|
||||||
<!-- title="字段配置"-->
|
<!-- title="字段配置"-->
|
||||||
<!-- :size="`${width}px`"-->
|
<!-- :size="`${width}px`"-->
|
||||||
<!-- append-to-body-->
|
<!-- append-to-body-->
|
||||||
<!-- destroy-on-close-->
|
<!-- destroy-on-close-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-form :model="formFieldForm" label-width="90px">-->
|
<!-- <el-form :model="formFieldForm" label-width="90px">-->
|
||||||
<!-- <el-form-item label="字段ID">-->
|
<!-- <el-form-item label="字段ID">-->
|
||||||
<!-- <el-input v-model="formFieldForm.id" clearable />-->
|
<!-- <el-input v-model="formFieldForm.id" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="类型">-->
|
<!-- <el-form-item label="类型">-->
|
||||||
<!-- <el-select-->
|
<!-- <el-select-->
|
||||||
<!-- v-model="formFieldForm.typeType"-->
|
<!-- v-model="formFieldForm.typeType"-->
|
||||||
<!-- placeholder="请选择字段类型"-->
|
<!-- placeholder="请选择字段类型"-->
|
||||||
<!-- clearable-->
|
<!-- clearable-->
|
||||||
<!-- @change="changeFieldTypeType"-->
|
<!-- @change="changeFieldTypeType"-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />-->
|
<!-- <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />-->
|
||||||
<!-- </el-select>-->
|
<!-- </el-select>-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
|
<!-- <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
|
||||||
<!-- <el-input v-model="formFieldForm.type" clearable />-->
|
<!-- <el-input v-model="formFieldForm.type" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="名称">-->
|
<!-- <el-form-item label="名称">-->
|
||||||
<!-- <el-input v-model="formFieldForm.label" clearable />-->
|
<!-- <el-input v-model="formFieldForm.label" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
|
<!-- <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
|
||||||
<!-- <el-input v-model="formFieldForm.datePattern" clearable />-->
|
<!-- <el-input v-model="formFieldForm.datePattern" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="默认值">-->
|
<!-- <el-form-item label="默认值">-->
|
||||||
<!-- <el-input v-model="formFieldForm.defaultValue" clearable />-->
|
<!-- <el-input v-model="formFieldForm.defaultValue" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- </el-form>-->
|
<!-- </el-form>-->
|
||||||
|
|
||||||
<!-- <!– 枚举值设置 –>-->
|
<!-- <!– 枚举值设置 –>-->
|
||||||
<!-- <template v-if="formFieldForm.type === 'enum'">-->
|
<!-- <template v-if="formFieldForm.type === 'enum'">-->
|
||||||
<!-- <el-divider key="enum-divider" />-->
|
<!-- <el-divider key="enum-divider" />-->
|
||||||
<!-- <p class="listener-filed__title" key="enum-title">-->
|
<!-- <p class="listener-filed__title" key="enum-title">-->
|
||||||
<!-- <span><Icon icon="ep:menu" />枚举值列表:</span>-->
|
<!-- <span><Icon icon="ep:menu" />枚举值列表:</span>-->
|
||||||
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
|
||||||
<!-- >添加枚举值</el-button-->
|
<!-- >添加枚举值</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </p>-->
|
<!-- </p>-->
|
||||||
<!-- <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>-->
|
<!-- <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>-->
|
||||||
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
||||||
<!-- <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="操作" width="90px">-->
|
<!-- <el-table-column label="操作" width="90px">-->
|
||||||
<!-- <template #default="scope">-->
|
<!-- <template #default="scope">-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
|
||||||
<!-- >编辑</el-button-->
|
<!-- >编辑</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-divider direction="vertical" />-->
|
<!-- <el-divider direction="vertical" />-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- style="color: #ff4d4f"-->
|
<!-- style="color: #ff4d4f"-->
|
||||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
|
||||||
<!-- >移除</el-button-->
|
<!-- >移除</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-table-column>-->
|
<!-- </el-table-column>-->
|
||||||
<!-- </el-table>-->
|
<!-- </el-table>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
|
|
||||||
<!-- <!– 校验规则 –>-->
|
<!-- <!– 校验规则 –>-->
|
||||||
<!-- <el-divider key="validation-divider" />-->
|
<!-- <el-divider key="validation-divider" />-->
|
||||||
<!-- <p class="listener-filed__title" key="validation-title">-->
|
<!-- <p class="listener-filed__title" key="validation-title">-->
|
||||||
<!-- <span><Icon icon="ep:menu" />约束条件列表:</span>-->
|
<!-- <span><Icon icon="ep:menu" />约束条件列表:</span>-->
|
||||||
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
|
||||||
<!-- >添加约束</el-button-->
|
<!-- >添加约束</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </p>-->
|
<!-- </p>-->
|
||||||
<!-- <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>-->
|
<!-- <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>-->
|
||||||
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
||||||
<!-- <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="操作" width="90px">-->
|
<!-- <el-table-column label="操作" width="90px">-->
|
||||||
<!-- <template #default="scope">-->
|
<!-- <template #default="scope">-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
|
||||||
<!-- >编辑</el-button-->
|
<!-- >编辑</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-divider direction="vertical" />-->
|
<!-- <el-divider direction="vertical" />-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- style="color: #ff4d4f"-->
|
<!-- style="color: #ff4d4f"-->
|
||||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
|
||||||
<!-- >移除</el-button-->
|
<!-- >移除</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-table-column>-->
|
<!-- </el-table-column>-->
|
||||||
<!-- </el-table>-->
|
<!-- </el-table>-->
|
||||||
|
|
||||||
<!-- <!– 表单属性 –>-->
|
<!-- <!– 表单属性 –>-->
|
||||||
<!-- <el-divider key="property-divider" />-->
|
<!-- <el-divider key="property-divider" />-->
|
||||||
<!-- <p class="listener-filed__title" key="property-title">-->
|
<!-- <p class="listener-filed__title" key="property-title">-->
|
||||||
<!-- <span><Icon icon="ep:menu" />字段属性列表:</span>-->
|
<!-- <span><Icon icon="ep:menu" />字段属性列表:</span>-->
|
||||||
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
|
||||||
<!-- >添加属性</el-button-->
|
<!-- >添加属性</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </p>-->
|
<!-- </p>-->
|
||||||
<!-- <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>-->
|
<!-- <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>-->
|
||||||
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
<!-- <el-table-column label="序号" width="50px" type="index" />-->
|
||||||
<!-- <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />-->
|
<!-- <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />-->
|
||||||
<!-- <el-table-column label="操作" width="90px">-->
|
<!-- <el-table-column label="操作" width="90px">-->
|
||||||
<!-- <template #default="scope">-->
|
<!-- <template #default="scope">-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
|
||||||
<!-- >编辑</el-button-->
|
<!-- >编辑</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-divider direction="vertical" />-->
|
<!-- <el-divider direction="vertical" />-->
|
||||||
<!-- <el-button-->
|
<!-- <el-button-->
|
||||||
<!-- type="primary"-->
|
<!-- type="primary"-->
|
||||||
<!-- link-->
|
<!-- link-->
|
||||||
<!-- style="color: #ff4d4f"-->
|
<!-- style="color: #ff4d4f"-->
|
||||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
|
||||||
<!-- >移除</el-button-->
|
<!-- >移除</el-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-table-column>-->
|
<!-- </el-table-column>-->
|
||||||
<!-- </el-table>-->
|
<!-- </el-table>-->
|
||||||
|
|
||||||
<!-- <!– 底部按钮 –>-->
|
<!-- <!– 底部按钮 –>-->
|
||||||
<!-- <div class="element-drawer__button">-->
|
<!-- <div class="element-drawer__button">-->
|
||||||
<!-- <el-button>取 消</el-button>-->
|
<!-- <el-button>取 消</el-button>-->
|
||||||
<!-- <el-button type="primary" @click="saveField">保 存</el-button>-->
|
<!-- <el-button type="primary" @click="saveField">保 存</el-button>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </el-drawer>-->
|
<!-- </el-drawer>-->
|
||||||
|
|
||||||
<!-- <el-dialog-->
|
<!-- <el-dialog-->
|
||||||
<!-- v-model="fieldOptionModelVisible"-->
|
<!-- v-model="fieldOptionModelVisible"-->
|
||||||
<!-- :title="optionModelTitle"-->
|
<!-- :title="optionModelTitle"-->
|
||||||
<!-- width="600px"-->
|
<!-- width="600px"-->
|
||||||
<!-- append-to-body-->
|
<!-- append-to-body-->
|
||||||
<!-- destroy-on-close-->
|
<!-- destroy-on-close-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- <el-form :model="fieldOptionForm" label-width="96px">-->
|
<!-- <el-form :model="fieldOptionForm" label-width="96px">-->
|
||||||
<!-- <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
|
<!-- <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
|
||||||
<!-- <el-input v-model="fieldOptionForm.id" clearable />-->
|
<!-- <el-input v-model="fieldOptionForm.id" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
|
<!-- <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
|
||||||
<!-- <el-input v-model="fieldOptionForm.name" clearable />-->
|
<!-- <el-input v-model="fieldOptionForm.name" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
|
<!-- <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
|
||||||
<!-- <el-input v-model="fieldOptionForm.config" clearable />-->
|
<!-- <el-input v-model="fieldOptionForm.config" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
|
<!-- <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
|
||||||
<!-- <el-input v-model="fieldOptionForm.value" clearable />-->
|
<!-- <el-input v-model="fieldOptionForm.value" clearable />-->
|
||||||
<!-- </el-form-item>-->
|
<!-- </el-form-item>-->
|
||||||
<!-- </el-form>-->
|
<!-- </el-form>-->
|
||||||
<!-- <template #footer>-->
|
<!-- <template #footer>-->
|
||||||
<!-- <el-button @click="fieldOptionModelVisible = false">取 消</el-button>-->
|
<!-- <el-button @click="fieldOptionModelVisible = false">取 消</el-button>-->
|
||||||
<!-- <el-button type="primary" @click="saveFieldOption">确 定</el-button>-->
|
<!-- <el-button type="primary" @click="saveFieldOption">确 定</el-button>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-dialog>-->
|
<!-- </el-dialog>-->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
import { ref, onMounted, inject,toRaw, watch,nextTick } from 'vue'
|
import { ref, onMounted, inject, toRaw, watch, nextTick } from 'vue'
|
||||||
import * as FormApi from '@/api/bpm/form'
|
import { listAllWFForm } from '@/api/process-boot/workflow/form'
|
||||||
|
|
||||||
defineOptions({ name: 'ElementForm' })
|
defineOptions({ name: 'ElementForm' })
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: String,
|
id: String,
|
||||||
type: String
|
type: String
|
||||||
})
|
})
|
||||||
const prefix = inject('prefix')
|
const prefix = inject('prefix')
|
||||||
const width = inject('width')
|
const width = inject('width')
|
||||||
@@ -244,12 +244,12 @@ const optionModelTitle = ref('')
|
|||||||
const fieldList = ref<any[]>([])
|
const fieldList = ref<any[]>([])
|
||||||
const formFieldForm = ref<any>({})
|
const formFieldForm = ref<any>({})
|
||||||
const fieldType = ref({
|
const fieldType = ref({
|
||||||
long: '长整型',
|
long: '长整型',
|
||||||
string: '字符串',
|
string: '字符串',
|
||||||
boolean: '布尔类',
|
boolean: '布尔类',
|
||||||
date: '日期类',
|
date: '日期类',
|
||||||
enum: '枚举类',
|
enum: '枚举类',
|
||||||
custom: '自定义类型'
|
custom: '自定义类型'
|
||||||
})
|
})
|
||||||
const formFieldIndex = ref(-1) // 编辑中的字段, -1 为新增
|
const formFieldIndex = ref(-1) // 编辑中的字段, -1 为新增
|
||||||
const formFieldOptionIndex = ref(-1) // 编辑中的字段配置项, -1 为新增
|
const formFieldOptionIndex = ref(-1) // 编辑中的字段配置项, -1 为新增
|
||||||
@@ -267,213 +267,215 @@ const otherExtensions = ref()
|
|||||||
|
|
||||||
const bpmnInstances = () => (window as any)?.bpmnInstances
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
const resetFormList = () => {
|
const resetFormList = () => {
|
||||||
bpmnELement.value = bpmnInstances().bpmnElement
|
bpmnELement.value = bpmnInstances().bpmnElement
|
||||||
formKey.value = bpmnELement.value.businessObject.formKey
|
formKey.value = bpmnELement.value.businessObject.formKey
|
||||||
if (formKey.value?.length > 0) {
|
if (formKey.value?.length > 0) {
|
||||||
formKey.value = parseInt(formKey.value)
|
formKey.value = parseInt(formKey.value)
|
||||||
}
|
}
|
||||||
// 获取元素扩展属性 或者 创建扩展属性
|
// 获取元素扩展属性 或者 创建扩展属性
|
||||||
elExtensionElements.value =
|
elExtensionElements.value =
|
||||||
bpmnELement.value.businessObject.get('extensionElements') ||
|
bpmnELement.value.businessObject.get('extensionElements') ||
|
||||||
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
||||||
// 获取元素表单配置 或者 创建新的表单配置
|
// 获取元素表单配置 或者 创建新的表单配置
|
||||||
formData.value =
|
formData.value =
|
||||||
elExtensionElements.value.values.filter((ex) => ex.$type === `${prefix}:FormData`)?.[0] ||
|
elExtensionElements.value.values.filter((ex) => ex.$type === `${prefix}:FormData`)?.[0] ||
|
||||||
bpmnInstances().moddle.create(`${prefix}:FormData`, { fields: [] })
|
bpmnInstances().moddle.create(`${prefix}:FormData`, { fields: [] })
|
||||||
|
|
||||||
// 业务标识 businessKey, 绑定在 formData 中
|
// 业务标识 businessKey, 绑定在 formData 中
|
||||||
businessKey.value = formData.value.businessKey
|
businessKey.value = formData.value.businessKey
|
||||||
|
|
||||||
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
||||||
otherExtensions.value = elExtensionElements.value.values.filter(
|
otherExtensions.value = elExtensionElements.value.values.filter(
|
||||||
(ex) => ex.$type !== `${prefix}:FormData`
|
(ex) => ex.$type !== `${prefix}:FormData`
|
||||||
)
|
)
|
||||||
|
|
||||||
// 复制原始值,填充表格
|
// 复制原始值,填充表格
|
||||||
fieldList.value = JSON.parse(JSON.stringify(formData.value.fields || []))
|
fieldList.value = JSON.parse(JSON.stringify(formData.value.fields || []))
|
||||||
|
|
||||||
// 更新元素扩展属性,避免后续报错
|
// 更新元素扩展属性,避免后续报错
|
||||||
updateElementExtensions()
|
updateElementExtensions()
|
||||||
}
|
}
|
||||||
const updateElementFormKey = () => {
|
const updateElementFormKey = () => {
|
||||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
||||||
formKey: formKey.value
|
formKey: formKey.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const updateElementBusinessKey = () => {
|
const updateElementBusinessKey = () => {
|
||||||
bpmnInstances().modeling.updateModdleProperties(toRaw(bpmnELement.value), formData.value, {
|
bpmnInstances().modeling.updateModdleProperties(toRaw(bpmnELement.value), formData.value, {
|
||||||
businessKey: businessKey.value
|
businessKey: businessKey.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 根据类型调整字段type
|
// 根据类型调整字段type
|
||||||
const changeFieldTypeType = (type) => {
|
const changeFieldTypeType = (type) => {
|
||||||
// this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
|
// this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
|
||||||
formFieldForm.value['type'] = type === 'custom' ? '' : type
|
formFieldForm.value['type'] = type === 'custom' ? '' : type
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开字段详情侧边栏
|
// 打开字段详情侧边栏
|
||||||
const openFieldForm = (field, index) => {
|
const openFieldForm = (field, index) => {
|
||||||
formFieldIndex.value = index
|
formFieldIndex.value = index
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const FieldObject = formData.value.fields[index]
|
const FieldObject = formData.value.fields[index]
|
||||||
formFieldForm.value = JSON.parse(JSON.stringify(field))
|
formFieldForm.value = JSON.parse(JSON.stringify(field))
|
||||||
// 设置自定义类型
|
// 设置自定义类型
|
||||||
// this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
|
// this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
|
||||||
formFieldForm.value['typeType'] = !fieldType.value[field.type] ? 'custom' : field.type
|
formFieldForm.value['typeType'] = !fieldType.value[field.type] ? 'custom' : field.type
|
||||||
// 初始化枚举值列表
|
// 初始化枚举值列表
|
||||||
field.type === 'enum' &&
|
field.type === 'enum' &&
|
||||||
(fieldEnumList.value = JSON.parse(JSON.stringify(FieldObject?.values || [])))
|
(fieldEnumList.value = JSON.parse(JSON.stringify(FieldObject?.values || [])))
|
||||||
// 初始化约束条件列表
|
// 初始化约束条件列表
|
||||||
fieldConstraintsList.value = JSON.parse(
|
fieldConstraintsList.value = JSON.parse(
|
||||||
JSON.stringify(FieldObject?.validation?.constraints || [])
|
JSON.stringify(FieldObject?.validation?.constraints || [])
|
||||||
)
|
)
|
||||||
// 初始化自定义属性列表
|
// 初始化自定义属性列表
|
||||||
fieldPropertiesList.value = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []))
|
fieldPropertiesList.value = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []))
|
||||||
} else {
|
} else {
|
||||||
formFieldForm.value = {}
|
formFieldForm.value = {}
|
||||||
// 初始化枚举值列表
|
// 初始化枚举值列表
|
||||||
fieldEnumList.value = []
|
fieldEnumList.value = []
|
||||||
// 初始化约束条件列表
|
// 初始化约束条件列表
|
||||||
fieldConstraintsList.value = []
|
fieldConstraintsList.value = []
|
||||||
// 初始化自定义属性列表
|
// 初始化自定义属性列表
|
||||||
fieldPropertiesList.value = []
|
fieldPropertiesList.value = []
|
||||||
}
|
}
|
||||||
fieldModelVisible.value = true
|
fieldModelVisible.value = true
|
||||||
}
|
}
|
||||||
// 打开字段 某个 配置项 弹窗
|
// 打开字段 某个 配置项 弹窗
|
||||||
const openFieldOptionForm = (option, index, type) => {
|
const openFieldOptionForm = (option, index, type) => {
|
||||||
fieldOptionModelVisible.value = true
|
fieldOptionModelVisible.value = true
|
||||||
fieldOptionType.value = type
|
fieldOptionType.value = type
|
||||||
formFieldOptionIndex.value = index
|
formFieldOptionIndex.value = index
|
||||||
if (type === 'property') {
|
if (type === 'property') {
|
||||||
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
|
return (optionModelTitle.value = '属性配置')
|
||||||
|
}
|
||||||
|
if (type === 'enum') {
|
||||||
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
|
return (optionModelTitle.value = '枚举值配置')
|
||||||
|
}
|
||||||
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
return (optionModelTitle.value = '属性配置')
|
return (optionModelTitle.value = '约束条件配置')
|
||||||
}
|
|
||||||
if (type === 'enum') {
|
|
||||||
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
|
||||||
return (optionModelTitle.value = '枚举值配置')
|
|
||||||
}
|
|
||||||
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
|
||||||
return (optionModelTitle.value = '约束条件配置')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存字段 某个 配置项
|
// 保存字段 某个 配置项
|
||||||
const saveFieldOption = () => {
|
const saveFieldOption = () => {
|
||||||
if (formFieldOptionIndex.value === -1) {
|
if (formFieldOptionIndex.value === -1) {
|
||||||
if (fieldOptionType.value === 'property') {
|
if (fieldOptionType.value === 'property') {
|
||||||
fieldPropertiesList.value.push(fieldOptionForm.value)
|
fieldPropertiesList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
if (fieldOptionType.value === 'constraint') {
|
||||||
|
fieldConstraintsList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
if (fieldOptionType.value === 'enum') {
|
||||||
|
fieldEnumList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldOptionType.value === 'property' &&
|
||||||
|
fieldPropertiesList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
|
fieldOptionType.value === 'constraint' &&
|
||||||
|
fieldConstraintsList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
|
fieldOptionType.value === 'enum' &&
|
||||||
|
fieldEnumList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
}
|
}
|
||||||
if (fieldOptionType.value === 'constraint') {
|
fieldOptionModelVisible.value = false
|
||||||
fieldConstraintsList.value.push(fieldOptionForm.value)
|
fieldOptionForm.value = {}
|
||||||
}
|
|
||||||
if (fieldOptionType.value === 'enum') {
|
|
||||||
fieldEnumList.value.push(fieldOptionForm.value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fieldOptionType.value === 'property' &&
|
|
||||||
fieldPropertiesList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
|
||||||
fieldOptionType.value === 'constraint' &&
|
|
||||||
fieldConstraintsList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
|
||||||
fieldOptionType.value === 'enum' &&
|
|
||||||
fieldEnumList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
|
||||||
}
|
|
||||||
fieldOptionModelVisible.value = false
|
|
||||||
fieldOptionForm.value = {}
|
|
||||||
}
|
}
|
||||||
// 保存字段配置
|
// 保存字段配置
|
||||||
const saveField = () => {
|
const saveField = () => {
|
||||||
const { id, type, label, defaultValue, datePattern } = formFieldForm.value
|
const { id, type, label, defaultValue, datePattern } = formFieldForm.value
|
||||||
const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, { id, type, label })
|
const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, { id, type, label })
|
||||||
defaultValue && (Field.defaultValue = defaultValue)
|
defaultValue && (Field.defaultValue = defaultValue)
|
||||||
datePattern && (Field.datePattern = datePattern)
|
datePattern && (Field.datePattern = datePattern)
|
||||||
// 构建属性
|
// 构建属性
|
||||||
if (fieldPropertiesList.value && fieldPropertiesList.value.length) {
|
if (fieldPropertiesList.value && fieldPropertiesList.value.length) {
|
||||||
const fieldPropertyList = fieldPropertiesList.value.map((fp) => {
|
const fieldPropertyList = fieldPropertiesList.value.map((fp) => {
|
||||||
return bpmnInstances().moddle.create(`${prefix}:Property`, {
|
return bpmnInstances().moddle.create(`${prefix}:Property`, {
|
||||||
id: fp.id,
|
id: fp.id,
|
||||||
value: fp.value
|
value: fp.value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
||||||
values: fieldPropertyList
|
values: fieldPropertyList
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 构建校验规则
|
// 构建校验规则
|
||||||
if (fieldConstraintsList.value && fieldConstraintsList.value.length) {
|
if (fieldConstraintsList.value && fieldConstraintsList.value.length) {
|
||||||
const fieldConstraintList = fieldConstraintsList.value.map((fc) => {
|
const fieldConstraintList = fieldConstraintsList.value.map((fc) => {
|
||||||
return bpmnInstances().moddle.create(`${prefix}:Constraint`, {
|
return bpmnInstances().moddle.create(`${prefix}:Constraint`, {
|
||||||
name: fc.name,
|
name: fc.name,
|
||||||
config: fc.config
|
config: fc.config
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, {
|
Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, {
|
||||||
constraints: fieldConstraintList
|
constraints: fieldConstraintList
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 构建枚举值
|
// 构建枚举值
|
||||||
if (fieldEnumList.value && fieldEnumList.value.length) {
|
if (fieldEnumList.value && fieldEnumList.value.length) {
|
||||||
Field.values = fieldEnumList.value.map((fe) => {
|
Field.values = fieldEnumList.value.map((fe) => {
|
||||||
return bpmnInstances().moddle.create(`${prefix}:Value`, { name: fe.name, id: fe.id })
|
return bpmnInstances().moddle.create(`${prefix}:Value`, { name: fe.name, id: fe.id })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 更新数组 与 表单配置实例
|
// 更新数组 与 表单配置实例
|
||||||
if (formFieldIndex.value === -1) {
|
if (formFieldIndex.value === -1) {
|
||||||
fieldList.value.push(formFieldForm.value)
|
fieldList.value.push(formFieldForm.value)
|
||||||
formData.value.fields.push(Field)
|
formData.value.fields.push(Field)
|
||||||
} else {
|
} else {
|
||||||
fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value)
|
fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value)
|
||||||
formData.value.fields.splice(formFieldIndex.value, 1, Field)
|
formData.value.fields.splice(formFieldIndex.value, 1, Field)
|
||||||
}
|
}
|
||||||
updateElementExtensions()
|
updateElementExtensions()
|
||||||
fieldModelVisible.value = false
|
fieldModelVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除某个 字段的 配置项
|
// 移除某个 字段的 配置项
|
||||||
const removeFieldOptionItem = (option, index, type) => {
|
const removeFieldOptionItem = (option, index, type) => {
|
||||||
// console.log(option, 'option')
|
// console.log(option, 'option')
|
||||||
if (type === 'property') {
|
if (type === 'property') {
|
||||||
fieldPropertiesList.value.splice(index, 1)
|
fieldPropertiesList.value.splice(index, 1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (type === 'enum') {
|
if (type === 'enum') {
|
||||||
fieldEnumList.value.splice(index, 1)
|
fieldEnumList.value.splice(index, 1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fieldConstraintsList.value.splice(index, 1)
|
fieldConstraintsList.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
// 移除 字段
|
// 移除 字段
|
||||||
const removeField = (field, index) => {
|
const removeField = (field, index) => {
|
||||||
console.log(field, 'field')
|
console.log(field, 'field')
|
||||||
fieldList.value.splice(index, 1)
|
fieldList.value.splice(index, 1)
|
||||||
formData.value.fields.splice(index, 1)
|
formData.value.fields.splice(index, 1)
|
||||||
updateElementExtensions()
|
updateElementExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateElementExtensions = () => {
|
const updateElementExtensions = () => {
|
||||||
// 更新回扩展元素
|
// 更新回扩展元素
|
||||||
const newElExtensionElements = bpmnInstances().moddle.create(`bpmn:ExtensionElements`, {
|
const newElExtensionElements = bpmnInstances().moddle.create(`bpmn:ExtensionElements`, {
|
||||||
values: otherExtensions.value.concat(formData.value)
|
values: otherExtensions.value.concat(formData.value)
|
||||||
})
|
})
|
||||||
// 更新到元素上
|
// 更新到元素上
|
||||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
||||||
extensionElements: newElExtensionElements
|
extensionElements: newElExtensionElements
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const formList = ref([]) // 流程表单的下拉框的数据
|
const formList = ref([]) // 流程表单的下拉框的数据
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
formList.value = await FormApi.getFormSimpleList()
|
await listAllWFForm().then(res => {
|
||||||
|
formList.value = res.data
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.id,
|
() => props.id,
|
||||||
(val) => {
|
(val) => {
|
||||||
val &&
|
val &&
|
||||||
val.length &&
|
val.length &&
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
resetFormList()
|
resetFormList()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,234 +1,182 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form label-width="100px">
|
<div>
|
||||||
<el-form-item label="规则类型" prop="candidateStrategy">
|
|
||||||
<el-select
|
<div v-if="dataType === 'ROLES'">
|
||||||
v-model="userTaskForm.candidateStrategy"
|
<el-select v-model='roleIds' :size='mini' placeholder='请选择 角色' @change='changeSelectRoles'>
|
||||||
clearable
|
<el-option
|
||||||
style="width: 100%"
|
v-for='item in roleOptions'
|
||||||
@change="changeCandidateStrategy"
|
:key='item.id'
|
||||||
>
|
:label='item.name'
|
||||||
<!-- <el-option
|
:value='`ROLE${item.id}`'
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)"
|
>
|
||||||
:key="dict.value"
|
</el-option>
|
||||||
:label="dict.label"
|
</el-select>
|
||||||
:value="dict.value"
|
</div>
|
||||||
/> -->
|
</div>
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy == 10"
|
|
||||||
label="指定角色"
|
|
||||||
prop="candidateParam"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="userTaskForm.candidateParam"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
style="width: 100%"
|
|
||||||
@change="updateElementTask"
|
|
||||||
>
|
|
||||||
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy == 20 || userTaskForm.candidateStrategy == 21"
|
|
||||||
label="指定部门"
|
|
||||||
prop="candidateParam"
|
|
||||||
span="24"
|
|
||||||
>
|
|
||||||
<el-tree-select
|
|
||||||
ref="treeRef"
|
|
||||||
v-model="userTaskForm.candidateParam"
|
|
||||||
:data="deptTreeOptions"
|
|
||||||
:props="defaultProps"
|
|
||||||
empty-text="加载中,请稍后"
|
|
||||||
multiple
|
|
||||||
node-key="id"
|
|
||||||
show-checkbox
|
|
||||||
@change="updateElementTask"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy == 22"
|
|
||||||
label="指定岗位"
|
|
||||||
prop="candidateParam"
|
|
||||||
span="24"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="userTaskForm.candidateParam"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
style="width: 100%"
|
|
||||||
@change="updateElementTask"
|
|
||||||
>
|
|
||||||
<el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy == 30"
|
|
||||||
label="指定用户"
|
|
||||||
prop="candidateParam"
|
|
||||||
span="24"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="userTaskForm.candidateParam"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
style="width: 100%"
|
|
||||||
@change="updateElementTask"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy === 40"
|
|
||||||
label="指定用户组"
|
|
||||||
prop="candidateParam"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="userTaskForm.candidateParam"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
style="width: 100%"
|
|
||||||
@change="updateElementTask"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userGroupOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="userTaskForm.candidateStrategy === 60"
|
|
||||||
label="流程表达式"
|
|
||||||
prop="candidateParam"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="userTaskForm.candidateParam[0]"
|
|
||||||
clearable
|
|
||||||
style="width: 72%"
|
|
||||||
@change="updateElementTask"
|
|
||||||
/>
|
|
||||||
<el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
|
|
||||||
>选择表达式</el-button
|
|
||||||
>
|
|
||||||
<!-- 选择弹窗 -->
|
|
||||||
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
// import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { listAllUserByDeptId } from '@/api/user-boot/user'
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount, toRaw, nextTick } from 'vue'
|
||||||
|
|
||||||
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
interface UserTaskForm {
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
dataType: any,
|
||||||
import * as RoleApi from '@/api/system/role'
|
assignee: any,
|
||||||
import * as DeptApi from '@/api/system/dept'
|
candidateUsers: any,
|
||||||
import * as PostApi from '@/api/system/post'
|
candidateGroups: any,
|
||||||
import * as UserApi from '@/api/system/user'
|
text: any
|
||||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
}
|
||||||
import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'UserTask' })
|
const userTaskForm: UserTaskForm = reactive({
|
||||||
const props = defineProps({
|
dataType: '',
|
||||||
id: String,
|
assignee: '',
|
||||||
type: String
|
candidateUsers: '',
|
||||||
})
|
candidateGroups: '',
|
||||||
const userTaskForm = ref({
|
text: []
|
||||||
candidateStrategy: undefined, // 分配规则
|
// dueDate: '',
|
||||||
candidateParam: [] // 分配选项
|
// followUpDate: '',
|
||||||
|
// priority: ''
|
||||||
})
|
})
|
||||||
|
const mini = ref('small')
|
||||||
|
const dataType = ref('ROLES')
|
||||||
|
const multiLoopType = ref('Null')
|
||||||
|
const roleIds = ref('')
|
||||||
|
const roleOptions = ref([{ id: '', name: '' }])
|
||||||
const bpmnElement = ref()
|
const bpmnElement = ref()
|
||||||
|
const isSequential = ref(false)
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
const bpmnInstances = () => (window as any)?.bpmnInstances
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
const changeSelectRoles = (val: any) => {
|
||||||
|
let groups = null
|
||||||
|
let text = null
|
||||||
|
if (val && val.length > 0) {
|
||||||
|
userTaskForm.dataType = 'ROLES'
|
||||||
|
groups = val.join() || null
|
||||||
|
let textArr = roleOptions.value.filter(k => val.indexOf(`ROLE${k.id}`) >= 0)
|
||||||
|
text = textArr?.map(k => k.name).join() || null
|
||||||
|
} else {
|
||||||
|
userTaskForm.dataType = ''
|
||||||
|
multiLoopType.value = 'Null'
|
||||||
|
}
|
||||||
|
userTaskForm.candidateGroups = groups
|
||||||
|
userTaskForm.text = text
|
||||||
|
updateElementTask()
|
||||||
|
}
|
||||||
|
|
||||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
/**
|
||||||
const deptTreeOptions = ref() // 部门树
|
* 跟新节点数据
|
||||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
*/
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
const updateElementTask = () => {
|
||||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
const taskAttr = Object.create(null)
|
||||||
|
for (let key in userTaskForm) {
|
||||||
|
taskAttr[key] = userTaskForm[key]
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(bpmnElement.value, taskAttr)
|
||||||
|
}
|
||||||
|
|
||||||
const resetTaskForm = () => {
|
const resetTaskForm = () => {
|
||||||
const businessObject = bpmnElement.value.businessObject
|
const bpmnElementObj = bpmnElement.value?.businessObject
|
||||||
if (!businessObject) {
|
if (!bpmnElementObj) {
|
||||||
return
|
return
|
||||||
}
|
|
||||||
if (businessObject.candidateStrategy != undefined) {
|
|
||||||
userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
|
|
||||||
} else {
|
|
||||||
userTaskForm.value.candidateStrategy = undefined
|
|
||||||
}
|
|
||||||
if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
|
|
||||||
if (userTaskForm.value.candidateStrategy === 60) {
|
|
||||||
// 特殊:流程表达式,只有一个 input 输入框
|
|
||||||
userTaskForm.value.candidateParam = [businessObject.candidateParam]
|
|
||||||
} else {
|
|
||||||
userTaskForm.value.candidateParam = businessObject.candidateParam
|
|
||||||
.split(',')
|
|
||||||
.map((item) => +item)
|
|
||||||
}
|
}
|
||||||
} else {
|
clearOptionsData()
|
||||||
userTaskForm.value.candidateParam = []
|
getRoleOptions()
|
||||||
}
|
let roleIdData = bpmnElementObj['candidateGroups'] || []
|
||||||
|
if (roleIdData && roleIdData.length > 0) {
|
||||||
|
roleIds.value = roleIdData.split(',')
|
||||||
|
}
|
||||||
|
getElementLoop(bpmnElementObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
|
/**
|
||||||
const changeCandidateStrategy = () => {
|
* 清空选项数据
|
||||||
userTaskForm.value.candidateParam = []
|
*/
|
||||||
updateElementTask()
|
const clearOptionsData = () => {
|
||||||
|
// this.selectedUser.ids = []
|
||||||
|
// this.selectedUser.text = []
|
||||||
|
roleIds.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 选中某个 options 时候,更新 bpmn 图 */
|
const getRoleOptions = () => {
|
||||||
const updateElementTask = () => {
|
if (!roleOptions.value || roleOptions.value.length <= 0) {
|
||||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
listAllUserByDeptId('').then(res => {
|
||||||
candidateStrategy: userTaskForm.value.candidateStrategy,
|
roleOptions.value = res.data
|
||||||
candidateParam: userTaskForm.value.candidateParam.join(',')
|
//默认第一个
|
||||||
})
|
if (roleOptions.value.length > 0) {
|
||||||
|
roleIds.value = roleOptions.value[0].id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开监听器弹窗
|
const getElementLoop = (businessObject: any) => {
|
||||||
const processExpressionDialogRef = ref()
|
if (!businessObject.loopCharacteristics) {
|
||||||
const openProcessExpressionDialog = async () => {
|
multiLoopType.value = 'Null'
|
||||||
processExpressionDialogRef.value.open()
|
return
|
||||||
}
|
}
|
||||||
const selectProcessExpression = (expression) => {
|
isSequential.value = businessObject.loopCharacteristics.isSequential
|
||||||
userTaskForm.value.candidateParam = [expression.expression]
|
if (businessObject.loopCharacteristics.completionCondition) {
|
||||||
|
if (businessObject.loopCharacteristics.completionCondition.body === '${nrOfCompletedInstances >= nrOfInstances}') {
|
||||||
|
multiLoopType.value = 'SequentialMultiInstance'
|
||||||
|
} else {
|
||||||
|
multiLoopType.value = 'ParallelMultiInstance'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.id,
|
() => props.id,
|
||||||
() => {
|
() => {
|
||||||
bpmnElement.value = bpmnInstances().bpmnElement
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
resetTaskForm()
|
resetTaskForm()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 获得角色列表
|
// 获得角色列表
|
||||||
roleOptions.value = await RoleApi.getSimpleRoleList()
|
// roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||||
// 获得部门列表
|
// 获得用户列表
|
||||||
const deptOptions = await DeptApi.getSimpleDeptList()
|
await listAllUserByDeptId('').then(res => {
|
||||||
deptTreeOptions.value = handleTree(deptOptions, 'id')
|
roleOptions.value = res.data
|
||||||
// 获得岗位列表
|
})
|
||||||
postOptions.value = await PostApi.getSimplePostList()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 获得用户组列表
|
|
||||||
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
bpmnElement.value = null
|
bpmnElement.value = null
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang='scss'>
|
||||||
|
.el-row .el-radio-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.el-radio {
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
+ .el-tag {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-label {
|
||||||
|
padding-left: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ VXETable.use(VXETablePluginExportXLSX, {
|
|||||||
})
|
})
|
||||||
window.XEUtils = XEUtils
|
window.XEUtils = XEUtils
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
//开启离线地图
|
//开启离线地图
|
||||||
// app.use(BaiduMapOffline, {
|
// app.use(BaiduMapOffline, {
|
||||||
|
|||||||
@@ -124,6 +124,14 @@ export const adminBaseRoute = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: pageTitle('router.formDesigner')
|
title: pageTitle('router.formDesigner')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'configSteps',
|
||||||
|
component: () => import('@/views/system/workflow/model/configSteps.vue'),
|
||||||
|
name: '模型设计器页面',
|
||||||
|
meta: {
|
||||||
|
title: pageTitle('router.formDesigner')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,4 +2,7 @@
|
|||||||
export const ADVANCE_BOOT = '/advance-boot'
|
export const ADVANCE_BOOT = '/advance-boot'
|
||||||
|
|
||||||
//技术监督
|
//技术监督
|
||||||
export const PROCESS_BOOT = '/process-boot'
|
export const PROCESS_BOOT = '/process-boot'
|
||||||
|
|
||||||
|
//工作流模块
|
||||||
|
export const WORKFLOW_BOOT = '/workflow-boot'
|
||||||
51
src/utils/formRules.js
Normal file
51
src/utils/formRules.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||||
|
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||||
|
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||||
|
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||||
|
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||||
|
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||||
|
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||||
|
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||||
|
*/
|
||||||
|
export const required = (message, trigger = ['blur', 'change']) => ({
|
||||||
|
required: true,
|
||||||
|
message,
|
||||||
|
trigger
|
||||||
|
})
|
||||||
|
|
||||||
|
// 常用正则规则大全:https://any86.github.io/any-rule/
|
||||||
|
|
||||||
|
export const rules = {
|
||||||
|
phone: {
|
||||||
|
pattern: /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/,
|
||||||
|
message: '请填写符合要求的11位手机号',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
|
||||||
|
message: '请填写正确的邮箱号',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
idCard: {
|
||||||
|
pattern:
|
||||||
|
/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
|
||||||
|
message: '请填写符合要求的身份证号',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
lettersNum: {
|
||||||
|
pattern: /^[A-Za-z0-9]+$/,
|
||||||
|
message: '填写内容须是字母或数字组成',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
pattern: /^\d{1,}$/,
|
||||||
|
message: '填写内容必须是纯数字',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
pattern: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/,
|
||||||
|
message: '只支持正数金额',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
}
|
||||||
161
src/utils/tool.js
Normal file
161
src/utils/tool.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
/**
|
||||||
|
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||||
|
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||||
|
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||||
|
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||||
|
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||||
|
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||||
|
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||||
|
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @Descripttion: 工具集
|
||||||
|
* @version: 1.1
|
||||||
|
* @LastEditors: yubaoshan
|
||||||
|
* @LastEditTime: 2022年4月19日10:58:41
|
||||||
|
*/
|
||||||
|
const tool = {}
|
||||||
|
|
||||||
|
// localStorage
|
||||||
|
tool.data = {
|
||||||
|
set(table, settings) {
|
||||||
|
const _set = JSON.stringify(settings)
|
||||||
|
return localStorage.setItem(table, _set)
|
||||||
|
},
|
||||||
|
get(table) {
|
||||||
|
let data = localStorage.getItem(table)
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
remove(table) {
|
||||||
|
return localStorage.removeItem(table)
|
||||||
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
return localStorage.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionStorage
|
||||||
|
tool.session = {
|
||||||
|
set(table, settings) {
|
||||||
|
const _set = JSON.stringify(settings)
|
||||||
|
return sessionStorage.setItem(table, _set)
|
||||||
|
},
|
||||||
|
get(table) {
|
||||||
|
let data = sessionStorage.getItem(table)
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
remove(table) {
|
||||||
|
return sessionStorage.removeItem(table)
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
return sessionStorage.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 千分符
|
||||||
|
tool.groupSeparator = (num) => {
|
||||||
|
num = `${num}`
|
||||||
|
if (!num.includes('.')) num += '.'
|
||||||
|
|
||||||
|
return num
|
||||||
|
.replace(/(\d)(?=(\d{3})+\.)/g, ($0, $1) => {
|
||||||
|
return `${$1},`
|
||||||
|
})
|
||||||
|
.replace(/\.$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有字典数组
|
||||||
|
tool.dictDataAll = () => {
|
||||||
|
return tool.data.get('DICT_TYPE_TREE_DATA')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字典翻译方法,界面插槽使用方法 {{ $TOOL.dictType('sex', record.sex) }}
|
||||||
|
tool.dictTypeData = (dictValue, value) => {
|
||||||
|
const dictTypeTree = tool.dictDataAll()
|
||||||
|
if (!dictTypeTree) {
|
||||||
|
return '需重新登录'
|
||||||
|
}
|
||||||
|
const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
|
||||||
|
if (!tree) {
|
||||||
|
return '无此字典'
|
||||||
|
}
|
||||||
|
const children = tree.children
|
||||||
|
const dict = children.find((item) => item.dictValue === value)
|
||||||
|
return dict ? dict.dictLabel : '无此字典项'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某个code下字典的列表,多用于字典下拉框
|
||||||
|
tool.dictTypeList = (dictValue) => {
|
||||||
|
const dictTypeTree = tool.dictDataAll()
|
||||||
|
if (!dictTypeTree) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
|
||||||
|
if (tree && tree.children) {
|
||||||
|
return tree.children
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某个code下字典的列表,基于dictTypeList 改进,保留老的,逐步替换
|
||||||
|
tool.dictList = (dictValue) => {
|
||||||
|
const dictTypeTree = tool.dictDataAll()
|
||||||
|
if (!dictTypeTree) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
|
||||||
|
if (tree) {
|
||||||
|
return tree.children.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item['dictValue'],
|
||||||
|
label: item['name']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 树形翻译 需要指定最顶级的 parentValue 和当级的value
|
||||||
|
tool.translateTree = (parentValue, value) => {
|
||||||
|
const tree = tool.dictDataAll().find((item) => item.dictValue === parentValue)
|
||||||
|
const targetNode = findNodeByValue(tree, value)
|
||||||
|
return targetNode ? targetNode.dictLabel : ''
|
||||||
|
}
|
||||||
|
const findNodeByValue = (node, value) => {
|
||||||
|
if (node.dictValue === value) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
const result = findNodeByValue(node.children[i], value)
|
||||||
|
if (result) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成UUID
|
||||||
|
tool.snowyUuid = () => {
|
||||||
|
let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
let r = (Math.random() * 16) | 0,
|
||||||
|
v = c === 'x' ? r : (r & 0x3) | 0x8
|
||||||
|
return v.toString(16)
|
||||||
|
})
|
||||||
|
// 首字符转换成字母
|
||||||
|
return 'xn' + uuid.slice(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default tool
|
||||||
@@ -90,12 +90,12 @@ onMounted(async () => {
|
|||||||
// 首次创建的 Model 模型,它是没有 bpmnXml,此时需要给它一个默认的
|
// 首次创建的 Model 模型,它是没有 bpmnXml,此时需要给它一个默认的
|
||||||
data.key = Math.random().toString(36).slice(-8)
|
data.key = Math.random().toString(36).slice(-8)
|
||||||
data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
|
data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
|
||||||
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
|
<bpmn2:definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
|
||||||
<process id="flow_${data.key}" name="${data.name || ''}" />
|
<process id="flow_${data.key}" name="${data.name || ''}" />
|
||||||
<bpmndi:BPMNDiagram id="BPMNDiagram">
|
<bpmndi:BPMNDiagram id="BPMNDiagram">
|
||||||
<bpmndi:BPMNPlane id="flow_${data.key}_di" bpmnElement="flow_${data.key}" />
|
<bpmndi:BPMNPlane id="flow_${data.key}_di" bpmnElement="flow_${data.key}" />
|
||||||
</bpmndi:BPMNDiagram>
|
</bpmndi:BPMNDiagram>
|
||||||
</definitions>`
|
</bpmn2:definitions>`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await ModelApi.readXml(prop.model?.deploymentId).then((res: any) => {
|
await ModelApi.readXml(prop.model?.deploymentId).then((res: any) => {
|
||||||
|
|||||||
363
src/views/system/workflow/model/configSteps.vue
Normal file
363
src/views/system/workflow/model/configSteps.vue
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card class='steps-card' :bordered='false'>
|
||||||
|
<el-row class='xn-row'>
|
||||||
|
<el-col :span='6'></el-col>
|
||||||
|
<el-col :span='12'>
|
||||||
|
<el-steps :current='current'>
|
||||||
|
<el-step v-for='item in steps' :key='item.title' :title='item.title'>
|
||||||
|
<template #icon>
|
||||||
|
<el-icon color='#409efc' class='no-inherit' v-if="item.title === '表单设计'">
|
||||||
|
<SetUp />
|
||||||
|
</el-icon>
|
||||||
|
|
||||||
|
<el-icon color='#409efc' class='no-inherit' v-if="item.title === '模型设计'">
|
||||||
|
<PieChart />
|
||||||
|
</el-icon>
|
||||||
|
|
||||||
|
<el-icon color='#409efc' class='no-inherit' v-if="item.title === '完成'">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
</el-step>
|
||||||
|
</el-steps>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span='6'>
|
||||||
|
<div style='float: right'>
|
||||||
|
<el-button :disabled='current !== 1' style='margin-left: 8px' @click='prev'>
|
||||||
|
<template #icon>
|
||||||
|
<Back />
|
||||||
|
</template>
|
||||||
|
后退
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="recordData.formType === 'DESIGN' ? current === 2 : current === 1"
|
||||||
|
type='primary' style='margin-left: 8px' @click='next'>
|
||||||
|
<template #icon>
|
||||||
|
<Right />
|
||||||
|
</template>
|
||||||
|
继续
|
||||||
|
</el-button>
|
||||||
|
<el-button type='primary' danger ghost style='margin-left: 8px' @click="emit('previousPage')">
|
||||||
|
<template #icon>
|
||||||
|
<Close />
|
||||||
|
</template>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div v-if='current === 0'>
|
||||||
|
<!-- <form-design ref="formDesign" v-if="recordData.formType === 'DESIGN'" :form-value="workFlowParame.formValue" />-->
|
||||||
|
<!-- <model-design ref="modelDesign" v-else />-->
|
||||||
|
<model-design ref='modelDesign' />
|
||||||
|
</div>
|
||||||
|
<div v-if='current === 1'>
|
||||||
|
<model-design ref='modelDesign' v-if="recordData.formType === 'DESIGN'" />
|
||||||
|
<el-card v-else>
|
||||||
|
<el-result status='success' title='操作成功' sub-title='此时您可以一键部署此流程啦'>
|
||||||
|
<template #extra>
|
||||||
|
<el-space size='middle'>
|
||||||
|
<el-button v-if='current > 0' style='margin-left: 8px' @click='prev'>
|
||||||
|
<template #icon>
|
||||||
|
<Back />
|
||||||
|
</template>
|
||||||
|
后退
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if='current == steps.length - 1'
|
||||||
|
type='primary'
|
||||||
|
:loading='submitLoading'
|
||||||
|
@click='seveDeployment'
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<Tools />
|
||||||
|
</template>
|
||||||
|
部署运行
|
||||||
|
</el-button>
|
||||||
|
</el-space>
|
||||||
|
</template>
|
||||||
|
</el-result>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
<div v-if='current === 2'>
|
||||||
|
<el-card>
|
||||||
|
<el-result status='success' title='操作成功' sub-title='此时您可以一键部署此流程啦'>
|
||||||
|
<template #extra>
|
||||||
|
<el-space size='middle'>
|
||||||
|
<el-button v-if='current > 0' style='margin-left: 8px' @click='prev'>
|
||||||
|
<template #icon>
|
||||||
|
<Back />
|
||||||
|
</template>
|
||||||
|
后退
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if='current == steps.length - 1'
|
||||||
|
type='primary'
|
||||||
|
:loading='submitLoading'
|
||||||
|
@click='seveDeployment'
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<Tools />
|
||||||
|
</template>
|
||||||
|
部署运行
|
||||||
|
</el-button>
|
||||||
|
</el-space>
|
||||||
|
</template>
|
||||||
|
</el-result>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Plus, SetUp, Select, PieChart, Back, Right, Close, Tools } from '@element-plus/icons-vue'
|
||||||
|
import { defineComponent, nextTick, ref } from 'vue'
|
||||||
|
import ModelDesign from './modelDesign.vue'
|
||||||
|
// import FormDesign from './formDesign.vue'
|
||||||
|
import config from '@/components/XnWorkflow/nodes/config/config'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
|
const emit = defineEmits({ previousPage: null })
|
||||||
|
|
||||||
|
const current = ref(0)
|
||||||
|
// const formDesign = ref(null)
|
||||||
|
const modelDesign = ref(null)
|
||||||
|
|
||||||
|
const recordData = ref({})
|
||||||
|
const tableJson = ref([])
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
// 分布步骤数据
|
||||||
|
const steps = ref([
|
||||||
|
{
|
||||||
|
title: '模型设计',
|
||||||
|
content: '模型设计'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '完成',
|
||||||
|
content: '您的流程已经完成,是否立即部署运行'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
// 整个工作流的数据模型外壳
|
||||||
|
const workFlowParame = {
|
||||||
|
formValue: {},
|
||||||
|
modelValue: cloneDeep(config.nodeModel.node)
|
||||||
|
}
|
||||||
|
// 打开这个界面
|
||||||
|
const configSteps = (record) => {
|
||||||
|
// 全局赋值
|
||||||
|
recordData.value = record
|
||||||
|
workFlowParame.formValue = record.formJson == null ? false : JSON.parse(record.formJson)
|
||||||
|
if (record.processJson == null) {
|
||||||
|
// 给模型设计器基础外壳给值
|
||||||
|
workFlowParame.modelValue.id = tool.snowyUuid()
|
||||||
|
workFlowParame.modelValue.type = 'process'
|
||||||
|
workFlowParame.modelValue.title = record.name
|
||||||
|
workFlowParame.modelValue.properties.configInfo = JSON.parse(
|
||||||
|
JSON.stringify(config.nodeConfigInfo.processConfigInfo)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 创建参与人节点
|
||||||
|
const userTaskNode = cloneDeep(config.nodeModel.node)
|
||||||
|
userTaskNode.id = tool.snowyUuid()
|
||||||
|
userTaskNode.title = '发起申请'
|
||||||
|
userTaskNode.dataLegal = false
|
||||||
|
userTaskNode.type = 'startTask'
|
||||||
|
|
||||||
|
// 创建发起人模型
|
||||||
|
const startEventNode = cloneDeep(config.nodeModel.node)
|
||||||
|
startEventNode.id = tool.snowyUuid()
|
||||||
|
startEventNode.title = '开始'
|
||||||
|
startEventNode.type = 'startEvent'
|
||||||
|
startEventNode.dataLegal = true
|
||||||
|
startEventNode.childNode = userTaskNode
|
||||||
|
|
||||||
|
// 装回全局模型中
|
||||||
|
workFlowParame.modelValue.childNode = startEventNode
|
||||||
|
} else {
|
||||||
|
workFlowParame.modelValue = JSON.parse(record.processJson)
|
||||||
|
}
|
||||||
|
// 判断表单类型
|
||||||
|
if (record.formType === 'DESIGN') {
|
||||||
|
// 判断如果是设计表单,那么第一步加入表单设计器
|
||||||
|
steps.value.unshift({
|
||||||
|
title: '表单设计',
|
||||||
|
content: '表单设计'
|
||||||
|
})
|
||||||
|
nextTick(() => {
|
||||||
|
// 给表单设计器给值,表单value,table数据结构
|
||||||
|
// formDesign.value.setValue(workFlowParame.formValue, JSON.parse(recordData.value.tableJson))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 给流程设计器给值
|
||||||
|
nextTick(() => {
|
||||||
|
modelDesign.value.setValue(workFlowParame, recordData.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 下一步
|
||||||
|
const next = () => {
|
||||||
|
current.value++
|
||||||
|
// 判断是哪一步
|
||||||
|
if (current.value === 1) {
|
||||||
|
if (recordData.value.formType === 'DESIGN') {
|
||||||
|
// formDesignNext()
|
||||||
|
} else {
|
||||||
|
modelDesignNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current.value === 2) {
|
||||||
|
modelDesignNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// const formDesignNext = () => {
|
||||||
|
// const formValue = formDesign.value.getValue()
|
||||||
|
// if (!formDataVerification(formValue)) {
|
||||||
|
// current.value--
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// workFlowParame.formValue = formValue
|
||||||
|
// nextTick(() => {
|
||||||
|
// modelDesign.value.setValue(workFlowParame, recordData.value)
|
||||||
|
// // 给流程设计器表单的字段
|
||||||
|
// modelDesign.value.setFormFieldListValue(workFlowParame.formValue.list)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
const modelDesignNext = () => {
|
||||||
|
workFlowParame.modelValue = modelDesign.value.getValue()
|
||||||
|
// 这里完成之后,需要调用保存接口,传给后端
|
||||||
|
recordData.value.formJson = JSON.stringify(workFlowParame.formValue)
|
||||||
|
// 校验整体的model设计后的json
|
||||||
|
if (!modelDataVerification(workFlowParame.modelValue)) {
|
||||||
|
current.value--
|
||||||
|
return
|
||||||
|
}
|
||||||
|
recordData.value.processJson = JSON.stringify(workFlowParame.modelValue)
|
||||||
|
// 保存进去
|
||||||
|
modelApi.submitForm(recordData.value, recordData.value.id).then(() => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 上一步
|
||||||
|
const prev = () => {
|
||||||
|
current.value--
|
||||||
|
if (current.value === 0) {
|
||||||
|
nextTick(() => {
|
||||||
|
if (recordData.value.formType === 'DESIGN') {
|
||||||
|
// formDesign.value.setValue(workFlowParame.formValue, JSON.parse(recordData.value.tableJson))
|
||||||
|
} else {
|
||||||
|
modelDesign.value.setValue(workFlowParame, recordData.value)
|
||||||
|
modelDesign.value.setFormFieldListValue(workFlowParame.formValue.list)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (current.value === 1) {
|
||||||
|
nextTick(() => {
|
||||||
|
modelDesign.value.setValue(workFlowParame, recordData.value)
|
||||||
|
modelDesign.value.setFormFieldListValue(workFlowParame.formValue.list)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 部署流程按钮
|
||||||
|
const seveDeployment = () => {
|
||||||
|
const param = {
|
||||||
|
id: recordData.value.id
|
||||||
|
}
|
||||||
|
submitLoading.value = true
|
||||||
|
modelApi
|
||||||
|
.modelDeploy(param)
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success('部署成功')
|
||||||
|
submitLoading.value = false
|
||||||
|
// 恢复到第一步
|
||||||
|
current.value = 0
|
||||||
|
// 告诉上一个界面成功了
|
||||||
|
emit('previousPage')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
submitLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 表单设计器选择好的内容校验
|
||||||
|
const formDataVerification = (formValue) => {
|
||||||
|
let error = 0
|
||||||
|
// 判断是否包含动态表格batch
|
||||||
|
if (formValue.list.length > 0) {
|
||||||
|
for (let i = 0; i < formValue.list.length; i++) {
|
||||||
|
const items = formValue.list[i]
|
||||||
|
// 处理判断动态表格中
|
||||||
|
if (formValue.list[i].type === 'batch') {
|
||||||
|
// 判断动态表格中是否有内容,没内容提示并不能下一步
|
||||||
|
if (items.list.length > 0) {
|
||||||
|
for (let j = 0; j < items.list.length; j++) {
|
||||||
|
const itemsObj = items.list[j]
|
||||||
|
if (!itemsObj.selectColumn) {
|
||||||
|
error++
|
||||||
|
ElMessage.warning('请选择动态表格中的子表字段')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error++
|
||||||
|
ElMessage.warning('包含了动态表格,但未选择表格中内容')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const type = items.type
|
||||||
|
// alert text divider card grid table
|
||||||
|
if (
|
||||||
|
(type !== 'alert') &
|
||||||
|
(type !== 'text') &
|
||||||
|
(type !== 'divider') &
|
||||||
|
(type !== 'card') &
|
||||||
|
(type !== 'grid') &
|
||||||
|
(type !== 'table')
|
||||||
|
) {
|
||||||
|
// 其他项判断确认选择了表,并且选择了字段,但凡有一个没选视为不过
|
||||||
|
if (!items.selectTable || !items.selectColumn) {
|
||||||
|
error++
|
||||||
|
ElMessage.warning('名称为:' + items.label + ' 的组件未选择表或字段')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error++
|
||||||
|
ElMessage.warning('未选择任何组件并赋予字段')
|
||||||
|
}
|
||||||
|
return error === 0
|
||||||
|
}
|
||||||
|
// 模型整体校验
|
||||||
|
const modelDataVerification = (modelValue) => {
|
||||||
|
if (JSON.stringify(modelValue).indexOf('dataLegal":false') !== -1) {
|
||||||
|
ElMessage.warning('流程未配置完成')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 抛出钩子
|
||||||
|
defineExpose({
|
||||||
|
configSteps
|
||||||
|
})
|
||||||
|
defineComponent({
|
||||||
|
ModelDesign
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.steps-card {
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-left: -12px;
|
||||||
|
margin-right: -12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-action {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xn-row {
|
||||||
|
margin-bottom: -10px;
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
<!--流程模型表单页面-->
|
<!--流程模型表单页面-->
|
||||||
<template>
|
<template>
|
||||||
<div class='default-main'>
|
<div class='default-main'>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<template v-slot:select>
|
<template v-slot:select>
|
||||||
<el-form-item label='模型名称'>
|
<el-form-item label='模型名称'>
|
||||||
<el-input
|
<el-input
|
||||||
v-model='tableStore.table.params.name'
|
v-model='tableStore.table.params.searchKey'
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label='流程类别'>
|
<el-form-item label='流程类别'>
|
||||||
<el-select v-model='tableStore.table.params.category' clearable>
|
<el-select v-model='tableStore.table.params.category' clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for='item in categoryList'
|
v-for='item in categoryList'
|
||||||
:key='item.id'
|
:key='item.id'
|
||||||
:label='item.name'
|
:label='item.name'
|
||||||
:value='item.code'
|
:value='item.code'
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:operation>
|
<template v-slot:operation>
|
||||||
<el-button type='primary' class='ml10' :icon='Plus' @click='add'>新增</el-button>
|
<el-button type='primary' class='ml10' :icon='Plus' @click='add'>新增</el-button>
|
||||||
</template>
|
</template>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<!--表格-->
|
<!--表格-->
|
||||||
<Table ref='tableRef' isGroup></Table>
|
<Table ref='tableRef' isGroup></Table>
|
||||||
<model-popup ref='modelPopup' />
|
<model-popup ref='modelPopup' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
@@ -42,132 +42,148 @@ import { useDictData } from '@/stores/dictData'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import ModelPopup from '@/views/system/workflow/model/modelPopup.vue'
|
import ModelPopup from '@/views/system/workflow/model/modelPopup.vue'
|
||||||
|
|
||||||
|
|
||||||
const { push } = useRouter()
|
const { push } = useRouter()
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
const categoryList = dictData.getBasicData('flow_category')
|
const categoryList = dictData.getBasicData('flow_category')
|
||||||
const modelPopup = ref()
|
const modelPopup = ref()
|
||||||
const tableStore = new TableStore({
|
|
||||||
url: '/process-boot/workflow/model/list',
|
|
||||||
method: 'POST',
|
|
||||||
column: [
|
|
||||||
{ title: '序号', type: 'seq', width: 80 },
|
|
||||||
{ title: '模型标识', minWidth: '160', field: 'modelKey' },
|
|
||||||
{ title: '模型名称', minWidth: '160', field: 'modelName' },
|
|
||||||
{
|
|
||||||
title: '流程类别', minWidth: '160', field: 'category',
|
|
||||||
formatter: (row: any) => {
|
|
||||||
return categoryList.filter(item => item.code === row.cellValue)[0].name
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '模型版本', minWidth: '120', field: 'version', type: 'html',
|
|
||||||
formatter: (obj: any) => {
|
|
||||||
const val = obj.row.version
|
|
||||||
return `<a href='javascript:void(0);' style='color: #409EFF;text-decoration: none'>V${val}</a>`
|
|
||||||
// return `<el-tag size='small'>v{{val}}</el-tag>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ title: '备注', minWidth: '160', field: 'description' },
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
align: 'center',
|
|
||||||
render: 'buttons',
|
|
||||||
minWidth: '230',
|
|
||||||
fixed: 'right',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
name: 'view',
|
|
||||||
title: '编辑',
|
|
||||||
type: 'primary',
|
|
||||||
icon: 'el-icon-EditPen',
|
|
||||||
render: 'basicButton',
|
|
||||||
click: row => {
|
|
||||||
modelPopup.value.open('修改模型', row)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'update',
|
|
||||||
title: '设计',
|
|
||||||
type: 'primary',
|
|
||||||
icon: 'el-icon-EditPen',
|
|
||||||
render: 'basicButton',
|
|
||||||
click: row => {
|
|
||||||
push(`/admin/model/processDesigner?id=${row.id}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'deploy',
|
|
||||||
title: '部署',
|
|
||||||
type: 'primary',
|
|
||||||
icon: 'el-icon-EditPen',
|
|
||||||
render: 'basicButton',
|
|
||||||
click: row => {
|
|
||||||
push(`/admin/form/formDesigner?id=${row.id}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'deploy',
|
|
||||||
title: '预览流程',
|
|
||||||
type: 'primary',
|
|
||||||
icon: 'el-icon-EditPen',
|
|
||||||
render: 'basicButton',
|
|
||||||
click: row => {
|
|
||||||
push(`/admin/form/formDesigner?id=${row.id}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'deploy',
|
|
||||||
title: '历史版本',
|
|
||||||
type: 'primary',
|
|
||||||
icon: 'el-icon-EditPen',
|
|
||||||
render: 'basicButton',
|
|
||||||
click: row => {
|
|
||||||
push(`/admin/form/formDesigner?id=${row.id}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'update',
|
|
||||||
title: '删除',
|
|
||||||
type: 'danger',
|
|
||||||
icon: 'el-icon-Delete',
|
|
||||||
render: 'confirmButton',
|
|
||||||
popconfirm: {
|
|
||||||
confirmButtonText: '确认',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
confirmButtonType: 'danger',
|
|
||||||
title: '确定删除吗?'
|
|
||||||
},
|
|
||||||
click: row => {
|
|
||||||
deleteWFForm(row.id).then(res => {
|
|
||||||
ElMessage.success('删除成功')
|
|
||||||
tableStore.index()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
beforeSearchFun: () => {
|
|
||||||
for (let key in tableStore.table.params) {
|
|
||||||
if (tableStore.table.params[key] === '') {
|
|
||||||
delete tableStore.table.params[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const tableStore = new TableStore({
|
||||||
|
url: '/workflow-boot/flw/model/page',
|
||||||
|
method: 'POST',
|
||||||
|
column: [
|
||||||
|
{ title: '序号', type: 'seq', width: 80 },
|
||||||
|
{ title: '流程名称', minWidth: '160', field: 'name' },
|
||||||
|
{
|
||||||
|
title: '流程分类', minWidth: '160', field: 'category',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return categoryList.filter(item => item.code === row.cellValue)[0].name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '模型版本', minWidth: '120', field: 'versionCode', type: 'html',
|
||||||
|
formatter: (obj: any) => {
|
||||||
|
const val = obj.row.versionCode
|
||||||
|
if (val) {
|
||||||
|
return `<a href='javascript:void(0);' style='color: #409EFF;text-decoration: none'>V${val}</a>`
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '表单类型', minWidth: '160', field: 'formType',
|
||||||
|
formatter: (obj: any) => {
|
||||||
|
const val = obj.row.formType
|
||||||
|
if (val == 'DESIGN') {
|
||||||
|
return '系统表单'
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '排序码', minWidth: '160', field: 'sortCode' },
|
||||||
|
{ title: '状态', minWidth: '160', field: 'modelStatus' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
render: 'buttons',
|
||||||
|
minWidth: '230',
|
||||||
|
fixed: 'right',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: 'view',
|
||||||
|
title: '编辑',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
modelPopup.value.open('修改模型', row)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'update',
|
||||||
|
title: '设计',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
push(`/admin/model/configSteps?id=${row.id}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deploy',
|
||||||
|
title: '部署',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
push(`/admin/form/formDesigner?id=${row.id}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deploy',
|
||||||
|
title: '预览流程',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
push(`/admin/form/formDesigner?id=${row.id}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deploy',
|
||||||
|
title: '历史版本',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
push(`/admin/form/formDesigner?id=${row.id}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'update',
|
||||||
|
title: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
icon: 'el-icon-Delete',
|
||||||
|
render: 'confirmButton',
|
||||||
|
popconfirm: {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
confirmButtonType: 'danger',
|
||||||
|
title: '确定删除吗?'
|
||||||
|
},
|
||||||
|
click: row => {
|
||||||
|
deleteWFForm(row.id).then(res => {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
beforeSearchFun: () => {
|
||||||
|
for (let key in tableStore.table.params) {
|
||||||
|
if (tableStore.table.params[key] === '') {
|
||||||
|
delete tableStore.table.params[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
})
|
})
|
||||||
//新增表单
|
//新增表单
|
||||||
const add = () => {
|
const add = () => {
|
||||||
modelPopup.value.open('新增模型')
|
modelPopup.value.open('新增模型')
|
||||||
}
|
}
|
||||||
|
|
||||||
tableStore.table.params.orderBy = 'desc'
|
tableStore.table.params.searchKey = ''
|
||||||
tableStore.table.params.name = ''
|
tableStore.table.params.category = ''
|
||||||
provide('tableStore', tableStore)
|
provide('tableStore', tableStore)
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 加载数据
|
// 加载数据
|
||||||
tableStore.index()
|
tableStore.index()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
163
src/views/system/workflow/model/modelDesign.vue
Normal file
163
src/views/system/workflow/model/modelDesign.vue
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<el-card>
|
||||||
|
<Workflow
|
||||||
|
v-model="modelDesignData.modelValue"
|
||||||
|
:form-field-list-value="formFieldListValue"
|
||||||
|
:record-data="recordData"
|
||||||
|
:sn-template-array="snTemplateArray"
|
||||||
|
:print-template-array="printTemplateArray"
|
||||||
|
:execution-listener-array="executionListenerArray"
|
||||||
|
:execution-listener-selector-for-custom-event-array="executionListenerSelectorForCustomEventArray"
|
||||||
|
:task-listener-array="taskListenerArray"
|
||||||
|
:selector-api-function="selectorApiFunction"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<script setup name="modelDesign">
|
||||||
|
import Workflow from '@/components/XnWorkflow/index.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
// import modelApi from '@/api/flw/modelApi'
|
||||||
|
// import userCenterApi from '@/api/sys/userCenterApi'
|
||||||
|
// import templatePrintApi from '@/api/flw/templatePrintApi'
|
||||||
|
// import templateSnApi from '@/api/flw/templateSnApi'
|
||||||
|
import {
|
||||||
|
executionListenerSelector,
|
||||||
|
executionListenerSelectorForCustomEvent,
|
||||||
|
taskListenerSelector
|
||||||
|
} from '@/api/workflow-boot/model'
|
||||||
|
import { deptTreeSelector } from '@/api/user-boot/dept'
|
||||||
|
import { flwTemplatePrintSelector, flwTemplateSnSelector } from '@/api/workflow-boot/template'
|
||||||
|
|
||||||
|
// 模型设计器的值
|
||||||
|
const modelDesignData = ref({})
|
||||||
|
// 模型主题值
|
||||||
|
const recordData = ref({})
|
||||||
|
// 序列号列表
|
||||||
|
const snTemplateArray = ref([])
|
||||||
|
// 打印模板列表
|
||||||
|
const printTemplateArray = ref([])
|
||||||
|
// 执行监听器数据
|
||||||
|
const executionListenerArray = ref([])
|
||||||
|
// 自定义事件执行监听器选择器
|
||||||
|
const executionListenerSelectorForCustomEventArray = ref([])
|
||||||
|
// 任务监听器数据
|
||||||
|
const taskListenerArray = ref([])
|
||||||
|
|
||||||
|
const formFieldListValue = ref([])
|
||||||
|
// 获取值
|
||||||
|
const getValue = () => {
|
||||||
|
return modelDesignData.value.modelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回显值
|
||||||
|
const setValue = (value, record) => {
|
||||||
|
modelDesignData.value = value
|
||||||
|
recordData.value = record
|
||||||
|
// 获取序列号列表
|
||||||
|
flwTemplateSnSelector().then((data) => {
|
||||||
|
snTemplateArray.value = data.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item['id'],
|
||||||
|
label: item['name']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取打印模板列表
|
||||||
|
flwTemplatePrintSelector().then((data) => {
|
||||||
|
printTemplateArray.value = data.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item['id'],
|
||||||
|
label: item['name']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取执行监听器数据
|
||||||
|
executionListenerSelector().then((data) => {
|
||||||
|
executionListenerArray.value = data.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取自定义事件执行监听器选择器
|
||||||
|
executionListenerSelectorForCustomEvent().then((data) => {
|
||||||
|
executionListenerSelectorForCustomEventArray.value = data.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取任务监听器数据
|
||||||
|
taskListenerSelector().then((data) => {
|
||||||
|
taskListenerArray.value = data.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 传字段的数据过来
|
||||||
|
const setFormFieldListValue = (value) => {
|
||||||
|
if (value) {
|
||||||
|
formFieldListValue.value = JSON.parse(JSON.stringify(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 传递设计器需要的API
|
||||||
|
const selectorApiFunction = {
|
||||||
|
orgTreeApi: (param) => {
|
||||||
|
return deptTreeSelector(param).then((orgTree) => {
|
||||||
|
return Promise.resolve(orgTree)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// orgPageApi: (param) => {
|
||||||
|
// return modelApi.modelOrgListSelector(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// rolePageApi: (param) => {
|
||||||
|
// return modelApi.modelRoleSelector(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// posPageApi: (param) => {
|
||||||
|
// return modelApi.modelPositionSelector(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// userPageApi: (param) => {
|
||||||
|
// return modelApi.modelUserSelector(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// checkedUserListApi: (param) => {
|
||||||
|
// return userCenterApi.userCenterGetUserListByIdList(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// checkedPosListApi: (param) => {
|
||||||
|
// return userCenterApi.userCenterGetPositionListByIdList(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// checkedOrgListApi: (param) => {
|
||||||
|
// return userCenterApi.userCenterGetOrgListByIdList(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// checkedRoleListApi: (param) => {
|
||||||
|
// return userCenterApi.userCenterGetRoleListByIdList(param).then((data) => {
|
||||||
|
// return Promise.resolve(data)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// 抛出钩子
|
||||||
|
defineExpose({
|
||||||
|
getValue,
|
||||||
|
setValue,
|
||||||
|
setFormFieldListValue
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -2,21 +2,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
draggable
|
draggable
|
||||||
class="cn-operate-dialog"
|
class='cn-operate-dialog'
|
||||||
v-model="modelVisible"
|
v-model='modelVisible'
|
||||||
:title="title"
|
:title='title'
|
||||||
style="width: 415px; height: 400px"
|
style='width: 415px; height: 400px'
|
||||||
top="30vh"
|
top='30vh'
|
||||||
>
|
>
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" ref="formRef">
|
<el-form :inline='false' :model='form' label-width='120px' :rules='rules' ref='formRef'>
|
||||||
<el-form-item label="模型标识">
|
<el-form-item label='名称' prop='name'>
|
||||||
<el-input v-model="form.modelKey" clearable disabled />
|
<el-input v-model='form.name' clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="模型名称" prop='modelName'>
|
<el-form-item label='表单类型'>
|
||||||
<el-input v-model="form.modelName" placeholder="请输入模型名称" clearable />
|
<el-input v-model='form.formType' disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="流程分类" prop="category">
|
<el-form-item label='流程分类' prop='category'>
|
||||||
<el-select v-model='form.category' clearable>
|
<el-select v-model='form.category' clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for='item in categoryList'
|
v-for='item in categoryList'
|
||||||
@@ -26,25 +26,31 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="描述" prop="description">
|
<el-form-item label='排序' prop='sortCode'>
|
||||||
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" maxlength="200" show-word-limit />
|
<el-input v-model='form.sortCode' placeholder='请输入排序' />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- <el-form-item label='描述' prop='description'>-->
|
||||||
|
<!-- <el-input v-model='form.description' type='textarea' placeholder='请输入内容' maxlength='200'-->
|
||||||
|
<!-- show-word-limit />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class='dialog-footer'>
|
||||||
<el-button @click="modelVisible = false">取消</el-button>
|
<el-button @click='modelVisible = false'>取消</el-button>
|
||||||
<el-button type="primary" @click="submit">确认</el-button>
|
<el-button type='primary' @click='submit'>确认</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
import { ref, reactive, inject } from 'vue'
|
import { ref, reactive, inject } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import TableStore from '@/utils/tableStore'
|
import TableStore from '@/utils/tableStore'
|
||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
import { addWFModel,updateWFModel } from '@/api/process-boot/workflow/model'
|
import { addWFModel, updateWFModel } from '@/api/process-boot/workflow/model'
|
||||||
|
import { addModel } from '@/api/workflow-boot/model'
|
||||||
|
|
||||||
|
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
@@ -57,17 +63,18 @@ const formRef = ref()
|
|||||||
|
|
||||||
// 注意不要和表单ref的命名冲突
|
// 注意不要和表单ref的命名冲突
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
modelId: undefined,
|
id: '',
|
||||||
modelKey: `Process_${new Date().getTime()}`,
|
name: '',
|
||||||
modelName: '',
|
formType: '系统表单',
|
||||||
category: '',
|
category: '',
|
||||||
description: '',
|
sortCode: 100
|
||||||
})
|
})
|
||||||
|
|
||||||
//form表单校验规则
|
//form表单校验规则
|
||||||
const rules = {
|
const rules = {
|
||||||
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||||
category: [{ required: true, message: '流程分类必选', trigger: 'change' }],
|
sortCode: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
|
||||||
|
category: [{ required: true, message: '流程分类必选', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = (text: string, data?: any) => {
|
const open = (text: string, data?: any) => {
|
||||||
@@ -83,7 +90,8 @@ const open = (text: string, data?: any) => {
|
|||||||
for (let key in form) {
|
for (let key in form) {
|
||||||
form[key] = ''
|
form[key] = ''
|
||||||
}
|
}
|
||||||
form.modelKey = `Process_${new Date().getTime()}`
|
form.formType = '系统表单'
|
||||||
|
form.sortCode = 100
|
||||||
}
|
}
|
||||||
modelVisible.value = true
|
modelVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -101,10 +109,10 @@ const resetForm = () => {
|
|||||||
const submit = () => {
|
const submit = () => {
|
||||||
formRef.value.validate(async (valid: any) => {
|
formRef.value.validate(async (valid: any) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (form.modelId) {
|
if (form.id) {
|
||||||
await updateWFModel(form)
|
await updateWFModel(form)
|
||||||
} else {
|
} else {
|
||||||
await addWFModel(form)
|
await addModel(form)
|
||||||
}
|
}
|
||||||
ElMessage.success('保存成功')
|
ElMessage.success('保存成功')
|
||||||
tableStore.index()
|
tableStore.index()
|
||||||
|
|||||||
@@ -1,276 +0,0 @@
|
|||||||
/**
|
|
||||||
*参考 https://github.com/bpmn-io/bpmn-js-i18n
|
|
||||||
*/
|
|
||||||
|
|
||||||
const translations = {
|
|
||||||
|
|
||||||
"Name":"名称",
|
|
||||||
"Value":"值",
|
|
||||||
"ID":"唯一标识(ID)",
|
|
||||||
"General":"基础属性",
|
|
||||||
|
|
||||||
"Activate the create/remove space tool": "启动创建/删除空间工具",
|
|
||||||
"Activate the global connect tool": "启动全局连接工具",
|
|
||||||
"Activate the hand tool": "启动手动工具",
|
|
||||||
"Activate the lasso tool": "启动套索工具",
|
|
||||||
"Ad-hoc": "Ad-hoc子流程",
|
|
||||||
"Add Lane above": "添加到通道之上",
|
|
||||||
"Add Lane below": "添加到通道之下",
|
|
||||||
"Append ConditionIntermediateCatchEvent": "添加中间条件捕获事件",
|
|
||||||
"Append element": "添加元素",
|
|
||||||
"Append EndEvent": "添加结束事件",
|
|
||||||
"Append Gateway": "添加网关",
|
|
||||||
"Append Intermediate/Boundary Event": "添加中间/边界事件",
|
|
||||||
"Append MessageIntermediateCatchEvent": "添加消息中间捕获事件",
|
|
||||||
"Append ReceiveTask": "添加接收任务",
|
|
||||||
"Append SignalIntermediateCatchEvent": "添加信号中间捕获事件",
|
|
||||||
"Append Task": "添加任务",
|
|
||||||
"Append TimerIntermediateCatchEvent": "添加定时器中间捕获事件",
|
|
||||||
"Append compensation activity": "追加补偿活动",
|
|
||||||
"Append {type}": "追加 {type}",
|
|
||||||
"Boundary Event": "边界事件",
|
|
||||||
"Business Rule Task": "规则任务",
|
|
||||||
"Call Activity": "引用流程",
|
|
||||||
"Cancel Boundary Event": "取消边界事件",
|
|
||||||
"Cancel End Event": "取消结束事件",
|
|
||||||
"Change type": "更改类型",
|
|
||||||
"Collapsed Pool": "折叠池",
|
|
||||||
"Collection": "集合",
|
|
||||||
"Compensation Boundary Event": "补偿边界事件",
|
|
||||||
"Compensation End Event": "结束补偿事件",
|
|
||||||
"Compensation Intermediate Throw Event": "中间补偿抛出事件",
|
|
||||||
"Compensation Start Event": "补偿启动事件",
|
|
||||||
"Complex Gateway": "复杂网关",
|
|
||||||
"Conditional Boundary Event": "条件边界事件",
|
|
||||||
"Conditional Boundary Event (non-interrupting)": "条件边界事件 (非中断)",
|
|
||||||
"Conditional Flow": "条件流",
|
|
||||||
"Conditional Intermediate Catch Event": "中间条件捕获事件",
|
|
||||||
"Conditional Start Event": "条件启动事件",
|
|
||||||
"Conditional Start Event (non-interrupting)": "条件启动事件 (非中断)",
|
|
||||||
"Connect using Association": "文本关联",
|
|
||||||
"Connect using DataInputAssociation": "数据关联",
|
|
||||||
"Connect using Sequence/MessageFlow or Association": "消息关联",
|
|
||||||
"Create IntermediateThrowEvent/BoundaryEvent": "创建中间抛出/边界事件",
|
|
||||||
"Create DataObjectReference": "创建数据对象引用",
|
|
||||||
"Create DataStoreReference": "创建数据存储引用",
|
|
||||||
"Create element": "创建元素",
|
|
||||||
"Create EndEvent": "创建结束事件",
|
|
||||||
"Create Gateway": "创建网关",
|
|
||||||
"Create Group": "创建组",
|
|
||||||
"Create Intermediate/Boundary Event": "创建中间/边界事件",
|
|
||||||
"Create Pool/Participant": "创建池/参与者",
|
|
||||||
"Create StartEvent": "创建开始事件",
|
|
||||||
"Create Task": "创建任务",
|
|
||||||
"Create expanded SubProcess": "创建可折叠子流程",
|
|
||||||
"Create {type}": "创建 {type}",
|
|
||||||
"Data": "数据",
|
|
||||||
"Data Object Reference": "数据对象引用",
|
|
||||||
"Data Store Reference": "数据存储引用",
|
|
||||||
"Default Flow": "默认流",
|
|
||||||
"Divide into three Lanes": "分成三条通道",
|
|
||||||
"Divide into two Lanes": "分成两条通道",
|
|
||||||
"Empty Pool": "空泳道",
|
|
||||||
"Empty Pool (removes content)": "清空泳道(删除内容)",
|
|
||||||
"End Event": "结束事件",
|
|
||||||
"Error Boundary Event": "错误边界事件",
|
|
||||||
"Error End Event": "结束错误事件",
|
|
||||||
"Error Start Event": "错误启动事件",
|
|
||||||
"Escalation Boundary Event": "升级边界事件",
|
|
||||||
"Escalation Boundary Event (non-interrupting)": "升级边界事件 (非中断)",
|
|
||||||
"Escalation End Event": "结束升级事件",
|
|
||||||
"Escalation Intermediate Throw Event": "中间升级抛出事件",
|
|
||||||
"Escalation Start Event": "升级启动事件",
|
|
||||||
"Escalation Start Event (non-interrupting)": "升级启动事件 (非中断)",
|
|
||||||
"Events": "事件",
|
|
||||||
"Event Sub Process": "事件子流程",
|
|
||||||
"Event based Gateway": "事件网关",
|
|
||||||
"Exclusive Gateway": "独占网关",
|
|
||||||
"Expanded Pool": "展开泳道",
|
|
||||||
"Gateways": "网关",
|
|
||||||
"Inclusive Gateway": "包容网关",
|
|
||||||
"Intermediate Throw Event": "中间抛出事件",
|
|
||||||
"Link Intermediate Catch Event": "中间链接捕获事件",
|
|
||||||
"Link Intermediate Throw Event": "中间链接抛出事件",
|
|
||||||
"Loop": "循环",
|
|
||||||
"Manual Task": "手动任务",
|
|
||||||
"Message Boundary Event": "消息边界事件",
|
|
||||||
"Message Boundary Event (non-interrupting)": "消息边界事件 (非中断)",
|
|
||||||
"Message End Event": "结束消息事件",
|
|
||||||
"Message Intermediate Catch Event": "中间消息捕获事件",
|
|
||||||
"Message Intermediate Throw Event": "中间消息抛出事件",
|
|
||||||
"Message Start Event": "消息启动事件",
|
|
||||||
"Message Start Event (non-interrupting)": "消息启动事件 (非中断)",
|
|
||||||
"Parallel Gateway": "并行网关",
|
|
||||||
"Parallel Multi Instance": "并行多实例",
|
|
||||||
"Participants": "参与者",
|
|
||||||
"Participant Multiplicity": "参与者多重性",
|
|
||||||
"Receive Task": "接受任务",
|
|
||||||
"Remove": "移除",
|
|
||||||
"Script Task": "脚本任务",
|
|
||||||
"Send Task": "发送任务",
|
|
||||||
"Sequence Flow": "顺序流",
|
|
||||||
"Sequential Multi Instance": "串行多实例",
|
|
||||||
"Service Task": "服务任务",
|
|
||||||
"Signal Boundary Event": "信号边界事件",
|
|
||||||
"Signal Boundary Event (non-interrupting)": "信号边界事件 (非中断)",
|
|
||||||
"Signal End Event": "结束信号事件",
|
|
||||||
"Signal Intermediate Catch Event": "中间信号捕获事件",
|
|
||||||
"Signal Intermediate Throw Event": "中间信号抛出事件",
|
|
||||||
"Signal Start Event": "信号启动事件",
|
|
||||||
"Signal Start Event (non-interrupting)": "信号启动事件 (非中断)",
|
|
||||||
"Start Event": "开始事件",
|
|
||||||
"Sub Process": "子流程",
|
|
||||||
"Sub Processes": "子流程",
|
|
||||||
"Sub Process (collapsed)": "可折叠子流程",
|
|
||||||
"Sub Process (expanded)": "可展开子流程",
|
|
||||||
"Task": "任务",
|
|
||||||
"Tasks": "任务",
|
|
||||||
"Terminate End Event": "终止边界事件",
|
|
||||||
"Timer Boundary Event": "定时边界事件",
|
|
||||||
"Timer Boundary Event (non-interrupting)": "定时边界事件 (非中断)",
|
|
||||||
"Timer Intermediate Catch Event": "中间定时捕获事件",
|
|
||||||
"Timer Start Event": "定时启动事件",
|
|
||||||
"Timer Start Event (non-interrupting)": "定时启动事件 (非中断)",
|
|
||||||
"Transaction": "事务",
|
|
||||||
"User Task": "用户任务",
|
|
||||||
"already rendered {element}": "{element} 已呈现",
|
|
||||||
"diagram not part of bpmn:Definitions": "图表不是 bpmn:Definitions 的一部分",
|
|
||||||
"element required": "需要元素",
|
|
||||||
"correcting missing bpmnElement on {plane} to {rootElement}": "在 {plane} 上更正缺失的 bpmnElement 为 {rootElement}",
|
|
||||||
"element {element} referenced by {referenced}#{property} not yet drawn": "元素 {element} 的引用 {referenced}#{property} 尚未绘制",
|
|
||||||
"failed to import {element}": "{element} 导入失败",
|
|
||||||
"flow elements must be children of pools/participants": "元素必须是池/参与者的子级",
|
|
||||||
"more than {count} child lanes": "超过 {count} 条通道",
|
|
||||||
"missing {semantic}#attachedToRef": "在 {element} 中缺少 {semantic}#attachedToRef",
|
|
||||||
"multiple DI elements defined for {element}": "为 {element} 定义了多个 DI 元素",
|
|
||||||
"no bpmnElement referenced in {element}": "{element} 中没有引用 bpmnElement",
|
|
||||||
"no diagram to display": "没有要显示的图表",
|
|
||||||
"no shape type specified": "未指定形状类型",
|
|
||||||
"no parent for {element} in {parent}": "在 {element} 中没有父元素 {parent}",
|
|
||||||
"no process or collaboration to display": "没有可显示的流程或协作",
|
|
||||||
"out of bounds release": "越界释放",
|
|
||||||
"Version tag":"版本标记",
|
|
||||||
"Change element":"改变元素",
|
|
||||||
"Documentation":"文档",
|
|
||||||
"PROCESS":"流程",
|
|
||||||
"Element documentation":"元素文档说明",
|
|
||||||
"User assignment":"分配用户",
|
|
||||||
"History cleanup":"历史记录清理",
|
|
||||||
"Time to live":"历史记录生存时间",
|
|
||||||
"Tasklist":"任务列表",
|
|
||||||
"Candidate starter":"候选启动器",
|
|
||||||
"Candidate starter groups":"候选启动组",
|
|
||||||
"Specify more than one group as a comma separated list.":"多个组用','分隔.",
|
|
||||||
"Candidate starter users":"候选发起人",
|
|
||||||
"Specify more than one user as a comma separated list.":"多个用户用','分隔.",
|
|
||||||
"External task":"外部任务",
|
|
||||||
"Startable":"可启动(Startable)",
|
|
||||||
"Executable":"可直接执行",
|
|
||||||
"Job execution":"作业执行",
|
|
||||||
"Priority":"优先级",
|
|
||||||
"Forms":"表单",
|
|
||||||
"Execution listeners":"执行侦听器",
|
|
||||||
"Extension properties":"扩展属性",
|
|
||||||
"Event type":"事件类型",
|
|
||||||
"Listener type":"侦听器类型",
|
|
||||||
"Field injection":"字段注入",
|
|
||||||
"Start initiator":"开始发起人",
|
|
||||||
"Initiator":"发起人",
|
|
||||||
"Asynchronous continuations":"异步延续",
|
|
||||||
"Before":"之前",
|
|
||||||
"After":"之后",
|
|
||||||
"Inputs":"输入",
|
|
||||||
"Outputs":"输出",
|
|
||||||
"Local variable name":"局部变量名称",
|
|
||||||
"Assignment type":"分配类型",
|
|
||||||
"Format":"格式",
|
|
||||||
"Type":"类型",
|
|
||||||
"Expression":"表达式(Expression)",
|
|
||||||
"Script":"脚本(Script)",
|
|
||||||
"Delegate expression":"委托表达式(Delegate expression)",
|
|
||||||
"Java class":"Java类(Java class)",
|
|
||||||
"start":"开始(start)",
|
|
||||||
"end":"结束(end)",
|
|
||||||
"Start typing \"${}\" to create an expression.":"开始键入\"${}\"以创建表达式.",
|
|
||||||
"Process variable name":"过程变量名称",
|
|
||||||
"List values":"列表值",
|
|
||||||
"Map entries":"映射条目",
|
|
||||||
"Key":"键",
|
|
||||||
"Values":"值",
|
|
||||||
"Form reference":"引用表单ID",
|
|
||||||
"Binding":"结合",
|
|
||||||
"Version":"版本",
|
|
||||||
"Form fields":"表单字段",
|
|
||||||
"Form key":"表单ID",
|
|
||||||
"Embedded or External Task Forms":"拓展表单",
|
|
||||||
"Camunda Forms":"标准表单",
|
|
||||||
"Generated Task Forms":"内置表单",
|
|
||||||
"Refers to the process variable name":"指的是(引用)过程变量名称",
|
|
||||||
"Label":"标签",
|
|
||||||
"Default value":"默认值",
|
|
||||||
"Constraints":"限制",
|
|
||||||
"Properties":"属性",
|
|
||||||
"Config":"配置",
|
|
||||||
"Implementation":"实施",
|
|
||||||
"Field injections":"字段注入",
|
|
||||||
"Task listeners":"任务侦听器",
|
|
||||||
"Listener ID":"侦听器ID",
|
|
||||||
"Message":"消息",
|
|
||||||
"Global message reference":"引用全局消息ID",
|
|
||||||
"Result variable":"结果变量",
|
|
||||||
"Resource":"资源",
|
|
||||||
"External resource":"外部资源",
|
|
||||||
"Inline script":"内联脚本",
|
|
||||||
"Process variables":"过程变量",
|
|
||||||
"Global signal reference":"引用全局信号ID",
|
|
||||||
"Signal":"信号",
|
|
||||||
"Called element":"被调用元素",
|
|
||||||
"In mapping propagation":"在映射传播中",
|
|
||||||
"Propagate all variables":"传播所有变量",
|
|
||||||
"Out mapping propagation":"向外映射传播",
|
|
||||||
"In mappings":"在映射中",
|
|
||||||
"Source":"来源",
|
|
||||||
"Target":"目标",
|
|
||||||
"Local":"局部的(Local)",
|
|
||||||
"Out mappings":"输出映射",
|
|
||||||
"Link":"链接",
|
|
||||||
"Timer":"定时器",
|
|
||||||
"Retry time cycle":"重试时间周期",
|
|
||||||
"Variable name":"变量名称",
|
|
||||||
"Condition Expression":"条件表达式",
|
|
||||||
"Condition":"条件",
|
|
||||||
"Process documentation":"流程文档",
|
|
||||||
"Assignee":"委托人",
|
|
||||||
"Candidate groups":"候选组",
|
|
||||||
"Candidate users":"候选用户",
|
|
||||||
"Due date":"期限",
|
|
||||||
"The due date as an EL expression (e.g. ${someDate}) or an ISO date (e.g. 2015-06-26T09:54:00).":"到期日期为EL表达式(例如${someDate})或ISO日期(例如2015-06-26T09:54:00)",
|
|
||||||
"Follow up date":"跟进日期",
|
|
||||||
"The follow up date as an EL expression (e.g. ${someDate}) or an ISO date (e.g. 2015-06-26T09:54:00).":"作为EL表达式(例如${someDate})或ISO日期(例如2015-06-26T09:54:00)的跟进日期",
|
|
||||||
"Connector ID":"连接器ID",
|
|
||||||
"Connector inputs":"连接器输入",
|
|
||||||
"Connector outputs":"连接器输出",
|
|
||||||
"Topic":"主题",
|
|
||||||
"Errors":"错误",
|
|
||||||
"Global error reference":"引用全局错误ID",
|
|
||||||
"Throw expression":"Throw表达式",
|
|
||||||
"Decision reference":"引用决策ID",
|
|
||||||
"Tenant ID":"租户ID",
|
|
||||||
"Multi-instance":"多实例",
|
|
||||||
"Loop cardinality":"循环基数",
|
|
||||||
"Completion condition":"完成条件",
|
|
||||||
"Element variable":"元素变量",
|
|
||||||
"Asynchronous before":"异步之前",
|
|
||||||
"Asynchronous after":"异步之后",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const customTranslate = (template, replacements) =>{
|
|
||||||
replacements = replacements || {};
|
|
||||||
// Translate
|
|
||||||
template = translations[template] || template;
|
|
||||||
// Replace
|
|
||||||
return template.replace(/{([^}]+)}/g, (_, key)=> {
|
|
||||||
return replacements[key] || '{' + key + '}';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
129
src/views/system/workflow/model_old/modelPopup.vue
Normal file
129
src/views/system/workflow/model_old/modelPopup.vue
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<!--模型的新增和修改的弹出框-->
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
draggable
|
||||||
|
class="cn-operate-dialog"
|
||||||
|
v-model="modelVisible"
|
||||||
|
:title="title"
|
||||||
|
style="width: 415px; height: 400px"
|
||||||
|
top="30vh"
|
||||||
|
>
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" ref="formRef">
|
||||||
|
<el-form-item label="模型标识">
|
||||||
|
<el-input v-model="form.modelKey" clearable disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型名称" prop='modelName'>
|
||||||
|
<el-input v-model="form.modelName" placeholder="请输入模型名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="流程分类" prop="category">
|
||||||
|
<el-select v-model='form.category' clearable>
|
||||||
|
<el-option
|
||||||
|
v-for='item in categoryList'
|
||||||
|
:key='item.id'
|
||||||
|
:label='item.name'
|
||||||
|
:value='item.code'
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述" prop="description">
|
||||||
|
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" maxlength="200" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-scrollbar>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="modelVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submit">确认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, inject } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import TableStore from '@/utils/tableStore'
|
||||||
|
import { useDictData } from '@/stores/dictData'
|
||||||
|
import { addWFModel,updateWFModel } from '@/api/process-boot/workflow/model'
|
||||||
|
|
||||||
|
|
||||||
|
const dictData = useDictData()
|
||||||
|
const categoryList = dictData.getBasicData('flow_category')
|
||||||
|
|
||||||
|
const tableStore = inject('tableStore') as TableStore
|
||||||
|
const modelVisible = ref(false)
|
||||||
|
const title = ref('')
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
// 注意不要和表单ref的命名冲突
|
||||||
|
const form = reactive({
|
||||||
|
modelId: undefined,
|
||||||
|
modelKey: `Process_${new Date().getTime()}`,
|
||||||
|
modelName: '',
|
||||||
|
category: '',
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
//form表单校验规则
|
||||||
|
const rules = {
|
||||||
|
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
||||||
|
category: [{ required: true, message: '流程分类必选', trigger: 'change' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (text: string, data?: any) => {
|
||||||
|
title.value = text
|
||||||
|
if (data) {
|
||||||
|
// 表单赋值
|
||||||
|
for (let key in form) {
|
||||||
|
form[key] = data[key]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resetForm()
|
||||||
|
// 在此处恢复默认表单
|
||||||
|
for (let key in form) {
|
||||||
|
form[key] = ''
|
||||||
|
}
|
||||||
|
form.modelKey = `Process_${new Date().getTime()}`
|
||||||
|
}
|
||||||
|
modelVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//重置表单内容
|
||||||
|
const resetForm = () => {
|
||||||
|
if (formRef.value) {
|
||||||
|
formRef.value.resetFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交用户表单数据
|
||||||
|
*/
|
||||||
|
const submit = () => {
|
||||||
|
formRef.value.validate(async (valid: any) => {
|
||||||
|
if (valid) {
|
||||||
|
if (form.modelId) {
|
||||||
|
await updateWFModel(form)
|
||||||
|
} else {
|
||||||
|
await addWFModel(form)
|
||||||
|
}
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
tableStore.index()
|
||||||
|
modelVisible.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/************针对tab切换*************/
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-upload-list__item {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
9
src/views/system/workflow/model_old/processDesigner.vue
Normal file
9
src/views/system/workflow/model_old/processDesigner.vue
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<h1>111</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts' setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user