基于Vue3和通义千问的AI流程图绘制工具实践
前言
在软件开发过程中,流程图是一个非常重要的工具,它可以帮助我们更好地理解和设计系统。本文将介绍一个基于Vue3和通义千问AI的流程图自动绘制工具的实现过程。
项目概述
这是一个将自然语言转换为流程图的Web应用程序。用户只需输入文本描述,系统就能自动生成对应的流程图,大大提升了流程图创建的效率。
主要特性
- 🚀 基于自然语言生成流程图
- 📊 使用Mermaid.js作为图表渲染引擎
- 🤖 集成通义千问大语言模型
- 💡 支持实时预览和优化
- 📥 支持导出配置文件和图片
- 🎨 基于Tailwind CSS的现代UI设计
技术栈详解
1. 前端框架 - Vue 3
项目采用Vue 3作为前端框架,利用其组合式API(Composition API)提供更好的代码组织和复用能力。主要使用了以下特性:
- 组合式API
- 响应式系统
- 组件化开发
2. UI框架 - Tailwind CSS
使用Tailwind CSS实现了响应式设计和现代化的UI界面:
javascript:e:\workspace\Git\ai-drawing-process-diagram-master\code\tailwind.config.js
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
darkMode: 'class',
theme: {
extend: {
colors: {
primary: '#4F46E5',
secondary: '#6366F1'
}
}
}
}
3. 消息通知系统
实现了一个优雅的消息通知组件,支持多种类型的消息提示:
- 成功提示
- 错误提示
- 警告提示
- 普通信息提示
4. Mermaid.js集成
使用Mermaid.js作为图表渲染引擎,支持多种图表类型:
- 流程图
- 时序图
- 甘特图
- 类图等
核心功能实现
1. AI文本解析
通过调用通义千问API,将用户输入的自然语言转换为结构化的图表描述。
2. 实时预览
实现了图表的实时预览功能,用户可以即时看到修改效果。
3. 导出功能
支持多种导出格式:
- PNG图片
- SVG矢量图
- Mermaid源代码
项目亮点
-
优雅的代码组织
- 采用模块化设计
- 清晰的代码结构
- 良好的代码复用
-
现代化的开发工具链
- Vue CLI
- PostCSS
- Tailwind CSS
- ESLint
-
用户体验优化
- 响应式设计
- 深色模式支持
- 流畅的动画效果
未来展望
项目计划添加以下功能:
- 支持更多图表类型
- 添加历史记录功能
- 优化生成质量
- 支持模板系统
- 增加协作功能
总结
这个项目展示了如何将现代前端技术与AI能力相结合,创造出实用的开发工具。通过Vue 3的组合式API和Tailwind CSS的原子化CSS,我们实现了一个既实用又美观的流程图绘制工具。
参考资料
核心源码
code/src/App.vue
html
<script setup>
import {ref} from 'vue';
import FlowChart from './components/FlowChart.vue';
import {callGpt} from '@/utils/util';
import Message from '@/utils/message';
const description = ref('');
const isGenerating = ref(false);
const flowChartRef = ref();
const flowData = ref('');
// 处理流程图生成
const handleGenerate = async () => {
if (!description.value.trim()) {
Message.warning('请输入流程描述');
return;
}
isGenerating.value = true;
flowData.value = ''; // 清空现有流程图
try {
const prompt = `请根据以下描述生成一个 Mermaid 流程图:${description.value}
要求:
1. 使用 graph LR 方向
2. 使用中文描述
3. 只返回 Mermaid 格式的代码,不要其他解释
4. 合理使用 subgraph 进行分组
5. 使用合适的节点形状和连接线样式`;
await callGpt(
prompt,
() => {},
(text) => {
// 处理返回的文本,提取 Mermaid 内容
const matchText = "```mermaid";
if (text.startsWith(matchText)) {
let indexOf = text.indexOf("```", matchText.length + 1);
if (indexOf > -1) {
flowData.value = text.substring(matchText.length, indexOf);
}
}
isGenerating.value = false;
if (flowData.value) {
Message.success('流程图生成完成');
} else {
Message.error('生成的内容格式不正确');
}
}
);
} catch (error) {
console.error('生成流程图失败:', error);
Message.error('生成流程图失败,请重试');
isGenerating.value = false;
}
};
// 导出配置文件
const exportConfig = () => {
if (isGenerating.value || !flowData.value) return;
flowChartRef.value?.exportConfig();
};
// 导出PNG图片
const exportToPNG = () => {
if (isGenerating.value || !flowData.value) return;
flowChartRef.value?.exportToPNG();
};
</script>
<template>
<div class="min-h-screen bg-gray-50">
<div class="max-w-7xl mx-auto">
<nav class="h-16 bg-white shadow-sm flex items-center justify-between px-8">
<div class="flex items-center space-x-2">
<span class="text-2xl font-['Pacifico'] text-primary">logo</span>
<span class="text-lg font-medium">AI绘制流程图</span>
</div>
<button class="w-10 h-10 rounded-button flex items-center justify-center hover:bg-gray-100 transition-colors">
<i class="fas fa-sun text-gray-600"></i>
</button>
</nav>
<main class="py-12">
<div class="mx-auto">
<div class="bg-white rounded-lg shadow-sm p-8">
<div class="space-y-6">
<div>
<label for="description" class="block text-sm font-medium text-gray-700 mb-2">
流程描述
</label>
<textarea
id="description"
v-model="description"
rows="4"
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring-primary sm:text-sm resize-none p-4 my-2"
placeholder="请输入您想要绘制的流程描述,例如:'一个用户注册流程,包含填写信息、验证邮箱和完成注册三个步骤'"
></textarea>
</div>
<div class="flex justify-between items-center">
<div class="space-x-3">
<button
v-if="!isGenerating && flowData"
class="inline-flex items-center px-4 py-2 rounded-md text-primary border border-primary text-sm font-medium hover:bg-primary/5"
@click="exportConfig"
>
<i class="fas fa-download mr-2"></i>
导出配置
</button>
<button
v-if="!isGenerating && flowData"
class="inline-flex items-center px-4 py-2 rounded-md text-primary border border-primary text-sm font-medium hover:bg-primary/5"
@click="exportToPNG"
>
<i class="fas fa-image mr-2"></i>
导出图片
</button>
</div>
<button
:class="[
'inline-flex items-center px-6 py-2.5 rounded-md text-white text-sm font-medium shadow-sm',
isGenerating ? 'bg-primary/70 cursor-not-allowed' : 'bg-primary hover:bg-primary/90'
]"
:disabled="isGenerating"
@click="handleGenerate"
>
<i class="fas fa-magic mr-2"></i>
{{ isGenerating ? '生成中...' : '开始生成' }}
</button>
</div>
<div class="mt-6" v-if="!isGenerating && flowData">
<h3 class="text-lg font-medium text-gray-900 mb-4">生成的流程图</h3>
<FlowChart
ref="flowChartRef"
:flow-data="flowData"
/>
</div>
</div>
</div>
<div class="mt-12 bg-white rounded-lg shadow-sm p-8">
<h2 class="text-xl font-medium mb-6">使用说明 & 实现原理</h2>
<div class="space-y-4 text-gray-600">
<div class="flex items-start space-x-3">
<div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="fas fa-lightbulb text-sm text-primary"></i>
</div>
<p>输入您想要绘制的流程描述,系统会通过 AI 分析您的描述,自动生成符合 Mermaid 语法的流程图代码。支持复杂的业务流程、系统架构等多种场景。</p>
</div>
<div class="flex items-start space-x-3">
<div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="fas fa-code text-sm text-primary"></i>
</div>
<p>系统使用 Mermaid.js 作为流程图渲染引擎,通过 AI 生成标准的 Mermaid 图表代码,确保流程图的专业性和可维护性。支持导出配置文件和图片,方便进一步编辑和分享。</p>
</div>
<div class="flex items-start space-x-3">
<div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="fas fa-robot text-sm text-primary"></i>
</div>
<p>后端采用通义千问大语言模型,结合优化的 Prompt 策略,能够准确理解用户意图,生成结构清晰、层次分明的流程图,支持中文描述直接转换为专业流程图。</p>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</template>
<style>
body {
min-height: 100vh;
}
#app {
min-height: 100vh;
}
body::-webkit-scrollbar {
width: 15px;
}
body::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 8px;
}
body::-webkit-scrollbar-thumb {
background: #6366f1;
border-radius: 8px;
border: 2px solid #f1f5f9;
}
body::-webkit-scrollbar-thumb:hover {
background: #4f46e5;
}
textarea {
border: 1px solid #e5e7eb;
padding: 1rem 1.25rem;
margin: 0.75rem 0;
line-height: 1.6;
font-size: 0.975rem;
transition: all 0.2s ease-in-out;
background-color: #f9fafb;
}
textarea:focus {
outline: none;
border-color: #4F46E5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
background-color: #ffffff;
}
textarea::placeholder {
color: #9ca3af;
font-size: 0.875rem;
}
</style>
code/src/components/FlowChart.vue
html
<script setup>
import { onMounted, ref, defineProps, defineExpose } from 'vue'
import mermaid from 'mermaid'
const props = defineProps({
flowData: {
type: String,
default: ''
}
});
const flowChartRef = ref();
// 初始化 mermaid 配置
mermaid.initialize({
startOnLoad: true,
theme: 'neutral',
securityLevel: 'loose',
flowchart: {
curve: 'basis',
padding: 15,
nodeSpacing: 50,
rankSpacing: 50,
htmlLabels: true,
diagramPadding: 8,
}
});
// 导出配置文件方法
const exportConfig = () => {
if (!props.flowData) return;
const blob = new Blob([props.flowData], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'flowchart.mmd';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
// 导出PNG图片方法
const exportToPNG = () => {
const svg = flowChartRef.value.querySelector('svg')
const svgData = new XMLSerializer().serializeToString(svg)
const bbox = svg.getBBox()
const width = bbox.width * 1.25
const height = bbox.height * 1.25
const newSvg = `
<svg width="${width}" height="${height}" viewBox="${0} ${0} ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
<rect x="${0}" y="${0}" width="${width}" height="${height}" fill="white"/>
${svgData}
</svg>
`
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
canvas.width = width * 2
canvas.height = height * 2
ctx.scale(2, 2)
ctx.drawImage(img, 0, 0, width, height)
const link = document.createElement('a')
link.download = '流程图.png'
link.href = canvas.toDataURL('image/png')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(newSvg)))
}
onMounted(() => {
mermaid.render('mermaid-diagram', `${props.flowData}`).then((result) => {
if (flowChartRef.value) {
flowChartRef.value.innerHTML = result.svg
}
})
})
// 暴露方法给父组件
defineExpose({
exportConfig,
exportToPNG,
});
</script>
<template>
<div class="flow-chart-container">
<div id="flow-chart" ref="flowChartRef"></div>
</div>
</template>
<style scoped>
.flow-chart-container {
background-color: white;
padding: 1.5rem;
border-radius: 0.75rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow-x: auto;
}
:deep(.node rect) {
fill: #fff;
stroke: #333;
stroke-width: 2px;
}
:deep(.edgeLabel) {
background-color: #fff;
padding: 4px;
border-radius: 4px;
}
:deep(.cluster rect) {
fill: #f5f5f5;
stroke: #ddd;
stroke-width: 1px;
rx: 5px;
ry: 5px;
}
</style>
效果展示
系统首页
绘制展示