fix(web): 技能页 bg-paper/删除虚构创作中徽标/设置页 try-finally/文风页移动端定高 grid

This commit is contained in:
Yaojia Wang
2026-06-29 16:52:43 +02:00
parent d7fcb8a329
commit 69f3c2a29b
4 changed files with 60 additions and 48 deletions

View File

@@ -1,5 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { BookOpen, ChevronRight, Clock3, Sparkles } from "lucide-react"; import { BookOpen, ChevronRight, Clock3 } from "lucide-react";
import { Badge } from "@/components/ui/Badge"; import { Badge } from "@/components/ui/Badge";
import type { ProjectResponse } from "@/lib/api/types"; import type { ProjectResponse } from "@/lib/api/types";
@@ -67,12 +67,8 @@ export function ProjectCard({ project, compact = false }: ProjectCardProps) {
</h2> </h2>
<div className="mt-2 flex flex-wrap gap-2"> <div className="mt-2 flex flex-wrap gap-2">
{project.genre ? <Badge>{project.genre}</Badge> : null} {project.genre ? <Badge>{project.genre}</Badge> : null}
<Badge variant="accent">
<Sparkles className="h-3 w-3" aria-hidden="true" />
</Badge>
{pendingCount > 0 ? ( {pendingCount > 0 ? (
<Badge variant="warning">{pendingCount} 稿</Badge> <Badge variant="warning">{pendingCount} </Badge>
) : null} ) : null}
</div> </div>
</div> </div>

View File

@@ -21,6 +21,7 @@ import { Select } from "@/components/ui/Select";
import { StatusNote } from "@/components/ui/StatusNote"; import { StatusNote } from "@/components/ui/StatusNote";
import { TextInput } from "@/components/ui/TextInput"; import { TextInput } from "@/components/ui/TextInput";
import { api } from "@/lib/api/client"; import { api } from "@/lib/api/client";
import { cardClass } from "@/lib/ui/variants";
import { import {
API_KEY_PROVIDERS, API_KEY_PROVIDERS,
KNOWN_PROVIDERS, KNOWN_PROVIDERS,
@@ -91,17 +92,22 @@ export function ProvidersSettings({
const saveRouting = async (): Promise<void> => { const saveRouting = async (): Promise<void> => {
setSavingRouting(true); setSavingRouting(true);
const { data, error } = await api.PUT("/settings/providers", { try {
body: { tier_routing: draftsToRoutingInput(routing) }, const { data, error } = await api.PUT("/settings/providers", {
}); body: { tier_routing: draftsToRoutingInput(routing) },
setSavingRouting(false); });
if (error || !data) { if (error || !data) {
toast("保存档位路由失败,请重试", "error"); toast("保存档位路由失败,请重试", "error");
return; return;
}
setProviders(data.providers ?? []);
setRouting(toRoutingDrafts(data.tier_routing ?? []));
toast("档位路由已保存", "success");
} catch {
toast("保存档位路由失败,请检查后端连接", "error");
} finally {
setSavingRouting(false);
} }
setProviders(data.providers ?? []);
setRouting(toRoutingDrafts(data.tier_routing ?? []));
toast("档位路由已保存", "success");
}; };
const saveCredential = async (providerId: string): Promise<void> => { const saveCredential = async (providerId: string): Promise<void> => {
@@ -111,43 +117,53 @@ export function ProvidersSettings({
return; return;
} }
setSavingId(providerId); setSavingId(providerId);
const { data, error } = await api.PUT("/settings/providers", { try {
body: { credentials: [{ provider: providerId, api_key: apiKey }] }, const { data, error } = await api.PUT("/settings/providers", {
}); body: { credentials: [{ provider: providerId, api_key: apiKey }] },
setSavingId(null); });
if (error || !data) { if (error || !data) {
toast("保存凭据失败,请重试", "error"); toast("保存凭据失败,请重试", "error");
return; return;
}
setProviders(data.providers ?? []);
setDrafts((prev) => ({ ...prev, [providerId]: "" }));
toast("凭据已保存", "success");
} catch {
toast("保存凭据失败,请检查后端连接", "error");
} finally {
setSavingId(null);
} }
setProviders(data.providers ?? []);
setDrafts((prev) => ({ ...prev, [providerId]: "" }));
toast("凭据已保存", "success");
}; };
const testConnection = async (providerId: string): Promise<void> => { const testConnection = async (providerId: string): Promise<void> => {
setTestingId(providerId); setTestingId(providerId);
const { data, error } = await api.POST("/settings/providers/test", { try {
body: { provider: providerId }, const { data, error } = await api.POST("/settings/providers/test", {
}); body: { provider: providerId },
setTestingId(null); });
if (error || !data) { if (error || !data) {
toast("测试连接失败", "error"); toast("测试连接失败", "error");
return; return;
}
setResults((prev) => ({
...prev,
[providerId]: { ok: data.ok, capabilities: data.capabilities },
}));
toast(
data.ok ? "连接成功" : "连接未通过",
data.ok ? "success" : "error",
);
} catch {
toast("测试连接失败,请检查后端连接", "error");
} finally {
setTestingId(null);
} }
setResults((prev) => ({
...prev,
[providerId]: { ok: data.ok, capabilities: data.capabilities },
}));
toast(data.ok ? "连接成功" : "连接未通过", data.ok ? "success" : "error");
}; };
return ( return (
<div className="grid gap-6 lg:grid-cols-[12rem_1fr]"> <div className="grid gap-6 lg:grid-cols-[12rem_1fr]">
<aside className="hidden lg:block"> <aside className="hidden lg:block">
<nav <nav aria-label="设置分组" className={cardClass("sticky top-4 p-2")}>
aria-label="设置分组"
className="sticky top-4 rounded border border-line bg-panel p-2"
>
<SettingsNavButton <SettingsNavButton
active={activeSection === "routing"} active={activeSection === "routing"}
icon={<Route className="h-4 w-4" aria-hidden="true" />} icon={<Route className="h-4 w-4" aria-hidden="true" />}

View File

@@ -46,19 +46,19 @@ export function SkillsPage({ project, skills }: SkillsPageProps) {
description="按来源、档位和表权限汇总当前可用技能。" description="按来源、档位和表权限汇总当前可用技能。"
/> />
<dl className="mt-4 grid grid-cols-3 gap-2 text-center"> <dl className="mt-4 grid grid-cols-3 gap-2 text-center">
<div className="rounded border border-line bg-paper p-3"> <div className="rounded border border-line bg-bg p-3">
<dt className="text-xs text-ink-soft"></dt> <dt className="text-xs text-ink-soft"></dt>
<dd className="mt-1 font-serif text-2xl text-ink"> <dd className="mt-1 font-serif text-2xl text-ink">
{summary.total} {summary.total}
</dd> </dd>
</div> </div>
<div className="rounded border border-line bg-paper p-3"> <div className="rounded border border-line bg-bg p-3">
<dt className="text-xs text-ink-soft"></dt> <dt className="text-xs text-ink-soft"></dt>
<dd className="mt-1 font-serif text-2xl text-ink"> <dd className="mt-1 font-serif text-2xl text-ink">
{summary.writableCount} {summary.writableCount}
</dd> </dd>
</div> </div>
<div className="rounded border border-line bg-paper p-3"> <div className="rounded border border-line bg-bg p-3">
<dt className="text-xs text-ink-soft"></dt> <dt className="text-xs text-ink-soft"></dt>
<dd className="mt-1 font-serif text-2xl text-ink"> <dd className="mt-1 font-serif text-2xl text-ink">
{summary.readonlyCount} {summary.readonlyCount}

View File

@@ -27,8 +27,8 @@ export function StylePage({ project, initialFingerprint }: StylePageProps) {
projectId={project.id} projectId={project.id}
activeNav="style" activeNav="style"
> >
<div className="grid h-[calc(100vh-var(--chrome,4rem))] grid-cols-1 lg:grid-cols-[28rem_1fr]"> <div className="flex min-h-[calc(100vh-var(--chrome,4rem))] flex-col lg:grid lg:h-[calc(100vh-var(--chrome,4rem))] lg:min-h-0 lg:grid-cols-[28rem_1fr]">
<section className="flex flex-col overflow-auto border-r border-line bg-panel px-6 py-6"> <section className="flex flex-col border-line bg-panel px-6 py-6 lg:overflow-auto lg:border-r">
<h1 className="mb-3 font-serif text-lg text-ink"></h1> <h1 className="mb-3 font-serif text-lg text-ink"></h1>
<StyleUpload <StyleUpload
busy={learn.busy} busy={learn.busy}
@@ -39,7 +39,7 @@ export function StylePage({ project, initialFingerprint }: StylePageProps) {
/> />
</section> </section>
<section className="overflow-auto bg-bg px-6 py-6"> <section className="bg-bg px-6 py-6 lg:overflow-auto">
<FingerprintView fingerprint={learn.fingerprint} /> <FingerprintView fingerprint={learn.fingerprint} />
</section> </section>
</div> </div>