后端生成闸+多项修复;前端全站更新;QA 审计与报告
后端: - 新增 celery_health 生成前置闸——无 worker 在线时图片/视频生成入口 一律 503,防"提交到 ARK 后无人轮询、结果悬空+额度冻结"的数据丢失 - 拼接导出:帧率跟随源众数、字幕逐句重映射、原声/BGM 混音修复 - 资金核算审计脚本 + genesis 账目回填 + 滞留预留清理命令 - 接入 yunqi provider 与豆包 TTS 模型(catalog/migrations/bootstrap 命令) 前端:全站页面更新(pipeline/library/products/projects/team/account 等), 新增共享 pager 分页组件 QA:刷新 function-audit 全量输出,新增 full-qa 报告 文档:BP 产品介绍资料、design/CLAUDE.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,32 @@
|
||||
import { useEffect, type ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Shield, X } from "lucide-react";
|
||||
import { Music, Shield, X } from "lucide-react";
|
||||
|
||||
// 通用媒体预览灯箱:点击图片放大 / 点击视频弹窗播放(复用 .np-lightbox 样式)
|
||||
// 浮层打开时锁住 body 滚动(否则滚轮会滚动遮罩后面的页面,体感"遮罩没盖住")。
|
||||
// 多浮层叠开用计数,最后一个关闭才解锁。
|
||||
let scrollLockCount = 0;
|
||||
export function useBodyScrollLock(active: boolean) {
|
||||
useEffect(() => {
|
||||
if (!active) return;
|
||||
scrollLockCount += 1;
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
scrollLockCount -= 1;
|
||||
if (scrollLockCount <= 0) { scrollLockCount = 0; document.body.style.overflow = ""; }
|
||||
};
|
||||
}, [active]);
|
||||
}
|
||||
|
||||
// 通用媒体预览灯箱:点击图片放大 / 点击视频弹窗播放 / 点击音频弹窗试听(复用 .np-lightbox 样式)
|
||||
// 背景点击 / Esc / 关闭键都可关闭;点媒体本身不关闭。
|
||||
export function MediaLightbox({ open, src, kind, name, close }: {
|
||||
open: boolean;
|
||||
src: string;
|
||||
kind?: "image" | "video";
|
||||
kind?: "image" | "video" | "audio";
|
||||
name?: string;
|
||||
close: () => void;
|
||||
}) {
|
||||
useBodyScrollLock(open && !!src);
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const onKey = (event: KeyboardEvent) => { if (event.key === "Escape") close(); };
|
||||
@@ -31,6 +47,11 @@ export function MediaLightbox({ open, src, kind, name, close }: {
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
style={{ maxWidth: "90vw", maxHeight: "88vh", borderRadius: "var(--r-md)", boxShadow: "0 20px 60px rgba(0,0,0,.5)", background: "#000", cursor: "default" }}
|
||||
/>
|
||||
) : kind === "audio" ? (
|
||||
<div className="lb-audio" onClick={(event) => event.stopPropagation()}>
|
||||
<Music aria-hidden="true" />
|
||||
<audio src={src} controls autoPlay />
|
||||
</div>
|
||||
) : (
|
||||
<img src={src} alt={name || "预览"} onClick={(event) => event.stopPropagation()} style={{ cursor: "default" }} />
|
||||
)}
|
||||
@@ -58,6 +79,7 @@ export function TeamModal({ open, title, subtitle, icon, close, children, footer
|
||||
children: ReactNode;
|
||||
footer?: ReactNode;
|
||||
}) {
|
||||
useBodyScrollLock(open);
|
||||
if (!open) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
return createPortal(
|
||||
@@ -81,6 +103,7 @@ export function ConfirmModal({ open, title, detail, confirmText, onCancel, onCon
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void | Promise<unknown>;
|
||||
}) {
|
||||
useBodyScrollLock(open);
|
||||
if (!open) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
return createPortal(
|
||||
@@ -97,6 +120,7 @@ export function ConfirmModal({ open, title, detail, confirmText, onCancel, onCon
|
||||
}
|
||||
|
||||
export function Drawer({ title, open, close, children }: { title: string; open: boolean; close: () => void; children: ReactNode }) {
|
||||
useBodyScrollLock(open);
|
||||
if (!open) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
return createPortal(
|
||||
|
||||
Reference in New Issue
Block a user