挨踢老王
vue流程图-纯css实现流程图-弹框里使用流程图
参考链接》 css+vue实现流程图
1.组件里创建tree.vue
<template>
<div :ref="treeRefName" :class="treeClassName">
<div
v-for="item in convertData"
:key="item.uuid"
:class="[
isChild ? 'process-tree-childNodes-row' : 'process-tree-roots',
isChild && item.isLong ? 'long-with-line' : '',
]"
:style="isChild ? {} : rootStyle"
>
<div class="line" v-if="item.isLong"></div>
<span class="process-tree-node" :class="isLeaftNode(item)">{{
item.name
}}</span>
<div
class="process-tree-childNodes"
v-if="item.children"
:class="item.children.length > 1 ? 'multiply-node' : 'single-node'"
>
<processTree :data="item.children || []" :isChild="true" />
</div>
</div>
</div>
</template>
<script>
import uuidv4 from "uuid";
export default {
name: "processTree",
props: {
data: {
type: Array,
default: () => {
return [];
},
},
isChild: {
type: Boolean,
default: false,
},
},
data() {
return {
convertData: this.convert(this.data),
rootStyle: {},
};
},
watch: {
data() {
this.convertData = this.convert(this.data);
},
},
computed: {
treeRefName() {
return this.isChild ? "childTree" : "baseTree";
},
treeClassName() {
return this.isChild ? "" : "process-tree";
},
},
methods: {
initDomWidth() {
let leafs = document.getElementsByClassName("leaf-node");
leafs = Array.from(leafs);
leafs = leafs.map((i) => {
let total = this.getOffset(i, "offsetLeft");
return total;
});
this.rootStyle = { width: Math.max(...leafs) * 1.5 + "px" };
},
getOffset(obj, offsetDir) {
var realNum = obj[offsetDir];
var positionParent = obj.offsetParent; //获取上一级定位元素对象
while (positionParent != null) {
realNum += positionParent[offsetDir];
positionParent = positionParent.offsetParent;
}
return realNum;
},
convert(arr) {
return arr.map((item) => {
item.uuid = uuidv4();
if (item.children && item.children.length > 0) {
item.children = this.convert(item.children);
}
return item;
});
},
isLeaftNode(data) {
return data.children && data.children.length > 0 ? "" : "leaf-node";
},
},
};
</script>
<style scoped>
.process-tree {
padding: 10px;
/* overflow: scroll; */
padding-bottom: 27px;
width: 100%;
padding-right: 0;
font-size: 0;
line-height: 0;
}
.process-tree-roots {
width: 250%;
margin-bottom: 20px;
}
.single-node::before {
content: "";
display: block;
position: absolute;
width: 23px;
height: 3px;
background: rgba(203, 221, 238, 1);
left: -23px;
top: 50%;
}
.multiply-node::before {
content: "";
display: block;
position: absolute;
width: 3px;
height: 100%;
background: rgba(203, 221, 238, 1);
left: -23px;
top: 0;
}
.process-tree-node {
position: relative;
padding: 6px 10px;
background: rgba(203, 221, 238, 1);
border-radius: 2px;
color: #333;
display: inline-block;
cursor: pointer;
min-width: 80px;
text-align: center;
font-size: 12px;
line-height: 1.8em;
vertical-align: middle;
min-height: 20px;
}
.process-tree-node::after {
content: "13";
display: block;
width: 20px;
height: 3px;
background: rgba(203, 221, 238, 1);
position: absolute;
left: 100%;
top: 50%;
}
.leaf-node::after {
display: none;
}
.process-tree-childNodes {
position: relative;
display: inline-block;
vertical-align: middle;
margin-left: 43px;
top: -0.5px;
}
.process-tree-childNodes > div {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.multiply-node .process-tree-childNodes-row::before {
content: "12";
display: block;
position: absolute;
width: 20px;
height: 3px;
background: rgba(203, 221, 238, 1);
left: -20px;
top: 50%;
font-size: 14px;
}
.multiply-node .process-tree-childNodes-row:first-child::after,
.multiply-node .process-tree-childNodes-row:last-child::after {
content: "";
position: absolute;
display: block;
width: 4px;
height: 50%;
background: #fff;
left: -23px;
}
.multiply-node .long-with-line:first-child::after,
.multiply-node .long-with-line:last-child::after {
left: -166px;
}
.multiply-node .process-tree-childNodes-row:first-child::after {
top: 0px;
}
.multiply-node .process-tree-childNodes-row:last-child::after {
bottom: -4px;
}
.process-tree-childNodes-row {
position: relative;
margin-bottom: 10px;
}
.process-tree-childNodes-row:last-child {
margin-bottom: 0;
}
.long-with-line {
margin-left: 142px;
}
.line {
position: absolute;
width: 142px;
height: 3px;
background-color: rgba(203, 221, 238, 1);
top: 50%;
left: -161px;
}
</style>
2.组件里创建treefa.vue
<template>
<div class="tree-wrap">
<ProcessTree ref="tree" :data="treeData" />
</div>
</template>
<script>
import ProcessTree from "../components/tree.vue";
export default {
props: {
treeData: {
type: Array,
default: () => {
return [];
},
},
},
data() {
return {};
},
mounted() {
this.$refs.tree.initDomWidth();
},
watch: {
treeData() {
this.$nextTick(this.$refs.tree.initDomWidth);
},
},
directives: {
dragabled: {
bind(el, binding, vnode, oldVnode) {
if (!binding) return;
el.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
let disX = e.clientX;
let disY = e.clientY;
el.style.cursor = "move";
document.onmousemove = function (e) {
e.preventDefault(); // 移动时禁用默认事件
// 通过事件委托,计算移动的距离
const left = e.clientX - disX;
disX = e.clientX;
el.scrollLeft += -left;
const top = e.clientY - disY;
disY = e.clientY;
el.scrollTop += -top;
};
document.onmouseup = function (e) {
el.style.cursor = "auto";
document.onmousemove = null;
document.onmouseup = null;
};
};
},
},
},
components: {
ProcessTree,
},
};
</script>
<style scoped>
.tree-wrap {
/* position: absolute;
overflow: hidden; */
width: 100%;
height: 100%;
/* top: 0;
left: 0; */
}
.tree-wrap > div {
width: calc(100% + 17px);
height: calc(100% + 17px);
}
</style>
3.页面弹框使用案例
<template>
<div class="tree-wrap">
<el-button type="text" @click="dialogVisible = true"
>点击打开 Dialog</el-button
>
<el-dialog
title="提示"
:visible.sync="dialogVisible"
:before-close="handleClose"
>
<Treefa ref="tree" :treeData="treeData" />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false"
>确 定</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
import Treefa from "../components/treefa.vue";
export default {
components: {
Treefa,
},
data() {
return {
dialogVisible: false,
treeData: [
{
name: "节点1",
children: [
{
name: "节点2",
children: [{ name: "节点3", children: [{ name: "节点3" },{ name: "节点3" }] }],
},
],
},
{
name: "节点1",
children: [
{ name: "节点5" },
{ name: "节点5", isLong: true },
{
name: "节点26",
children: [
{ name: "节点3" },
{ name: "节点3" },
{
name: "节点33",
children: [
{ name: "节点3" },
{ name: "节点3" },
{ name: "节点3" },
],
},
],
},
{ name: "节点3", children: [{ name: "节点3" }, { name: "节点3" }] },
{ name: "节点4", children: [{ name: "节点3" }] },
{ name: "节点5", isLong: true },
],
},
],
};
},
methods: {
handleClose(done) {
this.$confirm("确认关闭?")
.then((_) => {
done();
})
.catch((_) => {});
},
},
watch: {},
};
</script>
<style scoped>
</style>