Files
.shoudate/demo-vue/src/views/industry/IndustryDetail.vue
王文昊 02a23df5f1 feat(demo-vue): 新闻页面与行业动态页面新增多语言支持
- 升级组件与页面,支持根据语言环境(中/英)条件渲染标题与内容
- 在 IndustryNews 和 News 实体类中新增英文标题、英文内容字段
- sql导出demo_news_full_dump_utf8mb4_2026-04-28.sql
2026-04-28 20:57:24 +08:00

311 lines
6.5 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="industry-detail-page">
<HeaderComponent />
<main class="detail-content">
<section class="detail-banner">
<div class="banner-overlay">
<div class="banner-wrapper">
<h1 class="banner-title">{{ t('industry.detailTitle') }}</h1>
<p class="banner-subtitle">{{ t('industry.detailTitleSub') }}</p>
</div>
</div>
</section>
<section class="breadcrumb-section">
<div class="breadcrumb-wrapper">
<span class="breadcrumb-item" @click="goHome">{{ t('nav.home') }}</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-item" @click="goBack">{{ t('industry.listBanner') }}</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-item active">{{ t('industry.breadcrumbDetail') }}</span>
</div>
</section>
<section class="detail-section" v-loading="loading">
<div class="detail-wrapper">
<article class="article-content">
<h1 class="article-title">{{ locale === 'en-US' && industryDetail.titleEn ? industryDetail.titleEn : industryDetail.title }}</h1>
<div class="article-meta">
<span class="meta-item">
<el-icon><Calendar /></el-icon>
{{ t('industry.publicTime') }}{{ formatDate(industryDetail.publishDate) }}
</span>
<span class="meta-item">
<el-icon><View /></el-icon>
{{ t('common.viewsLabel') }}{{ industryDetail.views }}
</span>
</div>
<div class="article-body" v-html="locale === 'en-US' && industryDetail.contentEn ? industryDetail.contentEn : industryDetail.content"></div>
</article>
<div class="back-button">
<el-button type="primary" @click="goBack">
<el-icon><Back /></el-icon>
{{ t('industry.returnList') }}
</el-button>
</div>
</div>
</section>
</main>
<FooterComponent />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Calendar, View, Back } from '@element-plus/icons-vue'
import HeaderComponent from '@/components/HeaderComponent.vue'
import FooterComponent from '@/components/FooterComponent.vue'
import { post } from '@/utils/request'
import { industryApi } from '@/api/industry-api.ts'
const router = useRouter()
const route = useRoute()
const { t, locale } = useI18n()
// 行业动态详情数据
const industryDetail = ref<any>({
title: '',
content: '',
views: 0,
publishDate: ''
})
// 加载状态
const loading = ref(false)
// 获取行业动态详情
const getIndustryDetail = async (id: any) => {
try {
loading.value = true
// 先增加访问量
await industryApi.increaseViews(id)
// 查询详情通过list接口查询单条
const response = await post('/industry/list?pagenum=1&pagesize=1', {
id: id
})
if (response.data.list && response.data.list.length > 0) {
industryDetail.value = response.data.list[0]
}
} catch (error: any) {
console.error(t('industry.fetchDetailFail'), error)
} finally {
loading.value = false
}
}
// 格式化日期
const formatDate = (date: string) => {
if (!date) return ''
const d = new Date(date)
const year = d.getFullYear()
const month = (d.getMonth() + 1).toString().padStart(2, '0')
const day = d.getDate().toString().padStart(2, '0')
return `${year}-${month}-${day}`
}
// 返回首页
const goHome = () => {
router.push('/')
}
// 返回列表
const goBack = () => {
router.push('/industry')
}
// 组件加载时获取数据
onMounted(() => {
const id = route.query.id
if (id) {
getIndustryDetail(id)
}
})
</script>
<style scoped lang="less">
.industry-detail-page {
width: 100%;
min-height: 100vh;
background: #f8f9fa;
}
.detail-content {
width: 100%;
background: #f8f9fa;
padding-top: 70px;
}
.detail-banner {
position: relative;
background: url(../../assets/images/bg2.jpg) center center / cover no-repeat;
padding: 80px 0 60px;
text-align: center;
min-height: 350px;
.banner-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
}
.banner-wrapper {
position: relative;
z-index: 1;
max-width: 1400px;
margin: 0 auto;
padding: 0 100px;
}
.banner-title {
font-size: 36px;
font-weight: 700;
color: #fff;
margin: 0 0 8px;
}
.banner-subtitle {
font-size: 18px;
color: rgba(255, 255, 255, 0.9);
margin: 0;
}
}
.breadcrumb-section {
background: #fff;
padding: 20px 0;
border-bottom: 1px solid #e8e8e8;
.breadcrumb-wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 0 100px;
display: flex;
align-items: center;
}
.breadcrumb-item {
font-size: 14px;
color: #666;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
&.active {
color: #1a1a1a;
cursor: default;
}
}
.breadcrumb-separator {
margin: 0 8px;
color: #999;
}
}
.detail-section {
padding: 40px 0 80px;
.detail-wrapper {
max-width: 1000px;
margin: 0 auto;
padding: 0 100px;
}
}
.article-content {
background: #fff;
border-radius: 8px;
padding: 40px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.article-title {
font-size: 28px;
font-weight: 700;
color: #1a1a1a;
margin: 0 0 20px;
line-height: 1.4;
}
.article-meta {
display: flex;
gap: 24px;
padding-bottom: 20px;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 30px;
.meta-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
color: #666;
.el-icon {
font-size: 16px;
}
}
}
.article-body {
font-size: 16px;
line-height: 1.8;
color: #333;
p {
margin-bottom: 16px;
}
img {
max-width: 100%;
height: auto;
margin: 20px 0;
}
}
}
.back-button {
margin-top: 30px;
text-align: center;
}
@media (max-width: 768px) {
.banner-wrapper,
.breadcrumb-wrapper,
.detail-wrapper {
padding: 0 24px;
}
.detail-banner {
padding: 60px 0 40px;
.banner-title {
font-size: 28px;
}
}
.article-content {
padding: 24px;
.article-title {
font-size: 22px;
}
}
}
</style>