html
复制代码
<template>
<div class="TreeRight" style="width: 100%;height: 100%">
<div v-if="list.length" class="childS">
<div
v-drag
v-for="(item,index) in list"
id="child"
:key="item.id +'-child-'+index"
:class="{ mindRank: isRank }"
class="child">
<div
:style="{marginRight: item.children && item.children.length > 1 ? '20px' :'',opacity:item.isShow?1:0}"
class="child-item"
>
<div :id="item.id" class="childName">
<el-card class="childName-card">
<div v-if="isShowTitle" class="card-title">
<el-tooltip :content="item.label" class="item" effect="dark" placement="top">
<span>{{ item.label }}</span>
</el-tooltip>
<i v-show="!readOnly" class="el-icon-s-tools" style="float: right; padding: 3px 0;cursor: pointer" @click.stop="toolsClick(item)"/>
</div>
<component
:key="item.id"
:is="childComponent"
:data="item"
/>
</el-card>
<i v-show="!(item.children && item.children.length===0)&&!isShowBorderRight(item)&&!readOnly" class="el-icon-circle-plus-outline" @click.stop="spreadChild(item)"/>
<i v-show="!(item.children && item.children.length===0)&&isShowBorderRight(item)&&!readOnly" class="el-icon-remove-outline" @click.stop="foldChild(item)"/>
<div v-if="list.length>1" class="position-arrow">
<i class="el-icon-right"/>
</div>
</div>
<div v-if="item.children && item.children.length&&isShowBorderRight(item)" class="position-arrow">
<i class="el-icon-right"/>
</div>
</div>
<div v-if="item.children && item.children.length" class="child-children">
<VMindRowTree :list="item.children" :child-component="childComponent" :is-rank="isRank" :is-show-title="isShowTitle" :read-only="readOnly" v-on="$listeners"/>
</div>
<div
v-show="isFirst(item.id) && domMounted&&item.isShow"
class="borderLeftFirst"
/>
<div
v-show="isLast(item.id)&&item.isShow"
class="borderLeftLast"
/>
<div
v-show="!(isFirst(item.id) && domMounted)&&!isLast(item.id)&&item.parentId&&item.isShow"
class="borderLeftNormal"
/>
</div>
</div>
<div v-else>
<el-empty description="空空如也"/>
</div>
</div>
</template>
<script>
export default {
// 右侧树状的vMind
name: 'VMindRowTree',
components: {},
directives: {
drag: {
bind: function(el) {
const mindDiv = el
mindDiv.onmousedown = (e) => {
const arr = Array.from(mindDiv.classList)
if (!arr.includes('mindRank')) return
const disX = e.clientX - mindDiv.offsetLeft
const disY = e.clientY - mindDiv.offsetTop
console.log('offsetLeft和offsetTop', mindDiv.offsetLeft, mindDiv.offsetTop)
console.log('clientX和clientY', e.clientX, e.clientY)
document.onmousemove = (e) => {
const left = e.clientX - disX
const top = e.clientY - disY
mindDiv.style.left = left + 'px'
mindDiv.style.top = top + 'px'
}
document.onmouseup = (e) => {
document.onmousemove = null
document.onmouseup = null
}
}
}
}
},
props: {
// 数组
list: {
type: Array,
default: () => []
},
// 子组件
childComponent: {
type: Object,
required: true
},
// 是否显示title
isShowTitle: {
type: Boolean,
default: false
},
// 是否只读
readOnly: {
type: Boolean,
default: false
},
// 开启移动
isRank: {
type: Boolean,
default: false
}
},
data() {
return {
domMounted: false,
listNow: []
}
},
mounted() {
this.$nextTick(() => {
this.domMounted = true
})
},
methods: {
// 子项的isShow都为true显示
isShowBorderRight(node) {
if (node.children && node.children.length) {
return node.children.every((x) => x.isShow === true)
} else {
return false
}
},
// 同一个树杈的第一个元素
isFirst(id) {
return (
this.list.length > 1 && this.list.map((x) => x.id).indexOf(id) === 0
)
},
// 同一个树杈的最后一个元素
isLast(id) {
return (
this.list.length > 1 &&
this.list.map((x) => x.id).indexOf(id) === this.list.length - 1
)
},
// 展开
spreadChild(node) {
new Promise((resolve, reject) => {
// 展开的异步操作
this.$emit('spreadOrFoldChild', { node: node, type: 'spread' }, resolve)
}).then((res) => {
res && this.spreadOrFold(node, 'spread')
})
},
// 折叠
foldChild(node) {
new Promise((resolve, reject) => {
// 展开的异步操作
this.$emit('spreadOrFoldChild', { node: node, type: 'fold' }, resolve)
}).then((res) => {
res && this.spreadOrFold(node, 'fold')
})
},
// 折叠或展开方法
spreadOrFold(node, type) {
if (type === 'spread') {
// 展开当前节点下第一层
node.children.forEach((item) => {
item.isShow = true
})
} else if (type === 'fold') {
// 折叠当前节点下所有层
node.children.forEach((item) => {
item.isShow = false
if (item.children && item.children.length > 0) {
this.spreadOrFold(item, 'fold')
}
})
}
},
// 标题的事件
toolsClick(node) {
this.$emit('titleClick', node)
}
}
}
</script>
<style lang="scss" scoped>
.TreeRight {
display: flex;
.childS {
.child {
width: 100%;
display: flex;
background-color: #fff;
position: relative;
.borderLeftNormal,.borderLeftLast{
&:after{
content: "";
width: 1px;
height: 50%;
border-left: solid 1px #606266;
white-space: nowrap;
display: inline-block;
position: absolute;
left: -20px;
top: 0;
}
}
.borderLeftNormal,.borderLeftFirst{
&:before{
content: "";
width: 1px;
height: 50%;
border-left: solid 1px #606266;
white-space: nowrap;
display: inline-block;
position: absolute;
left: -20px;
bottom: 0;
}
}
.child-item {
display: flex;
align-items: center;
margin: 10px 0;
transition: opacity 0.2s linear;
.childName {
height: 100%;
display: flex;
align-items: center;
width: 450px;
text-align: center;
justify-content: center;
position: relative;
padding: 10px 0;
.position-arrow {
position: absolute;
left: -22px;
}
//.position-top {
// position: absolute;
// width: 3px;
// background-color: #fff;
// left: -23px;
// height: 10px;
//}
.childName-card{
height: auto;
width: 100%;
overflow: auto;
::v-deep.el-card__body{
padding: 8px 15px;
}
}
.childArrow {
width: 1px;
height: 100%;
background-color: black;
position: absolute;
display: flex;
align-items: center;
top: 0;
right: -16px;
}
}
}
.mindRank {
cursor: move;
}
}
.child-children {
display: flex;
flex-direction: column;
justify-content: center;
}
}
}
</style>
html
复制代码
<template>
<div v-loading="loading" class="vMind-warp" style="width: 100%;height: 100%">
<div class="header">
<div>
<el-input-number
v-model="num"
:precision="2"
:step="0.1"
:max="2"
:min="0"
style="width: 100px"
size="mini"
controls-position="right"
@change="numberChange"
/>
倍
</div>
<div>
<el-tooltip :content="'点击开启或关闭移动模式'" effect="dark" placement="top">
<el-button
:type="isRank ? 'primary' : ''"
icon="el-icon-rank"
circle
@click="rankFn"
/>
</el-tooltip>
</div>
<!-- <div>-->
<!-- <el-button-->
<!-- icon="el-icon-refresh"-->
<!-- circle-->
<!-- @click="refresh"-->
<!-- />-->
<!-- </div>-->
</div>
<div ref="refresh" class="mind">
<vMindRowTree
:is-rank="isRank"
:list="list"
:read-only="readOnly"
:child-component="component"
:is-show-title="isShowTitle"
:style="'transform: scale(' + num + ')'"
v-on="$listeners"/>
</div>
</div>
</template>
<script>
import vMindRowTree from '@/components/VmindRowTree/index'
export default {
name: 'VMindRowTreeWrap',
components: { vMindRowTree },
props: {
// 数组
listCache: {
type: Array,
default: () => []
},
// 子组件
childComponent: {
type: Object,
required: true
},
// 是否显示title
isShowTitle: {
type: Boolean,
default: false
},
// 是否只读
readOnly: {
type: Boolean,
default: false
}
},
data() {
return {
component: '',
isRank: false,
loading: false,
list: [],
num: 1
}
},
computed: {},
watch: {
num(newVal, oldVal) {
console.log(newVal, oldVal)
if (newVal < oldVal && newVal <= 0.5) {
this.num = 0.5
}
},
// 监听变化做初始化处理
listCache: {
handler() {
this.init()
},
immediate: true,
deep: true
}
},
created() {},
mounted() {
this.init()
},
methods: {
// 移动控制
rankFn() {
this.isRank = !this.isRank
},
numberChange() {
console.log(' this.num--', this.num)
},
// 初始化
init() {
const { listCache, childComponent } = this
this.list = JSON.parse(JSON.stringify(listCache))
this.component = childComponent
}
// 刷新
// refresh() {
// const { list, childComponent } = this
// this.list = JSON.parse(JSON.stringify(list))
// console.log('最新的', this.list)
// this.component = childComponent
// }
}
}
</script>
<style scoped lang="scss">
.vMind-warp{
width: 100%;
height: 100%;
position: relative;
.header{
position: absolute;
top: 0;
left: 0;
display: inline-block;
align-items: center;
z-index: 2;
height: 40px;
line-height: 40px;
background-color: #fff;
& > div {
display: inline-block;
margin-right: 20px;
}
}
.mind {
height: calc(100% - 40px);
width: 100%;
position: absolute;
user-select: none;
background-color: #fff;
}
}
</style>
json
复制代码
vMindData: [{
label: '根节点',
id: 1,
isShow: true,
parentId: null,
salesMetricsCompletions: [{ name: '张三', age: 16 }],
children: [
{ label: '1-1节点', id: 2, isShow: true, parentId: 1, salesMetricsCompletions: [{ name: '张三', age: 16 }],
children: [{ label: '1-1-1节点', id: 6, isShow: true, parentId: 2, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [
{ label: '1-1-1-1节点', id: 10, isShow: true, parentId: 6, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] },
{ label: '1-1-1-2节点', id: 11, isShow: true, parentId: 6, salesMetricsCompletions: [{ name: '张三', age: 16 }],
children: [{ label: '1-1-1-2-1节点', id: 12, isShow: true, parentId: 11, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }, { label: '1-1-1-2-2节点', id: 15, isShow: true, parentId: 11, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }] }]
}, { label: '1-1-2节点', id: 9, isShow: true, parentId: 2, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }]
},
{ label: '1-2节点', id: 3, isShow: true, parentId: 1, salesMetricsCompletions: [{ name: '张三', age: 16 }],
children: [{ label: '1-2-1节点', id: 7, isShow: true, parentId: 3, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }, { label: '1-2-2节点', id: 13, isShow: true, parentId: 3, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }] },
{ label: '1-3节点', id: 4, level: 1, isShow: true, parentId: 1, salesMetricsCompletions: [{ name: '张三', age: 16 }],
children: [{ label: '1-3-1节点', id: 8, isShow: true, parentId: 4, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }, { label: '1-3-2节点', id: 14, isShow: true, parentId: 4, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }]
},
{ label: '1-4节点', id: 5, isShow: true, parentId: 1, salesMetricsCompletions: [{ name: '张三', age: 16 }], children: [] }]
}]