Vue 组件中的可排序表头实现
在数据展示中,排序功能是非常常见的需求。本文将通过一个 SortableHeader
组件,展示如何实现可排序的表头。该组件支持根据用户点击的列进行升序、降序和重置排序,同时通过自定义事件将排序信息传递给父组件。
组件实现概述
该组件主要包含以下功能:
- 展示一个横向滚动的表头
- 支持点击表头的列来进行排序
- 根据当前的排序状态,高亮显示对应的排序图标(升序/降序)
代码分析
模板部分 (<template>
)
<template>
<scroll-view
class="scroll-view"
:style="{ overflowX: 'auto', whiteSpace: 'nowrap' }"
>
<view
class="header"
:style="{ display: 'flex', justifyContent: layoutStyle }"
>
<view
v-for="header in headers"
:key="header.key"
class="header-item"
:style="{ margin: `0 ${itemSpacing}px` }"
@click="requestSort(header.key,header.sortable)"
>
<view class="item">
<span class="column">{{ header.label }}</span>
<view
v-show="header.sortable"
class="sort-icons"
>
<view
class="sort-icon-up"
:class="getIconClass('up', header.key)"
/>
<view
class="sort-icon-down"
:class="getIconClass('down', header.key)"
/>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
主要逻辑:
v-for
循环生成表头,headers
是一个传入的属性,每个表头项有key
和label
以及可选的sortable
属性。requestSort
函数在点击列头时调用,用于切换排序状态。- 排序图标(上下箭头)仅在
sortable
为true
时显示,箭头样式通过getIconClass
进行动态控制。
脚本部分 (<script lang="ts">
)
import { defineComponent, ref, watch } from 'vue';
interface Header {
key: string;
label: string;
sortable?: boolean; // 添加 sortable 属性
}
export default defineComponent({
name: 'SortableHeader',
props: {
headers: {
type: Array as () => Header[],
required: true,
},
layoutStyle: {
type: String,
default: 'space-between',
},
itemSpacing: {
type: Number,
required: true,
default: 10,
},
defaultSort: {
type: Object as () => { key: string; state: number },
default: () => ({ key: '', state: 0 }), // 默认值
},
},
emits: ['sort'],
setup(props, { emit }) {
const sortConfig = ref<{ key: string; state: number }>({
key: props.defaultSort.key,
state: props.defaultSort.state,
});
watch(
() => props.defaultSort,
(newSort) => {
sortConfig.value = { key: newSort.key, state: newSort.state };
},
{ immediate: true }
);
const requestSort = (key: string, sortable: boolean) => {
if (!sortable) return;
if (sortConfig.value.key === key) {
sortConfig.value.state = sortConfig.value.state === 1 ? -1 : (sortConfig.value.state === -1 ? 0 : 1);
} else {
sortConfig.value.key = key;
sortConfig.value.state = 1;
}
emitSortEvent();
};
const emitSortEvent = () => {
if (sortConfig.value.state === 1) {
emit('sort', { key: sortConfig.value.key, order: 'asc', state: sortConfig.value.state });
} else if (sortConfig.value.state === -1) {
emit('sort', { key: sortConfig.value.key, order: 'desc', state: sortConfig.value.state });
}
};
const getIconClass = (direction: string, key: string) => {
if (sortConfig.value.state === 0) return 'inactive';
if (sortConfig.value.key !== key) return 'inactive';
return direction === 'up' && sortConfig.value.state === 1
? 'active'
: direction === 'down' && sortConfig.value.state === -1
? 'active'
: 'inactive';
};
return {
requestSort,
getIconClass,
};
},
});
主要逻辑:
-
Props:
headers
是表头的配置数组。layoutStyle
控制表头的对齐方式,默认为space-between
。itemSpacing
控制列之间的间距。defaultSort
指定默认的排序方式,包括排序字段key
和排序状态state
(0: 无排序,1: 升序,-1: 降序)。
-
响应式状态:
sortConfig
保存当前排序的字段和状态。
-
排序逻辑:
requestSort
根据传入的key
和sortable
来切换排序。如果点击的是当前排序的字段,则循环切换状态(升序、降序、无排序),否则将新字段设为升序。
-
事件发射:
emitSortEvent
通过emit
方法将排序信息传递给父组件,格式为{ key: '字段', order: 'asc/desc', state: 状态 }
。
-
图标样式:
getIconClass
根据当前排序状态返回不同的类名,以控制图标的高亮显示。
样式部分 (<style scoped lang="less">
)
.header {
display: flex;
cursor: pointer;
padding: 0 20px;
height: 90px;
background: var(--cr-bg);
align-items: center;
}
.scroll-view {
overflow-x: auto;
white-space: nowrap;
}
.header-item {
display: inline-flex;
align-items: center;
}
.item {
display: flex;
align-items: center;
}
.column {
font-weight: 400;
font-size: var(--font-normal);
color: var(--cr-font-light-grey);
text-align: right;
line-height: 28px;
}
.sort-icons {
display: flex;
flex-direction: column;
align-items: center;
margin-left: 6px;
}
.sort-icon-up,
.sort-icon-down {
width: 0;
height: 0;
display: inline-block;
margin-top: 0.125rem;
}
/* 上箭头样式 */
.sort-icon-up {
border-left: 0.175rem solid transparent;
border-right: 0.175rem solid transparent;
border-bottom: 0.2rem solid var(--border3);
}
.sort-icon-up.active {
border-bottom-color: var(--cr-font-light-grey);
}
/* 下箭头样式 */
.sort-icon-down {
border-left: 0.175rem solid transparent;
border-right: 0.175rem solid transparent;
border-top: 0.2rem solid var(--border3);
}
.sort-icon-down.active {
border-top-color: var(--cr-font-light-grey);
}
样式解读:
scroll-view
实现横向滚动,同时设置white-space: nowrap;
保证所有列在同一行。.sort-icons
用来控制排序图标的上下排列,包含上箭头(升序)和下箭头(降序)。sort-icon-up
和sort-icon-down
通过border
样式实现三角形的箭头效果,并通过类名active
和inactive
控制高亮显示。
组件使用示例
<template>
<SortableHeader
:headers="[
{ key: 'name', label: '名称', sortable: true },
{ key: 'date', label: '日期', sortable: true },
{ key: 'price', label: '价格', sortable: false }
]"
:layoutStyle="'space-between'"
:itemSpacing="20"
@sort="handleSort"
/>
</template>
<script setup lang="ts">
import SortableHeader from './SortableHeader.vue';
const handleSort = (sortInfo: { key: string; order: string; state: number }) => {
console.log('排序信息:', sortInfo);
};
</script>
小结
通过 SortableHeader
组件,您可以轻松实现带有排序功能的表头,并通过事件机制将排序状态传递给父组件。该组件的高扩展性和灵活性使得它可以轻松适配不同的表头需求,同时提供了良好的用户交互体验。