edo网站建设广州网络推广选择
之前使用DeepSeek协助开发过类似的页面,这是当时的页面效果:
这次的客户是某乡的养殖工厂,销售只争取到了供应链模块的定制开发,因为涉及到新的领域,需要做的是新瓶装旧酒,给功能界面提升档次,以便给用户更好的体验,便于推广其他模块。我们决定采用antv框架重新设计模块中的图表与表格,流程审批设计页面当然还是由我来开发的。
开始尝试问询:
帮我把下面的代码用antv x6重写:
<template>
<div class="approval-flow">
<a-button type="primary" @click="addNode" style="margin-bottom: 20px">
添加节点
</a-button>
<a-steps :current="null" direction="horizontal" class="custom-steps">
<a-step v-for="(node, index) in nodes" :key="node.id">
<template #title>
<div class="node-card">
<div class="card-header">
<span>{。。。。。。
因为之前并没有使用过antv框架的经验,简直无从问起,索性直接要求将之前的步骤条+表单的页面重写成为x6流程图,结果当然是惨不忍睹,先后尝试了文心一言、豆包、DeepSeek以及360纳米的多种模式,这种提问当时下文心一言、豆包和DeepSeek的答案差不多,都是一个只有一张画布的界面,添加按钮不好使,DeepSeek还是一样的服务器繁忙好多次。360纳米的多种模式只有一个模式给出了包含代码的答案,但是也等的很漫长。
总结这些答案发现了一点,就是x6的自定义节点和antd表单弹窗之间无法连贯,x6的节点种类繁多,新旧版本之间的注册节点方式有很大不同,导致代码千奇百怪。我需要的提问方式是把涉及到x6的节点的部分尽量的简化,得到一个x6简单节点+antd表单弹窗的代码,然后注册一个自定义节点加入到代码中。
提问文心一言3.5:
帮我用antv x6开发一个直线审批流程设计页面Vue2.0+antd+antv x6实现,节点的添加与编辑通过antd表单弹窗的形式实现,双击节点弹出编辑窗口,右键点击节点删除。
因为提问的内容比较抽象,失败的可能性很大,我这次使用的是回答速度快的一言。第一次还是给出了vue3.0的代码,再次提交后给出了比较靠谱的答案,竟然直接运行成功,大大超出了我的预期:
<template> <div> <!-- 添加按钮 --> <a-button @click="openAddModal">添加节点</a-button> <!-- 保存按钮 --> <a-button @click="saveProcess">保存流程</a-button> <!-- X6画布容器 --> <div ref="graphContainer" style="width: 100%; height: 800px; border: 1px solid #ccc;"></div> <!-- 添加节点模态框 --> <a-modal :visible="addModalVisible" title="添加节点" @ok="handleAddOk" @cancel="handleAddCancel" > <a-form :form="addForm"> <a-form-item label="节点名称"> <a-input v-decorator="[ 'name', { rules: [ { required: true, message: '请输入节点名称' } ] } ]" /> </a-form-item> <a-form-item label="关联角色"> <a-input v-decorator="[ 'role', { rules: [ { required: true, message: '请输入关联角色' } ] } ]" /> </a-form-item> </a-form> </a-modal> <!-- 修改节点模态框 --> <a-modal :visible="editModalVisible" title="修改节点" @ok="handleEditOk" @cancel="handleEditCancel" > <a-form :form="editForm"> <a-form-item label="节点名称"> <a-input v-decorator="[ 'name', { rules: [ { required: true, message: '请输入节点名称' } ] } ]" /> </a-form-item> <a-form-item label="关联角色"> <a-input v-decorator="[ 'role', { rules: [ { required: true, message: '请输入关联角色' } ] } ]" /> </a-form-item> </a-form> </a-modal> </div> </template> <script> import { Graph, Shape } from '@antv/x6'; export default { data() { return { graph: null, addModalVisible: false, addForm: this.$form.createForm(this), editModalVisible: false, editForm: this.$form.createForm(this), currentNode: null, nodes: [ { id: 'start', name: '发起人', role: '发起人', x: 50, y: 200 } ] }; }, mounted() { // 初始化X6图形 this.initGraph(); // 渲染节点 this.renderNodes(); }, methods: { initGraph() { // 创建X6图形实例 this.graph = new Graph({ container: this.$refs.graphContainer, grid: true, connecting: { snap: true }, interacting: { nodeMovable: false } }); // 监听节点点击事件 this.graph.on('node:click', ({ node }) => { this.openEditModal(node); }); // 监听节点右键事件 this.graph.on('node:contextmenu', ({ node, e }) => { if (node.id!== 'start') { // 删除节点 if (confirm('确定要删除该节点吗?')) { this.nodes = this.nodes.filter(n => n.id!== node.id); this.renderNodes(); } } }); }, renderNodes() { // 清空现有图形 this.graph.clearCells(); // 依次添加节点并连线 for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; const x6Node = new Shape.Rect({ id: node.id, x: node.x, y: node.y, width: 100, height: 50, label: `${node.name}\n${node.role}`, attrs: { body: { fill: '#f5f5f5', stroke: '#d9d9d9', strokeWidth: 1 } } }); this.graph.addNode(x6Node); if (i > 0) { const prevNode = this.nodes[i - 1]; const edge = new Shape.Edge({ source: prevNode.id, target: node.id }); this.graph.addEdge(edge); } } }, openAddModal() { // 打开添加节点模态框 this.addModalVisible = true; }, handleAddOk() { // 处理添加节点确认 this.addForm.validateFields((err, values) => { if (!err) { const lastNode = this.nodes[this.nodes.length - 1]; const newNode = { id: `node-${Date.now()}`, name: values.name, role: values.role, x: lastNode.x + 200, y: lastNode.y }; this.nodes.push(newNode); this.renderNodes(); this.addModalVisible = false; this.addForm.resetFields(); } }); }, handleAddCancel() { // 处理添加节点取消 this.addModalVisible = false; this.addForm.resetFields(); }, openEditModal(node) { // 打开修改节点模态框 this.currentNode = this.nodes.find(n => n.id === node.id); this.editForm.setFieldsValue({ name: this.currentNode.name, role: this.currentNode.role }); this.editModalVisible = true; }, handleEditOk() { // 处理修改节点确认 this.editForm.validateFields((err, values) => { if (!err) { this.currentNode.name = values.name; this.currentNode.role = values.role; this.renderNodes(); this.editModalVisible = false; this.editForm.resetFields(); } }); }, handleEditCancel() { // 处理修改节点取消 this.editModalVisible = false; this.editForm.resetFields(); }, saveProcess() { // 保存流程,这里可以添加保存到后端的逻辑 console.log(' 保存流程:', this.nodes); } } }; </script> <style scoped> /* 可根据需要添加自定义样式 */ </style>
运行效果:
1)初始界面
2)添加节点
3)删除节点,右键点击节点
有几个小问题
①我的提问中“添加与编辑通过antd表单弹窗的形式”这句导致一言给出代码中使用了两个表单来处理编辑与删除,这很好解决,参照之前版本的代码,去掉一个表单,然后通过node.id判断当前操作是编辑还是删除就可以了。
②表单中的角色使用的input而不是select,也是提问中没有声明的原因,这不影响,参照之前版本的代码,改一下就可以了。
③相比于上个版本的步骤条+卡片来说节点展示方式过于简单,需要自定义节点。
打开antv x6的图表示例,找到自定义节点,发现第一个例子就是我所需要的:
自定义节点 | X6
将代码中的节点替换为我的自定义节点就可以了,这是完善之后的代码:
<template> <div> <!-- 添加按钮 --> <a-button @click="openEditModal">添加节点</a-button> <!-- 保存按钮 --> <a-button @click="saveProcess">保存流程</a-button> <!-- X6画布容器 --> <div ref="graphContainer" style="width: 100%; height: 800px; border: 1px solid #ccc;"></div> <!-- 修改节点模态框 --> <a-modal :visible="editModalVisible" :title="title" @ok="handleEditOk" @cancel="handleEditCancel" > <a-form :form="editForm"> <a-form-item label="节点名称"> <a-input v-decorator="[ 'name', { rules: [ { required: true, message: '请输入节点名称' } ] } ]" /> </a-form-item> <a-form-item label="关联角色"><a-select @change="handleRoleChange" v-decorator="[ 'role', { rules: [ { required: true, message: '请输入关联角色' } ] } ]" placeholder="请选择角色"><a-select-optionv-for="role in roles":key="role.value":value="role.value">{{ role.label }}</a-select-option></a-select></a-form-item> </a-form> </a-modal> </div>
</template> <script> import { Graph, Shape } from '@antv/x6'; Graph.registerNode('custom-node',{width: 200,height: 60,attrs: {body: {stroke: '#5F95FF',strokeWidth: 1,fill: 'rgba(95,149,255,0.05)',refWidth: 1,refHeight: 1,},image: {'xlink:href': require('../assets/shenqingren.png'),width: 16,height: 16,x: 12,y: 12,},title: {text: '123',refX: 40,refY: 14,fill: 'rgba(0,0,0,0.85)',fontSize: 12,'text-anchor': 'start',},text: {text: '456',refX: 40,refY: 38,fontSize: 12,fill: 'rgba(0,0,0,0.6)','text-anchor': 'start',},},markup: [{tagName: 'rect',selector: 'body',},{tagName: 'image',selector: 'image',},{tagName: 'text',selector: 'title',},{tagName: 'text',selector: 'text',},],},true,
)export default { data() { return { graph: null, editModalVisible: false, editForm: this.$form.createForm(this), currentNode: {},title: '修改节点',roles: [{ label: "发起人角色", value: "applicant", icon: require('../assets/shenqingren.png') },{ label: "部门主管", value: "manager", icon: require('../assets/bumenshenhe.png') },{ label: "财务", value: "finance", icon: require('../assets/caiwushenhe.png') },{ label: "总经理", value: "ceo", icon: require('../assets/zongjinglishenhe.png') },],selectedIcon: '', // 用于存储选中的角色的图标nodes: [ { id: 'start', name: '发起人', role: 'applicant', icon: require('../assets/shenqingren.png'), x: 100, y: 50 } ] }; }, mounted() { // 初始化X6图形 this.initGraph(); // 渲染节点 this.renderNodes();this.editForm = this.$form.createForm(this);}, methods: { initGraph() { // 创建X6图形实例 this.graph = new Graph({ container: this.$refs.graphContainer, grid: true, connecting: { snap: true }, interacting: { nodeMovable: false } }); // 监听节点点击事件 this.graph.on('node:dblclick', ({ node }) => {this.openEditModal(node); }); // 监听节点右键事件 this.graph.on('node:contextmenu', ({ node, e }) => { console.log(e)if (node.id === 'start') { return} // 删除节点 if (confirm('确定要删除该节点吗?')) { this.nodes = this.nodes.filter(n => n.id!== node.id); this.renderNodes(); } }); }, renderNodes() { // 清空现有图形 this.graph.clearCells(); // 依次添加节点并连线 for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]this.graph.addNode({id: node.id,x: node.x,y: node.y,shape: 'custom-node',attrs: {title: {text: `${node.name}`, // 直接设置 text 属性},text: {text: `${node.role}`, // 直接设置 text 属性},image: {'xlink:href': node.icon, // 使用节点的 icon 属性width: 16,height: 16,x: 12,y: 12,},},})if (i > 0) { const prevNode = this.nodes[i - 1]; const edge = new Shape.Edge({ source: prevNode.id, target: node.id }); this.graph.addEdge(edge); } } }, handleRoleChange(value) {// 当用户选择角色时,根据值查找角色对象this.selectedIcon = this.roles.find(role => role.value === value).icon;},openEditModal(node) { // 打开修改节点模态框 this.editModalVisible = true; this.title = '修改节点';if(!node.id){this.title = '添加节点';this.currentNode = {};return}this.currentNode = this.nodes.find(n => n.id === node.id); this.selectedIcon = this.currentNode.iconthis.$nextTick(() => {this.editForm.setFieldsValue({name: this.currentNode.name,role: this.currentNode.role});});}, handleEditOk() { // 处理修改节点确认 this.editForm.validateFields((err, values) => { if (err) { return} if(this.currentNode.id){this.currentNode.name = values.name; this.currentNode.role = values.role;this.currentNode.icon = this.selectedIcon; this.renderNodes(); this.editModalVisible = false; this.editForm.resetFields();}else{ const lastNode = this.nodes[this.nodes.length - 1]; const newNode = { id: `node-${Date.now()}`, name: values.name, role: values.role,icon: this.selectedIcon, x: lastNode.x + 300, y: lastNode.y }; this.nodes.push(newNode); }this.renderNodes(); this.editModalVisible = false; this.editForm.resetFields();}); }, handleEditCancel() { // 处理修改节点取消 this.editModalVisible = false; this.editForm.resetFields(); }, saveProcess() { // 保存流程,这里可以添加保存到后端的逻辑 console.log(' 保存流程:', this.nodes); } }
};
</script> <style scoped>
/* 可根据需要添加自定义样式 */
</style>
运行效果:
1)点击按钮添加节点
2)双击节点弹出编辑
3)右击节点删除