From 6b988bd582bfcd17fee48c476a5a6e5cc172b0d5 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期三, 12 三月 2025 10:08:33 +0800
Subject: [PATCH] dev-2

---
 src/bpmn/panel/property/ExecutionListener.vue |  308 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 308 insertions(+), 0 deletions(-)

diff --git a/src/bpmn/panel/property/ExecutionListener.vue b/src/bpmn/panel/property/ExecutionListener.vue
new file mode 100644
index 0000000..3c584cb
--- /dev/null
+++ b/src/bpmn/panel/property/ExecutionListener.vue
@@ -0,0 +1,308 @@
+<template>
+  <div>
+    <vxe-toolbar>
+      <template #buttons>
+        <el-button type="primary" link size="small" @click="insertEvent">鏂板</el-button>
+        <el-button type="primary" link size="small" @click="removeSelectRowEvent">鍒犻櫎</el-button>
+      </template>
+    </vxe-toolbar>
+    <vxe-table
+      ref="tableRef"
+      size="mini"
+      height="100px"
+      border
+      show-overflow
+      keep-source
+      :data="tableData"
+      :menu-config="menuConfig"
+      @cell-dblclick="cellDBLClickEvent"
+      @menu-click="contextMenuClickEvent"
+    >
+      <vxe-column type="checkbox" width="40"></vxe-column>
+      <vxe-column type="seq" width="40"></vxe-column>
+      <vxe-column field="event" title="浜嬩欢" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="type" title="绫诲瀷" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="className" title="Java 绫诲悕" min-width="100px"> </vxe-column>
+    </vxe-table>
+
+    <el-dialog
+      v-model="formDialog.visible.value"
+      :title="formDialog.title.value"
+      width="600px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      append-to-body
+    >
+      <el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
+        <el-form-item label="浜嬩欢" prop="event">
+          <el-select v-model="formData.event">
+            <el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="绫诲瀷" prop="type">
+          <template #label>
+            <span>
+              绫诲瀷
+              <el-tooltip placement="top">
+                <el-icon><QuestionFilled /></el-icon>
+                <template #content>
+                  绫伙細绀轰緥 com.company.MyCustomListener锛岃嚜瀹氫箟绫诲繀椤诲疄鐜� org.flowable.engine.delegate.TaskListener 鎺ュ彛<br />
+                  琛ㄨ揪寮忥細绀轰緥 ${myObject.callMethod(task, task.eventName)}<br />
+                  濮旀墭琛ㄨ揪寮忥細绀轰緥 ${myListenerSpringBean} 锛岃 springBean 闇�瑕佸疄鐜� org.flowable.engine.delegate.TaskListener 鎺ュ彛
+                </template>
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.type">
+            <el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          :label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '琛ㄨ揪寮�'"
+          prop="className"
+        >
+          <el-input v-model="formData.className" type="text"></el-input>
+        </el-form-item>
+      </el-form>
+      <el-tabs type="border-card">
+        <el-tab-pane label="鍙傛暟">
+          <ListenerParam ref="listenerParamRef" :table-data="formData.params" />
+        </el-tab-pane>
+      </el-tabs>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="formDialog.closeDialog">鍙� 娑�</el-button>
+          <el-button type="primary" @click="submitEvent">纭� 瀹�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import ListenerParam from './ListenerParam.vue';
+import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
+import type { ExecutionListenerVO } from 'bpmnDesign';
+import type { Moddle, Modeler, ModdleElement } from 'bpmn';
+
+import usePanel from '../../hooks/usePanel';
+import useDialog from '@/hooks/useDialog';
+import useModelerStore from '@/store/modules/modeler';
+
+const emit = defineEmits(['close']);
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const selectRow = ref<ExecutionListenerVO | null>();
+const formDialog = useDialog({
+  title: selectRow.value ? '缂栬緫&淇濆瓨' : '鏂板&淇濆瓨'
+});
+
+const { showConfig, elementType, updateProperties } = usePanel({
+  element: toRaw(props.element)
+});
+const { getModdle } = useModelerStore();
+const moddle = getModdle();
+
+const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
+const tableRef = ref<VxeTableInstance<ExecutionListenerVO>>();
+const formRef = ref<ElFormInstance>();
+
+const initData: ExecutionListenerVO = {
+  event: '',
+  type: '',
+  className: '',
+  params: []
+};
+const formData = ref<ExecutionListenerVO>({ ...initData });
+const tableData = ref<ExecutionListenerVO[]>([]);
+const tableRules = ref<ElFormRules>({
+  event: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  type: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  className: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const submitEvent = async () => {
+  const error = await listenerParamRef.value.validate();
+  await formRef.value.validate((validate) => {
+    if (validate && !error) {
+      const $table = tableRef.value;
+      if ($table) {
+        formData.value.params = listenerParamRef.value.getTableData();
+        if (selectRow.value) {
+          Object.assign(selectRow.value, formData.value);
+        } else {
+          $table.insertAt({ ...formData.value }, -1);
+        }
+        updateElement();
+        formDialog.closeDialog();
+      }
+    }
+  });
+};
+
+const removeSelectRowEvent = async () => {
+  const $table = tableRef.value;
+  if ($table) {
+    const selectCount = $table.getCheckboxRecords().length;
+    if (selectCount === 0) {
+      proxy?.$modal.msgWarning('璇烽�夋嫨琛�');
+    } else {
+      await $table.removeCheckboxRow();
+      updateElement();
+    }
+  }
+};
+const insertEvent = async () => {
+  Object.assign(formData.value, initData);
+  selectRow.value = null;
+  formDialog.openDialog();
+};
+
+const editEvent = (row: ExecutionListenerVO) => {
+  Object.assign(formData.value, row);
+  selectRow.value = row;
+  formDialog.openDialog();
+};
+
+const removeEvent = async (row: ExecutionListenerVO) => {
+  await proxy?.$modal.confirm('鎮ㄧ‘瀹氳鍒犻櫎璇ユ暟鎹�?');
+  const $table = tableRef.value;
+  if ($table) {
+    await $table.remove(row);
+    updateElement();
+  }
+};
+const updateElement = () => {
+  const $table = tableRef.value;
+  const data = $table.getTableData().fullData;
+  if (data.length) {
+    let extensionElements = props.element.businessObject.get('extensionElements');
+    if (!extensionElements) {
+      extensionElements = moddle.create('bpmn:ExtensionElements');
+    }
+    // 娓呴櫎鏃у��
+    extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
+    data.forEach((item) => {
+      const executionListener = moddle.create('flowable:ExecutionListener');
+      executionListener['event'] = item.event;
+      executionListener[item.type] = item.className;
+      if (item.params && item.params.length) {
+        item.params.forEach((field) => {
+          const fieldElement = moddle.create('flowable:Field');
+          fieldElement['name'] = field.name;
+          fieldElement[field.type] = field.value;
+          executionListener.get('fields').push(fieldElement);
+        });
+      }
+      extensionElements.get('values').push(executionListener);
+    });
+    updateProperties({ extensionElements: extensionElements });
+  } else {
+    const extensionElements = props.element.businessObject[`extensionElements`];
+    if (extensionElements) {
+      extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
+    }
+  }
+};
+
+const cellDBLClickEvent: VxeTableEvents.CellDblclick<ExecutionListenerVO> = ({ row }) => {
+  editEvent(row);
+};
+
+const menuConfig = reactive<VxeTablePropTypes.MenuConfig<ExecutionListenerVO>>({
+  body: {
+    options: [
+      [
+        { code: 'edit', name: '缂栬緫', prefixIcon: 'vxe-icon-edit', disabled: false },
+        { code: 'remove', name: '鍒犻櫎', prefixIcon: 'vxe-icon-delete', disabled: false }
+      ]
+    ]
+  },
+  visibleMethod({ options, column }) {
+    const isDisabled = !column;
+    options.forEach((list) => {
+      list.forEach((item) => {
+        item.disabled = isDisabled;
+      });
+    });
+    return true;
+  }
+});
+const contextMenuClickEvent: VxeTableEvents.MenuClick<ExecutionListenerVO> = ({ menu, row, column }) => {
+  const $table = tableRef.value;
+  if ($table) {
+    switch (menu.code) {
+      case 'edit':
+        editEvent(row);
+        break;
+      case 'remove':
+        removeEvent(row);
+        break;
+    }
+  }
+};
+
+const initTableData = () => {
+  tableData.value =
+    props.element.businessObject.extensionElements?.values
+      .filter((item) => item.$type === 'flowable:ExecutionListener')
+      .map((item) => {
+        let type;
+        if ('class' in item) type = 'class';
+        if ('expression' in item) type = 'expression';
+        if ('delegateExpression' in item) type = 'delegateExpression';
+        return {
+          event: item.event,
+          type: type,
+          className: item[type],
+          params:
+            item.fields?.map((field) => {
+              let fieldType;
+              if ('stringValue' in field) fieldType = 'stringValue';
+              if ('expression' in field) fieldType = 'expression';
+              return {
+                name: field.name,
+                type: fieldType,
+                value: field[fieldType]
+              };
+            }) ?? []
+        };
+      }) ?? [];
+};
+
+onMounted(() => {
+  initTableData();
+});
+
+const typeSelect = [
+  { id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '绫�', value: 'class' },
+  { id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '琛ㄨ揪寮�', value: 'expression' },
+  { id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '濮旀墭琛ㄨ揪寮�', value: 'delegateExpression' }
+];
+const eventSelect = [
+  { id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '寮�濮�', value: 'start' },
+  { id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '缁撴潫', value: 'end' },
+  { id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '鍚敤', value: 'take' }
+];
+</script>
+
+<style scoped lang="scss">
+.el-badge {
+  :deep(.el-badge__content) {
+    top: 10px;
+  }
+}
+</style>

--
Gitblit v1.9.3