dootask/resources/assets/js/components/DropdownMenu.vue
2025-04-19 07:57:43 +08:00

199 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<EDropdown
ref="dropdown"
trigger="click"
class="general-operation-dropdown"
placement="bottom"
:size="size"
:style="styles"
@command="onCommand"
@visible-change="visibleChange">
<div ref="icon" class="general-operation-icon"></div>
<EDropdownMenu ref="dropdownMenu" slot="dropdown" class="general-operation-more-dropdown menu-dropdown">
<li class="general-operation-more-warp" :class="size">
<ul :style="ulStyle">
<EDropdownItem
v-for="(item, key) in list"
:key="key"
:command="item.value"
:divided="!!item.divided"
:disabled="(active === item.value && !activeClick) || !!item.disabled">
<div class="item-box" :style="item.style" :class="item.className">
<div class="item">{{language ? $L(item.label) : item.label}}</div>
<div v-if="tickShow" class="tick">
<i v-if="active === item.value && !item.disabled" class="taskfont">&#xe684;</i>
</div>
</div>
</EDropdownItem>
</ul>
</li>
</EDropdownMenu>
</EDropdown>
</template>
<script>
import {mapState} from "vuex";
export default {
data() {
return {
visible: false,
list: [], // 数据列表: [{label: '', value: ''}]
size: 'small', // 下拉框大小
active: '', // 当前选中的值
activeClick: false, // 当前选中的值是否可以被点击
onUpdate: null, // 选中后的回调函数
scrollHide: true, // 滚动立即隐藏
tickShow: true, // 是否显示打勾默认为true如果 active === undefined 默认为false
maxHeight: 0, // 滚动区域最大高度
language: true, // 是否国际化 item.label
element: null,
target: null,
styles: {},
}
},
beforeDestroy() {
if (this.target) {
this.target.removeEventListener('scroll', this.handlerEventListeners);
}
},
computed: {
...mapState(['menuOperation']),
ulStyle({maxHeight}) {
return maxHeight > 0 ? {maxHeight: `${maxHeight}px`} : {};
}
},
watch: {
menuOperation(data) {
if (data.event && data.list) {
if (this.$refs.dropdown.visible && this.element === data.event.target) {
this.hide();
return;
}
const eventRect = data.event.target.getBoundingClientRect();
this.styles = {
left: `${eventRect.left}px`,
top: `${eventRect.top}px`,
width: `${eventRect.width}px`,
height: `${eventRect.height}px`,
}
this.list = data.list;
this.size = ['small', 'medium', 'large'].includes(data.size) ? data.size : 'small';
this.active = data.active && this.list.find(item => item.value === data.active) ? data.active : '';
this.activeClick = typeof data.activeClick === "boolean" ? data.activeClick : false;
this.onUpdate = typeof data.onUpdate === "function" ? data.onUpdate : null;
this.scrollHide = typeof data.scrollHide === "boolean" ? data.scrollHide : true;
this.tickShow = typeof data.tickShow === "boolean" ? data.tickShow : (typeof data.active !== "undefined");
this.maxHeight = typeof data.maxHeight === "number" ? data.maxHeight : 0;
this.language = typeof data.language === "boolean" ? data.language : true;
//
this.$refs.icon.focus();
this.show();
this.updatePopper();
this.setupEventListeners(data.event)
} else {
this.hide()
}
}
},
methods: {
show() {
this.$refs.dropdown.show()
},
hide() {
this.$refs.dropdown.hide()
},
onCommand(value) {
this.hide();
if (typeof this.onUpdate === "function") {
this.onUpdate(value);
}
},
visibleChange(visible) {
this.visible = visible;
},
updatePopper() {
setTimeout(() => {
this.$refs.dropdownMenu.updatePopper();
}, 0);
},
setupEventListeners(event) {
this.element = event.target;
let target = this.getScrollParent(this.element);
if (target === window.document.body || target === window.document.documentElement) {
target = window;
}
if (this.target) {
if (this.target === target) {
return;
}
this.target.removeEventListener('scroll', this.handlerEventListeners);
}
this.target = target;
this.target.addEventListener('scroll', this.handlerEventListeners);
},
handlerEventListeners(e) {
if (!this.visible || !this.element) {
return
}
if (this.scrollHide) {
this.hide();
return;
}
const scrollRect = e.target.getBoundingClientRect();
const eventRect = this.element.getBoundingClientRect();
if (eventRect.top < scrollRect.top || eventRect.top > scrollRect.top + scrollRect.height) {
this.hide();
return;
}
this.styles = {
left: `${eventRect.left}px`,
top: `${eventRect.top}px`,
width: `${eventRect.width}px`,
height: `${eventRect.height}px`,
};
this.updatePopper();
},
getScrollParent(element) {
const parent = element.parentNode;
if (!parent) {
return element;
}
if (parent === window.document) {
if (window.document.body.scrollTop || window.document.body.scrollLeft) {
return window.document.body;
} else {
return window.document.documentElement;
}
}
if (
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow')) !== -1 ||
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow-x')) !== -1 ||
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow-y')) !== -1
) {
return parent;
}
return this.getScrollParent(element.parentNode);
},
getStyleComputedProperty(element, property) {
const css = window.getComputedStyle(element, null);
return css[property];
}
}
}
</script>