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.
216 lines
6.1 KiB
216 lines
6.1 KiB
|
6 years ago
|
<template>
|
||
|
|
<el-select
|
||
|
6 years ago
|
ref="select"
|
||
|
|
:value="value"
|
||
|
|
:multiple="multiple"
|
||
|
|
:disabled="disabled"
|
||
|
|
:size="size"
|
||
|
|
:clearable="clearable"
|
||
|
|
:placeholder="placeholder"
|
||
|
|
:popper-append-to-body="popperAppendToBody"
|
||
|
|
:automatic-dropdown="automaticDropdown"
|
||
|
|
@input="e => $emit('input',e)"
|
||
|
|
@change="e => $emit('change',e)"
|
||
|
|
@remove-tag="removeTag"
|
||
|
6 years ago
|
>
|
||
|
|
<div class="tree-select-container" slot="empty">
|
||
|
|
<el-input
|
||
|
6 years ago
|
ref="search"
|
||
|
|
v-if="filterable && filterMethod"
|
||
|
|
v-model="search"
|
||
|
|
size="mini"
|
||
|
|
clearable
|
||
|
|
@input="onSearch"
|
||
|
6 years ago
|
/>
|
||
|
|
<el-tree
|
||
|
6 years ago
|
ref="tree"
|
||
|
|
:data="data"
|
||
|
|
:node-key="nodeKey"
|
||
|
|
:props="props"
|
||
|
|
:show-checkbox="multiple"
|
||
|
|
:default-checked-keys="defaultCheckedKeys"
|
||
|
|
:current-node-key="multiple ? undefined : value"
|
||
|
|
:highlight-current="!multiple"
|
||
|
|
:expand-on-click-node="false"
|
||
|
|
:check-on-click-node="multiple"
|
||
|
|
:filter-node-method="filterMethod"
|
||
|
|
:accordion="accordion"
|
||
|
|
@node-click="nodeClick"
|
||
|
|
@check="check"
|
||
|
6 years ago
|
>
|
||
|
|
<slot slot-scope="{node,data}">
|
||
|
6 years ago
|
<span :class="{'el-tree-node__label':true,'is-disabled':node.disabled}">{{ node.label }}</span>
|
||
|
6 years ago
|
</slot>
|
||
|
|
</el-tree>
|
||
|
|
</div>
|
||
|
|
</el-select>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
6 years ago
|
import {debounce} from "@/util"
|
||
|
|
import {flatTree} from "@/util/tree"
|
||
|
6 years ago
|
|
||
|
|
export default {
|
||
|
|
name: "TreeSelect",
|
||
|
|
|
||
|
|
props: {
|
||
|
|
value: {required: true},
|
||
|
|
data: {type: Array, default: () => []},
|
||
|
|
multiple: Boolean,
|
||
|
|
disabled: Boolean,
|
||
|
|
size: String,
|
||
|
|
clearable: {type: Boolean, default: true},
|
||
|
|
placeholder: String,
|
||
|
|
filterable: Boolean,
|
||
|
|
filterMethod: Function,
|
||
|
|
nodeKey: {type: String, default: 'id'},
|
||
|
6 years ago
|
props: {
|
||
|
6 years ago
|
default() {
|
||
|
|
return {
|
||
|
|
children: 'children',
|
||
|
|
label: 'label',
|
||
|
|
disabled: 'disabled'
|
||
|
6 years ago
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
6 years ago
|
accordion: {type: Boolean, default: true},
|
||
|
|
popperAppendToBody: Boolean,
|
||
|
|
automaticDropdown: Boolean
|
||
|
|
},
|
||
|
|
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
currentNode: null,
|
||
|
|
search: ''
|
||
|
|
}
|
||
|
|
},
|
||
|
6 years ago
|
|
||
|
6 years ago
|
computed: {
|
||
|
|
defaultCheckedKeys() {
|
||
|
|
return this.multiple ? this.value : []
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
watch: {
|
||
|
|
value(v) {
|
||
|
|
const method = this.multiple ? 'setCheckedKeys' : 'setCurrentKey'
|
||
|
|
this.$refs.tree[method](v || null)
|
||
|
|
if (!v && !this.multiple) this.currentNode = null
|
||
|
6 years ago
|
},
|
||
|
|
|
||
|
6 years ago
|
data() {
|
||
|
|
this.initSelectOptions()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
methods: {
|
||
|
|
removeTag(v) {
|
||
|
|
const tree = this.$refs.tree
|
||
|
|
const value = [...this.value]
|
||
|
|
|
||
|
|
//被移除的选项的所有父级的ID
|
||
|
|
const parents = []
|
||
|
|
let removeNode = tree.getNode(v)
|
||
|
|
while (removeNode && removeNode.parent) {
|
||
|
|
parents.push(removeNode.parent.key)
|
||
|
|
removeNode = removeNode.parent
|
||
|
|
}
|
||
|
6 years ago
|
|
||
|
6 years ago
|
//移除所有子级、所有父级
|
||
|
|
for (let i = value.length - 1; i >= 0; i--) {
|
||
|
|
const isChild = this.getNodeParent(tree.getNode(value[i]), ({key}) => key === v)
|
||
|
|
if (isChild || parents.includes(value[i])) {
|
||
|
|
value.splice(i, 1)
|
||
|
|
}
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
|
||
|
|
value.length < this.value.length && this.$emit('input', value)
|
||
|
6 years ago
|
},
|
||
|
|
|
||
|
6 years ago
|
onSearch(v) {
|
||
|
|
this.$refs.tree.filter(v)
|
||
|
|
},
|
||
|
6 years ago
|
|
||
|
6 years ago
|
nodeClick(data, node) {
|
||
|
|
if (this.multiple) return
|
||
|
6 years ago
|
|
||
|
6 years ago
|
if (node.disabled) {
|
||
|
|
return this.$refs.tree.setCurrentKey(null)
|
||
|
|
}
|
||
|
6 years ago
|
|
||
|
6 years ago
|
if (this.currentNode === data) {
|
||
|
|
this.currentNode = null
|
||
|
|
this.$refs.tree.setCurrentKey(null)
|
||
|
|
}
|
||
|
|
else this.currentNode = data
|
||
|
6 years ago
|
|
||
|
6 years ago
|
this.$emit('input', this.currentNode && this.currentNode[this.nodeKey], this.currentNode)
|
||
|
|
this.$refs.select.blur()
|
||
|
|
},
|
||
|
6 years ago
|
|
||
|
6 years ago
|
check(data, {checkedKeys}) {
|
||
|
|
this.multiple && this.$emit('input', checkedKeys)
|
||
|
|
},
|
||
|
6 years ago
|
|
||
|
6 years ago
|
getNodeParent(node, predicate) {
|
||
|
|
if (!node || predicate(node)) return node
|
||
|
|
return this.getNodeParent(node.parent, predicate)
|
||
|
6 years ago
|
},
|
||
|
|
|
||
|
6 years ago
|
initSelectOptions() {
|
||
|
|
this.$refs.select.cachedOptions =
|
||
|
|
flatTree(this.data, this.props.children)
|
||
|
|
.map(i => ({
|
||
|
|
value: i[this.nodeKey],
|
||
|
|
currentLabel: i[this.props.label]
|
||
|
|
}))
|
||
|
|
this.selectOptionsTrigger()
|
||
|
6 years ago
|
},
|
||
|
|
|
||
|
6 years ago
|
//模拟el-select对options的watch
|
||
|
|
selectOptionsTrigger() {
|
||
|
|
const select = this.$refs.select
|
||
|
|
if (select.$isServer) return
|
||
|
|
this.$nextTick(() => {
|
||
|
|
select.broadcast('ElSelectDropdown', 'updatePopper')
|
||
|
|
})
|
||
|
|
if (select.multiple) {
|
||
|
|
select.resetInputHeight()
|
||
|
|
}
|
||
|
|
let inputs = select.$el.querySelectorAll('input')
|
||
|
|
if ([].indexOf.call(inputs, document.activeElement) === -1) {
|
||
|
|
select.setSelected()
|
||
|
|
}
|
||
|
|
if (select.defaultFirstOption && (select.filterable || select.remote) && select.filteredOptionsCount) {
|
||
|
|
select.checkDefaultFirstOption()
|
||
|
|
}
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
},
|
||
|
|
|
||
|
|
created() {
|
||
|
|
this.onSearch = debounce(this.onSearch)
|
||
|
|
},
|
||
|
|
|
||
|
|
mounted() {
|
||
|
|
this.currentNode = this.$refs.tree.getCurrentNode()
|
||
|
|
this.initSelectOptions()
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss">
|
||
|
6 years ago
|
.tree-select-container {
|
||
|
|
padding: 8px;
|
||
|
6 years ago
|
|
||
|
6 years ago
|
> .el-input {
|
||
|
|
margin-bottom: 8px;
|
||
|
|
}
|
||
|
6 years ago
|
|
||
|
6 years ago
|
.el-tree-node__label.is-disabled {
|
||
|
|
background-color: #edf2fc;
|
||
|
|
border-color: #dcdfe6;
|
||
|
|
cursor: not-allowed;
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
}
|
||
|
6 years ago
|
</style>
|