Commit 5eea9632 authored by trumansdo's avatar trumansdo
Browse files

添加自定义树下拉选择器组件

parent 4c9c9cb9
// 树形下拉框
<template>
<div class="sp-tree-select" :style="{ width: width }">
<!-- 选中框区 -->
<el-popover
v-model="options_show"
placement="bottom"
:width="width"
:trigger="trigger"
:disabled="disabled"
transition="fade-in"
>
<el-scrollbar class="sp-treeselect-popover">
<el-input
v-if="filterable"
v-model="filterText"
:size="size"
placeholder="请输入关键词"
></el-input>
<el-tree
ref="tree-select"
class="sp-options-tree"
highlight-current
:data="selfData"
:props="selfProps"
:node-key="nodeKey"
:show-checkbox="checkbox"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:default-checked-keys="checked_keys"
:default-expand-all="defaultExpandAll"
:default-expanded-keys="defaultExpandedKeys"
@check="handleCheckChange"
@node-click="treeItemClick"
></el-tree>
</el-scrollbar>
<!---->
<div
slot="reference"
class="selected-box"
:class="[{ 'sp-disabled': disabled, 'no-wrap': nowrap }, sizeClass]"
>
<div class="tag-box">
<div v-show="selecteds.length > 0">
<template v-if="!collapseTags">
<el-tag
v-for="item in selecteds"
:key="item[nodeKey]"
closable
:size="size"
class="sp-select-tag"
:title="item[selfProps.label]"
@close="tabClose(item[nodeKey])"
>{{ item[selfProps.label] }}</el-tag
>
</template>
<template v-else>
<el-tag
closable
:size="size"
class="sp-select-tag"
:title="collapseTagsItem[selfProps.label]"
@close="tabClose(collapseTagsItem[nodeKey])"
>{{ collapseTagsItem[selfProps.label] }}</el-tag
>
<el-tag
v-if="selecteds.length > 1"
:size="size"
class="sp-select-tag"
>+{{ selecteds.length - 1 }}</el-tag
>
</template>
</div>
<p v-show="selecteds.length == 0" class="sp-placeholder-box">
{{ placeholder }}
</p>
</div>
<div class="icon-box">
<transition name="fade-rotate" mode="out-in">
<i v-if="!options_show" key="top" class="el-icon-arrow-down"></i>
<i v-else key="btm" class="el-icon-arrow-up"></i>
</transition>
</div>
</div>
</el-popover>
</div>
</template>
<script>
/**
* https://github.com/hql7/wl-vue-select
*/
export default {
name: 'WlTreeSelect',
model: {
prop: 'value', //这里使我们定义的v-model属性
event: 'change'
},
props: {
// 数据
data: {
type: Array,
default: () => []
},
// 树结构配置
props: {
type: Object,
default: () => {
return {};
}
},
// node-key
nodeKey: {
type: String,
default: 'id'
},
// 选中数据
value: {
type: [String, Number, Array, Object],
default: undefined
},
// 是否可多选
checkbox: {
type: Boolean,
default: false
},
// 多选时是否将选中值按文字的形式展示
collapseTags: {
type: Boolean,
default: false
},
// 是否只可选叶子节点
leaf: {
type: Boolean,
default: false
},
// 宽度
width: {
type: String,
default: 'auto'
},
// 触发方式 click/focus/hover/manual
trigger: {
type: String,
default: 'click'
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 是否允许多行显示
nowrap: {
type: Boolean,
default: false
},
// 多选时,清空选项关闭
noCheckedClose: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择'
},
size: {
type: String,
default: 'medium'
},
//是否展开全部
defaultExpandAll: {
type: Boolean,
default: true
},
//默认展开的节点的 key 的数组
defaultExpandedKeys: {
type: Array,
default: () => {
return [];
}
},
// 是否使用搜索
filterable: {
type: Boolean,
default: false
},
// 自定义筛选函数
filterFnc: {
type: Function,
default: undefined
}
},
data() {
return {
selecteds: [], // 选中数据
options_show: false, // 是否显示下拉选项
checked_keys: [], // 默认选中
guid: '00000000-0000-0000-0000-000000000000',
filterText: ''
};
},
computed: {
selfData() {
return this.data;
},
selfProps() {
return {
label: 'label',
children: 'children',
disabled: data => {
return data.disabled;
},
...this.props
};
},
sizeClass() {
let size_class = 'size-medium';
switch (this.size) {
case 'medium':
size_class = 'size-medium';
break;
case 'small':
size_class = 'size-small';
break;
case 'default':
size_class = 'size-default';
break;
case 'mini':
size_class = 'size-mini';
break;
default:
size_class = 'size-medium';
break;
}
return size_class;
},
// 开启collapseTags时首个选中值
collapseTagsItem() {
return this.selecteds[0] || {};
}
},
watch: {
value(val) {
this.checkDefaultValue();
},
// 树节点搜索
filterText(val) {
this.$refs['tree-select'].filter(val);
}
},
created() {
this.checkDefaultValue();
},
methods: {
// 树节点-checkbox选中
handleCheckChange(val, { checkedNodes, checkedKeys }) {
let nodes = this.$refs['tree-select'].getCheckedNodes(this.leaf);
this.selecteds = nodes;
this.$emit('change', nodes);
if (checkedKeys.length === 0 && this.noCheckedClose)
this.options_show = false;
},
// 树节点-点击选中
treeItemClick(item, node) {
if (this.checkbox || (this.leaf && !node.isLeaf)) {
return;
}
this.selecteds = [item];
this.options_show = false;
this.$emit('change', this.selecteds);
},
// tag标签关闭
tabClose(Id) {
if (this.disabled) return;
if (this.checkbox) {
this.$refs['tree-select'].setChecked(Id, false, true);
this.selecteds = this.$refs['tree-select'].getCheckedNodes();
if (this.selecteds.length === 0 && this.noCheckedClose)
this.options_show = false;
} else {
this.selecteds = [];
this.$refs['tree-select'].setCurrentKey(null);
this.options_show = false;
}
this.$emit('change', this.selecteds);
},
// 清空数据
clear() {
this.selecteds = [];
},
// 处理默认选中数据
checkDefaultValue() {
let val = this.value;
if (!val || (Array.isArray(val) && val.length === 0)) {
this.selecteds = [];
if (!this.checkbox) return;
this.checked_keys = [];
this.$nextTick(() => {
this.$refs['tree-select'].setCheckedKeys([]);
});
return;
}
// 多选处理
if (this.checkbox) {
this.checked_keys =
typeof val[0] === 'object' ? val.map(i => i[this.nodeKey]) : val;
this.$nextTick(() => {
this.selecteds = this.$refs['tree-select'].getCheckedNodes(this.leaf);
});
return;
}
// 单选处理
if (typeof val === 'object') {
let _val = Array.isArray(val) ? val[0] : val;
this.selecteds = [_val];
this.$nextTick(() => {
this.$refs['tree-select'].setCurrentNode(_val);
});
} else {
this.$nextTick(() => {
this.$refs['tree-select'].setCurrentKey(val);
let _node = this.$refs['tree-select'].getCurrentNode();
this.selecteds = _node ? [_node] : [];
});
}
},
// 关闭
closeOptions() {
this.options_show = false;
},
// 树节点筛选
filterNode(value, data) {
if (this.filterFnc) return this.filterFnc(value, data);
if (!value) return true;
return data[this.selfProps.label].indexOf(value) !== -1;
}
}
};
</script>
<style lang="scss">
.sp-tree-select {
position: relative;
display: inline-block;
width: 240px;
vertical-align: middle;
outline: none;
.selected-box {
display: flex;
border: 1px solid #dcdfe6;
padding: 0 5px 0 8px;
width: 100%;
min-height: 36px;
line-height: 34px;
box-sizing: border-box;
border-radius: 4px;
cursor: pointer;
outline: none;
&:focus {
border-color: #409eff;
}
> .tag-box {
display: inline-block;
width: calc(100% - 20px);
text-align: left;
}
> .icon-box {
float: right;
display: flex;
width: 20px;
justify-content: center;
align-items: Center;
color: #c0c4cc;
}
}
.selected-box.size-small {
min-height: 32px;
line-height: 30px;
}
.selected-box.size-mini {
min-height: 28px;
line-height: 26px;
}
.selected-box.size-default {
min-height: 40px;
line-height: 38px;
}
.no-wrap {
height: 36px;
> .tag-box {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.sp-disabled {
background: #eee;
cursor: no-drop;
&:focus {
border-color: #dcdfe6;
}
.el-tag__close {
cursor: no-drop;
}
}
.sp-select-tag {
max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
word-wrap: break-word;
word-break: break-all;
vertical-align: middle;
}
.sp-select-tag + .sp-select-tag {
margin-left: 4px;
}
}
.sp-treeselect-popover {
height: 360px;
> .el-scrollbar__wrap {
overflow-x: hidden;
}
}
.sp-options-tree {
display: inline-block !important;
min-width: 100%;
.el-tree-node__content {
height: 34px;
line-height: 34px;
}
}
.sp-placeholder-box {
color: #c0c4cc;
margin: 0;
}
// 过度效果
.fade-in-enter-active,
.fade-in-leave-active {
transition: all 0.4s;
}
.fade-in-enter, .fade-in-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(-10px);
}
.fade-rotate-enter-active,
.fade-rotate-leave-active {
transition: all 0.2s;
}
.fade-rotate-enter, .fade-rotate-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: rotateZ(45deg);
}
</style>
<!-- <!--
* @Author: 一日看尽长安花 * @Author: 一日看尽长安花
* @since: 2020-03-29 16:00:50 * @since: 2020-03-29 16:00:50
* @LastEditTime: 2020-04-26 21:59:58 * @LastEditTime: 2020-05-10 14:51:34
* @LastEditors: 一日看尽长安花 * @LastEditors: 一日看尽长安花
* @Description: * @Description:
--> -->
...@@ -39,6 +39,15 @@ ...@@ -39,6 +39,15 @@
></el-cascader> ></el-cascader>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5">
<el-form-item label="测试">
<sp-tree-select
width="170px"
:data="treeData"
size="mini"
></sp-tree-select>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row :gutter="10" type="flex" justify="end"> <el-row :gutter="10" type="flex" justify="end">
<el-col :span="2"> <el-col :span="2">
...@@ -111,10 +120,11 @@ import { immaditeLoadRoles } from '@/api/role'; ...@@ -111,10 +120,11 @@ import { immaditeLoadRoles } from '@/api/role';
import { immaditeLoadOrgs } from '@/api/org'; import { immaditeLoadOrgs } from '@/api/org';
import { getUserRoles, getUserById, deleteUserRoles } from '@/api/user'; import { getUserRoles, getUserById, deleteUserRoles } from '@/api/user';
import AddUserRole from './add-user-role'; import AddUserRole from './add-user-role';
import SpTreeSelect from '@/components/Wrapper/SpTreeSelect';
export default { export default {
name: 'ManagerUserRole', name: 'ManagerUserRole',
components: { Pagination, AddUserRole }, components: { Pagination, AddUserRole, SpTreeSelect },
data() { data() {
return { return {
id: this.$route.params.id, id: this.$route.params.id,
...@@ -135,7 +145,43 @@ export default { ...@@ -135,7 +145,43 @@ export default {
}, },
dialogTitle: '', dialogTitle: '',
dialogData: {}, dialogData: {},
visible: false visible: false,
treeData: [
{
id: 'love',
label: '所有和你走过的风光',
value: 1,
children: [
{
id: 1,
label: '海边',
value: 11,
children: [
{
id: 5,
label: '蓬莱',
value: 111
}
]
},
{
id: 2,
label: '森林',
value: 2
},
{
id: 3,
label: '草原',
value: 3
},
{
id: 4,
label: '古城',
value: 4
}
]
}
]
}; };
}, },
mounted() { mounted() {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment