| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- <template>
- <el-select
- ref="select"
- :value="value"
- :placeholder="placeholder"
- :size="size"
- :clearable="clearable"
- :disabled="disabled"
- :filterable="filterable"
- :filter-method="filterMethod"
- style="width: 100%;"
- @clear="clear"
- @focus="focus"
- @visible-change="visibleChange"
- >
- <el-option
- ref="option"
- class="tree-select__option"
- :value="optionData.id"
- :label="optionData.name"
- >
- <el-tree
- ref="tree"
- class="tree-select__tree"
- :class="`tree-select__tree--${multiple ? 'checked' : 'radio'}`"
- :node-key="nodeKey"
- :data="data"
- :props="props"
- :default-expanded-keys="[value]"
- :show-checkbox="multiple"
- :highlight-current="!multiple"
- :expand-on-click-node="multiple"
- :filter-node-method="filterNode"
- @node-click="handleNodeClick"
- @check-change="handleCheckChange"
- ></el-tree>
- </el-option>
- </el-select>
- </template>
- <script>
- export default {
- name: 'TreeSelect',
- props: {
- // v-model绑定
- value: {
- type: [String, Number],
- default: ''
- },
- // 节点是否可被选择 多选还是单选
- multiple: {
- type: Boolean,
- default: false
- },
- clearable: {
- type: Boolean,
- default: true
- },
- // 树形的数据
- data: {
- type: Array,
- default: function () {
- return []
- }
- },
- // 每个树节点用来作为唯一标识的属性
- nodeKey: {
- type: [String, Number],
- default: 'id'
- },
- // 组件大小
- size:{
- type: String,
- default: 'small'
- },
- // 是否可搜索
- filterable: {
- type: Boolean,
- default: false
- },
- // 是否禁用
- disabled: {
- type: Boolean,
- default: false
- },
- // tree的props配置
- props: {
- type: Object,
- default: function () {
- return {
- label: 'label',
- children: 'children'
- }
- }
- },
- placeholder:{
- type: String,
- default: '请选择'
- },
- },
- data() {
- return {
- optionData: {
- id: '',
- name: ''
- },
- filterFlag: false
- }
- },
- watch: {
- value: {
- // 选择的数据发生变化触发
- handler(val) {
- if (!this.isEmpty(this.data)) {
- this.init(val)
- }
- },
- immediate: true
- },
- data: function (val) {
- if (!this.isEmpty(val)) {
- this.init(this.value)
- }
- }
- },
- created() {},
- methods: {
- // 是否为空
- isEmpty(val) {
- for (let key in val) {
- return false
- }
- return true
- },
- // 节点被点击时的回调
- handleNodeClick(data) {
- if (this.multiple) {
- return
- }
- this.$emit('input', data[this.nodeKey])
- this.$refs.select.visible = false
- // 当点击选中树的节点时,下拉框不会自动隐藏,感觉体验不太好。查看文档,
- // select组件也没有控制下拉框显示隐藏的属性,然后在select组件源码中找到了visible属性,用来控制下拉框显示隐藏。
- // 点击选中树的节点时设置 this.$refs.select.visible = false 即可。
- },
- // 节点选中状态发生变化时的回调
- handleCheckChange() {
- const nodes = this.$refs.tree.getCheckedNodes()
- const value = nodes.map((item) => item[this.nodeKey]).join(',')
- this.$emit('input', value)
- },
- init(val) {
- // 多选
- if (this.multiple) {
- const arr = val.toString().split(',')
- this.$nextTick(() => {
- // setCurrentKey() 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
- this.$refs.tree.setCheckedKeys(arr)
- // getCheckedNodes() 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
- const nodes = this.$refs.tree.getCheckedNodes()
- this.optionData.id = val
- this.optionData.name = nodes
- .map((item) => item[this.props.label])
- .join(',')
- })
- }
- // 单选
- else {
- val = val === '' ? null : val
- this.$nextTick(() => {
- // setCurrentKey() 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
- this.$refs.tree.setCurrentKey(val)
- if (val === null) {
- return
- }
- // getNode() 根据 data 或者 key 拿到 Tree 组件中的 node
- const node = this.$refs.tree.getNode(val)
- this.optionData.id = val
- this.optionData.name = node.label
- })
- }
- },
- // 下拉框出现/隐藏时触发
- visibleChange(e) {
- if (e) {
- const tree = this.$refs.tree
- this.filterFlag && tree.filter('')
- this.filterFlag = false
- let selectDom = null
- if(this.multiple) {
- // $el.querySelector() == document.querySelector() // 获取 dom 元素
- selectDom = tree.$el.querySelector('.el-tree-node.is-checked')
- } else {
- selectDom = tree.$el.querySelector('.is-current')
- }
- setTimeout(() => {
- this.$refs.select.scrollToOption({ $el: selectDom })
- }, 0)
- }
- },
- // 下拉框点击
- clear() {
- this.$emit('input', '')
- },
- // 聚焦
- focus(){
- this.$emit('focus')
- },
- // 自定义搜索方法
- filterMethod(val) {
- this.filterFlag = true
- this.$refs.tree.filter(val)
- },
- // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
- filterNode(value, data) {
- if (!value) return true
- const label = this.props.label || 'name'
- return data[label].indexOf(value) !== -1
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- // 得用 ::v-deep
- ::v-deep.tree-select__option {
- &.el-select-dropdown__item {
- height: auto;
- line-height: 1;
- padding: 0;
- background-color: #fff;
- }
- }
- // 得用 ::v-deep
- .tree-select__tree {
- padding: 4px 20px;
- font-weight: 400;
- &.tree-select__tree--radio {
- .el-tree-node.is-current > .el-tree-node__content {
- //选中字体的样式
- color:red;
- font-weight: 700;
- }
- }
- }
- </style>
|