Files
BigStickNewOfficialWebsite/components/sections/CaseGridSection.vue
T
2026-07-10 18:23:41 +08:00

151 lines
4.4 KiB
Vue

<script setup>
import { computed, ref, watch } from "vue";
import { caseCategories, cases as defaultCases } from "~/data/cases";
const props = defineProps({
cases: {
type: Array,
default: () => defaultCases,
},
title: {
type: String,
default: "",
},
previewLimit: {
type: Number,
default: 3,
},
preview: {
type: Boolean,
default: false,
},
routes: {
type: Array,
default: () => [],
},
sectionId: {
type: String,
default: "cases",
},
});
const route = useRoute();
const activeCategory = ref("全部");
const visibleCategories = computed(() =>
props.preview ? ["全部"] : caseCategories,
);
const filteredCases = computed(() => {
const items =
activeCategory.value === "全部"
? props.cases
: props.cases.filter((item) => item.category === activeCategory.value);
return props.preview
? items.filter((item) => item.featured).slice(0, props.previewLimit)
: items;
});
watch(
() => route.query.category,
(category) => {
activeCategory.value =
typeof category === "string" && caseCategories.includes(category)
? category
: "全部";
},
{ immediate: true },
);
</script>
<template>
<section :id="sectionId" class="bg-[#f7f8f7] py-20">
<div class="container-gm">
<div class="flex flex-col gap-6 md:flex-row md:items-end md:justify-between" data-reveal>
<div class="max-w-3xl">
<p class="text-sm uppercase tracking-[0.3em] text-[#9a9a9a]">Cases</p>
<h2 class="section-title mt-4">
{{ title || (preview ? "真实落地成果,见证增长实效" : "案例库") }}
</h2>
</div>
<div class="flex flex-col items-start gap-3 md:items-end">
<SectionRouteLinks :routes="routes" />
<NuxtLink
v-if="preview"
to="/cases"
class="inline-flex items-center gap-2 text-sm font-medium text-gm-green"
>
查看全部案例 <span aria-hidden="true"></span>
</NuxtLink>
</div>
</div>
<div v-if="!preview" class="mt-8 flex flex-wrap gap-3" data-reveal>
<button
v-for="category in visibleCategories"
:key="category"
type="button"
class="rounded-full border px-6 py-2 text-sm transition-all duration-300"
:class="
activeCategory === category
? 'border-gm-green bg-gm-green text-white'
: 'border-gm-border bg-white text-[#666] hover:border-[#d5d5d5]'
"
@click="activeCategory = category"
>
{{ category }}
</button>
</div>
<div class="mt-10 grid gap-6 md:grid-cols-2 xl:grid-cols-3" data-stagger>
<NuxtLink
v-for="item in filteredCases"
:key="item.title"
:to="`/cases/${item.slug}`"
class="overflow-hidden rounded-lg border border-gm-border bg-white shadow-sm transition hover:shadow-hover"
data-stagger-item
data-hover-card
>
<div class="h-56 overflow-hidden bg-gm-light">
<img
:src="item.image"
:alt="item.title"
class="h-full w-full object-cover transition duration-500 hover:scale-105"
loading="lazy"
/>
</div>
<div class="p-6">
<p class="text-xs font-medium text-gm-green">{{ item.category }}</p>
<h3 class="mt-3 text-xl font-semibold leading-7 text-[#262626]">
{{ item.title }}
</h3>
<p class="mt-3 text-sm leading-7 text-[#777]">{{ item.summary }}</p>
<div class="mt-5 flex flex-wrap gap-2">
<span
v-for="tag in item.tags"
:key="tag"
class="rounded-full bg-gm-light px-3 py-1 text-xs text-[#666]"
>
{{ tag }}
</span>
</div>
<div class="mt-6 grid gap-2">
<p
v-for="metric in item.metrics"
:key="metric"
class="text-sm font-medium text-[#262626]"
>
{{ metric }}
</p>
</div>
<p class="mt-5 font-mono text-[11px] text-[#999]">
案例路径 {{ item.path }}
</p>
</div>
</NuxtLink>
</div>
</div>
</section>
</template>