- 升级组件与页面,支持根据语言环境(中/英)条件渲染标题与内容 - 在 IndustryNews 和 News 实体类中新增英文标题、英文内容字段 - sql导出demo_news_full_dump_utf8mb4_2026-04-28.sql
311 lines
6.5 KiB
Vue
311 lines
6.5 KiB
Vue
<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>
|