2024-11-13 17:51:34 +08:00

114 lines
3.1 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.

<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, onBeforeUnmount, onMounted, ref } from "vue"
import { type RouteRecordName, type RouteRecordRaw } from "vue-router"
interface Props {
list: RouteRecordRaw[]
isPressUpOrDown: boolean
}
/** 选中的菜单 */
const modelValue = defineModel<RouteRecordName | undefined>({ required: true })
const props = defineProps<Props>()
const instance = getCurrentInstance()
const scrollbarHeight = ref<number>(0)
/** 菜单的样式 */
const itemStyle = (item: RouteRecordRaw) => {
const flag = item.name === modelValue.value
return {
background: flag ? "var(--el-color-primary)" : "",
color: flag ? "#ffffff" : ""
}
}
/** 鼠标移入 */
const handleMouseenter = (item: RouteRecordRaw) => {
// 如果上键或下键与 mouseenter 事件同时生效,则以上下键为准,不执行该函数的赋值逻辑
if (props.isPressUpOrDown) return
modelValue.value = item.name
}
/** 计算滚动可视区高度 */
const getScrollbarHeight = () => {
// el-scrollbar max-height="40vh"
scrollbarHeight.value = Number((window.innerHeight * 0.4).toFixed(1))
}
/** 根据下标计算到顶部的距离 */
const getScrollTop = (index: number) => {
const currentInstance = instance?.proxy?.$refs[`resultItemRef${index}`] as HTMLDivElement[]
if (!currentInstance) return 0
const currentRef = currentInstance[0]
const scrollTop = currentRef.offsetTop + 128 // 128 = 两个 result-item 56 + 56 = 112高度与上下 margin8 + 8 = 16大小之和
return scrollTop > scrollbarHeight.value ? scrollTop - scrollbarHeight.value : 0
}
/** 在组件挂载前添加窗口大小变化事件监听器 */
onBeforeMount(() => {
window.addEventListener("resize", getScrollbarHeight)
})
/** 在组件挂载时立即计算滚动可视区高度 */
onMounted(() => {
getScrollbarHeight()
})
/** 在组件卸载前移除窗口大小变化事件监听器 */
onBeforeUnmount(() => {
window.removeEventListener("resize", getScrollbarHeight)
})
defineExpose({ getScrollTop })
</script>
<template>
<!-- 外层 div 不能删除是用来接收父组件 click 事件的 -->
<div>
<div
v-for="(item, index) in list"
:key="index"
:ref="`resultItemRef${index}`"
class="result-item"
:style="itemStyle(item)"
@mouseenter="handleMouseenter(item)"
>
<SvgIcon v-if="item.meta?.svgIcon" :name="item.meta.svgIcon" />
<component v-else-if="item.meta?.elIcon" :is="item.meta.elIcon" class="el-icon" />
<span class="result-item-title">
{{ item.meta?.title }}
</span>
<SvgIcon v-if="modelValue && modelValue === item.name" name="keyboard-enter" />
</div>
</div>
</template>
<style lang="scss" scoped>
@import "@/styles/mixins.scss";
.result-item {
display: flex;
align-items: center;
height: 56px;
padding: 0 15px;
margin-bottom: 8px;
border: 1px solid var(--el-border-color);
border-radius: 4px;
cursor: pointer;
.svg-icon {
min-width: 1em;
font-size: 18px;
}
.el-icon {
width: 1em;
font-size: 18px;
}
&-title {
flex: 1;
margin-left: 12px;
@extend %ellipsis;
}
}
</style>