当前位置: 首页 > news >正文

Maotu流程图编辑器:Vue3项目中的集成实战与自定义流程开发指南

文章目录

  • 前言
  • 安装依赖
  • 一、Maotu 组件的集成
    • 1.目录结构设计
    • 2.编辑页面核心代码
      • 2.1编辑页面得图标导入
    • 3.预览页面
      • 3.1预览页面代码
      • 3.2 API 请求与 Token 统一封装
  • 二、自定义流程
    • 1.新建自定义流程
    • 2.http请求
    • 3.解析请求数据
    • 4.设置图元属性
      • 5.调用流程
  • 三、数据更新处理办法
  • 四、常见问题与解决方案
    • 1.Token失效问题
    • 2.节点数据格式不一致
    • 3.自定义节点样式丢失
  • 总结

前言

Maotu是一款功能强大的流程图编辑组件,在我们的项目中发挥了重要作用。它不仅提供了直观的可视化界面,还支持高度自定义的节点类型和交互逻辑,使复杂业务流程的设计变得简单高效。
相比其他流程图工具,Maotu的优势在于:

  • 完善的节点自定义能力
  • 灵活的数据导入导出
  • 与Vue3项目无缝集成
  • 支持复杂条件分支和业务规则

安装依赖

Maotu

$ npm install maotu或
$ yarn add maotu或
$ pnpm install maotu

一、Maotu 组件的集成

1.目录结构设计

src/
├── views/
│   └── maotu/
│       ├── MtEdit.vue  // 编辑页面
│       └── MtPre.vue   // 预览页面
├── network/
│   └── maotuRequest.ts // 专用API请求封装
└── router/└── index.ts        // 路由配置

2.编辑页面核心代码

在这里插入图片描述

<template><div class="mt-edit-container"><div v-if="loading" class="loading-overlay"><el-icon class="loading-icon"><Loading /></el-icon><span>加载中...</span></div><mt-edit ref="mtEditRef" @on-preview-click="onPreviewClick" @on-return-click="onReturnClick"@on-save-click="onSaveClick"></mt-edit></div>
</template>
<script setup lang="ts">
import 'maotu/dist/style.css';
import { MtEdit, leftAsideStore } from 'maotu';
import getDatas from "@/network/index";
import { ref, onMounted, nextTick } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { ElMessage } from 'element-plus';
import { Loading } from '@element-plus/icons-vue';// 定义编辑器数据类型
interface EditorData {[key: string]: any;
}const router = useRouter();
const route = useRoute();
const mtEditRef = ref<InstanceType<typeof MtEdit>>();
const id = route.query.id;
const loading = ref(true); // 加载状态// 组件挂载后加载数据
onMounted(async () => {try {// 设置页面标题document.title = '项目编辑';// 等待组件完全渲染await nextTick();// 先加载项目数据await EditXmInfo();// 再加载图标数据await edit_iconAll();} catch (error) {console.error('初始化错误:', error);ElMessage.error('加载数据失败,请刷新页面重试');loading.value = false;}
});// 预览项目
const onPreviewClick = (exportJson: EditorData) => {console.log(exportJson, '这是要传给预览组件的数据');const url = window.location.origin + window.location.pathname + '#' + 'TxPre' + '?id=' + id;window.open(url, '_blank',);
};// 返回上一页
const onReturnClick = () => {router.go(-1);
};// 保存项目数据
const onSaveClick = async (exportJson: EditorData) => {if (!exportJson) {ElMessage.error('保存失败:数据无效');return;}try {loading.value = true; // 开始保存时显示加载状态console.log(exportJson, '这是要保存的数据');const json = JSON.stringify(exportJson);const params = {id: id,json: json,};const res = await getDatas("zutai/EditXmSave_json", params);console.log("保存的数据请求", JSON.parse(JSON.stringify(res)));if (res.data.code == 0) {ElMessage({ message: '保存成功!', type: 'success', });EditXmInfo();} else {ElMessage.error(res.data.msg || '保存失败');}} catch (error) {console.error('保存错误:', error);ElMessage.error('保存失败,请稍后重试');} finally {loading.value = false; // 无论成功失败,都结束加载状态}
};// 加载项目数据
const EditXmInfo = async () => {loading.value = true;try {const res = await getDatas("zutai/EditXmInfo", {id: id,});console.log("加载项目数据", JSON.parse(JSON.stringify(res)));if (res.data.code == 0) {if (res.data.result.json) {await nextTick(); // 确保组件已经渲染mtEditRef.value?.setImportJson(res.data.result.json);} else {console.log("首次json为空");}} else {ElMessage.error(res.data.msg || '加载数据失败');}} catch (error) {console.error('加载项目数据错误:', error);ElMessage.error('加载数据失败,请稍后重试');} finally {loading.value = false;}
};// 所有图标
const edit_iconAll = async () => {try {const res = await getDatas("zutai/edit_iconAll");// 检查res是否存在if (!res || !res.data) {console.error('获取图标数据返回为空');ElMessage.error('获取图标数据失败');return;}console.log("所有图标", JSON.parse(JSON.stringify(res.data)));if (res.data.code == 0) {// 添加安全检查,确保lists存在且是数组const iconLists = res.data.lists || [];iconLists.forEach((item: any) => {if (item && item.title && item.icon) {leftAsideStore.registerConfig(item.title, item.icon);}});} else {ElMessage.error(res.data.msg || '获取图标失败');}} catch (error) {console.error('获取图标出错:', error);ElMessage.error('获取图标数据失败,请稍后重试');}
};</script>
<style lang="less">
// 变量定义
@editor-height: 92vh;
@loading-bg: rgba(0, 0, 0, 0.7);.mt-edit-container {position: relative;height: @editor-height;// 加载状态覆盖层.loading-overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;background-color: @loading-bg;display: flex;flex-direction: column;align-items: center;justify-content: center;z-index: 1000;color: white;.loading-icon {font-size: 2rem;margin-bottom: 1rem;animation: rotate 1s linear infinite;}@keyframes rotate {from {transform: rotate(0deg);}to {transform: rotate(360deg);}}}
}.items-center {padding-top: 5px;
}.w-45px {padding-left: 10px !important;
}.h-45px {width: 80px !important;
}.pl-20px {height: 30px !important;
}
</style>

2.1编辑页面得图标导入

编辑页面里面有图片图标导入,这里是导入得数据格式,按着这个格式来注册就会生成对应得title和内容,(所有图标那段就是,具体得单独上传,修改,删除让后端添加接口就行了,)
在这里插入图片描述

3.预览页面

在这里插入图片描述
我这里得温度和湿度得数据都是请求的来得数据,方法再后边

3.1预览页面代码

<template><div class="MtPre"><div v-if="loadError" class="error-container"><div class="error-message"><i class="el-icon-warning"></i><p>{{ errorMsg }}</p><el-button type="primary" @click="reloadData">重试</el-button></div></div><div v-else class="preview-container" v-loading="loading" element-loading-text="加载中..."><mt-preview :device-info="deviceInfo" ref="MtPreviewRef" @on-event-call-back="onEventCallBack"></mt-preview></div></div>
</template>
<script setup lang="ts">
import 'maotu/dist/style.css';
import { MtEdit, MtPreview } from 'maotu';
import { ref, onMounted, provide, reactive } from 'vue';
import getDatas from "@/network/index";
import { ElMessage } from 'element-plus';
import service from '@/network/maotuRequest';
import { useRouter, useRoute } from 'vue-router';// 组件引用
const MtPreviewRef = ref<InstanceType<typeof MtEdit>>();// 提供自定义请求方法给maotu组件
provide('mt-request', service);// 路由参数
const route = useRoute();
const id = route.query.id;// 状态管理
const loading = ref(true);
const loadError = ref(false);
const errorMsg = ref('数据加载失败,请重试');
const deviceInfo = ref<any[]>([]);// 设置页面标题
document.title = '组态预览';/*** 加载组态数据*/
const loadData = async () => {ElMessage({ message: '先保存再预览才可以看到最新数据!', type: 'warning', });if (!id) {loadError.value = true;errorMsg.value = '缺少组态ID参数';loading.value = false;return;}loading.value = true;loadError.value = false;try {// 获取组态数据const res = await getDatas("zutai/EditXmInfo", { id });if (res.data && res.data.code === 0 && res.data.result?.json) {MtPreviewRef.value?.setImportJson(res.data.result.json);loading.value = false;} else {throw new Error(res.data?.msg || '数据加载失败');}} catch (error) {console.error('组态加载错误:', error);loadError.value = true;errorMsg.value = error instanceof Error ? error.message : '数据加载失败,请重试';loading.value = false;}
};/*** 重新加载数据*/
const reloadData = () => {loadData();
};/*** 事件回调处理*/
const onEventCallBack = (event: any) => {console.log("事件回调:", JSON.parse(JSON.stringify(event)));// 在此处理事件回调逻辑
};// 生命周期钩子
onMounted(() => {loadData();
});
</script>
<style lang="less" scoped>
::deep {.el-image {display: none;}
}.MtPre {height: 88.5vh;position: relative;//background-color: #f5f7fa;
}.preview-container {height: 100%;width: 100%;
}.loading-container {display: flex;justify-content: center;align-items: center;height: 100%;background-color: rgba(255, 255, 255, 0.8);
}.error-container {display: flex;justify-content: center;align-items: center;height: 100%;.error-message {text-align: center;padding: 24px;border-radius: 4px;background-color: #fff;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);i {font-size: 48px;color: #f56c6c;margin-bottom: 16px;}p {margin-bottom: 16px;color: #606266;}}
}
</style>

3.2 API 请求与 Token 统一封装

为保证 Maotu 相关请求与主项目风格一致,我们新建了 src/network/maotuRequest.ts,实现了如下功能:
Token 统一:所有请求 header 统一携带 access-token,值为 token。
状态码处理:对 401/100 等特殊状态码做统一拦截与提示。
错误处理:请求失败时自动弹窗提示,并支持重试机制。
HTTP 方法封装:常用的 GET/POST 方法统一封装,便于流程图节点内直接调用。
Maotu 组件通过 provide(‘mt-request’, service) 注入自定义请求方法,保证流程图节点的 HTTP 请求与主项目一致。

import axios from 'axios';
import { ElMessage } from 'element-plus';// 创建axios实例
const service = axios.create({baseURL: '/api',//请求头timeout: 30000,headers: {"Access-Control-Allow-Origin": "*","Connection": "Keep-Alive","Content-Type": "application/json; charset = utf-8"}
});// 请求拦截器
service.interceptors.request.use((config) => {// 从localStorage获取tokenconst token = localStorage.getItem('token');if (token) {config.headers['access-token'] = `${token}`;}return config;},(error) => {console.error('请求错误:', error);return Promise.reject(error);}
);// 响应拦截器
service.interceptors.response.use((res): any => {const code = res.data.code || 200;const message = res.data.message || '请求成功';// 处理不同的状态码switch (code) {case 200:return Promise.resolve(res.data);case 401:ElMessage.error('登录信息已过期,请重新登录');// 可以在这里处理登出逻辑localStorage.removeItem('token');window.location.href = '/login';return Promise.reject(new Error('登录信息已过期'));case 403:ElMessage.error('没有权限访问该资源');return Promise.reject(new Error('没有权限'));case 500:ElMessage.error('服务器错误');return Promise.reject(new Error('服务器错误'));default:ElMessage.error(message);return Promise.reject(new Error(message));}},(error) => {console.error('响应错误:', error);ElMessage.error(error.message || '请求失败');return Promise.reject(error);}
);// 封装GET请求
export const get = (url: string, params?: any) => {return service({url,method: 'get',params});
};// 封装POST请求
export const post = (url: string, data?: any) => {return service({url,method: 'post',data});
};// 封装PUT请求
export const put = (url: string, data?: any) => {return service({url,method: 'put',data});
};// 封装DELETE请求
export const del = (url: string, params?: any) => {return service({url,method: 'delete',params});
};export default service; 

二、自定义流程

在这里插入图片描述

1.新建自定义流程

在这里插入图片描述
在这里插入图片描述

2.http请求

鼠标放到开始节点和结束节点中间,添加节点
在这里插入图片描述

选中http请求节点
在这里插入图片描述
地址写你自己得请求地址
在这里插入图片描述

3.解析请求数据

从控制台看这个数据:
一定要写args来点数据,这个是maotu写死得名称,(一开始我用我自己得名称一直拿不到数据)
res:是自定义得,后边选中刚才得http请求得节点

console.log(`zigbee:`,args.res)

这也就可以看到请求数据
在这里插入图片描述
整体得节点配置,根据我的接口返回的数据和节点配置数据进行对比,
在这里插入图片描述

4.设置图元属性

就是绑定文字,键值对等数据使用请求拿到得数据,我刚才配置了温度,湿度,门窗等信息;
在这里插入图片描述
绑定之后预览看到的数据就是这里绑定得数据,就可以实现数据交互。

5.调用流程

先再编辑页面点击空白处,左侧弹出层,选中全局事件,添加事件,事件类型选中页面初始化,事件行为选择调用流程,选择我们刚才添加得流程,就可以了,不然预览页面不生效。
在这里插入图片描述

三、数据更新处理办法

再添加一个流程,为计时器流程,就可以解决数据更新得问题。
记得再页面初始化调用计时器流程,不然不生效!!!!
在这里插入图片描述

四、常见问题与解决方案

1.Token失效问题

解决方案:在请求拦截器中统一处理401状态,自动刷新token或跳转登录页

2.节点数据格式不一致

解决方案:统一使用JSON Schema校验数据格式,确保前后端数据结构一致

3.自定义节点样式丢失

解决方案:将样式定义放入节点配置中,随节点数据一起保存和加载

总结

Maotu流程图编辑器在我们的项目中发挥了重要作用,通过合理的集成和定制开发,我们不仅实现了复杂业务流程的可视化设计,还提升了整体系统的自动化水平和用户体验。
希望本文对你在Vue3项目中集成和定制Maotu流程图编辑器有所帮助!如有问题,欢迎在评论区交流讨论。

相关文章:

  • 基于 CNN-SHAP 分析卷积神经网络的多分类预测【MATLAB】
  • JS红宝书10.1-10.5 函数
  • JS - 函数防抖详解
  • 从零开始的python学习(八)P108+P109+P110+P111+P112+P113+P114
  • 提升移动端网页调试效率:WebDebugX 与常见工具组合实践
  • WebGL与Three.js:从基础到应用的关系与原理解析
  • Web 架构之 API 安全防护:防刷、防爬、防泄漏
  • WEB3全栈开发——面试专业技能点P7前端与链上集成
  • 【CANN全新升级】CANN创新MLAPO算子,DeepSeek模型推理效率倍增
  • 如何用Coze+Fetch快速构建结构化文档
  • 在ARM+Ascend NPU上适配Step-Audio模型
  • JS红宝书笔记 10.6 - 10.10 函数
  • Android Framework 之 AudioDeviceBroker
  • 【论文阅读】大模型优化器(Large Language Models As Optimizers)
  • 全面掌握Pandas时间序列处理:从基础到实战
  • UE5 学习系列(二)用户操作界面及介绍
  • Vue 模板语句的数据来源
  • MybatisPlus枚举类的应用与转换
  • 六、接口关联
  • 【Kubernetes】Ingress-nginx快速入门
  • 开鲁网站seo不用下载/网站建设规划要点详解
  • 青岛做网站建设价格低/seo是指什么
  • 建站宝盒成品网站演示/广州seo招聘信息
  • 软件开发和编程的区别/电脑系统优化软件排行榜
  • 营销型网站开发制作/营销推广公司
  • 沙井网站建设/网络建站工作室