mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-02 19:00:49 +08:00
feat: add toggle for showing raw logs and update log display logic
This commit is contained in:
@@ -752,6 +752,8 @@
|
|||||||
"loaded_lines": "Loaded: {{count}} lines",
|
"loaded_lines": "Loaded: {{count}} lines",
|
||||||
"filtered_lines": "Filtered: {{count}} lines",
|
"filtered_lines": "Filtered: {{count}} lines",
|
||||||
"hide_management_logs": "Hide {{prefix}} logs",
|
"hide_management_logs": "Hide {{prefix}} logs",
|
||||||
|
"show_raw_logs": "Show Raw Logs",
|
||||||
|
"show_raw_logs_hint": "Show original log text for easier multi-line copy",
|
||||||
"search_placeholder": "Search logs by content or keyword",
|
"search_placeholder": "Search logs by content or keyword",
|
||||||
"search_empty_title": "No matching logs found",
|
"search_empty_title": "No matching logs found",
|
||||||
"search_empty_desc": "Try a different keyword or clear the filters.",
|
"search_empty_desc": "Try a different keyword or clear the filters.",
|
||||||
|
|||||||
@@ -752,6 +752,8 @@
|
|||||||
"loaded_lines": "已载入 {{count}} 行",
|
"loaded_lines": "已载入 {{count}} 行",
|
||||||
"filtered_lines": "已过滤 {{count}} 行",
|
"filtered_lines": "已过滤 {{count}} 行",
|
||||||
"hide_management_logs": "屏蔽 {{prefix}} 日志",
|
"hide_management_logs": "屏蔽 {{prefix}} 日志",
|
||||||
|
"show_raw_logs": "显示原始日志",
|
||||||
|
"show_raw_logs_hint": "直接显示原始日志文本,方便多行复制",
|
||||||
"search_placeholder": "搜索日志内容或关键字",
|
"search_placeholder": "搜索日志内容或关键字",
|
||||||
"search_empty_title": "未找到匹配的日志",
|
"search_empty_title": "未找到匹配的日志",
|
||||||
"search_empty_desc": "尝试更换关键字或清空筛选条件。",
|
"search_empty_desc": "尝试更换关键字或清空筛选条件。",
|
||||||
|
|||||||
@@ -267,6 +267,30 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rawLog {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
cursor: text;
|
||||||
|
user-select: text;
|
||||||
|
white-space: pre;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family:
|
||||||
|
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||||
|
monospace;
|
||||||
|
font-size: 12.5px;
|
||||||
|
line-height: 1.45;
|
||||||
|
|
||||||
|
@include tablet {
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mobile {
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 11.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.logRow {
|
.logRow {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 170px 1fr;
|
grid-template-columns: 170px 1fr;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Modal } from '@/components/ui/Modal';
|
|||||||
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
|
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
|
||||||
import {
|
import {
|
||||||
IconDownload,
|
IconDownload,
|
||||||
|
IconCode,
|
||||||
IconEyeOff,
|
IconEyeOff,
|
||||||
IconRefreshCw,
|
IconRefreshCw,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
@@ -383,6 +384,7 @@ export function LogsPage() {
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const deferredSearchQuery = useDeferredValue(searchQuery);
|
const deferredSearchQuery = useDeferredValue(searchQuery);
|
||||||
const [hideManagementLogs, setHideManagementLogs] = useState(true);
|
const [hideManagementLogs, setHideManagementLogs] = useState(true);
|
||||||
|
const [showRawLogs, setShowRawLogs] = useState(false);
|
||||||
const [errorLogs, setErrorLogs] = useState<ErrorLogItem[]>([]);
|
const [errorLogs, setErrorLogs] = useState<ErrorLogItem[]>([]);
|
||||||
const [loadingErrors, setLoadingErrors] = useState(false);
|
const [loadingErrors, setLoadingErrors] = useState(false);
|
||||||
const [errorLogsError, setErrorLogsError] = useState('');
|
const [errorLogsError, setErrorLogsError] = useState('');
|
||||||
@@ -632,10 +634,12 @@ export function LogsPage() {
|
|||||||
return { filteredLines: working, removedCount: removed };
|
return { filteredLines: working, removedCount: removed };
|
||||||
}, [baseLines, hideManagementLogs, trimmedSearchQuery]);
|
}, [baseLines, hideManagementLogs, trimmedSearchQuery]);
|
||||||
|
|
||||||
const parsedVisibleLines = useMemo(
|
const parsedVisibleLines = useMemo(() => {
|
||||||
() => filteredLines.map((line) => parseLogLine(line)),
|
if (showRawLogs) return [];
|
||||||
[filteredLines]
|
return filteredLines.map((line) => parseLogLine(line));
|
||||||
);
|
}, [filteredLines, showRawLogs]);
|
||||||
|
|
||||||
|
const rawVisibleText = useMemo(() => filteredLines.join('\n'), [filteredLines]);
|
||||||
|
|
||||||
const canLoadMore = !isSearching && logState.visibleFrom > 0;
|
const canLoadMore = !isSearching && logState.visibleFrom > 0;
|
||||||
|
|
||||||
@@ -817,6 +821,22 @@ export function LogsPage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ToggleSwitch
|
||||||
|
checked={showRawLogs}
|
||||||
|
onChange={setShowRawLogs}
|
||||||
|
label={
|
||||||
|
<span
|
||||||
|
className={styles.switchLabel}
|
||||||
|
title={t('logs.show_raw_logs_hint', {
|
||||||
|
defaultValue: 'Show original log text for easier multi-line copy',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<IconCode size={16} />
|
||||||
|
{t('logs.show_raw_logs', { defaultValue: 'Show raw logs' })}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className={styles.toolbar}>
|
<div className={styles.toolbar}>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -870,14 +890,14 @@ export function LogsPage() {
|
|||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="hint">{t('logs.loading')}</div>
|
<div className="hint">{t('logs.loading')}</div>
|
||||||
) : logState.buffer.length > 0 && parsedVisibleLines.length > 0 ? (
|
) : logState.buffer.length > 0 && filteredLines.length > 0 ? (
|
||||||
<div ref={logViewerRef} className={styles.logPanel} onScroll={handleLogScroll}>
|
<div ref={logViewerRef} className={styles.logPanel} onScroll={handleLogScroll}>
|
||||||
{canLoadMore && (
|
{canLoadMore && (
|
||||||
<div className={styles.loadMoreBanner}>
|
<div className={styles.loadMoreBanner}>
|
||||||
<span>{t('logs.load_more_hint')}</span>
|
<span>{t('logs.load_more_hint')}</span>
|
||||||
<div className={styles.loadMoreStats}>
|
<div className={styles.loadMoreStats}>
|
||||||
<span>
|
<span>
|
||||||
{t('logs.loaded_lines', { count: parsedVisibleLines.length })}
|
{t('logs.loaded_lines', { count: filteredLines.length })}
|
||||||
</span>
|
</span>
|
||||||
{removedCount > 0 && (
|
{removedCount > 0 && (
|
||||||
<span className={styles.loadMoreCount}>
|
<span className={styles.loadMoreCount}>
|
||||||
@@ -890,6 +910,11 @@ export function LogsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{showRawLogs ? (
|
||||||
|
<pre className={styles.rawLog} spellCheck={false}>
|
||||||
|
{rawVisibleText}
|
||||||
|
</pre>
|
||||||
|
) : (
|
||||||
<div className={styles.logList}>
|
<div className={styles.logList}>
|
||||||
{parsedVisibleLines.map((line, index) => {
|
{parsedVisibleLines.map((line, index) => {
|
||||||
const rowClassNames = [styles.logRow];
|
const rowClassNames = [styles.logRow];
|
||||||
@@ -987,6 +1012,7 @@ export function LogsPage() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : logState.buffer.length > 0 ? (
|
) : logState.buffer.length > 0 ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
|
|||||||
Reference in New Issue
Block a user