114 lines
3.1 KiB
Vue
Raw Normal View History

2023-08-10 14:48:22 +08:00
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, onBeforeUnmount, onMounted, ref } from "vue"
2023-08-10 14:48:22 +08:00
import { type RouteRecordName, type RouteRecordRaw } from "vue-router"
interface Props {
list: RouteRecordRaw[]
isPressUpOrDown: boolean
2023-08-10 14:48:22 +08:00
}
/** 选中的菜单 */
const modelValue = defineModel<RouteRecordName | undefined>({ required: true })
2023-08-10 14:48:22 +08:00
const props = defineProps<Props>()
const instance = getCurrentInstance()
const scrollbarHeight = ref<number>(0)
/** 菜单的样式 */
const itemStyle = (item: RouteRecordRaw) => {
const flag = item.name === modelValue.value
2023-08-10 14:48:22 +08:00
return {
background: flag ? "var(--el-color-primary)" : "",
2024-03-28 21:34:24 +08:00
color: flag ? "#ffffff" : ""
2023-08-10 14:48:22 +08:00
}
}
/** 鼠标移入 */
const handleMouseenter = (item: RouteRecordRaw) => {
// 如果上键或下键与 mouseenter 事件同时生效,则以上下键为准,不执行该函数的赋值逻辑
if (props.isPressUpOrDown) return
modelValue.value = item.name
2023-08-10 14:48:22 +08:00
}
/** 计算滚动可视区高度 */
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" />
2023-08-10 14:48:22 +08:00
</div>
</div>
</template>
<style lang="scss" scoped>
2024-11-13 17:51:34 +08:00
@import "@/styles/mixins.scss";
2023-08-10 14:48:22 +08:00
.result-item {
display: flex;
align-items: center;
height: 56px;
padding: 0 15px;
2024-11-13 17:51:34 +08:00
margin-bottom: 8px;
2023-08-10 14:48:22 +08:00
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;
2024-11-13 17:51:34 +08:00
@extend %ellipsis;
2023-08-10 14:48:22 +08:00
}
}
</style>