feat: v1.3.0 差异化重构——星图点亮体系
P0 修复: - 详情页正文渲染(content 分段),71 条知识不再空白 - 问答页去登录墙:游客每日 3 次(IP 限流),登录后每日 5 次 - 修正问答配额为真正的每日重置语义 点亮闭环: - KnowledgeLit 模型 + 服务端判题(POST /:id/quiz,答案不下发) - /api/knowledge/stats 星域统计 + 点亮进度 + streak + litIds - AI 批量生成 71 条读后检测题(scripts/generate-quiz.js) 设计系统 2.0: - 70 个统一线性图标替换全部 emoji 图标 - 主题 token mixin 化,修复 H5 暗色级联 bug,MP 跟随系统(darkmode) - 首页星域导航(进度环)、我的页学习者档案、星域列表页重写 - 硬编码色清理,误植 map 页移除 服务端加固: - trust proxy 真实 IP、错误信息脱敏、ask 独立限流、无效 ObjectId 404 - seo.js ESM→CJS(修复容器启动)、AI 导师"维度精灵"人格化
This commit is contained in:
+254
-186
@@ -1,75 +1,93 @@
|
||||
<template>
|
||||
<view class="page-home page-layout">
|
||||
<!-- 状态栏占位 -->
|
||||
<view class="status-bar-placeholder"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="header">
|
||||
<view class="brand">
|
||||
<view class="brand-icon">✦</view>
|
||||
<view class="brand-icon">
|
||||
<AppIcon name="planet-white" :size="20" />
|
||||
</view>
|
||||
<view class="brand-text">
|
||||
<text class="title">AI 维度</text>
|
||||
<text class="subtitle">宇之然 · 探索</text>
|
||||
<text class="subtitle">宇之然 · 知识星图</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-actions">
|
||||
<view class="icon-btn" @tap="onNotify">
|
||||
<text>🔔</text>
|
||||
</view>
|
||||
<view class="icon-btn" @tap="goProfile">
|
||||
<text>👤</text>
|
||||
<AppIcon name="user-gray" :size="18" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 问候语 -->
|
||||
<view class="greeting">
|
||||
<text class="greeting-title">探索 </text>
|
||||
<text class="greeting-highlight">人工智能</text>
|
||||
<text class="greeting-desc">从 5 个维度,系统理解 AI 的过去、现在与未来</text>
|
||||
<!-- 连续点亮 streak -->
|
||||
<view class="streak-bar" v-if="streak > 0" @tap="goFirstUnlit">
|
||||
<AppIcon name="flame" :size="16" />
|
||||
<text class="streak-text">已连续点亮 {{ streak }} 天</text>
|
||||
<text class="streak-sub">{{ todayLit ? '今日已点亮,继续保持' : '今日还没点亮,去逛逛' }}</text>
|
||||
<view class="streak-go">
|
||||
<AppIcon name="arrow-white" :size="12" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 继续探索 -->
|
||||
<view class="resume-card" v-if="lastRead" @tap="resumeRead">
|
||||
<view class="resume-icon">
|
||||
<AppIcon name="clock" :size="18" />
|
||||
</view>
|
||||
<view class="resume-info">
|
||||
<text class="resume-label">继续探索</text>
|
||||
<text class="resume-title">{{ lastRead.title }}</text>
|
||||
</view>
|
||||
<AppIcon name="arrow-gray" :size="14" />
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar" @tap="onSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<AppIcon name="search-gray" :size="16" />
|
||||
<text class="search-placeholder">搜搜你想了解的 AI 知识...</text>
|
||||
</view>
|
||||
|
||||
<!-- 五维度网格 -->
|
||||
<!-- 五大星域 -->
|
||||
<view class="section-header">
|
||||
<text class="section-title">✦ 五个维度</text>
|
||||
<text class="section-title">五大星域</text>
|
||||
<text class="section-more" v-if="userStats">{{ userStats.totalLit }}/{{ statsTotal }} 颗已点亮</text>
|
||||
</view>
|
||||
<view class="dim-grid">
|
||||
<view
|
||||
v-for="dim in dimensions"
|
||||
:key="dim.id"
|
||||
class="dim-card"
|
||||
:class="{ 'dim-full': dim.isFull }"
|
||||
:style="{ background: dim.bg }"
|
||||
@tap="goDimension(dim)"
|
||||
>
|
||||
<view class="dim-icon" :style="{ background: dim.iconBg, color: dim.color }">
|
||||
<text>{{ dim.icon }}</text>
|
||||
<view class="dim-top">
|
||||
<view class="dim-icon" :style="{ background: dim.iconBg }">
|
||||
<AppIcon :name="dim.icon" :size="18" />
|
||||
</view>
|
||||
<ProgressRing
|
||||
:size="34"
|
||||
:num-size="9"
|
||||
:percent="dimPercent(dim.id)"
|
||||
:color="dim.color"
|
||||
/>
|
||||
</view>
|
||||
<text class="dim-name">{{ dim.name }}</text>
|
||||
<text class="dim-desc">{{ dim.tagline }}</text>
|
||||
<view class="dim-foot">
|
||||
<view class="dim-lit" :style="{ background: dim.bg, color: dim.color }">
|
||||
{{ dimLitText(dim.id) }}
|
||||
</view>
|
||||
<text class="dim-count">{{ dimCountText(dim.id) }}</text>
|
||||
</view>
|
||||
<text class="dim-name" :style="{ color: dim.color }">{{ dim.name }}</text>
|
||||
<text class="dim-desc">{{ dim.desc }}</text>
|
||||
<text class="dim-count">
|
||||
{{ dim.isFull ? '今日已更新 8 条' : `${dim.articleCount} 篇 · ${dim.quizCount} 个问答` }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 今日快讯 -->
|
||||
<view class="section-header">
|
||||
<text class="section-title">📡 今日快讯</text>
|
||||
<text class="section-more" @tap="goTrend">查看全部 →</text>
|
||||
<text class="section-title">今日快讯</text>
|
||||
<text class="section-more" @tap="goTrend">查看全部</text>
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, idx) in newsList"
|
||||
:key="idx"
|
||||
class="news-card"
|
||||
@tap="goNewsDetail(item)"
|
||||
>
|
||||
<view v-for="(item, idx) in newsList" :key="idx" class="news-card" @tap="goNewsDetail(item)">
|
||||
<view class="news-tag" :class="item.tagClass">{{ item.tag }}</view>
|
||||
<text class="news-title">{{ item.title }}</text>
|
||||
<view class="news-meta">
|
||||
@@ -83,34 +101,56 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onPullDownRefresh, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onPullDownRefresh, onShow, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import { ref, computed } from 'vue'
|
||||
import { DIMENSIONS } from '@/utils/dimensions'
|
||||
import { trendApi } from '@/utils/api'
|
||||
import { knowledgeApi, trendApi } from '@/utils/api'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import AppIcon from '@/components/AppIcon.vue'
|
||||
import ProgressRing from '@/components/ProgressRing.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const dimensions = ref(DIMENSIONS)
|
||||
|
||||
const newsList = ref([])
|
||||
const loading = ref(true)
|
||||
const byDim = ref({}) // 各星域知识总数
|
||||
const userStats = ref(null) // { totalLit, litByDim, streak }
|
||||
const lastRead = ref(null)
|
||||
|
||||
const TAG_HOT = '热门'
|
||||
const TAG_PICK = '精选'
|
||||
const TAG_NEW = '最新'
|
||||
|
||||
const statsTotal = computed(() => Object.values(byDim.value).reduce((a, b) => a + b, 0))
|
||||
const streak = computed(() => userStats.value?.streak || 0)
|
||||
const todayLit = computed(() => !!uni.getStorageSync('lit_today'))
|
||||
|
||||
onShow(() => {
|
||||
loadStats()
|
||||
loadNews()
|
||||
lastRead.value = uni.getStorageSync('last_read') || null
|
||||
})
|
||||
|
||||
onPullDownRefresh(async () => {
|
||||
try {
|
||||
const data = await trendApi.list({ limit: 5 })
|
||||
// 页面已有响应式引用会自动更新(保持既有逻辑)
|
||||
} catch (e) {}
|
||||
await Promise.all([loadStats(), loadNews()])
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchNews()
|
||||
})
|
||||
|
||||
async function fetchNews() {
|
||||
async function loadStats() {
|
||||
try {
|
||||
const data = await trendApi.list({ limit: 5 })
|
||||
newsList.value = data.list.map(item => ({
|
||||
tag: item.hot ? '🔥 热门' : item.picked ? '📌 精选' : '🚀 最新',
|
||||
const data = await knowledgeApi.stats(userStore.isLoggedIn ? userStore.token : '')
|
||||
byDim.value = data.byDim || {}
|
||||
userStats.value = data.user || null
|
||||
} catch (e) {
|
||||
// 静默降级:环形显示 0%
|
||||
}
|
||||
}
|
||||
|
||||
async function loadNews() {
|
||||
try {
|
||||
const data = await trendApi.list({ limit: 3 })
|
||||
newsList.value = (data.list || []).slice(0, 3).map(item => ({
|
||||
tag: item.hot ? TAG_HOT : item.picked ? TAG_PICK : TAG_NEW,
|
||||
tagClass: item.hot ? 'hot' : item.picked ? 'pick' : 'new',
|
||||
title: item.title,
|
||||
source: item.source,
|
||||
@@ -118,17 +158,27 @@ async function fetchNews() {
|
||||
_id: item._id
|
||||
}))
|
||||
} catch (e) {
|
||||
// 降级:本地数据
|
||||
newsList.value = [
|
||||
{ tag: '🔥 热门', tagClass: 'hot', title: 'DeepSeek 被曝自研推理芯片', source: '量子位', time: '2 小时前' },
|
||||
{ tag: '🚀 最新', tagClass: 'new', title: '蚂蚁灵波开源 LingBot-World 2.0', source: '量子位', time: '4 小时前' },
|
||||
{ tag: '📌 精选', tagClass: 'pick', title: '阿里获 ACL 2026 最佳资源论文奖', source: '机器之心', time: '6 小时前' }
|
||||
]
|
||||
} finally {
|
||||
loading.value = false
|
||||
newsList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function dimPercent(dimId) {
|
||||
const total = byDim.value[dimId] || 0
|
||||
if (!total) return 0
|
||||
const lit = userStats.value?.litByDim?.[dimId] || 0
|
||||
return Math.round((lit / total) * 100)
|
||||
}
|
||||
|
||||
function dimLitText(dimId) {
|
||||
const lit = userStats.value?.litByDim?.[dimId] || 0
|
||||
return `${lit} 颗已点亮`
|
||||
}
|
||||
|
||||
function dimCountText(dimId) {
|
||||
const total = byDim.value[dimId] || 0
|
||||
return `共 ${total} 颗星球`
|
||||
}
|
||||
|
||||
function formatTime(dateStr) {
|
||||
const diff = Date.now() - new Date(dateStr).getTime()
|
||||
const hours = Math.floor(diff / 3600000)
|
||||
@@ -141,6 +191,16 @@ function goDimension(dim) {
|
||||
uni.navigateTo({ url: `/pages/dimension/dimension?id=${dim.id}&name=${dim.name}` })
|
||||
}
|
||||
|
||||
function goFirstUnlit() {
|
||||
uni.navigateTo({ url: '/pages/dimension/dimension?id=1' })
|
||||
}
|
||||
|
||||
function resumeRead() {
|
||||
if (lastRead.value?.id) {
|
||||
uni.navigateTo({ url: `/pages/detail/detail?id=${lastRead.value.id}` })
|
||||
}
|
||||
}
|
||||
|
||||
function goTrend() {
|
||||
uni.switchTab({ url: '/pages/trend/trend' })
|
||||
}
|
||||
@@ -153,21 +213,19 @@ function onSearch() {
|
||||
uni.navigateTo({ url: '/pages/search/search' })
|
||||
}
|
||||
|
||||
function onNotify() {
|
||||
uni.showToast({ title: '暂无新消息', icon: 'none' })
|
||||
}
|
||||
|
||||
function goNewsDetail(item) {
|
||||
uni.setStorageSync('__news_item__', JSON.stringify(item))
|
||||
uni.navigateTo({ url: '/pages/news/news' })
|
||||
}
|
||||
|
||||
// 微信小程序分享
|
||||
// #ifdef MP-WEIXIN
|
||||
onShareAppMessage(() => ({
|
||||
title: 'AI 维度 - 你的AI知识助手',
|
||||
title: 'AI 维度 · 5 分钟点亮一颗 AI 知识星球',
|
||||
path: '/pages/index/index'
|
||||
}))
|
||||
onShareTimeline(() => ({
|
||||
title: 'AI 维度 · 5 分钟点亮一颗 AI 知识星球'
|
||||
}))
|
||||
// #endif
|
||||
</script>
|
||||
|
||||
@@ -178,15 +236,13 @@ onShareAppMessage(() => ({
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.status-bar-placeholder {
|
||||
height: 44px;
|
||||
}
|
||||
.status-bar-placeholder { height: 44px; }
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0 20px;
|
||||
padding: 6px 0 18px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
@@ -196,38 +252,32 @@ onShareAppMessage(() => ({
|
||||
}
|
||||
|
||||
.brand-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, rgba(124,77,255,0.3), rgba(0,188,212,0.2));
|
||||
border: 1px solid rgba(124,77,255,0.2);
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, var(--color-brand), #8b5cf6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
box-shadow: 0 0 20px rgba(124,77,255,0.1);
|
||||
box-shadow: 0 4px 14px var(--color-brand-glow);
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.brand-text { display: flex; flex-direction: column; }
|
||||
.brand-text .title {
|
||||
font-size: 20px;
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.brand-text .subtitle {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
color: var(--text-tertiary);
|
||||
letter-spacing: 1.5px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.header-actions { display: flex; gap: 10px; }
|
||||
.icon-btn {
|
||||
width: 36px; height: 36px;
|
||||
border-radius: 50%;
|
||||
@@ -236,167 +286,185 @@ onShareAppMessage(() => ({
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.greeting {
|
||||
margin-bottom: 20px;
|
||||
padding: 24px 20px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(139,92,246,0.04));
|
||||
border: 1px solid rgba(99,102,241,0.1);
|
||||
/* streak */
|
||||
.streak-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
background: linear-gradient(135deg, rgba(229, 28, 28, 0.08), rgba(223, 119, 0, 0.06));
|
||||
border: 1px solid rgba(229, 28, 28, 0.15);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.greeting-title {
|
||||
font-size: 26px;
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
.greeting-highlight {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #b388ff, #7c4dff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.greeting-desc {
|
||||
display: block;
|
||||
.streak-text {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--dim5-color);
|
||||
}
|
||||
.streak-sub {
|
||||
flex: 1;
|
||||
font-size: 11px;
|
||||
color: var(--text-tertiary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
.streak-go {
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 50%;
|
||||
background: var(--dim5-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
/* 继续探索 */
|
||||
.resume-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 14px;
|
||||
padding: 12px 16px;
|
||||
box-shadow: var(--card-shadow, none);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.resume-icon {
|
||||
width: 38px; height: 38px;
|
||||
border-radius: 12px;
|
||||
background: var(--color-brand-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.resume-info { flex: 1; min-width: 0; }
|
||||
.resume-label {
|
||||
font-size: 11px;
|
||||
color: var(--text-tertiary);
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.resume-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 搜索 */
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 24px;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.search-icon { color: var(--text-muted); }
|
||||
.search-placeholder { color: var(--text-muted); font-size: 14px; }
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 14px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 10px;
|
||||
background: rgba(99,102,241,0.04);
|
||||
}
|
||||
.section-title {
|
||||
font-size: 17px;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.section-more {
|
||||
font-size: 12px;
|
||||
.search-placeholder {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* 星域网格 */
|
||||
.dim-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.dim-card {
|
||||
border-radius: 18px;
|
||||
padding: 20px 16px;
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
.dim-full {
|
||||
grid-column: 1 / -1;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 14px;
|
||||
box-shadow: var(--card-shadow, none);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.dim-card:active { transform: scale(0.97); }
|
||||
|
||||
.dim-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.dim-icon {
|
||||
width: 42px; height: 42px;
|
||||
border-radius: 12px;
|
||||
width: 34px; height: 34px;
|
||||
border-radius: 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.dim-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: 0.5px;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.dim-desc {
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: var(--text-tertiary);
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.dim-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.dim-lit {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
padding: 3px 8px;
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
.dim-count {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 快讯 */
|
||||
.news-card {
|
||||
border-radius: 14px;
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 10px;
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: var(--card-shadow, none);
|
||||
}
|
||||
|
||||
.news-tag {
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--radius-full);
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.news-tag.hot {
|
||||
background: rgba(239,83,80,0.12);
|
||||
color: #ef5350;
|
||||
}
|
||||
.news-tag.new {
|
||||
background: rgba(0,188,212,0.12);
|
||||
color: #4dd0e1;
|
||||
}
|
||||
.news-tag.pick {
|
||||
background: rgba(124,77,255,0.12);
|
||||
color: #b388ff;
|
||||
}
|
||||
|
||||
.news-tag.hot { background: var(--color-hot-bg); color: var(--color-hot); }
|
||||
.news-tag.new { background: var(--color-new-bg); color: var(--color-new); }
|
||||
.news-tag.pick { background: var(--color-pick-bg); color: var(--color-pick); }
|
||||
.news-title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.news-meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.bottom-spacer {
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
.bottom-spacer { height: 30px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user