LogicFlow 学习笔记——10. LogicFlow 进阶 边

我们可以基于 Vue 组件自定义边,可以在边上添加任何想要的 Vue 组件,甚至将原有的边通过样式隐藏,重新绘制。
如 Example3 中所示:
在这里插入图片描述

锚点

默认情况下,LogicFlow 只记录节点与节点的信息。但是在一些业务场景下,需要关注到锚点,比如在 UML 类图中的关联关系;或者锚点表示节点的入口和出口之类。这个时候需要重写连线的保存方法,将锚点信息也一起保存。

class CustomEdgeModel2 extends LineEdgeModel {
  // 重写此方法,使保存数据是能带上锚点数据。
  getData() {
    const data = super.getData();
    data.sourceAnchorId = this.sourceAnchorId;
    data.targetAnchorId = this.targetAnchorId;
    return data;
  }
}

动画

由于 LogicFlow 是基于 svg 的流程图编辑框架,所以我们可以给 svg 添加动画的方式来给流程图添加动画效果。为了方便使用,我们也内置了基础的动画效果。在定义边的时候,可以将属性isAnimation设置为 true 就可以让边动起来,也可以使用lf.openEdgeAnimation(edgeId)来开启边的默认动画。

class CustomEdgeModel extends PolylineEdgeModel {
  setAttributes() {
    this.isAnimation = true;
  }
  getEdgeAnimationStyle() {
    const style = super.getEdgeAnimationStyle();
    style.strokeDasharray = "5 5";
    style.animationDuration = "10s";
    return style;
  }
}

下面我们对上面的内容写一个简单的样例:
样例中使用了 JSX 所以需要进行配置,在项目中,运行pnpm install @vitejs/plugin-vue-jsx并在vite.config.js增加如下配置:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

export default defineConfig({
  plugins: [vue(), vueJsx()]
});

新建src/views/Example/LogicFlowAdvance/Edge/Example01/CustomCard.vue代码如下:

<script setup lang="tsx">
import { ref } from 'vue'

const props = defineProps({
  properties: {
    type: Object,
    required: true
  }
})

type Answer = {
  text: string
  id: string
}

type Properties = {
  title: string
  content: string
  answers: Answer[]
}

// Example props passed to the component
const properties = ref(props.properties as Properties)
</script>
<template>
  <div class="html-card">
    <!-- <ElButton οnclick="alert(123)" type="primary" style="margin-left: 15px">Title</ElButton> -->
    <div class="html-card-header">{{ properties.title }}</div>
    <div class="html-card-body">{{ properties.content }}</div>
    <div class="html-card-footer">
      <div v-for="answer in properties.answers" :key="answer.id" class="html-card-label">
        {{ answer.text }}
      </div>
    </div>
  </div>
</template>
<style scoped>
.html-card {
  width: 240px;
  height: 100%;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  border-radius: 4px;
  border: 1px solid #ebeef5;
  background-color: #fff;
  overflow: hidden;
  color: #303133;
  transition: 0.3s;
  box-sizing: border-box;
  padding: 5px;
}
/* 定义节点不被允许连接的时候,节点样式 */
.lf-node-not-allow .html-card {
  border-color: #f56c6c;
}
.lf-node-allow .html-card {
  border-color: #67c23a;
}
.html-card-header {
  font-size: 12px;
  line-height: 24px;
  margin-left: 14px;
}
.html-card-header:before {
  content: '';
  position: absolute;
  left: 5px;
  top: 13px;
  display: block;
  width: 7px;
  height: 7px;
  border: 1px solid #cbcef5;
  border-radius: 6px;
}
.html-card-body {
  font-size: 12px;
  color: #6f6a6f;
  margin-top: 5px;
}

.html-card-footer {
  display: flex;
  position: absolute;
  bottom: 5px;
}
.html-card-label {
  font-size: 12px;
  line-height: 16px;
  padding: 2px;
  background: #ebeef5;
  margin-right: 10px;
}
</style>

新建src/views/Example/LogicFlowAdvance/Edge/Example01/CustomCard.tsx代码如下:

import { HtmlNode, HtmlNodeModel } from '@logicflow/core'
import { createApp, h, App, VNode, render } from 'vue'
import CustomCard from './CustomCard.vue'

class HtmlCard extends HtmlNode {
  isMounted: boolean
  app: App<Element>
  r: VNode

  constructor(props: any) {
    super(props)
    this.isMounted = false
    this.r = h(CustomCard, {
      properties: props.model.getProperties(),
      text: props.model.inputData
    })
    this.app = createApp({
      render: () => this.r
    })
  }

  // 重写HtmlNode的setHtml,来控制html节点内容。
  setHtml(rootEl: HTMLElement) {
    if(!this.isMounted) {
        this.isMounted = true
        const node = this.getCardEl()
        render(node, rootEl)
    } else {
      if (this.r.component) {
        this.r.component.props.properties = this.props.model.getProperties();
      }
    }
  }
  getCardEl() {
    const { properties } = this.props.model
    return <><CustomCard properties={properties} /></>
  }
}
class HtmlCardModel extends HtmlNodeModel {
  initNodeData(data: any) {
    super.initNodeData(data)
    // 禁止节点文本可以编辑
    this.text.editable = false
    this.width = 240
    // 定义连接规则,只允许出口节点连接入口节点
    const rule = {
      message: '只允许出口节点连接入口节点',
      validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
        console.log(sourceNode, targetNode)
        console.log(sourceAnchor, targetAnchor)
        return sourceAnchor.type === 'sourceAnchor' && targetAnchor.type === 'targetAnchor'
      }
    }
    this.sourceRules.push(rule)
  }
  setAttributes() {
    const {
      properties: { content }
    } = this
    // 动态计算节点的高度
    const rowSize = Math.ceil(content.length / 20)
    this.height = 60 + rowSize * 18
  }
  /**
   * 计算每个锚点的位置
   */
  getDefaultAnchor() {
    const { height, x, y, id, properties } = this
    const anchorPositon = []
    anchorPositon.push({
      x,
      y: y - height / 2,
      type: 'targetAnchor',
      id: `${id}_targetAnchor`
    })
    if (properties.answers) {
      let preOffset = 5
      properties.answers.forEach((answer: any) => {
        const text = answer.text
        // 计算每个锚点的位置,锚点的位置一般相对节点中心点进行偏移
        const offsetX = preOffset + (this.getBytesLength(text) * 6 + 4) / 2 - this.width / 2
        preOffset += this.getBytesLength(text) * 6 + 4 + 10
        const offsetY = height / 2
        anchorPositon.push({
          x: x + offsetX,
          y: y + offsetY,
          type: 'sourceAnchor',
          id: answer.id
        })
      })
    }
    return anchorPositon
  }
  getBytesLength(word: any) {
    if (!word) {
      return 0
    }
    let totalLength = 0
    for (let i = 0; i < word.length; i++) {
      const c = word.charCodeAt(i)
      if (word.match(/[A-Z]/)) {
        totalLength += 1.5
      } else if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
        totalLength += 1.2
      } else {
        totalLength += 2
      }
    }
    return totalLength
  }
}

export default {
  type: 'html-card',
  view: HtmlCard,
  model: HtmlCardModel
}

新建src/views/Example/LogicFlowAdvance/Edge/Example01/CustomEdge.tsx代码如下:

import { BezierEdge, BezierEdgeModel } from '@logicflow/core'

class CustomEdge extends BezierEdge {}

class CustomEdgeModel extends BezierEdgeModel {
  getEdgeStyle() {
    const style = super.getEdgeStyle()
    // svg属性
    style.strokeWidth = 1
    style.stroke = '#ababac'
    return style
  }
  /**
   * 重写此方法,使保存数据是能带上锚点数据。
   */
  getData() {
    const data: any = super.getData()
    data.sourceAnchorId = this.sourceAnchorId
    data.targetAnchorId = this.targetAnchorId
    return data
  }

  setAttributes() {
    this.isAnimation = true;
  }
}

export default {
  type: 'custom-edge',
  view: CustomEdge,
  model: CustomEdgeModel
}

新建src/views/Example/LogicFlowAdvance/Edge/Example01/data.ts,内容如下:

const data = {
  nodes: [
    {
      id: 'node_id_1',
      type: 'html-card',
      x: 340,
      y: 100,
      properties: {
        title: '普通话术',
        content: '喂,您好,这里是XX装饰,专业的装修品牌。请问您最近有装修吗?',
        answers: [
          { id: '1', text: '装好了' },
          { id: '2', text: '肯定' },
          { id: '3', text: '拒绝' },
          { id: '4', text: '否定' },
          { id: '5', text: '默认' }
        ]
      }
    },
    {
      id: 'node_id_2',
      type: 'html-card',
      x: 160,
      y: 300,
      properties: {
        title: '推荐话术',
        content:
          '先生\\女士,您好!几年来,我们通过对各种性质的建筑空间进行设计和施工,使我们积累了丰富的管理、设计和施工经验,公司本着以绿色环保为主题,对家居住宅、办公、商铺等不同特点的室内装饰产品形成了独特的装饰理念。',
        answers: [
          { id: '1', text: '感兴趣' },
          { id: '2', text: '不感兴趣' },
          { id: '3', text: '拒绝' }
        ]
      }
    },
    {
      id: 'node_id_3',
      type: 'html-card',
      x: 480,
      y: 260,
      properties: { title: '结束话术', content: '抱歉!打扰您了!', answers: [] }
    },
    {
      id: 'node_id_4',
      type: 'html-card',
      x: 180,
      y: 500,
      properties: {
        title: '结束话术',
        content: '好的,我们将安排师傅与您联系!',
        answers: []
      }
    }
  ],
  edges: [
    {
      id: 'e54d545f-3381-4769-90ef-0ee469c43e9c',
      type: 'custom-edge',
      sourceNodeId: 'node_id_1',
      targetNodeId: 'node_id_2',
      startPoint: { x: 289, y: 148 },
      endPoint: { x: 160, y: 216 },
      properties: {},
      pointsList: [
        { x: 289, y: 148 },
        { x: 289, y: 248 },
        { x: 160, y: 116 },
        { x: 160, y: 216 }
      ],
      sourceAnchorId: '2',
      targetAnchorId: 'node_id_2_targetAnchor'
    },
    {
      id: 'ea4eb652-d5de-4a85-aae5-c38ecc013fe6',
      type: 'custom-edge',
      sourceNodeId: 'node_id_2',
      targetNodeId: 'node_id_4',
      startPoint: { x: 65, y: 384 },
      endPoint: { x: 180, y: 461 },
      properties: {},
      pointsList: [
        { x: 65, y: 384 },
        { x: 65, y: 484 },
        { x: 180, y: 361 },
        { x: 180, y: 461 }
      ],
      sourceAnchorId: '1',
      targetAnchorId: 'node_id_4_targetAnchor'
    },
    {
      id: 'da216c9e-6afe-4472-baca-67d98abb1d31',
      type: 'custom-edge',
      sourceNodeId: 'node_id_1',
      targetNodeId: 'node_id_3',
      startPoint: { x: 365, y: 148 },
      endPoint: { x: 480, y: 221 },
      properties: {},
      pointsList: [
        { x: 365, y: 148 },
        { x: 365, y: 248 },
        { x: 480, y: 121 },
        { x: 480, y: 221 }
      ],
      sourceAnchorId: '4',
      targetAnchorId: 'node_id_3_targetAnchor'
    },
    {
      id: '47e8aff3-1124-403b-8c64-78d94ec03298',
      type: 'custom-edge',
      sourceNodeId: 'node_id_1',
      targetNodeId: 'node_id_3',
      startPoint: { x: 327, y: 148 },
      endPoint: { x: 480, y: 221 },
      properties: {},
      pointsList: [
        { x: 327, y: 148 },
        { x: 327, y: 248 },
        { x: 476, y: 161 },
        { x: 480, y: 221 }
      ],
      sourceAnchorId: '3',
      targetAnchorId: 'node_id_3_targetAnchor'
    }
  ]
}

export default data

最后新建src/views/Example/LogicFlowAdvance/Edge/Example01/Example01.vue内容如下:

<script setup lang="ts">
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/style/index.css'
import { onMounted } from 'vue'
import data from './data'
import CustomCard from './CustomCard'
import CustomEdge from './CustomEdge'
import CustomEdge2 from './CustomEdge2'

// 在组件挂载时执行
onMounted(() => {
  // 创建 LogicFlow 实例
  const lf = new LogicFlow({
    container: document.getElementById('container')!, // 指定容器元素
    grid: true // 启用网格
  })
  lf.register(CustomCard)
  lf.register(CustomEdge)
  lf.register(CustomEdge2)
  lf.setDefaultEdgeType('custom-edge')
  lf.render(data)
})
</script>

<template>
  <h3>Example01</h3>
  <div id="container"></div>
  <!-- 用于显示 LogicFlow 图表的容器 -->
</template>

<style>
#container {
  /* 容器宽度 */
  width: 100%;
  /* 容器高度 */
  height: 600px;
}
</style>

样例运行如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/716382.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Transformer模型探索:Hugging Face库实战篇二——模型与分词器解析

注&#xff1a;本系列教程仅供学习使用, 由原作者授权, 均转载自小昇的 博客 。 文章目录 前言模型 加载模型 保存模型 分词器 分词策略 加载与保存分词器编码与解码文本 处理多段文本 Padding 操作 Attention masks直接使用分词器编码句子对 前言 在上一篇文章 《开箱即…

QT(超详细从0开始)

目录 1.2 Qt的优点 2.安装Qt 3.创建项目 4.解读Qt自动生成的代码 ​编辑 5.Qt Designer 6.Qt对象数 7.Qt乱码问题 8.Qt坐标系的认识 9.信号和槽 9.1 connect 9.2 自定义槽函数 9.3 自定义信号 9.4 断开信号链接&#xff08;disconnect&#xff09; 9.5.lambda表…

【尚庭公寓SpringBoot + Vue 项目实战】租约管理(十四)

【尚庭公寓SpringBoot Vue 项目实战】租约管理&#xff08;十四&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】租约管理&#xff08;十四&#xff09;1、业务介绍2、逻辑介绍3、接口开发3.1、保存或更新租约信息3.2、根据条件分页查询租约列表3.3、根据ID查询租…

STM32的通用定时器中断编程

如果遇到需要单片机产生严格时序的场景&#xff08;比如DAC输出特定模拟信号&#xff0c;GPIO口控制模拟开关&#xff09;&#xff0c;延时函数可能就无法胜任了。最近在工作时公司上级教会了我使用“令牌”思维&#xff08;中断标志位)编写单片机裸机程序&#xff0c;今天写一…

c++初始化列表(特点),隐式类型转换(示例,explicit关键字)

目录 初始化列表 定义 特点 必须使用初始化列表的成员变量 初始化顺序 隐式类型转换 示例 explicit关键字 初始化列表 Date::Date(const Date& d) {_year d._year;_month d._month;_day d._day; }Date::Date(const Date& d) :_year(d._year),_month(d._mon…

66aix AI生成系统-中文版安装

66aix是一款多功能的AI助手工具&#xff0c;可以帮助您生成独特的内容&#xff0c;美化和修改您的文章内容或&#xff0c;以及生成图像&#xff0c;去除图像背景。同时&#xff0c;它还包括完整功能的语音转换文本系统。 系统要求 PHP PHP 8 Extensions cURL, OpenSSL, mbstrin…

简易版 | 代码生成器(包含插件)

一、代码生成器 先导入依赖 <!-- Mybatis-Plus --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.6</version> </dependency><!-- 代码生成器 --…

css之浮动float

float 设计初衷 仅仅是为了实现文字环绕 图文混排效果 特性 包裹 收缩 坚挺 隔绝 也就是BFC(Block Formating content) - “块级格式化上下文” 破坏 高度塌陷&#xff08;浮动使高度塌陷不是bug &#xff0c;而是标准&#xff0c;特性使然&#xff09; 清除浮动 clear 作…

MySQL----事务的隔离级别(附带每一级别实例截图)

先来回顾一下事务并发可能存在的三大问题&#xff1a; 脏读&#xff08;Dirty Read&#xff09;–不能接受 一个事务读取了另一个事务未提交的数据。例如当事务A和事务B并发执行时&#xff0c;当事务A更新后&#xff0c;事务B查询读取到A尚未提交的数据&#xff0c;此时事务A…

矩阵乘法的直觉

矩阵乘法是什么意思&#xff1f; 一种常见的观点是矩阵乘法缩放/旋转/倾斜几何平面&#xff1a; NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜…

Django REST framework序列化器详解:普通序列化器与模型序列化器的选择与运用

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff…

实用技巧:跳过TCODE权限检查ALINK_CALL_TRANSACTION

RFC&#xff1a;ALINK_CALL_TRANSACTION 遇到tcode 提示没有权限打开&#xff0c;可以通过这个RFC,debug 修改检查值&#xff0c;打开TCODE。 适用于紧急情况 断点打在20行&#xff0c;SY-SUBRC 的值改成 1

碳化硅陶瓷膜出色的耐腐蚀性能

在科技日新月异的今天&#xff0c;材料科学的发展为各个领域带来了革命性的变革。碳化硅陶瓷膜&#xff0c;作为一种高性能的先进陶瓷材料&#xff0c;凭借其独特的物理和化学特性&#xff0c;正在逐步成为现代工业不可或缺的一部分。 碳化硅陶瓷膜&#xff0c;顾名思义&#x…

TensorRT的循环样例代码

官方文档地址 https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#define-loops 非顺序结构,其内容确实有点乱,而且没有完整可运行的样例。 可以有多个IIteratorLayer, IRecurrenceLayer, and ILoopOutputLayer 层,最多有2个ITripLimitLayers层。 …

有人说考个PMP证两个星期搞定?

PMP考试的时间并不需要太久&#xff0c;如果高效用心备考的话在对考试需要准备的时间上也只需要2-3个月的业余时间。而一次考试的时间也只需要半天&#xff0c;一门科目&#xff0c;就是《PMBOK》的知识。所以如果想学习项目管理考PMP认证的朋友&#xff0c;大可放心参加考试。…

【递归、搜索与回溯】综合练习三

综合练习三 1.优美的排列3.N 皇后3.有效的数独4.解数独 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.优美的排列 题目链接&#xff1a;5…

用寄存器读取文件的数据的时候,寄存器怎么读取,寄存器的高位和低位分别是什么

如图所示 寄存器读取数据的时候&#xff0c;数据自身是什么样的&#xff0c;寄存器读的时候就原样存储在寄存器里&#xff0c;高位就是第一个数据&#xff0c;低位就是最后一个数据 寄存器读取数据原理是&#xff0c;将给定的二进制数反转&#xff0c;我理解成调转一下车头&…

驾驭未来:智能网关如何革新车联网体验

车联网&#xff08;Internet of Vehicles&#xff09;是一个跨领域的技术综合体&#xff0c;它基于物联网&#xff0c;利用先进的信息通信技术实现车与车、车与路、车与人、车与服务平台等的全方位网络连接。 龙兴物联智能网关是集成了多协议、多接口&#xff0c;具有综合数据采…

Three.js动效(第15辑):让前端手撕UI,拳打后端的效果。

three.js的设计效果非常复杂&#xff0c;后端提供的数据接口问题百出&#xff0c;这很容易让前端手撕UI、拳打后端&#xff0c;这种请详细该如何办呢&#xff1f; 前端 VS UI&#xff1a; 1. 沟通协调&#xff1a;UI和前端应该加强沟通&#xff0c;理解对方的工作难点和需求&…

「GitHub热点速览」7个学编程必看的开源项目!附链接可直达!

前言 今天特推的两个项目都是异常实用的项目&#xff0c;一个是直接将视频替换成另外一个语种&#xff1b;另外一个则是解决日志阅读问题的 tailspin&#xff0c;让你在成千上万条日志中快速定位特定的日志。 另外&#xff0c;还有两大集成者&#xff0c;一个是解决可观测性的…