ai创建积分计算,授权页面添加
This commit is contained in:
@@ -12,6 +12,7 @@ import GenerateConver from './pages/GenerateConver';
|
|||||||
import InitialReplication from './pages/InitialReplication';
|
import InitialReplication from './pages/InitialReplication';
|
||||||
import RemoveLens from './pages/RemoveLens';
|
import RemoveLens from './pages/RemoveLens';
|
||||||
import GeneratedRecord from './pages/GeneratedRecord';
|
import GeneratedRecord from './pages/GeneratedRecord';
|
||||||
|
import AuthorizationPage from './pages/AuthorizationPage';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -93,6 +94,8 @@ const App = () => {
|
|||||||
<Route path="initial" element={<InitialReplication />} />
|
<Route path="initial" element={<InitialReplication />} />
|
||||||
<Route path="removelens" element={<RemoveLens />} />
|
<Route path="removelens" element={<RemoveLens />} />
|
||||||
<Route path="generated" element={<GeneratedRecord />} />
|
<Route path="generated" element={<GeneratedRecord />} />
|
||||||
|
<Route path="authorization" element={<AuthorizationPage />} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -342,7 +342,12 @@ export async function gethistoryItems(Pagebreak: any): Promise<any[]> {
|
|||||||
return api.get('/generation-ai/history/'+Pagebreak);
|
return api.get('/generation-ai/history/'+Pagebreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除ai对话历史记录
|
||||||
|
export async function deleteHistory(id: string): Promise<void> {
|
||||||
|
await api.delete(`/generation-ai/tasks/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function calculateCredits(): Promise<any[]> {
|
||||||
|
return api.get('/credits/credit-ratios');
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import React, { useEffect, useState, useLayoutEffect, useRef, useCallback } from 'react';
|
||||||
|
import { Button, Table, Checkbox, Tag, Space, message } from 'antd';
|
||||||
|
import { PlusOutlined, CheckCircleOutlined, ClockCircleOutlined, CiCircleOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
// 模拟授权数据
|
||||||
|
const mockAuthorizations = [
|
||||||
|
{ id: '1867060028363785', status: 'active', description: '用户张三的API授权' },
|
||||||
|
{ id: '1867059757929740', status: 'pending', description: '用户李四的API授权' },
|
||||||
|
{ id: '1867059808785418', status: 'active', description: '用户王五的API授权' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 状态配置
|
||||||
|
const statusConfig = {
|
||||||
|
active: { label: '已授权', color: 'green', icon: CheckCircleOutlined },
|
||||||
|
pending: { label: '待授权', color: 'gold', icon: ClockCircleOutlined },
|
||||||
|
expired: { label: '已过期', color: 'red', icon: CiCircleOutlined },
|
||||||
|
revoked: { label: '已撤销', color: 'gray', icon: CiCircleOutlined },
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthorizationPage: React.FC = () => {
|
||||||
|
const [authorizations, setAuthorizations] = useState(mockAuthorizations);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
// 状态标签渲染
|
||||||
|
const renderStatus = (status: string) => {
|
||||||
|
const config = statusConfig[status as keyof typeof statusConfig] || statusConfig.expired;
|
||||||
|
const Icon = config.icon;
|
||||||
|
return (
|
||||||
|
<Tag color={config.color} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||||
|
<Icon size={14} />
|
||||||
|
{config.label}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
// {
|
||||||
|
// title: '',
|
||||||
|
// dataIndex: 'selection',
|
||||||
|
// key: 'selection',
|
||||||
|
// width: 60,
|
||||||
|
// render: (_: any, record: typeof mockAuthorizations[0]) => (
|
||||||
|
// <Checkbox
|
||||||
|
// checked={selectedRowKeys.includes(record.id)}
|
||||||
|
// onChange={(e) => {
|
||||||
|
// if (e.target.checked) {
|
||||||
|
// setSelectedRowKeys([...selectedRowKeys, record.id]);
|
||||||
|
// } else {
|
||||||
|
// setSelectedRowKeys(selectedRowKeys.filter(key => key !== record.id));
|
||||||
|
// }
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
width: 80,
|
||||||
|
render: (text: number) => <span style={{ color: '#94a3b8' }}>{text}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '授权ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text: string) => (
|
||||||
|
<span style={{ fontWeight: 500, color: '#1e293b' }}>{text}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '授权状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
width: 140,
|
||||||
|
render: (text: string) => renderStatus(text),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 处理点击授权按钮
|
||||||
|
const handleAuthorize = () => {
|
||||||
|
if (selectedRowKeys.length === 0) {
|
||||||
|
message.warning('请先选择需要授权的记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
// 模拟授权操作
|
||||||
|
setTimeout(() => {
|
||||||
|
setAuthorizations(prev =>
|
||||||
|
prev.map(item =>
|
||||||
|
selectedRowKeys.includes(item.id) ? { ...item, status: 'active' } : item
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
setLoading(false);
|
||||||
|
message.success(`成功授权 ${selectedRowKeys.length} 条记录`);
|
||||||
|
}, 800);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 准备表格数据(添加序号)
|
||||||
|
const tableData = authorizations.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
index: index + 1,
|
||||||
|
key: item.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: 24, minHeight: '94vh', background: '#f8fafc' }}>
|
||||||
|
{/* 页面标题 */}
|
||||||
|
<div style={{ marginBottom: 20 }}>
|
||||||
|
<h1 style={{ fontSize: 24, fontWeight: 600, color: '#1e293b', marginBottom: 8 }}>
|
||||||
|
授权管理
|
||||||
|
</h1>
|
||||||
|
{/* <p style={{ color: '#64748b' }}>管理系统授权信息,查看和操作授权状态</p> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 操作栏 */}
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={handleAuthorize}
|
||||||
|
loading={loading}
|
||||||
|
// disabled={selectedRowKeys.length === 0}
|
||||||
|
style={{
|
||||||
|
height: 40,
|
||||||
|
padding: '0 24px',
|
||||||
|
borderRadius: 8,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
点击授权
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 表格 */}
|
||||||
|
<div style={{ background: '#fff', borderRadius: 12, boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>
|
||||||
|
<Table
|
||||||
|
dataSource={tableData}
|
||||||
|
columns={columns}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 10,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showTotal: (total) => `共 ${total} 条记录`,
|
||||||
|
}}
|
||||||
|
rowKey="id"
|
||||||
|
bordered={false}
|
||||||
|
style={{ padding: 16 }}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 底部提示 */}
|
||||||
|
{/* <div style={{ marginTop: 16, textAlign: 'center', color: '#94a3b8', fontSize: 13 }}>
|
||||||
|
提示:勾选记录后点击"点击授权"按钮可批量授权
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthorizationPage;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ import {
|
|||||||
XOutlined,
|
XOutlined,
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { gethistory,gethistoryItems } from '../api';
|
import { gethistory, gethistoryItems } from '../api';
|
||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
@@ -117,7 +117,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
// 从URL中提取exp时间戳(支持相对路径和完整URL)
|
// 从URL中提取exp时间戳(支持相对路径和完整URL)
|
||||||
const extractExpTimestamp = (url: string): number | null => {
|
const extractExpTimestamp = (url: string): number | null => {
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 尝试作为完整URL解析
|
// 尝试作为完整URL解析
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
@@ -340,9 +340,9 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
||||||
src={coverUrl}
|
src={coverUrl}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
opacity: isLoaded ? 1 : 0,
|
opacity: isLoaded ? 1 : 0,
|
||||||
transition: 'opacity 0.3s ease-in-out'
|
transition: 'opacity 0.3s ease-in-out'
|
||||||
@@ -357,9 +357,9 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
||||||
src={mediaUrl}
|
src={mediaUrl}
|
||||||
alt="图片预览"
|
alt="图片预览"
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
opacity: isLoaded ? 1 : 0,
|
opacity: isLoaded ? 1 : 0,
|
||||||
transition: 'opacity 0.3s ease-in-out'
|
transition: 'opacity 0.3s ease-in-out'
|
||||||
@@ -421,12 +421,12 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: 'opacity 0.2s',
|
transition: 'opacity 0.2s',
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
(e.currentTarget as HTMLElement).style.opacity = '1';
|
(e.currentTarget as HTMLElement).style.opacity = '1';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
(e.currentTarget as HTMLElement).style.opacity = '0';
|
(e.currentTarget as HTMLElement).style.opacity = '0';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
点击预览
|
点击预览
|
||||||
</div>
|
</div>
|
||||||
@@ -434,7 +434,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 下载文件
|
// 下载文件
|
||||||
const handleDownload = (item: any) => {
|
const handleDownload = (item: any) => {
|
||||||
const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${item.videoUrl || item.imageUrl}`;
|
const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${item.videoUrl || item.imageUrl}`;
|
||||||
@@ -445,16 +445,16 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 预览文件
|
// 预览文件
|
||||||
const handlePreview = (item: any) => {
|
const handlePreview = (item: any) => {
|
||||||
|
|
||||||
setPreviewItem(item);
|
setPreviewItem(item);
|
||||||
setPreviewVisible(true);
|
setPreviewVisible(true);
|
||||||
// 触发事件通知布局组件关闭浮动按钮
|
// 触发事件通知布局组件关闭浮动按钮
|
||||||
window.dispatchEvent(new Event('previewOpen'));
|
window.dispatchEvent(new Event('previewOpen'));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭预览并暂停视频
|
// 关闭预览并暂停视频
|
||||||
const handleClosePreview = () => {
|
const handleClosePreview = () => {
|
||||||
// 方法1: 使用 ref
|
// 方法1: 使用 ref
|
||||||
@@ -470,7 +470,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
});
|
});
|
||||||
setPreviewVisible(false);
|
setPreviewVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let parameters = '';
|
let parameters = '';
|
||||||
@@ -490,7 +490,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
setRecordList(prev => [...prev, ...data]);
|
setRecordList(prev => [...prev, ...data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTotalnumber(res?.totalDays || 0);
|
setTotalnumber(res?.totalDays || 0);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (Pagebreak.page === 1) {
|
if (Pagebreak.page === 1) {
|
||||||
@@ -509,16 +509,16 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
page: prev.page + 1
|
page: prev.page + 1
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 分组加载更多
|
// 分组加载更多
|
||||||
const handleGroupLoadMore = async (time:string, date: string, page: number) => {
|
const handleGroupLoadMore = async (time: string, date: string, page: number) => {
|
||||||
if (loadingGroups.has(date)) return;
|
if (loadingGroups.has(date)) return;
|
||||||
setLoadingGroups(prev => new Set([...prev, date]));
|
setLoadingGroups(prev => new Set([...prev, date]));
|
||||||
|
|
||||||
const addpage = page + 1;
|
const addpage = page + 1;
|
||||||
|
|
||||||
let parameters = ``;
|
let parameters = ``;
|
||||||
|
|
||||||
if (filterType === 'project') {
|
if (filterType === 'project') {
|
||||||
@@ -529,10 +529,10 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res: any = await gethistoryItems(parameters);
|
const res: any = await gethistoryItems(parameters);
|
||||||
|
|
||||||
// gethistoryItems 返回数组,直接使用
|
// gethistoryItems 返回数组,直接使用
|
||||||
const newItems: any[] = res.items || [];
|
const newItems: any[] = res.items || [];
|
||||||
|
|
||||||
if (newItems && newItems.length > 0) {
|
if (newItems && newItems.length > 0) {
|
||||||
setRecordList(prev => prev.map(group => {
|
setRecordList(prev => prev.map(group => {
|
||||||
if (group.generatedDate === time) {
|
if (group.generatedDate === time) {
|
||||||
@@ -566,14 +566,14 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div style={{ marginBottom: 20 }}>
|
{/* <div style={{ marginBottom: 20 }}>
|
||||||
<Typography.Title level={3} style={{ margin: '0 0 4px', color: '#1a1a2e', fontWeight: 700 }}>
|
<Typography.Title level={3} style={{ margin: '0 0 4px', color: '#1a1a2e', fontWeight: 700 }}>
|
||||||
生成历史
|
生成历史
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Typography.Text style={{ color: '#94a3b8', fontSize: 14 }}>
|
<Typography.Text style={{ color: '#94a3b8', fontSize: 14 }}>
|
||||||
查看所有生成的视频和图片记录
|
查看所有生成的视频和图片记录
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
{/* First row filter: 项目记录 / 创作记录 */}
|
{/* First row filter: 项目记录 / 创作记录 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -646,7 +646,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
: '#f8f9fc',
|
: '#f8f9fc',
|
||||||
border: filterMedia === 'video' ? 'none' : '1px solid #e2e8f0',
|
border: filterMedia === 'video' ? 'none' : '1px solid #e2e8f0',
|
||||||
color: filterMedia === 'video' ? '#fff' : '#64748b',
|
color: filterMedia === 'video' ? '#fff' : '#64748b',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
icon={<VideoCameraOutlined />}
|
icon={<VideoCameraOutlined />}
|
||||||
>
|
>
|
||||||
@@ -680,7 +680,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ padding: '0 4px' }}>
|
<div style={{ padding: '0 4px' }}>
|
||||||
{recordlist.map((group: any,index: number) => (
|
{recordlist.map((group: any, index: number) => (
|
||||||
<div key={index} style={{ marginBottom: 32 }}>
|
<div key={index} style={{ marginBottom: 32 }}>
|
||||||
{/* Date label */}
|
{/* Date label */}
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -750,7 +750,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 预览弹窗 */}
|
{/* 预览弹窗 */}
|
||||||
{previewVisible && previewItem && (
|
{previewVisible && previewItem && (
|
||||||
<div
|
<div
|
||||||
@@ -811,7 +811,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 内容区域 */}
|
{/* 内容区域 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -825,12 +825,12 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}>
|
}}>
|
||||||
{/* 媒体预览 */}
|
{/* 媒体预览 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: '280px',
|
minWidth: '280px',
|
||||||
maxWidth: '800px',
|
maxWidth: '800px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
minHeight: '200px',
|
minHeight: '200px',
|
||||||
}}>
|
}}>
|
||||||
@@ -846,9 +846,9 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewItem.videoUrl}`}
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewItem.videoUrl}`}
|
||||||
controls
|
controls
|
||||||
autoPlay
|
autoPlay
|
||||||
style={{
|
style={{
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: '55vh',
|
maxHeight: '55vh',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
}}
|
}}
|
||||||
@@ -857,16 +857,16 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${previewItem.imageUrl}&w=300&q=50`}
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${previewItem.imageUrl}&w=300&q=50`}
|
||||||
alt="预览"
|
alt="预览"
|
||||||
style={{
|
style={{
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: '55vh',
|
maxHeight: '55vh',
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
borderRadius: 8
|
borderRadius: 8
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 参数信息 */}
|
{/* 参数信息 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -882,7 +882,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', display: 'block', marginBottom: 16 }}>
|
<Typography.Text strong style={{ fontSize: 14, color: '#475569', display: 'block', marginBottom: 16 }}>
|
||||||
文件信息
|
文件信息
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
{/* ID */}
|
{/* ID */}
|
||||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
@@ -891,7 +891,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
{previewItem.id || '-'}
|
{previewItem.id || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
{/* 类型 */}
|
{/* 类型 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>类型</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>类型</span>
|
||||||
@@ -899,59 +899,59 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
{filterMedia === 'video' ? '视频' : '图片'}
|
{filterMedia === 'video' ? '视频' : '图片'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: 12 }}>
|
<div style={{ display: 'flex', gap: 12 }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13, flexShrink: 0, width: 40 }}>请求</span>
|
<span style={{ color: '#94a3b8', fontSize: 13, flexShrink: 0, width: 40 }}>请求</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, flex: 1, wordBreak: 'break-all' }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, flex: 1, wordBreak: 'break-all' }}>
|
||||||
{previewItem.originalPrompt}
|
{previewItem.originalPrompt}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 分辨率 */}
|
{/* 分辨率 */}
|
||||||
{filterMedia === 'image' && (
|
{filterMedia === 'image' && (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.imageProportion}
|
{previewItem.imageProportion}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.imageSize}
|
{previewItem.imageSize}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.imagePx}
|
{previewItem.imagePx}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{filterMedia === 'video' && (
|
{filterMedia === 'video' && (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.aspectRatio}
|
{previewItem.aspectRatio}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.resolution}
|
{previewItem.resolution}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
||||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||||
{previewItem.imagePx}
|
{previewItem.imagePx}
|
||||||
</span>
|
</span>
|
||||||
</div> */}
|
</div> */}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 时长(视频) */}
|
{/* 时长(视频) */}
|
||||||
{filterMedia === 'video' && (
|
{filterMedia === 'video' && (
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
@@ -961,7 +961,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 文件大小 */}
|
{/* 文件大小 */}
|
||||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>文件大小</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>文件大小</span>
|
||||||
@@ -969,7 +969,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
{previewItem.size ? formatFileSize(previewItem.size) : '-'}
|
{previewItem.size ? formatFileSize(previewItem.size) : '-'}
|
||||||
</span>
|
</span>
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
{/* 创建时间 */}
|
{/* 创建时间 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>创建时间</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>创建时间</span>
|
||||||
@@ -977,7 +977,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
{formatDateTime(previewItem.createdAt || previewItem.generatedDate)}
|
{formatDateTime(previewItem.createdAt || previewItem.generatedDate)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 生成引擎 */}
|
{/* 生成引擎 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>生成引擎</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>生成引擎</span>
|
||||||
@@ -986,7 +986,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 分隔线 */}
|
{/* 分隔线 */}
|
||||||
<div style={{ borderTop: '1px dashed #e2e8f0', margin: '16px 0' }} />
|
<div style={{ borderTop: '1px dashed #e2e8f0', margin: '16px 0' }} />
|
||||||
|
|
||||||
@@ -1038,29 +1038,42 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 */}
|
||||||
<div style={{ display: 'flex', gap: 12 ,marginTop:20}}>
|
<div >
|
||||||
<Button
|
<div style={{ display: 'flex', gap: 12, marginTop: 20 }}>
|
||||||
type="primary"
|
<Button
|
||||||
icon={<DownloadOutlined />}
|
type="primary"
|
||||||
onClick={() => {
|
icon={<DownloadOutlined />}
|
||||||
// 暂停视频
|
onClick={() => {
|
||||||
if (videoRef.current) {
|
// 暂停视频
|
||||||
videoRef.current.pause();
|
if (videoRef.current) {
|
||||||
}
|
videoRef.current.pause();
|
||||||
const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewItem.videoUrl || previewItem.imageUrl}`;
|
}
|
||||||
window.open(url, '_blank');
|
const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewItem.videoUrl || previewItem.imageUrl}`;
|
||||||
}}
|
window.open(url, '_blank');
|
||||||
style={{ flex: 1, borderRadius: 8 }}
|
}}
|
||||||
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
|
style={{ flex: 1, borderRadius: 8 }}
|
||||||
>
|
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
|
||||||
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'}
|
>
|
||||||
</Button>
|
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'}
|
||||||
<Button
|
</Button>
|
||||||
onClick={handleClosePreview}
|
<Button
|
||||||
style={{ flex: 1, borderRadius: 8 }}
|
onClick={handleClosePreview}
|
||||||
>
|
style={{ flex: 1, borderRadius: 8 }}
|
||||||
关闭
|
>
|
||||||
</Button>
|
关闭
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
|
||||||
|
style={{width:'100%', borderRadius: 8 ,marginTop:20,color:'#4c49cc'}}
|
||||||
|
>
|
||||||
|
|
||||||
|
推送巨量引擎后台
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1095,14 +1108,14 @@ function formatDateTime(dateString: string): string {
|
|||||||
if (!dateString) return '-';
|
if (!dateString) return '-';
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
if (isNaN(date.getTime())) return '-';
|
if (isNaN(date.getTime())) return '-';
|
||||||
|
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,7 @@ const LoginPage: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="password" rules={[{ required: true, message: '请设置密码' }, { min: 6, message: '密码至少6位' }]}>
|
<Form.Item name="password" rules={[{ required: true, message: '请设置密码' }, { min: 6, message: '密码至少6位' }]}>
|
||||||
<Input.Password prefix={<LockOutlined style={{ color: '#94a3b8', marginRight: 8 }} />} placeholder="请设置密码(至少6位)" style={inputStyle} />
|
<Input.Password prefix={<LockOutlined style={{ color: '#94a3b8', marginRight: 8 }} />} placeholder="请设置密码(至少6位)" style={inputStyle} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
Reference in New Issue
Block a user