<template>
|
<el-popover v-model:visible="visible" :width="pannelWidth" :placement="placement" :trigger="trigger" popper-class="packaged-dropdown-menu-popover"> <!-- 下拉面板响应区域 start -->
|
<template #reference>
|
<div class="packaged-dropdown-menu-trigger-target" ref="target" v-on:[trigger]="targetTrigger">
|
<div><slot></slot></div>
|
<el-icon class="drop-icon"><arrow-down /></el-icon>
|
</div>
|
</template>
|
<!-- 下拉面板响应区域 end -->
|
<div class="packaged-dropdown-menu-popper" :class="[multiple?'has-bottom':'',hasSearch?'has-search':'']" :style="{
|
maxHeight:maxHeight,
|
paddingTop:(hasSearch?'48px':'0'),
|
paddingBottom:(multiple?'48px':'0')
|
}" @click.stop="">
|
<!-- 搜索输入框 start -->
|
<div class="search-bar" v-if="hasSearch">
|
<el-input size="small" :placeholder="placeholder" v-model="searchValue" @change="onSearch">
|
<template #append>
|
<el-button @click.stop="onSearch"><el-icon class="drop-icon"><el-search /></el-icon></el-button>
|
</template>
|
</el-input>
|
</div>
|
<!-- 搜索输入框 end -->
|
<!-- 底部操作区域 start -->
|
<div class="bottom-row" v-if="multiple">
|
<div class="all-check">
|
<el-checkbox v-model="selectAll" label="全选" @change="onChangeSelectAll" />
|
</div>
|
<div class="btns-group">
|
<el-button size="small" @click="onCancel">取 消</el-button>
|
<el-button size="small" type="primary" @click="onConfirm">确 定</el-button>
|
</div>
|
</div>
|
<!-- 底部操作区域 end -->
|
<!-- 菜单列表 start -->
|
<div class="menu-list" v-loading="loading">
|
<el-scrollbar>
|
<div class="menu-list-item-group">
|
<div class="menu-list-item" v-for="(item,index) in menuList" :key="'drop-down-item-'+index" @click="onItemClick(index,$event)">
|
<div class="item-checkbox" v-if="multiple">
|
<el-checkbox v-model="item.checked" @change="onChangeSelect" />
|
</div>
|
<div class="item-content">{{item[labelProp]}}</div>
|
</div>
|
</div>
|
</el-scrollbar>
|
</div>
|
<!-- 菜单列表 end -->
|
</div>
|
</el-popover>
|
</template>
|
|
<script>
|
import {ArrowDown,Search} from '@element-plus/icons'
|
export default {
|
name:'packagedDropdownMenu',
|
components:{ArrowDown,elSearch:Search},
|
props:{
|
trigger:{
|
type:String,
|
default:'click'
|
},
|
placement:{
|
type:String,
|
default:'bottom' /* 弹出层位置 top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end */
|
},
|
/* 下拉面板宽度 */
|
pannelWidth:{
|
type:String,
|
default:'240px'
|
},
|
/* 下拉菜单数据源,可通过改变menuRefresh更新菜单视图 */
|
dataSource:{
|
type:Array,
|
default:function(){
|
return []
|
}
|
},
|
valueProp:{
|
type:String,
|
default:'value'
|
},
|
labelProp:{
|
type:String,
|
default:'label'
|
},
|
/* 下拉项数据是否不是json对象,默认是json对象false,不是json对象为true */
|
metaItem:{
|
type:Boolean,
|
default:false
|
},
|
/* 下拉面板最大高度 */
|
maxHeight:{
|
type:String,
|
default:'300px'
|
},
|
/* 搜索输入框默认提示 */
|
placeholder:{
|
type:String,
|
default:'请输入...'
|
},
|
/* 是否是多选 */
|
multiple:{
|
type:Boolean,
|
default:true
|
},
|
/* 是否开启搜索 */
|
hasSearch:{
|
type:Boolean,
|
default:false
|
},
|
/* 菜单列表更新标记,此数据变化会触发菜单列表更新 */
|
menuRefresh:{
|
type:Number,
|
default:0
|
},
|
/* 菜单列表的loading效果 */
|
loading:{
|
type:Boolean,
|
default:false
|
},
|
},
|
emits:['confirm','search'],
|
data(){
|
return {
|
visible:false,
|
selectAll:true,
|
menuList:[],
|
searchValue:''
|
}
|
},
|
created() {
|
this.refreshMenus()
|
},
|
watch:{
|
menuRefresh(newVal,oldVal){
|
if (newVal!==oldVal) {
|
this.refreshMenus()
|
}
|
}
|
},
|
methods:{
|
/* 更新菜单 */
|
refreshMenus(){
|
this.menuList = this.dataSource.map((item)=>{
|
let obj = {};
|
if (this.metaItem) {
|
obj[this.valueProp] = item;
|
obj[this.labelProp] = item;
|
} else {
|
obj[this.valueProp] = item[this.valueProp];
|
obj[this.labelProp] = item[this.labelProp];
|
}
|
obj.checked = true;
|
return obj;
|
})
|
this.selectAll = true;
|
},
|
close(){
|
this.visible = false;
|
},
|
onCancel(){
|
this.close()
|
},
|
onConfirm(){
|
let arr1=[],arr2=[];
|
this.menuList.forEach((item,index)=>{
|
if (item.checked) {
|
if (this.metaItem) {
|
arr1.push(item[this.valueProp])
|
} else {
|
let obj = {}
|
obj[this.valueProp] = item[this.valueProp];
|
obj[this.labelProp] = item[this.labelProp];
|
arr1.push(obj)
|
}
|
arr2.push(index)
|
}
|
})
|
this.$emit('confirm',arr1,arr2)
|
this.close()
|
},
|
/* 多选时候单个菜单选择 */
|
onChangeSelect(value){
|
let allChecked = true;
|
this.menuList.forEach((item)=>{
|
if (!item.checked) {
|
allChecked = false;
|
}
|
})
|
this.selectAll = allChecked
|
},
|
/* 全选事件 */
|
onChangeSelectAll(value) {
|
this.menuList.forEach((item)=>{
|
item.checked = value
|
})
|
},
|
onItemClick(index,e){
|
/* 点击区域判断 */
|
let targetClass = e.target.className;
|
if (targetClass!=='item-content') return false;
|
/* ---------------- */
|
if (!this.multiple) {
|
/* 单选 */
|
let obj = {}
|
obj[this.valueProp] = this.menuList[index][this.valueProp];
|
obj[this.labelProp] = this.menuList[index][this.labelProp];
|
this.$emit('confirm',obj,index)
|
this.close()
|
}
|
},
|
onSearch(){
|
this.$emit('search',this.searchValue)
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss">
|
.packaged-dropdown-menu-trigger-target{
|
display: flex;
|
align-items: center;
|
.drop-icon{
|
margin-left: 4px;
|
}
|
}
|
.el-popover{
|
&.el-popper{
|
&.packaged-dropdown-menu-popover{
|
padding: 0;
|
}
|
}
|
}
|
.packaged-dropdown-menu-popper{
|
position: relative;
|
width: 100%;
|
box-sizing: border-box;
|
&.has-bottom,&.has-search{
|
min-height: 52px;
|
}
|
&.has-bottom.has-search{
|
min-height: 102px;
|
}
|
.search-bar,.bottom-row{
|
position: absolute;
|
width: 100%;
|
left: 0;
|
box-sizing: border-box;
|
}
|
.search-bar{
|
padding: 8px;
|
border-bottom: 1px solid #e4e7ed;
|
top:0;
|
}
|
.bottom-row{
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 4px 8px;
|
border-top: 1px solid #e4e7ed;
|
bottom: 0;
|
}
|
.menu-list{
|
height: 100%;
|
}
|
.menu-list-item-group{
|
padding: 6px 0;
|
}
|
.menu-list-item{
|
display: flex;
|
align-items: center;
|
padding: 2px 8px;
|
cursor: default;
|
&:hover{
|
background-color: #F0F8FF;
|
}
|
.item-checkbox{
|
width: 22px;
|
flex-shrink: 0;
|
display: flex;
|
align-items: center;
|
.el-checkbox{
|
height: auto;
|
}
|
}
|
.item-content{
|
flex: 1;
|
word-break:break-all;
|
word-wrap:break-word;
|
font-size: 14px;
|
font-weight: normal;
|
line-height: 1.4em;
|
color: #333333;
|
}
|
}
|
}
|
</style>
|