¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <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="请è¾å
¥æµç¨Key" /> |
| | | </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 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' |
| | | |
| | | // è¡¨æ ¼åå®ä¹ |
| | | 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 |
| | | } |
| | | |
| | | // 鿝BPMNç¼è¾å¨ |
| | | const destroyBpmnEditor = () => { |
| | | if (bpmnModeler.value) { |
| | | bpmnModeler.value.destroy() |
| | | bpmnModeler.value = null |
| | | } |
| | | } |
| | | |
| | | // åå§åBPMNç¼è¾å¨ |
| | | 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> |
| | | ` |
| | | |
| | | // åå§åç¼è¾å¨å¹¶å è½½XML |
| | | 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('æ°æµç¨XML:', 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; /* éè¦ï¼å
许flex容卿¶ç¼© */ |
| | | 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> |