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:
@@ -32,18 +32,12 @@ const hasNext = ref(false)
|
|||||||
const isLoadMore = ref(false)
|
const isLoadMore = ref(false)
|
||||||
const loadMoreText = ref(t('common.loading'))
|
const loadMoreText = ref(t('common.loading'))
|
||||||
|
|
||||||
/* ---------------- 筛选与排序(参考投票中心) ---------------- */
|
/* ---------------- 筛选与排序(内联:分类参考图库顶部,排序一排) ---------------- */
|
||||||
interface IFilterOption {
|
interface IFilterOption {
|
||||||
label: string
|
label: string
|
||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFilterItem {
|
|
||||||
key: 'category' | 'sort'
|
|
||||||
label: string
|
|
||||||
options: IFilterOption[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 分类列表(筛选选项数据源) */
|
/** 分类列表(筛选选项数据源) */
|
||||||
const categoryList = ref<ICategory[]>([])
|
const categoryList = ref<ICategory[]>([])
|
||||||
|
|
||||||
@@ -55,55 +49,28 @@ const sortMap: Record<string, string[]> = {
|
|||||||
pinned: ['spec.pinned,desc'],
|
pinned: ['spec.pinned,desc'],
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 筛选维度:分类筛选 + 排序 */
|
/** 分类选项(含"全部",参考图库顶部设计) */
|
||||||
const filterConfig = computed<IFilterItem[]>(() => [
|
const categoryOptions = computed<IFilterOption[]>(() => [
|
||||||
{
|
{ label: '全部', value: '' },
|
||||||
key: 'category',
|
...categoryList.value.map(c => ({ label: c.spec.displayName, value: c.metadata.name })),
|
||||||
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 sortOptions: IFilterOption[] = [
|
||||||
|
{ label: '默认排序', value: 'default' },
|
||||||
|
{ label: '最新', value: 'latest' },
|
||||||
|
{ label: '最旧', value: 'oldest' },
|
||||||
|
{ label: '置顶', value: 'pinned' },
|
||||||
|
]
|
||||||
|
|
||||||
/** 各维度当前选中值(空串 = 全部) */
|
/** 各维度当前选中值(空串 = 全部) */
|
||||||
const filterValues = ref<Record<string, string>>({ category: '', sort: 'default' })
|
const filterValues = ref<Record<string, string>>({ category: '', sort: 'default' })
|
||||||
|
|
||||||
/** 当前选中中文标签(用于筛选栏展示) */
|
/** 切换分类/排序:重置分页并重新查询 */
|
||||||
const filterLabels = computed(() => {
|
function handleSelectFilter(key: 'category' | 'sort', value: string) {
|
||||||
const map: Record<string, string> = {}
|
if (filterValues.value[key] === value)
|
||||||
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)
|
|
||||||
return
|
return
|
||||||
filterValues.value[item.key] = option.value
|
filterValues.value[key] = value
|
||||||
filterPopup.value.show = false
|
|
||||||
isLoadMore.value = false
|
isLoadMore.value = false
|
||||||
articleList.value = []
|
articleList.value = []
|
||||||
queryParams.value.page = 1
|
queryParams.value.page = 1
|
||||||
@@ -228,18 +195,33 @@ onReachBottom(() => {
|
|||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar default-title="文章列表" title-color="text-gray-900" />
|
<uh-navbar default-title="文章列表" title-color="text-gray-900" />
|
||||||
|
|
||||||
<!-- 筛选栏:分类 + 排序 -->
|
<!-- 第一行:分类 Tab(参考图库顶部设计) -->
|
||||||
<view class="box-border flex items-center justify-between gap-x-2 px-3 pt-2">
|
<wd-sticky v-if="categoryOptions.length > 1" class="w-full">
|
||||||
<view
|
<scroll-view :scroll-x="true" class="w-full whitespace-nowrap pt-2">
|
||||||
v-for="f in filterConfig" :key="f.key"
|
<view
|
||||||
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"
|
v-for="cate in categoryOptions" :key="cate.value"
|
||||||
:class="[filterValues[f.key] !== f.options[0].value ? 'bg-secondary text-gray-900 font-bold' : 'bg-white/80 text-gray-600']"
|
class="uh-global-card-glass uh-shadow-xs mb-1 ml-3 inline-flex border rounded-2xl px-4 py-1 text-sm"
|
||||||
@click="handleOpenFilter(f)"
|
:class="{ 'bg-primary text-gray-900 font-bold': filterValues.category === cate.value }"
|
||||||
>
|
@click="handleSelectFilter('category', cate.value)"
|
||||||
<text class="truncate text-xs">{{ filterLabels[f.key] }}</text>
|
>
|
||||||
<wd-icon name="arrow-down" size="10px" />
|
{{ 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>
|
||||||
</view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 加载/错误/空占位(状态机) -->
|
<!-- 加载/错误/空占位(状态机) -->
|
||||||
<uh-data-loading
|
<uh-data-loading
|
||||||
@@ -266,32 +248,5 @@ onReachBottom(() => {
|
|||||||
{{ loadMoreText }}
|
{{ loadMoreText }}
|
||||||
</view>
|
</view>
|
||||||
</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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user