Restyle the web UI in a light theme, step 2: remaining pages
- News: show the list and the article detail side by side; the drawer becomes a persistent detail panel with a revision picker and +/- line counts. - Crawler console: one card per source instead of tabs, plus a merged live log panel filterable by source and level. - Chat: segmented Chat / CONTEXT.md switch, message cards with times, bordered tool-call rows and a context usage bar. - Login, Skills and Settings move to the panel style; diff and highlight colors follow the new palette. The market K-line area stays dark.
This commit is contained in:
1 parent
c018b3693a
commit
916a2dee73
15 files changed
+685
-477
No files matched your search
@@ -0,0 +1,123 @@
|
||||
<script lang="ts" setup>
|
||||
import type { NewsDetail, Revision } from "@/apis/news"
|
||||
import { Link } from "@element-plus/icons-vue"
|
||||
import { getNews, listRevisions } from "@/apis/news"
|
||||
import { useSourceNames } from "@/composables/useSourceNames"
|
||||
import { diffLines, formatTime, htmlToLines } from "@/utils/text"
|
||||
|
||||
const props = defineProps<{ newsId: number | null }>()
|
||||
|
||||
const sourceNames = useSourceNames()
|
||||
const detail = ref<NewsDetail>()
|
||||
const revisions = ref<Revision[]>([])
|
||||
const activeTab = ref("content")
|
||||
// 当前查看的修订序号,与上一版对比
|
||||
const selectedRevision = ref<number>()
|
||||
|
||||
watch(() => props.newsId, async (id) => {
|
||||
if (id == null)
|
||||
return
|
||||
activeTab.value = "content"
|
||||
detail.value = undefined
|
||||
const [news, revs] = await Promise.all([getNews(id), listRevisions(id)])
|
||||
detail.value = news
|
||||
revisions.value = revs
|
||||
selectedRevision.value = revs[0]?.revision_no
|
||||
})
|
||||
|
||||
// 第三方正文放进无脚本权限的 sandbox iframe,避免其中的脚本访问本站
|
||||
const srcdoc = computed(() => detail.value && `<!doctype html><html><head><meta charset="utf-8">
|
||||
<meta name="referrer" content="no-referrer"><base target="_blank">
|
||||
<style>body{font:15px/1.8 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif;color:#334155;margin:0 4px}img{max-width:100%;height:auto}</style>
|
||||
</head><body>${detail.value.content_html}</body></html>`)
|
||||
|
||||
const diff = computed(() => {
|
||||
const index = revisions.value.findIndex(r => r.revision_no === selectedRevision.value)
|
||||
if (index < 0)
|
||||
return []
|
||||
const current = revisions.value[index]
|
||||
const previous = revisions.value[index + 1]
|
||||
const toLines = (r?: Revision) => r ? [r.title, r.summary, ...htmlToLines(r.content_html)].filter(Boolean) : []
|
||||
return diffLines(toLines(previous), toLines(current))
|
||||
})
|
||||
|
||||
const diffStats = computed(() => ({
|
||||
add: diff.value.filter(line => line.type === "add").length,
|
||||
del: diff.value.filter(line => line.type === "del").length
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never" class="flex flex-col" body-class="min-h-0 flex-1 flex flex-col gap-3">
|
||||
<el-empty v-if="newsId == null" description="点击左侧新闻查看详情" class="m-auto" />
|
||||
<template v-else-if="detail">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<el-tag size="small">
|
||||
{{ sourceNames[detail.source_code] ?? detail.source_code }}
|
||||
</el-tag>
|
||||
<el-tag v-if="detail.is_paywalled" size="small" type="warning">
|
||||
付费(仅公开部分)
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="text-xl text-slate-900 font-bold leading-snug">
|
||||
{{ detail.title || detail.excerpt.slice(0, 60) }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-x-6 gap-y-2 border border-slate-200 rounded-lg border-solid bg-slate-50 px-3 py-2 text-sm">
|
||||
<div>
|
||||
<span class="label">发布时间</span><span class="num">{{ formatTime(detail.published_at, "YYYY-MM-DD HH:mm:ss") }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">作者</span>{{ detail.author || "-" }}
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
<span class="label">标签</span>
|
||||
<el-tag v-for="tag in detail.tags" :key="tag" size="small" type="info">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
<span v-if="!detail.tags.length">-</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<el-link :href="detail.url" target="_blank" type="primary" :icon="Link">
|
||||
原文链接
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab" class="min-h-0 flex flex-1 flex-col">
|
||||
<el-tab-pane label="正文" name="content" class="h-full">
|
||||
<iframe :srcdoc="srcdoc" sandbox="allow-popups allow-popups-to-escape-sandbox" class="h-full w-full border-0" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="`修订(${revisions.length})`" name="revisions" class="h-full overflow-auto">
|
||||
<div class="mb-3 flex flex-wrap items-center gap-3 border border-slate-200 rounded-lg border-solid bg-slate-50 px-3 py-2">
|
||||
<span class="text-xs text-slate-500">比对版本</span>
|
||||
<el-select v-model="selectedRevision" size="small" class="w-64">
|
||||
<el-option
|
||||
v-for="(r, i) in revisions" :key="r.revision_no" :value="r.revision_no"
|
||||
:label="`v${r.revision_no} · ${formatTime(r.captured_at)}${revisions[i + 1] ? ` vs v${revisions[i + 1].revision_no}` : '(首版)'}`"
|
||||
/>
|
||||
</el-select>
|
||||
<span class="num text-xs text-emerald-700">+{{ diffStats.add }} 行</span>
|
||||
<span class="num text-xs text-rose-700">−{{ diffStats.del }} 行</span>
|
||||
</div>
|
||||
<div class="overflow-hidden border border-slate-200 rounded-lg border-solid text-sm leading-7">
|
||||
<div
|
||||
v-for="(line, i) in diff" :key="i" class="flex gap-2 px-3"
|
||||
:class="{ 'bg-emerald-50 text-emerald-900': line.type === 'add', 'bg-rose-50 text-rose-900 line-through': line.type === 'del' }"
|
||||
>
|
||||
<span class="num w-3 shrink-0 text-slate-400">{{ line.type === "add" ? "+" : line.type === "del" ? "−" : "" }}</span>
|
||||
<span class="min-w-0">{{ line.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<div v-else v-loading="true" class="h-full" />
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user