优化参数配置,简化组件构建
This commit is contained in:
13
src/App.vue
13
src/App.vue
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ProjectList from './components/ProjectList.vue'
|
import ProjectList from './components/ProjectList.vue'
|
||||||
import type { ListItem, MenuItem, ListConfig } from '@/types'
|
import type { ListItem, MenuItem } from '@/types'
|
||||||
|
|
||||||
// 生成模拟数据
|
// 生成模拟数据
|
||||||
const listData: ListItem[] = Array.from({ length: 789 }, (_, i) => ({
|
const listData: ListItem[] = Array.from({ length: 789 }, (_, i) => ({
|
||||||
@@ -15,14 +15,6 @@ const listData: ListItem[] = Array.from({ length: 789 }, (_, i) => ({
|
|||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 自定义配置
|
|
||||||
const listConfig: ListConfig = {
|
|
||||||
itemHeight: 50,
|
|
||||||
itemPadding: 16,
|
|
||||||
bufferCount: 10,
|
|
||||||
scrollDebounceTime: 16,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自定义菜单项
|
// 自定义菜单项
|
||||||
const menuItems: MenuItem[] = [
|
const menuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
@@ -57,10 +49,7 @@ const handleItemClick = (item: ListItem): void => {
|
|||||||
<template>
|
<template>
|
||||||
<ProjectList
|
<ProjectList
|
||||||
:data="listData"
|
:data="listData"
|
||||||
:config="listConfig"
|
|
||||||
:menu-items="menuItems"
|
:menu-items="menuItems"
|
||||||
:show-toolbar="true"
|
|
||||||
:toolbar-height="40"
|
|
||||||
@click="handleItemClick"
|
@click="handleItemClick"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VirtualList from './VirtualList.vue'
|
import VirtualList from './VirtualList.vue'
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import type { ListItem, MenuItem, ListConfig } from '@/types'
|
import type { ListItem, MenuItem } from '@/types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件属性接口
|
* 组件属性接口
|
||||||
@@ -9,20 +9,17 @@ import type { ListItem, MenuItem, ListConfig } from '@/types'
|
|||||||
interface Props {
|
interface Props {
|
||||||
/** 列表数据 */
|
/** 列表数据 */
|
||||||
data: ListItem[]
|
data: ListItem[]
|
||||||
/** 虚拟列表配置 */
|
|
||||||
config: ListConfig
|
|
||||||
/** 菜单项配置 */
|
/** 菜单项配置 */
|
||||||
menuItems: MenuItem[]
|
menuItems: MenuItem[]
|
||||||
/** 是否显示工具栏 */
|
|
||||||
showToolbar?: boolean
|
|
||||||
/** 工具栏高度 */
|
|
||||||
toolbarHeight?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
showToolbar: true,
|
|
||||||
toolbarHeight: 40
|
// ===== 工具栏配置常量 =====
|
||||||
})
|
/** 是否显示工具栏 */
|
||||||
|
const SHOW_TOOLBAR = true
|
||||||
|
/** 工具栏高度(像素) */
|
||||||
|
const TOOLBAR_HEIGHT = 40
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件事件定义
|
* 组件事件定义
|
||||||
@@ -237,7 +234,6 @@ const onAfterLeave = (el: Element): void => {
|
|||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<VirtualList
|
<VirtualList
|
||||||
:data="data"
|
:data="data"
|
||||||
:config="config"
|
|
||||||
:frozen="listFrozen"
|
:frozen="listFrozen"
|
||||||
:disable-hover="showMenu || temporaryDisableHover"
|
:disable-hover="showMenu || temporaryDisableHover"
|
||||||
@select="handleSelect"
|
@select="handleSelect"
|
||||||
@@ -248,7 +244,7 @@ const onAfterLeave = (el: Element): void => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<div v-if="showToolbar" class="toolbar" :style="{ height: `${toolbarHeight}px` }">
|
<div v-if="SHOW_TOOLBAR" class="toolbar" :style="{ height: `${TOOLBAR_HEIGHT}px` }">
|
||||||
<div class="toolbar-content">
|
<div class="toolbar-content">
|
||||||
<div class="total-count">共 {{ data.length }} 项</div>
|
<div class="total-count">共 {{ data.length }} 项</div>
|
||||||
<div class="toolbar-spacer"></div>
|
<div class="toolbar-spacer"></div>
|
||||||
@@ -335,7 +331,7 @@ const onAfterLeave = (el: Element): void => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 弹性间隔:用于推开左右两侧的内容 */
|
/* 弹性间隔:用<EFBFBD><EFBFBD>推开左右两侧的内容 */
|
||||||
.toolbar-spacer {
|
.toolbar-spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import type { ListItem, ListConfig } from '@/types'
|
import type { ListItem } from '@/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: ListItem[]
|
data: ListItem[]
|
||||||
config: ListConfig
|
|
||||||
height?: string | number
|
height?: string | number
|
||||||
frozen?: boolean
|
frozen?: boolean
|
||||||
disableHover?: boolean
|
disableHover?: boolean
|
||||||
@@ -23,9 +22,19 @@ const emit = defineEmits<{
|
|||||||
showToast: [message: string]
|
showToast: [message: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
// ===== 列表配置常量 =====
|
||||||
|
/** 列表项基础高度(像素) */
|
||||||
|
const ITEM_HEIGHT = 50
|
||||||
|
/** 列表项内边距总和(像素) */
|
||||||
|
const ITEM_PADDING = 16
|
||||||
|
/** 上下缓冲区域的项目数量 */
|
||||||
|
const BUFFER_COUNT = 10
|
||||||
|
/** 滚动防抖时间(毫秒) */
|
||||||
|
const SCROLL_DEBOUNCE_TIME = 16
|
||||||
|
|
||||||
// 计算列表项实际总高度
|
// 计算列表项实际总高度
|
||||||
const totalItemHeight = computed(() =>
|
const totalItemHeight = computed(() =>
|
||||||
props.config.itemHeight + props.config.itemPadding
|
ITEM_HEIGHT + ITEM_PADDING
|
||||||
)
|
)
|
||||||
|
|
||||||
// 响应式状态管理
|
// 响应式状态管理
|
||||||
@@ -46,10 +55,10 @@ const visibleCount = computed(() =>
|
|||||||
|
|
||||||
// 计算当前需要渲染的数据
|
// 计算当前需要渲染的数据
|
||||||
const visibleData = computed(() => {
|
const visibleData = computed(() => {
|
||||||
const visibleStart = Math.max(0, startIndex.value - props.config.bufferCount)
|
const visibleStart = Math.max(0, startIndex.value - BUFFER_COUNT)
|
||||||
const visibleEnd = Math.min(
|
const visibleEnd = Math.min(
|
||||||
props.data.length,
|
props.data.length,
|
||||||
startIndex.value + visibleCount.value + props.config.bufferCount
|
startIndex.value + visibleCount.value + BUFFER_COUNT
|
||||||
)
|
)
|
||||||
|
|
||||||
return props.data.slice(visibleStart, visibleEnd).map((item, index) => ({
|
return props.data.slice(visibleStart, visibleEnd).map((item, index) => ({
|
||||||
@@ -60,7 +69,7 @@ const visibleData = computed(() => {
|
|||||||
|
|
||||||
// 计算列表偏移量
|
// 计算列表偏移量
|
||||||
const offsetY = computed(() =>
|
const offsetY = computed(() =>
|
||||||
Math.max(0, (startIndex.value - props.config.bufferCount) * totalItemHeight.value)
|
Math.max(0, (startIndex.value - BUFFER_COUNT) * totalItemHeight.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 计算虚拟列表总高度
|
// 计算虚拟列表总高度
|
||||||
@@ -117,7 +126,7 @@ function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (..
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理滚动事件(已防抖)
|
* 处理滚动事件(已防抖)
|
||||||
* 根据滚动位置更新可视区域的起始索引
|
* 根据滚动位置更新可视区<EFBFBD><EFBFBD>的起始索引
|
||||||
*/
|
*/
|
||||||
const handleScroll = debounce(() => {
|
const handleScroll = debounce(() => {
|
||||||
if (!containerRef.value) return
|
if (!containerRef.value) return
|
||||||
@@ -127,7 +136,7 @@ const handleScroll = debounce(() => {
|
|||||||
if (newStartIndex !== startIndex.value) {
|
if (newStartIndex !== startIndex.value) {
|
||||||
startIndex.value = newStartIndex
|
startIndex.value = newStartIndex
|
||||||
}
|
}
|
||||||
}, props.config.scrollDebounceTime)
|
}, SCROLL_DEBOUNCE_TIME)
|
||||||
|
|
||||||
/** 鼠标悬停禁用状态 */
|
/** 鼠标悬停禁用状态 */
|
||||||
const disableHover = ref<boolean>(false)
|
const disableHover = ref<boolean>(false)
|
||||||
@@ -293,7 +302,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 初始化容器高度
|
// 初始化容器高度
|
||||||
updateContainerHeight()
|
updateContainerHeight()
|
||||||
// 添加全局事件监听
|
// 添加全局事<EFBFBD><EFBFBD>监听
|
||||||
window.addEventListener('resize', updateContainerHeight)
|
window.addEventListener('resize', updateContainerHeight)
|
||||||
window.addEventListener('keydown', handleKeyDown)
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
})
|
})
|
||||||
@@ -341,7 +350,7 @@ onBeforeUnmount(() => {
|
|||||||
:key="item.index"
|
:key="item.index"
|
||||||
class="list-item"
|
class="list-item"
|
||||||
:class="{ 'selected': selectedIndex === item.index }"
|
:class="{ 'selected': selectedIndex === item.index }"
|
||||||
:style="{ height: `${config.itemHeight}px` }"
|
:style="{ height: `${ITEM_HEIGHT}px` }"
|
||||||
@mouseenter="handleMouseEnter(item.index)"
|
@mouseenter="handleMouseEnter(item.index)"
|
||||||
@click="handleClick(item)"
|
@click="handleClick(item)"
|
||||||
@contextmenu="handleContextMenu($event, item)"
|
@contextmenu="handleContextMenu($event, item)"
|
||||||
|
|||||||
@@ -37,17 +37,3 @@ export interface MenuItem {
|
|||||||
/** 菜单项点击处理函数,接收当前选中的列表项作为参数 */
|
/** 菜单项点击处理函数,接收当前选中的列表项作为参数 */
|
||||||
action: (item: ListItem | null) => void
|
action: (item: ListItem | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 虚拟列表配置接口
|
|
||||||
*/
|
|
||||||
export interface ListConfig {
|
|
||||||
/** 列表项基础高度(像素) */
|
|
||||||
itemHeight: number
|
|
||||||
/** 列表项内边距总和(像素) */
|
|
||||||
itemPadding: number
|
|
||||||
/** 上下缓冲区域的项目数量 */
|
|
||||||
bufferCount: number
|
|
||||||
/** 滚动防抖时间(毫秒) */
|
|
||||||
scrollDebounceTime: number
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user