286 lines
9.8 KiB
Vue
286 lines
9.8 KiB
Vue
<template>
|
|
<div class="news-view-page">
|
|
<div class="orb orb-1"></div>
|
|
<div class="orb orb-2"></div>
|
|
<div class="orb orb-3"></div>
|
|
|
|
<section class="news-hero">
|
|
<div class="news-hero-glow news-hero-glow-1"></div>
|
|
<div class="news-hero-glow news-hero-glow-2"></div>
|
|
<div class="news-shell">
|
|
<div class="hero-content">
|
|
<span class="hero-badge">{{ $t('newsView.badge') }}</span>
|
|
<div class="hero-title-row">
|
|
<h1>{{ $t('newsView.title') }}</h1>
|
|
</div>
|
|
<p>{{ $t('newsView.intro') }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="filter-section">
|
|
<div class="news-shell">
|
|
<div class="filter-tabs">
|
|
<button
|
|
v-for="item in localizedFilterTabs"
|
|
:key="item.value"
|
|
type="button"
|
|
class="filter-tab"
|
|
:class="{ active: activeCategory === item.value }"
|
|
@click="handleCategoryChange(item.value)"
|
|
>
|
|
{{ item.label }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="featured-section">
|
|
<div class="news-shell">
|
|
<article v-if="featuredNews" class="news-featured" @click="goNewsDetail(featuredNews)">
|
|
<div class="news-item-glow"></div>
|
|
<span class="hover-detail">{{ $t('newsView.detail') }} <i class="el-icon-arrow-right"></i></span>
|
|
<div class="news-featured-inner">
|
|
<div class="news-featured-img">
|
|
<img :src="featuredNews.img || fallbackNewsImage" :alt="featuredNews.title" @error="handleImageError">
|
|
</div>
|
|
<div class="news-featured-body">
|
|
<div class="news-featured-date">
|
|
<span class="category-pill" :style="{ background: featuredNews.tagColor }">{{ featuredNews.tag }}</span>
|
|
{{ featuredNews.date }}
|
|
</div>
|
|
<h2>{{ featuredNews.title }}</h2>
|
|
<p>{{ featuredNews.desc }}</p>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="news-list-section">
|
|
<div class="news-shell">
|
|
<div class="news-grid">
|
|
<article
|
|
v-for="(item, index) in filteredNewsList"
|
|
:key="item.id"
|
|
class="news-item"
|
|
:style="{ animationDelay: `${(index + 1) * 0.08}s` }"
|
|
@click="goNewsDetail(item)"
|
|
>
|
|
<div class="news-item-glow"></div>
|
|
<span class="hover-detail">{{ $t('newsView.detail') }} <i class="el-icon-arrow-right"></i></span>
|
|
<div class="news-item-img">
|
|
<img :src="item.img || fallbackNewsImage" :alt="item.title" @error="handleImageError">
|
|
<div class="news-item-img-overlay"></div>
|
|
<span class="news-item-tag" :style="{ background: item.tagColor }">{{ item.tag }}</span>
|
|
</div>
|
|
<div class="news-item-body">
|
|
<div class="news-item-date">{{ item.date }}</div>
|
|
<h3>{{ item.title }}</h3>
|
|
<p>{{ item.desc }}</p>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
|
|
<div class="pagination">
|
|
<button type="button" class="pagination-btn" :disabled="page <= 1" @click="changePage(page - 1)">
|
|
<i class="el-icon-arrow-left"></i>
|
|
</button>
|
|
<button
|
|
v-for="item in visiblePages"
|
|
:key="item"
|
|
type="button"
|
|
class="pagination-btn"
|
|
:class="{ active: page === item }"
|
|
@click="changePage(item)"
|
|
>
|
|
{{ item }}
|
|
</button>
|
|
<button type="button" class="pagination-btn" :disabled="page >= totalPages" @click="changePage(page + 1)">
|
|
<i class="el-icon-arrow-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { reqNewsList } from '@/api/newsapi/newsapi'
|
|
export default {
|
|
name: 'NewsView',
|
|
data() {
|
|
return {
|
|
fallbackNewsImage: require('@/assets/image/news.jpg'),
|
|
activeCategory: 'all',
|
|
page: 1,
|
|
pageSize: 10,
|
|
total: 0,
|
|
filterTabValues: [
|
|
{ key: 'all', value: 'all', zh: '', en: '' },
|
|
{ key: 'corporate', value: 'corporate', zh: '企业动态', en: 'Corporate News' },
|
|
{ key: 'product', value: 'product', zh: '产品动态', en: 'Product News' },
|
|
{ key: 'industry', value: 'industry', zh: '行业洞察', en: 'Industry Insights' },
|
|
{ key: 'event', value: 'event', zh: '活动资讯', en: 'Event News' }
|
|
],
|
|
newsList: []
|
|
}
|
|
},
|
|
computed: {
|
|
isEnglish() {
|
|
return this.$i18n && this.$i18n.locale === 'en-US'
|
|
},
|
|
featuredNews() {
|
|
return this.newsList[0]
|
|
},
|
|
filteredNewsList() {
|
|
return this.newsList.slice(1)
|
|
},
|
|
localizedFilterTabs() {
|
|
return this.filterTabValues.map(item => ({
|
|
...item,
|
|
label: item.key === 'all' ? this.$t('newsView.all') : this.$t(`newsView.categories.${item.key}`)
|
|
}))
|
|
},
|
|
totalPages() {
|
|
return Math.max(Math.ceil(this.total / this.pageSize), 1)
|
|
},
|
|
visiblePages() {
|
|
return Array.from({ length: Math.min(this.totalPages, 3) }, (_, index) => index + 1)
|
|
}
|
|
},
|
|
watch: {
|
|
isEnglish() {
|
|
this.getNewsList()
|
|
}
|
|
},
|
|
created() {
|
|
this.getNewsList()
|
|
},
|
|
methods: {
|
|
goAbout() {
|
|
this.$router.push('/homePage/about')
|
|
},
|
|
goNewsDetail(item) {
|
|
if (!item || !item.id) return
|
|
this.$router.push({
|
|
name: 'homePageNewsDetail',
|
|
params: { id: item.id }
|
|
}).catch(() => {
|
|
this.$router.push(`/homePage/news/detail/${item.id}`)
|
|
})
|
|
},
|
|
handleImageError(event) {
|
|
if (event && event.target && event.target.src !== this.fallbackNewsImage) {
|
|
event.target.src = this.fallbackNewsImage
|
|
}
|
|
},
|
|
handleCategoryChange(value) {
|
|
this.activeCategory = value
|
|
this.page = 1
|
|
this.getNewsList()
|
|
},
|
|
changePage(page) {
|
|
if (page < 1 || page > this.totalPages || page === this.page) return
|
|
this.page = page
|
|
this.getNewsList()
|
|
},
|
|
getActiveCategoryConfig() {
|
|
return this.filterTabValues.find(item => item.value === this.activeCategory) || this.filterTabValues[0]
|
|
},
|
|
buildNewsParams() {
|
|
const category = this.getActiveCategoryConfig()
|
|
return {
|
|
url_link: window.location.href.split('#')[0] || window.location.href,
|
|
title_zh: '',
|
|
title_en: '',
|
|
article_type_zh: category.zh || '',
|
|
article_type_en: category.en || '',
|
|
current_page: String(this.page),
|
|
page_size: String(this.pageSize)
|
|
}
|
|
},
|
|
getTagColor(type) {
|
|
const map = {
|
|
corporate: 'rgba(16,185,129,0.85)',
|
|
product: 'rgba(99,102,241,0.85)',
|
|
industry: 'rgba(13,148,136,0.85)',
|
|
event: 'rgba(245,158,11,0.85)'
|
|
}
|
|
return map[this.getCategoryKey(type)] || 'rgba(99,102,241,0.85)'
|
|
},
|
|
getCategoryKey(type) {
|
|
const map = {
|
|
企业动态: 'corporate',
|
|
'Corporate News': 'corporate',
|
|
产品动态: 'product',
|
|
'Product News': 'product',
|
|
行业洞察: 'industry',
|
|
'Industry Insights': 'industry',
|
|
活动资讯: 'event',
|
|
'Event News': 'event'
|
|
}
|
|
if (map[type]) return map[type]
|
|
const matchedLabel = Object.keys(map).find(label => String(type || '').startsWith(label))
|
|
return matchedLabel ? map[matchedLabel] : ''
|
|
},
|
|
getCategorySuffix(type) {
|
|
const labels = ['企业动态', 'Corporate News', '产品动态', 'Product News', '行业洞察', 'Industry Insights', '活动资讯', 'Event News']
|
|
const matchedLabel = labels.find(label => String(type || '').startsWith(label))
|
|
return matchedLabel ? String(type).slice(matchedLabel.length) : ''
|
|
},
|
|
getLocalizedCategory(type) {
|
|
const key = this.getCategoryKey(type)
|
|
return key ? `${this.$t(`newsView.categories.${key}`)}${this.getCategorySuffix(type)}` : type
|
|
},
|
|
getLocalizedField(row, zhField, enField, legacyField) {
|
|
if (this.isEnglish && row[enField]) return row[enField]
|
|
return row[zhField] || row[legacyField] || row[enField] || ''
|
|
},
|
|
normalizeImageUrl(url) {
|
|
if (!url) return ''
|
|
if (/^(https?:|blob:|data:|\/\/)/.test(url)) return url
|
|
return `${window.location.origin}/idfile?path=${url}`
|
|
},
|
|
normalizeNewsItem(row) {
|
|
const type = this.getLocalizedField(row, 'article_type_zh', 'article_type_en', 'article_type') || '企业动态'
|
|
const localizedType = this.getLocalizedCategory(type)
|
|
return {
|
|
id: row.id,
|
|
category: localizedType,
|
|
tag: localizedType,
|
|
tagColor: this.getTagColor(type),
|
|
date: this.getLocalizedField(row, 'publish_time_zh', 'publish_time_en', 'publish_time'),
|
|
title: this.getLocalizedField(row, 'title_zh', 'title_en', 'title') || '未命名文章',
|
|
desc: this.getLocalizedField(row, 'summary_zh', 'summary_en', 'summary'),
|
|
img: this.normalizeImageUrl(this.getLocalizedField(row, 'cover_img_zh', 'cover_img_en', 'cover_img')),
|
|
views: row.read_count || 0
|
|
}
|
|
},
|
|
async getNewsList() {
|
|
try {
|
|
const res = await reqNewsList(this.buildNewsParams())
|
|
if (res && res.status === true) {
|
|
this.newsList = Array.isArray(res.data) ? res.data.map(item => this.normalizeNewsItem(item)) : []
|
|
const pagination = res.pagination || {}
|
|
this.total = Number(pagination.total || this.newsList.length)
|
|
this.page = Number(pagination.current_page || this.page)
|
|
this.pageSize = Number(pagination.page_size || this.pageSize)
|
|
return
|
|
}
|
|
this.newsList = []
|
|
this.total = 0
|
|
} catch (error) {
|
|
this.newsList = []
|
|
this.total = 0
|
|
this.$message.error(this.$t('newsView.loadFail'))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
@import url('../../../assets/less/news/news.less');
|
|
</style>
|