解决账户中心的问题
This commit is contained in:
@@ -60,6 +60,11 @@ function FeatureIcon({ name }: { name: string }) {
|
||||
}
|
||||
|
||||
function featureIconForLedger(ledger: Ledger): string {
|
||||
const tt = (ledger.task_type || "").toLowerCase();
|
||||
if (tt.includes("image") || tt.includes("triview") || tt.includes("product_image") || tt.includes("person_image") || tt.includes("scene_image")) return "image";
|
||||
if (tt.includes("digest") || tt.includes("video_digest")) return "scan";
|
||||
if (tt.includes("free_video") || tt.includes("film") || tt.includes("video_segment") || tt.includes("voiceover") || tt.includes("export")) return "film";
|
||||
if (tt.includes("storyboard")) return "clapperboard";
|
||||
const text = `${ledger.ledger_type} ${ledger.reason}`.toLowerCase();
|
||||
if (text.includes("image") || text.includes("图片")) return "image";
|
||||
if (text.includes("digest") || text.includes("提示词") || text.includes("提炼")) return "scan";
|
||||
@@ -69,6 +74,17 @@ function featureIconForLedger(ledger: Ledger): string {
|
||||
return "clapperboard";
|
||||
}
|
||||
|
||||
function ledgerFeatureLabel(ledger: Ledger): string {
|
||||
if (ledger.task_type) return taskTypeLabel(ledger.task_type);
|
||||
return ledgerTypeLabel(ledger.ledger_type);
|
||||
}
|
||||
|
||||
function ledgerLocationLabel(ledger: Ledger): string {
|
||||
if (ledger.project_title) return ledger.project_title;
|
||||
if (ledger.ledger_type === "recharge") return "账户充值";
|
||||
return ledgerReasonLabel(ledger.reason);
|
||||
}
|
||||
|
||||
function formatMonthLabel(ym: string) {
|
||||
const [y, m] = ym.split("-");
|
||||
return `${y}年${Number(m)}月`;
|
||||
@@ -183,13 +199,14 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
setLedgersLoading(true);
|
||||
api.ledgers(billPage, BILLS_PER_PAGE, {
|
||||
ledger_type: billType === "all" ? undefined : billType,
|
||||
month: ledgerMonth || undefined,
|
||||
}).then((data) => {
|
||||
if (!alive) return;
|
||||
setLedgerRows(data.results);
|
||||
setLedgerCount(data.count);
|
||||
}).catch(() => {}).finally(() => { if (alive) setLedgersLoading(false); });
|
||||
return () => { alive = false; };
|
||||
}, [billPage, billType, reloadFlag]);
|
||||
}, [billPage, billType, ledgerMonth, reloadFlag]);
|
||||
|
||||
const [billJump, setBillJump] = useState("");
|
||||
|
||||
@@ -253,10 +270,8 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
setBillPage(1);
|
||||
}, [featureFilter]);
|
||||
|
||||
const visibleLedgers = useMemo(() => {
|
||||
if (!ledgerMonth) return ledgerRows;
|
||||
return ledgerRows.filter((l) => (l.created_at || "").startsWith(ledgerMonth));
|
||||
}, [ledgerRows, ledgerMonth]);
|
||||
// 月份/类型已在服务端过滤,当前页结果直接展示
|
||||
const visibleLedgers = ledgerRows;
|
||||
|
||||
const billTotalPages = Math.max(1, Math.ceil(ledgerCount / BILLS_PER_PAGE));
|
||||
const safeBillPage = Math.min(billPage, billTotalPages);
|
||||
@@ -266,20 +281,40 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
setBillJump("");
|
||||
}
|
||||
|
||||
function exportLedgers() {
|
||||
if (visibleLedgers.length === 0) {
|
||||
async function exportLedgers() {
|
||||
const pageSize = 100;
|
||||
const all: Ledger[] = [];
|
||||
let page = 1;
|
||||
let total = Infinity;
|
||||
try {
|
||||
while (all.length < total) {
|
||||
const data = await api.ledgers(page, pageSize, {
|
||||
ledger_type: billType === "all" ? undefined : billType,
|
||||
month: ledgerMonth || undefined,
|
||||
});
|
||||
total = data.count;
|
||||
all.push(...data.results);
|
||||
if (data.results.length === 0) break;
|
||||
page += 1;
|
||||
if (page > 500) break; // 安全上限
|
||||
}
|
||||
} catch {
|
||||
onNotify("error", "导出失败,请稍后重试");
|
||||
return;
|
||||
}
|
||||
if (all.length === 0) {
|
||||
onNotify("info", "当前没有可导出的流水");
|
||||
return;
|
||||
}
|
||||
const header = ["功能", "消耗位置", "成员", "时间", "消耗积分"];
|
||||
const lines = visibleLedgers.map((l) => {
|
||||
const lines = all.map((l) => {
|
||||
const amt = Number(l.amount);
|
||||
const signed = (amt > 0 && l.ledger_type === "recharge") || l.ledger_type === "release" || l.ledger_type === "refund"
|
||||
? `+${pts(Math.abs(amt))}`
|
||||
: `-${pts(Math.abs(amt))}`;
|
||||
return [
|
||||
ledgerTypeLabel(l.ledger_type),
|
||||
ledgerReasonLabel(l.reason).replace(/,/g, ","),
|
||||
ledgerFeatureLabel(l),
|
||||
ledgerLocationLabel(l).replace(/,/g, ","),
|
||||
l.user_label || "系统",
|
||||
formatLedgerTime(l.created_at),
|
||||
signed,
|
||||
@@ -292,7 +327,7 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
a.download = `积分消耗流水-${ledgerMonth || "all"}.csv`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
onNotify("success", "已导出积分消耗流水");
|
||||
onNotify("success", `已导出 ${all.length} 条积分消耗流水`);
|
||||
}
|
||||
|
||||
const monthUsageDisplay = trend?.month_charged != null ? Number(trend.month_charged) : (selectedMonth === months[0]?.value ? used : null);
|
||||
@@ -456,7 +491,7 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
onChange={(v) => { setLedgerMonth(v); setBillPage(1); }}
|
||||
options={months.map((m) => ({ value: m.value, label: m.label }))}
|
||||
/>
|
||||
<button className="compact-action billing-ledger-export" type="button" onClick={exportLedgers}>
|
||||
<button className="compact-action billing-ledger-export" type="button" onClick={() => void exportLedgers()}>
|
||||
<Download size={16} strokeWidth={2} />
|
||||
导出流水
|
||||
</button>
|
||||
@@ -491,11 +526,11 @@ export function AccountPage({ billing, projects: _projects, team, onRecharge: _o
|
||||
<div className="billing-ledger-row" key={l.id} data-ledger-month={(l.created_at || "").slice(0, 7)} data-ledger-feature={l.ledger_type}>
|
||||
<div className="billing-ledger-feature">
|
||||
<span className="billing-ledger-icon"><FeatureIcon name={featureIconForLedger(l)} /></span>
|
||||
<strong>{ledgerTypeLabel(l.ledger_type)}</strong>
|
||||
<strong title={ledgerFeatureLabel(l)}>{ledgerFeatureLabel(l)}</strong>
|
||||
</div>
|
||||
<div className="billing-ledger-copy">
|
||||
<strong title={ledgerReasonLabel(l.reason)}>{ledgerReasonLabel(l.reason)}</strong>
|
||||
<span>{l.ledger_type === "recharge" ? "账户充值" : "积分变动 · 可追溯流水"}</span>
|
||||
<strong title={ledgerLocationLabel(l)}>{ledgerLocationLabel(l)}</strong>
|
||||
<span>{ledgerTypeLabel(l.ledger_type)}{l.reason ? ` · ${ledgerReasonLabel(l.reason)}` : ""}</span>
|
||||
</div>
|
||||
<div className="billing-ledger-meta">
|
||||
<strong>{l.user_label || "系统"}</strong>
|
||||
|
||||
Reference in New Issue
Block a user