fix: 修复日志页面自动滚动问题 (#382)
This commit is contained in:
@@ -5,4 +5,4 @@ mod response;
|
||||
mod routes;
|
||||
mod wrapper;
|
||||
|
||||
pub use routes::{MpscWriter, router};
|
||||
pub use routes::{MAX_HISTORY_LOGS, MpscWriter, router};
|
||||
|
||||
@@ -6,7 +6,7 @@ use axum::response::sse::{Event, KeepAlive, Sse};
|
||||
use axum::routing::get;
|
||||
use axum::{Extension, Router};
|
||||
use futures::{Stream, StreamExt};
|
||||
pub use mpsc::MpscWriter;
|
||||
pub use mpsc::{MAX_HISTORY_LOGS, MpscWriter};
|
||||
use tokio_stream::wrappers::BroadcastStream;
|
||||
|
||||
pub(super) fn router() -> Router {
|
||||
|
||||
@@ -5,7 +5,7 @@ use parking_lot::Mutex;
|
||||
use tokio::sync::broadcast;
|
||||
use tracing_subscriber::fmt::MakeWriter;
|
||||
|
||||
const MAX_HISTORY_LOGS: usize = 20;
|
||||
pub const MAX_HISTORY_LOGS: usize = 30;
|
||||
|
||||
pub struct MpscWriter {
|
||||
pub sender: broadcast::Sender<String>,
|
||||
|
||||
@@ -23,7 +23,7 @@ mod me;
|
||||
mod video_sources;
|
||||
mod videos;
|
||||
|
||||
pub use logs::MpscWriter;
|
||||
pub use logs::{MAX_HISTORY_LOGS, MpscWriter};
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new().route("/image-proxy", get(image_proxy)).nest(
|
||||
|
||||
@@ -24,7 +24,7 @@ use task::{http_server, video_downloader};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
use crate::api::MpscWriter;
|
||||
use crate::api::{MAX_HISTORY_LOGS, MpscWriter};
|
||||
use crate::config::{ARGS, VersionedConfig};
|
||||
use crate::database::setup_database;
|
||||
use crate::utils::init_logger;
|
||||
@@ -79,7 +79,7 @@ fn spawn_task(
|
||||
/// 初始化日志系统、打印欢迎信息,初始化数据库连接和全局配置
|
||||
async fn init() -> (Arc<DatabaseConnection>, MpscWriter) {
|
||||
let (tx, _rx) = tokio::sync::broadcast::channel(30);
|
||||
let log_history = Arc::new(Mutex::new(VecDeque::with_capacity(20)));
|
||||
let log_history = Arc::new(Mutex::new(VecDeque::with_capacity(MAX_HISTORY_LOGS + 1)));
|
||||
let log_writer = MpscWriter::new(tx, log_history.clone());
|
||||
|
||||
init_logger(&ARGS.log_level, Some(log_writer.clone()));
|
||||
|
||||
@@ -28,8 +28,8 @@ html {
|
||||
--border: oklch(0.929 0.013 255.508);
|
||||
--input: oklch(0.929 0.013 255.508);
|
||||
--ring: oklch(0.704 0.04 256.788);
|
||||
--chart-1: oklch(0.37 0.04 257);
|
||||
--chart-2: oklch(0.13 0.04 265);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
@@ -113,6 +113,9 @@ html {
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--font-mono:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace, ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
<SheetTitle class="text-lg">编辑状态</SheetTitle>
|
||||
<SheetDescription class="text-muted-foreground space-y-1 text-sm">
|
||||
<div>修改视频和分页的下载状态。可以将任务重置为未开始状态,或者标记为已完成。</div>
|
||||
<div class="font-medium text-red-600">
|
||||
<div class="font-medium text-rose-600">
|
||||
⚠️ 已完成任务被重置为未开始,任务重新执行时会覆盖现存文件。
|
||||
</div>
|
||||
</SheetDescription>
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
|
||||
// 获取状态显示信息
|
||||
function getStatusInfo(value: number) {
|
||||
if (value === 7) return { label: '已完成', class: 'text-green-600', dotClass: 'bg-green-500' };
|
||||
if (value === 7)
|
||||
return { label: '已完成', class: 'text-emerald-600', dotClass: 'bg-emerald-600' };
|
||||
if (value >= 1 && value <= 4)
|
||||
return { label: `失败${value}次`, class: 'text-red-600', dotClass: 'bg-red-500' };
|
||||
return { label: '未开始', class: 'text-yellow-600', dotClass: 'bg-yellow-500' };
|
||||
return { label: `失败${value}次`, class: 'text-rose-600', dotClass: 'bg-rose-600' };
|
||||
return { label: '未开始', class: 'text-yellow-600', dotClass: 'bg-yellow-600' };
|
||||
}
|
||||
|
||||
$: statusInfo = getStatusInfo(currentStatus);
|
||||
@@ -71,8 +72,8 @@
|
||||
onclick={() => onStatusChange(7)}
|
||||
{disabled}
|
||||
class="h-7 min-w-[60px] cursor-pointer px-3 text-xs {currentStatus === 7
|
||||
? 'border-green-600 bg-green-600 font-medium text-white hover:bg-green-700'
|
||||
: 'hover:border-green-400 hover:bg-green-50 hover:text-green-700'}"
|
||||
? 'border-emerald-600 bg-emerald-600 font-medium text-white hover:bg-emerald-700'
|
||||
: 'hover:border-emerald-400 hover:bg-emerald-50 hover:text-emerald-700'}"
|
||||
>
|
||||
已完成
|
||||
</Button>
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
|
||||
function getSegmentColor(status: number): string {
|
||||
if (status === 7) {
|
||||
return 'bg-emerald-500'; // 恢复更高对比度的绿色
|
||||
return 'bg-emerald-500';
|
||||
} else if (status === 0) {
|
||||
return 'bg-slate-400'; // 恢复更清晰的灰色 - 未开始
|
||||
return 'bg-yellow-500';
|
||||
} else {
|
||||
return 'bg-rose-500'; // 恢复更清晰的红色 - 失败
|
||||
return 'bg-rose-500';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
const failed = downloadStatus.filter((status) => status !== 7 && status !== 0).length;
|
||||
|
||||
if (completed === total) {
|
||||
return { text: '完成', color: 'outline' }; // 更简洁的文案
|
||||
return { text: '完成', color: 'outline' };
|
||||
} else if (failed > 0) {
|
||||
return { text: '失败', color: 'destructive' };
|
||||
} else {
|
||||
@@ -136,7 +136,7 @@
|
||||
<div
|
||||
class="h-1.5 w-full cursor-help rounded-full transition-all {getSegmentColor(
|
||||
status
|
||||
)} hover:opacity-80"
|
||||
)}"
|
||||
></div>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<BreadCrumb items={$breadcrumbStore} />
|
||||
</div>
|
||||
</header>
|
||||
<div class="w-full overflow-y-auto px-6 py-2" style="scrollbar-width: thin;">
|
||||
<div class="w-full overflow-y-auto px-6 py-2" style="scrollbar-width: thin;" id="main">
|
||||
<slot />
|
||||
</div>
|
||||
</Sidebar.Inset>
|
||||
|
||||
@@ -87,29 +87,29 @@
|
||||
const videoChartConfig = {
|
||||
videos: {
|
||||
label: '视频数量',
|
||||
color: 'var(--chart-1)'
|
||||
color: 'var(--color-slate-700)'
|
||||
}
|
||||
} satisfies Chart.ChartConfig;
|
||||
|
||||
const memoryChartConfig = {
|
||||
used: {
|
||||
label: '整体占用',
|
||||
color: 'var(--chart-1)'
|
||||
color: 'var(--color-slate-700)'
|
||||
},
|
||||
process: {
|
||||
label: '程序占用',
|
||||
color: 'var(--chart-2)'
|
||||
color: 'var(--color-slate-950)'
|
||||
}
|
||||
} satisfies Chart.ChartConfig;
|
||||
|
||||
const cpuChartConfig = {
|
||||
used: {
|
||||
label: '整体占用',
|
||||
color: 'var(--chart-1)'
|
||||
color: 'var(--color-slate-700)'
|
||||
},
|
||||
process: {
|
||||
label: '程序占用',
|
||||
color: 'var(--chart-2)'
|
||||
color: 'var(--color-slate-950)'
|
||||
}
|
||||
} satisfies Chart.ChartConfig;
|
||||
|
||||
|
||||
@@ -8,15 +8,18 @@
|
||||
let logEventSource: EventSource | null = null;
|
||||
let logs: Array<{ timestamp: string; level: string; message: string }> = [];
|
||||
let shouldAutoScroll = true;
|
||||
let main: HTMLElement | null = null;
|
||||
|
||||
function checkScrollPosition() {
|
||||
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
|
||||
shouldAutoScroll = scrollTop + clientHeight >= scrollHeight - 5;
|
||||
if (main) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = main;
|
||||
shouldAutoScroll = scrollTop + clientHeight >= scrollHeight - 5;
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
if (shouldAutoScroll) {
|
||||
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });
|
||||
if (shouldAutoScroll && main) {
|
||||
main.scrollTop = main.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,23 +48,24 @@
|
||||
|
||||
onMount(() => {
|
||||
setBreadcrumb([{ label: '日志' }]);
|
||||
window.addEventListener('scroll', checkScrollPosition);
|
||||
main = document.getElementById('main');
|
||||
main?.addEventListener('scroll', checkScrollPosition);
|
||||
startLogStream();
|
||||
return () => {
|
||||
stopLogStream();
|
||||
window.removeEventListener('scroll', checkScrollPosition);
|
||||
main?.removeEventListener('scroll', checkScrollPosition);
|
||||
};
|
||||
});
|
||||
|
||||
function getLevelColor(level: string) {
|
||||
switch (level) {
|
||||
case 'ERROR':
|
||||
return 'text-red-600';
|
||||
return 'text-rose-600';
|
||||
case 'WARN':
|
||||
return 'text-yellow-600';
|
||||
case 'INFO':
|
||||
default:
|
||||
return 'text-green-600';
|
||||
return 'text-emerald-600';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -112,8 +112,8 @@
|
||||
{:else}
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-2 w-2 rounded-full bg-green-500"></div>
|
||||
<span class="text-sm text-green-600">已认证</span>
|
||||
<div class="h-2 w-2 rounded-full bg-emerald-500"></div>
|
||||
<span class="text-sm text-emerald-600">已认证</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
Reference in New Issue
Block a user