优化UI体验

This commit is contained in:
Hommy
2026-06-05 18:34:09 +08:00
parent 0e31bd8fb0
commit ec840b62c9
4 changed files with 91 additions and 20 deletions
+12 -13
View File
@@ -61,6 +61,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -825,7 +826,6 @@
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
@@ -847,7 +847,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -864,7 +863,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
@@ -879,7 +877,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -1652,6 +1649,7 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@@ -2235,6 +2233,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -2380,6 +2379,7 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -2716,6 +2716,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -3342,8 +3343,7 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
"optional": true
},
"node_modules/cross-env": {
"version": "10.1.0",
@@ -3564,6 +3564,7 @@
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.0.12",
"builder-util": "26.0.11",
@@ -3882,7 +3883,6 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
@@ -3903,7 +3903,6 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
@@ -5866,7 +5865,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
@@ -5884,7 +5882,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
@@ -6007,6 +6004,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6059,6 +6057,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -6748,7 +6747,6 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
@@ -6812,7 +6810,6 @@
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.6"
},
@@ -6827,7 +6824,6 @@
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -6896,6 +6892,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -7095,6 +7092,7 @@
"integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -7203,6 +7201,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -1,6 +1,17 @@
import { formatToTime } from '../utils/date';
import { partitionLogsForDisplay } from '../utils/downloadLog';
import { useEffect, useRef } from 'react';
function LogItem({ log, index, logIconMap }) {
return (
<li className={`log-item ${log.level}`}>
<span className="log-time">[{formatToTime(log.time)}]</span>
<i className={`log-icon show ${logIconMap[log.level] || 'hide'}`}></i>
<span className="log-message">{log.message}</span>
</li>
);
}
function LogModule({ logs, onClear }) {
const logIconMap = {
error: "fas fa-times-circle",
@@ -9,7 +20,9 @@ function LogModule({ logs, onClear }) {
all: "fas fa-check-circle",
};
const logListRef = useRef(null);
// 当日志列表变化时,自动滚动到底部
const { settled, active } = partitionLogsForDisplay(logs);
// 当日志列表变化时,自动滚动到底部,便于查看正在下载的配置/资源
useEffect(() => {
if (logListRef.current) {
logListRef.current.scrollTop = logListRef.current.scrollHeight;
@@ -28,14 +41,24 @@ function LogModule({ logs, onClear }) {
<div className="log-empty">暂无日志记录</div>
) : (
<ul className="log-list" ref={logListRef}>
{logs.map((log, index) => (
<li key={log.id || `${log.time}-${index}`} className={`log-item ${log.level}`}>
<span className="log-time">[{formatToTime(log.time)}]</span>
<i className={`log-icon show ${logIconMap[log.level] || 'hide'}`}></i>
<span className={`log-message`}>
{log.message}
{settled.map((log, index) => (
<LogItem key={log.id || `${log.time}-${index}`} log={log} index={index} logIconMap={logIconMap} />
))}
{active.length > 0 && (
<li className="log-active-divider" aria-label="正在下载">
<span className="log-active-divider-label">
<i className="fas fa-spinner fa-spin"></i>
正在下载 ({active.length})
</span>
</li>
)}
{active.map((log, index) => (
<LogItem
key={log.id || `${log.time}-active-${index}`}
log={log}
index={index}
logIconMap={logIconMap}
/>
))}
</ul>
)}
@@ -234,3 +234,25 @@
.log-item:last-child {
border-bottom: none;
}
.log-active-divider {
padding: 6px 12px;
border-top: 1px solid #b8daff;
border-bottom: 1px solid #b8daff;
background: linear-gradient(180deg, #eef6ff 0%, #e3f0ff 100%);
list-style: none;
}
.log-active-divider-label {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.875rem;
font-weight: 600;
color: var(--secondary-color);
}
.log-active-divider + .log-item,
.log-active-divider ~ .log-item {
background-color: #f7fbff;
}
@@ -1,5 +1,32 @@
const TERMINAL_LEVELS = new Set(["success", "error", "all"]);
/** 单文件下载日志(配置/资源),且仍处于下载中 */
export function isActiveFileDownloadLog(log) {
return (
log?.level === "loading" &&
typeof log?.id === "string" &&
log.id.startsWith("file-")
);
}
/**
* 将日志拆为「已完成/其他」与「下载中的配置/资源」,后者固定展示在底部。
*/
export function partitionLogsForDisplay(logs) {
const settled = [];
const active = [];
for (const log of logs) {
if (isActiveFileDownloadLog(log)) {
active.push(log);
} else {
settled.push(log);
}
}
return { settled, active };
}
function mergeLogEntry(existing, incoming) {
if (TERMINAL_LEVELS.has(existing.level) && incoming.level === "loading") {
return existing;