Ver código fonte

feat:bpmn流程设计器增加http任务组件

beihai 3 meses atrás
pai
commit
69831144fb

+ 29 - 0
src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js

@@ -89,6 +89,26 @@ F.prototype.getPaletteEntries = function () {
     create.start(event, elementFactory.createParticipantShape())
   }
 
+  function createHttpServiceTask(event) {
+    const httpTask = elementFactory.createShape({
+      type: 'bpmn:ServiceTask'
+    })
+
+    const businessObject = httpTask.businessObject
+
+    if (typeof businessObject.set === 'function') {
+      businessObject.set('flowable:type', 'http')
+    } else {
+      businessObject['flowable:type'] = 'http'
+    }
+
+    if (!businessObject.name) {
+      businessObject.name = translate('HTTP Task')
+    }
+
+    create.start(event, httpTask)
+  }
+
   assign(actions, {
     'hand-tool': {
       group: 'tools',
@@ -177,6 +197,15 @@ F.prototype.getPaletteEntries = function () {
       'bpmn-icon-service',
       translate('Create Service Task')
     ),
+    'create.http-service-task': {
+      group: 'activity',
+      className: 'bpmn-icon-service',
+      title: translate('Create HTTP Task'),
+      action: {
+        dragstart: createHttpServiceTask,
+        click: createHttpServiceTask
+      }
+    },
     'create.data-object': createAction(
       'bpmn:DataObjectReference',
       'data-object',

+ 29 - 0
src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js

@@ -96,6 +96,26 @@ PaletteProvider.prototype.getPaletteEntries = function () {
     create.start(event, elementFactory.createParticipantShape())
   }
 
+  function createHttpServiceTask(event) {
+    const httpTask = elementFactory.createShape({
+      type: 'bpmn:ServiceTask'
+    })
+
+    const businessObject = httpTask.businessObject
+
+    if (typeof businessObject.set === 'function') {
+      businessObject.set('flowable:type', 'http')
+    } else {
+      businessObject['flowable:type'] = 'http'
+    }
+
+    if (!businessObject.name) {
+      businessObject.name = translate('HTTP Task')
+    }
+
+    create.start(event, httpTask)
+  }
+
   assign(actions, {
     'hand-tool': {
       group: 'tools',
@@ -177,6 +197,15 @@ PaletteProvider.prototype.getPaletteEntries = function () {
       'bpmn-icon-service',
       translate('Create Service Task')
     ),
+    'create.http-service-task': {
+      group: 'activity',
+      className: 'bpmn-icon-service',
+      title: translate('Create HTTP Task'),
+      action: {
+        dragstart: createHttpServiceTask,
+        click: createHttpServiceTask
+      }
+    },
     'create.data-object': createAction(
       'bpmn:DataObjectReference',
       'data-object',

+ 1 - 0
src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js

@@ -43,6 +43,7 @@ export default {
   'Manual Task': '手工任务',
   'Business Rule Task': '业务规则任务',
   'Service Task': '服务任务',
+  'HTTP Task': 'HTTP任务',
   'Script Task': '脚本任务',
   'Call Activity': '调用活动',
   'Sub-Process (collapsed)': '子流程(折叠的)',

+ 252 - 17
src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue

@@ -1,10 +1,11 @@
 <template>
   <div>
-    <el-form-item label="执行类型" key="executeType">
-      <el-select v-model="serviceTaskForm.executeType">
+    <el-form-item label="执行方式" key="executeType">
+      <el-select v-model="serviceTaskForm.executeType" @change="handleExecuteTypeChange">
         <el-option label="Java类" value="class" />
         <el-option label="表达式" value="expression" />
         <el-option label="代理表达式" value="delegateExpression" />
+        <el-option label="HTTP 调用" value="http" />
       </el-select>
     </el-form-item>
     <el-form-item
@@ -31,46 +32,273 @@
     >
       <el-input v-model="serviceTaskForm.delegateExpression" clearable @change="updateElementTask" />
     </el-form-item>
+    <template v-if="serviceTaskForm.executeType === 'http'">
+      <el-form-item label="请求方法" key="http-method">
+        <el-select v-model="httpTaskForm.requestMethod">
+          <el-option label="GET" value="GET" />
+          <el-option label="POST" value="POST" />
+          <el-option label="PUT" value="PUT" />
+          <el-option label="DELETE" value="DELETE" />
+          <el-option label="PATCH" value="PATCH" />
+          <el-option label="HEAD" value="HEAD" />
+          <el-option label="OPTIONS" value="OPTIONS" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="请求地址" key="http-url" prop="requestUrl">
+        <el-input v-model="httpTaskForm.requestUrl" clearable />
+      </el-form-item>
+      <el-form-item label="请求头" key="http-headers">
+        <el-input
+          v-model="httpTaskForm.requestHeaders"
+          type="textarea"
+          resize="vertical"
+          :autosize="{ minRows: 2, maxRows: 4 }"
+          clearable
+        />
+      </el-form-item>
+      <!--      <el-form-item label="禁止重定向" key="http-disallow-redirects">-->
+      <!--        <el-switch v-model="httpTaskForm.disallowRedirects" />-->
+      <!--      </el-form-item>-->
+      <!--      <el-form-item label="忽略异常" key="http-ignore-exception">-->
+      <!--        <el-switch v-model="httpTaskForm.ignoreException" />-->
+      <!--      </el-form-item>-->
+      <!--      <el-form-item label="瞬态保存响应参数" key="http-save-transient">-->
+      <!--        <el-switch v-model="httpTaskForm.saveResponseParametersTransient" />-->
+      <!--      </el-form-item>-->
+      <!--      <el-form-item label="JSON 保存响应变量" key="http-save-json">-->
+      <!--        <el-switch v-model="httpTaskForm.saveResponseVariableAsJson" />-->
+      <!--      </el-form-item>-->
+    </template>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { updateElementExtensions } from '@/components/bpmnProcessDesigner/package/utils'
 defineOptions({ name: 'ServiceTask' })
 const props = defineProps({
   id: String,
   type: String
 })
 
-const defaultTaskForm = ref({
+const prefix = (inject('prefix', 'flowable') || 'flowable') as string
+const flowableTypeKey = `${prefix}:type`
+const flowableFieldType = `${prefix}:Field`
+
+const HTTP_FIELD_NAMES = [
+  'requestMethod',
+  'requestUrl',
+  'requestHeaders',
+  'disallowRedirects',
+  'ignoreException',
+  'saveResponseParametersTransient',
+  'saveResponseVariableAsJson'
+]
+const HTTP_BOOLEAN_FIELDS = new Set([
+  'disallowRedirects',
+  'ignoreException',
+  'saveResponseParametersTransient',
+  'saveResponseVariableAsJson'
+])
+
+const DEFAULT_TASK_FORM = {
   executeType: '',
   class: '',
   expression: '',
   delegateExpression: ''
-})
+}
 
-const serviceTaskForm = ref<any>({})
+const DEFAULT_HTTP_FORM = {
+  requestMethod: 'GET',
+  requestUrl: '',
+  requestHeaders: 'Content-Type: application/json',
+  disallowRedirects: false,
+  ignoreException: false,
+  saveResponseParametersTransient: false,
+  saveResponseVariableAsJson: false
+}
+
+const serviceTaskForm = ref({ ...DEFAULT_TASK_FORM })
+const httpTaskForm = ref({ ...DEFAULT_HTTP_FORM })
 const bpmnElement = ref()
+const httpInitializing = ref(false)
 
 const bpmnInstances = () => (window as any)?.bpmnInstances
 
-const resetTaskForm = () => {
-  for (let key in defaultTaskForm.value) {
-    let value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key]
-    serviceTaskForm.value[key] = value
-    if (value) {
-      serviceTaskForm.value.executeType = key
+const collectHttpExtensionInfo = () => {
+  const businessObject = bpmnElement.value?.businessObject
+  const extensionElements = businessObject?.extensionElements
+  const httpFields = new Map<string, string>()
+  const otherExtensions: any[] = []
+
+  extensionElements?.values?.forEach((item: any) => {
+    if (item?.$type === flowableFieldType && HTTP_FIELD_NAMES.includes(item.name)) {
+      const value = item.string ?? item.stringValue ?? item.expression ?? ''
+      httpFields.set(item.name, value)
+    } else {
+      otherExtensions.push(item)
+    }
+  })
+
+  return { httpFields, otherExtensions }
+}
+
+const resetHttpDefaults = () => {
+  httpInitializing.value = true
+  httpTaskForm.value = { ...DEFAULT_HTTP_FORM }
+  nextTick(() => {
+    httpInitializing.value = false
+  })
+}
+
+const resetHttpForm = () => {
+  httpInitializing.value = true
+  const { httpFields } = collectHttpExtensionInfo()
+  const nextForm: Record<string, any> = { ...DEFAULT_HTTP_FORM }
+
+  HTTP_FIELD_NAMES.forEach((name) => {
+    const stored = httpFields.get(name)
+    if (stored !== undefined) {
+      nextForm[name] = HTTP_BOOLEAN_FIELDS.has(name) ? stored === 'true' : stored
+    }
+  })
+
+  httpTaskForm.value = nextForm
+  nextTick(() => {
+    httpInitializing.value = false
+    updateHttpExtensions(true)
+  })
+}
+
+const resetServiceTaskForm = () => {
+  const businessObject = bpmnElement.value?.businessObject
+  const nextForm: Record<string, any> = { ...DEFAULT_TASK_FORM }
+
+  if (businessObject) {
+    if (businessObject.class) {
+      nextForm.class = businessObject.class
+      nextForm.executeType = 'class'
+    }
+    if (businessObject.expression) {
+      nextForm.expression = businessObject.expression
+      nextForm.executeType = 'expression'
+    }
+    if (businessObject.delegateExpression) {
+      nextForm.delegateExpression = businessObject.delegateExpression
+      nextForm.executeType = 'delegateExpression'
+    }
+    if (businessObject.$attrs?.[flowableTypeKey] === 'http') {
+      nextForm.executeType = 'http'
+    }
+  }
+
+  serviceTaskForm.value = nextForm
+
+  if (nextForm.executeType === 'http') {
+    resetHttpForm()
+  } else {
+    resetHttpDefaults()
+  }
+}
+
+const shouldPersistField = (name: string, value: any) => {
+  if (HTTP_BOOLEAN_FIELDS.has(name)) return true
+  if (name === 'requestMethod') return true
+  if (name === 'requestUrl') return !!value
+  return value !== undefined && value !== ''
+}
+
+const updateHttpExtensions = (force = false) => {
+  if (!bpmnElement.value) return
+  if (!force && (httpInitializing.value || serviceTaskForm.value.executeType !== 'http')) {
+    return
+  }
+
+  const { httpFields: existingFields, otherExtensions } = collectHttpExtensionInfo()
+
+  const desiredEntries: [string, string][] = []
+  HTTP_FIELD_NAMES.forEach((name) => {
+    const rawValue = httpTaskForm.value[name]
+    if (!shouldPersistField(name, rawValue)) {
+      return
     }
+
+    const persisted = HTTP_BOOLEAN_FIELDS.has(name)
+      ? String(!!rawValue)
+      : rawValue === undefined
+        ? ''
+        : String(rawValue)
+
+    desiredEntries.push([name, persisted])
+  })
+
+  if (
+    !force &&
+    desiredEntries.length === existingFields.size &&
+    desiredEntries.every(([name, value]) => existingFields.get(name) === value)
+  ) {
+    return
+  }
+
+  const moddle = bpmnInstances().moddle
+  const httpFieldElements = desiredEntries.map(([name, value]) =>
+    moddle.create(flowableFieldType, {
+      name,
+      string: value
+    })
+  )
+
+  updateElementExtensions(bpmnElement.value, [...otherExtensions, ...httpFieldElements])
+}
+
+const removeHttpExtensions = () => {
+  if (!bpmnElement.value) return
+  const { httpFields, otherExtensions } = collectHttpExtensionInfo()
+  if (!httpFields.size) {
+    return
+  }
+
+  if (!otherExtensions.length) {
+    bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
+      extensionElements: null
+    })
+    return
   }
+
+  updateElementExtensions(bpmnElement.value, otherExtensions)
 }
 
 const updateElementTask = () => {
-  let taskAttr = Object.create(null);
-  const type = serviceTaskForm.value.executeType;
-  for (let key in serviceTaskForm.value) {
-    if (key !== 'executeType' && key !== type) taskAttr[key] = null;
+  if (!bpmnElement.value) return
+
+  const taskAttr: Record<string, any> = {
+    class: null,
+    expression: null,
+    delegateExpression: null,
+    [flowableTypeKey]: null
+  }
+
+  const type = serviceTaskForm.value.executeType
+  if (type === 'class' || type === 'expression' || type === 'delegateExpression') {
+    taskAttr[type] = serviceTaskForm.value[type] || null
+  } else if (type === 'http') {
+    taskAttr[flowableTypeKey] = 'http'
   }
-  taskAttr[type] = serviceTaskForm.value[type] || "";
+
   bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
+
+  if (type === 'http') {
+    updateHttpExtensions(true)
+  } else {
+    removeHttpExtensions()
+  }
+}
+
+const handleExecuteTypeChange = (value: string) => {
+  serviceTaskForm.value.executeType = value
+  if (value === 'http') {
+    resetHttpForm()
+  }
+  updateElementTask()
 }
 
 onBeforeUnmount(() => {
@@ -82,10 +310,17 @@ watch(
   () => {
     bpmnElement.value = bpmnInstances().bpmnElement
     nextTick(() => {
-      resetTaskForm()
+      resetServiceTaskForm()
     })
   },
   { immediate: true }
 )
 
+watch(
+  () => httpTaskForm.value,
+  () => {
+    updateHttpExtensions()
+  },
+  { deep: true }
+)
 </script>