165 lines
4.3 KiB
Vue
165 lines
4.3 KiB
Vue
<script setup lang="ts">
|
||
import { onMounted } from 'vue'
|
||
import { storeToRefs } from 'pinia'
|
||
import { useRouter } from 'vue-router'
|
||
import { PaginationBar } from '@/components'
|
||
// 页面容器:只做组件引入、props 分发、事件中转,数据只经 store
|
||
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(() => {
|
||
// 筛选面板选项全部来自接口:业务类型 / 所属行业 / 项目规模三组都走 /categories,完成时间由前端算
|
||
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 {
|
||
// 设计实测:页脚顶 y1811,正文最底约 y1733 → 底距 78
|
||
padding-bottom: 78px;
|
||
|
||
// 设计稿页头条 矩形 148284 @(0,73) 1920×179 白底通栏;文案左缩进 112
|
||
&__hero {
|
||
height: 179px;
|
||
box-sizing: border-box;
|
||
padding-top: 32px; // 标题 @(112,105) → 73+32
|
||
background: @white;
|
||
}
|
||
|
||
// 页头文案与内容区不对齐:设计实测文案在 x112(内容区 x70 + 42)
|
||
&__hero-inner {
|
||
padding-left: 42px;
|
||
}
|
||
|
||
&__title {
|
||
margin: 0;
|
||
font-size: 36px; // 设计 @(112,105) 36/700 #121212
|
||
font-weight: 700;
|
||
line-height: 52px;
|
||
color: @cases-hero-title;
|
||
}
|
||
|
||
&__slogan {
|
||
margin: 15px 0 0; // 副标题 @(112,172),标题底 105+52=157 → 间距 15
|
||
font-size: 24px; // 设计 24/500 #666666
|
||
font-weight: 500;
|
||
line-height: 37px;
|
||
color: @cases-muted;
|
||
}
|
||
|
||
// 设计稿:筛选面板 @(70,272) 516 宽,卡片区 @(606,423) 起 1244 宽,主区间距 20
|
||
&__body {
|
||
.flex(row, flex-start, flex-start);
|
||
gap: 20px;
|
||
padding-top: 20px; // 筛选面板顶 272 − 页头条底(73+179) = 20
|
||
}
|
||
|
||
&__main {
|
||
.flex(column, flex-start);
|
||
flex: 1;
|
||
min-width: 0;
|
||
gap: 24px;
|
||
}
|
||
|
||
&__pager {
|
||
.flex(row, flex-end);
|
||
}
|
||
}
|
||
</style>
|