|
@@ -5,6 +5,7 @@ import { VueNodeModel } from '@logicflow/vue-node-registry';
|
|
|
|
|
|
|
|
import { useEventListener } from '@/libs/logic-flow/use';
|
|
import { useEventListener } from '@/libs/logic-flow/use';
|
|
|
|
|
|
|
|
|
|
+import { ID_End, ID_Start } from './config';
|
|
|
import type { FlowNodeProperties } from './index';
|
|
import type { FlowNodeProperties } from './index';
|
|
|
import FlowNodeInlay from './FlowNodeInlay.vue';
|
|
import FlowNodeInlay from './FlowNodeInlay.vue';
|
|
|
|
|
|
|
@@ -21,6 +22,8 @@ const getGraph = inject<() => GraphModelInstance>('getGraph');
|
|
|
const text = ref('');
|
|
const text = ref('');
|
|
|
const id = ref('');
|
|
const id = ref('');
|
|
|
|
|
|
|
|
|
|
+const popover = ref(false);
|
|
|
|
|
+
|
|
|
const properties = ref<FlowNodeProperties>({
|
|
const properties = ref<FlowNodeProperties>({
|
|
|
width: 0,
|
|
width: 0,
|
|
|
height: 0,
|
|
height: 0,
|
|
@@ -29,6 +32,10 @@ const properties = ref<FlowNodeProperties>({
|
|
|
iconColor: '#000',
|
|
iconColor: '#000',
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+const appendNodes = shallowRef<{ id: string; text: string }[]>([]);
|
|
|
|
|
+const linkNodes = shallowRef<{ id: string; text: string; disabled?: boolean }[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+let mouseenterTimer: ReturnType<typeof setTimeout>;
|
|
|
tryOnMounted(() => {
|
|
tryOnMounted(() => {
|
|
|
const model = getNode?.()!;
|
|
const model = getNode?.()!;
|
|
|
const graph = getGraph?.()!;
|
|
const graph = getGraph?.()!;
|
|
@@ -43,15 +50,102 @@ tryOnMounted(() => {
|
|
|
(event) => updateProperties(event.properties),
|
|
(event) => updateProperties(event.properties),
|
|
|
(event) => event.id === id.value
|
|
(event) => event.id === id.value
|
|
|
);
|
|
);
|
|
|
|
|
+ useEventListener(
|
|
|
|
|
+ graph.eventCenter,
|
|
|
|
|
+ 'node:dnd-drag',
|
|
|
|
|
+ () => {
|
|
|
|
|
+ popover.value = false;
|
|
|
|
|
+ clearTimeout(mouseenterTimer);
|
|
|
|
|
+ },
|
|
|
|
|
+ (event) => event.data?.id === id.value
|
|
|
|
|
+ );
|
|
|
|
|
+ useEventListener(
|
|
|
|
|
+ graph.eventCenter,
|
|
|
|
|
+ 'node:drag',
|
|
|
|
|
+ () => {
|
|
|
|
|
+ popover.value = false;
|
|
|
|
|
+ clearTimeout(mouseenterTimer);
|
|
|
|
|
+ },
|
|
|
|
|
+ (event) => event.data?.id === id.value
|
|
|
|
|
+ );
|
|
|
|
|
+ useEventListener(
|
|
|
|
|
+ graph.eventCenter,
|
|
|
|
|
+ 'node:mouseenter',
|
|
|
|
|
+ (event) => {
|
|
|
|
|
+ mouseenterTimer = setTimeout(() => {
|
|
|
|
|
+ popover.value = true;
|
|
|
|
|
+ const quickNodes = event.data.properties?.quickNodes ?? [];
|
|
|
|
|
+ appendNodes.value = [...quickNodes];
|
|
|
|
|
+ linkNodes.value = [];
|
|
|
|
|
+ const outgoing = graph.getNodeOutgoingNode(event.data.id).map((node) => node.id);
|
|
|
|
|
+ for (const { id } of graph.nodes) {
|
|
|
|
|
+ const index = appendNodes.value.findIndex((node) => node.id === id);
|
|
|
|
|
+ if (index !== -1) {
|
|
|
|
|
+ const node = appendNodes.value.splice(index, 1).at(0);
|
|
|
|
|
+ linkNodes.value.push({ ...node, disabled: outgoing.includes(node.id) });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ triggerRef(appendNodes);
|
|
|
|
|
+ triggerRef(linkNodes);
|
|
|
|
|
+ }, 100);
|
|
|
|
|
+ },
|
|
|
|
|
+ (event) => event.data?.id === id.value
|
|
|
|
|
+ );
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
function updateProperties(props: FlowNodeProperties) {
|
|
function updateProperties(props: FlowNodeProperties) {
|
|
|
properties.value = Object.assign(properties.value, props);
|
|
properties.value = Object.assign(properties.value, props);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+function configure(event: MouseEvent) {
|
|
|
|
|
+ const data = getNode?.().getData();
|
|
|
|
|
+ // @ts-ignore
|
|
|
|
|
+ if (data) getGraph?.()?.eventCenter?.emit('node:click', { data, e: event, position: void 0 });
|
|
|
|
|
+ popover.value = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function remove() {
|
|
|
|
|
+ const data = getNode?.().getData();
|
|
|
|
|
+ if (data) getGraph?.()?.deleteNode(data.id);
|
|
|
|
|
+ popover.value = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function link(targetNodeId: string) {
|
|
|
|
|
+ const data = getNode?.().getData();
|
|
|
|
|
+ if (data)
|
|
|
|
|
+ getGraph?.()?.eventCenter?.emit('connection:quick', {
|
|
|
|
|
+ sourceNodeId: data.id,
|
|
|
|
|
+ targetNodeId,
|
|
|
|
|
+ });
|
|
|
|
|
+ popover.value = false;
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
|
- <FlowNodeInlay class="node" :id :text v-bind="properties"></FlowNodeInlay>
|
|
|
|
|
|
|
+ <FlowNodeInlay v-if="id === ID_End" class="node" :id :text v-bind="properties"></FlowNodeInlay>
|
|
|
|
|
+ <a-popover v-else :title="text" placement="right" trigger="hover" v-model:open="popover" :mouseEnterDelay="1">
|
|
|
|
|
+ <template #content>
|
|
|
|
|
+ <div class="popover-content-wrapper">
|
|
|
|
|
+ <a-space wrap>
|
|
|
|
|
+ <a-button type="primary" v-if="properties?.configurable" @click="configure">配置数据</a-button>
|
|
|
|
|
+ <a-button danger :disabled="id === ID_End || id === ID_Start" @click.prevent="remove">移除节点</a-button>
|
|
|
|
|
+ </a-space>
|
|
|
|
|
+ <div style="margin-top: 14px" v-if="appendNodes.length">
|
|
|
|
|
+ <div class="title">添加下流节点</div>
|
|
|
|
|
+ <a-space wrap>
|
|
|
|
|
+ <a-button v-for="node in appendNodes" :key="node.id" type="dashed" @click.prevent="link(node.id)">{{ node.text }}</a-button>
|
|
|
|
|
+ </a-space>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="margin-top: 14px" v-if="linkNodes.length">
|
|
|
|
|
+ <div class="title">连接下流节点</div>
|
|
|
|
|
+ <a-space wrap>
|
|
|
|
|
+ <a-button v-for="node in linkNodes" :key="node.id" :disabled="node.disabled" type="dashed" @click.prevent="link(node.id)">{{ node.text }}</a-button>
|
|
|
|
|
+ </a-space>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <FlowNodeInlay class="node" :id :text v-bind="properties"></FlowNodeInlay>
|
|
|
|
|
+ </a-popover>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
@@ -62,4 +156,13 @@ function updateProperties(props: FlowNodeProperties) {
|
|
|
--icon-background: v-bind(properties.iconBackground);
|
|
--icon-background: v-bind(properties.iconBackground);
|
|
|
--icon-color: v-bind(properties.iconColor);
|
|
--icon-color: v-bind(properties.iconColor);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+.popover-content-wrapper {
|
|
|
|
|
+ min-width: 300px;
|
|
|
|
|
+ .title {
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ color: rgba(0, 0, 0, 0.88);
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|