Merge branch 'main' of gitee.com:wg123/video-gen into main
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user