129 lines
4.1 KiB
Vue
129 lines
4.1 KiB
Vue
<script setup>
|
|
import { computed, ref, watch } from "vue";
|
|
import { insightCategories, insights as defaultInsights } from "~/data/insights";
|
|
|
|
const props = defineProps({
|
|
insights: {
|
|
type: Array,
|
|
default: () => defaultInsights,
|
|
},
|
|
routes: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
sectionId: {
|
|
type: String,
|
|
default: "insights",
|
|
},
|
|
});
|
|
|
|
const route = useRoute();
|
|
const activeCategory = ref("全部");
|
|
|
|
const filteredInsights = computed(() =>
|
|
activeCategory.value === "全部"
|
|
? props.insights
|
|
: props.insights.filter((item) => item.category === activeCategory.value),
|
|
);
|
|
|
|
const featured = computed(() => props.insights.find((item) => item.featured));
|
|
|
|
watch(
|
|
() => route.query.category,
|
|
(category) => {
|
|
activeCategory.value =
|
|
typeof category === "string" && insightCategories.includes(category)
|
|
? category
|
|
: "全部";
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<section :id="sectionId" class="bg-white py-20">
|
|
<div class="container-gm">
|
|
<div class="mb-8 flex flex-col gap-4 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-[#a1a1a1]">Insights</p>
|
|
<h2 class="section-title mt-4">文章列表、热门推荐与分类筛选</h2>
|
|
</div>
|
|
<SectionRouteLinks :routes="routes" />
|
|
</div>
|
|
|
|
<article
|
|
v-if="featured"
|
|
class="grid overflow-hidden rounded-lg border border-gm-border bg-[#f7f8f7] lg:grid-cols-[1.05fr_0.95fr]"
|
|
data-reveal
|
|
>
|
|
<div class="p-8 md:p-12">
|
|
<p class="text-sm font-medium text-gm-green">精选观点</p>
|
|
<h2 class="mt-4 text-balance text-3xl font-semibold leading-tight text-[#262626] md:text-[42px]">
|
|
{{ featured.title }}
|
|
</h2>
|
|
<p class="mt-5 max-w-2xl text-base leading-8 text-[#777]">
|
|
{{ featured.excerpt }}
|
|
</p>
|
|
<p class="mt-8 text-sm text-[#999]">{{ featured.date }} · {{ featured.category }}</p>
|
|
<p class="mt-3 font-mono text-[11px] text-[#999]">
|
|
内容路径 {{ featured.path }}
|
|
</p>
|
|
</div>
|
|
<img
|
|
:src="featured.image"
|
|
:alt="featured.title"
|
|
class="h-full min-h-[320px] w-full object-cover"
|
|
/>
|
|
</article>
|
|
|
|
<div class="mt-12 flex flex-wrap gap-3" data-reveal>
|
|
<button
|
|
v-for="category in insightCategories"
|
|
: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 filteredInsights"
|
|
:key="item.title"
|
|
:to="`/insights/${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-52 overflow-hidden bg-gm-light">
|
|
<img
|
|
:src="item.image"
|
|
:alt="item.title"
|
|
class="h-full w-full object-cover"
|
|
loading="lazy"
|
|
/>
|
|
</div>
|
|
<div class="p-6">
|
|
<p class="text-xs font-medium text-gm-green">{{ item.category }}</p>
|
|
<h3 class="mt-3 text-lg font-semibold leading-7 text-[#262626]">
|
|
{{ item.title }}
|
|
</h3>
|
|
<p class="mt-3 text-sm leading-7 text-[#777]">{{ item.excerpt }}</p>
|
|
<p class="mt-5 text-xs text-[#9b9b9b]">{{ item.date }}</p>
|
|
<p class="mt-3 font-mono text-[11px] text-[#999]">
|
|
内容路径 {{ item.path }}
|
|
</p>
|
|
</div>
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|