feat: 新增作品和动态详情页
✨ Work 详情页 (/work/[slug]): - Hero 大图 + 渐变遮罩 - 项目概述、挑战、解决方案 - 项目截图画廊 - 右侧信息卡片(客户、年份、周期、服务) - 访问项目按钮 - 相关项目推荐 - 返回列表按钮 - 完整中英文内容 ✨ News 详情页 (/news/[slug]): - Hero 大图 + 渐变遮罩 - 分类标签 + 日期 + 阅读时间 + 作者 - 文章正文(多段落 + 引用 + 图片) - 分享按钮(Twitter、LinkedIn、复制链接) - 相关文章推荐 - 返回列表按钮 - 完整中英文内容 🔧 列表页更新: - work.vue → work/index.vue - news.vue → news/index.vue - 卡片链接改为 NuxtLink 到详情页 🌍 国际化: - 6 个作品详情(中文 + 英文) - 6 篇文章详情(中文 + 英文) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<main class="min-h-screen">
|
||||
<!-- 返回按钮 -->
|
||||
<div class="fixed top-28 left-4 md:left-8 z-30">
|
||||
<NuxtLink
|
||||
to="/work"
|
||||
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-full bg-white/90 backdrop-blur-sm shadow-lg hover:shadow-xl transition-all duration-300 text-sm font-semibold text-black hover:-translate-x-1"
|
||||
>
|
||||
<Icon name="ph:arrow-left" class="w-5 h-5" />
|
||||
{{ t('work.backToList') }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<!-- Hero 大图 -->
|
||||
<section class="relative h-[60vh] md:h-[70vh] overflow-hidden">
|
||||
<img
|
||||
:src="project.image"
|
||||
:alt="project.title"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent"></div>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-8 md:p-16">
|
||||
<div class="max-w-[1200px] mx-auto">
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<span
|
||||
v-for="tag in project.tags"
|
||||
:key="tag"
|
||||
class="px-3 py-1 text-xs md:text-sm bg-white/20 backdrop-blur-sm text-white rounded-full"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-4 tracking-tight">
|
||||
{{ project.title }}
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl text-white/80 max-w-2xl">
|
||||
{{ project.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 项目信息 -->
|
||||
<section class="max-w-[1200px] mx-auto px-6 md:px-10 py-16 md:py-24">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12 lg:gap-16">
|
||||
<!-- 左侧:项目详情 -->
|
||||
<div class="lg:col-span-2 space-y-12">
|
||||
<div>
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-black mb-6">{{ t('work.detail.overview') }}</h2>
|
||||
<p class="text-gray-700 text-base md:text-lg leading-relaxed">
|
||||
{{ t(`work.detail.items.${slug}.overview`) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-black mb-6">{{ t('work.detail.challenge') }}</h2>
|
||||
<p class="text-gray-700 text-base md:text-lg leading-relaxed">
|
||||
{{ t(`work.detail.items.${slug}.challenge`) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-black mb-6">{{ t('work.detail.solution') }}</h2>
|
||||
<p class="text-gray-700 text-base md:text-lg leading-relaxed">
|
||||
{{ t(`work.detail.items.${slug}.solution`) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 项目截图 -->
|
||||
<div class="space-y-6">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-black">{{ t('work.detail.gallery') }}</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div
|
||||
v-for="(img, index) in project.gallery"
|
||||
:key="index"
|
||||
class="rounded-2xl overflow-hidden shadow-lg hover:shadow-2xl transition-shadow duration-300"
|
||||
>
|
||||
<img :src="img" :alt="`${project.title} screenshot ${index + 1}`" class="w-full h-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:项目信息卡片 -->
|
||||
<div class="space-y-8">
|
||||
<div class="bg-gray-50 rounded-2xl p-6 md:p-8 sticky top-32">
|
||||
<h3 class="text-lg font-bold text-black mb-6">{{ t('work.detail.projectInfo') }}</h3>
|
||||
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">{{ t('work.detail.client') }}</p>
|
||||
<p class="text-base font-semibold text-black">{{ t(`work.detail.items.${slug}.client`) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">{{ t('work.detail.year') }}</p>
|
||||
<p class="text-base font-semibold text-black">{{ project.year }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">{{ t('work.detail.duration') }}</p>
|
||||
<p class="text-base font-semibold text-black">{{ t(`work.detail.items.${slug}.duration`) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">{{ t('work.detail.services') }}</p>
|
||||
<div class="flex flex-wrap gap-2 mt-2">
|
||||
<span
|
||||
v-for="tag in project.tags"
|
||||
:key="tag"
|
||||
class="px-3 py-1 text-xs bg-black text-white rounded-full"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 访问项目按钮 -->
|
||||
<a
|
||||
v-if="project.url"
|
||||
:href="project.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="mt-8 w-full inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-full bg-black text-white font-semibold hover:bg-gray-800 transition-colors duration-300"
|
||||
>
|
||||
{{ t('work.detail.visitProject') }}
|
||||
<Icon name="ph:arrow-up-right" class="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 相关项目 -->
|
||||
<section class="bg-gray-50 py-16 md:py-24">
|
||||
<div class="max-w-[1200px] mx-auto px-6 md:px-10">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-black mb-10 text-center">{{ t('work.detail.relatedProjects') }}</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<NuxtLink
|
||||
v-for="related in relatedProjects"
|
||||
:key="related.id"
|
||||
:to="`/work/${related.id}`"
|
||||
class="group block rounded-2xl overflow-hidden bg-white shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1"
|
||||
>
|
||||
<div class="relative aspect-[4/3] overflow-hidden">
|
||||
<img :src="related.image" :alt="related.title" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" />
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<h3 class="text-lg font-bold text-black group-hover:text-gray-600 transition-colors">{{ related.title }}</h3>
|
||||
<p class="text-sm text-gray-500 mt-1">{{ related.tags[0] }}</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSeoMeta, createError } from '#imports'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const slug = route.params.slug as string
|
||||
|
||||
interface WorkItem {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
image: string
|
||||
tags: string[]
|
||||
category: string
|
||||
year: string
|
||||
url?: string
|
||||
gallery: string[]
|
||||
}
|
||||
|
||||
const workData: Record<string, WorkItem> = {
|
||||
'forma-digital': {
|
||||
id: 'forma-digital',
|
||||
title: 'Forma Digital',
|
||||
description: 'A modern e-commerce platform with seamless user experience and dynamic product showcases.',
|
||||
image: 'https://picsum.photos/id/201/1200/800',
|
||||
tags: ['Web Design', 'E-commerce', 'React'],
|
||||
category: 'webDesign',
|
||||
year: '2026',
|
||||
url: '#',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/201/800/600',
|
||||
'https://picsum.photos/id/202/800/600',
|
||||
'https://picsum.photos/id/203/800/600',
|
||||
'https://picsum.photos/id/204/800/600',
|
||||
]
|
||||
},
|
||||
'one-step': {
|
||||
id: 'one-step',
|
||||
title: 'One Step',
|
||||
description: 'Brand identity and marketing campaign for a fitness app focused on sustainable habits.',
|
||||
image: 'https://picsum.photos/id/180/1200/800',
|
||||
tags: ['Branding', 'Marketing', 'UI/UX'],
|
||||
category: 'branding',
|
||||
year: '2024',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/180/800/600',
|
||||
'https://picsum.photos/id/181/800/600',
|
||||
'https://picsum.photos/id/182/800/600',
|
||||
'https://picsum.photos/id/183/800/600',
|
||||
]
|
||||
},
|
||||
'nero-vision': {
|
||||
id: 'nero-vision',
|
||||
title: 'Nero Vision',
|
||||
description: 'Full-stack web application for creative professionals to manage their portfolios.',
|
||||
image: 'https://picsum.photos/id/119/1200/800',
|
||||
tags: ['Development', 'Vue.js', 'Node.js'],
|
||||
category: 'development',
|
||||
year: '2025',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/119/800/600',
|
||||
'https://picsum.photos/id/120/800/600',
|
||||
'https://picsum.photos/id/121/800/600',
|
||||
'https://picsum.photos/id/122/800/600',
|
||||
]
|
||||
},
|
||||
'bold-moves': {
|
||||
id: 'bold-moves',
|
||||
title: 'Bold Moves',
|
||||
description: 'Interactive website showcasing innovative architectural designs with 3D visualizations.',
|
||||
image: 'https://picsum.photos/id/164/1200/800',
|
||||
tags: ['Web Design', '3D', 'Three.js'],
|
||||
category: 'webDesign',
|
||||
year: '2024',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/164/800/600',
|
||||
'https://picsum.photos/id/165/800/600',
|
||||
'https://picsum.photos/id/166/800/600',
|
||||
'https://picsum.photos/id/167/800/600',
|
||||
]
|
||||
},
|
||||
'echo-labs': {
|
||||
id: 'echo-labs',
|
||||
title: 'Echo Labs',
|
||||
description: 'Corporate branding and visual identity for a tech startup in the AI space.',
|
||||
image: 'https://picsum.photos/id/101/1200/800',
|
||||
tags: ['Branding', 'Visual Identity', 'Corporate'],
|
||||
category: 'branding',
|
||||
year: '2025',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/101/800/600',
|
||||
'https://picsum.photos/id/102/800/600',
|
||||
'https://picsum.photos/id/103/800/600',
|
||||
'https://picsum.photos/id/104/800/600',
|
||||
]
|
||||
},
|
||||
'flux-api': {
|
||||
id: 'flux-api',
|
||||
title: 'Flux API',
|
||||
description: 'Scalable REST API with comprehensive documentation and developer tools.',
|
||||
image: 'https://picsum.photos/id/326/1200/800',
|
||||
tags: ['Development', 'API', 'Python'],
|
||||
category: 'development',
|
||||
year: '2025',
|
||||
gallery: [
|
||||
'https://picsum.photos/id/326/800/600',
|
||||
'https://picsum.photos/id/327/800/600',
|
||||
'https://picsum.photos/id/328/800/600',
|
||||
'https://picsum.photos/id/329/800/600',
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const project = workData[slug]
|
||||
|
||||
if (!project) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Project not found' })
|
||||
}
|
||||
|
||||
const relatedProjects = computed(() => {
|
||||
return Object.values(workData)
|
||||
.filter(p => p.id !== slug)
|
||||
.slice(0, 3)
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: () => `${project.title} - ${t('work.title')}`,
|
||||
description: () => project.description,
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user