【需求】
在antdesign中的a-tree组件时,为每个节点右侧添加了复选框以及多选操作,由于ant官方tree的逻辑并不支持项目的业务逻辑,因此手撸了这边的逻辑,过程中遇到了两个问题。如下图所示:
【解决办法】
问题1,为右侧复选框区域添加sticky布局即可,代码如下:
<div
:class="['check-box',
viewParam.checkable&&viewParam.treeSelectable ? 'check-box-2' : !viewParam.checkable && !viewParam.treeSelectable ? 'check-box-0' : 'check-box-1']">
...
</div>
.check-box {
position: sticky;
background-color: #fafbfc;
padding-right: 10px;
margin-top: -24px;
text-align: right;
}
.check-box-1 {
width: 30px;
left: 220px;
}
.check-box-2 {
width: 50px;
left: 204px;
}
问题2,涉及到动态变化,一开始准备通过变量统一改改变,但每个树的节点,位置状态都不同,无法进行统一设置,因此想到了自定义指令监听,代码如下:
<a-tree
id="wd-org-tree-inside"
v-if="renderComponent"
v-model="valueCode"
v-titleBoxChange="titleBoxChange"
v-selectOrgChange="selectOrgChange"
showIcon
showLine
:tree-data="treeData"
:replaceFields="{ key: 'id', title: 'title', children: 'children' }"
:load-data="onLoadData"
:expandedKeys="expandedKeys"
:multiple="viewParam.multiple"
:checkable="false"
:selectable="viewParam.selectable"
:defaultSelectedKeys="viewParam.defaultSelectedKeys"
:selectedKeys="selectedKeys"
:draggable="viewParam.draggable"
@expand="onExpand"
@select="onSelect"
@load="onLoad"
>
<template slot-scope="item" slot="custom">
<div :class="['title-box']" v-on:mouseover="handleListFocus(item)">
<!--字体、logo颜色规则: 绿色代表虚拟机构 灰色代表失效状态 蓝色代表待生效状态; 虚拟和生效中则显示虚拟部门的颜色; 虚拟和失效则显示虚拟的颜色、失效标签用失效颜色; 待生效和虚拟则显示虚拟颜色、未来标签用未来颜色 -->
<span
:class="{
'color-blue':
item.status == '20' && (item.type == '10' || item.type == '20'),
'color-aaa':
item.status == '30' && (item.type == '10' || item.type == '20'),
'green-color': item.type == '50',
'color-freeze': item.b05214 == '20', // 冻结
'color-purple' :item.b00208 == '1', // 境外
'color-gray': item.status == '30' // 失效
}"
>
<!-- <span v-if="pid == item.pId"> -->
<span v-if="pid == item.id">
<!--最上层节点前展示此logo-->
<icon-font class="ico" type="icon-shu-jituan"></icon-font>
</span>
<span v-else>
<!--其它节点前展示logo: 机构使用机构图标,部门和虚拟节点用部门图标-->
<icon-font v-if="item.type === '10'" type="icon-shu-jigou"></icon-font>
<icon-font v-if="item.type === '20' || item.type === '50'" type="icon-shu-bumen"></icon-font>
<icon-font v-if="item.type === '30'" type="icon-shu-gangwei"></icon-font>
</span>
<!-- {{ item.title }} -->
<span
v-html="
item.title.replace(
new RegExp(queryParam.searchStr, 'g'),
'<span style=color:#f50>' + queryParam.searchStr + '</span>'
)
"
></span>
</span>
<span v-if="item.b05214 == '20'">
<i class="btn-freeze">冻结</i>
</span>
<span v-if="item.type == '50'">
<!--节点类型为50,则显示‘虚拟’-->
<i class="btn-br">虚拟</i>
</span>
<span v-if="item.status == '20'">
<!--节点状态为20,则显示‘未来’-->
<i class="btn-tree">未来</i>
</span>
<span v-else-if="item.status == '30'">
<!--节点状态为30,则显示‘失效’-->
<i class="btn-no">失效</i>
</span>
<span v-if="item.b00208 == '1'">
<i class="btn-abroad">境外</i>
</span>
<div
:class="['check-box', viewParam.checkable&&viewParam.treeSelectable ? 'check-box-2' : !viewParam.checkable && !viewParam.treeSelectable? 'check-box-0' : 'check-box-1']"
>
<a-popover
v-if="viewParam.treeSelectable"
:arrowPointAtCenter="true"
:getPopupContainer="
triggerNode => {
return triggerNode.parentNode.parentNode
}
"
>
<template slot="content">
<div class="trigger-select-box">
<div
v-for="(operator, index) in triggerSelectOptions"
:key="operator+index"
class="trigger-select-item"
@click.stop="handeleSelect(operator.value, item)"
>{{ operator.text }}</div>
</div>
</template>
<icon-font v-show="item.isShowIcon" class="trigger-icon" type="icon-piliangxuanze"></icon-font>
</a-popover>
<a-checkbox v-if="viewParam.checkable" v-model="item.isChecked"></a-checkbox>
</div>
</div>
</template>
</a-tree>
以v-titleBoxChange="titleBoxChange"为例,其他可以不用过多关注
包含三部分
1、DOM绑定,即 v-titleBoxChange="titleBoxChange"
2、注册指令
在directives中注册,directives和methods、mounted等同级。如下代码所示,bind中进行绑定,设置定时器,一旦有变化就通过binding.value({})回调函数进行更新。
3、函数中输出DOM变化
directives: {
titleBoxChange: {
bind(el, binding) {
console.log(el, '绑定', binding)
let titleBoxDomLength = 0
function titleBoxDomLengthChange() {
const titleBoxDom = el.getElementsByClassName('title-box')
if (titleBoxDomLength != titleBoxDom?.length) {
binding.value({
titleBoxDomLength: titleBoxDom?.length ? titleBoxDom.length : 0, // 关键(这传入的是函数,所以执行此函数)
titleBoxDom: titleBoxDom,
})
}
titleBoxDomLength = titleBoxDom?.length ? titleBoxDom.length : 0
}
el.__vueSetInterval__ = setInterval(titleBoxDomLengthChange, 300)
},
unbind(el) {
console.log(el, '解绑')
clearInterval(el.__vueSetInterval__)
},
},
},
methods: {
titleBoxChange({ titleBoxDomLength, titleBoxDom }) {
let maxWidth = 0
// 获取最大宽度
for (let i = 0; i < titleBoxDomLength; i++) {
const spanDom = titleBoxDom[i].firstChild
maxWidth =
maxWidth > spanDom.offsetWidth ? maxWidth : spanDom.offsetWidth
}
maxWidth += 100
// 更新宽度
for (let i = 0; i < titleBoxDomLength; i++) {
let moveLeft = 0
let tempDom = titleBoxDom[i].parentNode.parentNode.parentNode.parentNode // 最近的url
while (tempDom.tagName == 'UL') {
moveLeft += parseFloat(
window
.getComputedStyle(tempDom, null)
.getPropertyValue('padding-left')
)
tempDom = tempDom.parentNode.parentNode
}
titleBoxDom[i].setAttribute('style', `width:${maxWidth - moveLeft}px`)
}
this.setSelectDomWidth() // 设置选中状态
},
}