初始化

This commit is contained in:
2026-03-26 20:18:20 +08:00
commit 120a5b4dfd
368 changed files with 35926 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
<script setup lang="tsx">
import { shallowRef, useTemplateRef, watch } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { vResizeObserver } from '@vueuse/components';
import type { CustomBehaviorOption, Graph } from '@antv/g6';
import { useAntFlow } from './antv-g6-flow';
import { nodeStatus } from './status';
import type { CustomGraphData } from './types';
defineOptions({ name: 'AntvFLow' });
interface Props {
behaviors?: CustomBehaviorOption[];
data: CustomGraphData;
selected?: string;
height?: string;
autoFit?: 'view' | 'center';
}
const props = defineProps<Props>();
const containerRef = useTemplateRef('containerRef');
const graphRef = shallowRef<Graph | null>(null);
// 监听容器尺寸变化,调整画布大小为图容器大小
const onContainerResize = useDebounceFn(() => {
if (graphRef.value) {
graphRef.value.resize();
}
}, 5);
async function draw() {
if (graphRef.value) {
graphRef.value.destroy();
}
const { graph } = useAntFlow({
container: 'antv-flow',
data: props.data,
behaviors: props.behaviors,
autoFit: props.autoFit
});
graphRef.value = graph;
await selectNode();
}
async function selectNode() {
if (props.selected && graphRef.value) {
try {
await graphRef.value.setElementState(props.selected, 'selected');
} catch {}
}
}
function zoomOut() {
graphRef.value?.zoomBy(0.9);
}
function zoomIn() {
graphRef.value?.zoomBy(1.1);
}
function resetZoom() {
graphRef.value?.zoomTo(1);
graphRef.value?.fitCenter();
}
function fitZoom() {
graphRef.value?.fitView();
graphRef.value?.fitCenter();
}
watch(
[() => props.data, () => props.selected],
() => {
draw();
},
{ deep: true }
);
defineExpose({ selectNode, graph: graphRef });
</script>
<template>
<div class="relative">
<!-- canvas toolbar -->
<div class="absolute left-0 right-0 z-1 flex items-center items-stretch justify-between">
<ElButtonGroup size="small" class="bg-white!">
<ElButton @click="zoomOut">
<icon-mingcute:zoom-out-line />
</ElButton>
<ElButton @click="zoomIn">
<icon-mingcute:zoom-in-line />
</ElButton>
<ElButton @click="resetZoom">
<icon-icon-park-outline:equal-ratio />
</ElButton>
<ElButton @click="fitZoom">
<icon-gg:ratio />
</ElButton>
</ElButtonGroup>
<div class="flex-center gap-12px">
<ElPopover placement="bottom-end" :width="200" :animated="false">
<template #reference>
<ElButton size="small" class="bg-white!">
<icon-fe:question />
</ElButton>
</template>
<div class="flex-col gap-8px">
<div span="2" class="text-12px font-bold">节点图例</div>
<ElRow>
<ElCol v-for="(config, status) in nodeStatus" :key="status" :span="12" class="mb-8px flex-center">
<ElTag size="small" round :bordered="false">
<template #default>
<icon-f7:flag-circle-fill v-if="status === 'MILESTONE'" :style="{ color: config.color }" />
<icon-f7:circle-fill v-else :style="{ color: config.color }" />
{{ config.type }}
</template>
</ElTag>
</ElCol>
</ElRow>
</div>
</ElPopover>
</div>
</div>
<!-- canvas container -->
<div
id="antv-flow"
ref="containerRef"
v-resize-observer="onContainerResize"
class="w-full"
:style="{ height: props.height || '300px' }"
@contextmenu="event => event.preventDefault()"
></div>
</div>
</template>