Files
jinshen-website/app/pages/productions/[...slug].vue

281 lines
7.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.

<template>
<div class="page-container">
<div v-if="production">
<!-- 面包屑导航 -->
<el-breadcrumb class="breadcrumb" separator="/">
<el-breadcrumb-item class="text-md opacity-50">
<NuxtLink :to="$localePath('/')">{{ $t('navigation.home') }}</NuxtLink>
</el-breadcrumb-item>
<el-breadcrumb-item class="text-md opacity-50">
<NuxtLink :to="$localePath('/productions')">{{ $t('navigation.productions') }}</NuxtLink>
</el-breadcrumb-item>
<el-breadcrumb-item class="text-md opactiy-50">{{ production.title }}</el-breadcrumb-item>
</el-breadcrumb>
<!-- 产品详情内容 -->
<div class="production-header">
<div class="production-image">
<el-image :src="useStrapiMedia(production?.cover?.url || '')" :alt="production.title" fit="contain" />
</div>
<div class="production-info">
<h1>{{ production.title }}</h1>
<p class="summary">{{ production.summary }}</p>
</div>
</div>
<!-- 产品详细描述 -->
<div class="production-content">
<el-tabs v-model="activeName" class="production-tabs" stretch>
<el-tab-pane label="产品详情" name="details">
<markdown-renderer :content="production.production_details || ''" />
</el-tab-pane>
<el-tab-pane label="技术规格" name="specs">
<spec-table :data="production.production_specs" />
</el-tab-pane>
<el-tab-pane label="常见问题" name="faq">
<div class="faq-list">
<el-collapse class="faq-collapse" accordion>
<el-collapse-item
v-for="(question, index) in production.questions" :key="index" :title="question.title"
:name="String(index)">
<markdown-renderer :content="question.content || ''" />
</el-collapse-item>
</el-collapse>
</div>
</el-tab-pane>
<el-tab-pane label="相关文档" name="documents">
<div class="document-list">
<el-card v-for="(doc, index) in production.documents" :key="index" class="document-card">
<div class="document-info">
<h3>{{ doc.caption || doc.name }}</h3>
<div class="document-content">
<span v-if="doc.size" class="document-meta">大小: {{ formatFileSize(doc.size) }}</span>
<span v-if="doc.ext" class="document-meta">格式: {{ formatFileExtension(doc.ext) }}</span>
<el-button
class="download-button" type="primary"
@click="downloadFile(doc.name, useStrapiMedia(doc.url || ''))">
下载
</el-button>
</div>
</div>
</el-card>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
<!-- 加载状态 -->
<div v-else-if="pending" class="loading">
<el-skeleton style="--el-skeleton-circle-size: 400px">
<template #template>
<el-skeleton-item variant="circle" />
</template>
</el-skeleton>
<el-skeleton :rows="5" animated />
</div>
<!-- 未找到产品 -->
<div v-else class="not-found">
<el-result icon="warning" :title="$t('product-not-found')" :sub-title="$t('product-not-found-desc')">
<template #extra>
<el-button type="primary" @click="$router.push('/productions')">
{{ $t('back-to-productions') }}
</el-button>
</template>
</el-result>
</div>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const { findOne } = useStrapi()
const { getStrapiLocale } = useLocalizations()
const strapiLocale = getStrapiLocale()
const production = ref<Production | null>(null)
const pending = ref(true)
const activeName = ref('details') // 默认选中概览标签
// 获取路由参数slug 或 id
const documentId = computed(() => route.params.slug as string)
const downloadFile = async (filename: string, url: string) => {
const response = await fetch(url)
const blob = await response.blob()
const fileUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = fileUrl
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(fileUrl)
}
onMounted(async () => {
try {
const response = await findOne<Production>('productions', documentId.value, {
populate: {
production_specs: {
populate: '*',
},
production_images: {
populate: '*',
},
cover: {
populate: '*',
},
questions: {
populate: '*',
},
documents: {
populate: '*',
},
},
locale: strapiLocale,
})
if (response.data) {
const item = response.data
production.value = {
...item,
}
}
console.log('Fetched Production:', production.value)
} catch (error) {
console.error('Failed to fetch production:', error)
} finally {
pending.value = false
}
})
// SEO
useHead({
title: computed(() => production.value?.title || 'Product Detail'),
meta: [
{
name: 'description',
content: computed(() => production.value?.summary || '')
}
]
})
</script>
<style scoped>
.page-container {
min-height: 60vh;
}
.breadcrumb {
padding: 2rem;
}
.production-header {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
}
.production-image .el-image {
width: 100%;
height: 400px;
border-radius: 8px;
}
.production-info h1 {
margin-top: 2rem;
margin-bottom: 1rem;
font-size: 2rem;
}
.summary {
color: var(--el-color-info);
font-size: 1rem;
line-height: 1.6;
margin-bottom: 2rem;
}
.production-tabs ::v-deep(.el-tabs__nav) {
min-width: 30%;
float: right;
}
.production-tabs ::v-deep(.el-tabs__content) {
padding: 10px;
}
.production-content h2 {
color: var(--el-text-color-primary);
margin: 0;
}
.document-list {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1rem;
}
.document-meta {
color: var(--el-text-color-secondary);
font-size: 0.8rem;
}
.download-button {
margin-left: auto;
}
.document-content {
display: flex;
align-items: center;
gap: 1rem;
}
::v-deep(.el-collapse-item__header) {
font-size: 1rem;
padding: 1rem;
}
.faq-collapse {
border: none;
}
.faq-collapse :deep(.el-collapse-item) {
margin-bottom: 1rem;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1);
}
.faq-collapse :deep(.el-collapse-item__header) {
border: 1px solid var(--el-border-color-light);
}
.loading {
display: flex;
justify-content: center;
align-items: center;
margin-top: 1rem;
}
.not-found {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.production-header {
grid-template-columns: 1fr;
gap: 2rem;
}
.production-info h1 {
font-size: 2rem;
}
}
</style>