feat(core): notification inbox infinite scroll + command palette fix (+ pending WIP)
消息中心:全量渲染 → 真·后端分页滚动加载 - backend(ops/views): NotificationPagination(10/页,page_size 可覆盖)+ 响应回 type_counts(按收件人绝对计数,不受分页/搜索影响) - frontend(messages): 自管分页,滚到底加载下一批;tab/搜索走服务端并重置到第1页; 代号作废在途旧请求防切换卡空白;乐观标已读;「已加载 X / Y」分母用当前筛选总数 - api/App/types: listNotifications 支持 page/page_size/search;allNotifications 携带 type_counts 命令面板(侧边栏搜索):修复点开后 UI 错位 - app-shell: 遮罩 className 漏了基类 shell-command-bg(只有 .show)致无定位塌到左下; 补回基类 + header 类名对齐 .shell-command-h - messages-page.css: 工作台收进视口高度,收件箱在面板内滚动 本次提交一并带入此前若干未提交 WIP(account/ai-tools/library/pipeline/products/settings + accounts/ai/assets/billing/projects 后端),按用户要求整体推 dev。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import type { Asset, BillingSummary, ExportPoll, Product, Project, Team, Timelin
|
||||
import type { Notice, Page } from "./route-config";
|
||||
import { money, stageOrder, statusPill } from "./stage-config";
|
||||
import { CornerMarks, Decorations, Sidebar, ToastLike } from "../components/app-shell";
|
||||
import { MediaLightbox } from "../components/overlays";
|
||||
import { IconKitSvg } from "../components/IconKitSvg";
|
||||
|
||||
// 真实资产缩略图注入:与全站一致用 --mock-media-url(.placeholder.has-mock-media 负责 cover 裁切 + 8px 圆角)
|
||||
@@ -158,6 +159,8 @@ export function PipelinePage(props: {
|
||||
const activeDot = navigated ? viewStage : projectStage;
|
||||
const completed = Math.max(projectStage - 1, activeDot - 1);
|
||||
const [chatText, setChatText] = useState("");
|
||||
// 媒体预览灯箱(视频片段播放 / 故事板分镜放大)
|
||||
const [preview, setPreview] = useState<{ src: string; kind: "image" | "video"; name: string } | null>(null);
|
||||
const [chatMode, setChatMode] = useState<"ai" | "theme" | "manual">("ai");
|
||||
const [chatAttachments, setChatAttachments] = useState<Array<{ name: string; chars: number }>>([]);
|
||||
const chatTextareaRef = useRef<HTMLTextAreaElement | null>(null);
|
||||
@@ -796,7 +799,7 @@ export function PipelinePage(props: {
|
||||
{(() => {
|
||||
const url = frameUrl(sbActiveFrame);
|
||||
return (
|
||||
<div className={`placeholder sb-main-img${url ? " has-mock-media" : ""}`} id="sb-main-img" style={url ? mediaStyle(url) : undefined}>
|
||||
<div className={`placeholder sb-main-img${url ? " has-mock-media" : ""}`} id="sb-main-img" style={url ? { ...mediaStyle(url), cursor: "zoom-in" } : undefined} role={url ? "button" : undefined} tabIndex={url ? 0 : undefined} title={url ? "点击放大" : undefined} onClick={url ? () => setPreview({ src: url, kind: "image", name: `场 ${sbSelected + 1}` }) : undefined} onKeyDown={url ? (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setPreview({ src: url, kind: "image", name: `场 ${sbSelected + 1}` }); } } : undefined}>
|
||||
<span className="ph-frame">{sbActiveFrame ? `场 ${sbSelected + 1}` : "// 故事板未生成"}</span>
|
||||
</div>
|
||||
);
|
||||
@@ -899,7 +902,7 @@ export function PipelinePage(props: {
|
||||
const busy = ["running", "queued"].includes(seg.status);
|
||||
return (
|
||||
<div className="video-card" key={seg.id} data-video-id={seg.id}>
|
||||
<div className="placeholder video-thumb" style={{ position: "relative", overflow: "hidden" }}>
|
||||
<div className="placeholder video-thumb" style={{ position: "relative", overflow: "hidden" }} role={url ? "button" : undefined} tabIndex={url ? 0 : undefined} title={url ? "点击播放" : undefined} onClick={url ? () => setPreview({ src: url, kind: "video", name: `场 ${seg.sort_order + 1}` }) : undefined} onKeyDown={url ? (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setPreview({ src: url, kind: "video", name: `场 ${seg.sort_order + 1}` }); } } : undefined}>
|
||||
{url
|
||||
? <video src={url} muted playsInline preload="metadata" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", borderRadius: "inherit" }} />
|
||||
: <span className="ph-frame">场 {seg.sort_order + 1}</span>}
|
||||
@@ -1199,6 +1202,7 @@ export function PipelinePage(props: {
|
||||
})()}
|
||||
</div>
|
||||
</main>
|
||||
<MediaLightbox open={!!preview} src={preview?.src || ""} kind={preview?.kind} name={preview?.name} close={() => setPreview(null)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user