5 changed files with 287 additions and 0 deletions
@ -0,0 +1,146 @@ |
|||
<template> |
|||
<div ref="containerRef" style="width: 100%"></div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue'; |
|||
import { InternalEvent, Graph, Geometry, Cell } from '@maxgraph/core'; |
|||
import type { VertexParameterType } from './types/VertexParameterType'; |
|||
|
|||
const containerRef = ref(); |
|||
let graph: Graph | null = null; |
|||
let parent: Cell | null = null; |
|||
let entries: VertexParameterType[] = []; |
|||
const cellTitleMap = {}; |
|||
|
|||
class ComponentsGraph extends Graph { |
|||
getTooltipForCell(cell) { |
|||
return cellTitleMap[cell.id]; |
|||
} |
|||
} |
|||
|
|||
onMounted(() => { |
|||
InternalEvent.disableContextMenu(containerRef.value); |
|||
graph = new ComponentsGraph(containerRef.value); |
|||
graph.setPanning(true); |
|||
graph.setHtmlLabels(true); |
|||
graph.setTooltips(true); |
|||
parent = graph.getDefaultParent(); |
|||
addEntry({ |
|||
id: 'start', |
|||
title: '开始', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:47px">开始</div>`, |
|||
width: 50, |
|||
height: 50, |
|||
style: { shape: 'ellipse' }, |
|||
}); |
|||
addEntry({ |
|||
id: 'decide', |
|||
title: '条件判断', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:97px">条件判断</div>`, |
|||
width: 100, |
|||
height: 50, |
|||
style: { shape: 'rhombus' }, |
|||
}); |
|||
addEntry({ |
|||
id: 'commandSet', |
|||
title: '指令集', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:47px">指令集</div>`, |
|||
width: 50, |
|||
height: 50, |
|||
style: { shape: 'rectangle' }, |
|||
}); |
|||
addEntry({ |
|||
id: 'resource', |
|||
title: '资源', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:47px">资源</div>`, |
|||
width: 50, |
|||
height: 50, |
|||
style: { shape: 'rectangle' }, |
|||
}); |
|||
|
|||
addEntry({ |
|||
id: 'resource', |
|||
title: '指令资源', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:47px">指令资源</div>`, |
|||
width: 50, |
|||
height: 50, |
|||
style: { shape: 'rectangle' }, |
|||
}); |
|||
|
|||
addEntry({ |
|||
id: 'subModel', |
|||
title: '子模型', |
|||
value: `<div class="w-full h-full align-middle text-center" style="width:47px">子模型</div>`, |
|||
width: 50, |
|||
height: 50, |
|||
style: { shape: 'rectangle' }, |
|||
}); |
|||
|
|||
refresh(); |
|||
}); |
|||
|
|||
const addVertexWithDefaultStyle = (parameter: VertexParameterType): Cell | undefined => { |
|||
addVertex({ |
|||
id: parameter.id, |
|||
title: parameter.title, |
|||
value: parameter.value, |
|||
geometry: parameter.geometry, |
|||
style: { |
|||
//'rectangle' | 'ellipse' | 'doubleEllipse' | 'rhombus' | 'line' | 'image' | 'arrow' | 'arrowConnector' | 'label' | 'cylinder' | 'swimlane' | 'connector' | 'actor' | 'cloud' | 'triangle' | 'hexagon'; |
|||
shape: parameter.style?.shape, |
|||
verticalAlign: parameter.style?.verticalAlign || 'middle', |
|||
verticalLabelPosition: parameter.style?.verticalLabelPosition || 'middle', |
|||
align: parameter.style?.align || 'left', |
|||
fontSize: parameter.style?.fontSize || 12, |
|||
rounded: parameter.style?.rounded || true, |
|||
whiteSpace: parameter.style?.whiteSpace || 'wrap', |
|||
fillColor: parameter.style?.fillColor || '#F7F2E0', |
|||
strokeColor: parameter.style?.strokeColor || 'black', |
|||
}, |
|||
}); |
|||
}; |
|||
|
|||
const addVertex = (parameter: VertexParameterType): Cell | undefined => { |
|||
const vertex = graph?.insertVertex({ |
|||
id: parameter.id, |
|||
parent: parent, |
|||
position: [parameter.geometry?.x || 0, parameter.geometry?.y || 0], |
|||
size: [parameter.geometry?.width || 10, parameter.geometry?.height || 10], |
|||
style: parameter.style, |
|||
value: parameter.value, |
|||
}); |
|||
cellTitleMap[parameter.id] = parameter.title; |
|||
return vertex; |
|||
}; |
|||
|
|||
const addEntry = (entry: VertexParameterType) => { |
|||
entry.geometry = new Geometry(0, 0, entry.width, entry.height); |
|||
entries.push(entry); |
|||
}; |
|||
|
|||
const refresh = () => { |
|||
let availableWidth = containerRef.value.clientWidth; |
|||
let currentWidth = 0; |
|||
for (const entry of entries) { |
|||
if (currentWidth + entry.width > availableWidth) { |
|||
entry.geometry.x = 0; |
|||
} else { |
|||
entry.geometry.x = currentWidth; |
|||
} |
|||
currentWidth += entry.width; |
|||
} |
|||
|
|||
graph.batchUpdate(() => { |
|||
for (const entry of entries) { |
|||
entry.geometry = new Geometry(70, 80, entry.width, entry.height); |
|||
} |
|||
for (const entry of entries) { |
|||
addVertexWithDefaultStyle(entry); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
addEntry, |
|||
}); |
|||
</script> |
@ -0,0 +1,53 @@ |
|||
<template> |
|||
<div ref="containerRef"></div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue'; |
|||
import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core'; |
|||
|
|||
const containerRef = ref(); |
|||
|
|||
onMounted(() => { |
|||
//const container = <HTMLElement>document.getElementById('graph-container'); |
|||
// Disables the built-in context menu |
|||
InternalEvent.disableContextMenu(containerRef.value); |
|||
|
|||
const graph = new Graph(containerRef.value); |
|||
graph.setPanning(true); // Use mouse right button for panning |
|||
// Gets the default parent for inserting new cells. This |
|||
// is normally the first child of the root (ie. layer 0). |
|||
const parent = graph.getDefaultParent(); |
|||
|
|||
// Adds cells to the model in a single step |
|||
graph.batchUpdate(() => { |
|||
const vertex01 = graph.insertVertex({ |
|||
parent, |
|||
position: [10, 10], |
|||
size: [100, 100], |
|||
value: 'rectangle', |
|||
}); |
|||
const vertex02 = graph.insertVertex({ |
|||
parent, |
|||
position: [350, 90], |
|||
size: [50, 50], |
|||
style: { |
|||
fillColor: 'orange', |
|||
shape: 'ellipse', |
|||
verticalAlign: 'top', |
|||
verticalLabelPosition: 'bottom', |
|||
}, |
|||
value: 'ellipse', |
|||
}); |
|||
graph.insertEdge({ |
|||
parent, |
|||
source: vertex01, |
|||
target: vertex02, |
|||
value: 'edge', |
|||
style: { |
|||
edgeStyle: 'orthogonalEdgeStyle', |
|||
rounded: true, |
|||
}, |
|||
}); |
|||
}); |
|||
}); |
|||
</script> |
@ -0,0 +1,22 @@ |
|||
<template> |
|||
<div style="height: 100%" class="px-1"> |
|||
<q-tabs model-value="currentSelectedTabRef" align="left" dense inline-label shrink outside-arrows mobile-arrows :breakpoint="0" no-caps> |
|||
<q-tab name="setting" label="设置" /> |
|||
<q-tab name="style" label="样式" /> |
|||
<q-tab name="text" label="文本" /> |
|||
<q-tab name="arrange" label="排列" /> |
|||
</q-tabs> |
|||
|
|||
<q-tab-panels v-model="currentSelectedTabRef" animated swipeable keep-alive style="height: calc(100% - 48px)"> |
|||
<q-tab-panel name="setting" class="px-0 pb-0" style="height: 100%"> </q-tab-panel> |
|||
<q-tab-panel name="style" class="px-0 pb-0" style="height: 100%"> </q-tab-panel> |
|||
<q-tab-panel name="text" class="px-0 pb-0" style="height: 100%"> </q-tab-panel> |
|||
<q-tab-panel name="arrange" class="px-0 pb-0" style="height: 100%"> </q-tab-panel> |
|||
</q-tab-panels> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
|
|||
const currentSelectedTabRef = ref('style'); |
|||
</script> |
@ -0,0 +1,19 @@ |
|||
<template> |
|||
<q-list class="rounded-borders"> |
|||
<q-expansion-item label="可用组件" default-opened switch-toggle-side header-style="padding-left:8px;padding-right:8px; " expand-icon-class="px-0"> |
|||
<Components></Components> |
|||
</q-expansion-item> |
|||
<q-expansion-item label="Account settings" switch-toggle-side header-style="padding-left:8px;padding-right:8px; " expand-icon-class="px-0"> |
|||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quidem, eius reprehenderit eos corrupti commodi magni quaerat ex numquam, dolorum officiis modi |
|||
facere maiores architecto suscipit iste eveniet doloribus ullam aliquid. |
|||
</q-expansion-item> |
|||
</q-list> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import Components from './Components.vue'; |
|||
</script> |
|||
<style> |
|||
.q-item__section--avatar { |
|||
min-width: 36px !important; |
|||
} |
|||
</style> |
@ -0,0 +1,47 @@ |
|||
<template> |
|||
<q-toolbar class="w-full border-b rounded-sm gap-x-1"> |
|||
<q-btn outline icon="bi-floppy" /> |
|||
<q-separator vertical /> |
|||
<q-btn-dropdown label="100%" outline> |
|||
<q-list> |
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label>Reset View</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
|
|||
<q-separator vertical /> |
|||
|
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label>25%</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
|
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label>50%</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
</q-list> |
|||
</q-btn-dropdown> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-zoom-in" /> |
|||
<q-btn outline icon="bi-zoom-out" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="undo" /> |
|||
<q-btn outline icon="redo" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-trash" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-layers" /> |
|||
<q-btn outline icon="bi-layers-half" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-paint-bucket" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-palette" /> |
|||
<q-separator vertical /> |
|||
<q-btn outline icon="bi-bezier2" /> |
|||
</q-toolbar> |
|||
</template> |
|||
<script setup lang="ts"></script> |
Loading…
Reference in new issue