因为这个项目license问题无法开源,更多技术支持与服务请加入我的知识星球。
1、在仿钉钉流程中,不同panel比如开始节点,审批人节点等之间会有数据传递,最简单就是一个开始节点选择一个form,在排它条件节点就会需要这个表单的字段信息来进行条件设置。
这个属于vue3里同级别组件传值,目前主要有两种实现方式。
1.1 Vue3兄弟组件传值,我们可以使用"Mitt"插件来实现。通过使用事件总线的方式,我们可以将数据从一个组件传递给另一个组件,实现兄弟组件之间的通信。
1.2 利用 Vue 3 自身的provide 和 inject 响应式 API 来实现兄弟组件之间的数据共享和通信。这里来给大家介绍下vue3中,兄弟组件传值的两种方式。
我们这里采用第二种方式,不需要另外的插件方式
2、在panel父组件里定义如下:
主要就是下面两行,增加form字段共享数据
const formFields = ref<any>([])
provide('formFields', formFields)
<script setup lang="ts">
import { ref, reactive, computed, inject, provide } from 'vue';
import { ClickOutside as vClickOutside } from 'element-plus'
import type { Component } from 'vue'
import Start from './StartPanel.vue'
import Approval from './ApprovalPanel.vue'
import Cc from './CcPanel.vue'
import Timer from './TimerPanel.vue'
import Notify from './NotifyPanel.vue'
import Condition from './ConditionPanel.vue'
import End from './EndPanel.vue'
import type { FlowNode } from '../nodes/type'
defineProps<{
activeData: FlowNode
}>()
const penalVisible = defineModel<boolean>({ required: true })
const panels: Recordable<Component> = {
start: Start,
approval: Approval,
cc: Cc,
timer: Timer,
notify: Notify,
condition: Condition,
end: End
}
const showInput = ref(false)
const onClickOutside = () => {
if (showInput.value) {
showInput.value = false
}
}
const formFields = ref<any>([])
provide('formFields', formFields)
</script>
<template>
<el-drawer v-model="penalVisible" size="35%">
<template #header="{ titleId, titleClass }">
<span :id="titleId" :class="titleClass">
<el-input
v-click-outside="onClickOutside"
@blur="onClickOutside"
maxlength="30"
v-model="activeData.name"
v-show="showInput"
></el-input>
<el-link icon="EditPen" v-show="!showInput" @click="showInput = true">
{{ activeData?.name || '节点配置' }}
</el-link>
</span>
</template>
<component :is="panels[activeData.type]" :activeData="activeData" />
</el-drawer>
</template>
<style scoped lang="scss">
:deep(.el-tabs__content) {
margin-top: 50px;
margin-left: -120px;
}
</style>
2、在startPanel的开始节点里进行数据选择
const formFields = inject('formFields') 就是这个进行数据的inject
<script setup lang="ts">
import { ref, reactive, computed, inject, watchEffect, onMounted } from 'vue';
import type { FormProperty, StartNode } from '../nodes/type'
import type { Field } from '@/views/lowflow/components/Render/type'
import type { Ref } from 'vue'
import ExecutionListeners from './ExecutionListeners.vue'
import { listForm } from '@/views/lowflow/api/modules/model'
//const { fields } = inject<{ fields: Ref<Field[]> }>('flowDesign', { fields: ref([]) })
const formFields = inject('formFields')
const props = defineProps<{
activeData: StartNode
}>()
const activeName = ref('basicSettings')
const allReadonly = computed({
get() {
return props.activeData.formProperties.every((e) => e.readonly)
},
set(val) {
props.activeData.formProperties.forEach((e) => (e.readonly = val))
if (val) {
allHidden.value = false
allRequired.value = false
}
}
})
const allHidden = computed({
get() {
return props.activeData.formProperties.every((e) => e.hidden)
},
set(val) {
props.activeData.formProperties.forEach((e) => (e.hidden = val))
if (val) {
allRequired.value = false
allReadonly.value = false
}
}
})
const allRequired = computed({
get() {
return props.activeData.formProperties.every((e) => e.required)
},
set(val) {
props.activeData.formProperties.forEach((e) => (e.required = val))
if (val) {
allReadonly.value = false
allHidden.value = false
}
}
})
const changeReadonly = (row: FormProperty) => {
if (row.readonly) {
row.required = false
row.hidden = false
}
}
const changeRequired = (row: FormProperty) => {
if (row.required) {
row.readonly = false
row.hidden = false
}
}
const changeHidden = (row: FormProperty) => {
if (row.hidden) {
row.readonly = false
row.required = false
}
}
const formOptions = ref<any>([])
const localScope = ref(false)
const componentDict = [ //VForm3组件对应的El组件字典
{
id: "input",
name: "ElInput"
},
{
id: "date",
name: "ElDatePicker"
},
{
id: "number",
name: "ElInputNumber"
},
]
/** 查询表单列表 */
const getFormList = () => {
listForm().then(res => formOptions.value = res.result.records)
}
const updateFormKey = (formKey) => {
console.log("updateFormKey formKey",formKey)
props.activeData.formKey = formKey
const formItem = formOptions.value?.find((f) => f.id === formKey)
const formContent = JSON.parse(formItem.formContent)
formFields.value = formContent.widgetList.map( item => { //VForm3转换成仿钉钉设别的格式
return {
id: item.id,
type: "formItem",
label: item.options.label,
name: componentDict.find(dict => dict.id === item.type)?.name?componentDict.find(dict => dict.id === item.type)?.name:"ElInput",
value: item.options.defaultValue?item.options.readonly:null,
readonly: item.options.readonly?item.options.readonly:false,
required: item.options.required,
hidden: item.options.hidden,
props: {
valueFormat: "YYYY-MM-DD",
multiple: false,
disabled: item.options.disabled,
placeholder: item.options.placeholder?item.options.placeholder:"",
style: {
"width": "100%"
}
}
}
})
props.activeData.formProperties = formFields.value
console.log("updateFormKey formProperties",props.activeData.formProperties)
}
const updateFormScope = () => {
}
watchEffect(() => {
const formProperties = props.activeData.formProperties
console.log("watchEffect formProperties",formProperties)
props.activeData.formProperties = formProperties?.map((field) => ({
id: field.id,
name: field?.name,
readonly: field?.readonly,
hidden: field?.hidden,
required: field?.required
}))
props.activeData.formProperties?.forEach((item) => {
const properties = formProperties.find((f) => f.id === item.id)
if (properties) {
item.readonly = properties.readonly
item.hidden = properties.hidden
item.required = properties.required
}
})
})
onMounted(() => {
getFormList();
});
</script>
<template>
<el-tabs v-model="activeName" stretch class="el-segmented">
<el-tab-pane label="基础设置" name="basicSettings">
<el-form-item label="流程分类">
<el-input disabled v-model="activeData.category"/>
</el-form-item>
<el-form-item label="应用类型">
<el-input disabled v-model="activeData.appType"/>
</el-form-item>
<el-form label-position="top" label-width="90px">
<el-form-item prop="executionListeners" label="执行监听器">
<ExecutionListeners :node="activeData" />
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="表单选择" name="formSelect">
<el-form size="small" label-width="90px" @submit.native.prevent>
<el-form-item label="表单" prop="formKey">
<el-select v-model="activeData.formKey" placeholder="请选择表单" @change="updateFormKey" clearable>
<el-option v-for="item in formOptions" :key="item.id" :label="item.formName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item prop="localScope">
<span slot="label">
<el-tooltip content="若为节点表单,则表单信息仅在此节点可用,默认为全局表单,表单信息在整个流程实例中可用" placement="top-start">
<i class="header-icon el-icon-info"></i>
</el-tooltip>
<span>节点表单</span>
</span>
<el-switch disabled v-model="activeData.localScope" active-text="是" inactive-text="否" @change="updateFormScope()" />
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="表单权限" name="formPermissions">
<el-table :data="activeData.formProperties">
<el-table-column prop="name" label="字段" />
<el-table-column prop="readonly">
<template #header>
<el-checkbox v-model="allReadonly" label="只读" />
</template>
<template #default="{ row }">
<el-checkbox v-model="row.readonly" @change="changeReadonly(row)" />
</template>
</el-table-column>
<el-table-column prop="required">
<template #header>
<el-checkbox v-model="allRequired" label="必填" />
</template>
<template #default="{ row }">
<el-checkbox v-model="row.required" @change="changeRequired(row)" />
</template>
</el-table-column>
<el-table-column prop="hidden">
<template #header>
<el-checkbox v-model="allHidden" label="隐藏" />
</template>
<template #default="{ row }">
<el-checkbox v-model="row.hidden" @change="changeHidden(row)" />
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</template>
<style scoped lang="scss">
@import '@/views/lowflow/styles/el-segmented.scss';
.el-segmented {
width:100%;
}
.el-segmented :deep(.el-tabs__header) {
width:100%;
}
.el-segmented :deep(.el-tabs__content) {
width:100%;
position: absolute;
margin-left: 0px;
}
</style>
4、这样在排它网关就可以用这个共享数据了
<script setup lang="ts">
import { ref, reactive, computed, inject, onMounted } from 'vue';
import type { ConditionNode } from '../nodes/type'
import type { Ref } from 'vue'
import type { Field } from '@/views/lowflow/components/Render/type'
import AdvancedFilter from '@/views/lowflow/components/AdvancedFilter/index.vue'
import JSelectUserByDept from '/@/components/Form/src/jeecg/components/JSelectUserByDept.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JSelectRole from '/@/components/Form/src/jeecg/components/JSelectRole.vue';
//const { fields } = inject<{ fields: Ref<Field[]> }>('flowDesign', { fields: ref([]) })
const formFields = inject('formFields')
defineProps<{
activeData: ConditionNode
}>()
const initialFormFields = ref<Field[]>([
{
id: 'initiator',
name: 'JSelectUserByDept',
type: 'formItem',
label: '发起人',
value: null,
readonly: false,
required: true,
hidden: false,
props: {
key: undefined,
multiple: false,
placeholder: '请选择发起人',
class: [],
style: {
width: '100%'
}
}
}
])
onMounted(() => {
console.log("onMounted formFields",formFields)
console.log("onMounted filter-fields",[...initialFormFields.value, ...formFields.value])
});
</script>
<template>
<AdvancedFilter
v-model="activeData.conditions"
:filter-fields="[...initialFormFields, ...formFields]"
/>
</template>
<style scoped lang="scss"></style>
5、效果图
表单字段如下