vue3+ts 绘制流程图 vueflow 附代码及效果图

news/2024/11/17 8:55:39/

已完成渲染流程图,自定义模板内容(上下分级),自定义样式,新增节点addRandomNode,点击修改节点nodeClickHandler(从父组件传值)

官网:https://vueflow.dev/guide/node.html#node-template
文档比较复杂,很多想找的方法没法一下就找到需要注意

我装了三个,如果npm安装报错可以试试yarn add

    "@vue-flow/background": "^1.2.0","@vue-flow/core": "^1.20.2","vue3-flowchart": "^0.19.1"

<template><div style="height: 500px;"><VueFlow v-model="elements" @nodeClick="nodeClickHandler"  fit-view-on-init @edgeClick="clickadd"@edge-update="onEdgeUpdate" :style="{ background: 'transparent' }" :default-zoom="1.5"><Background :pattern-color="'#aaa'" gap="8" /><template #node-custom="props"><CustomNode :node="props"  /></template>
<!-- 新增 @edgeClick="clickadd"  --></VueFlow></div><dynamic-modal :visible="visible" top="15vh" width="500px" :title="title" :form="addForm" :items="items" @submit="submit"@reset="reset" @close="close" /><dynamic-modal :visible="visible_edit" top="15vh" width="500px" :title="'修改节点'" :form="editForm" :items="items" @submit="submit2"@reset="reset" @close="close" />
</template><script setup>
import { ref, reactive, computed, watch , markRaw } from 'vue'
import { Panel, PanelPosition, VueFlow, useVueFlow , isNode,applyChanges} from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { onMounted } from 'vue'
import useLoading from '@/hooks/loading'
import dynamicModal from '@/components/modal/dynamicModal.vue';
import { Message } from '@arco-design/web-vue'
import { queryPostList } from '@/api/data-assets/base-assets.ts'
import { useI18n } from 'vue-i18n';
import router from '@/router';
import CustomNode  from './CustomNode.vue'const props = defineProps(['processData'])
const { loading, setLoading } = useLoading(true);const { t } = useI18n()const { nodes, addNodes, updateEdge, removeEdges, getElements, getNode, addEdges, onConnect, dimensions } = useVueFlow()
onConnect((params) => addEdges(params))
const visible = ref(false)
const visible_edit = ref(false)
const title = ref('选择节点操作')
const img_params = ref({})
// const nodeTypes = {
//   custom: markRaw(CustomNode),
// }
let addForm = ref({nodeOperations: '',postNames:[],ccNames:[],
})
let editForm = ref({nodeOperations: '',
})
const op2 = [{ label: '审批', value: '审批' },{ label: '确认', value: '确认' },{ label: '处理', value: '处理' },
]
let optionPost = ref([])
// 新增、编辑弹框
const items = reactive([{ field: 'nodeOperations',width:24, label: computed(()=> t('data.departmentCollaboration.columns.process.flow_1')),disabled:true, type: 'radio_comm',option: op2, rules: [{ required: true, message: computed(()=> t('data.departmentCollaboration.columns.process.select')) }], placeholder: computed(()=> t('data.dictory.form.columns.desc.placeholder'))},{ field: 'postIds', type: 'select', label: computed(()=> t('data.departmentCollaboration.columns.process.flow_2')),multiple:true,fieldNames: { label: 'postName', value: 'postId' }, rules: [{ required: true, message: computed(()=> t('data.departmentCollaboration.columns.process.select')) }],option: optionPost, placeholder: computed(()=> t('data.departmentCollaboration.columns.process.select')) },{ field: 'ccIds', type: 'select', label: computed(()=> t('data.departmentCollaboration.columns.process.flow_3')),multiple:true,fieldNames: { label: 'postName', value: 'postId' }, rules: [{ required: true, message: computed(()=> t('data.departmentCollaboration.columns.process.select')) }],option: optionPost, placeholder: computed(()=> t('data.departmentCollaboration.columns.process.select')) }])
// 提交方法
const submit = async(params)=> {// console.log(params,img_params.value)// let ccNames = []// params.ccIds.forEach(id=>{//   ccNames.push(optionPost.value.filter(i=>i.postId==id)[0].postName)// })// params.ccNames = ccNames// let postNames = []// params.postIds.forEach(id=>{//   postNames.push(optionPost.value.filter(i=>i.postId==id)[0].postName)// })// params.postNames = postNamestry {await addRandomNode(params)visible.value = falseMessage.success("添加成功")} catch (error) {console.log(error)}
}// 重置方法
const reset = ()=> {addForm.value = {remark:undefined}editForm.value = {remark:undefined}
}
const close = ()=> {visible.value = falsevisible_edit.value = falseaddForm.value = {remark:undefined}editForm.value = {remark:undefined}
}
function clickadd  (params){Message.info("暂无法新增")// img_params.value=params// console.log("点击",params)// visible.value = true
}
const handleEdgeMouseEnter = (edge) => {console.log('Mouse entered edge:', edge);
};
// let elements = ref([])
// elements = computed(() => {
//     return JSON.parse(JSON.stringify(props.processData))
//   })
const elements = ref([{id: '1', type: 'input', position: { x: 400, y: 5 }, label: "发起人",data: {title: '发起人',content: '这是发起人的内容',ccIds:[],//抄送人IDs 实际为岗位数组postIds:[],//操作人IDs 实际为岗位数组postNames:[],ccNames:[],nodeOperations:"确认",//操作节点},},{id: '2', type: 'custom',  position: { x: 400, y: 100 }, label: "审批",// template: markRaw(OverwriteCustomNode),data: {ccIds:[],//抄送人IDs 实际为岗位数组postIds:[],//操作人IDs 实际为岗位数组postNames:[],ccNames:[],nodeOperations:"审批",//操作节点},},{id: '3', type: 'custom',  position: { x: 400, y: 250 }, label: "处理",data: {ccIds:[],//抄送人IDs 实际为岗位数组postIds:[],//操作人IDs 实际为岗位数组postNames:[],ccNames:[],nodeOperations:"处理",//操作节点},},{id: '4',type: 'custom',   position: { x: 400, y: 400 }, label: "确认",data: {ccIds:[],//抄送人IDs 实际为岗位数组postIds:[],//操作人IDs 实际为岗位数组postNames:[],ccNames:[],nodeOperations:"确认",//操作节点},},{id: '5', type: 'output', position: { x: 400, y: 550 }, label: "完成",data: {title: '流程结束',content: '这是流程结束的内容',},},{ id: 'e1-2', source: '1', type: 'smoothstep', label: '+', target: '2',labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },},{ id: 'e2-3', source: '2', type: 'smoothstep', label: '+', target: '3',labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },},{ id: 'e3-4', source: '3', type: 'smoothstep', label: '+', target: '4',labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },},{ id: 'e4-5', source: '4', type: 'smoothstep', label: '+', target: '5',labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },},
])
//取父组件的值
//elements.value = props.processData
// console.log(props.processData)
// 修改方法
const submit2 = async(params)=> {console.log("params",JSON.stringify(params))let ccNames = []JSON.parse(JSON.stringify(params.ccIds)).forEach(id=>{ccNames.push(optionPost.value.filter(i=>i.postId==id)[0].postName)})params.ccNames = ccNameslet postNames = []params.postIds.forEach(id=>{postNames.push(optionPost.value.filter(i=>i.postId==id)[0].postName)})params.postNames = postNamesconsole.log("params",JSON.stringify(params))console.log(elements.value,elements)elements.value.forEach(e => {if(e.id == img_params.value.node.id){// console.log("匹配到了")e.data = JSON.parse(JSON.stringify(params))e.label = e.data.nodeOperations +"<br/>"+ e.data.postNames// console.log(e)}});console.log(elements.value)visible_edit.value=false// try {//   await addRandomNode(params)//   visible.value = false//   Message.success("添加成功")// } catch (error) {// }
}
const nodeClickHandler = (params) => {console.log()console.log(JSON.stringify(params.node.data));// 如果是发起工单和流程结束,跳过if(params.node.label=="发起人" || params.node.label=="完成" ) returnimg_params.value=paramsconsole.log("点击",params)visible_edit.value = trueeditForm.value = params.node.data
};
// addNodes(node1)
// addEdges(edge1)//新增一个节点
function addRandomNode(params) {console.log(img_params)//1.新建node,节点位置为(img_params.value.edge.events.sourceX+img_params.value.edge.events.targetX)/2-30,(img_params.value.edge.events.sourceY+img_params.value.edge.events.targetY)/2-15,//2.将当前edge线段的target改为新建的nodeid//3.新建一个edge线段,source为新建的nodeid,target为原有target,即img_params.value里面的//4.将当前新建的node节点以及往后的所有节点的y都新增150const nodeId = (nodes.value.length + 1).toString()+'add'// console.log(getNode,img_params.value.edge.source)// console.log(getNode,img_params.value.edge.source)// const source = getNode(img_params.value.edge.source)// console.log(source)const newNode = {id: nodeId,label: JSON.parse(JSON.stringify(params.nodeOperations)),type:'custom',// label: `新增Node: ${nodeId}`,position: { x: img_params.value.edge.sourceX-75, y: (img_params.value.edge.sourceY + img_params.value.edge.targetY) / 2 - 15 },// position: { x: (img_params.value.edge.sourceX + img_params.value.edge.targetX) / 2 - 30, y: (img_params.value.edge.sourceY + img_params.value.edge.targetY) / 2 - 15 },data:JSON.parse(JSON.stringify(params))// position: { x: Math.random() * dimensions.value.width, y: Math.random() * dimensions.value.height },}removeEdges(img_params.value.edge.id)const newEdge = {id: img_params.value.edge.id,source: img_params.value.edge.source,type: img_params.value.edge.type,label: img_params.value.edge.label,target: nodeId,updatable: img_params.value.edge.updatable,// events: { click: clickadd },labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },}const newEdge2 = {id: nodeId + 'add',source: nodeId,type: img_params.value.edge.type,label: img_params.value.edge.label,target: img_params.value.edge.target,updatable: img_params.value.edge.updatable,// events: { click: clickadd },labelBgPadding: [8, 4],labelStyle: { fill: '#fff', fontWeight: 700 },labelBgBorderRadius: 4,labelBgStyle: { fill: '#10b981', color: '#fff', fillOpacity: 1 },}
//   //找到当前指向的下一个节点
//   let index = elements.value.findIndex(item => item.id == img_params.value.edge.target)
//   // console.log(elements.value,img_params.value.edge.target)
//   // console.log(elements.value.findIndex(item => item.id == img_params.value.edge.target))
//   //将节点的y设置
//   for (let i = 0; i > index-1; i++) {
//     let node = elements.value[i]
//     if(isNode(node)){
//       console.log("是node")
//       node.position.y = node.position.y +153
//       // applyChanges(elements.value,{nodes:[node]})
//     }    
//   }
// console.log(elements.value)addNodes([newNode])addEdges([newEdge])addEdges([newEdge2])
}//新增一个分支
function onEdgeUpdate({ edge, connection }) {console.log(edge, connection)return updateEdge(edge, connection)
}
const dosomething = ()=>{// return getElements.valuereturn elements.value
}
onMounted(async() => {const a = { page: 1, pageSize: 1000 }try {const { data } = await queryPostList(a)optionPost.value = data.records} catch (err) {Message.error(err.message)}
})
watch(visible_edit, (newValue) => {// console.log(elements.value)if (!newValue) {// console.log("修改??",newValue)// 可以在这里执行重新渲染操作elements.value = [...elements.value];// console.log("修elements改??",elements.value)}
});
defineExpose({dosomething
})
</script><style>
/* these are necessary styles for vue flow */
@import "@vue-flow/core/dist/style.css";/* this contains the default theme, these are optional styles */
@import "@vue-flow/core/dist/theme-default.css";.custom-node {background-color: #f2f2f2;border: 1px solid #ccc;padding: 8px;display: flex;flex-direction: column;align-items: center;justify-content: center;
}.node-title {font-weight: bold;
}.node-content {margin-top: 4px;
}</style>

http://www.ppmy.cn/news/733422.html

相关文章

随手笔记——ROS下CMakeLists.txt中add_executable、target_link_libraries简单使用

随手笔记——ROS下CMakeLists.txt中add_executable、target_link_libraries简单使用说明 说明使用举例 说明 add_executable() 生成可执行文件 target_link_libraries() 为可执行文件或库添加链接库 使用举例 add_executable(iat_publish src/iat_publish.cppsrc/speech_rec…

液晶显示屏划痕修复方法

现在大家使用的电子设备都采用了液晶屏&#xff0c;而在日常使用中难免磕磕碰碰&#xff0c;这也使得液晶屏幕上经常会留下一些小划痕。天天看着这些碍眼的划痕&#xff0c;大家心里一定很不爽吧&#xff0c;实际上我们可以自己动手通过一些小技巧来将它们赶走。 工具/原料 铅笔…

光盘显示0字节可用_佳显LCD液晶专业生产液晶显示模块

GDRAM的读写&#xff1a;首先说明对GDRAM的操作基本单位是一个字&#xff0c;也就是2个字节&#xff0c;就是说读写时一次***少写2个字节&#xff0c;一次***少读2个字节。写数据&#xff1a;先开启扩展指令集(0x36),然后送地址&#xff0c;这里的地址与DDRAM中的略有不同&…

关于显示屏幕的一点点总结

关于显示屏幕的一点点总结 一、触摸屏类型: 1)电阻式触摸屏:四线电阻屏、五线电阻屏

LCD工控液晶触摸屏贴合如何进行贴合工程?

许多LCD液晶屏 s是没有触摸技术的简单显示屏。然而&#xff0c;随着社会应用的发展&#xff0c;许多工业设备和仅具有显示功能的显示设备已经不能满足市场的技术要求&#xff0c;人机交互越来越普遍。为了实现人机交互功能&#xff0c;触摸面板&#xff0c;如电阻式触摸屏、电容…

清洁机器人--屏幕显示LCD方案之MCU GD32F470及其液晶RGB接口的LCD驱动代码

1.MCU GD32F470及其液晶RGB接口 STM32F429 支持LCD RGB硬件接口,但是其存在供货 价格问题,暂时不选,选择国产替代方案。国产MCU中支持LCD RGB 硬件接口的有GD32F450 GD32F470.根据GD FAE最新消息450不在新推客户,推GD32F470. 2.不同的分辨率涉及不同RAM需求,如内置RAM不足…

使用Python控制1602液晶屏实时显示时间(附PyCharm远程调试)

前言 原创文章,转载引用务必注明链接。水平有限,如有疏漏,欢迎指正。 本文介绍一下UP板的GPIO资源使用,以及一个使用Python演示一个简单的demo。 本文使用Markdown写成,为获得更好的阅读体验和正常的图片、链接,请访问我的博客: http://www.cnblogs.com/sjqlwy/p/up_160…

计算机显示屏幕会对人体产生,一种自动除尘功能的计算机显示屏专利_专利申请于2018-07-13_专利查询 - 天眼查...

1. 一种自动除尘功能的计算机显示屏&#xff0c;其特征在于:包括边框(31)&#xff0c;所述边框(31)内固 定安装有液晶屏(30)&#xff0c;所述边框(31)四角处设有对称的两个第一固定块(1)和第二固定块 (16)&#xff0c;所述第一固定块(1)与第二固定块(16)相对设置&#xff0c;所…