<template>
|
<vue-flowchart-editor ref="flowChart" class="vue-flowchart-editor">
|
<div class="vfe-chart">
|
<!-- 顶部菜单 -->
|
<div v-if="type !== 'detail'" class="vfe-chart-header">
|
<editor-toolbar @handler-save="handlerSave" @reset-tree="handleResetTree" :chart-data="chartData"/>
|
</div>
|
<div class="vfe-chart-container">
|
<!-- 左侧项目栏 -->
|
<div v-if="type !== 'detail'" class="vfe-chart-sidebar">
|
<editor-item-panel :node-items="chartDataNodeItems"/>
|
</div>
|
<!-- 主图表 -->
|
<div class="vfe-chart-main">
|
<flow
|
:data="chartData"
|
:onAfterChange="onAfterChange"
|
:onAfterItemSelected="onAfterItemSelected"
|
/>
|
<!-- 提示框 -->
|
<div class="tooltip">
|
<template v-for="item in tooltipData">
|
<p>{{ item.name }}: {{ item.value }}</p>
|
</template>
|
</div>
|
</div>
|
|
<div v-if="type !== 'detail'" class="vfe-chart-panel">
|
<div class="vfe-chart-panel-detail">
|
<editor-detail-panel
|
ref="EditorDetailPanel"
|
:currentSelectedLine="currentSelectedLine"
|
:existEdges="existEdges"
|
:existNodes="existNodes"
|
:rowData="rowData"
|
:type="type"
|
/>
|
</div>
|
</div>
|
|
</div>
|
</div>
|
|
<!-- 右键菜单 -->
|
<editor-context-menu v-if="type !== 'detail'"/>
|
<!-- 自定义边配置 -->
|
<register-edge
|
:config="customEdgeConfig"
|
extend="flow-polyline"
|
name="custom-polyline"
|
/>
|
<!-- 自定义命令组件 -->
|
<custom-command :download="downloadImage"/>
|
</vue-flowchart-editor>
|
</template>
|
|
<script>
|
import VueFlowchartEditor, {Flow, RegisterEdge} from "vue-flowchart-editor";
|
import EditorToolbar from "./components/Toolbar";
|
import EditorItemPanel from "./components/ItemPanel";
|
import EditorDetailPanel from "./components/DetailPanel";
|
import EditorMinimap from "./components/EditorMinimap";
|
import EditorContextMenu from "./components/ContextMenu";
|
import CustomCommand from "./components/CustomCommand";
|
|
export default {
|
name: "FlowchartEditor",
|
|
components: {
|
VueFlowchartEditor,
|
Flow,
|
EditorToolbar,
|
EditorItemPanel,
|
EditorDetailPanel,
|
EditorMinimap,
|
EditorContextMenu,
|
CustomCommand,
|
RegisterEdge,
|
},
|
|
props: ["chartData", "chartDataNodeItems", "saveData", "rowData", "type"],
|
|
data() {
|
return {
|
// 当前图表的数据
|
flowChartData: {},
|
// 自定义边的配置
|
customEdgeConfig: {
|
getActivedStyle(/*item*/) {
|
return {
|
lineWidth: 3, // 活跃状态下的边样式
|
};
|
},
|
getSelectedStyle(/*item*/) {
|
return {
|
lineWidth: 3, // 选中状态下的边样式
|
};
|
},
|
},
|
// 工具提示显示开关
|
tooltipShow: true,
|
// 工具提示数据
|
tooltipData: [],
|
// 已存在的节点和边
|
existNodes: this.chartData.nodes || [],
|
existEdges: this.chartData.edges || [],
|
// 当前选中的边
|
currentSelectedLine: {}
|
};
|
},
|
|
mounted() {
|
// 组件挂载完成后,自动缩放画布(仅在添加模式下)
|
if (this.type === "add") {
|
this.$nextTick(() => {
|
this.$refs.flowChart.propsAPI.executeCommand("autoZoom");
|
});
|
}
|
},
|
|
methods: {
|
update(){
|
this.$refs.flowChart.propsAPI.read(this.flowChartData);
|
},
|
// 处理图表数据更改的事件
|
onAfterChange(e) {
|
try {
|
// 如果添加了节点且节点没有被更改过
|
if (e.action === "add" && e.model.type === "node") {
|
if (!e.model.change) {
|
this.$refs.flowChart.propsAPI.remove(e.item); // 移除原有的节点
|
e.model.id = e.model.label; // 使用标签作为节点的 ID
|
e.model.change = true;
|
this.$refs.flowChart.propsAPI.add("node", e.model); // 添加新的节点
|
}
|
}
|
} catch (err) {
|
// 处理节点 ID 冲突错误
|
if (
|
err.message ===
|
`id:${e.model.label} has already been set, please set new one`
|
) {
|
this.$message.error("不能添加已存在的节点!");
|
}
|
}
|
// 保存当前的节点和边数据
|
const {nodes, edges} = this.$refs.flowChart.propsAPI.save();
|
this.existNodes = nodes || [];
|
this.existEdges = edges || [];
|
},
|
// 处理选中线变化的事件 回填当前线信息
|
onAfterItemSelected({item}) {
|
if (item.target) {
|
// 查找当前选中的边
|
const currentEdge = this.existEdges.filter(itm => itm.id === item.id)[0] || {};
|
if (!Array.isArray(currentEdge.events)) {
|
currentEdge.events = []
|
}
|
this.currentSelectedLine = currentEdge;
|
}
|
},
|
|
// 下载图片
|
_downloadImage(data, filename = "flowchart.png") {
|
const a = document.createElement("a");
|
a.href = data;
|
a.download = filename;
|
document.body.appendChild(a);
|
a.click();
|
},
|
|
// 调用下载图片方法
|
downloadImage() {
|
const page = this.$refs["flowChart"].propsAPI.editor.getCurrentPage();
|
this._downloadImage(page.saveImage().toDataURL("image/png"));
|
},
|
// 获取新的行数据(从详细面板中)
|
getNewRowData() {
|
return this.$refs.EditorDetailPanel.getNewRowDate();
|
},
|
// 获取当前图表的数据
|
getFlowData() {
|
return this.$refs.flowChart.propsAPI.save();
|
},
|
// 获取边事件列表(从详细面板中)
|
getEdgesEvents() {
|
return this.$refs.EditorDetailPanel.edgeEventList;
|
},
|
|
// 调用父组件重置
|
handleResetTree(){
|
this.$emit('reset-tree');
|
},
|
handlerSave(){
|
this.$emit('handler-save')
|
}
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.vue-flowchart-editor {
|
display: flex;
|
flex: 1;
|
flex-direction: column;
|
width: 100%;
|
height: 100%;
|
background: #fff;
|
}
|
|
.vfe-chart {
|
position: relative;
|
width: 100%;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
|
.vfe-chart-header {
|
border: 1px solid #e6e9ed;
|
padding: 8px;
|
}
|
|
.vfe-chart-container {
|
flex: 1;
|
display: flex;
|
height: 550px;
|
overflow: hidden;
|
|
.vfe-chart-main {
|
position: relative;
|
flex: 1;
|
max-height: calc(100% - 5px); // fix scroll show
|
|
.tooltip {
|
position: absolute;
|
display: none;
|
top: 0;
|
left: 0;
|
width: 100px;
|
height: auto;
|
padding: 15px;
|
border-radius: 10px;
|
z-index: 999;
|
opacity: 0.8;
|
color: #ffffff;
|
font-size: 12px;
|
background-color: #000;
|
|
p {
|
margin: 0;
|
}
|
}
|
}
|
|
.vfe-chart-sidebar {
|
margin-top: 10px;
|
position: relative;
|
display: flex;
|
justify-content: center;
|
width: 16%;
|
background-color: #fafafa;
|
border-right: 1px solid #e6e9ed;
|
}
|
|
.vfe-chart-panel {
|
position: relative;
|
width: 300px;
|
background-color: #fafafa;
|
border-left: 1px solid #e6e9ed;
|
overflow-y: scroll;
|
|
.vfe-chart-panel-detail {
|
box-sizing: border-box;
|
padding: 10px;
|
}
|
}
|
}
|
}
|
</style>
|