Bootstrap

vue3使用AntV X6 (图可视化引擎)历程[三]

点击自定义节点中的按钮去更新视图,主要采用callback 回调方式调用

一、案例效果

未点击【上面】按钮之前
在这里插入图片描述
点击之后
在这里插入图片描述

二、案例代码

  • 自定义节点内容 NodeHtml.vue
<template>
  <div class="status-node">
    <div v-if="nodeInfo?.isUpstreamExpandable" class="text-[#f00]" @click="upstreamHandleFn">上面</div>
    <div class="content" @click="handleClick">{{ nodeInfo?.label }}</div>
    <div v-if="nodeInfo?.isDownstreamExpandable" class="text-[#00f]">下面</div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from 'vue';

const props = defineProps({
  nodeElementItem: {
    type: Object,
    required: true,
  },
  upstreamHandle: {
    type: Function,
    required: true,
  },
});

const nodeInfo = ref();

const handleClick = () => {
  console.log('---handleClick');
};

const initNodeData = () => {
  nodeInfo.value = props.nodeElementItem.store.data;
  console.log(' nodeInfo.value ', nodeInfo.value);
};

const upstreamHandleFn = () => {
  props.upstreamHandle();
};
onMounted(() => {
  initNodeData();
});
</script>
<style lang="less" scoped>
.status-node {
  height: 50px;
  width: 200px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #8f8f8fa1;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.1);
  z-index: 999;
}

.content {
  color: #000;
  font-size: 14px;
}
</style>

  • 传入props.upstreamHandle 方法 attrConfig.ts
import { streamData } from '@/common/components/topologyToolKit/data';
import NodeElement from '@/views/featureManage/featureList/topologyToolKit/NodeHtml.vue';
import { h } from 'vue';
import { formatNodeData } from './topology';

const upstreamHandle = (originalNodeData: any, callback: (data: any) => void) => {
  console.log('Upstream handle triggered', originalNodeData);
  const streamFormatData = formatNodeData(streamData);
  originalNodeData.nodes = [...originalNodeData.nodes, ...streamFormatData.nodes];
  originalNodeData.edges = [...originalNodeData.edges, ...streamFormatData.edges];
  // 调用回调函数,传递更新后的 originalNodeData
  callback(originalNodeData);
};

/**
 * 自定义注册节点配置
 */
export const registerOption = (originalNodeData: any, callback: (data: any) => void) => ({
  shape: 'vue-shape',
  width: 100,
  height: 100,
  component: ({ node }: { node: any }) => {
    console.log('1666-originalNodeData', originalNodeData);

    // 将 node 数据传递给 NodeElement 组件
    return h(NodeElement, { nodeElementItem: node, upstreamHandle: () => upstreamHandle(originalNodeData, callback) });
  },
});

  • 触发upstreamHandle 回调callback -TopologyDependent.vue
<template>
  <div :id="domId" class="w-full h-[95%]"></div>
</template>
<script setup lang="ts">
import { registerOption } from '@/views/featureManage/featureList/topologyToolKit/attrConfig';
import { DagreLayout, DagreLayoutOptions } from '@antv/layout';
import { Graph } from '@antv/x6';
import { register } from '@antv/x6-vue-shape';
import { computed, nextTick, onMounted, ref, toRaw } from 'vue';
import { initDependOption } from './initDependConfig';

const props = defineProps({
  domId: {
    type: String,
    default: '',
  },
  nodeData: {
    type: Object,
  },
  targetIdData: {
    type: Array as () => number[],
    required: false,
  },
});

const container = ref<any>();

const nodeUpdateData = computed(() => {
  return props.nodeData;
});

const initGraph = (data: any) => {
  console.log('-8888---data', data);

  container.value = document.getElementById(props.domId);
  const width = container.value?.scrollWidth;
  const height = container.value?.scrollHeight || 800;

  const graph = new Graph({
    container: container.value,
    width, // 设置图形的宽度
    height, // 设置图形的高度
    ...initDependOption.option,
  });

  const dagreLayout = new DagreLayout({ ...(initDependOption.layout as DagreLayoutOptions) });
  const model = dagreLayout.layout(data);

  console.log('111==data', data, model);

  graph.fromJSON(model);

  graph.on('node:click', ({ e, x, y, node, view }) => {
    console.log('---115-e, x, y, node, view', e, x, y, node, view);
  });
};

const handleUpdateNodeData = (updatedNodeData: any) => {
  const rawNodeData = toRaw(updatedNodeData); // 获取原始对象
  initGraph(rawNodeData);

  console.log('333333======Updated originalNodeData:', updatedNodeData);
};

onMounted(() => {
  nextTick(() => {
    if (props.domId && document.getElementById(props.domId)) {
      const rawNodeData = toRaw(nodeUpdateData.value); // 获取原始对象
      initGraph(rawNodeData);
      register({ ...registerOption(rawNodeData, handleUpdateNodeData) });
    }
  });
});
</script>

  • 父组件 BloodTopology.vue
<template>
  <div>
    <TopologyCompact>
      <template #main-board-box>
        <TopologyDependent domId="featureBloodContainer" :nodeData="originalNodeData" />
      </template>
      <template #right-drawer-box>
        <RightDrawer :width="350">
          <template #rightContent> rightContent </template>
        </RightDrawer>
      </template>
    </TopologyCompact>
  </div>
</template>

<script lang="ts" setup>
import { bloodData } from '@/common/components/topologyToolKit/data';
import RightDrawer from '@/common/components/topologyToolKit/RightDrawer.vue';
import TopologyCompact from '@/common/components/topologyToolKit/TopologyCompact.vue';
import TopologyDependent from '@/common/components/topologyToolKit/TopologyDependent.vue';
import { onMounted, ref } from 'vue';
import { formatNodeData } from '../topologyToolKit/topology';

const originalNodeData = ref({
  nodes: [],
  edges: [],
});

const initNodeData = () => {
  originalNodeData.value = formatNodeData(bloodData);
};
onMounted(() => {
  initNodeData();
});
</script>

  • mock 数据 @/common/components/topologyToolKit/data
export const bloodData = {
  nodes: [
    {
      featureId: '12345',
      featureNameCn: '订单金额总和',
      featureNameEn: 'orderAmountSum',
      isUpstreamExpandable: true,
      isDownstreamExpandable: true,
      upstreamFeatureIds: ['55555'],
      downstreamFeatureIds: ['67890', '54321'],
    },
    {
      featureId: '67890',
      featureNameCn: '单品订单金额',
      featureNameEn: 'singleItemOrderAmount',
      isUpstreamExpandable: false,
      isDownstreamExpandable: true,
      upstreamFeatureIds: ['12345'],
      downstreamFeatureIds: ['23456'],
    },
    {
      featureId: '54321',
      featureNameCn: '订单数量',
      featureNameEn: 'orderQuantity',
      isUpstreamExpandable: false,
      isDownstreamExpandable: false,
      upstreamFeatureIds: ['12345'],
      downstreamFeatureIds: [],
    },
  ],
  edges: [
    {
      source: '12345',
      target: '67890',
    },
    {
      source: '12345',
      target: '54321',
    },
  ],
};

export const streamData = {
  nodes: [
    {
      featureId: '23456',
      featureNameCn: '平均订单金额',
      featureNameEn: 'averageOrderAmount',
      isUpstreamExpandable: true,
      isDownstreamExpandable: true,
      upstreamFeatureIds: ['67890'],
      downstreamFeatureIds: ['33333'],
    },
    {
      featureId: '34567',
      featureNameCn: '大额订单数量',
      featureNameEn: 'largeOrderQuantity',
      isUpstreamExpandable: true,
      isDownstreamExpandable: false,
      upstreamFeatureIds: ['67890'],
      downstreamFeatureIds: [],
    },
  ],
  edges: [
    {
      source: '67890',
      target: '23456',
    },
    {
      source: '67890',
      target: '34567',
    },
  ],
};
;