提交 68367fb8 编写于 作者: Z zhaoke

Merge branch 'main' of git.zcorp.cc:pangu/zentaoatf

<template> <template>
<div <div
class="dropdown-menu" class="dropdown-menu"
:class="menuClass ?? 'layer rounded'" :class="menuClass ?? 'layer rounded'"
:style="menuFinalStyle" :style="menuFinalStyle"
@click="_handleClickMenu" @click="_handleClickMenu"
ref="menuRef" ref="menuRef"
> >
<template v-if="state.show"> <template v-if="state.show">
<input type="text" class="keywords"
v-if="filter"
v-model="keywords"
@input="onKeywordsChanged"
@click.stop />
<List <List
:items="items" :items="itemsToShow"
:replaceFields="replaceFields" :replaceFields="replaceFields"
:checkedKey="checkedKey" :checkedKey="checkedKey"
:activeKey="activeKey" :activeKey="activeKey"
:keyName="keyName" :keyName="keyName"
:class="listClass" :class="listClass"
:compact="listCompact" :compact="listCompact"
:divider="listDivider" :divider="listDivider"
@click="emit('click', $event)" @click="emit('click', $event)"
/> />
<slot /> <slot/>
</template> </template>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { withDefaults, defineProps, ref, reactive, onMounted, onUnmounted, computed, defineEmits } from 'vue'; import {computed, defineEmits, defineProps, onMounted, onUnmounted, reactive, ref, withDefaults} from 'vue';
import { useWindowSize, onClickOutside } from '@vueuse/core' import {onClickOutside, useWindowSize} from '@vueuse/core'
import List from './List.vue'; import List from './List.vue';
import {ListItemKey, ListItemProps} from './ListItem.vue'; import {ListItemKey, ListItemProps} from './ListItem.vue';
export type DropdownMenuPosition = 'bottom' | 'left' | 'right' | 'top' | 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom' |{left: number, top: number} | ((toggleElement: HTMLElement) => {left: number, top: number}); export type DropdownMenuPosition =
'bottom'
| 'left'
| 'right'
| 'top'
| 'bottom-left'
| 'bottom-right'
| 'top-left'
| 'top-right'
| 'left-top'
| 'left-bottom'
| 'right-top'
| 'right-bottom'
| { left: number, top: number }
| ((toggleElement: HTMLElement) => { left: number, top: number });
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
toggle?: object | string, toggle?: object | string,
triggerEvent?: string, triggerEvent?: string,
position?: DropdownMenuPosition, position?: DropdownMenuPosition,
items?: ListItemProps[] | Record<string, any>[], items?: ListItemProps[] | Record<string, any>[],
keyName?: string, keyName?: string,
checkedKey?: ListItemKey, checkedKey?: ListItemKey,
activeKey?: ListItemKey, activeKey?: ListItemKey,
replaceFields?: Record<string, string>, replaceFields?: Record<string, string>,
listClass?: string, listClass?: string,
listCompact?: boolean, listCompact?: boolean,
listDivider?: boolean, listDivider?: boolean,
defaultShow?: boolean, defaultShow?: boolean,
showOnHover?: boolean, showOnHover?: boolean,
hideOnClickAway?: boolean, hideOnClickAway?: boolean,
hideOnClickMenu?: boolean, hideOnClickMenu?: boolean,
menuClass?: string, menuClass?: string,
limitInWindow?: boolean, limitInWindow?: boolean,
menuStyle?: object menuStyle?: object
filter?: boolean
}>(), {hideOnClickAway: true, hideOnClickMenu: true}); }>(), {hideOnClickAway: true, hideOnClickMenu: true});
const menuRef = ref<HTMLElement>(); const menuRef = ref<HTMLElement>();
...@@ -57,166 +78,178 @@ const cleanUpRef = ref(); ...@@ -57,166 +78,178 @@ const cleanUpRef = ref();
const state = reactive({show: !!props.defaultShow, showed: false}); const state = reactive({show: !!props.defaultShow, showed: false});
const showTimerRef = ref<number>(0); const showTimerRef = ref<number>(0);
const keywords = ref('')
function _toggle(show?: boolean) { function _toggle(show?: boolean) {
if (show === undefined) { if (show === undefined) {
show = !state.show; show = !state.show;
} }
if (state.show === show) { if (state.show === show) {
return; return;
} }
if (showTimerRef.value) { if (showTimerRef.value) {
clearTimeout(showTimerRef.value); clearTimeout(showTimerRef.value);
showTimerRef.value = 0; showTimerRef.value = 0;
} }
state.show = show; state.show = show;
state.showed = false; state.showed = false;
if (show) { if (show) {
showTimerRef.value = setTimeout(() => { showTimerRef.value = setTimeout(() => {
state.showed = true; state.showed = true;
showTimerRef.value = 0; showTimerRef.value = 0;
}, 100); }, 100);
} }
} }
function _handleClickMenu(event: MouseEvent) { function _handleClickMenu(event: MouseEvent) {
if (!props.hideOnClickMenu || event.target && event.target instanceof HTMLElement && event.target.closest('.not-hide-menu')) { if (!props.hideOnClickMenu || event.target && event.target instanceof HTMLElement && event.target.closest('.not-hide-menu')) {
return; return;
} }
if (state.showed) { if (state.showed) {
_toggle(false); _toggle(false);
} }
} }
const emit = defineEmits<{(type: 'click', event: {originalEvent: Event, key: ListItemKey, item: ListItemProps | Record<string, any>}) : void}>(); const itemsToShow = computed(() => {
let arr = props.items?.filter((item) => {
return !keywords.value || item.name.indexOf(keywords.value) > -1;
})
return arr
})
const onKeywordsChanged = () => {
console.log('onKeywordsChanged')
}
const emit = defineEmits<{ (type: 'click', event: { originalEvent: Event, key: ListItemKey, item: ListItemProps | Record<string, any> }): void }>();
onClickOutside(menuRef, _event => { onClickOutside(menuRef, _event => {
if (props.hideOnClickAway && state.showed) { if (props.hideOnClickAway && state.showed) {
_toggle(false); _toggle(false);
} }
}); });
function getToggleElement() { function getToggleElement() {
const {toggle} = props; const {toggle} = props;
if (!toggle) { if (!toggle) {
if (menuRef.value) { if (menuRef.value) {
return menuRef.value.closest('.dropdown')?.querySelector('.dropdown-toggle') as HTMLElement || undefined; return menuRef.value.closest('.dropdown')?.querySelector('.dropdown-toggle') as HTMLElement || undefined;
}
return undefined;
}
if (toggle instanceof HTMLElement) {
return toggle;
}
if (typeof toggle === 'string') {
return document.querySelector(toggle) as HTMLElement || undefined;
} }
return undefined; return undefined;
}
if (toggle instanceof HTMLElement) {
return toggle;
}
if (typeof toggle === 'string') {
return document.querySelector(toggle) as HTMLElement || undefined;
}
return undefined;
} }
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const menuFinalStyle = computed(() => { const menuFinalStyle = computed(() => {
if (!state.show) { if (!state.show) {
return {display: 'none'}; return {display: 'none'};
} }
const style: Record<string, any> = { const style: Record<string, any> = {
display: 'block', display: 'block',
opacity: 1, opacity: 1,
top: '0px', top: '0px',
left: '0px', left: '0px',
}; };
const element = getToggleElement(); const element = getToggleElement();
if (!element || !menuRef.value) { if (!element || !menuRef.value) {
return style; return style;
} }
if (!state.showed) { if (!state.showed) {
style.opacity = 0; style.opacity = 0;
return style; return style;
} }
const {position = 'bottom-left'} = props; const {position = 'bottom-left'} = props;
const bounding = element.getBoundingClientRect(); const bounding = element.getBoundingClientRect();
const menuBounding = menuRef.value.getBoundingClientRect(); const menuBounding = menuRef.value.getBoundingClientRect();
if (typeof position === 'function') { if (typeof position === 'function') {
Object.assign(style, position(element)); Object.assign(style, position(element));
} else if (typeof position === 'object') { } else if (typeof position === 'object') {
Object.assign(style, position); Object.assign(style, position);
} else if (position === 'bottom') { } else if (position === 'bottom') {
style.top = bounding.bottom; style.top = bounding.bottom;
style.left = Math.round(bounding.left + (bounding.width / 2) - (menuBounding.width / 2)); style.left = Math.round(bounding.left + (bounding.width / 2) - (menuBounding.width / 2));
} else if (position === 'bottom-left') { } else if (position === 'bottom-left') {
style.top = bounding.bottom; style.top = bounding.bottom;
style.left = bounding.left; style.left = bounding.left;
} else if (position === 'bottom-right') { } else if (position === 'bottom-right') {
style.top = bounding.bottom; style.top = bounding.bottom;
style.left = bounding.right - menuBounding.width; style.left = bounding.right - menuBounding.width;
} else if (position === 'top') { } else if (position === 'top') {
style.top = bounding.top - menuBounding.height; style.top = bounding.top - menuBounding.height;
style.left = bounding.left + (bounding.width / 2) - (menuBounding.width / 2); style.left = bounding.left + (bounding.width / 2) - (menuBounding.width / 2);
} else if (position === 'top-left') { } else if (position === 'top-left') {
style.top = bounding.top - menuBounding.height; style.top = bounding.top - menuBounding.height;
style.left = bounding.left; style.left = bounding.left;
} else if (position === 'top-right') { } else if (position === 'top-right') {
style.top = bounding.top - menuBounding.height; style.top = bounding.top - menuBounding.height;
style.left = bounding.right - menuBounding.width; style.left = bounding.right - menuBounding.width;
} else if (position === 'left') { } else if (position === 'left') {
style.left = bounding.left - menuBounding.width; style.left = bounding.left - menuBounding.width;
style.top = bounding.top + (bounding.height / 2) - (menuBounding.height / 2); style.top = bounding.top + (bounding.height / 2) - (menuBounding.height / 2);
} else if (position === 'left-top') { } else if (position === 'left-top') {
style.left = bounding.left - menuBounding.width; style.left = bounding.left - menuBounding.width;
style.top = bounding.top; style.top = bounding.top;
} else if (position === 'left-bottom') { } else if (position === 'left-bottom') {
style.left = bounding.left - menuBounding.width; style.left = bounding.left - menuBounding.width;
style.top = bounding.bottom - menuBounding.height; style.top = bounding.bottom - menuBounding.height;
} else if (position === 'right') { } else if (position === 'right') {
style.left = bounding.left - menuBounding.width; style.left = bounding.left - menuBounding.width;
} else if (position === 'right-top') { } else if (position === 'right-top') {
style.left = bounding.right; style.left = bounding.right;
style.top = bounding.top; style.top = bounding.top;
} else if (position === 'right-bottom') { } else if (position === 'right-bottom') {
style.left = bounding.right; style.left = bounding.right;
style.top = bounding.bottom - menuBounding.height; style.top = bounding.bottom - menuBounding.height;
} }
if (props.limitInWindow !== false) { if (props.limitInWindow !== false) {
style.left = Math.min(windowSize.width.value - menuBounding.width, style.left); style.left = Math.min(windowSize.width.value - menuBounding.width, style.left);
style.top = Math.min(windowSize.height.value - menuBounding.height, style.top); style.top = Math.min(windowSize.height.value - menuBounding.height, style.top);
} }
style.maxHeight = `${Math.round(windowSize.height.value - style.top - 10)}px`; style.maxHeight = `${Math.round(windowSize.height.value - style.top - 10)}px`;
style.maxWidth = `${Math.round(windowSize.width.value - style.left - 10)}px`; style.maxWidth = `${Math.round(windowSize.width.value - style.left - 10)}px`;
style.left = `${Math.round(style.left)}px`; style.left = `${Math.round(style.left)}px`;
style.top = `${Math.round(style.top)}px`; style.top = `${Math.round(style.top)}px`;
style.overflow = 'auto'; style.overflow = 'auto';
if (props.menuStyle) {
Object.assign(style, props.menuStyle);
}
return style; if (props.menuStyle) {
Object.assign(style, props.menuStyle);
}
return style;
}); });
onMounted(() => { onMounted(() => {
const toggleElement = getToggleElement(); const toggleElement = getToggleElement();
if (!toggleElement) { if (!toggleElement) {
return; return;
} }
const triggerEvent = props.triggerEvent ?? 'click'; const triggerEvent = props.triggerEvent ?? 'click';
const handler = (_event) => { const handler = (_event) => {
if (state.showed) { if (state.showed) {
_toggle(false); _toggle(false);
} else if (!state.show) { } else if (!state.show) {
_toggle(true); _toggle(true);
} }
}; };
toggleElement.addEventListener(triggerEvent, handler, false); toggleElement.addEventListener(triggerEvent, handler, false);
cleanUpRef.value = () => { cleanUpRef.value = () => {
toggleElement.removeEventListener(triggerEvent, handler, false); toggleElement.removeEventListener(triggerEvent, handler, false);
}; };
}); });
onUnmounted(() => { onUnmounted(() => {
if (cleanUpRef.value) { if (cleanUpRef.value) {
cleanUpRef.value(); cleanUpRef.value();
} }
}) })
</script> </script>
...@@ -228,11 +261,25 @@ onUnmounted(() => { ...@@ -228,11 +261,25 @@ onUnmounted(() => {
padding-top: var(--space-sm); padding-top: var(--space-sm);
padding-bottom: var(--space-sm); padding-bottom: var(--space-sm);
} }
.dropdown-menu :deep(.list-item) { .dropdown-menu :deep(.list-item) {
padding-left: var(--space-lg); padding-left: var(--space-lg);
padding-right: var(--space-lg); padding-right: var(--space-lg);
} }
.dropdown-menu :deep(.list-item.has-checkmark) { .dropdown-menu :deep(.list-item.has-checkmark) {
padding-right: var(--space-sm); padding-right: var(--space-sm);
} }
.dropdown-menu .keywords {
margin: 3px 10px;
padding: 0 5px;
height: 22px;
outline: 0;
}
.dropdown-menu .keywords input {
}
</style> </style>
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
<DropdownMenu <DropdownMenu
v-if="products.length > 0" v-if="products.length > 0"
:filter="true"
toggle="#productMenuToggle" toggle="#productMenuToggle"
class="scrollbar-y scrollbar-hover" class="scrollbar-y scrollbar-hover"
:items="products" :items="products"
...@@ -43,6 +44,7 @@ ...@@ -43,6 +44,7 @@
@click="selectProduct" @click="selectProduct"
:replaceFields="replaceFields" :replaceFields="replaceFields"
/> />
<SitesModal <SitesModal
:show="showSitesModal" :show="showSitesModal"
@cancel="sitesModalClose" @cancel="sitesModalClose"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册