style(web): 用户工作台 - 主操作改描边并统一弹窗浮层样式

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ddCat
2026-09-09 10:34:14 +08:00
parent fbfb233014
commit 427bae27ed
6 changed files with 137 additions and 62 deletions

View File

@@ -3,7 +3,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { keepPreviousData, useQuery, useQueryClient } from "@tanstack/react-query";
import { App, Button, Drawer, Dropdown, Form, Input, Modal, Popconfirm, Progress, Select, Space, Tag, Typography } from "antd";
import type { MenuProps } from "antd";
import { IconButton } from "@/components/ui/base/buttons";
import { useNavigate } from "react-router";
import { CollectionGrid, ListToolbar, PageHeader, PaginationBar, WorkspacePage } from "@/components/layout/workspace-page";
@@ -551,7 +550,7 @@ export default function AssetsPage() {
</>
) : (
<>
<Button className="library-primary-action" type="primary" icon={<Plus className="size-3.5" />} onClick={openCreate}>
<Button icon={<Plus className="size-3.5" />} onClick={openCreate}>
新增素材
</Button>
<Button icon={<Images className="size-3.5" />} onClick={() => setBatchUploadOpen(true)}>
@@ -560,7 +559,7 @@ export default function AssetsPage() {
<Button icon={<FolderOpen className="size-3.5" />} onClick={() => navigate("/plugins/eagle")}>
Eagle 素材库
</Button>
<IconButton variant="solid" icon={Download} aria-label="导出全部素材" title="导出全部素材" onClick={() => void exportAllAssets()} />
<Button aria-label="导出全部素材" title="导出全部素材" icon={<Download className="size-3.5" />} onClick={() => void exportAllAssets()} />
<Dropdown
trigger={["click"]}
menu={{
@@ -570,7 +569,7 @@ export default function AssetsPage() {
],
}}
>
<IconButton variant="solid" icon={FileUp} aria-label="导入素材" title="导入素材" />
<Button aria-label="导入素材" title="导入素材" icon={<FileUp className="size-3.5" />} />
</Dropdown>
</>
)}

View File

@@ -613,7 +613,7 @@ export default function ProjectChaptersView({ detail, refreshProject }: ProjectD
</section>
<CreateChapterModal open={createOpen} onClose={() => setCreateOpen(false)} loading={createMutation.isPending} onSubmit={(values) => createMutation.mutate(values)} />
<ImportNovelModal open={importOpen} loading={importMutation.isPending} onClose={() => setImportOpen(false)} onImport={(chapters) => importMutation.mutate(chapters)} />
<Modal title="提取章节角色、场景与道具" open={characterExtractOpen} width={500} okText="开始提取" cancelText="取消" okButtonProps={{ disabled: !selectedTextModel }} onCancel={() => setCharacterExtractOpen(false)} onOk={() => void extractCharacters()} styles={{ body: { paddingTop: 12 } }}>
<Modal className="library-modal" title="提取章节角色、场景与道具" open={characterExtractOpen} width={500} okText="开始提取" cancelText="取消" okButtonProps={{ disabled: !selectedTextModel }} onCancel={() => setCharacterExtractOpen(false)} onOk={() => void extractCharacters()} styles={{ body: { paddingTop: 12 } }}>
<div className="grid gap-4">
<div className="rounded-lg border border-border/70 bg-foreground/[.018] px-3 py-2.5">
<div className="text-[var(--fs-tiny)] text-foreground/42">当前章节</div>
@@ -626,7 +626,7 @@ export default function ProjectChaptersView({ detail, refreshProject }: ProjectD
</label>
</div>
</Modal>
<Modal title="生成章节分镜" open={storyboardOpen} width={560} okText={storyboardImpact.shotCount ? "重新生成分镜" : "生成分镜"} cancelText="取消" okButtonProps={{ disabled: !selectedTextModel }} onCancel={() => setStoryboardOpen(false)} onOk={() => void createStoryboard()} styles={{ body: { paddingTop: 12 } }}>
<Modal className="library-modal" title="生成章节分镜" open={storyboardOpen} width={560} okText={storyboardImpact.shotCount ? "重新生成分镜" : "生成分镜"} cancelText="取消" okButtonProps={{ disabled: !selectedTextModel }} onCancel={() => setStoryboardOpen(false)} onOk={() => void createStoryboard()} styles={{ body: { paddingTop: 12 } }}>
<div className="grid gap-4">
<div className="rounded-lg border border-border/70 bg-foreground/[.018] px-3 py-2.5">
<div className="text-[var(--fs-tiny)] text-foreground/42">当前章节</div>
@@ -647,7 +647,7 @@ export default function ProjectChaptersView({ detail, refreshProject }: ProjectD
</div>
</div>
</Modal>
<Modal title="移动章节" open={Boolean(moveTargetId)} width={400} okText="移动" cancelText="取消" okButtonProps={{ disabled: !movePosition || movePosition < 1 || movePosition > orderedUnits.length, loading: reorderMutation.isPending }} onCancel={() => { setMoveTargetId(""); setMovePosition(null); }} onOk={moveChapterToPosition} styles={{ body: { paddingTop: 12 } }}>
<Modal className="library-modal" title="移动章节" open={Boolean(moveTargetId)} width={400} okText="移动" cancelText="取消" okButtonProps={{ disabled: !movePosition || movePosition < 1 || movePosition > orderedUnits.length, loading: reorderMutation.isPending }} onCancel={() => { setMoveTargetId(""); setMovePosition(null); }} onOk={moveChapterToPosition} styles={{ body: { paddingTop: 12 } }}>
<div className="text-xs leading-5 text-foreground/50">输入目标章节位置。适合上千章项目的长距离调整,移动后其他章节会自动顺延。</div>
<label className="mt-3 flex items-center gap-2 text-sm"><span className="shrink-0">移动到第</span><InputNumber min={1} max={orderedUnits.length} precision={0} value={movePosition} onChange={setMovePosition} className="min-w-0 flex-1" /><span className="shrink-0">章</span></label>
</Modal>
@@ -736,7 +736,7 @@ function ToolbarDivider() {
}
function CreateChapterModal({ open, onClose, loading, onSubmit }: { open: boolean; onClose: () => void; loading: boolean; onSubmit: (values: { title: string; sourceText?: string }) => void }) {
return <Modal title="添加章节" open={open} footer={null} destroyOnHidden onCancel={onClose} width={480} styles={{ body: { paddingTop: 12 } }}><Form layout="vertical" onFinish={onSubmit}><Form.Item name="title" label="章节标题" rules={[{ required: true, whitespace: true, message: "请输入章节标题" }]}><Input autoFocus placeholder="例如:雨夜归城" /></Form.Item><Form.Item name="sourceText" label="正文(可选)"><Input.TextArea rows={4} placeholder="创建后仍可继续编辑和排版" /></Form.Item><div className="flex justify-end gap-2"><Button onClick={onClose}>取消</Button><Button type="primary" htmlType="submit" loading={loading}>创建章节</Button></div></Form></Modal>;
return <Modal className="library-modal" title="添加章节" open={open} footer={null} destroyOnHidden onCancel={onClose} width={480} styles={{ body: { paddingTop: 12 } }}><Form layout="vertical" onFinish={onSubmit}><Form.Item name="title" label="章节标题" rules={[{ required: true, whitespace: true, message: "请输入章节标题" }]}><Input autoFocus placeholder="例如:雨夜归城" /></Form.Item><Form.Item name="sourceText" label="正文(可选)"><Input.TextArea rows={4} placeholder="创建后仍可继续编辑和排版" /></Form.Item><div className="flex justify-end gap-2"><Button onClick={onClose}>取消</Button><Button type="primary" htmlType="submit" loading={loading}>创建章节</Button></div></Form></Modal>;
}
function ImportNovelModal({ open, loading, onClose, onImport }: { open: boolean; loading: boolean; onClose: () => void; onImport: (chapters: Array<{ title: string; plainText: string }>) => void }) {

View File

@@ -2,7 +2,7 @@ import { App, Button, Input, InputNumber } from "antd";
import { Select } from "@/components/ui/base/select";
import { SettingsRow } from "@/components/ui/product/settings-row";
import { ArrowLeft, Boxes, Bug, Cloud, MessageSquareText, MonitorUp, RadioTower, SlidersHorizontal, SquareTerminal, Workflow } from "lucide-react";
import { useEffect, useMemo, useState, type ReactNode } from "react";
import { useEffect, useLayoutEffect, useMemo, useState, type ReactNode } from "react";
import { useNavigate, useSearchParams } from "react-router";
import { UserOSSSettingsForm } from "@/components/layout/user-oss-settings-form";
@@ -65,6 +65,11 @@ export default function SettingsPage() {
const isVisibleConfigSection = (value: string | null): value is ConfigSectionKey => isConfigSection(value) && visibleConfigSections.some((section) => section.key === value);
useLayoutEffect(() => {
document.body.classList.add("app-user-overlays");
return () => document.body.classList.remove("app-user-overlays");
}, []);
useEffect(() => {
if (isVisibleConfigSection(requestedSection)) {
setActiveTab(requestedSection);
@@ -212,7 +217,7 @@ export default function SettingsPage() {
};
return (
<main className="settings-page app-workspace-page flex h-full min-h-0 flex-col text-foreground">
<main className="settings-page app-workspace-page app-user-workspace flex h-full min-h-0 flex-col text-foreground">
<header className="settings-topbar shrink-0">
<div className="flex min-w-0 items-center gap-2.5">
{shouldPromptContinue ? (