feat(*): 创作体验 - 支持资源技能引用并统一生成反馈与画布列表展示

This commit is contained in:
ddCat
2026-08-02 22:29:44 +08:00
parent e404d4f716
commit 0253072232
20 changed files with 589 additions and 130 deletions

View File

@@ -11,6 +11,7 @@ import { WorkspaceSignalIcon } from "@/components/ui/aceternity/workspace-signal
import { CometCard } from "@/components/ui/aceternity/comet-card";
import { aceternityMotion } from "@/lib/aceternity-motion";
import { checkinCredits, getWallet, redeemCredits, type CreditLedgerEntry, type WalletSummary } from "@/services/api/wallet";
import { modelDisplayName, useEffectiveConfig, type AiConfig } from "@/stores/use-config-store";
type LedgerFilter = "all" | "income" | "consume" | "refund";
@@ -25,6 +26,7 @@ export default function WalletPage() {
const { message } = App.useApp();
const screens = Grid.useBreakpoint();
const reducedMotion = useReducedMotion();
const config = useEffectiveConfig();
const [wallet, setWallet] = useState<WalletSummary | null>(null);
const [code, setCode] = useState("");
const [filter, setFilter] = useState<LedgerFilter>("all");
@@ -99,8 +101,8 @@ export default function WalletPage() {
width: 400,
ellipsis: true,
render: (_, entry) => (
<div className="min-w-0 max-w-full overflow-hidden" title={[entry.model || ledgerTitle(entry), [sceneLabel(entry.scene), entry.note].filter(Boolean).join(" · ")].filter(Boolean).join("\n")}>
<div className="truncate font-medium">{entry.model || ledgerTitle(entry)}</div>
<div className="min-w-0 max-w-full overflow-hidden" title={[ledgerModelName(config, entry), [sceneLabel(entry.scene), entry.note].filter(Boolean).join(" · ")].filter(Boolean).join("\n")}>
<div className="truncate font-medium">{ledgerModelName(config, entry)}</div>
<div className="mt-1 truncate text-xs text-foreground/50">{[sceneLabel(entry.scene), entry.note].filter(Boolean).join(" · ") || "无补充说明"}</div>
</div>
),
@@ -206,7 +208,7 @@ export default function WalletPage() {
<Table className="app-data-table wallet-ledger-table" rowKey="id" size="middle" loading={loading} columns={columns} dataSource={entries} pagination={false} tableLayout="fixed" scroll={{ x: 990 }} />
</TableSurface>
) : (
<div className="overflow-hidden rounded-md border border-border/70 bg-background">{entries.length ? entries.map((entry) => <LedgerMobileRow key={entry.id} entry={entry} />) : <WorkspaceState compact icon="wallet" title="没有匹配的积分记录" description="切换流水类型,或完成一次生成后再回来查看。" />}</div>
<div className="overflow-hidden rounded-md border border-border/70 bg-background">{entries.length ? entries.map((entry) => <LedgerMobileRow key={entry.id} config={config} entry={entry} />) : <WorkspaceState compact icon="wallet" title="没有匹配的积分记录" description="切换流水类型,或完成一次生成后再回来查看。" />}</div>
)}
<PaginationBar
current={page}
@@ -237,7 +239,7 @@ function BalanceMetric({ label, description, value, icon }: { label: string; des
);
}
function LedgerMobileRow({ entry }: { entry: CreditLedgerEntry }) {
function LedgerMobileRow({ config, entry }: { config: AiConfig; entry: CreditLedgerEntry }) {
const meta = ledgerTypeMeta(entry.type);
return (
<article className="flex items-start gap-3 border-b border-border px-4 py-4 last:border-b-0">
@@ -245,7 +247,7 @@ function LedgerMobileRow({ entry }: { entry: CreditLedgerEntry }) {
<div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="truncate text-sm font-medium">{entry.model || ledgerTitle(entry)}</div>
<div className="truncate text-sm font-medium">{ledgerModelName(config, entry)}</div>
<div className="mt-1 text-xs text-foreground/45">{formatTime(entry.createdAt)}</div>
</div>
<CreditDelta value={entry.amountMicrocredits} />
@@ -298,6 +300,10 @@ function ledgerTitle(entry: CreditLedgerEntry) {
return entry.note || "积分调整";
}
function ledgerModelName(config: AiConfig, entry: CreditLedgerEntry) {
return entry.model ? modelDisplayName(config, entry.model) : ledgerTitle(entry);
}
function sceneLabel(scene?: string) {
const labels: Record<string, string> = { image: "图片生成", text: "文本生成", video: "视频生成", audio: "音频生成", storyboard: "分镜生成" };
return scene ? labels[scene] || "其他场景" : "";