Files
cz-web/src/views/cases/index.vue
2026-10-08 17:26:33 +08:00

167 lines
4.0 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.
<!-- 页面:成功案例库(Cases) -->
<script setup lang="ts">
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { PaginationBar } from '@/components'
import { useCasesStore } from '@/stores/modules/cases'
import type { CaseQuery } from '@/types/business/cases'
import CasesFilter from './cpns/CasesFilter.vue'
import CasesSearchBar from './cpns/CasesSearchBar.vue'
import CasesGrid from './cpns/CasesGrid.vue'
const router = useRouter()
const casesStore = useCasesStore()
const { query, list, total, previewTotal, typeOptions, industryOptions, scaleOptions, caseTotal }
= storeToRefs(casesStore)
function onUpdateQuery(next: CaseQuery): void {
casesStore.setQuery(next)
}
function onUpdateKeyword(keyword: string): void {
casesStore.setQuery({ ...query.value, keyword })
}
function onSearch(): void {
void casesStore.search()
}
function onReset(): void {
void casesStore.resetQuery()
}
function onToggleFavoriteOnly(): void {
void casesStore.toggleFavoriteOnly()
}
function onFavorite(id: string): void {
void casesStore.toggleFavorite(id)
}
function onPageChange(page: number): void {
void casesStore.changePage(page)
}
function onViewDetail(id: string): void {
void router.push(`/cases/${id}`)
}
onMounted(() => {
void Promise.all([casesStore.fetchCategories(), casesStore.fetchCases()])
})
</script>
<template>
<div class="cases">
<header class="cases__hero">
<div class="container cases__hero-inner">
<h1 class="cases__title">
成功案例库
</h1>
<p class="cases__slogan">
海量软硬件研发与量产落地真实案例,借鉴成熟实践,降低项目试错成本。
</p>
</div>
</header>
<div class="container cases__body">
<CasesFilter
:query="query"
:type-options="typeOptions"
:industry-options="industryOptions"
:scale-options="scaleOptions"
:case-total="caseTotal"
:preview-total="previewTotal"
@update:query="onUpdateQuery"
@search="onSearch"
@reset="onReset"
/>
<div class="cases__main">
<CasesSearchBar
:keyword="query.keyword"
:favorite-only="query.favoriteOnly"
@update:keyword="onUpdateKeyword"
@search="onSearch"
@reset="onReset"
@toggle-favorite-only="onToggleFavoriteOnly"
/>
<CasesGrid
:cases="list"
:total="total"
@view="onViewDetail"
@favorite="onFavorite"
/>
<div class="cases__pager">
<PaginationBar
:page="query.page"
:page-size="query.pageSize"
:total="total"
@page-change="onPageChange"
/>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.cases {
padding-bottom: 78px;
&__hero {
height: 179px;
box-sizing: border-box;
padding-top: 32px;
background: @white;
}
// 设计稿 #31:hero 标题 x=112、副标题 x=112;
// 主体筛选栏 x=70、右侧操作区右边界 1850。两套留白并存,
// 所以 hero 内容单独内缩,不动全局 .container 的 70px 留白。
&__hero-inner {
padding-left: 112px;
padding-right: 112px;
}
&__title {
margin: 0;
font-size: 36px;
font-weight: 700;
line-height: 52px;
color: @cases-hero-title;
}
&__slogan {
margin: 15px 0 0;
font-size: 24px;
font-weight: 500;
line-height: 37px;
color: @cases-muted;
}
&__body {
.flex(row, flex-start, flex-start);
gap: 20px;
padding-top: 20px;
}
&__main {
// 设计稿:右侧操作区 x=606 w=1244(右边界 1850,与筛选栏区留白 70 对称)。
// mixin 签名是 .flex(direction; justify; align),第三参才是 align-items。
// 纵向排列时 justify 无意义,align 用 stretch 让子项撑满 1244。
.flex(column, flex-start, stretch);
flex: 1;
min-width: 0;
gap: 24px;
}
&__pager {
.flex(row, flex-end);
}
}
</style>