9 changed files with 416 additions and 24 deletions
@ -1,11 +1,386 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<span>你好</span> |
|||
<div style="height: 100%;width: 100%"> |
|||
<el-row> |
|||
<el-col :span="4"> |
|||
<div style="text-align: center;margin-top: 10px;" > |
|||
<el-button icon="Plus" type="primary" @click="showAddDialog">新增节点</el-button> |
|||
<el-button type="primary" @click="save"> |
|||
<i class="ri-save-3-line"></i> 保存 |
|||
</el-button> |
|||
</div> |
|||
<div style="text-align: center;margin-top: 10px;" > |
|||
<el-button class="moduleButton" style="width: 50%;height: 100px;" :style="{background: checked ==='元数据'?'#ebf5ff':'none'}" @click="changeBackGround('元数据信息')"> |
|||
元数据信息模块 |
|||
</el-button> |
|||
</div> |
|||
<div style="text-align: center;margin-top: 10px"> |
|||
<el-button class="moduleButton" style="width: 50%;height: 100px" :style="{background: checked ==='其他模块'?'#ebf5ff':'none'}" @click="changeBackGround('其他模块')"> |
|||
其他模块----敬请期待 |
|||
</el-button> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="20"> |
|||
<div id="flow-container"></div> |
|||
</el-col> |
|||
</el-row> |
|||
<el-dialog |
|||
v-model="showAdd" |
|||
title="新增审批节点" |
|||
width="500" |
|||
> |
|||
<el-form :model="addForm" class="demo-form-inline"> |
|||
<el-form-item label="审批角色/人员:"> |
|||
<el-radio-group v-model="addForm.roleOrUser"> |
|||
<el-radio-button label="Role" value="Role" /> |
|||
<el-radio-button label="User" value="User" /> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-if="addForm.roleOrUser === 'Role'" label="选择角色:"> |
|||
<el-select v-model="addForm.role"> |
|||
<el-option v-for="item in roleList" :value="item.roleKey+'-'+item.remark" :label="item.remark" :key="item.roleKey"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item v-if="addForm.roleOrUser === 'User'" label="选择用户:"> |
|||
<el-input v-model="addForm.user" placeholder="请输入用户登录名"></el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button @click="showAdd = false">取消</el-button> |
|||
<el-button type="primary" @click="confirmAddNode"> |
|||
确定 |
|||
</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
<script setup> |
|||
import { Graph, Cell, Shape } from '@antv/x6' |
|||
import {v4 as uuid} from 'uuid' |
|||
import { Stencil } from '@antv/x6-plugin-stencil' |
|||
import { Keyboard } from '@antv/x6-plugin-keyboard' |
|||
import { Selection } from '@antv/x6-plugin-selection' |
|||
import { Clipboard } from '@antv/x6-plugin-clipboard' |
|||
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue' |
|||
import { listRole } from "@/api/system/role"; |
|||
import {getflowList } from "@/api/flow/flow"; |
|||
|
|||
|
|||
let graph = null |
|||
const roleList = ref([]) |
|||
const data = ref([]) |
|||
const checked = ref('元数据') |
|||
const ports = ref({ |
|||
groups: { |
|||
top: { |
|||
position: 'top', |
|||
attrs: { |
|||
circle: { |
|||
r: 4, |
|||
magnet: true, |
|||
stroke: '#5F95FF', |
|||
strokeWidth: 1, |
|||
fill: '#fff', |
|||
style: { |
|||
visibility: 'hidden', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
right: { |
|||
position: 'right', |
|||
attrs: { |
|||
circle: { |
|||
r: 4, |
|||
magnet: true, |
|||
stroke: '#5F95FF', |
|||
strokeWidth: 1, |
|||
fill: '#fff', |
|||
style: { |
|||
visibility: 'hidden', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
bottom: { |
|||
position: 'bottom', |
|||
attrs: { |
|||
circle: { |
|||
r: 4, |
|||
magnet: true, |
|||
stroke: '#5F95FF', |
|||
strokeWidth: 1, |
|||
fill: '#fff', |
|||
style: { |
|||
visibility: 'hidden', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
left: { |
|||
position: 'left', |
|||
attrs: { |
|||
circle: { |
|||
r: 4, |
|||
magnet: true, |
|||
stroke: '#5F95FF', |
|||
strokeWidth: 1, |
|||
fill: '#fff', |
|||
style: { |
|||
visibility: 'hidden', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
items: [ |
|||
{ |
|||
group: 'top', |
|||
}, |
|||
{ |
|||
group: 'right', |
|||
}, |
|||
{ |
|||
group: 'bottom', |
|||
}, |
|||
{ |
|||
group: 'left', |
|||
}, |
|||
], |
|||
}) |
|||
const showAdd = ref(false) |
|||
const addForm = ref({roleOrUser:'Role',role:'',user:''}) |
|||
function confirmAddNode(){ |
|||
let node = { |
|||
"id": uuid(), |
|||
"shape": "activity", |
|||
"width": 100, |
|||
"height": 60, |
|||
"position": { |
|||
"x": 0, |
|||
"y": 0 |
|||
}, |
|||
} |
|||
if (addForm.value.roleOrUser === 'Role'){ |
|||
let split = addForm.value.role.split("-") |
|||
node.label = split[1] |
|||
node.code = split[0] |
|||
node.type = 'Role' |
|||
}else { |
|||
node.label = addForm.value.user |
|||
node.code = addForm.value.user |
|||
node.type = 'User' |
|||
} |
|||
graph.addNode(node) |
|||
showAdd.value = false |
|||
} |
|||
function showPorts (ports, show){ |
|||
for (let i = 0, len = ports.length; i < len; i += 1) { |
|||
ports[i].style.visibility = show ? 'visible' : 'hidden' |
|||
} |
|||
} |
|||
function register(){ |
|||
Graph.registerNode( |
|||
'activity', |
|||
{ |
|||
inherit: 'rect', |
|||
markup: [ |
|||
{ |
|||
tagName: 'rect', |
|||
selector: 'body', |
|||
}, |
|||
{ |
|||
tagName: 'image', |
|||
selector: 'img', |
|||
}, |
|||
{ |
|||
tagName: 'text', |
|||
selector: 'label', |
|||
}, |
|||
], |
|||
attrs: { |
|||
body: { |
|||
rx: 6, |
|||
ry: 6, |
|||
stroke: '#5F95FF', |
|||
fill: '#EFF4FF', |
|||
strokeWidth: 1, |
|||
}, |
|||
img: { |
|||
x: 6, |
|||
y: 6, |
|||
width: 16, |
|||
height: 16, |
|||
'xlink:href': |
|||
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*pwLpRr7QPGwAAAAAAAAAAAAAARQnAQ', |
|||
}, |
|||
label: { |
|||
fontSize: 12, |
|||
fill: '#262626', |
|||
}, |
|||
}, |
|||
ports: { ...ports.value }, |
|||
}, |
|||
true, |
|||
) |
|||
Graph.registerEdge( |
|||
'bpmn-edge', |
|||
{ |
|||
inherit: 'edge', |
|||
attrs: { |
|||
line: { |
|||
stroke: '#A2B1C3', |
|||
strokeWidth: 2, |
|||
}, |
|||
}, |
|||
}, |
|||
true, |
|||
) |
|||
} |
|||
function changeBackGround(module){ |
|||
if (module === '元数据信息'){ |
|||
checked.value = '元数据' |
|||
} |
|||
if (module === '其他模块'){ |
|||
checked.value = '其他模块' |
|||
} |
|||
getflowList(checked.value).then(res=>{ |
|||
|
|||
}) |
|||
} |
|||
function showAddDialog(){ |
|||
showAdd.value = true |
|||
listRole({pageNum:1,pageSize:100,status:0}).then(res=>{ |
|||
roleList.value = res.rows; |
|||
}) |
|||
} |
|||
function save(){ |
|||
let json = graph.toJSON() |
|||
let array = json.cells; |
|||
console.log(array) |
|||
let flows = [] |
|||
if (array.length>0){ |
|||
for (let i = 0; i < array.length; i++) { |
|||
let cell = array[i] |
|||
if (cell.shape === "activity"){ |
|||
flows.push({ |
|||
id: cell.id, |
|||
code: cell.code, |
|||
text: cell.attrs.text.text, |
|||
type: cell.type, |
|||
x: cell.position.x, |
|||
y: cell.position.y, |
|||
parent: [], |
|||
}) |
|||
} |
|||
} |
|||
for (let i = 0; i < array.length; i++) { |
|||
let cell = array[i] |
|||
if (cell.shape === "edge"){ |
|||
let target = cell.target.cell |
|||
let source = cell.source.cell |
|||
let sourceNode = flows.find(item => item.id === source) |
|||
let targetNode = flows.find(item => item.id === target) |
|||
if (sourceNode !== undefined && targetNode !== undefined){ |
|||
for (let j = 0; j < flows.length; j++) { |
|||
if (flows[j].id === target){ |
|||
flows[j].parent.push(source) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
console.log(flows) |
|||
} |
|||
function loadGraphData(){ |
|||
let array = [] |
|||
data.value.forEach((item) => { |
|||
let node = { |
|||
id: item.id, |
|||
code: item.code, |
|||
attrs: {text:{text: item.text}}, |
|||
position:{x:item.x,y:item.y}, |
|||
width: 100, |
|||
height: 60, |
|||
shape: "activity" |
|||
} |
|||
array.push(node) |
|||
}) |
|||
data.value.forEach((item) => { |
|||
if (item.parent && item.parent.length > 0){ |
|||
item.parent.forEach((parentNodeId) =>{ |
|||
let node = { |
|||
id: uuid(), |
|||
shape: 'edge', |
|||
source: {cell: parentNodeId}, |
|||
target: {cell: item.id} |
|||
} |
|||
array.push(node) |
|||
}) |
|||
} |
|||
}) |
|||
graph.fromJSON(array) |
|||
graph.zoomToFit({ padding:10, maxScale: 1 }) |
|||
} |
|||
onMounted(re=>{ |
|||
register() |
|||
graph = new Graph({ |
|||
container: document.getElementById('flow-container'), |
|||
width: 800, |
|||
height: 800, |
|||
grid:true, |
|||
autoResize: true, |
|||
connecting: { |
|||
router: 'orth', |
|||
}, |
|||
createEdge() { |
|||
return new Shape.Edge({ |
|||
"id": uuid(), |
|||
"shape": "bpmn-edge", |
|||
"source": "7", |
|||
"target": "13" |
|||
}); |
|||
} |
|||
}) |
|||
graph.use(new Keyboard()) |
|||
.use( |
|||
new Selection({ |
|||
rubberband: true, |
|||
showNodeSelectionBox: true, |
|||
}), |
|||
) |
|||
// select all |
|||
graph.bindKey(['meta+a', 'ctrl+a'], () => { |
|||
const nodes = graph.getNodes() |
|||
if (nodes) { |
|||
graph.select(nodes) |
|||
} |
|||
}) |
|||
// delete |
|||
graph.bindKey('backspace', () => { |
|||
const cells = graph.getSelectedCells() |
|||
if (cells.length) { |
|||
graph.removeCells(cells) |
|||
} |
|||
}) |
|||
graph.on('node:mouseenter', () => { |
|||
const container = document.getElementById('flow-container') |
|||
const ports = container.querySelectorAll('.x6-port-body',) |
|||
showPorts(ports, true) |
|||
}) |
|||
graph.on('node:mouseleave', () => { |
|||
const container = document.getElementById('flow-container') |
|||
const ports = container.querySelectorAll('.x6-port-body',) |
|||
showPorts(ports, false) |
|||
}) |
|||
loadGraphData() |
|||
}) |
|||
|
|||
</script> |
|||
<style scoped lang="scss"> |
|||
|
|||
.moduleButton:hover{ |
|||
background: #ebf5ff !important; |
|||
} |
|||
</style> |
Loading…
Reference in new issue