c1d6dd3b29
- 目录重命名: backend/wdkj-server/ → server/, frontend/ai-dimension/ → client/ - 删除 20+ 冗余文件(WDKJ 旧脚本、Windows 脚本、过时文档、设计稿) - 更新 package.json 元数据(移除 wdkj 命名) - 完善三级 .gitignore(根 + server + client) - 重写 README.md 和 CHANGELOG.md - 工具脚本移至 scripts/
357 lines
8.3 KiB
Vue
357 lines
8.3 KiB
Vue
<template>
|
||
<view class="page-news-detail">
|
||
<view class="status-bar-placeholder"></view>
|
||
<!-- 导航 -->
|
||
<view class="nav-bar">
|
||
<view class="nav-back" @click="goBack">‹</view>
|
||
<text class="nav-title">AI 资讯</text>
|
||
<view class="nav-action">
|
||
<text class="nav-share" @click="onShare">↗</text>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="content">
|
||
<!-- 资讯头图 -->
|
||
<view class="news-hero" :style="{background: heroBg}">
|
||
<text class="hero-emoji">{{ heroEmoji }}</text>
|
||
</view>
|
||
|
||
<!-- 分类标签 -->
|
||
<view class="category-tag">
|
||
<text class="tag-text">{{ categoryLabel }}</text>
|
||
</view>
|
||
|
||
<!-- 标题 -->
|
||
<text class="news-title">{{ news.title }}</text>
|
||
|
||
<!-- 元信息 -->
|
||
<view class="meta-info">
|
||
<view class="meta-item">
|
||
<text class="meta-label">来源</text>
|
||
<text class="meta-value">{{ news.source }}</text>
|
||
</view>
|
||
<view class="meta-item">
|
||
<text class="meta-label">时间</text>
|
||
<text class="meta-value">{{ formatDate(news.newsDate) }}</text>
|
||
</view>
|
||
<view class="meta-item">
|
||
<text class="meta-label">阅读</text>
|
||
<text class="meta-value">{{ news.viewCount || 0 }} 次</text>
|
||
</view>
|
||
<view class="meta-item" v-if="news.hot">
|
||
<text class="meta-label hot">🔥</text>
|
||
<text class="meta-value">{{ news.hotCount || 0 }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 摘要 -->
|
||
<view class="summary-card">
|
||
<text class="summary-label">摘要</text>
|
||
<text class="summary-text">{{ news.summary }}</text>
|
||
</view>
|
||
|
||
<!-- 正文 -->
|
||
<view class="news-body" v-if="newsBody">
|
||
<rich-text :nodes="newsBody"></rich-text>
|
||
</view>
|
||
|
||
<!-- 相关话题 -->
|
||
<view class="related-tags" v-if="relatedTags.length">
|
||
<text class="related-label">相关话题</text>
|
||
<view class="tags-row">
|
||
<view class="tag-item" v-for="(tag, i) in relatedTags" :key="i">
|
||
<text class="tag-text">{{ tag }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作栏 -->
|
||
<view class="action-bar">
|
||
<view class="action-btn" @click="onFavorite">
|
||
<text class="action-icon">{{ isCollected ? '❤️' : '🤍' }}</text>
|
||
<text class="action-text">{{ isCollected ? '已收藏' : '收藏' }}</text>
|
||
</view>
|
||
<view class="action-btn" @click="onShare">
|
||
<text class="action-icon">↗</text>
|
||
<text class="action-text">分享</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="footer-spacer"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { trendApi, knowledgeApi } from '@/utils/api'
|
||
|
||
const news = ref({})
|
||
const isCollected = ref(false)
|
||
const loading = ref(true)
|
||
|
||
const heroBg = computed(() => {
|
||
const c = news.value.category || 'product'
|
||
return {
|
||
product: 'linear-gradient(135deg, rgba(179,136,255,0.3), rgba(239,83,80,0.15))',
|
||
paper: 'linear-gradient(135deg, rgba(77,208,225,0.3), rgba(77,182,172,0.15))',
|
||
tool: 'linear-gradient(135deg, rgba(255,213,79,0.3), rgba(239,83,80,0.1))',
|
||
company: 'linear-gradient(135deg, rgba(77,182,172,0.3), rgba(179,136,255,0.15))'
|
||
}[c] || 'linear-gradient(135deg, rgba(179,136,255,0.3), rgba(77,208,225,0.15))'
|
||
})
|
||
|
||
const heroEmoji = computed(() => {
|
||
return { product: '🚀', paper: '📄', tool: '🔧', company: '🏢' }[news.value.category || 'product'] || '📰'
|
||
})
|
||
|
||
const categoryLabel = computed(() => {
|
||
const c = news.value.category || 'product'
|
||
return { product: '产品动态', paper: '研究论文', tool: '工具发布', company: '公司动态' }[c] || '资讯'
|
||
})
|
||
|
||
function formatDate(dateStr) {
|
||
if (!dateStr) return '—'
|
||
try {
|
||
const d = new Date(dateStr)
|
||
if (isNaN(d)) return dateStr
|
||
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`
|
||
} catch (e) {
|
||
return dateStr
|
||
}
|
||
}
|
||
|
||
const newsBody = computed(() => {
|
||
const content = news.value.body || ''
|
||
if (!content) {
|
||
return `<p>${news.value.summary || ''}</p><p style="margin-top:16px;color:rgba(255,255,255,0.35)">完整内容暂未提供,将持续更新...</p>`
|
||
}
|
||
return content
|
||
})
|
||
|
||
const relatedTags = computed(() => {
|
||
return news.value.tags || []
|
||
})
|
||
|
||
async function loadDetail() {
|
||
const id = uni.getStorageSync('__news_id__')
|
||
loading.value = false
|
||
// Try API detail first, fallback to local data from trend page
|
||
if (id) {
|
||
try {
|
||
const data = await trendApi.detail(id)
|
||
news.value = data
|
||
isCollected.value = data.collected || false
|
||
return
|
||
} catch (e) {
|
||
// fall through to local
|
||
}
|
||
}
|
||
// Fallback: read from trend page's stored item
|
||
try {
|
||
const raw = uni.getStorageSync('__news_item__')
|
||
if (raw) {
|
||
news.value = JSON.parse(raw)
|
||
}
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
}
|
||
|
||
function goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
function onFavorite() {
|
||
isCollected.value = !isCollected.value
|
||
uni.showToast({ title: isCollected.value ? '已收藏' : '已取消收藏', icon: 'none' })
|
||
}
|
||
|
||
function onShare() {
|
||
uni.setClipboardData({
|
||
data: news.value.link || `https://ai-dimension.yuzhiran.com.cn/news/${uni.getStorageSync('__news_id__')}`,
|
||
success: () => uni.showToast({ title: '链接已复制', icon: 'none' })
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadDetail()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-news-detail {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--bg-primary);
|
||
}
|
||
|
||
.status-bar-placeholder { height: 44px; }
|
||
|
||
.nav-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 16px;
|
||
height: 44px;
|
||
}
|
||
|
||
.nav-back {
|
||
font-size: 28px;
|
||
color: #fff;
|
||
padding: 0 8px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.nav-share { font-size: 22px; color: rgba(255,255,255,0.5); }
|
||
|
||
.content {
|
||
flex: 1;
|
||
background: var(--bg-primary);
|
||
}
|
||
|
||
// Hero
|
||
.news-hero {
|
||
height: 180px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0 0 24px 24px;
|
||
}
|
||
|
||
.hero-emoji { font-size: 72px; }
|
||
|
||
// Category
|
||
.category-tag {
|
||
margin: 24px 20px 0;
|
||
}
|
||
|
||
.tag-text {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--dim1-color);
|
||
letter-spacing: 2px;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
// Title
|
||
.news-title {
|
||
display: block;
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
line-height: 1.45;
|
||
color: var(--text-primary);
|
||
margin: 12px 20px 16px;
|
||
}
|
||
|
||
// Meta
|
||
.meta-info {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
margin: 0 20px 20px;
|
||
padding: 16px;
|
||
background: rgba(255,255,255,0.02);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.meta-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.meta-label {
|
||
font-size: 11px;
|
||
color: rgba(255,255,255,0.35);
|
||
}
|
||
|
||
.meta-label.hot { font-size: 13px; }
|
||
|
||
.meta-value { font-size: 12px; color: rgba(255,255,255,0.7); }
|
||
|
||
// Summary
|
||
.summary-card {
|
||
margin: 0 20px 20px;
|
||
padding: 16px;
|
||
background: linear-gradient(135deg, rgba(179,136,255,0.08), rgba(77,208,225,0.05));
|
||
border: 1px solid rgba(179,136,255,0.12);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.summary-label {
|
||
display: block;
|
||
font-size: 11px;
|
||
color: var(--dim1-color);
|
||
letter-spacing: 1px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.summary-text {
|
||
font-size: 14px;
|
||
line-height: 1.7;
|
||
color: rgba(255,255,255,0.85);
|
||
}
|
||
|
||
// Body
|
||
.news-body {
|
||
margin: 0 20px 20px;
|
||
font-size: 14px;
|
||
line-height: 1.8;
|
||
color: rgba(255,255,255,0.7);
|
||
}
|
||
|
||
// Related tags
|
||
.related-tags {
|
||
margin: 0 20px 20px;
|
||
}
|
||
|
||
.related-label {
|
||
display: block;
|
||
font-size: 12px;
|
||
color: rgba(255,255,255,0.4);
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.tags-row { display: flex; flex-wrap: wrap; gap: 8px; }
|
||
|
||
.tag-item {
|
||
padding: 6px 12px;
|
||
background: rgba(255,255,255,0.03);
|
||
border: 1px solid rgba(255,255,255,0.08);
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.tag-text { font-size: 12px; color: rgba(255,255,255,0.6); }
|
||
|
||
// Action bar
|
||
.action-bar {
|
||
margin: 0 20px;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
padding: 14px;
|
||
background: rgba(255,255,255,0.03);
|
||
border: 1px solid rgba(255,255,255,0.08);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.action-icon { font-size: 18px; }
|
||
.action-text { font-size: 13px; color: rgba(255,255,255,0.7); }
|
||
|
||
.footer-spacer { height: 60px; }
|
||
</style>
|