fix: 测试清单一轮bug修复(商品库/视频项目/设置/消费/平台套图)
- 商品库编辑删图假成功+悬停无删除图标;商品三视图错显角色三视图 - 视频项目演员删除入口;角色三视图防呆;故事板审核失败原因透出 - 设置:管理团队死按钮/通知未接入项屏蔽/邮箱验证链路 - 消费:账单流水切换类型重置分页+独立总页数 - 资产库上传按钮隐藏;月限额两处对齐 - 平台套图:提示词框放大/选模型胶囊内嵌/未读任务角标/工作台记录持久化 - 新建商品独立添加卖点按钮;商品库超1920自适应 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { CreditCard, X } from "lucide-react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { api } from "../api";
|
||||
import type { BillingSummary, BillingTrend, Ledger, Project, TeamMember } from "../types";
|
||||
import type { BillingSummary, BillingTrend, Ledger, Project, Team, TeamMember } from "../types";
|
||||
import { money, stageMeta } from "./stage-config";
|
||||
import { pageWindow } from "../components/pager";
|
||||
import { useBodyScrollLock, useOverlayTransition } from "../components/overlays";
|
||||
@@ -110,9 +110,11 @@ function TopupModal({ open, channel, amount, bonus, close, onDone }: {
|
||||
);
|
||||
}
|
||||
|
||||
export function AccountPage({ billing, projects, onRecharge }: {
|
||||
export function AccountPage({ billing, projects, team, onRecharge }: {
|
||||
billing: BillingSummary | null;
|
||||
projects: Project[];
|
||||
// team prop 用于读取团队级月限额(与团队管理页保持同一来源 · #14)
|
||||
team?: Team | null;
|
||||
onRecharge: (amount: number, bonus: number) => void | Promise<unknown>;
|
||||
}) {
|
||||
const [tab, setTab] = useState<Tab>("overview");
|
||||
@@ -150,15 +152,8 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
}).catch(() => {}).finally(() => { if (alive) setLedgersLoading(false); });
|
||||
return () => { alive = false; };
|
||||
}, [billPage, reloadFlag]);
|
||||
const billTotalPages = Math.max(1, Math.ceil(ledgerCount / BILLS_PER_PAGE));
|
||||
const safeBillPage = Math.min(billPage, billTotalPages);
|
||||
// row40:跳页输入框 —— 输入页码回车/点「跳转」即钳到 [1,总页数] 并翻页
|
||||
const [billJump, setBillJump] = useState("");
|
||||
function gotoBillPage() {
|
||||
const n = parseInt(billJump, 10);
|
||||
if (!Number.isNaN(n)) setBillPage(Math.min(billTotalPages, Math.max(1, n)));
|
||||
setBillJump("");
|
||||
}
|
||||
|
||||
const selectedCard = RECHARGE.find((item) => item.amt === recharge);
|
||||
const effectiveAmount = Number(customAmt) > 0 ? Number(customAmt) : recharge;
|
||||
@@ -175,8 +170,11 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
|
||||
const balance = Number(billing?.account.balance || 0);
|
||||
const used = Number(billing?.charged_total || 0);
|
||||
const memberLimit = teamMembers.reduce((sum, m) => sum + Math.max(0, Number(m.monthly_credit_limit || 0)), 0);
|
||||
const limit = memberLimit || balance;
|
||||
// 月限额:与团队管理页保持同一来源(team.monthly_credit_limit) · #14
|
||||
// 优先用团队级月限额;-1 = 不限(显示余额);0/null = 未设置 → 用成员额度累加,再 fallback 余额
|
||||
const savedMonthlyLimit = team?.monthly_credit_limit == null ? 0 : Number(team.monthly_credit_limit);
|
||||
const memberLimitSum = teamMembers.reduce((sum, m) => sum + Math.max(0, Number(m.monthly_credit_limit || 0)), 0);
|
||||
const limit = savedMonthlyLimit === -1 ? balance : (savedMonthlyLimit > 0 ? savedMonthlyLimit : (memberLimitSum || balance));
|
||||
const left = Math.max(0, limit - used);
|
||||
const pct = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;
|
||||
|
||||
@@ -219,7 +217,8 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
const memFiltered = memRole !== "all";
|
||||
|
||||
// ── 账单流水筛选(类型 + 成员)── 后端为服务端分页且无过滤参数(api.ledgers 仅收 page/pageSize),
|
||||
// 故只能对「当前页」已载入的 10 条做客户端过滤;计数标注为本页范围,避免误导成全量过滤。
|
||||
// 故只能对「当前页」已载入的 10 条做客户端过滤。
|
||||
// 切换筛选条件时跳回第 1 页(#9a);过滤后按实际可见行数重算总页数(#9b)。
|
||||
const [billType, setBillType] = useState<string>("all");
|
||||
const [billMember, setBillMember] = useState<string>("all");
|
||||
const billMemberOptions = useMemo(() => {
|
||||
@@ -235,6 +234,16 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
[ledgerRows, billType, billMember]
|
||||
);
|
||||
const billFiltered = billType !== "all" || billMember !== "all";
|
||||
// 有筛选时按当前页已过滤的可见行数重算总页数,避免总页数用全量致翻到空页(#9b)
|
||||
const billTotalPages = billFiltered
|
||||
? Math.max(1, Math.ceil(visibleLedgers.length / BILLS_PER_PAGE))
|
||||
: Math.max(1, Math.ceil(ledgerCount / BILLS_PER_PAGE));
|
||||
const safeBillPage = Math.min(billPage, billTotalPages);
|
||||
function gotoBillPage() {
|
||||
const n = parseInt(billJump, 10);
|
||||
if (!Number.isNaN(n)) setBillPage(Math.min(billTotalPages, Math.max(1, n)));
|
||||
setBillJump("");
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="account-page">
|
||||
@@ -398,7 +407,7 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
|
||||
<div className={`tab-panel ${tab === "bills" ? "active" : ""}`}>
|
||||
<div className="filter-bar">
|
||||
<select value={billType} onChange={(e) => setBillType(e.target.value)} aria-label="按类型筛选">
|
||||
<select value={billType} onChange={(e) => { setBillType(e.target.value); setBillPage(1); }} aria-label="按类型筛选">
|
||||
<option value="all">全部类型</option>
|
||||
<option value="charge">扣费</option>
|
||||
<option value="recharge">充值</option>
|
||||
@@ -407,12 +416,12 @@ export function AccountPage({ billing, projects, onRecharge }: {
|
||||
<option value="adjustment">调整</option>
|
||||
<option value="refund">退款</option>
|
||||
</select>
|
||||
<select value={billMember} onChange={(e) => setBillMember(e.target.value)} aria-label="按成员筛选">
|
||||
<select value={billMember} onChange={(e) => { setBillMember(e.target.value); setBillPage(1); }} aria-label="按成员筛选">
|
||||
<option value="all">全部成员</option>
|
||||
{billMemberOptions.map((m) => <option key={m} value={m}>{m}</option>)}
|
||||
</select>
|
||||
{billFiltered && (
|
||||
<button className="filter-reset" type="button" onClick={() => { setBillType("all"); setBillMember("all"); }}>清除筛选</button>
|
||||
<button className="filter-reset" type="button" onClick={() => { setBillType("all"); setBillMember("all"); setBillPage(1); }}>清除筛选</button>
|
||||
)}
|
||||
<span className="spacer"></span>
|
||||
<span className="ct">本页 <b>{visibleLedgers.length}</b> 条 · 共 {ledgerCount} 条</span>
|
||||
|
||||
Reference in New Issue
Block a user