1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-09-12 16:40:40 +08:00

refactor(articles): 重构筛选排序UI与逻辑

将原有的弹窗筛选改为顶部分类tab和内联排序行,拆分筛选配置为分类和排序独立数据,移除筛选弹层逻辑
This commit is contained in:
小莫唐尼
2026-09-09 03:13:20 +08:00
parent b4239da1af
commit baad90fb0e
+43 -88
View File
@@ -32,18 +32,12 @@ const hasNext = ref(false)
const isLoadMore = ref(false)
const loadMoreText = ref(t('common.loading'))
/* ---------------- 筛选与排序(参考投票中心) ---------------- */
/* ---------------- 筛选与排序(内联:分类参考图库顶部,排序一排) ---------------- */
interface IFilterOption {
label: string
value: string
}
interface IFilterItem {
key: 'category' | 'sort'
label: string
options: IFilterOption[]
}
/** 分类列表(筛选选项数据源) */
const categoryList = ref<ICategory[]>([])
@@ -55,55 +49,28 @@ const sortMap: Record<string, string[]> = {
pinned: ['spec.pinned,desc'],
}
/** 筛选维度:分类筛选 + 排序 */
const filterConfig = computed<IFilterItem[]>(() => [
{
key: 'category',
label: '分类',
options: [
{ label: '全部', value: '' },
...categoryList.value.map(c => ({ label: c.spec.displayName, value: c.metadata.name })),
],
},
{
key: 'sort',
label: '排序',
options: [
{ label: '默认排序', value: 'default' },
{ label: '最新', value: 'latest' },
{ label: '最旧', value: 'oldest' },
{ label: '置顶', value: 'pinned' },
],
},
/** 分类选项(含"全部",参考图库顶部设计) */
const categoryOptions = computed<IFilterOption[]>(() => [
{ label: '全部', value: '' },
...categoryList.value.map(c => ({ label: c.spec.displayName, value: c.metadata.name })),
])
/** 排序选项(一排内联) */
const sortOptions: IFilterOption[] = [
{ label: '默认排序', value: 'default' },
{ label: '最新', value: 'latest' },
{ label: '最旧', value: 'oldest' },
{ label: '置顶', value: 'pinned' },
]
/** 各维度当前选中值(空串 = 全部) */
const filterValues = ref<Record<string, string>>({ category: '', sort: 'default' })
/** 当前选中中文标签(用于筛选栏展示) */
const filterLabels = computed(() => {
const map: Record<string, string> = {}
for (const f of filterConfig.value) {
const cur = filterValues.value[f.key]
map[f.key] = f.options.find(o => o.value === cur)?.label || f.options[0].label
}
return map
})
/** 筛选弹层 */
const filterPopup = ref<{ show: boolean, item: IFilterItem | null }>({ show: false, item: null })
function handleOpenFilter(item: IFilterItem) {
filterPopup.value = { show: true, item }
}
/** 选择筛选/排序:重置分页并重新查询 */
function handleSelectFilter(option: IFilterOption) {
const item = filterPopup.value.item
if (!item)
/** 切换分类/排序:重置分页并重新查询 */
function handleSelectFilter(key: 'category' | 'sort', value: string) {
if (filterValues.value[key] === value)
return
filterValues.value[item.key] = option.value
filterPopup.value.show = false
filterValues.value[key] = value
isLoadMore.value = false
articleList.value = []
queryParams.value.page = 1
@@ -228,18 +195,33 @@ onReachBottom(() => {
<!-- 自定义导航 -->
<uh-navbar default-title="文章列表" title-color="text-gray-900" />
<!-- 筛选栏:分类 + 排序 -->
<view class="box-border flex items-center justify-between gap-x-2 px-3 pt-2">
<view
v-for="f in filterConfig" :key="f.key"
class="uh-global-card-glass box-border flex flex-1 items-center justify-center gap-1 border rounded-full px-2 py-1 text-gray-500"
:class="[filterValues[f.key] !== f.options[0].value ? 'bg-secondary text-gray-900 font-bold' : 'bg-white/80 text-gray-600']"
@click="handleOpenFilter(f)"
>
<text class="truncate text-xs">{{ filterLabels[f.key] }}</text>
<wd-icon name="arrow-down" size="10px" />
<!-- 第一行:分类 Tab(参考图库顶部设计) -->
<wd-sticky v-if="categoryOptions.length > 1" class="w-full">
<scroll-view :scroll-x="true" class="w-full whitespace-nowrap pt-2">
<view
v-for="cate in categoryOptions" :key="cate.value"
class="uh-global-card-glass uh-shadow-xs mb-1 ml-3 inline-flex border rounded-2xl px-4 py-1 text-sm"
:class="{ 'bg-primary text-gray-900 font-bold': filterValues.category === cate.value }"
@click="handleSelectFilter('category', cate.value)"
>
{{ cate.label }}
</view>
</scroll-view>
</wd-sticky>
<!-- 第二行:排序一排 -->
<scroll-view :scroll-x="true" class="w-full whitespace-nowrap">
<view class="box-border flex gap-2 px-3 py-2">
<view
v-for="opt in sortOptions" :key="opt.value"
class="uh-global-card-glass uh-shadow-xs inline-flex border rounded-full px-3 py-1 text-xs"
:class="{ 'bg-secondary text-gray-900 font-bold': filterValues.sort === opt.value }"
@click="handleSelectFilter('sort', opt.value)"
>
{{ opt.label }}
</view>
</view>
</view>
</scroll-view>
<!-- 加载/错误/空占位(状态机) -->
<uh-data-loading
@@ -266,32 +248,5 @@ onReachBottom(() => {
{{ loadMoreText }}
</view>
</view>
<!-- 回顶 -->
<view
class="to-top-btn fixed bottom-[160rpx] right-6 z-6 h-[72rpx] w-[72rpx] flex items-center justify-center rounded-full bg-white shadow-sm"
@click="handleToTopPage()"
>
<wd-icon name="arrow-up" size="20px" color="#03a9f4" />
</view>
<!-- 筛选弹层 -->
<uh-glass-popup v-model="filterPopup.show" :z-index="99" position="bottom" custom-class="rounded-2xl">
<view v-if="filterPopup.item" class="box-border p-4">
<view class="text-md mb-4 text-center text-gray-900 font-bold">
{{ filterPopup.item.label }}
</view>
<view class="flex flex-col gap-2">
<view
v-for="opt in filterPopup.item.options" :key="opt.label"
class="uh-global-card-glass box-border border rounded-xl px-5 py-2 text-center text-sm"
:class="filterValues[filterPopup.item.key] === opt.value ? 'bg-primary text-gray-900 font-bold' : 'text-gray-700'"
@click="handleSelectFilter(opt)"
>
{{ opt.label }}
</view>
</view>
</view>
</uh-glass-popup>
</view>
</template>