项目场景:
需求:一个下拉框,显示如图内容,点击确认选中数据展示在下拉框中,点击取消保持原数据不变,标签与树联动。
原因分析:
使用select与tree结合,不能完全满足设计图效果。所以选择自定义封装组件,
代码如下(小白一枚,欢迎指正):
<template>
<div>
<div ref="frame" class="select-frame" @click="showPanel = !showPanel;">
<span v-if="sureName != ''" class="text">{{ sureName }}</span>
<span v-else class="text" placeholder="请选择"></span>
<i class="el-icon-arrow-down" :class="[showPanel ? 'is' : '']"></i>
</div>
<div ref="panel" class="select-panel" v-show="showPanel">
<el-input v-model="filterText" placeholder="搜索关键字"></el-input>
<span class="tag-title">已选<i>{{ valueName.length }}</i>人</span>
<div class="tag-list">
<el-tag v-for="(item, index) in valueName" :key="index" closable @close="handleChangeTag(item)">{{
item.label }}</el-tag>
</div>
<el-tree ref="tree" class="tree-box" :data="data" show-checkbox node-key="id" :props="defaultProps"
@check="handleNodeClick" :filter-node-method="filterNode">
</el-tree>
<div class="buttons">
<el-button size="mini" @click="canselChangeNode">取 消</el-button>
<el-button size="mini" @click="reseSelecttNode">重 置</el-button>
<el-button type="primary" size="mini" @click="sureSelectNode">确 认</el-button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
// 数据集合
options: {
type: Array,
default: () => {
return []
}
},
// 初始值
valueMultiple: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
filterText: '',
defaultProps: {
children: 'children',
label: 'label'
},
valueName: [], // 展示标签集合
resultValue: [], // 传给父组件的数据集合
sureName: '', // 下拉框显示数据
showPanel: false, //显示选择面板
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
mounted() {
// 监听点击事件
document.addEventListener('click', this.clickOut, true)
},
methods: {
// 查询节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
// 点击其他地方,关闭选择框
clickOut(e) {
let frame = this.$refs.frame;
let panel = this.$refs.panel;
if (!frame.contains(e.target) && !panel.contains(e.target)) {
this.showPanel = false;
}
},
// 监听节点勾选情况
handleNodeClick(node, select) {
this.valueName = [];
this.resultValue = [];
// 获取目前选中节点
select.checkedNodes.forEach((item) => {
if (item.children == null || item.children == undefined) {
this.valueName.push(item); // 标签集合
this.resultValue.push(item.id) // 选中节点的id集合
}
})
},
// 监听选中标签改变 -删除标签
handleChangeTag(tag) {
this.valueName = this.valueName.filter((item) => { return item.id != tag.id });
// 设置删标签对应-节点的勾选状态
this.$refs.tree.setChecked(tag, false);
},
// 取消 不改变原来的选项
canselChangeNode() {
this.resultValue = [];
this.valueMultiple.forEach((item) => { this.resultValue.push(item.id) })
this.valueName = this.valueMultiple;
this.sureName = this.valueName;
// 还原设置选中节点
this.$refs.tree.setCheckedNodes(this.valueMultiple);
this.$emit('getValue', this.resultValue, this.valueMultiple)
// 关闭下拉框
this.showPanel = false;
},
// 重置
reseSelecttNode() {
// 清除选中节点
this.$refs.tree.setCheckedKeys([]);
this.valueName = [];
this.resultValue = [];
},
// 确认
sureSelectNode() {
let name = []
this.valueName.forEach((item) => { name.push(item.label) })
this.sureName = name.toString();
this.$emit('getValue', this.resultValue, this.valueName)
this.showPanel = false;
},
}
}
</script>
<style lang="scss" scoped>
.select-frame {
height: 34px;
width: 240px;
border: 1px solid #dddfe6;
border-radius: 2px;
line-height: 30px;
padding: 0px 10px;
cursor: pointer;
i {
float: right;
color: #c0c4cc;
margin-top: 8px;
}
.is {
transform: rotate(-180deg);
transition: all 0.5s;
}
.text {
width: 180px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.text:empty::before {
content: attr(placeholder);
vertical-align: middle;
color: #c0c4cc;
}
}
.select-panel {
width: 240px;
margin-top: 5px;
position: absolute;
border: 1px solid #dddfe6;
background-color: #fff;
border-radius: 7px;
padding: 10px;
.tag-title {
display: block;
margin: 10px 0px;
color: #636D91;
font-size: 12px;
i {
color: #3470ff;
margin: 0px 5px;
}
}
.tag-list {
max-height: 100px;
overflow-y: auto;
.el-tag {
margin: 5px;
}
}
.tree-box {
max-height: 240px;
border-top: 1px solid #Edf0f3;
padding: 8px;
overflow-y: auto;
}
.buttons {
display: flex;
justify-content: space-around;
margin: 12px 0px;
border-top: 1px solid #Edf0f3;
padding-top: 10px;
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-thumb {
background-color: #eceef1;
}
}
</style>