ai创建积分计算,授权页面添加
This commit is contained in:
@@ -12,6 +12,7 @@ import GenerateConver from './pages/GenerateConver';
|
||||
import InitialReplication from './pages/InitialReplication';
|
||||
import RemoveLens from './pages/RemoveLens';
|
||||
import GeneratedRecord from './pages/GeneratedRecord';
|
||||
import AuthorizationPage from './pages/AuthorizationPage';
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +94,8 @@ const App = () => {
|
||||
<Route path="initial" element={<InitialReplication />} />
|
||||
<Route path="removelens" element={<RemoveLens />} />
|
||||
<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);
|
||||
}
|
||||
|
||||
// 删除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,
|
||||
ClockCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { gethistory,gethistoryItems } from '../api';
|
||||
import { gethistory, gethistoryItems } from '../api';
|
||||
|
||||
const { Search } = Input;
|
||||
const { Text } = Typography;
|
||||
@@ -117,7 +117,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
// 从URL中提取exp时间戳(支持相对路径和完整URL)
|
||||
const extractExpTimestamp = (url: string): number | null => {
|
||||
if (!url) return null;
|
||||
|
||||
|
||||
try {
|
||||
// 尝试作为完整URL解析
|
||||
const urlObj = new URL(url);
|
||||
@@ -340,9 +340,9 @@ const GeneratedRecord: React.FC = () => {
|
||||
<img
|
||||
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
||||
src={coverUrl}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: isLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out'
|
||||
@@ -357,9 +357,9 @@ const GeneratedRecord: React.FC = () => {
|
||||
ref={mediaRef as React.RefObject<HTMLImageElement>}
|
||||
src={mediaUrl}
|
||||
alt="图片预览"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: isLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out'
|
||||
@@ -421,12 +421,12 @@ const GeneratedRecord: React.FC = () => {
|
||||
opacity: 0,
|
||||
transition: 'opacity 0.2s',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
(e.currentTarget as HTMLElement).style.opacity = '1';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
(e.currentTarget as HTMLElement).style.opacity = '0';
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
(e.currentTarget as HTMLElement).style.opacity = '1';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
(e.currentTarget as HTMLElement).style.opacity = '0';
|
||||
}}
|
||||
>
|
||||
点击预览
|
||||
</div>
|
||||
@@ -434,7 +434,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// 下载文件
|
||||
const handleDownload = (item: any) => {
|
||||
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();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
|
||||
// 预览文件
|
||||
const handlePreview = (item: any) => {
|
||||
|
||||
|
||||
setPreviewItem(item);
|
||||
setPreviewVisible(true);
|
||||
// 触发事件通知布局组件关闭浮动按钮
|
||||
window.dispatchEvent(new Event('previewOpen'));
|
||||
};
|
||||
|
||||
|
||||
// 关闭预览并暂停视频
|
||||
const handleClosePreview = () => {
|
||||
// 方法1: 使用 ref
|
||||
@@ -470,7 +470,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
});
|
||||
setPreviewVisible(false);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
let parameters = '';
|
||||
@@ -490,7 +490,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
} else {
|
||||
setRecordList(prev => [...prev, ...data]);
|
||||
}
|
||||
|
||||
|
||||
setTotalnumber(res?.totalDays || 0);
|
||||
}).catch((err) => {
|
||||
if (Pagebreak.page === 1) {
|
||||
@@ -509,16 +509,16 @@ const GeneratedRecord: React.FC = () => {
|
||||
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;
|
||||
setLoadingGroups(prev => new Set([...prev, date]));
|
||||
|
||||
|
||||
const addpage = page + 1;
|
||||
|
||||
|
||||
let parameters = ``;
|
||||
|
||||
if (filterType === 'project') {
|
||||
@@ -529,10 +529,10 @@ const GeneratedRecord: React.FC = () => {
|
||||
|
||||
try {
|
||||
const res: any = await gethistoryItems(parameters);
|
||||
|
||||
|
||||
// gethistoryItems 返回数组,直接使用
|
||||
const newItems: any[] = res.items || [];
|
||||
|
||||
|
||||
if (newItems && newItems.length > 0) {
|
||||
setRecordList(prev => prev.map(group => {
|
||||
if (group.generatedDate === time) {
|
||||
@@ -566,14 +566,14 @@ const GeneratedRecord: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
{/* <div style={{ marginBottom: 20 }}>
|
||||
<Typography.Title level={3} style={{ margin: '0 0 4px', color: '#1a1a2e', fontWeight: 700 }}>
|
||||
生成历史
|
||||
</Typography.Title>
|
||||
<Typography.Text style={{ color: '#94a3b8', fontSize: 14 }}>
|
||||
查看所有生成的视频和图片记录
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* First row filter: 项目记录 / 创作记录 */}
|
||||
<div style={{
|
||||
@@ -646,7 +646,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
: '#f8f9fc',
|
||||
border: filterMedia === 'video' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterMedia === 'video' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<VideoCameraOutlined />}
|
||||
>
|
||||
@@ -680,7 +680,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
/>
|
||||
) : (
|
||||
<div style={{ padding: '0 4px' }}>
|
||||
{recordlist.map((group: any,index: number) => (
|
||||
{recordlist.map((group: any, index: number) => (
|
||||
<div key={index} style={{ marginBottom: 32 }}>
|
||||
{/* Date label */}
|
||||
<div style={{
|
||||
@@ -750,7 +750,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* 预览弹窗 */}
|
||||
{previewVisible && previewItem && (
|
||||
<div
|
||||
@@ -811,7 +811,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div style={{
|
||||
flex: 1,
|
||||
@@ -825,12 +825,12 @@ const GeneratedRecord: React.FC = () => {
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
{/* 媒体预览 */}
|
||||
<div style={{
|
||||
<div style={{
|
||||
flex: 1,
|
||||
minWidth: '280px',
|
||||
maxWidth: '800px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '200px',
|
||||
}}>
|
||||
@@ -846,9 +846,9 @@ const GeneratedRecord: React.FC = () => {
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewItem.videoUrl}`}
|
||||
controls
|
||||
autoPlay
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '55vh',
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '55vh',
|
||||
borderRadius: 8,
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
@@ -857,16 +857,16 @@ const GeneratedRecord: React.FC = () => {
|
||||
<img
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${previewItem.imageUrl}&w=300&q=50`}
|
||||
alt="预览"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '55vh',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 8
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '55vh',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 8
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{/* 参数信息 */}
|
||||
<div style={{
|
||||
width: '100%',
|
||||
@@ -882,7 +882,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', display: 'block', marginBottom: 16 }}>
|
||||
文件信息
|
||||
</Typography.Text>
|
||||
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{/* ID */}
|
||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
@@ -891,7 +891,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
{previewItem.id || '-'}
|
||||
</span>
|
||||
</div> */}
|
||||
|
||||
|
||||
{/* 类型 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>类型</span>
|
||||
@@ -899,59 +899,59 @@ const GeneratedRecord: React.FC = () => {
|
||||
{filterMedia === 'video' ? '视频' : '图片'}
|
||||
</span>
|
||||
</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: '#334155', fontSize: 13, fontWeight: 500, flex: 1, wordBreak: 'break-all' }}>
|
||||
{previewItem.originalPrompt}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 分辨率 */}
|
||||
{filterMedia === 'image' && (
|
||||
<>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imageProportion}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imageSize}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imagePx}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{filterMedia === 'video' && (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imageProportion}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imageSize}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imagePx}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{filterMedia === 'video' && (
|
||||
<>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>比例</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.aspectRatio}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.resolution}
|
||||
</span>
|
||||
</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: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.aspectRatio}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>分辨率</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.resolution}
|
||||
</span>
|
||||
</div>
|
||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span>
|
||||
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
|
||||
{previewItem.imagePx}
|
||||
</span>
|
||||
</div> */}
|
||||
</>
|
||||
)}
|
||||
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 时长(视频) */}
|
||||
{filterMedia === 'video' && (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
@@ -961,7 +961,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* 文件大小 */}
|
||||
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>文件大小</span>
|
||||
@@ -969,7 +969,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
{previewItem.size ? formatFileSize(previewItem.size) : '-'}
|
||||
</span>
|
||||
</div> */}
|
||||
|
||||
|
||||
{/* 创建时间 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>创建时间</span>
|
||||
@@ -977,7 +977,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
{formatDateTime(previewItem.createdAt || previewItem.generatedDate)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 生成引擎 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>生成引擎</span>
|
||||
@@ -986,7 +986,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 分隔线 */}
|
||||
<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}}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
// 暂停视频
|
||||
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');
|
||||
}}
|
||||
style={{ flex: 1, borderRadius: 8 }}
|
||||
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
|
||||
>
|
||||
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleClosePreview}
|
||||
style={{ flex: 1, borderRadius: 8 }}
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
<div >
|
||||
<div style={{ display: 'flex', gap: 12, marginTop: 20 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
// 暂停视频
|
||||
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');
|
||||
}}
|
||||
style={{ flex: 1, borderRadius: 8 }}
|
||||
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
|
||||
>
|
||||
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleClosePreview}
|
||||
style={{ flex: 1, borderRadius: 8 }}
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
|
||||
style={{width:'100%', borderRadius: 8 ,marginTop:20,color:'#4c49cc'}}
|
||||
>
|
||||
|
||||
推送巨量引擎后台
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1095,14 +1108,14 @@ function formatDateTime(dateString: string): string {
|
||||
if (!dateString) return '-';
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) return '-';
|
||||
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ const LoginPage: React.FC = () => {
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
|
||||
<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} />
|
||||
</Form.Item>
|
||||
|
||||
Reference in New Issue
Block a user