使用el-pop + el-cascader-panel封装
每次级联树展开时,需要动态计算弹窗的宽度,还不如直接使用el-cascader + el-button进行封装
<template>
<el-popover
ref="popover"
placement="bottom"
width="200"
:trigger="trigger"
v-model="visible"
>
<el-cascader-panel
:options="children"
@change="handleCascaderClick"
:props="cascaderProps"
></el-cascader-panel>
<slot name="button" slot="reference">
<el-button
v-if="children.length"
:type="buttonConfig.type || 'text'"
:size="buttonConfig.size"
v-permissions="getPermissionCode(buttonConfig)"
@click="handleClick(buttonConfig)"
v-bind="{ ...buttonConfig }"
>
{{ buttonConfig.btn}}
<i
class="el-icon-arrow-down el-icon--right"
v-if="!buttonConfig.hideArrow"
></i>
</el-button>
</slot>
</el-popover>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
const defaultCascaderProps = {
expandTrigger: 'hover',
value: 'key',
label: 'btn',
}
@Component({
name: 'BaseCascadeDropDown' //? 级联下拉按钮
})
export default class extends Vue {
@Prop({ default: () => ({}) }) buttonConfig!: any //? 下拉按钮的配置
@Prop({ default: () => ({}) }) data?: any //? 下拉按钮的数据,用于动态属性的配置,后面也会将数据传出入
@Prop({ default: () => [] }) children: any[] //? 下拉按钮的选项
@Prop({ default: 'hover' }) trigger: string //? 气泡的触发方式,,默认悬浮展示
@Prop({ default: 'hover' }) expandTrigger: string //? 级联次级菜单的展开方式,默认悬浮展示
@Prop({ default: () => defaultCascaderProps }) cascaderProps: any //? 级联的配置选项,次级菜单展开方式,默认悬浮展示
//* 事件监听
//* eventHandler //? 按钮列的总事件监听
hide = false
visible = false
mounted() {
const popoverRef: any = this.$refs.popover
// if (!popoverRef.popperElm.children.length) {
// this.hide = true
// }
}
togglePopover() {
this.visible = !this.visible;
}
getPermissionCode(button) { //todo 获取权限码
if (typeof button.permissionCode === 'function') {
return button.permissionCode(this.data, button.key)
}
return button.permissionCode
}
handleClick(button) {
this.togglePopover()
let loadingInstance
if (button.needLoading) {
loadingInstance = this.$loading({
lock: button.loadLock || true,
text: button.loadText || 'Loading',
spinner: button.loadSpinner || 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
}
const params = {
event: button.key,
data: this.data,
extraParams: {
button,
closeLoading: () => {
button.needLoading && loadingInstance.close()
}
}
}
if (typeof button.submit === 'function') {
button.submit(params)
}
this.handleEventHandler(params)
}
handleCascaderClick(value) {
this.visible = false;
const subButtonConfig = this.children.find(
(subButton) => subButton.key === value
) //? 子按钮的配置
let loadingInstance
if (subButtonConfig.needLoading) {
loadingInstance = this.$loading({
lock: subButtonConfig.loadLock || true,
text: subButtonConfig.loadText || 'Loading',
spinner: subButtonConfig.loadSpinner || 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
}
const params = {
event: subButtonConfig.key,
data: this.data,
extraParams: {
button: subButtonConfig,
subButtonConfig,
closeLoading: () => {
subButtonConfig.needLoading && loadingInstance.close()
}
}
}
if (typeof subButtonConfig.submit === 'function') {
subButtonConfig.submit(params)
}
this.handleEventHandler(params)
}
needHidden(button) {
//todo 是否需要按钮隐藏
if (typeof button.hidden === 'function') {
return !!button.hidden(this.data, button.key || button.permissionCode)
}
return button.hidden
}
needDisabled(button) {
//todo 判断是否需要禁用按钮
if (typeof button.disabled === 'function') {
return button.disabled(this.data, button.key || button.permissionCode)
}
return button.disabled
}
handleEventHandler({ event, data, extraParams = {} }) {
//todo 调用外部方法
this.$emit('eventHandler', {
event,
data,
extraParams
})
}
}
</script>
<style lang="scss" scoped>
</style>
使用el-cascader + el-button进行封装
1、隐藏el-cascader里面的input框;
2、点击按钮时,弹出el-cascader会自动计算宽度的弹窗
3、每次点击(后)需要将勾选状态重置 —— 发现el-cascader组件内部问题,修改其勾选状态不生效,直接修改el-cascader组件内的数据也不生效,只能将其销毁重建
<template>
<div>
<el-cascader
ref="cascader"
v-model="value"
v-if="cascaderShow"
class="cascader_dropdown"
:options="children"
@change="handleCascaderClick"
:props="cascaderProps"
></el-cascader>
<slot name="button">
<el-button
v-if="children.length"
:type="buttonConfig.type || 'text'"
:size="buttonConfig.size"
v-permissions="getPermissionCode(buttonConfig)"
@click="handleClick(buttonConfig)"
v-bind="{ ...buttonConfig }"
>
{{ buttonConfig.btn }}
<i
class="el-icon-arrow-down el-icon--right"
v-if="!buttonConfig.hideArrow"
></i>
</el-button>
</slot>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { isBoolean } from 'xe-utils'
const defaultCascaderProps = {
expandTrigger: 'hover',
multiple: false,
value: 'key',
label: 'btn',
children: 'children'
}
@Component({
name: 'BaseCascadeDropDown' //? 级联下拉按钮
})
export default class extends Vue {
@Prop({ default: () => ({}) }) buttonConfig!: any //? 下拉按钮的配置
@Prop({ default: () => ({}) }) data?: any //? 下拉按钮的数据,用于动态属性的配置,后面也会将数据传出入
@Prop({ default: () => [] }) children: any[] //? 下拉按钮的选项
@Prop({ default: 'hover' }) trigger: string //? 气泡的触发方式,,默认悬浮展示
@Prop({ default: 'hover' }) expandTrigger: string //? 级联次级菜单的展开方式,默认悬浮展示
@Prop({ default: () => defaultCascaderProps }) cascaderProps: any //? 级联的配置选项,次级菜单展开方式,默认悬浮展示
//* 事件监听
//* eventHandler //? 按钮列的总事件监听
value = []
hide = false
visible = false
cascaderShow = true
mounted() {
const cascaderRef: any = this.$refs.cascader;
// if (!popoverRef.popperElm.children.length) {
// this.hide = true
// }
}
toggleDropDownVisible(visible?) {
const cascaderRef: any = this.$refs.cascader;
this.visible = isBoolean(visible) ? visible : !this.visible;
cascaderRef.toggleDropDownVisible(this.visible)
}
getPermissionCode(button) { //todo 获取权限码
if (typeof button.permissionCode === 'function') {
return button.permissionCode(this.data, button.key)
}
return button.permissionCode
}
findCurrentItem(valuePath, data) { //todo 根据路径找到对应树形结构中的某个节点
const { value, children } = this.cascaderProps;
if (Array.isArray(valuePath) && valuePath.length) {
const currentItem = data.find(item => item[value] === valuePath[0])
valuePath.shift()
if (!valuePath.length) { //* 没有下一级
//* 找到当前级别的数据
return currentItem
}
return this.findCurrentItem(valuePath, currentItem[children])
}
}
handleClick(button) {
this.toggleDropDownVisible()
let loadingInstance
if (button.needLoading) {
loadingInstance = this.$loading({
lock: button.loadLock || true,
text: button.loadText || 'Loading',
spinner: button.loadSpinner || 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
}
const params = {
event: button.key,
data: this.data,
extraParams: {
button,
closeLoading: () => {
button.needLoading && loadingInstance.close()
}
}
}
if (typeof button.submit === 'function') {
button.submit(params)
}
this.handleEventHandler(params)
}
handleCascaderClick(valuePath) {
if (Array.isArray(valuePath) && valuePath.length) {
const subButtonConfig = this.findCurrentItem(valuePath, this.children)
let loadingInstance
if (subButtonConfig.needLoading) {
loadingInstance = this.$loading({
lock: subButtonConfig.loadLock || true,
text: subButtonConfig.loadText || 'Loading',
spinner: subButtonConfig.loadSpinner || 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
}
const params = {
event: subButtonConfig.key,
data: this.data,
extraParams: {
button: subButtonConfig,
subButtonConfig,
closeLoading: () => {
subButtonConfig.needLoading && loadingInstance.close()
}
}
}
if (typeof subButtonConfig.submit === 'function') {
subButtonConfig.submit(params)
}
this.handleEventHandler(params)
}
this.toggleDropDownVisible(false) //* 关闭弹窗
this.visible = false
this.cascaderShow = false; //* 临时将其销毁重建
this.$nextTick(() => {
this.cascaderShow = true;
})
}
needHidden(button) {
//todo 是否需要按钮隐藏
if (typeof button.hidden === 'function') {
return !!button.hidden(this.data, button.key || button.permissionCode)
}
return button.hidden
}
needDisabled(button) {
//todo 判断是否需要禁用按钮
if (typeof button.disabled === 'function') {
return button.disabled(this.data, button.key || button.permissionCode)
}
return button.disabled
}
handleEventHandler({ event, data, extraParams = {} }) {
//todo 调用外部方法
this.$emit('eventHandler', {
event,
data,
extraParams
})
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.cascader_dropdown {
input {
display: none;
}
}
// .el-cascader__dropdown {
// .el-cascader-menu {
// min-width: 130px !important;
// }
// }
}
</style>