refactor(web): 技术信息折叠铺到媒体库 / 审计 / 运行状态,清理正常态徽章与解释型页描述(DEAI-2)
- 媒体库详情:作品 UUID 进技术信息;「实际路径与库根」改「文件位置」,正常态(已核验、 可访问、主位置)不加徽章;外部身份显示「TMDB 90228 · 你确认的 / 自动匹配」,修订号进悬停; 元数据字段来源默认收起(「显示来源」,跟随界面设置);别名默认只列中英文与手动加的; 「内容单元」→「季与集」,「临时单元」→「编号待确认」;侧栏与页标题统一「媒体库」 - 审计:动作列不再重复英文码,对象列显示中文类型 / 能力名,ID 只在详情;补 capability.denied - 运行状态:进程、已下线实例、趋势图与最大表收进折叠区;图例同名序列加序号,不再拼接 - 站点卡:账号正常与登录方式不加徽章,健康不显示毫秒,限流移出卡片 - 文件管理:目录「正常」徽章与重复的剩余空间去掉;已登记 → 已入库,临时文件 → 未完成 - 规则:「修订」列改「最后修改」;不支持的关键字合成一行;页签改「默认规则 / 自动收新作品 / 已忽略」; 默认规则说明去掉「带层」等算法描述 - 站点 Passkey 与登录用通行密钥区分命名;命令面板提示去重 - 删解释型页描述(审计、搜索、下载、下载计划、RSS、规则、媒体库、运行状态、站点、任务、计划、整理) Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
5a717e414c
commit
57ce40168d
31 files changed
+253
-209
No files matched your search
@@ -48,6 +48,16 @@ export function HistoryChart({
|
||||
return [...m.values()].sort((a, b) => String(a.at).localeCompare(String(b.at)));
|
||||
}, [series, metric]);
|
||||
|
||||
// 同名序列(容器重建后 pid 相同)加序号区分,图例不再重复拼接
|
||||
const labels = new Map<string, string>();
|
||||
const seen = new Map<string, number>();
|
||||
for (const s of series) {
|
||||
const base = seriesLabel(s);
|
||||
const n = (seen.get(base) ?? 0) + 1;
|
||||
seen.set(base, n);
|
||||
labels.set(s.instanceId, n > 1 ? `${base}(${n})` : base);
|
||||
}
|
||||
|
||||
if (rows.length === 0) return <EmptyState plain title="这段时间没有采样" className="py-8" />;
|
||||
|
||||
return (
|
||||
@@ -80,13 +90,19 @@ export function HistoryChart({
|
||||
}}
|
||||
cursor={{ stroke: "var(--color-muted-foreground)", strokeDasharray: "3 3" }}
|
||||
/>
|
||||
{series.length > 1 ? <Legend iconType="plainline" wrapperStyle={{ fontSize: 12 }} /> : null}
|
||||
{series.length > 1 ? (
|
||||
<Legend
|
||||
iconType="plainline"
|
||||
wrapperStyle={{ fontSize: 12, paddingTop: 4 }}
|
||||
formatter={(v) => <span className="mr-3 text-muted-foreground">{String(v)}</span>}
|
||||
/>
|
||||
) : null}
|
||||
{series.map((s, i) => (
|
||||
<Line
|
||||
key={s.instanceId}
|
||||
type="monotone"
|
||||
dataKey={s.instanceId}
|
||||
name={seriesLabel(s)}
|
||||
name={labels.get(s.instanceId) ?? seriesLabel(s)}
|
||||
stroke="var(--color-foreground)"
|
||||
strokeOpacity={OPACITY[i % OPACITY.length]}
|
||||
strokeDasharray={DASH[i % DASH.length]}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useStorageCleanup, useSystemStorage } from "~/api/system.ts";
|
||||
import { DataTable } from "~/components/console/data-table.tsx";
|
||||
import { ErrorState, LoadingState } from "~/components/console/empty-state.tsx";
|
||||
import { StatusBadge, type StatusTone } from "~/components/console/status-badge.tsx";
|
||||
import { TechInfo } from "~/components/console/tech-info.tsx";
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { formatRelative } from "~/lib/labels.ts";
|
||||
@@ -257,52 +258,56 @@ export function StorageSection({ health }: { health: SystemHealth }) {
|
||||
</ul>
|
||||
|
||||
<div className="grid gap-6 xl:grid-cols-2">
|
||||
<Panel title={`体积最大的表(前 ${TOP_TABLES})`} note="含索引与 TOAST">
|
||||
<DataTable<TableRow>
|
||||
rows={s.db.tables.slice(0, TOP_TABLES)}
|
||||
rowKey={(t) => `${t.schema}.${t.name}`}
|
||||
empty={{ title: "取不到表体积" }}
|
||||
columns={[
|
||||
{
|
||||
key: "name",
|
||||
header: "表",
|
||||
cell: (t) => (
|
||||
<span className="font-mono text-caption break-all">
|
||||
{t.schema === "public" ? t.name : `${t.schema}.${t.name}`}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "total",
|
||||
header: "合计",
|
||||
align: "right",
|
||||
cell: (t) => <span className="whitespace-nowrap tabular-nums">{formatBytes(t.totalBytes, 1)}</span>,
|
||||
},
|
||||
{
|
||||
key: "idx",
|
||||
header: "索引",
|
||||
align: "right",
|
||||
hideBelow: "md",
|
||||
cell: (t) => (
|
||||
<span className="whitespace-nowrap tabular-nums text-muted-foreground">
|
||||
{formatBytes(t.indexBytes, 1)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rows",
|
||||
header: "估算行数",
|
||||
align: "right",
|
||||
hideBelow: "sm",
|
||||
cell: (t) => (
|
||||
<span className="tabular-nums text-muted-foreground">
|
||||
{t.rowsEstimate === null ? "—" : t.rowsEstimate.toLocaleString("zh-CN")}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Panel>
|
||||
<TechInfo title={`数据库里最大的 ${TOP_TABLES} 张表`} columns={1}>
|
||||
<Panel title={`体积最大的表(前 ${TOP_TABLES})`} note="含索引与 TOAST">
|
||||
<DataTable<TableRow>
|
||||
rows={s.db.tables.slice(0, TOP_TABLES)}
|
||||
rowKey={(t) => `${t.schema}.${t.name}`}
|
||||
empty={{ title: "取不到表体积" }}
|
||||
columns={[
|
||||
{
|
||||
key: "name",
|
||||
header: "表",
|
||||
cell: (t) => (
|
||||
<span className="font-mono text-caption break-all">
|
||||
{t.schema === "public" ? t.name : `${t.schema}.${t.name}`}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "total",
|
||||
header: "合计",
|
||||
align: "right",
|
||||
cell: (t) => (
|
||||
<span className="whitespace-nowrap tabular-nums">{formatBytes(t.totalBytes, 1)}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "idx",
|
||||
header: "索引",
|
||||
align: "right",
|
||||
hideBelow: "md",
|
||||
cell: (t) => (
|
||||
<span className="whitespace-nowrap tabular-nums text-muted-foreground">
|
||||
{formatBytes(t.indexBytes, 1)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rows",
|
||||
header: "估算行数",
|
||||
align: "right",
|
||||
hideBelow: "sm",
|
||||
cell: (t) => (
|
||||
<span className="tabular-nums text-muted-foreground">
|
||||
{t.rowsEstimate === null ? "—" : t.rowsEstimate.toLocaleString("zh-CN")}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Panel>
|
||||
</TechInfo>
|
||||
<div className="space-y-6">
|
||||
<Panel title="缓存" note="条数含尚未清理的过期条目">
|
||||
<DataTable<KvRow>
|
||||
|
||||
Reference in new issue
Block a user