You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
8.5 KiB
329 lines
8.5 KiB
<!--下拉树el-select+el-tree -->
|
|
<template>
|
|
<el-select
|
|
ref="select"
|
|
popper-class="TREE_SELECT_POPPER"
|
|
:value="showLabel"
|
|
:size="size"
|
|
allow-create
|
|
:default-first-option="true"
|
|
:placeholder="placeholder"
|
|
:clearable="clearable"
|
|
:disabled="disabled"
|
|
:filterable="filterable"
|
|
:filter-method="selectFilter"
|
|
@visible-change="visibleChange"
|
|
@change="selectChange"
|
|
@focus="selectFocus"
|
|
@clear="clearSelect"
|
|
>
|
|
<el-option class="option_li" :style="{height: optionHeight+'px'}" :value="showLabel" :label="showLabel">
|
|
<el-scrollbar class="option_li_scroll">
|
|
<el-tree
|
|
ref="tree"
|
|
show-checkbox
|
|
:accordion="accordion"
|
|
:data="options"
|
|
:props="props"
|
|
:node-key="props.value"
|
|
:show-checkbox="multiple"
|
|
:render-content="renderContent"
|
|
:default-expand-all="defaultExpandAll"
|
|
:default-expanded-keys="defaultExpanded"
|
|
:default-checked-keys="(multiple && Array.isArray(value)) ? value : []"
|
|
:expand-on-click-node="false"
|
|
:check-strictly="checkStrictly"
|
|
:filter-node-method="treeFilter"
|
|
empty-text=""
|
|
@node-click="nodeClick"
|
|
@check-change="nodeCheck"
|
|
/>
|
|
<!-- <el-button v-if="!options.length" type="primary" plain size="small" @click="addNode">+</el-button>-->
|
|
</el-scrollbar>
|
|
</el-option>
|
|
</el-select>
|
|
</template>
|
|
<script type="es6">
|
|
let value = 1000
|
|
export default {
|
|
name: 'VElTreeSelect',
|
|
props: {
|
|
props: { // 配置项
|
|
type: Object,
|
|
default() {
|
|
return {
|
|
value: 'value',
|
|
label: 'label',
|
|
children: 'children'
|
|
}
|
|
}
|
|
},
|
|
options: { // 选项列表数据
|
|
type: Array,
|
|
default() {
|
|
return []
|
|
}
|
|
},
|
|
multiple: { // 是否可多选
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
value: { // 绑定值
|
|
type: [String, Number, Array],
|
|
default() {
|
|
return this.multiple ? [] : ''
|
|
}
|
|
},
|
|
accordion: { // 是否每次只展开一个同级树节点
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
size: String,
|
|
filterable: { // 是否可搜索
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
clearable: { // 是否可清空
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
disabled: { // 是否禁用
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
placeholder: String,
|
|
checkStrictly: { // 父子是否不互相关联
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
defaultExpandAll: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
defaultExpandedKeys: {
|
|
type: Array,
|
|
default() { return [] }
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
newChild: [],
|
|
curNode: '',
|
|
optionHeight: 247 // el-option高度
|
|
}
|
|
},
|
|
computed: {
|
|
showLabel() {
|
|
let label = {
|
|
a: '',
|
|
b: ''
|
|
}
|
|
const value = this.value
|
|
if (this.multiple) { // 多选
|
|
if (Array.isArray(value) && value.length > 0) {
|
|
const labelArr = []
|
|
const a = []
|
|
const b = []
|
|
value.forEach(value => {
|
|
if (value.self) {
|
|
labelArr.push({ a: value.label })
|
|
} else {
|
|
labelArr.push(this.queryTree(this.options, value))
|
|
}
|
|
})
|
|
labelArr.forEach(item => {
|
|
a.push(item.a)
|
|
b.push(item.b)
|
|
})
|
|
label.a = a.join(',')
|
|
label.b = b.join(',')
|
|
}
|
|
} else { // 单选
|
|
if (value) {
|
|
label = this.queryTree(this.options, value)
|
|
}
|
|
}
|
|
this.$emit('handleNode', label.a)
|
|
return label.a
|
|
},
|
|
// 默认展开
|
|
defaultExpanded() {
|
|
let keys = []
|
|
if (this.value) {
|
|
keys = (this.multiple && Array.isArray(this.value)) ? this.value : [this.value]
|
|
} else {
|
|
keys = this.defaultExpandedKeys
|
|
}
|
|
return keys
|
|
}
|
|
},
|
|
methods: {
|
|
// 搜索树状数据中的 ID,获取label
|
|
queryTree(tree, id) {
|
|
let stark = []
|
|
stark = stark.concat(tree)
|
|
const label = {
|
|
a: '',
|
|
b: ''
|
|
}
|
|
while (stark.length) {
|
|
const temp = stark.shift()
|
|
if (temp[this.props.children]) {
|
|
stark = stark.concat(temp[this.props.children])
|
|
}
|
|
if (temp[this.props.value] === id) {
|
|
label.a = temp[this.props.label]
|
|
label.b = temp[this.props.value]
|
|
}
|
|
}
|
|
return label
|
|
},
|
|
clearSelect(val) {
|
|
if (!val) {
|
|
val = this.multiple ? [] : ''
|
|
}
|
|
this.newChild = []
|
|
this.$emit('input', val)
|
|
},
|
|
// queryTree(tree, id) {
|
|
// let stark = []
|
|
// stark = stark.concat(tree)
|
|
// let label = ''
|
|
// while (stark.length) {
|
|
// const temp = stark.shift()
|
|
// if (temp[this.props.children]) {
|
|
// stark = stark.concat(temp[this.props.children])
|
|
// }
|
|
// if (temp[this.props.value] === id) {
|
|
// label = temp[this.props.label]
|
|
// }
|
|
// }
|
|
// return label
|
|
// },
|
|
// 提交值
|
|
emitVal(val) {
|
|
if (!val) {
|
|
val = this.multiple ? [] : ''
|
|
}
|
|
this.$emit('input', val)
|
|
},
|
|
// select框获得焦点
|
|
selectFocus() {
|
|
this.$refs.tree.filter('')
|
|
},
|
|
// 下拉框出现/隐藏
|
|
visibleChange(show) {
|
|
this.$refs.tree.filter('')
|
|
if (show) {
|
|
this.$nextTick(() => {
|
|
const tree_H = this.$refs.tree.$el.clientHeight
|
|
if (tree_H < this.optionHeight) {
|
|
this.optionHeight = tree_H
|
|
}
|
|
})
|
|
}
|
|
},
|
|
// select option过滤
|
|
selectFilter(label) {
|
|
this.$refs.tree.filter(label)
|
|
return true
|
|
},
|
|
// 树过滤方法
|
|
treeFilter(query, data) {
|
|
if (!query) {
|
|
return true
|
|
} else {
|
|
const labelArray = query.split(',')
|
|
return labelArray.some(value => {
|
|
return value && data[this.props.label].includes(value)
|
|
})
|
|
}
|
|
},
|
|
selectChange(node) {
|
|
const newChild = { value: value++, label: node, self: 999, children: [] }
|
|
const keys = this.$refs.tree.getCheckedKeys()
|
|
this.newChild = this.newChild.concat([newChild])
|
|
const news = keys.concat(this.newChild)
|
|
this.emitVal(news)
|
|
},
|
|
// 点击节点
|
|
nodeClick(node) {
|
|
if (!this.multiple) {
|
|
this.emitVal(node[this.props.value])
|
|
this.$refs.select.blur() // 使select失去焦点 隐藏下拉框
|
|
}
|
|
},
|
|
// 点击复选框
|
|
nodeCheck() {
|
|
const keys = this.$refs.tree.getCheckedKeys()
|
|
const news = keys.concat(this.newChild)
|
|
this.emitVal(news)
|
|
},
|
|
append(data) {
|
|
this.$emit('add', data)
|
|
},
|
|
remove(data) {
|
|
this.$emit('remove', data)
|
|
},
|
|
renderContent(h, { node, data, store }) {
|
|
if (node.label === '公共' || node.label === '个人') {
|
|
return (
|
|
<span style='flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px;'>
|
|
<span>
|
|
<span>{node.label}</span>
|
|
</span>
|
|
<span>
|
|
<el-button icon='el-icon-plus' style='font-size: 12px;color: #409EFF' type='text' on-click={ () => this.append(data) }></el-button>
|
|
</span>
|
|
</span>)
|
|
} else {
|
|
return (
|
|
<span style='flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px;'>
|
|
<span>
|
|
<span>{node.label}</span>
|
|
</span>
|
|
<span>
|
|
<el-button icon='el-icon-plus' style='font-size: 12px;color: #409EFF' type='text' on-click={ () => this.append(data) }></el-button>
|
|
<el-button icon='el-icon-delete' style='font-size: 12px;color: #F56C6C' type='text' on-click={ () => this.remove(data) }></el-button>
|
|
</span>
|
|
</span>)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" type="text/scss">
|
|
.el-scrollbar__wrap{
|
|
background-color: white;
|
|
}
|
|
.TREE_SELECT_POPPER>.el-scrollbar {
|
|
>.el-scrollbar__bar.is-vertical {
|
|
.el-scrollbar__thumb,.el-scrollbar__thumb:hover {
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style scoped lang="scss" type="text/scss">
|
|
.el-select-dropdown__item{
|
|
height: 175px !important;
|
|
}
|
|
.option_li {
|
|
padding: 0;
|
|
&.selected {
|
|
font-weight: normal;
|
|
}
|
|
.option_li_scroll {
|
|
height: 100%;
|
|
.el-scrollbar__wrap{
|
|
overflow-x: hidden;
|
|
}
|
|
}
|
|
}
|
|
.el-tree {
|
|
box-sizing: border-box;
|
|
padding: 0 12px;
|
|
}
|
|
</style>
|