基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-不同panel兄弟之间的数据传递

因为这个项目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)

javascript 复制代码
<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

javascript 复制代码
<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、这样在排它网关就可以用这个共享数据了

javascript 复制代码
<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、效果图

表单字段如下

相关推荐
高山我梦口香糖26 分钟前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_7482352429 分钟前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240251 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar1 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人2 小时前
前端知识补充—CSS
前端·css
GISer_Jing2 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245522 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v2 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing3 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600953 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js