mirror of
https://github.com/stardrophere/InsightRadar.git
synced 2026-06-06 00:00:05 +08:00
界面优化
This commit is contained in:
@@ -19,13 +19,18 @@ const avatarUrl = computed(
|
|||||||
`https://ui-avatars.com/api/?name=${encodeURIComponent(displayName.value)}&background=6366f1&color=fff`,
|
`https://ui-avatars.com/api/?name=${encodeURIComponent(displayName.value)}&background=6366f1&color=fff`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const navItems = [
|
const navItems = computed(() => {
|
||||||
|
const items = [
|
||||||
{ name: '全局热点池', icon: 'fa-solid fa-fire', route: '/' },
|
{ name: '全局热点池', icon: 'fa-solid fa-fire', route: '/' },
|
||||||
{ name: '事件追溯分析', icon: 'fa-solid fa-chart-line', route: '/search' },
|
{ name: '事件追溯分析', icon: 'fa-solid fa-chart-line', route: '/search' },
|
||||||
{ name: '公关修改追踪', icon: 'fa-solid fa-mask', route: '/revisions' },
|
{ name: '公关修改追踪', icon: 'fa-solid fa-mask', route: '/revisions' },
|
||||||
{ name: '我的泛订阅', icon: 'fa-solid fa-rss', route: '/topics' },
|
]
|
||||||
{ name: 'AI 简报设置', icon: 'fa-solid fa-paper-plane', route: '/delivery' },
|
if (authStore.isAuthenticated) {
|
||||||
]
|
items.push({ name: '我的泛订阅', icon: 'fa-solid fa-rss', route: '/topics' })
|
||||||
|
items.push({ name: 'AI 简报设置', icon: 'fa-solid fa-paper-plane', route: '/delivery' })
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
})
|
||||||
|
|
||||||
function isActive(path: string) {
|
function isActive(path: string) {
|
||||||
return route.path === path
|
return route.path === path
|
||||||
@@ -72,7 +77,7 @@ function toggleSidebar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 用户信息 -->
|
<!-- 用户信息 -->
|
||||||
<div class="sidebar-user">
|
<div class="sidebar-user" v-if="authStore.isAuthenticated">
|
||||||
<img :src="avatarUrl" class="user-avatar" alt="头像" />
|
<img :src="avatarUrl" class="user-avatar" alt="头像" />
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<p class="user-name">{{ displayName }}</p>
|
<p class="user-name">{{ displayName }}</p>
|
||||||
@@ -85,6 +90,18 @@ function toggleSidebar() {
|
|||||||
<i class="fa-solid fa-right-from-bracket"></i>
|
<i class="fa-solid fa-right-from-bracket"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sidebar-user guest-user" v-else>
|
||||||
|
<div class="user-info">
|
||||||
|
<p class="user-name">游客,您好</p>
|
||||||
|
<p class="user-status guest">
|
||||||
|
<span class="status-dot gray"></span>
|
||||||
|
未登录
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button class="login-btn" title="前往登录" @click="router.push('/login')">
|
||||||
|
<i class="fa-solid fa-arrow-right-to-bracket"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
<!-- 主内容区 -->
|
||||||
@@ -240,6 +257,10 @@ function toggleSidebar() {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-status.guest {
|
||||||
|
color: var(--text-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
@@ -249,17 +270,25 @@ function toggleSidebar() {
|
|||||||
animation: pulse-dot 2s infinite;
|
animation: pulse-dot 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-dot.gray {
|
||||||
|
background: var(--text-placeholder);
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse-dot {
|
@keyframes pulse-dot {
|
||||||
0%, 100% { opacity: 1; }
|
0%, 100% { opacity: 1; }
|
||||||
50% { opacity: 0.4; }
|
50% { opacity: 0.4; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-btn {
|
.logout-btn, .login-btn {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-btn:hover {
|
.logout-btn:hover {
|
||||||
@@ -267,6 +296,11 @@ function toggleSidebar() {
|
|||||||
background: rgba(239, 68, 68, 0.1);
|
background: rgba(239, 68, 68, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-btn:hover {
|
||||||
|
color: var(--brand-primary);
|
||||||
|
background: var(--brand-primary-alpha);
|
||||||
|
}
|
||||||
|
|
||||||
/* ==========================================
|
/* ==========================================
|
||||||
主内容区
|
主内容区
|
||||||
========================================== */
|
========================================== */
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const router = createRouter({
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: DashboardLayout,
|
component: DashboardLayout,
|
||||||
meta: { requiresAuth: true },
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -46,11 +45,13 @@ const router = createRouter({
|
|||||||
path: 'topics',
|
path: 'topics',
|
||||||
name: 'topics',
|
name: 'topics',
|
||||||
component: () => import('@/views/TopicsView.vue'),
|
component: () => import('@/views/TopicsView.vue'),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'delivery',
|
path: 'delivery',
|
||||||
name: 'delivery',
|
name: 'delivery',
|
||||||
component: () => import('@/views/DeliveryView.vue'),
|
component: () => import('@/views/DeliveryView.vue'),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const minHot = ref(savedThreshold !== null ? Number(savedThreshold) : 3)
|
|||||||
|
|
||||||
// 增加时间筛选和排序
|
// 增加时间筛选和排序
|
||||||
const hoursRange = ref(48)
|
const hoursRange = ref(48)
|
||||||
const sortBy = ref('hot_score')
|
const sortBy = ref('created_at')
|
||||||
const recSortBy = ref('match_score')
|
const recSortBy = ref('match_score')
|
||||||
|
|
||||||
// 当前鼠标悬停的平台行标识
|
// 当前鼠标悬停的平台行标识
|
||||||
@@ -91,8 +91,9 @@ const hoursOptions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const sortOptions = [
|
const sortOptions = [
|
||||||
|
{ label: '时间排序', value: 'created_at' },
|
||||||
{ label: '热度排序', value: 'hot_score' },
|
{ label: '热度排序', value: 'hot_score' },
|
||||||
{ label: '时间排序', value: 'created_at' },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const recSortOptions = [
|
const recSortOptions = [
|
||||||
@@ -278,7 +279,7 @@ async function loadEvents(append = false) {
|
|||||||
|
|
||||||
/** 加载推荐匹配数据 */
|
/** 加载推荐匹配数据 */
|
||||||
async function loadRecommendations() {
|
async function loadRecommendations() {
|
||||||
if (!userId.value) return
|
if (!authStore.isAuthenticated || !userId.value) return
|
||||||
loadingRecommend.value = true
|
loadingRecommend.value = true
|
||||||
try {
|
try {
|
||||||
const [keywords, recommended] = await Promise.all([
|
const [keywords, recommended] = await Promise.all([
|
||||||
@@ -678,7 +679,7 @@ watch(() => route.query.event, (newId) => {
|
|||||||
<div class="widgets-column">
|
<div class="widgets-column">
|
||||||
|
|
||||||
<!-- 为你推荐(基于用户关键词的匹配) -->
|
<!-- 为你推荐(基于用户关键词的匹配) -->
|
||||||
<section class="widget-panel recommend-widget">
|
<section class="widget-panel recommend-widget" v-if="authStore.isAuthenticated">
|
||||||
<div class="widget-header recommend-header">
|
<div class="widget-header recommend-header">
|
||||||
<h3>
|
<h3>
|
||||||
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
||||||
|
|||||||
@@ -287,6 +287,10 @@ onUnmounted(() => {
|
|||||||
<button class="btn-primary" :disabled="authStore.loading" type="submit">
|
<button class="btn-primary" :disabled="authStore.loading" type="submit">
|
||||||
{{ isSubmitting ? '登录中...' : (loginMode === 'password' ? '密码登录' : '邮箱验证码登录') }}
|
{{ isSubmitting ? '登录中...' : (loginMode === 'password' ? '密码登录' : '邮箱验证码登录') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn-primary guest-btn" @click="router.push('/')" style="margin-top: 12px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border-subtle);">
|
||||||
|
以游客身份体验
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="form-footer">
|
<p class="form-footer">
|
||||||
@@ -574,6 +578,14 @@ onUnmounted(() => {
|
|||||||
border: 1px solid rgba(16, 185, 129, 0.2);
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary.guest-btn:hover {
|
||||||
|
background: var(--bg-hover) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-color: var(--border-strong) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-footer {
|
.form-footer {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -265,6 +265,9 @@ onUnmounted(() => {
|
|||||||
<button class="btn-primary" :disabled="authStore.loading" type="submit">
|
<button class="btn-primary" :disabled="authStore.loading" type="submit">
|
||||||
{{ isSubmitting ? '提交中...' : '注册并登录' }}
|
{{ isSubmitting ? '提交中...' : '注册并登录' }}
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn-primary guest-btn" @click="router.push('/')" style="margin-top: 12px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border-subtle);">
|
||||||
|
以游客身份体验
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="form-footer">
|
<p class="form-footer">
|
||||||
@@ -533,6 +536,14 @@ onUnmounted(() => {
|
|||||||
border: 1px solid rgba(16, 185, 129, 0.2);
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary.guest-btn:hover {
|
||||||
|
background: var(--bg-hover) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-color: var(--border-strong) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-footer {
|
.form-footer {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<!-- 公关修改追踪页:展示热搜标题被偷偷修改的历史记录 -->
|
<!-- 公关修改追踪页:展示热搜标题被偷偷修改的历史记录 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, reactive } from 'vue'
|
||||||
|
|
||||||
import { fetchHeadlineRevisions } from '@/api/events'
|
import { fetchHeadlineRevisions } from '@/api/events'
|
||||||
import type { HeadlineRevision } from '@/types/event'
|
import type { HeadlineRevision } from '@/types/event'
|
||||||
@@ -141,6 +141,13 @@ function changeRange(hours: number) {
|
|||||||
loadRevisions()
|
loadRevisions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 控制每个卡片的折叠状态 */
|
||||||
|
const expandedCards = reactive<Record<number, boolean>>({})
|
||||||
|
|
||||||
|
function toggleExpand(eventId: number) {
|
||||||
|
expandedCards[eventId] = !expandedCards[eventId]
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(loadRevisions)
|
onMounted(loadRevisions)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -219,7 +226,9 @@ onMounted(loadRevisions)
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chain-area">
|
<div class="chain-area">
|
||||||
|
<!-- 当记录超过 7 条且未展开时,只显示前 3 条和后 3 条,中间用折叠按钮代替 -->
|
||||||
<template v-for="(title, idx) in chain.titles" :key="idx">
|
<template v-for="(title, idx) in chain.titles" :key="idx">
|
||||||
|
<template v-if="chain.titles.length <= 7 || expandedCards[chain.event_id] || idx < 3 || idx >= chain.titles.length - 3">
|
||||||
<div
|
<div
|
||||||
class="chain-title"
|
class="chain-title"
|
||||||
:class="{
|
:class="{
|
||||||
@@ -237,9 +246,21 @@ onMounted(loadRevisions)
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="idx < chain.titles.length - 1" class="chain-arrow">
|
<div v-if="idx < chain.titles.length - 1" class="chain-arrow">
|
||||||
<i class="fa-solid fa-arrow-down"></i>
|
<i class="fa-solid fa-arrow-down" v-if="chain.titles.length <= 7 || expandedCards[chain.event_id] || idx !== 2"></i>
|
||||||
|
<!-- 在第3条的箭头处显示折叠按钮 -->
|
||||||
|
<button v-else class="expand-btn" @click="toggleExpand(chain.event_id)">
|
||||||
|
<i class="fa-solid fa-angles-down"></i>
|
||||||
|
展开省略的 {{ chain.titles.length - 6 }} 次修改
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
|
<div v-if="chain.titles.length > 7 && expandedCards[chain.event_id]" class="chain-arrow collapse-arrow">
|
||||||
|
<button class="expand-btn collapse-btn" @click="toggleExpand(chain.event_id)">
|
||||||
|
<i class="fa-solid fa-angles-up"></i>
|
||||||
|
收起修改记录
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -536,8 +557,53 @@ onMounted(loadRevisions)
|
|||||||
.chain-arrow {
|
.chain-arrow {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
padding: 3px 0 3px 22px;
|
padding: 3px 0 3px 22px;
|
||||||
color: var(--text-placeholder);
|
color: var(--text-placeholder);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.expand-btn {
|
||||||
|
background: var(--bg-surface);
|
||||||
|
border: 1px dashed var(--border-strong);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-btn:hover {
|
||||||
|
background: var(--brand-primary-alpha);
|
||||||
|
color: var(--brand-primary);
|
||||||
|
border-color: var(--brand-primary);
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-arrow {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 12px 0 0 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-top: 1px dashed var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 0;
|
||||||
|
color: var(--text-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-btn:hover {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const keyword = ref('')
|
|||||||
const searchResult = ref<SearchTimelineResponse | null>(null)
|
const searchResult = ref<SearchTimelineResponse | null>(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const hours = ref(2)
|
const hours = ref(2)
|
||||||
const searchMode = ref<'exact' | 'semantic' | 'hybrid'>('hybrid')
|
const searchMode = ref<'exact' | 'semantic' | 'hybrid'>('exact')
|
||||||
const selectedTimeLabel = ref<string | null>(null)
|
const selectedTimeLabel = ref<string | null>(null)
|
||||||
|
|
||||||
const timeOptions = [
|
const timeOptions = [
|
||||||
|
|||||||
Reference in New Issue
Block a user