From 7bf1f40b58ee3c61664b5f16a84cbaac59f88735 Mon Sep 17 00:00:00 2001
From: du <13220750630.163.com>
Date: 星期一, 23 六月 2025 15:00:28 +0800
Subject: [PATCH] 流程图

---
 ruoyi-ui/apps/web-antd/src/views/system/process/index.vue |  507 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 462 insertions(+), 45 deletions(-)

diff --git a/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue b/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue
index 279ebf2..fd598fd 100644
--- a/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue
+++ b/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue
@@ -1,55 +1,472 @@
 <template>
-<div style="padding: 20px">
-  <a-card style="width: 100%" title="娴佺▼瀹氫箟鍒楄〃">
-    <a-table :dataSource="dataSource" :columns="columns" />
-  </a-card>
-</div>
+  <div class="process-definition-container">
+    <a-card title="娴佺▼瀹氫箟鍒楄〃" :bordered="false">
+      <div class="table-actions">
+        <a-button type="primary" @click="showCreateModal">鏂板娴佺▼鍥�</a-button>
+      </div>
+      <a-table
+        :columns="columns"
+        :data-source="definitions"
+        :row-key="record => record.id"
+        :pagination="pagination"
+        :loading="loading"
+        @change="handleTableChange"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'action'">
+            <a-space>
+              <a-button type="link" @click="showDiagram(record)">鏌ョ湅</a-button>
+              <a-button type="link" @click="editDiagram(record)">淇敼</a-button>
+              <a-button type="link" danger @click="deleteDefinition(record)">鍒犻櫎</a-button>
+            </a-space>
+          </template>
+        </template>
+      </a-table>
+    </a-card>
+
+    <!-- 娴佺▼鍥炬煡鐪嬫ā鎬佹 -->
+    <a-modal
+      v-model:visible="diagramVisible"
+      title="娴佺▼鍥炬煡鐪�"
+      width="80%"
+      :footer="null"
+      @cancel="handleDiagramCancel"
+    >
+      <div style="text-align: center">
+        <img
+          v-if="currentDiagramUrl"
+          :src="currentDiagramUrl"
+          alt="娴佺▼鍥�"
+          style="max-width: 100%"
+        />
+        <a-skeleton v-else active />
+      </div>
+    </a-modal>
+
+    <!-- 娴佺▼鍥剧紪杈戞ā鎬佹 -->
+    <a-modal
+      v-model:visible="editorVisible"
+      :title="editorTitle"
+      width="90%"
+      :maskClosable="false"
+      :okText="'淇濆瓨'"
+      :cancelText="'鍙栨秷'"
+      :confirmLoading="editorSaving"
+      @ok="handleEditorOk"
+      @cancel="handleEditorCancel"
+      :destroyOnClose="true"
+      :afterClose="handleEditorAfterClose"
+      :style="{ top: '20px' }"
+      :bodyStyle="{
+      padding: '0',
+      height: 'calc(100vh - 100px)',
+      overflow: 'hidden',
+      display: 'flex',
+      flexDirection: 'column'
+    }"
+    >
+      <div class="editor-container">
+        <div v-if="isCreateMode" class="create-form">
+          <a-form layout="vertical">
+            <a-form-item label="娴佺▼鍚嶇О" required>
+              <a-input v-model:value="newProcess.name" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" />
+            </a-form-item>
+            <a-form-item label="娴佺▼Key" required>
+              <a-input v-model:value="newProcess.key" placeholder="璇疯緭鍏ユ祦绋婯ey" />
+            </a-form-item>
+            <a-form-item label="娴佺▼鎻忚堪">
+              <a-textarea v-model:value="newProcess.description" placeholder="璇疯緭鍏ユ祦绋嬫弿杩�" />
+            </a-form-item>
+          </a-form>
+        </div>
+        <div class="bpmn-editor" ref="bpmnEditor"></div>
+      </div>
+    </a-modal>
+  </div>
 </template>
 
-<script>
-export default {
-  setup() {
-    return {
-      dataSource: [
-        {
-          key: '1',
-          name: '鑳″溅鏂�',
-          age: 32,
-          address: '瑗挎箹鍖烘箹搴曞叕鍥�1鍙�',
-        },
-        {
-          key: '2',
-          name: '鑳″溅绁�',
-          age: 42,
-          address: '瑗挎箹鍖烘箹搴曞叕鍥�1鍙�',
-        },
-      ],
+<script setup>
+import { ref, onMounted, nextTick } from 'vue'
+import { message, Modal } from 'ant-design-vue'
+import BpmnModeler from 'bpmn-js/lib/Modeler'
+import 'bpmn-js/dist/assets/diagram-js.css'
+import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
+import 'bpmn-js/dist/assets/bpmn-js.css'
 
-      columns: [
-        {
-          title: '濮撳悕',
-          dataIndex: 'name',
-          key: 'name',
-          align: 'center',
-        },
-        {
-          title: '骞撮緞',
-          dataIndex: 'age',
-          key: 'age',
-          align: 'center',
-        },
-        {
-          title: '浣忓潃',
-          dataIndex: 'address',
-          key: 'address',
-          align: 'center',
-        },
-      ],
-    };
+// 琛ㄦ牸鍒楀畾涔�
+const columns = [
+  {
+    title: 'ID',
+    dataIndex: 'id',
+    key: 'id',
+    ellipsis: true
   },
-};
+  {
+    title: '鍚嶇О',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: 'Key',
+    dataIndex: 'key',
+    key: 'key'
+  },
+  {
+    title: '鎿嶄綔',
+    key: 'action',
+  }
+]
+
+// 鏁版嵁鐘舵��
+const definitions = ref([])
+const loading = ref(false)
+const pagination = ref({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true,
+  pageSizeOptions: ['10', '20', '50']
+})
+
+// 娴佺▼鍥炬煡鐪嬬浉鍏崇姸鎬�
+const diagramVisible = ref(false)
+const currentDiagramUrl = ref('')
+const currentProcessDefinition = ref(null)
+
+// 娴佺▼鍥剧紪杈戠浉鍏崇姸鎬�
+const editorVisible = ref(false)
+const editorSaving = ref(false)
+const editorTitle = ref('娴佺▼鍥剧紪杈�')
+const bpmnModeler = ref(null)
+const bpmnEditor = ref(null)
+const isCreateMode = ref(false)
+const newProcess = ref({
+  name: '',
+  key: '',
+  description: ''
+})
+
+// 鑾峰彇娴佺▼瀹氫箟鍒楄〃
+const fetchProcessDefinitions = async (params = {}) => {
+  loading.value = true
+  try {
+    // 妯℃嫙鏁版嵁
+    const mockData = [
+      { id: '1', name: '璇峰亣娴佺▼', key: 'leaveProcess'},
+      { id: '2', name: '鎶ラ攢娴佺▼', key: 'expenseProcess'},
+      { id: '3', name: '閲囪喘娴佺▼', key: 'purchaseProcess' }
+    ]
+    // 妯℃嫙鍒嗛〉
+    const start = (params.page - 1) * params.size
+    const end = start + params.size
+    definitions.value = mockData.slice(start, end)
+    pagination.value.total = mockData.length
+  } catch (error) {
+    message.error('鍔犺浇娴佺▼瀹氫箟澶辫触:'+ error.message)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 琛ㄦ牸鍒嗛〉/鎺掑簭鍙樺寲澶勭悊
+const handleTableChange = (pag, filters, sorter) => {
+  const params = {
+    page: pag.current,
+    size: pag.pageSize
+  }
+
+  if (sorter.field) {
+    params.sort = sorter.field
+    params.order = sorter.order === 'ascend' ? 'asc' : 'desc'
+  }
+
+  fetchProcessDefinitions(params)
+}
+
+// 鏄剧ず娴佺▼鍥�
+const showDiagram = (record) => {
+  currentProcessDefinition.value = record
+  currentDiagramUrl.value = `https://via.placeholder.com/800x600?text=娴佺▼鍥�+${record.id}`
+  diagramVisible.value = true
+}
+
+// 閿�姣丅PMN缂栬緫鍣�
+const destroyBpmnEditor = () => {
+  if (bpmnModeler.value) {
+    bpmnModeler.value.destroy()
+    bpmnModeler.value = null
+  }
+}
+
+// 鍒濆鍖朆PMN缂栬緫鍣�
+const initBpmnEditor = async (xml) => {
+  await nextTick()
+
+  // 鍏堥攢姣佹棫鐨勭紪杈戝櫒
+  destroyBpmnEditor()
+
+  try {
+    // 鍒涘缓鏂扮殑缂栬緫鍣ㄥ疄渚�
+    bpmnModeler.value = new BpmnModeler({
+      container: bpmnEditor.value,
+    })
+
+    // 鍔犺浇XML鎴栭粯璁ゆ祦绋嬪浘
+    const diagram = xml || `
+      <?xml version="1.0" encoding="UTF-8"?>
+      <bpmn:definitions
+        xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
+        xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+        xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
+        xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
+        id="Definitions_1"
+        targetNamespace="http://bpmn.io/schema/bpmn">
+        <bpmn:process id="Process_1" isExecutable="false">
+          <bpmn:startEvent id="StartEvent_1" />
+        </bpmn:process>
+        <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+          <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
+            <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
+              <dc:Bounds x="173" y="102" width="36" height="36" />
+            </bpmndi:BPMNShape>
+          </bpmndi:BPMNPlane>
+        </bpmndi:BPMNDiagram>
+      </bpmn:definitions>
+    `
+
+    await bpmnModeler.value.importXML(diagram)
+  } catch (err) {
+    console.error('Error rendering diagram', err)
+    message.error('鍒濆鍖栨祦绋嬪浘缂栬緫鍣ㄥけ璐�: ' + err.message)
+  }
+}
+
+// 鏄剧ず鍒涘缓鏂版祦绋嬫ā鎬佹
+const showCreateModal = () => {
+  isCreateMode.value = true
+  editorTitle.value = '鍒涘缓鏂版祦绋嬪浘'
+  editorVisible.value = true
+
+  // 閲嶇疆琛ㄥ崟
+  newProcess.value = {
+    name: '',
+    key: '',
+    description: ''
+  }
+
+  // 鍒濆鍖栫紪杈戝櫒
+  initBpmnEditor()
+}
+
+// 缂栬緫娴佺▼鍥�
+const editDiagram = async (record) => {
+  isCreateMode.value = false
+  currentProcessDefinition.value = record
+  editorTitle.value = `缂栬緫娴佺▼ - ${record.name}`
+  editorVisible.value = true
+
+  // 妯℃嫙鍔犺浇娴佺▼瀹氫箟XML
+  const mockXml = `
+    <?xml version="1.0" encoding="UTF-8"?>
+    <bpmn:definitions
+      xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
+      xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+      xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
+      xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
+      id="Definitions_1"
+      targetNamespace="http://bpmn.io/schema/bpmn">
+      <bpmn:process id="${record.id}" name="${record.name}" isExecutable="true">
+        <bpmn:startEvent id="StartEvent_1" name="寮�濮�" />
+        <bpmn:userTask id="UserTask_1" name="鎻愪氦鐢宠" />
+        <bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="UserTask_1" />
+        <bpmn:endEvent id="EndEvent_1" name="缁撴潫" />
+        <bpmn:sequenceFlow id="Flow_2" sourceRef="UserTask_1" targetRef="EndEvent_1" />
+      </bpmn:process>
+      <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+        <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="${record.id}">
+          <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
+            <dc:Bounds x="173" y="102" width="36" height="36" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNShape id="UserTask_1_di" bpmnElement="UserTask_1">
+            <dc:Bounds x="280" y="80" width="100" height="80" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
+            <dc:Bounds x="450" y="102" width="36" height="36" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
+            <di:waypoint x="209" y="120" />
+            <di:waypoint x="280" y="120" />
+          </bpmndi:BPMNEdge>
+          <bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
+            <di:waypoint x="380" y="120" />
+            <di:waypoint x="450" y="120" />
+          </bpmndi:BPMNEdge>
+        </bpmndi:BPMNPlane>
+      </bpmndi:BPMNDiagram>
+    </bpmn:definitions>
+  `
+
+  // 鍒濆鍖栫紪杈戝櫒骞跺姞杞絏ML
+  initBpmnEditor(mockXml)
+}
+
+// 淇濆瓨娴佺▼鍥句慨鏀�
+const handleEditorOk = async () => {
+  editorSaving.value = true
+
+  try {
+    if (isCreateMode.value) {
+      // 楠岃瘉琛ㄥ崟
+      if (!newProcess.value.name || !newProcess.value.key) {
+        message.error('璇峰~鍐欐祦绋嬪悕绉板拰Key')
+        return
+      }
+
+      // 鑾峰彇XML
+      const { xml } = await bpmnModeler.value.saveXML({ format: true })
+      console.log('鏂版祦绋媂ML:', xml)
+
+      // 妯℃嫙鍒涘缓鏂版祦绋�
+      await new Promise(resolve => setTimeout(resolve, 1000))
+
+      // 娣诲姞鍒板垪琛�
+      const newId = Math.max(...definitions.value.map(d => parseInt(d.id))) + 1
+      definitions.value.unshift({
+        id: newId.toString(),
+        name: newProcess.value.name,
+        key: newProcess.value.key,
+        version: 1
+      })
+
+      message.success('鏂版祦绋嬪垱寤烘垚鍔�')
+    } else {
+      // 鑾峰彇淇敼鍚庣殑XML
+      const { xml } = await bpmnModeler.value.saveXML({ format: true })
+      console.log('淇敼鍚庣殑XML:', xml)
+
+      // 妯℃嫙淇濆瓨
+      await new Promise(resolve => setTimeout(resolve, 1000))
+
+      message.success('娴佺▼鍥句繚瀛樻垚鍔�')
+    }
+
+    editorVisible.value = false
+  } catch (error) {
+    console.error('Error saving BPMN diagram', error)
+    message.error('鎿嶄綔澶辫触: ' + error.message)
+  } finally {
+    editorSaving.value = false
+  }
+}
+
+// 鍒犻櫎娴佺▼瀹氫箟
+const deleteDefinition = (record) => {
+  Modal.confirm({
+    title: '纭鍒犻櫎娴佺▼?',
+    content: `纭畾瑕佸垹闄ゆ祦绋� "${record.name}" 鍚楋紵姝ゆ搷浣滀笉鍙仮澶嶃�俙,
+    okText: '纭',
+    okType: 'danger',
+    cancelText: '鍙栨秷',
+    onOk() {
+      // 妯℃嫙鍒犻櫎
+      definitions.value = definitions.value.filter(item => item.id !== record.id)
+      message.success('娴佺▼鍒犻櫎鎴愬姛')
+    }
+  })
+}
+
+// 妯℃�佹鍏抽棴鍚庢竻鐞�
+const handleEditorAfterClose = () => {
+  destroyBpmnEditor()
+}
+
+const handleEditorCancel = () => {
+  editorVisible.value = false
+}
+
+const handleDiagramCancel = () => {
+  diagramVisible.value = false
+}
+
+// 鍒濆鍖栧姞杞芥暟鎹�
+onMounted(() => {
+  const params = {
+    page: 1,
+    size: 10
+  }
+  fetchProcessDefinitions(params)
+})
 </script>
 
 <style scoped>
+.process-definition-container {
+  padding: 20px;
+  background: #fff;
+}
 
+.table-actions {
+  margin-bottom: 16px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.editor-container {
+  height: 600px;
+  display: flex;
+  flex-direction: column;
+}
+.bpmn-editor {
+  flex: 1;
+  border: 1px solid #d9d9d9;
+  border-radius: 2px;
+  margin-top: 16px;
+  min-height: 500px; /* 纭繚缂栬緫鍣ㄦ湁瓒冲楂樺害 */
+}
+
+.create-form {
+  padding: 16px;
+  background: #fafafa;
+  border-radius: 2px;
+  border: 1px solid #d9d9d9;
+  margin-bottom: 16px;
+}
+
+/* 闅愯棌BPMN姘村嵃 */
+.bpmn-editor :deep(.bjs-powered-by) {
+  display: none !important;
+}
+.editor-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+}
+
+.bpmn-editor {
+  flex: 1;
+  min-height: 0; /* 閲嶈锛氬厑璁竑lex瀹瑰櫒鏀剁缉 */
+  border: 1px solid #d9d9d9;
+  border-radius: 2px;
+  margin-top: 16px;
+}
+
+.create-form {
+  padding: 16px;
+  background: #fafafa;
+  border-radius: 2px;
+  border: 1px solid #d9d9d9;
+  margin-bottom: 16px;
+}
+
+/* 闅愯棌BPMN姘村嵃 */
+.bpmn-editor :deep(.bjs-powered-by) {
+  display: none !important;
+}
+
+/* 纭繚BPMN宸ュ叿鏍忓彲瑙� */
+.bpmn-editor :deep(.djs-palette) {
+  top: 20px;
+  left: 20px;
+}
 </style>

--
Gitblit v1.9.3