+
+
+
+ 授权管理
+
+ }
+ onClick={handleAuthorize}
+ loading={loading}
+ style={{
+ borderRadius: 8,
+ fontSize: 14,
+ fontWeight: 500,
+ }}
+ >
+ 点击授权
+
+
+
+ setSearchParams(prev => ({ ...prev, account_userid: e.target.value }))}
+ style={{ width: 180 }}
+ onPressEnter={() => { setCurrentPage(1); loadOAuthList(1, pageSize); }}
+ />
+
+ `共 ${t} 条记录`,
+ onChange: (page, size) => {
+ setCurrentPage(page);
+ setPageSize(size);
+ loadOAuthList(page, size);
+ },
+ size: 'small',
+ }}
+ />
+
+
+ {
+ setShowModal(false);
+ setSelectedOpenType(undefined);
+ }}
+ okText="确认授权"
+ cancelText="取消"
+ confirmLoading={loading}
+ >
+
+
+ );
+};
+
+export default AuthorizationPage;
\ No newline at end of file
diff --git a/video-gen-admin/src/pages/AdminConsume.tsx b/video-gen-admin/src/pages/AdminConsume.tsx
new file mode 100644
index 00000000..c7593f02
--- /dev/null
+++ b/video-gen-admin/src/pages/AdminConsume.tsx
@@ -0,0 +1,267 @@
+import React, { useState, useEffect } from 'react';
+import { Card, Space, Table, Button, Typography, Modal, Checkbox, Input, message } from 'antd';
+import { ArrowLeftOutlined, DollarOutlined, SettingOutlined } from '@ant-design/icons';
+import { useNavigate } from 'react-router-dom';
+
+// 表头字段模拟数据
+const mockColumnsData = [
+ { name: 'id', description: 'id' },
+ { name: 'star_id', description: '星图ID' },
+ { name: 'demand_id', description: '任务ID' },
+ { name: 'demand_name', description: '任务名称' },
+ { name: 'demander_name', description: '客户名称' },
+ { name: 'brand', description: '品牌' },
+ { name: 'max_publish_count', description: '最大发布数量' },
+ { name: 'product_name', description: '产品名称' },
+ { name: 'product_information', description: '产品信息' },
+];
+
+interface ConsumptionRecord {
+ id: string;
+ star_id?: string;
+ demand_id?: string;
+ demand_name?: string;
+ demander_name?: string;
+ brand?: string;
+ max_publish_count?: number;
+ product_name?: string;
+ product_information?: string;
+}
+
+const mockConsumptionRecords: ConsumptionRecord[] = [
+ { id: 'C001', star_id: 'ST001', demand_id: 'DM001', demand_name: '产品推广任务', demander_name: 'XX品牌方', brand: 'XX品牌', max_publish_count: 10, product_name: 'XX产品', product_information: '产品信息描述' },
+ { id: 'C002', star_id: 'ST002', demand_id: 'DM002', demand_name: '新品发布任务', demander_name: 'YY品牌方', brand: 'YY品牌', max_publish_count: 5, product_name: 'YY产品', product_information: '新品信息描述' },
+ { id: 'C003', star_id: 'ST003', demand_id: 'DM003', demand_name: '品牌宣传任务', demander_name: 'ZZ品牌方', brand: 'ZZ品牌', max_publish_count: 8, product_name: 'ZZ产品', product_information: '品牌信息描述' },
+ { id: 'C004', star_id: 'ST001', demand_id: 'DM004', demand_name: '活动促销任务', demander_name: 'XX品牌方', brand: 'XX品牌', max_publish_count: 15, product_name: 'XX活动产品', product_information: '活动产品信息' },
+ { id: 'C005', star_id: 'ST004', demand_id: 'DM005', demand_name: '节日营销任务', demander_name: 'AA品牌方', brand: 'AA品牌', max_publish_count: 20, product_name: 'AA节日产品', product_information: '节日产品信息' },
+];
+
+const ConsumePage: React.FC = () => {
+ const navigate = useNavigate();
+ const [consumptionRecords, setConsumptionRecords] = useState(mockConsumptionRecords);
+ const [loading, setLoading] = useState(false);
+ const [showModal, setShowModal] = useState(false);
+ const [columnsData, setColumnsData] = useState<{ name: string; description: string }[]>([]);
+ const [selectedColumns, setSelectedColumns] = useState([]);
+ const [searchText, setSearchText] = useState('');
+
+ useEffect(() => {
+ setLoading(true);
+ setTimeout(() => {
+ setConsumptionRecords(mockConsumptionRecords);
+ setLoading(false);
+ }, 500);
+
+ setTimeout(() => {
+ setColumnsData(mockColumnsData);
+ const saved = localStorage.getItem('consumeColumns');
+ if (saved) {
+ setSelectedColumns(JSON.parse(saved));
+ } else {
+ setSelectedColumns(mockColumnsData.map(item => item.name));
+ }
+ }, 300);
+ }, []);
+
+ const tableData = consumptionRecords.map((item, index) => ({
+ ...item,
+ index: index + 1,
+ key: item.id,
+ }));
+
+ const handleBack = () => {
+ navigate('/authorization');
+ };
+
+ const dynamicColumns = columnsData
+ .filter(col => selectedColumns.includes(col.name))
+ .map(col => ({
+ title: col.description,
+ dataIndex: col.name,
+ key: col.name,
+ ellipsis: true,
+ }));
+
+ return (
+
+
+
+
+
+ 消耗记录
+
+
+ } onClick={() => setShowModal(true)}>自定义表头
+ } onClick={handleBack}>返回
+
+
+ `共 ${t} 条记录`,
+ size: 'small',
+ }}
+ />
+
+
+ {
+ localStorage.setItem('consumeColumns', JSON.stringify(selectedColumns));
+ message.success('表头设置已保存');
+ setShowModal(false);
+ }}
+ onCancel={() => {
+ setShowModal(false);
+ setSearchText('');
+ }}
+ okText="确定"
+ cancelText="取消"
+ width={800}
+ bodyStyle={{ padding: 16 }}
+ >
+
+
+
setSearchText(e.target.value)}
+ style={{ marginBottom: 8 }}
+ />
+
+ 输入指标名称进行搜索
+
+
+
+ 任务详情指标
+
+
0}
+ indeterminate={selectedColumns.length > 0 && selectedColumns.length < columnsData.length}
+ onChange={(e) => {
+ if (e.target.checked) {
+ setSelectedColumns(columnsData.map(item => item.name));
+ } else {
+ setSelectedColumns([]);
+ }
+ }}
+ style={{ marginBottom: 12 }}
+ >
+ 全选
+
+
+ {columnsData
+ .filter(item =>
+ item.description.toLowerCase().includes(searchText.toLowerCase()) ||
+ item.name.toLowerCase().includes(searchText.toLowerCase())
+ )
+ .map(item => (
+ {
+ if (e.target.checked) {
+ setSelectedColumns(prev => [...prev, item.name]);
+ } else {
+ setSelectedColumns(prev => prev.filter(col => col !== item.name));
+ }
+ }}
+ >
+ {item.description}
+
+ ))}
+
+
+
+
+
+
+ 已添加({selectedColumns.length})
+
+
+
+
+
+
+
+ {selectedColumns.length === 0 ? (
+
+ 暂无已选指标
+
+ ) : (
+
+ {selectedColumns.map((colName, index) => {
+ const col = columnsData.find(c => c.name === colName);
+ return (
+
{
+ e.dataTransfer.setData('index', String(index));
+ }}
+ onDragOver={(e) => {
+ e.preventDefault();
+ }}
+ onDrop={(e) => {
+ const fromIndex = parseInt(e.dataTransfer.getData('index'));
+ const toIndex = index;
+ if (fromIndex !== toIndex) {
+ const newSelected = [...selectedColumns];
+ const [removed] = newSelected.splice(fromIndex, 1);
+ newSelected.splice(toIndex, 0, removed);
+ setSelectedColumns(newSelected);
+ }
+ }}
+ >
+ {col?.description || colName}
+
+ {index + 1}
+
+
+ );
+ })}
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default ConsumePage;
diff --git a/video-gen-admin/src/pages/Adminplatform.tsx b/video-gen-admin/src/pages/Adminplatform.tsx
new file mode 100644
index 00000000..1b1494df
--- /dev/null
+++ b/video-gen-admin/src/pages/Adminplatform.tsx
@@ -0,0 +1,157 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Button, Card, Space, Table, Tag, Typography, message, Modal, Input, Upload,
+} from 'antd';
+import {
+ HistoryOutlined, ReloadOutlined, PlusOutlined, UploadOutlined,
+} from '@ant-design/icons';
+import { getOperationLogs } from '../api';
+import { formatDate } from '../utils/formatDate';
+
+interface OperationLog {
+ id: string;
+ userId: string;
+ username: string;
+ action: string;
+ method: string;
+ path: string;
+ detail?: string;
+ ip?: string;
+ createdAt: string;
+}
+
+const METHOD_COLORS: Record = { POST: 'green', PUT: 'blue', DELETE: 'red' };
+
+const AdminPlatform: React.FC = () => {
+ const [logs, setLogs] = useState([]);
+ const [total, setTotal] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const [page, setPage] = useState(1);
+ const [showModal, setShowModal] = useState(false);
+ const [formData, setFormData] = useState({ title: '', description: '', image: '' });
+
+ const load = async (p?: number) => {
+ setLoading(true);
+ try {
+ const res = await getOperationLogs(p || page);
+ setLogs(res.items || []);
+ setTotal(res.total || 0);
+ } catch {
+ message.error('加载平台管理失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => { load(); }, []);
+
+ const columns = [
+ {
+ title: '操作人', dataIndex: 'username', width: 120,
+ render: (v: string) => {v},
+ },
+ {
+ title: '操作', dataIndex: 'action', width: 160,
+ render: (v: string) => {v},
+ },
+ {
+ title: '方法', dataIndex: 'method', width: 80,
+ render: (v: string) => {v},
+ },
+ {
+ title: '路径', dataIndex: 'path', width: 220, ellipsis: true,
+ render: (v: string) => {v},
+ },
+ {
+ title: '时间', dataIndex: 'createdAt', width: 160,
+ render: (v: string) => {formatDate(v)},
+ },
+ ];
+
+ return (
+
+
+
+
+
+ 平台管理
+
+
+ } onClick={() => setShowModal(true)}>新增
+ } onClick={() => load()}>刷新
+
+
+ `共 ${t} 条记录`,
+ onChange: (p) => { setPage(p); load(p); },
+ }}
+ scroll={{ x: 800 }}
+ />
+
+
+ {
+ message.success('新增成功');
+ setShowModal(false);
+ setFormData({ title: '', description: '', image: '' });
+ load();
+ }}
+ onCancel={() => {
+ setShowModal(false);
+ setFormData({ title: '', description: '', image: '' });
+ }}
+ okText="确认"
+ cancelText="取消"
+ >
+
+
+ 标题
+ setFormData(prev => ({ ...prev, title: e.target.value }))}
+ />
+
+
+ 描述
+ setFormData(prev => ({ ...prev, description: e.target.value }))}
+ rows={4}
+ />
+
+
+
封面图片
+
{
+ if (info.file.status === 'done') {
+ setFormData(prev => ({ ...prev, image: info.file.response?.url || '' }));
+ }
+ }}
+ >
+
+
+
+
+
+
+ );
+};
+
+export default AdminPlatform;
diff --git a/video-gen-app/.env b/video-gen-app/.env
index ea3cda13..302d39cd 100644
--- a/video-gen-app/.env
+++ b/video-gen-app/.env
@@ -1,5 +1,5 @@
-#VITE_API_BASE=http://localhost:8000
-VITE_API_BASE=http://ceshi.apiforeign.minzhong.cn
+VITE_API_BASE=http://192.168.120.17:8000
+# VITE_API_BASE=http://ceshi.apiforeign.minzhong.cn
VITE_USE_MOCK=false
# Encryption disabled for dev — enable in production
VITE_ENCRYPTION_KEY=
diff --git a/video-gen-app/src/api/index.ts b/video-gen-app/src/api/index.ts
index b15ffb10..79ff978e 100644
--- a/video-gen-app/src/api/index.ts
+++ b/video-gen-app/src/api/index.ts
@@ -577,4 +577,35 @@ export async function getArea(params?: GetAreaParams): Promise {
if (params?.parent_code) query.set('parent_code', params.parent_code);
return api.get(`/pre-test-template/getArea?${query.toString()}`);
}
-//
+
+// ── Upload Material ───────────────────────────────────────
+
+export interface AsyncBatchUploadTask {
+ advertiser_ids: string[];
+ resource_ids: string[];
+ oauth_id: string;
+}
+
+export interface AsyncBatchUploadParams {
+ tasks: AsyncBatchUploadTask[];
+}
+
+export async function asyncBatchUploadMaterial(params: AsyncBatchUploadParams): Promise {
+ return api.post('/upload-material/async-batch-upload', params);
+}
+
+// 获取上传历史
+// /api/upload-material/upload-history
+export interface UploadHistoryParams {
+ status?: string;
+ page?: number;
+ pageSize?: number;
+}
+export async function getUploadHistory(params: UploadHistoryParams): Promise {
+ const searchParams = new URLSearchParams();
+ if (params.status) searchParams.set('status', params.status);
+ if (params.page) searchParams.set('page', String(params.page));
+ if (params.pageSize) searchParams.set('page_size', String(params.pageSize));
+ const query = searchParams.toString();
+ return api.get(`/upload-material/upload-history${query ? `?${query}` : ''}`);
+}
diff --git a/video-gen-app/src/pages/AuthorizationPage.tsx b/video-gen-app/src/pages/AuthorizationPage.tsx
index c274477a..570919fd 100644
--- a/video-gen-app/src/pages/AuthorizationPage.tsx
+++ b/video-gen-app/src/pages/AuthorizationPage.tsx
@@ -65,7 +65,6 @@ const AuthorizationPage: React.FC = () => {
useEffect(() => {
loadOAuthList();
- handleCallback();
}, []);
const loadOAuthList = async (page = 1, pageSize = 10, params = searchParams) => {
@@ -97,22 +96,6 @@ const AuthorizationPage: React.FC = () => {
}
};
- const handleCallback = async () => {
- const params = new URLSearchParams(window.location.search);
- const authCode = params.get('auth_code');
- const state = params.get('state');
- if (authCode && state) {
- try {
- await juliang_callback({ auth_code: authCode, state });
- message.success('授权成功');
- window.history.replaceState({}, document.title, window.location.pathname);
- loadOAuthList();
- } catch (error) {
- message.error('授权回调失败');
- }
- }
- };
-
const handleAuthorize = () => {
setShowModal(true);
};
diff --git a/video-gen-app/src/pages/GeneratedRecord.tsx b/video-gen-app/src/pages/GeneratedRecord.tsx
index eebc84ba..b3d172bd 100644
--- a/video-gen-app/src/pages/GeneratedRecord.tsx
+++ b/video-gen-app/src/pages/GeneratedRecord.tsx
@@ -12,9 +12,9 @@ import {
XOutlined,
ClockCircleOutlined,
UploadOutlined,
- PlusOutlined,
+
} from '@ant-design/icons';
-import { gethistory, gethistoryItems, getDefaultPreTest, getOAuthList } from '../api';
+import { gethistory, gethistoryItems, getOAuthList, asyncBatchUploadMaterial, getUploadHistory } from '../api';
const { Search } = Input;
const { Text } = Typography;
@@ -35,30 +35,19 @@ const GeneratedRecord: React.FC = () => {
const videoRef = React.createRef();
const [selectedDate, setSelectedDate] = useState('');
- // 批量上传相关状态
- const [uploadModalVisible, setUploadModalVisible] = useState(false);
- const [uploadFiles, setUploadFiles] = useState([]);
- const [uploadProgress, setUploadProgress] = useState<{ [key: string]: number }>({});
const [uploading, setUploading] = useState(false);
// 上传配置弹窗相关状态
const [uploadConfigModalVisible, setUploadConfigModalVisible] = useState(false);
- const [preTestTemplates, setPreTestTemplates] = useState([
- { id: 'template_001', name: '前测模板A' },
- { id: 'template_002', name: '前测模板B' },
- { id: 'template_003', name: '前测模板C' },
- ]);
- const [preTestLoading, setPreTestLoading] = useState(false);
const [accountIdList, setAccountIdList] = useState<{
accountId: string;
- authStatus: 'pending' | 'authorized' | 'failed';
}[]>([]);
const [accountIdInput, setAccountIdInput] = useState('');
const [oauthList, setOauthList] = useState([]);
const [oauthLoading, setOauthLoading] = useState(false);
const [oauthTotal, setOauthTotal] = useState(0);
- const [selectedOauthItems, setSelectedOauthItems] = useState([]);
+ const [selectedOauthItems, setSelectedOauthItems] = useState<{ value: string; label: string } | undefined>(undefined);
const [oauthPage, setOauthPage] = useState(1);
const [oauthPageSize, setOauthPageSize] = useState(10);
const [oauthSelectOpen, setOauthSelectOpen] = useState(false);
@@ -69,6 +58,15 @@ const GeneratedRecord: React.FC = () => {
message: string;
}[]>([]);
+ // 上传任务历史弹窗相关状态
+ const [uploadHistoryModalVisible, setUploadHistoryModalVisible] = useState(false);
+ const [uploadHistoryList, setUploadHistoryList] = useState([]);
+ const [uploadHistoryTotal, setUploadHistoryTotal] = useState(0);
+ const [uploadHistoryPage, setUploadHistoryPage] = useState(1);
+ const [uploadHistoryPageSize, setUploadHistoryPageSize] = useState(10);
+ const [uploadHistoryLoading, setUploadHistoryLoading] = useState(false);
+ const [uploadHistoryStatus, setUploadHistoryStatus] = useState('');
+
// 多选相关状态
const [isSelectionMode, setIsSelectionMode] = useState(false);
const [selectedItems, setSelectedItems] = useState>(new Set());
@@ -573,58 +571,6 @@ const GeneratedRecord: React.FC = () => {
setPreviewVisible(false);
};
- // 批量上传相关函数
- const handleUploadChange = (info: any) => {
- // 过滤文件类型
- const validFiles = info.fileList.filter((file: any) => {
- const type = file.type.toLowerCase();
- return type.startsWith('image/') || type.startsWith('video/');
- });
-
- // 检查无效文件并提示
- const invalidFiles = info.fileList.filter((file: any) => {
- const type = file.type.toLowerCase();
- return !type.startsWith('image/') && !type.startsWith('video/');
- });
-
- if (invalidFiles.length > 0) {
- message.warning(`已过滤 ${invalidFiles.length} 个无效文件,仅支持图片和视频`);
- }
-
- setUploadFiles(validFiles);
- };
-
- const handleRemoveFile = (file: any) => {
- setUploadFiles(prev => prev.filter(f => f.uid !== file.uid));
- };
-
- const handleStartUpload = async () => {
- if (uploadFiles.length === 0) {
- message.warning('请先选择要上传的文件');
- return;
- }
- setUploading(true);
- // 模拟批量上传过程
- for (let i = 0; i < uploadFiles.length; i++) {
- const file = uploadFiles[i];
- setUploadProgress(prev => ({ ...prev, [file.uid]: 0 }));
-
- // 模拟上传进度
- for (let progress = 0; progress <= 100; progress += 10) {
- await new Promise(resolve => setTimeout(resolve, 100));
- setUploadProgress(prev => ({ ...prev, [file.uid]: progress }));
- }
- }
- // 上传完成
- await new Promise(resolve => setTimeout(resolve, 500));
- message.success(`成功上传 ${uploadFiles.length} 个文件`);
- setUploading(false);
- setUploadFiles([]);
- setUploadModalVisible(false);
- // 刷新页面数据
- setPagebreak(prev => ({ ...prev, page: 1 }));
- };
-
// 多选相关函数
const handleToggleSelect = (itemId: string) => {
setSelectedItems(prev => {
@@ -665,7 +611,6 @@ const GeneratedRecord: React.FC = () => {
try {
const res = await getOAuthList({ page, page_size: pageSize });
const data = res?.data || res;
- console.log(res);
setOauthList(data || []);
setOauthTotal(res.pagination.total || 0);
} catch (error) {
@@ -677,97 +622,79 @@ const GeneratedRecord: React.FC = () => {
}
};
- const handleStartBatchUpload = async () => {
- setUploading(true);
- const uploadProgressMap: { [key: string]: { status: 'pending' | 'uploading' | 'success' | 'error'; message: string } } = {};
-
- for (const itemId of selectedItems) {
- uploadProgressMap[itemId] = { status: 'pending', message: '' };
- }
- setBatchUploadProgress(Object.entries(uploadProgressMap).map(([itemId, value]) => ({ itemId, ...value })));
-
+ const loadUploadHistory = async () => {
+ setUploadHistoryLoading(true);
try {
+ const res = await getUploadHistory({
+ status: uploadHistoryStatus || undefined,
+ page: uploadHistoryPage,
+ pageSize: uploadHistoryPageSize,
+ });
+ const data = res?.data || res;
+ setUploadHistoryList(data || []);
+ setUploadHistoryTotal(res.pagination?.total || res.total || 0);
+ } catch (error) {
+ console.error('加载上传历史失败:', error);
+ setUploadHistoryList([]);
+ setUploadHistoryTotal(0);
+ } finally {
+ setUploadHistoryLoading(false);
+ }
+ };
+
+ const handleOpenUploadHistory = () => {
+ setUploadHistoryModalVisible(true);
+ setUploadHistoryPage(1);
+ setUploadHistoryStatus('');
+ loadUploadHistory();
+ };
+
+ const handleUploadHistorySearch = () => {
+ setUploadHistoryPage(1);
+ loadUploadHistory();
+ };
+
+ const handleUploadHistoryPageChange = (page: number, pageSize: number) => {
+ setUploadHistoryPage(page);
+ setUploadHistoryPageSize(pageSize);
+ loadUploadHistory();
+ };
+
+ const handleStartBatchUpload = async () => {
+ if (!selectedOauthItems) {
+ message.warning('请先选择授权账户');
+ return;
+ }
+ if (selectedItems.size === 0) {
+ message.warning('请先选择要上传的媒体');
+ return;
+ }
+ setUploading(true);
+ try {
+ const tasks: {
+ advertiser_ids: string[];
+ resource_ids: string[];
+ oauth_id: string;
+ type: string;
+ }[] = [];
+ const advertiserIds = accountIdList.map(account => account.accountId);
+ const type = filterType === 'project' ? 'generation_record' : 'chat_task';
for (const itemId of selectedItems) {
- let item: any = null;
- let mediaUrl = '';
- let mediaType = '';
-
- for (const group of recordlist) {
- const found = group.items.find((i: any) => i.id === itemId);
- if (found) {
- item = found;
- break;
- }
- }
-
- if (!item) continue;
-
- if (filterMedia === 'video' && item.videoUrl) {
- mediaUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${item.videoUrl}`;
- mediaType = 'video';
- } else if (filterMedia === 'image' && item.imageUrl) {
- mediaUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${item.imageUrl}`;
- mediaType = 'image';
- } else {
- continue;
- }
-
- uploadProgressMap[itemId] = { status: 'uploading', message: '正在上传...' };
- setBatchUploadProgress(Object.entries(uploadProgressMap).map(([itemId, value]) => ({ itemId, ...value })));
-
- try {
- const response = await fetch(mediaUrl);
- if (!response.ok) throw new Error('下载失败');
- const blob = await response.blob();
- const file = new File([blob], item.title || `media_${itemId}`, {
- type: mediaType === 'video' ? 'video/mp4' : 'image/jpeg'
- });
-
- for (const accountItem of accountIdList) {
- const form = new FormData();
- form.append('file', file);
- form.append('media_type', mediaType);
- form.append('original_id', itemId);
- form.append('account_id', accountItem.accountId);
-
- const token = localStorage.getItem('auth_token');
- const uploadResponse = await fetch(
- `${import.meta.env.VITE_API_BASE || 'http://localhost:8000'}/api/generation-records/batch-upload`,
- {
- method: 'POST',
- headers: token ? { Authorization: `Bearer ${token}` } : {},
- body: form,
- }
- );
-
- const result = await uploadResponse.json();
- if (!uploadResponse.ok) {
- throw new Error(result.message || '上传失败');
- }
- }
-
- uploadProgressMap[itemId] = { status: 'success', message: '上传成功' };
- } catch (error: any) {
- console.error(`上传失败: ${itemId}`, error);
- uploadProgressMap[itemId] = { status: 'error', message: error.message || '上传失败' };
- }
- setBatchUploadProgress(Object.entries(uploadProgressMap).map(([itemId, value]) => ({ itemId, ...value })));
+ tasks.push({
+ advertiser_ids: advertiserIds,
+ resource_ids: [itemId],
+ oauth_id: selectedOauthItems.value,
+ type,
+ });
}
-
- const successCount = Object.values(uploadProgressMap).filter(p => p.status === 'success').length;
- const failCount = Object.values(uploadProgressMap).filter(p => p.status === 'error').length;
-
- if (failCount === 0) {
- message.success(`成功上传 ${successCount} 个文件`);
- } else {
- message.warning(`上传完成:成功 ${successCount} 个,失败 ${failCount} 个`);
- }
-
+ // console.log(tasks);
+ await asyncBatchUploadMaterial({ tasks });
+ message.success(`已提交 ${tasks.length} 个上传任务,后台异步处理中`);
setIsSelectionMode(false);
setSelectedItems(new Set());
- } catch (error) {
- message.error('批量上传失败');
- console.error(error);
+ } catch (error: any) {
+ console.error('批量上传失败:', error);
+ message.error(error.message || '批量上传失败');
} finally {
setUploading(false);
}
@@ -775,13 +702,7 @@ const GeneratedRecord: React.FC = () => {
// 日期选择器变化处理函数
const handleDateChange = (dateString: string) => {
- console.log(dateString);
-
- console.log('qweqwe');
-
setSelectedDate(dateString);
-
-
};
useEffect(() => {
@@ -809,16 +730,11 @@ const GeneratedRecord: React.FC = () => {
total: res.total,
page: res.page,
}];
- console.log('data', data);
if (recordList && recordList[0].items.length > 0) {
- setRecordList(recordList);
-
+ setRecordList(recordList);
}else{
setRecordList([]);
}
-
- console.log('recordList', recordList);
-
}).catch((err) => {
}).finally(() => {
setLoading(false);
@@ -835,15 +751,12 @@ const GeneratedRecord: React.FC = () => {
data.forEach(group => {
group.page = 1;
});
- console.log('qweqweqwe', data);
-
// 如果是第一页,替换数据;否则追加数据
if (Pagebreak.page === 1) {
setRecordList(data);
} else {
setRecordList(prev => [...prev, ...data]);
}
-
setTotalnumber(res?.totalDays || 0);
}).catch((err) => {
if (Pagebreak.page === 1) {
@@ -857,22 +770,6 @@ const GeneratedRecord: React.FC = () => {
}, [filterType, filterMedia, Pagebreak.page, selectedDate]);
- useEffect(() => {
- setPreTestLoading(true);
- getDefaultPreTest().then((res: any) => {
- const data = res?.data || res;
- setPreTestTemplates(Array.isArray(data) ? data : []);
- }).catch(() => {
- setPreTestTemplates([
- { id: 'template_001', name: '前测模板A' },
- { id: 'template_002', name: '前测模板B' },
- { id: 'template_003', name: '前测模板C' },
- ]);
- }).finally(() => {
- setPreTestLoading(false);
- });
- }, []);
-
// 加载更多
const handleLoadMore = () => {
if (loading) return;
@@ -935,16 +832,6 @@ const GeneratedRecord: React.FC = () => {
return (
- {/* Header */}
- {/*
-
- 生成历史
-
-
- 查看所有生成的视频和图片记录
-
-
*/}
-
{/* 操作栏:筛选 + 上传按钮 */}
{
>
批量上传
+
)}
@@ -1069,58 +957,84 @@ const GeneratedRecord: React.FC = () => {
background: '#fff',
border: '1px solid #f0f0f5',
}}>
- 媒体类型:
-
-
-
- handleDateChange(dateString || '')}
- format="YYYY-MM-DD"
- style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
- placeholder="选择日期"
- />
- {selectedDate && (
+
+
媒体类型:
+
+
- )}
-
+
+
handleDateChange(dateString || '')}
+ format="YYYY-MM-DD"
+ style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
+ placeholder="选择日期"
+ />
+ {selectedDate && (
+
+ )}
+
+
+ }
+ onClick={handleOpenUploadHistory}
+ style={{
+ borderRadius: 8,
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ border: 'none',
+ color: '#ffffff',
+ fontWeight: 600,
+ boxShadow: '0 4px 15px rgba(102, 126, 234, 0.4)',
+ transition: 'all 0.3s ease',
+ }}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.transform = 'translateY(-2px)';
+ e.currentTarget.style.boxShadow = '0 6px 20px rgba(102, 126, 234, 0.6)';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.4)';
+ }}
+ >
+ 查询上传任务历史
+
{/* Content area */}
@@ -1206,147 +1120,6 @@ const GeneratedRecord: React.FC = () => {
)}
- {/* 批量上传弹窗 */}
- {
- setUploadModalVisible(false);
- setUploadFiles([]);
- }}
- footer={null}
- width={600}
- >
-
- {/* 上传区域 */}
-
false} // 手动控制上传
- accept="image/*,video/*"
- listType="picture-card"
- onRemove={handleRemoveFile}
- >
-
-
-
- {/* 上传列表和进度 */}
- {uploadFiles.length > 0 && (
-
-
- 已选择 {uploadFiles.length} 个文件
-
-
- {uploadFiles.map((file) => (
-
-
- {file.type?.startsWith('image/') ? (
-
- ) : file.type?.startsWith('video/') ? (
-
- ) : (
-
- )}
-
-
-
- {file.name}
-
- {uploadProgress[file.uid] !== undefined && (
-
- )}
-
-
}
- onClick={() => handleRemoveFile(file)}
- style={{
- background: 'transparent',
- border: 'none',
- color: '#999',
- }}
- />
-
- ))}
-
-
- )}
-
- {/* 操作按钮 */}
-
-
-
-
-
-
-
{/* 上传配置弹窗 */}
{
setUploadConfigModalVisible(false);
setAccountIdList([]);
setAccountIdInput('');
- setSelectedOauthItems([]);
+ setSelectedOauthItems(undefined);
setBatchUploadProgress([]);
}}
footer={null}
@@ -1363,32 +1136,12 @@ const GeneratedRecord: React.FC = () => {
>
- 选择授权账户(可多选)
+ 选择授权账户
-