目录

  • 引言
  • 目的
  • 适用场景
  • 环境准备
  • 基础组件 (index.vue)
  • 自定义组件 (矩形、菱形等)
    • RectangleNode.vue (矩形节点):
    • DiamondNode.vue (菱形节点):
    • ImageNode(自定义图片节点):
  • 操作实现 (#操作实现)
    •      拖拽节点 (#拖拽节点)
    •      连线 (多连接点)
    •      删除节点
    •      保存为 JSON
    •      导入 JSON
  • 性能优化建议
  • 常见问题与解决
  • 总结
  • `demo见:`

引言

        Vue Flow 是一个基于 Vue 的流程图库,结合 Vue.js 的组件化优势,可用于创建交互式、可视化的流程图。本文将逐步引导你整合 Vue Flow,涵盖从环境配置到自定义节点、拖拽、连线、删除、JSON 管理和优化,适合初学者和高级开发者。

目的

 本文旨在提供一个全面的指南,帮助开发者:

  • 理解 Vue Flow 的核心功能和集成流程。
  • 实现交互式流程图,包括拖拽、连线和节点管理。
  • 掌握数据持久化(保存和导入 JSON),提升项目实用性。
  • 优化性能并解决常见问题,确保生产环境稳定。

适用场景

  • 工作流管理:设计审批或任务流程。
  • 数据可视化:展示组织结构或网络拓扑。
  • 工业自动化:模拟设备连接和生产流程。
  • 教育工具:可视化算法或逻辑步骤。
  • 说明插图:绘制一个工业流程图,包含 “设备1”, “工序1”, “设备2” 节点和连接线。

环境准备

  • 项目初始化:使用 Vue 3 + Vite 创建项目。

  • 依赖安装:

npm install @vue-flow/core @vue-flow/background @vue-flow/controls @vue-flow/minimap ant-design-vue

@vue-flow/core:核心库。
@vue-flow/background:背景网格。
@vue-flow/controls:交互控制。
@vue-flow/minimap:画布概览。
ant-design-vue:用于按钮样式。
在这里插入图片描述

基础组件 (index.vue)

创建 src/components/FlowChart.vue 作为流程图容器。

<template><div class="flow-container"><div class="layout"><!-- 左侧可选项区域 --><div class="sidebar"><h3>可拖拽节点</h3><divv-for="option in nodeOptions":key="option.type"class="draggable-node":data-type="option.type"draggable="true"@dragstart="onDragStart"><div class="node-label">{{ option.label }}</div><div class="node-preview-wrapper"><div :class="['inner-shape', option.shapeClass]"><div class="label">{{ option.label }}</div></div></div></div></div><!-- 右侧 Vue Flow 画布 --><div class="flow-area"><div class="toolbar"><a-button type="primary" @click="addRandomNode">添加随机节点</a-button>
<!--          <a-button type="danger" @click="deleteSelected">删除选中节点</a-button>--><a-button type="default" @click="exportJson">导出 JSON</a-button><a-button type="default" @click="saveExportJson">保存流程图</a-button></div><VueFlowv-model:nodes="nodes"v-model:edges="edges":node-types="nodeTypes":default-edge-options="{type: 'smoothstep',animated: true,markerEnd: { type: 'arrowclosed', color: '#ff0000' },style: { stroke: '#ff0000', strokeWidth: 2, strokeDasharray: 'none' },}":connection-line-style="{ stroke: '#ff0000', strokeWidth: 2, strokeDasharray: 'none' }":fit-view-on-init="true":connectable="true"@drop="onDrop"@dragover.prevent@connect="onConnect"@node-click="onNodeClick"@node-drag-start="onNodeDragStart"@node-drag-stop="onNodeDragStop"@pane-click="onPaneClick"><Background variant="dots" :gap="20" /><Controls /><MiniMap /></VueFlow></div></div></div>
</template><script setup lang="ts">
import {ref, markRaw, onMounted} from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import type { NodeTypesObject } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'
import { message } from 'ant-design-vue'
import DiamondNode from './DiamondNode.vue'
import CircleNode from './CircleNode.vue'
import ImageNode from './ImageNode.vue'
import DeviceNode from './DeviceNode.vue'
import UserNode from './UserNode.vue'
import RoundedRectangleNode from './RoundedRectangleNode.vue'
import { editingNodeId } from './store'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import '../style/node-styles.css'const nodes = ref([])
const edges = ref<any>([])const nodeTypes: NodeTypesObject = {diamond: markRaw(DiamondNode),circle: markRaw(CircleNode),image: markRaw(ImageNode),user: markRaw(UserNode),device: markRaw(DeviceNode),roundedRectangle: markRaw(RoundedRectangleNode),
}const { addNodes, getNodes, deleteElements,removeNodes } = useVueFlow()const nodeOptions = ref([{ type: 'circle', label: '开始节点', shapeClass: 'node-circle' },{ type: 'circle', label: '结束节点', shapeClass: 'node-circle' },// { type: 'image', label: '图片节点', shapeClass: 'node-image' }, // 新增图片节点// { type: 'device', label: '设备节点', shapeClass: 'node-device' }, // 新增设备节点// { type: 'user', label: '人员节点', shapeClass: 'node-user' }, // 新增人员节点// { type: 'roundedRectangle', label: '圆角长方形节点', shapeClass: 'node-rounded-rectangle' },])function initData(){return [{ type: 'device', label: '设备节点', shapeClass: 'node-device',id:1,code:'123' },{ type: 'device', label: '工序1', shapeClass: 'node-device',id:1,code:'123' },{ type: 'device', label: '工序2', shapeClass: 'node-device',id:1,code:'123' },{ type: 'device', label: '工序3', shapeClass: 'node-device',id:1,code:'123' },]
}
function getData(){nodes.value = [{"":""}];edges.value = [{"":""}];onMounted(() => {let data  =initData();nodeOptions.value = [...nodeOptions.value, ...data];getData();
})
// Drag-and-drop handlers
function onDragStart(event: DragEvent) {if (event.dataTransfer && event.target instanceof HTMLElement) {const type = event.target.dataset.typeif (type) {event.dataTransfer.setData('application/vueflow-node-type', type)}}
}function onDrop(event: DragEvent) {const type = event.dataTransfer?.getData('application/vueflow-node-type')if (!type) returnconst bounds = (event.currentTarget as HTMLElement).getBoundingClientRect()const position = {x: event.clientX,y: event.clientY,}const label = type === 'diamond' ? '判断节点' : type === 'circle' ? '开始节点' : '圆角长方形节点'addNodes([{id: `${type}-${Date.now()}`,type,position,data: { label },},])
}// Connection handler
function onConnect(params: any) {edges.value = [...edges.value,{...params,id: `edge-${params.source}-${params.target}`,type: 'smoothstep',animated: true,markerEnd: { type: 'arrowclosed', color: '#ff0000' },style: { stroke: '#ff0000', strokeWidth: 2, strokeDasharray: 'none' },},]
}// Node interaction handlers
function onNodeClick(event: any, node: any) {nodes.value = nodes.value.map(n => ({...n,selected: n.id === node.id ? true : false,}))// if (node.type === 'circle') {//   editingNodeId.value = node.id // 仅对圆形节点启用编辑模式// }
}function onNodeDragStart(event: any, node: any) {console.log('Node drag started:', node)
}function onNodeDragStop(event: any, node: any) {console.log('Node drag stopped:', node)
}function onPaneClick() {nodes.value = nodes.value.map(n => ({ ...n, selected: false }))editingNodeId.value = null // 退出编辑模式
}// Add a random node
function addRandomNode() {const types = ['diamond', 'circle', 'roundedRectangle','image','user','device']const type = types[Math.floor(Math.random() * types.length)]const label = type === 'diamond' ? '判断节点' : type === 'circle' ? '圆形节点' : '圆角长方形节点'addNodes([{id: `${type}-${Date.now()}`,type,position: { x: Math.random() * 500, y: Math.random() * 500 },data: { label },},])
}// Delete selected nodes
function deleteSelected() {const selectedIds = getNodes.value.filter(n => n.selected).map(n => n.id)if (selectedIds.length === 0) {message.warning('请先选中一个节点')return}removeNodes({ selectedIds })message.success('节点删除成功')editingNodeId.value = null // 退出编辑模式
}function saveExportJson(){//todo 保存到数据库
}
// Export JSON
function exportJson() {const json = JSON.stringify({ nodes: nodes.value ,edges: edges.value }, null, 2)const blob = new Blob([json], { type: 'application/json' })const url = window.URL.createObjectURL(blob)const a = document.createElement('a')a.href = urla.download = 'flowchart.json'a.click()window.URL.revokeObjectURL(url)message.success('JSON 文件已导出')
}// 更新节点标签
function updateNodeLabel(nodeId: string, newLabel: string) {nodes.value = nodes.value.map(n =>n.id === nodeId ? { ...n, data: { ...n.data, label: newLabel } } : n)editingNodeId.value = null // 编辑完成后退出编辑模式
}
</script>

       说明:基础组件包含侧边栏(拖拽源)、工具栏(操作按钮)和 VueFlow 画布,绑定了节点和边数据。

自定义组件 (矩形、菱形等)

定义不同形状的节点,添加多连接点。

RectangleNode.vue (矩形节点):

<template><div class="node rectangle"><Handle type="target" position="top" id="top-target" /><Handle type="source" position="top" id="top-source" /><Handle type="target" position="bottom" id="bottom-target" /><Handle type="source" position="bottom" id="bottom-source" /><Handle type="target" position="left" id="left-target" /><Handle type="source" position="left" id="left-source" /><Handle type="target" position="right" id="right-target" /><Handle type="source" position="right" id="right-source" /><div class="label">{{ data.label }}</div></div>
</template><script setup>
import { Handle } from '@vue-flow/core'
defineProps({ data: Object })
</script><style scoped>
.node {width: 100px;height: 60px;border: 1px solid #333;display: flex;align-items: center;justify-content: center;
}
.label { text-align: center; }
</style>

DiamondNode.vue (菱形节点):

<template><div class="diamond-node"><!-- 顶部连接点:source 和 target --><Handle type="target" position="top" class="handle" id="top-target" /><Handle type="source" position="top" class="handle" id="top-source" /><!-- 左侧连接点:source 和 target --><Handle type="target" position="left" class="handle" id="left-target" /><Handle type="source" position="left" class="handle" id="left-source" /><!-- 右侧连接点:source 和 target --><Handle type="target" position="right" class="handle" id="right-target" /><Handle type="source" position="right" class="handle" id="right-source" /><!-- 底部连接点:source 和 target --><Handle type="target" position="bottom" class="handle" id="bottom-target" /><Handle type="source" position="bottom" class="handle" id="bottom-source" /><!-- 节点本体 --><div class="diamond"><div class="label">{{ data.label }}</div></div></div>
</template><script setup>
import { Handle } from '@vue-flow/core'defineProps({data: Object,
})
</script><style scoped>
.diamond-node {position: relative;width: 80px;height: 80px;overflow: visible;
}.diamond {width: 100%;height: 100%;background: #2ec4b6;transform: rotate(45deg);display: flex;align-items: center;justify-content: center;border: 2px solid #333;color: white;font-weight: bold;box-sizing: border-box;z-index: 1;
}.label {transform: rotate(-45deg);text-align: center;pointer-events: none;font-size: 12px;padding: 4px;max-width: 90%;word-break: break-all;
}.handle {width: 10px;height: 10px;background: #ff0000;border-radius: 50%;position: absolute;z-index: 2;
}:deep(.vue-flow__handle-top) {top: -10px; /* 增加偏移量,使点移到顶部外部 */left: 50%;transform: translateX(-50%);
}:deep(.vue-flow__handle-bottom) {bottom: -10px; /* 增加偏移量,使点移到底部外部 */left: 50%;transform: translateX(-50%);
}:deep(.vue-flow__handle-left) {left: -10px; /* 增加偏移量,使点移到左侧外部 */top: 50%;transform: translateY(-50%);
}:deep(.vue-flow__handle-right) {right: -10px; /* 增加偏移量,使点移到右侧外部 */top: 50%;transform: translateY(-50%);
}
</style>

说明:每个节点包含上下左右的 target 和 source 连接点,允许多向连接。

ImageNode(自定义图片节点):

<template><div class="image-node"><!-- 顶部连接点:source 和 target --><Handle type="target" position="top" class="handle" id="top-target" /><Handle type="source" position="top" class="handle" id="top-source" /><!-- 左侧连接点:source 和 target --><Handle type="target" position="left" class="handle" id="left-target" /><Handle type="source" position="left" class="handle" id="left-source" /><!-- 右侧连接点:source 和 target --><Handle type="target" position="right" class="handle" id="right-target" /><Handle type="source" position="right" class="handle" id="right-source" /><!-- 底部连接点:source 和 target --><Handle type="target" position="bottom" class="handle" id="bottom-target" /><Handle type="source" position="bottom" class="handle" id="bottom-source" /><!-- 节点本体 --><div class="image-container"><img :src="imageSrc" alt="Image Node" class="node-image" /></div></div>
</template><script setup>
import { Handle } from '@vue-flow/core'
import { computed } from 'vue'defineProps({data: Object,
})const imageSrc = computed(() => {return new URL('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODBweCIgaGVpZ2h0PSI4MHB4IiB2aWV3Qm94PSIwIDAgODAgODAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cCAyPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+CiAgICAgICAgPGNpcmNsZSBpZD0icGF0aC0xIiBjeD0iMzYiIGN5PSIzNiIgcj0iMzYiPjwvY2lyY2xlPgogICAgICAgIDxmaWx0ZXIgeD0iLTkuNyUiIHk9Ii02LjklIiB3aWR0aD0iMTE5LjQlIiBoZWlnaHQ9IjExOS40JSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iZmlsdGVyLTIiPgogICAgICAgICAgICA8ZmVPZmZzZXQgZHg9IjAiIGR5PSIyIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIj48L2ZlT2Zmc2V0PgogICAgICAgICAgICA8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyIiBpbj0ic2hhZG93T2Zmc2V0T3V0ZXIxIiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVHYXVzc2lhbkJsdXI+CiAgICAgICAgICAgIDxmZUNvbXBvc2l0ZSBpbj0ic2hhZG93Qmx1ck91dGVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9Im91dCIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIj48L2ZlQ29tcG9zaXRlPgogICAgICAgICAgICA8ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAgIDAgMCAwIDAgMCAgIDAgMCAgIDAgMCAwIDAgMCAgMCAwIDAgMC4wNCAwIiB0eXBlPSJtYXRyaXgiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIj48L2ZlQ29sb3JNYXRyaXg+CiAgICAgICAgPC9maWx0ZXI+CiAgICA8L2RlZnM+CiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i5Z+656GA5rWB56iL5Zu+LTAxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTA2LjAwMDAwMCwgLTkzLjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTEwLjAwMDAwMCwgOTUuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iT3ZhbCI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGwtb3BhY2l0eT0iMC49MiIgZmlsbD0iI2NjY2M5OThjIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgc3Ryb2tlPSIjY2NjYzMzYzkiIHN0cm9rZS13aWR0aD0iMSIgY3g9IjM2IiBjeT0iMzYiIHI9IjM1LjUiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPHRleHQgaWQ9Iue7k+adn+iKgueCuSIgZm9udC1mYW1pbHk9IlBpbmdGYW5nU0MtUmVndWxhciwgUGluZ0ZhbmcgU0MiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGxpbmUtc3BhY2luZz0iMTIiIGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMC42NSI+CiAgICAgICAgICAgICAgICAgICAgPHRzcGFuIHg9IjEyIiB5PSI0MSI+57uT5p2f6IqC54K5PC90c3Bhbj4KICAgICAgICAgICAgICAgIDwvdGV4dD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+', import.meta.url).href
})
</script><style scoped>
.image-node {position: relative;width: 50px; /* 调整为适合图片的尺寸 */height: 50px; /* 调整为适合图片的尺寸 */overflow: visible;
}.image-container {width: 100%;height: 100%;border-radius: 50%;border: 2px solid #333;display: flex;align-items: center;justify-content: center;color: white;font-weight: bold;box-sizing: border-box;
}.node-image {max-width: 50px;max-height: 50px;overflow: visible;position: relative;}.handle {width: 10px;height: 10px;background: #ff0000;border-radius: 50%;position: absolute;z-index: 2;
}:deep(.vue-flow__handle-top) {top: -5px; /* 移到外部 */left: 50%;transform: translateX(-50%);
}:deep(.vue-flow__handle-bottom) {bottom: -5px; /* 移到外部 */left: 50%;transform: translateX(-50%);
}:deep(.vue-flow__handle-left) {left: -5px; /* 移到外部 */top: 50%;transform: translateY(-50%);
}:deep(.vue-flow__handle-right) {right: -5px; /* 移到外部 */top: 50%;transform: translateY(-50%);
}
</style>

在这里插入图片描述

操作实现 (#操作实现)

     拖拽节点 (#拖拽节点)

原理: 使用 draggable 和 onDrop,从侧边栏拖动节点到画布。
代码: 见 index.vue 中的 onDragStart 和 onDrop。
效果:拖动 “开始” 或 “决策” 到画布,生成对应节点,位置基于鼠标坐标。

使用 draggable 和 onDrop,从侧边栏拖动节点到画布。
代码:见 index.vue 中的 onDragStart 和 onDrop。
效果:拖动 “开始” 或 “决策” 到画布,生成对应节点,位置基于鼠标坐标。

     连线 (多连接点)

原理:@connect 捕获连接,Handle 定义多连接点,smoothstep 提供平滑线。
代码:见 index.vue 中的 onConnect。
效果:点击节点任意连接点拖动,生成红线连接,箭头指向目标。
在这里插入图片描述

     删除节点

原理:@node-click 选中,deleteSelected 调用 removeElements。
代码:见 index.vue 中的 onNodeClick 和 deleteSelected。
效果:点击节点高亮,点击删除按钮移除节点及相关边。

在这里插入图片描述

     保存为 JSON

原理:exportJson 序列化 nodes 和 edges,生成下载文件。
代码:见 index.vue 中的 exportJson。

unction exportJson() {const json = JSON.stringify({ nodes: nodes.value ,edges: edges.value }, null, 2)const blob = new Blob([json], { type: 'application/json' })const url = window.URL.createObjectURL(blob)const a = document.createElement('a')a.href = urla.download = 'flowchart.json'a.click()window.URL.revokeObjectURL(url)message.success('JSON 文件已导出')
}

效果:点击按钮下载 flowchart.json,包含当前结构。
在这里插入图片描述

     导入 JSON

原理:importJson 解析上传文件,更新 nodes 和 edges。
代码:见 index.vue 中的 importJson。
效果:上传 JSON 文件,画布还原节点和边。

性能优化建议

  • 限制节点数量:使用虚拟列表优化大规模节点渲染。
  • 防抖处理:对 onDrop 和 onConnect 添加防抖,减少频繁更新。
  • 懒加载:大图节点时,延迟加载图片资源。

常见问题与解决

  • 拖拽位置偏移:检查 bounds 计算,调整 event.clientX - bounds.left。
  • 连接点无效:确保 Handle ID 唯一,检查 connectable 属性。
  • JSON 解析错误:验证文件格式,添加错误处理:
reader.onload = (e: any) => {try {const data = JSON.parse(e.target.result)nodes.value = data.nodes || []edges.value = data.edges || []message.success('导入成功')} catch (error) {message.error('JSON 格式错误')}
}

总结

      通过 Vue 整合 Vue Flow,开发者可构建功能丰富的交互式流程图。本文从环境准备到自定义节点、拖拽、连线、删除和 JSON 管理,提供了完整指南。Vue Flow 的灵活性使其适用于工作流、数据可视化等场景,建议根据需求优化性能并处理异常。

demo见:

        https://gitee.com/codingtodie/vue-integration-with-vue-flow

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# WPF - Prism 学习篇:搭建项目(一)

一、前期准备开发工具&#xff1a;Visual Studio 2022二、创建项目1、创建WPF 应用“WpfApp.StudyDemo”&#xff1a;2、项目结构如下&#xff1a; 三、安装 Prism1、选中项目“WpfApp.PrismDemo”&#xff0c;在右键菜单中选择“管理 NuGet 程序包(N)...”。2、在搜索框中输入…

单片机 基于rt-thread 系统 使用 CCM内存

一、开发环境 开发板&#xff1a;野火stm32f407 系统&#xff1a;rt-thread V4.1.1 二、链接脚本配置 ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ****************************…

【UnityAssetBundle】AB包卸载资源

AB包的卸载高效稳定游戏开发的强制要求&#xff0c;它解决了资源管理中的内存泄漏问题&#xff0c;为动态的内容加载、热更新、大型世界的构建提供了内存保障&#xff0c;最终提升了游戏性能、稳定性和用户体验。卸载资源方式一&#xff08;推荐使用&#xff09;&#xff1a;卸…

【万字长文】深度学习2 yolov5修改为自己的数据集

数据预处理 使用labelme可以直接导出适用于yolo模型的txt文本数据&#xff0c;也可以直接导出默认的json数据结构&#xff0c;后面我会提供代码进行转换。自行进行标注&#xff0c;图片与标注一一对应&#xff0c;更多要求不赘述。因为我做最简单的检索模型&#xff0c;不做切…

ubuntu18编译RealSense SDK 2.0

参考文章&#xff1a;https://dev.intelrealsense.com/docs/compiling-librealsense-for-linux-ubuntu-guide1、安装依赖 sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade sudo apt-get install libssl-dev libusb-1.0-0-dev libud…

算法学习笔记:9.Kruskal 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题

在图论的众多算法中&#xff0c;Kruskal 算法以其简洁高效的特性&#xff0c;成为求解最小生成树&#xff08;Minimum Spanning Tree&#xff0c;MST&#xff09;的经典方法。无论是在通信网络的优化设计、电路布线的成本控制&#xff0c;还是在计算机考研 408 的备考过程中&am…

Vue+Openlayers加载OSM、加载天地图

文章目录1. 介绍2. 加载底图2.1 加载默认OSM地图2.2 加载天地图1. 介绍 Openlayers官网&#xff1a;https://openlayers.org/ 安裝依赖&#xff1a;npm i ol 2. 加载底图 参考博客&#xff1a; vueopenlayers环境配置&#xff1a;https://blog.csdn.net/cuclife/article/det…

Python处理电子表格文件库之pyexcel使用详解

概要 pyexcel是一个功能强大的Python第三方库,专门用于处理各种格式的电子表格文件。核心价值在于提供了统一的接口来读取、写入和操作Excel、CSV、ODS等多种电子表格格式,极大简化了数据处理工作流程。与传统的单一格式处理库不同,pyexcel采用了插件化架构,使开发者能够通…

【网络安全】恶意 Python 包“psslib”仿冒 passlib,可导致 Windows 系统关闭

文章目录恶意 Python 包“psslib”仿冒 passlib如何避免psslib的威胁恶意 Python 包“psslib”仿冒 passlib Socket 的威胁研究团队发现了一个名为 psslib 的恶意 Python 包&#xff0c;旨在以提供密码安全功能为幌子突然关闭 Windows 系统。 该软件包由威胁行为者使用别名 u…

ai之对接电信ds后端服务,通过nginx代理转发https为http,对外请求,保持到达第三方后请求头不变

前置环境&#xff1a; 在微信小程序中嵌入H5页面&#xff08;智能客服&#xff09;&#xff0c;需要让h5页面在https的域名服务器上。即通过 nginx 部署成web服务&#xff0c;还得配置域名和端口443访问。电信的第三方deepseek服务 &#xff0c;只接收http请求&#xff0c;暂未…

第十四节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入门 - Flask 后端 生产部署讲解

Vben5 系列文章目录 💻 基础篇 ✅ 第一节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入门 ✅ 第二节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入门 - Python Flask 后端开发详解(附源码) ✅ 第三节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入…

Unity开发如何解决iOS闪退问题

一、iOS闪退常见原因及排查方法1. 内存问题&#xff08;最常见原因&#xff09; 症状表现&#xff1a; 设备发热后闪退 加载大型场景时崩溃 控制台出现EXC_RESOURCE RESOURCE_TYPE_MEMORY日志 解决方案&#xff1a; // 内存监控代码 void Update() { Debug.Log($"内存使用…

【机器学习笔记 Ⅲ】5 强化学习

强化学习&#xff08;Reinforcement Learning, RL&#xff09; 强化学习是机器学习的一个分支&#xff0c;其核心思想是让智能体&#xff08;Agent&#xff09;通过与环境&#xff08;Environment&#xff09;的交互学习最优策略&#xff08;Policy&#xff09;&#xff0c;以最…

pytorch深度学习-卷积神经网络CNN-MNIST-gpu加速

一、为什么需要 CNN&#xff1f;从图像识别的 “麻烦” 说起假设你想让电脑识别一张图片里有没有猫。 如果用传统神经网络&#xff1a;一张 100100 的彩色图片&#xff0c;有 100100330000 个像素点&#xff0c;每个像素点都是一个输入神经元。传统网络需要每个输入神经元和隐藏…

【阿里巴巴JAVA开发手册】IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式,不要使用Windows格式。

问题&#xff1a;当使用 IDEA SSH 远程开发时&#xff0c;SFTP 同步的 Windows 本地编辑的 config/plugin_config 文件文本内容中 “换行符”与 Unix、Linux 的文件文本内容换行符字符集不一致&#xff0c;导致 docker 容器中自定义 /opt/seatunnel/bin/install_plugin 在执行以…

自动驾驶ROS2应用技术详解

自动驾驶ROS2应用技术详解 目录 自动驾驶ROS2节点工作流程自动驾驶感知融合技术详解多传感器数据同步技术详解ROS2多节点协作与自动驾驶系统最小节点集 1. 自动驾驶ROS2节点工作流程 1.1 感知输出Topic的后续处理 在自动驾驶系统中&#xff0c;感知节点输出的各种Topic会被…

Redis底层实现原理之订阅发布机制

文章目录1. 通知类型2. 实现原理2.1 Pub/Sub2.1.1 基础知识点2.1.2 频道和订阅者的存储通知原理2.1.3 键空间通知2.1.4 客户端消费2.1.5 缺陷2.2 Redis Stream2.2.1 基础知识点2.2.2 基础数据结构2.2.3 消费者组管理2.2.4 消息和消费者持久化2.2.5 消息生产和消费2.2.6 消费者拉…

【MATLAB代码】AOA与TDOA混合定位例程,自适应基站数量,二维,可调节锚点数量。订阅专栏后,可直接查看matlab源代码

本文给出一个matlab代码,用于在二维平面上,使用AOA的角度测量和TDOA的到达时间差的测量,来达到对未知点的精确定位。最后输出定位示意图、真实点坐标、仅AOA定位坐标与误差、仅TDOA定位的坐标与误差、AOA+TDOA混合定位的坐标与误差。订阅专栏后可直接查看源代码,粘贴到MATL…

Node.js 所有主要版本的发布时间、稳定版本(Stable)和长期支持版本(LTS) 的整理

以下是 Node.js 所有主要版本的发布时间、稳定版本&#xff08;Stable&#xff09;和长期支持版本&#xff08;LTS&#xff09; 的整理&#xff0c;涵盖从早期版本到当前最新版本的信息。 &#x1f4c5; Node.js 版本发布规律 每 6 个月发布一个新主版本&#xff08;偶数月&am…

【牛客刷题】小红的v三元组

文章目录 一、题目介绍1.1 题目描述1.2 输入描述1.3 输出描述1.4 示例二、解题思路2.1 核心算法设计2.2 性能优化关键2.3 算法流程图三、算法实现四、算法分析4.1 时间复杂度4.2 空间复杂度4.3 正确性证明五、为什么选择离散化+树状数组的解法?5.1 问题本质分析5.2 解法设计思…