管理后台生成记录创作记录修复操作显示BUG

This commit is contained in:
2026-06-05 10:18:30 +08:00
parent fd9a259917
commit 737d6ee1b6
9 changed files with 904 additions and 261 deletions
@@ -3,7 +3,7 @@ import {
Button, Card, Select, Space, Table, Tag, Typography, message,
} from 'antd';
import {
WalletOutlined, ArrowUpOutlined, ArrowDownOutlined, ReloadOutlined,
WalletOutlined, ArrowUpOutlined, ArrowDownOutlined, ReloadOutlined, RollbackOutlined,
} from '@ant-design/icons';
import { getCreditRecords } from '../api';
import { formatDate } from '../utils/formatDate';
@@ -18,6 +18,12 @@ interface CreditRecord {
createdAt: string;
}
const CREDIT_TYPE_MAP: Record<string, { text: string; color: string; icon: React.ReactNode }> = {
recharge: { text: '充值', color: 'green', icon: <ArrowUpOutlined /> },
consume: { text: '消费', color: 'red', icon: <ArrowDownOutlined /> },
refund: { text: '退回', color: 'blue', icon: <RollbackOutlined /> },
};
const AdminCreditRecords: React.FC = () => {
const [records, setRecords] = useState<CreditRecord[]>([]);
const [total, setTotal] = useState(0);
@@ -54,11 +60,14 @@ const AdminCreditRecords: React.FC = () => {
},
{
title: '类型', dataIndex: 'type', width: 100,
render: (v: string) => (
<Tag color={v === 'recharge' ? 'green' : 'red'} icon={v === 'recharge' ? <ArrowUpOutlined /> : <ArrowDownOutlined />}>
{v === 'recharge' ? '充值' : '消费'}
</Tag>
),
render: (v: string) => {
const cfg = CREDIT_TYPE_MAP[v] || { text: v || '-', color: 'default', icon: null };
return (
<Tag color={cfg.color} icon={cfg.icon}>
{cfg.text}
</Tag>
);
},
},
{
title: '变动积分', dataIndex: 'amount', width: 120, sorter: (a: CreditRecord, b: CreditRecord) => a.amount - b.amount,
@@ -140,6 +149,7 @@ const AdminCreditRecords: React.FC = () => {
{ value: '', label: '全部类型' },
{ value: 'recharge', label: '充值' },
{ value: 'consume', label: '消费' },
{ value: 'refund', label: '退回' },
]}
/>
</Space>
@@ -29,7 +29,6 @@ import type { GenerationAIMediaReference, GenerationAITaskOut } from '../types';
import { formatDate } from '../utils/formatDate';
const RAW_API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8000';
// const RAW_API_BASE = 'http://ceshi.apiforeign.minzhong.cn';
// 资源 URL 由后端返回相对路径,例如 /generate/images/xxx.png?exp=xxx&sign=xxx。
// 如果 VITE_API_BASE 被配置成 http://host/api 或 /api,这里会去掉 /api,避免拼成 /api/generate/xxx 导致 404。
const RESOURCE_BASE = RAW_API_BASE.replace(/\/api\/?$/i, '').replace(/\/$/, '');
@@ -323,6 +322,25 @@ const AdminGenerationAiRecords: React.FC = () => {
}, 0);
};
const handleOpenExternalResource = (url?: string | null, type = '素材') => {
if (!url) {
message.warning(`${type}链接为空,暂无法查看`);
return;
}
if (isBlobUrl(url)) {
message.warning('本地临时素材已失效,暂无法查看');
return;
}
if (isUrlExpired(url)) {
message.warning(`${type}链接已超时,请刷新列表或重新搜索后再查看`);
return;
}
window.open(apiUrl(url), '_blank', 'noopener,noreferrer');
};
const columns = useMemo(() => [
{
title: '用户', key: 'user', width: 150,
@@ -649,6 +667,13 @@ const AdminGenerationAiRecords: React.FC = () => {
return (
<div key={refKey} style={{ width: 94 }}>
<div
role="button"
tabIndex={0}
title="点击新页面查看素材"
onClick={() => handleOpenExternalResource(refUrl, isVideo ? '视频素材' : isImage ? '图片素材' : '素材')}
onKeyDown={(e) => {
if (e.key === 'Enter') handleOpenExternalResource(refUrl, isVideo ? '视频素材' : isImage ? '图片素材' : '素材');
}}
style={{
width: 94,
height: 94,
@@ -660,6 +685,7 @@ const AdminGenerationAiRecords: React.FC = () => {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
}}
>
{isImage ? (
@@ -687,14 +713,18 @@ const AdminGenerationAiRecords: React.FC = () => {
style={{ display: 'none' }}
/>
<div style={{ color: '#64748b', fontSize: 12, textAlign: 'center' }}>
<VideoCameraOutlined />
<PlayCircleOutlined style={{ fontSize: 18 }} />
<div></div>
<div style={{ fontSize: 10, color: '#94a3b8', marginTop: 2 }}></div>
</div>
</>
) : null}
{!isImage && !isVideo ? (
<div style={{ color: '#64748b', fontSize: 12, textAlign: 'center' }}></div>
<div style={{ color: '#64748b', fontSize: 12, textAlign: 'center' }}>
<div style={{ fontSize: 10, color: '#94a3b8', marginTop: 2 }}></div>
</div>
) : null}
{state === 'checking' && isImage ? (
File diff suppressed because it is too large Load Diff
+10 -9
View File
@@ -11,7 +11,7 @@ export interface User {
export interface CreditRecord {
id: string;
type: 'consume' | 'recharge';
type: 'consume' | 'recharge' | 'refund';
amount: number;
description: string;
createdAt: string;
@@ -208,9 +208,10 @@ export interface AdminGenerationRecord {
duration?: number;
aspectRatio?: string;
resolution?: string;
status: 'prompt_optimized' | 'generating' | 'completed' | 'failed';
status: 'optimizing' | 'prompt_optimized' | 'generating' | 'completed' | 'failed' | string;
videoUrl?: string;
references?: { url: string; type: string; name: string }[];
videoCoverUrl?: string;
references?: GenerationAIMediaReference[] | null;
creditsCost: number;
textCreditsCost: number;
textTokensUsed: number;
@@ -218,12 +219,12 @@ export interface AdminGenerationRecord {
errorMessage?: string;
createdAt: string;
generatedAt?: string;
genType?: string,
imageSize?: string,
imageUrl?: string,
imageTokensUsed?: number,
imageProportion?: string,
imagePx?: string,
genType?: string;
imageSize?: string;
imageUrl?: string;
imageTokensUsed?: number;
imageProportion?: string;
imagePx?: string;
}
export type GenerationAITaskStatus = 'pending' | 'generating' | 'completed' | 'failed' | string;