130 lines
3.6 KiB
Vue
130 lines
3.6 KiB
Vue
<template>
|
|
<div>
|
|
<select v-model="selectedGroup" @change="focusGroup">
|
|
<option v-for="g in groups" :key="g" :value="g">
|
|
Group {{ g }}
|
|
</option>
|
|
</select>
|
|
|
|
<div ref="cyEl" style="width: 100%; height: 800px;"></div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue';
|
|
import cytoscape from "cytoscape";
|
|
|
|
let CytoscapeOverlays = null
|
|
|
|
if (import.meta.client) {
|
|
const useOverlay = (await import('@/composables/useOverlay')).default
|
|
CytoscapeOverlays = await useOverlay()
|
|
}
|
|
|
|
useHead({
|
|
script: [
|
|
{
|
|
src: '/dist/cy_custom.js',
|
|
defer: true
|
|
}
|
|
]
|
|
});
|
|
|
|
const cyEl = ref(null);
|
|
const cy = ref(null);
|
|
const selectedGroup = ref(null);
|
|
const groups = ref([]);
|
|
|
|
onMounted(() => {
|
|
fetch("/group43200.json")
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
const nodes = data.entries.map(e => ({
|
|
data: { id: `n${e.id}`, label: e.graphics.name, group: e.group },
|
|
position: { x: e.graphics.x, y: e.graphics.y },
|
|
}));
|
|
const edges = data.relations.map(r => ({
|
|
data: {
|
|
source: `n${r.entry1}`,
|
|
target: `n${r.entry2}`,
|
|
}
|
|
}));
|
|
groups.value = [...new Set(data.entries.map(e => e.group))];
|
|
|
|
const colorPalette = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080'];
|
|
const groupColorMap = {};
|
|
groups.value.forEach((group, index) => {
|
|
groupColorMap[group] = colorPalette[index % colorPalette.length];
|
|
});
|
|
|
|
cy.value = cytoscape({
|
|
container: cyEl.value,
|
|
elements: [...nodes, ...edges],
|
|
style: [
|
|
{
|
|
selector: 'node',
|
|
style: {
|
|
label: 'data(label)',
|
|
'background-color': (node) => groupColorMap[node.data('group')] || '#ccc',
|
|
'text-valign': 'center',
|
|
'text-halign': 'center',
|
|
'width': 30,
|
|
'height': 30
|
|
}
|
|
},
|
|
{
|
|
selector: 'edge',
|
|
style: {
|
|
'line-color': '#ccc',
|
|
'target-arrow-shape': 'triangle'
|
|
}
|
|
}
|
|
],
|
|
layout: { name: 'preset' },
|
|
zoomingEnabled: true,
|
|
userZoomingEnabled: true
|
|
});
|
|
|
|
setTimeout(() => {
|
|
if (typeof applyCustomZoomHandling === 'function') {
|
|
applyCustomZoomHandling(cy.value);
|
|
}
|
|
}, 100);
|
|
});
|
|
});
|
|
|
|
function focusGroup() {
|
|
if (!cy.value || !selectedGroup.value) return;
|
|
|
|
const groupNodes = cy.value.nodes().filter(ele => ele.data('group') === selectedGroup.value);
|
|
if (groupNodes.length === 0) return;
|
|
|
|
const bb = groupNodes.boundingBox();
|
|
const container = cy.value.container();
|
|
const viewportWidth = container.clientWidth;
|
|
const viewportHeight = container.clientHeight;
|
|
const padding = 40;
|
|
const zoomX = (viewportWidth - 2 * padding) / bb.w;
|
|
const zoomY = (viewportHeight - 2 * padding) / bb.h;
|
|
const zoom = Math.min(zoomX, zoomY, 5);
|
|
|
|
cy.value.animate({
|
|
zoom: zoom,
|
|
center: { eles: groupNodes }
|
|
}, {
|
|
duration: 800
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.cy-container {
|
|
width: 100vw !important;
|
|
max-width: 100vw !important;
|
|
min-width: 0 !important;
|
|
height: 800px !important;
|
|
overflow: hidden !important;
|
|
position: relative;
|
|
}
|
|
</style>
|
|
|