Merge branch 'main' of gitee.com:wg123/video-gen into main

This commit is contained in:
18610128193
2026-07-03 10:47:38 +08:00
4 changed files with 90 additions and 49 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
}
})();
</script>
<script type="module" crossorigin src="/assets/index-DNsZEwFL.js"></script>
<script type="module" crossorigin src="/assets/index-BpU4LxvO.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
</head>
<body>
@@ -27,6 +27,7 @@ import {
SearchOutlined,
VideoCameraOutlined,
} from '@ant-design/icons';
import dayjs from 'dayjs';
import { getAdminGenerationAiTasks } from '../api';
import type { GenerationAIMediaReference, GenerationAITaskOut } from '../types';
import { formatDate } from '../utils/formatDate';
@@ -214,17 +215,9 @@ const InfoItem: React.FC<{ label: string; value?: React.ReactNode }> = ({ label,
</div>
);
const todayStart = (): Date => {
const d = new Date();
d.setHours(0, 0, 0, 0);
return d;
};
const todayStart = () => dayjs().startOf('day');
const todayEnd = (): Date => {
const d = new Date();
d.setHours(23, 59, 59, 999);
return d;
};
const todayEnd = () => dayjs().endOf('day');
const AdminGenerationAiRecords: React.FC = () => {
const [records, setRecords] = useState<GenerationAITaskOut[]>([]);
@@ -249,6 +242,13 @@ const AdminGenerationAiRecords: React.FC = () => {
const [videoPlaying, setVideoPlaying] = useState(false);
const videoRef = useRef<HTMLVideoElement | null>(null);
// 资源预览弹窗(附件 / 最终结果)
const [resourcePreview, setResourcePreview] = useState<{
url: string;
type: 'image' | 'video';
title: string;
} | null>(null);
const load = useCallback(async () => {
setLoading(true);
try {
@@ -392,6 +392,21 @@ const AdminGenerationAiRecords: React.FC = () => {
window.open(apiUrl(url), '_blank', 'noopener,noreferrer');
};
/** 弹窗预览资源(图片/视频) */
const handlePreviewResource = (url: string, type: 'image' | 'video', title: string) => {
if (isBlobUrl(url)) {
message.warning('本地临时素材已失效,暂无法查看');
return;
}
if (isUrlExpired(url)) {
message.warning('链接已超时,请刷新列表或重新搜索后再查看');
return;
}
setResourcePreview({ url: apiUrl(url), type, title });
};
const handleCloseResourcePreview = () => setResourcePreview(null);
const columns = useMemo(() => [
{
title: '用户', key: 'user', width: 150,
@@ -903,7 +918,7 @@ const AdminGenerationAiRecords: React.FC = () => {
placeholder="类型筛选"
value={filterGenType || undefined}
style={{ width: 120 }}
onChange={(v) => { setFilterGenType(v || ''); setPage(1); setReloadKey((k) => k + 1); }}
onChange={(v) => { setFilterGenType(v || ''); setPage(1); }}
options={[
{ value: 'image', label: '图片' },
{ value: 'video', label: '视频' },
@@ -915,7 +930,7 @@ const AdminGenerationAiRecords: React.FC = () => {
placeholder="引擎筛选"
value={filterEngineId || undefined}
style={{ width: 180 }}
onChange={(v) => { setFilterEngineId(v || ''); setPage(1); setQueryEngineId(v || ''); setReloadKey((k) => k + 1); }}
onChange={(v) => { setFilterEngineId(v || ''); setPage(1); setQueryEngineId(v || ''); }}
optionFilterProp="label"
options={Array.from(
new Map(
@@ -929,10 +944,9 @@ const AdminGenerationAiRecords: React.FC = () => {
)}
/>
<RangePicker
showTime
value={createdRange}
onChange={(dates) => { setCreatedRange(dates); }}
placeholder={['创建开始', '创建结束']}
placeholder={['开始日期', '结束日期']}
/>
<Input
placeholder="用户ID搜索"
@@ -1071,7 +1085,7 @@ const AdminGenerationAiRecords: React.FC = () => {
key={idx}
size="small"
icon={refType === 'video' ? <PlayCircleOutlined /> : <LinkOutlined />}
onClick={() => handleOpenExternalResource(refUrl, refType === 'video' ? '视频附件' : '图片附件')}
onClick={() => handlePreviewResource(refUrl, refType === 'video' ? 'video' : 'image', title)}
>
{title}
</Button>
@@ -1092,10 +1106,10 @@ const AdminGenerationAiRecords: React.FC = () => {
size="small"
type="link"
icon={<PlayCircleOutlined />}
onClick={() => handleOpenExternalResource(preview.videoUrl, '视频')}
onClick={() => handlePreviewResource(preview.videoUrl!, 'video', '生成视频')}
style={{ padding: 0 }}
>
</Button>
) : null}
{preview.genType === 'image' && preview.imageUrl ? (
@@ -1103,10 +1117,10 @@ const AdminGenerationAiRecords: React.FC = () => {
size="small"
type="link"
icon={<FileImageOutlined />}
onClick={() => handleOpenExternalResource(preview.imageUrl, '图片')}
onClick={() => handlePreviewResource(preview.imageUrl!, 'image', '生成图片')}
style={{ padding: 0 }}
>
</Button>
) : null}
</div>
@@ -1129,6 +1143,34 @@ const AdminGenerationAiRecords: React.FC = () => {
<Empty description="暂无详情" />
)}
</Modal>
{/* 资源预览弹窗(附件 / 最终结果) */}
<Modal
title={resourcePreview?.title || '资源预览'}
open={!!resourcePreview}
onCancel={handleCloseResourcePreview}
footer={null}
width={resourcePreview?.type === 'video' ? 800 : 600}
destroyOnHidden
>
{resourcePreview ? (
resourcePreview.type === 'video' ? (
<video
key={resourcePreview.url}
src={resourcePreview.url}
controls
autoPlay
style={{ width: '100%', maxHeight: 600, background: '#000', borderRadius: 8 }}
/>
) : (
<img
src={resourcePreview.url}
alt={resourcePreview.title}
style={{ width: '100%', maxHeight: 600, objectFit: 'contain', borderRadius: 8 }}
/>
)
) : null}
</Modal>
</div>
);
};
+27 -28
View File
@@ -10,16 +10,16 @@ import { formatDate } from '../utils/formatDate';
interface OAuthApp {
id: string;
appId: string;
app_id: string;
secret: string;
status: number;
count: number;
openType: number;
authUrl?: string;
max_count: number;
open_type: number;
auth_url?: string;
company?: string;
createBy: string;
createdAt: string;
updatedAt: string;
create_by: string;
created_at: string;
updated_at: string;
}
const AdminOauthAppList: React.FC = () => {
@@ -82,11 +82,11 @@ const AdminOauthAppList: React.FC = () => {
const app = await getOauthApp(id);
setCurrentApp(app);
updateForm.setFieldsValue({
app_id: app.appId,
app_id: app.app_id,
secret: app.secret,
open_type: app.openType,
count: app.count,
auth_url: app.authUrl,
open_type: app.open_type,
count: app.max_count,
auth_url: app.auth_url,
company: app.company,
});
setUpdateModalVisible(true);
@@ -140,13 +140,13 @@ const AdminOauthAppList: React.FC = () => {
{ title: 'ID', dataIndex: 'id',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '应用ID', dataIndex: 'appId',
{ title: '应用ID', dataIndex: 'app_id',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '应用密钥', dataIndex: 'secret',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '开户方式', dataIndex: 'openType',
{ title: '开户方式', dataIndex: 'open_type',
render: (v: number) => {
const typeMap: Record<number, string> = {
1: '千川', 2: '广告', 3: '本地推', 4: '星图', 5: '快手代理商',
@@ -158,23 +158,22 @@ const AdminOauthAppList: React.FC = () => {
{ title: '归属公司', dataIndex: 'company',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '授权次数', dataIndex: 'count',
{ title: '授权次数', dataIndex: 'max_count',
render: (v: number) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '状态', dataIndex: 'status',
render: (v: number) => <Tag color={v === 1 ? 'green' : 'red'}>{v === 1 ? '正常' : '禁用'}</Tag>,
},
{ title: '授权URL', dataIndex: 'authUrl',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '创建人', dataIndex: 'createBy',
{ title: '授权URL', dataIndex: 'auth_url',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '创建时间', dataIndex: 'createdAt',
{ title: '创建', dataIndex: 'create_by',
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{ title: '创建时间', dataIndex: 'created_at',
render: (v: string) => <Typography.Text>{formatDate(v)}</Typography.Text>,
},
{ title: '更新时间', dataIndex: 'updatedAt',
{ title: '更新时间', dataIndex: 'updated_at',
render: (v: string) => <Typography.Text>{formatDate(v)}</Typography.Text>,
},
{ title: '操作',
@@ -312,22 +311,22 @@ const AdminOauthAppList: React.FC = () => {
{currentApp && (
<div style={{ lineHeight: '2' }}>
<p><strong>ID:</strong> {currentApp.id}</p>
<p><strong>ID:</strong> {currentApp.appId}</p>
<p><strong>ID:</strong> {currentApp.app_id}</p>
<p><strong>:</strong> {currentApp.secret}</p>
<p><strong>:</strong> {(() => {
const typeMap: Record<number, string> = {
1: '千川', 2: '广告', 3: '本地推', 4: '星图', 5: '快手代理商',
6: '巨量星图', 7: '巨量服务单', 8: '腾讯服务单', 9: '腾讯营销K2', 10: '腾讯营销K3'
};
return typeMap[currentApp.openType] || currentApp.openType;
return typeMap[currentApp.open_type] || currentApp.open_type;
})()}</p>
<p><strong>:</strong> {currentApp.company || '-'}</p>
<p><strong>:</strong> {currentApp.count}</p>
<p><strong>:</strong> {currentApp.max_count}</p>
<p><strong>:</strong> {currentApp.status === 1 ? '正常' : '禁用'}</p>
<p><strong>URL:</strong> {currentApp.authUrl || '-'}</p>
<p><strong>:</strong> {currentApp.createBy}</p>
<p><strong>:</strong> {formatDate(currentApp.createdAt)}</p>
<p><strong>:</strong> {formatDate(currentApp.updatedAt)}</p>
<p><strong>URL:</strong> {currentApp.auth_url || '-'}</p>
<p><strong>:</strong> {currentApp.create_by}</p>
<p><strong>:</strong> {formatDate(currentApp.created_at)}</p>
<p><strong>:</strong> {formatDate(currentApp.updated_at)}</p>
</div>
)}
</Modal>