后端生成闸+多项修复;前端全站更新;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:
zyc
2026-06-15 10:06:15 +08:00
co-authored by Claude Fable 5
parent 890cb9ab67
commit 216a711291
92 changed files with 5114 additions and 2027 deletions
+17 -3
View File
@@ -1,9 +1,12 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { CircleDollarSign, KeyRound, UserPlus } from "lucide-react";
import type { BillingSummary, Notification, Team, TeamMember, User } from "../types";
import type { Page } from "./route-config";
import { money } from "./stage-config";
import { ConfirmModal, TeamModal } from "../components/overlays";
import { Pager } from "../components/pager";
const MEMBERS_PER_PAGE = 10;
// 角色 → pill key/label(对齐 api-bridge roleUi)
function roleUi(role: string): { key: "super" | "admin" | "member"; label: string } {
@@ -84,6 +87,12 @@ export function TeamPage({ team, user, members, billing, notifications = [], nav
const email = member.user.email || "";
return !needle || `${name} ${email}`.toLowerCase().includes(needle);
});
// 成员分页:每页 10 人,搜索变化回第 1 页
const [memberPage, setMemberPage] = useState(1);
useEffect(() => { setMemberPage(1); }, [search]);
const memberTotalPages = Math.max(1, Math.ceil(list.length / MEMBERS_PER_PAGE));
const memberCurPage = Math.min(memberPage, memberTotalPages);
const pagedMembers = list.slice((memberCurPage - 1) * MEMBERS_PER_PAGE, memberCurPage * MEMBERS_PER_PAGE);
function openEdit(member: TeamMember) {
setEditTarget(member);
@@ -93,13 +102,15 @@ export function TeamPage({ team, user, members, billing, notifications = [], nav
async function submitCreate() {
if (!cuUser.trim() || cuPass.length < 8) return;
await onCreateMember({
const created = await onCreateMember({
username: cuUser.trim(),
password: cuPass,
name: cuName.trim() || undefined,
role: cuRole,
monthly_credit_limit: Number(cuMonthly) || 0
});
// 创建失败(action 报错返回 null)时保持弹窗与已填内容,只成功才关闭并清空
if (!created) return;
setModal("");
setCuUser("");
setCuPass("");
@@ -241,6 +252,7 @@ export function TeamPage({ team, user, members, billing, notifications = [], nav
<span className="spacer"></span>
<input className="input" id="member-search" placeholder="搜索姓名 / 手机号" style={{ height: "32px", fontSize: "12px", width: "220px" }} value={search} onChange={(event) => setSearch(event.target.value)} />
</div>
<div className="members-table-wrap">
<table className="t members-table" style={{ border: 0, borderRadius: 0 }}>
<thead>
<tr>
@@ -253,7 +265,7 @@ export function TeamPage({ team, user, members, billing, notifications = [], nav
</tr>
</thead>
<tbody id="members-tbody">
{list.map((member) => {
{pagedMembers.map((member) => {
const rawName = (member.user.username || "").trim();
const email = (member.user.email || "").trim();
// 用户名是邮箱时取 @ 前作为显示名,完整邮箱作副行,避免名字与邮箱重复显示
@@ -287,6 +299,8 @@ export function TeamPage({ team, user, members, billing, notifications = [], nav
})}
</tbody>
</table>
</div>
<Pager page={memberCurPage} total={list.length} pageSize={MEMBERS_PER_PAGE} onChange={setMemberPage} />
</div>
</div>