Files
cz-web/src/views/profile/favorites.vue
2026-09-30 15:55:36 +08:00

149 lines
3.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { FilterPanel, PaginationBar } from '@/components'
import type { BusinessType } from '@/types/api'
import { useProfileStore } from '@/stores/modules/profile'
import FavoritesGrid from './cpns/FavoritesGrid.vue'
interface CategoryOption {
key: BusinessType | 'all'
label: string
}
const CATEGORY_OPTIONS: CategoryOption[] = [
{ key: 'all', label: '全部' },
{ key: 'internet', label: '互联网软件' },
{ key: 'hardware', label: '硬件电路' },
{ key: 'iot', label: '物联网全案' },
{ key: 'manufacturing', label: '生产制造' },
]
const router = useRouter()
const profileStore = useProfileStore()
const { favorites, favoritesTotal, favoritesCounts, favoritesQuery } = storeToRefs(profileStore)
// 本地搜索条件(搜索/重置时同步进 store)
const keyword = ref('')
const category = ref<BusinessType | 'all'>('all')
function countOf(key: BusinessType | 'all'): number {
if (key === 'all') return favoritesTotal.value
return favoritesCounts.value[key] ?? 0
}
function onSearch(): void {
void profileStore.searchFavorites({ keyword: keyword.value, category: category.value })
}
function onReset(): void {
keyword.value = ''
category.value = 'all'
void profileStore.resetFavorites()
}
function onSelectCategory(key: BusinessType | 'all'): void {
category.value = key
void profileStore.searchFavorites({ keyword: keyword.value, category: key })
}
function onPageChange(page: number): void {
void profileStore.changeFavoritesPage(page)
}
function onViewDetail(id: string): void {
void router.push(`/cases/${id}`)
}
onMounted(() => {
keyword.value = favoritesQuery.value.keyword
category.value = favoritesQuery.value.category
void Promise.all([profileStore.fetchFavorites(), profileStore.fetchFavoritesCounts()])
})
</script>
<template>
<div class="favorites-page">
<FilterPanel
v-model:keyword="keyword"
keyword-placeholder="搜索收藏案例标题"
@search="onSearch"
@reset="onReset"
/>
<div class="favorites-page__categories">
<button
v-for="item in CATEGORY_OPTIONS"
:key="item.key"
class="favorites-page__cat"
:class="{ 'is-active': category === item.key }"
@click="onSelectCategory(item.key)"
>
{{ item.label }}({{ countOf(item.key) }})
</button>
</div>
<FavoritesGrid :cases="favorites" @view="onViewDetail" />
<PaginationBar
:page="favoritesQuery.page"
:page-size="favoritesQuery.pageSize"
:total="favoritesTotal"
@page-change="onPageChange"
/>
</div>
</template>
<style lang="less" scoped>
// 收藏列表无独立设计稿:与个人中心面板一致 —— 分类条平铺在白面板上,选中态用主色 token
.favorites-page {
.flex(column, flex-start);
gap: @spacing-base;
width: 100%;
&__categories {
.flex(row, flex-start);
flex-wrap: wrap;
gap: @spacing-sm;
width: 100%;
padding: 0;
}
&__cat {
height: 28px;
padding: 0 @spacing-base;
border: 1px solid @profile-line;
border-radius: @radius-round;
background: @white;
color: @text-color;
font-size: 11px;
transition: all 0.2s;
&:hover {
border-color: @primary-strong;
color: @primary-strong;
}
&.is-active {
border-color: @primary-strong;
background: @profile-nav-active-bg;
color: @primary-strong;
font-weight: 600;
}
}
// 分页与地址/资质页保持同一激活态(设计 20 实取 #D1E0FE + #024FFF 字)
:deep(.pagination) {
justify-content: flex-end;
padding: 0;
}
:deep(.pagination__btn.is-active) {
border-color: @profile-page-active-bg;
background: @profile-page-active-bg;
color: @primary-strong;
}
}
</style>