素材云批量提交
This commit is contained in:
@@ -431,7 +431,10 @@ export async function getOpenTypeList(params?: {
|
||||
page_size?: number;
|
||||
type_name?: string;
|
||||
open_type?: number;
|
||||
}): Promise<{ total: number; items: any[] }> {
|
||||
}): Promise<{
|
||||
pagination: any;
|
||||
data: never[];
|
||||
}> {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.page) q.set('page', String(params.page));
|
||||
if (params?.page_size) q.set('page_size', String(params.page_size));
|
||||
|
||||
@@ -10,8 +10,8 @@ import { formatDate } from '../utils/formatDate';
|
||||
|
||||
interface OpenType {
|
||||
id: string;
|
||||
open_type: number;
|
||||
type_name: string;
|
||||
openType: number;
|
||||
typeName: string;
|
||||
description: string;
|
||||
thumb?: string;
|
||||
createdAt: string;
|
||||
@@ -49,6 +49,8 @@ const AdminPlatform: React.FC = () => {
|
||||
const [detailModalVisible, setDetailModalVisible] = useState(false);
|
||||
const [updateModalVisible, setUpdateModalVisible] = useState(false);
|
||||
const [currentOpenType, setCurrentOpenType] = useState<OpenType | null>(null);
|
||||
const [createThumbUrl, setCreateThumbUrl] = useState('');
|
||||
const [updateThumbUrl, setUpdateThumbUrl] = useState('');
|
||||
|
||||
const [createForm] = Form.useForm();
|
||||
const [updateForm] = Form.useForm();
|
||||
@@ -60,8 +62,8 @@ const AdminPlatform: React.FC = () => {
|
||||
page: p || page,
|
||||
page_size: ps || pageSize,
|
||||
});
|
||||
setOpenTypes(res.items || []);
|
||||
setTotal(res.total || 0);
|
||||
setOpenTypes(res.data || []);
|
||||
setTotal(res.pagination.total || 0);
|
||||
} catch {
|
||||
message.error('加载开户方式列表失败');
|
||||
} finally {
|
||||
@@ -76,16 +78,16 @@ const AdminPlatform: React.FC = () => {
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
const values = await createForm.validateFields();
|
||||
console.log(values);
|
||||
await createOpenType({
|
||||
open_type: values.open_type,
|
||||
type_name: values.type_name,
|
||||
description: values.description,
|
||||
thumb: values.thumb,
|
||||
thumb: createThumbUrl,
|
||||
});
|
||||
message.success('创建成功');
|
||||
setCreateModalVisible(false);
|
||||
createForm.resetFields();
|
||||
setCreateThumbUrl('');
|
||||
load();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '创建失败');
|
||||
@@ -95,7 +97,7 @@ const AdminPlatform: React.FC = () => {
|
||||
const handleDetail = async (id: string) => {
|
||||
try {
|
||||
const openType = await getOpenType(id);
|
||||
setCurrentOpenType(openType);
|
||||
setCurrentOpenType(openType.data || {});
|
||||
setDetailModalVisible(true);
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '获取详情失败');
|
||||
@@ -105,13 +107,8 @@ const AdminPlatform: React.FC = () => {
|
||||
const handleUpdate = async (id: string) => {
|
||||
try {
|
||||
const openType = await getOpenType(id);
|
||||
setCurrentOpenType(openType);
|
||||
updateForm.setFieldsValue({
|
||||
open_type: openType.open_type,
|
||||
type_name: openType.type_name,
|
||||
description: openType.description,
|
||||
thumb: openType.thumb,
|
||||
});
|
||||
setCurrentOpenType(openType.data || {});
|
||||
setUpdateThumbUrl(openType.thumb || '');
|
||||
setUpdateModalVisible(true);
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '获取详情失败');
|
||||
@@ -126,12 +123,13 @@ const AdminPlatform: React.FC = () => {
|
||||
open_type: values.open_type,
|
||||
type_name: values.type_name,
|
||||
description: values.description,
|
||||
thumb: values.thumb,
|
||||
thumb: updateThumbUrl,
|
||||
});
|
||||
message.success('更新成功');
|
||||
setUpdateModalVisible(false);
|
||||
updateForm.resetFields();
|
||||
setCurrentOpenType(null);
|
||||
setUpdateThumbUrl('');
|
||||
load();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '更新失败');
|
||||
@@ -166,13 +164,13 @@ const AdminPlatform: React.FC = () => {
|
||||
},
|
||||
{
|
||||
title: '开户类型',
|
||||
dataIndex: 'open_type',
|
||||
dataIndex: 'openType',
|
||||
width: 100,
|
||||
render: (v: number) => <Typography.Text>{v}</Typography.Text>,
|
||||
},
|
||||
{
|
||||
title: '类型名称',
|
||||
dataIndex: 'type_name',
|
||||
dataIndex: 'typeName',
|
||||
width: 150,
|
||||
render: (v: string) => <Typography.Text strong>{v}</Typography.Text>,
|
||||
},
|
||||
@@ -191,9 +189,12 @@ const AdminPlatform: React.FC = () => {
|
||||
title: '缩略图',
|
||||
dataIndex: 'thumb',
|
||||
width: 120,
|
||||
render: (v: string) => (
|
||||
v ? <img src={v} alt="thumb" style={{ width: 80, height: 60, objectFit: 'cover' }} /> : '-'
|
||||
),
|
||||
render: (v: string) => {
|
||||
if (!v) return '-';
|
||||
const baseUrl = import.meta.env.VITE_API_BASE || 'http://localhost:8000';
|
||||
const fullUrl = v.startsWith('http') ? v : `${baseUrl}${v}`;
|
||||
return <img src={fullUrl} alt="thumb" style={{ width: 80, height: 60, objectFit: 'cover' }} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
@@ -291,7 +292,9 @@ const AdminPlatform: React.FC = () => {
|
||||
onCancel={() => {
|
||||
setCreateModalVisible(false);
|
||||
createForm.resetFields();
|
||||
setCreateThumbUrl('');
|
||||
}}
|
||||
mask={{ closable: false }}
|
||||
okText="创建"
|
||||
cancelText="取消"
|
||||
width={600}
|
||||
@@ -326,28 +329,28 @@ const AdminPlatform: React.FC = () => {
|
||||
>
|
||||
<Input.TextArea placeholder="请输入描述" rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="thumb"
|
||||
label="缩略图"
|
||||
>
|
||||
<Form.Item label="缩略图">
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
fileList={createThumbUrl ? [{ uid: '1', name: 'thumb', status: 'done', url: createThumbUrl }] : []}
|
||||
customRequest={async ({ file, onSuccess, onError }) => {
|
||||
try {
|
||||
const uploadFile = assertUploadFile(file);
|
||||
const res = await uploadImage(uploadFile);
|
||||
console.log(res);
|
||||
createForm.setFieldsValue({ thumb: res.url });
|
||||
setCreateThumbUrl(res.url);
|
||||
onSuccess?.({ url: res.url });
|
||||
} catch (e) {
|
||||
onError?.(normalizeUploadError(e));
|
||||
}
|
||||
}}
|
||||
onRemove={() => setCreateThumbUrl('')}
|
||||
>
|
||||
<div>
|
||||
<UploadOutlined style={{ fontSize: 24, color: '#999' }} />
|
||||
<div style={{ marginTop: 8 }}>上传图片</div>
|
||||
</div>
|
||||
{!createThumbUrl && (
|
||||
<div>
|
||||
<UploadOutlined style={{ fontSize: 24, color: '#999' }} />
|
||||
<div style={{ marginTop: 8 }}>上传图片</div>
|
||||
</div>
|
||||
)}
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
@@ -356,32 +359,68 @@ const AdminPlatform: React.FC = () => {
|
||||
<Modal
|
||||
title="开户方式详情"
|
||||
open={detailModalVisible}
|
||||
onOk={() => setDetailModalVisible(false)}
|
||||
onCancel={() => {
|
||||
setDetailModalVisible(false);
|
||||
setCurrentOpenType(null);
|
||||
}}
|
||||
mask={{ closable: false }}
|
||||
okText="关闭"
|
||||
cancelText="取消"
|
||||
width={600}
|
||||
width={520}
|
||||
>
|
||||
{currentOpenType && (
|
||||
<div style={{ lineHeight: '2' }}>
|
||||
<p><strong>ID:</strong> {currentOpenType.id}</p>
|
||||
<p><strong>开户类型:</strong> {currentOpenType.open_type}</p>
|
||||
<p><strong>类型名称:</strong> {currentOpenType.type_name}</p>
|
||||
<p><strong>描述:</strong> {currentOpenType.description}</p>
|
||||
<p>
|
||||
<strong>缩略图:</strong>{' '}
|
||||
{currentOpenType.thumb ? (
|
||||
<img
|
||||
src={currentOpenType.thumb}
|
||||
alt="thumb"
|
||||
style={{ width: 120, height: 80, objectFit: 'cover' }}
|
||||
/>
|
||||
) : '-'}
|
||||
</p>
|
||||
<p><strong>创建时间:</strong> {formatDate(currentOpenType.createdAt)}</p>
|
||||
<p><strong>更新时间:</strong> {formatDate(currentOpenType.updatedAt)}</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 16, background: '#f8fafc', borderRadius: 8 }}>
|
||||
<div style={{
|
||||
width: 80, height: 60, borderRadius: 6, overflow: 'hidden',
|
||||
background: currentOpenType.thumb ? 'transparent' : '#e2e8f0',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}>
|
||||
{currentOpenType.thumb ? (
|
||||
<img
|
||||
src={currentOpenType.thumb.startsWith('http') ? currentOpenType.thumb : `${import.meta.env.VITE_API_BASE || 'http://localhost:8000'}${currentOpenType.thumb}`}
|
||||
alt="thumb"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
) : (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>暂无图片</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 16, fontWeight: 600 }}>{currentOpenType.typeName}</div>
|
||||
<div style={{ fontSize: 12, color: '#64748b', marginTop: 4 }}>开户类型: {currentOpenType.openType}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80, flexShrink: 0 }}>ID</Typography.Text>
|
||||
<Typography.Text>{currentOpenType.id}</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80, flexShrink: 0 }}>开户类型</Typography.Text>
|
||||
<Typography.Text>{currentOpenType.openType}</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80, flexShrink: 0 }}>类型名称</Typography.Text>
|
||||
<Typography.Text>{currentOpenType.typeName}</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80 }}>描述</Typography.Text>
|
||||
<div style={{ padding: 12, background: '#f8fafc', borderRadius: 6, fontSize: 13, lineHeight: 1.6 }}>
|
||||
{currentOpenType.description || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80, flexShrink: 0 }}>创建时间</Typography.Text>
|
||||
<Typography.Text>{formatDate(currentOpenType.createdAt)}</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Typography.Text type="secondary" style={{ width: 80, flexShrink: 0 }}>更新时间</Typography.Text>
|
||||
<Typography.Text>{formatDate(currentOpenType.updatedAt)}</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
@@ -395,6 +434,16 @@ const AdminPlatform: React.FC = () => {
|
||||
updateForm.resetFields();
|
||||
setCurrentOpenType(null);
|
||||
}}
|
||||
afterOpenChange={(open) => {
|
||||
if (open && currentOpenType) {
|
||||
updateForm.setFieldsValue({
|
||||
open_type: currentOpenType.openType,
|
||||
type_name: currentOpenType.typeName,
|
||||
description: currentOpenType.description,
|
||||
});
|
||||
}
|
||||
}}
|
||||
mask={{ closable: false }}
|
||||
okText="更新"
|
||||
cancelText="取消"
|
||||
width={600}
|
||||
@@ -424,29 +473,23 @@ const AdminPlatform: React.FC = () => {
|
||||
>
|
||||
<Input.TextArea placeholder="请输入描述" rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="thumb"
|
||||
label="缩略图"
|
||||
>
|
||||
<Form.Item label="缩略图">
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
defaultFileList={
|
||||
currentOpenType?.thumb
|
||||
? [{ uid: '1', name: 'thumb', status: 'done', url: currentOpenType.thumb }]
|
||||
: []
|
||||
}
|
||||
fileList={updateThumbUrl ? [{ uid: '1', name: 'thumb', status: 'done', url: updateThumbUrl }] : []}
|
||||
customRequest={async ({ file, onSuccess, onError }) => {
|
||||
try {
|
||||
const uploadFile = assertUploadFile(file);
|
||||
const res = await uploadImage(uploadFile);
|
||||
updateForm.setFieldsValue({ thumb: res.url });
|
||||
setUpdateThumbUrl(res.url);
|
||||
onSuccess?.({ url: res.url });
|
||||
} catch (e) {
|
||||
onError?.(normalizeUploadError(e));
|
||||
}
|
||||
}}
|
||||
onRemove={() => setUpdateThumbUrl('')}
|
||||
>
|
||||
{!updateForm.getFieldValue('thumb') && (
|
||||
{!updateThumbUrl && (
|
||||
<div>
|
||||
<UploadOutlined style={{ fontSize: 24, color: '#999' }} />
|
||||
<div style={{ marginTop: 8 }}>上传图片</div>
|
||||
|
||||
+2
-2
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-Dm7vTAAt.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CUOi61z4.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -39,21 +39,21 @@ const GeneratedRecord: React.FC = () => {
|
||||
|
||||
// 上传配置弹窗相关状态
|
||||
const [uploadConfigModalVisible, setUploadConfigModalVisible] = useState(false);
|
||||
const [accountIdList, setAccountIdList] = useState<{
|
||||
const [accountIdLists, setAccountIdLists] = useState<{
|
||||
accountId: string;
|
||||
}[]>([]);
|
||||
const [accountIdInput, setAccountIdInput] = useState('');
|
||||
}[][]>([[]]);
|
||||
const [accountIdInputs, setAccountIdInputs] = useState<string[]>(['']);
|
||||
|
||||
const [oauthList, setOauthList] = useState<any[]>([]);
|
||||
const [oauthLoading, setOauthLoading] = useState(false);
|
||||
const [oauthTotal, setOauthTotal] = useState(0);
|
||||
const [selectedOauthItems, setSelectedOauthItems] = useState<{ value: string; label: string } | undefined>(undefined);
|
||||
const [selectedOauthItems, setSelectedOauthItems] = useState<({ value: string; label: string } | undefined)[]>([undefined]);
|
||||
const [materialFileNames, setMaterialFileNames] = useState<Map<string, string>>(new Map());
|
||||
const [unifiedFileName, setUnifiedFileName] = useState('');
|
||||
const updateFilenameDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [oauthPage, setOauthPage] = useState(1);
|
||||
const [oauthPageSize, setOauthPageSize] = useState(10);
|
||||
const [oauthSelectOpen, setOauthSelectOpen] = useState(false);
|
||||
const [oauthSelectOpens, setOauthSelectOpens] = useState<boolean[]>([false]);
|
||||
|
||||
// 上传任务历史弹窗相关状态
|
||||
const [uploadHistoryModalVisible, setUploadHistoryModalVisible] = useState(false);
|
||||
@@ -682,8 +682,21 @@ const GeneratedRecord: React.FC = () => {
|
||||
message.warning('请先选择要上传的媒体');
|
||||
return;
|
||||
}
|
||||
setAccountIdList([]);
|
||||
setAccountIdInput('');
|
||||
setAccountIdLists([[]]);
|
||||
setAccountIdInputs(['']);
|
||||
setSelectedOauthItems([undefined]);
|
||||
setUploadConfigModalVisible(true);
|
||||
};
|
||||
|
||||
const handleSinglePushToMedia = () => {
|
||||
if (!previewItem) return;
|
||||
const resourceId = getItemResourceId(previewItem);
|
||||
setSelectedItems(new Set([resourceId]));
|
||||
setAccountIdLists([[]]);
|
||||
setAccountIdInputs(['']);
|
||||
setSelectedOauthItems([undefined]);
|
||||
setMaterialFileNames(new Map());
|
||||
setUnifiedFileName('');
|
||||
setUploadConfigModalVisible(true);
|
||||
};
|
||||
|
||||
@@ -743,7 +756,8 @@ const GeneratedRecord: React.FC = () => {
|
||||
|
||||
// 批量上传素材
|
||||
const handleStartBatchUpload = async () => {
|
||||
if (!selectedOauthItems) {
|
||||
const validOauthItems = selectedOauthItems.filter(item => item !== undefined);
|
||||
if (validOauthItems.length === 0) {
|
||||
message.warning('请先选择授权账户');
|
||||
return;
|
||||
}
|
||||
@@ -759,42 +773,34 @@ const GeneratedRecord: React.FC = () => {
|
||||
oauth_id: string;
|
||||
source_model: string;
|
||||
}[] = [];
|
||||
const advertiserIds = accountIdList.map(account => account.accountId);
|
||||
// 创建itemId到item对象的映射
|
||||
const itemMap = new Map<string, any>();
|
||||
recordlist.forEach((group: any) => {
|
||||
group.items.forEach((item: any) => {
|
||||
const resourceId = getItemResourceId(item);
|
||||
itemMap.set(resourceId, item);
|
||||
});
|
||||
});
|
||||
const resourceIds = Array.from(selectedItems);
|
||||
|
||||
for (const itemId of selectedItems) {
|
||||
const item = itemMap.get(itemId);
|
||||
// 根据item是否有generatedResourceId来决定source_model
|
||||
let sourceModel: string;
|
||||
if (item && hasGeneratedResourceId(item)) {
|
||||
sourceModel = 'generated_resources';
|
||||
} else {
|
||||
sourceModel = filterType === 'project' ? 'generation_records' : 'chat_generation_tasks';
|
||||
for (let i = 0; i < validOauthItems.length; i++) {
|
||||
const oauthItem = validOauthItems[i];
|
||||
const advertiserIds = accountIdLists[i]?.map(account => account.accountId) || [];
|
||||
|
||||
if (advertiserIds.length === 0) {
|
||||
message.warning(`第 ${i + 1} 组授权账户未设置账户ID,已跳过`);
|
||||
continue;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
advertiser_ids: advertiserIds,
|
||||
resource_ids: [itemId],
|
||||
oauth_id: selectedOauthItems.value,
|
||||
source_model: sourceModel,
|
||||
resource_ids: resourceIds,
|
||||
oauth_id: oauthItem.value,
|
||||
source_model: filterType === 'project' ? 'generation_records' : 'chat_generation_tasks',
|
||||
});
|
||||
}
|
||||
|
||||
await asyncBatchUploadMaterial({ tasks });
|
||||
message.success(`已提交 ${tasks.length} 个上传任务,后台异步处理中`);
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
// 关闭弹窗并清理状态
|
||||
setUploadConfigModalVisible(false);
|
||||
setAccountIdList([]);
|
||||
setAccountIdInput('');
|
||||
setSelectedOauthItems(undefined);
|
||||
setAccountIdLists([[]]);
|
||||
setAccountIdInputs(['']);
|
||||
setSelectedOauthItems([undefined]);
|
||||
setMaterialFileNames(new Map());
|
||||
setUnifiedFileName('');
|
||||
} catch (error: any) {
|
||||
@@ -1323,13 +1329,13 @@ const GeneratedRecord: React.FC = () => {
|
||||
|
||||
{/* 上传配置弹窗 */}
|
||||
<Modal
|
||||
title="批量上传配置"
|
||||
title={selectedItems.size === 1 ? '上传配置' : '批量上传配置'}
|
||||
open={uploadConfigModalVisible}
|
||||
onCancel={() => {
|
||||
setUploadConfigModalVisible(false);
|
||||
setAccountIdList([]);
|
||||
setAccountIdInput('');
|
||||
setSelectedOauthItems(undefined);
|
||||
setAccountIdLists([[]]);
|
||||
setAccountIdInputs(['']);
|
||||
setSelectedOauthItems([undefined]);
|
||||
setMaterialFileNames(new Map());
|
||||
setUnifiedFileName('');
|
||||
}}
|
||||
@@ -1457,149 +1463,202 @@ const GeneratedRecord: React.FC = () => {
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block' }}>
|
||||
选择授权账户
|
||||
</Typography.Text>
|
||||
<Select
|
||||
value={selectedOauthItems}
|
||||
onChange={(value) => {
|
||||
setSelectedOauthItems(value as { value: string; label: string } | undefined);
|
||||
}}
|
||||
placeholder="点击选择授权账户"
|
||||
style={{ width: '100%', marginBottom: 16, borderRadius: 8 }}
|
||||
popupRender={() => (
|
||||
<div style={{ padding: 8, width: 800, maxHeight: 500, overflow: 'auto' }}>
|
||||
<Table
|
||||
dataSource={oauthList}
|
||||
columns={[
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权账户名称',
|
||||
dataIndex: 'accountName',
|
||||
key: 'accountName',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权应用ID',
|
||||
dataIndex: 'appid',
|
||||
key: 'appid',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权用户ID',
|
||||
dataIndex: 'accountUserid',
|
||||
key: 'accountUserid',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权账户角色',
|
||||
dataIndex: 'accountRole',
|
||||
key: 'accountRole',
|
||||
width: 160,
|
||||
render: (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
ADVERTISER: '客户',
|
||||
CUSTOMER_ADMIN: '普通版工作台-管理员',
|
||||
CUSTOMER_OPERATOR: '普通版工作台-协作者',
|
||||
AGENT: '代理商',
|
||||
CHILD_AGENT: '二级代理商',
|
||||
PLATFORM_ROLE_STAR: '星图账户',
|
||||
PLATFORM_ROLE_SHOP_ACCOUNT: '抖音店铺账户',
|
||||
PLATFORM_ROLE_QIANCHUAN_AGENT: '千川代理商',
|
||||
PLATFORM_ROLE_STAR_AGENT: '星图代理商',
|
||||
PLATFORM_ROLE_AWEME: '抖音号',
|
||||
PLATFORM_ROLE_STAR_MCN: '星图MCN机构',
|
||||
PLATFORM_ROLE_STAR_ISV: '星图服务商',
|
||||
AGENT_SYSTEM_ACCOUNT: '代理商系统账户',
|
||||
PLATFORM_ROLE_LOCAL_AGENT: '本地推代理商',
|
||||
PLATFORM_ROLE_YUNTU_BRAND_ISV_ADMIN: '云图品牌服务商管理员',
|
||||
PLATFORM_ROLE_LIFE: '抖音来客账户',
|
||||
PLATFORM_ROLE_ENTERPRISE_BP_ADMIN: '升级版工作台管理员',
|
||||
PLATFORM_ROLE_ENTERPRISE_BP_OPERATOR: '升级版工作台协作者',
|
||||
};
|
||||
return roleMap[role] || role;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '授权账户用户名',
|
||||
dataIndex: 'accountUsername',
|
||||
key: 'accountUsername',
|
||||
width: 120,
|
||||
render: (text: string) => <span style={{ color: text ? '#1e293b' : '#94a3b8' }}>{text || '-'}</span>,
|
||||
},
|
||||
]}
|
||||
loading={oauthLoading}
|
||||
pagination={{
|
||||
current: oauthPage,
|
||||
pageSize: oauthPageSize,
|
||||
total: oauthTotal,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
onChange: (page, size) => {
|
||||
setOauthPage(page);
|
||||
setOauthPageSize(size);
|
||||
loadOAuthList(page, size);
|
||||
},
|
||||
}}
|
||||
rowKey="id"
|
||||
size="small"
|
||||
scroll={{ x: 'max-content' }}
|
||||
onRow={(record) => ({
|
||||
onClick: () => {
|
||||
const id = String(record.id);
|
||||
setSelectedOauthItems({ value: id, label: String(record.accountUserid) });
|
||||
setOauthSelectOpen(false);
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
backgroundColor: selectedOauthItems?.value === String(record.id) ? '#e6f7ff' : undefined,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{selectedOauthItems.map((oauthItem, index) => (
|
||||
<div key={index} style={{ marginBottom: 16, padding: 12, border: '1px solid #e2e8f0', borderRadius: 8 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569' }}>
|
||||
授权账户 {index + 1}
|
||||
</Typography.Text>
|
||||
{selectedOauthItems.length > 1 && (
|
||||
<Button
|
||||
type="text"
|
||||
danger
|
||||
onClick={() => {
|
||||
const newOauthItems = [...selectedOauthItems];
|
||||
const newAccountIdInputs = [...accountIdInputs];
|
||||
const newAccountIdLists = [...accountIdLists];
|
||||
const newOauthSelectOpens = [...oauthSelectOpens];
|
||||
newOauthItems.splice(index, 1);
|
||||
newAccountIdInputs.splice(index, 1);
|
||||
newAccountIdLists.splice(index, 1);
|
||||
newOauthSelectOpens.splice(index, 1);
|
||||
setSelectedOauthItems(newOauthItems);
|
||||
setAccountIdInputs(newAccountIdInputs);
|
||||
setAccountIdLists(newAccountIdLists);
|
||||
setOauthSelectOpens(newOauthSelectOpens);
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
open={oauthSelectOpen}
|
||||
onOpenChange={(open) => {
|
||||
setOauthSelectOpen(open);
|
||||
if (open) {
|
||||
loadOAuthList(1, oauthPageSize);
|
||||
}
|
||||
}}
|
||||
labelInValue
|
||||
fieldNames={{ label: 'accountUserid', value: 'id' }}
|
||||
/>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block', marginTop: 16 }}>
|
||||
粘贴账户ID(每行一个或用逗号分隔)
|
||||
</Typography.Text>
|
||||
<Input.TextArea
|
||||
value={accountIdInput}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setAccountIdInput(value);
|
||||
const ids = value.split(/[\n,]/)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
const uniqueIds = [...new Set(ids)];
|
||||
const textAccounts = uniqueIds.map(id => ({ accountId: id }));
|
||||
const seen = new Set<string>();
|
||||
const finalAccounts = textAccounts.filter(a => {
|
||||
if (seen.has(a.accountId)) return false;
|
||||
seen.add(a.accountId);
|
||||
return true;
|
||||
});
|
||||
setAccountIdList(finalAccounts);
|
||||
}}
|
||||
placeholder="粘贴账户ID,每行一个或用逗号分隔,例如:
|
||||
<Select
|
||||
value={oauthItem}
|
||||
onChange={(value) => {
|
||||
const newOauthItems = [...selectedOauthItems];
|
||||
newOauthItems[index] = value as { value: string; label: string } | undefined;
|
||||
setSelectedOauthItems(newOauthItems);
|
||||
}}
|
||||
placeholder="点击选择授权账户"
|
||||
style={{ width: '100%', marginBottom: 12, borderRadius: 8 }}
|
||||
popupRender={() => (
|
||||
<div style={{ padding: 8, width: 800, maxHeight: 500, overflow: 'auto' }}>
|
||||
<Table
|
||||
dataSource={oauthList}
|
||||
columns={[
|
||||
{
|
||||
title: '授权账户ID',
|
||||
dataIndex: 'accountId',
|
||||
key: 'accountId',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权账户名称',
|
||||
dataIndex: 'accountName',
|
||||
key: 'accountName',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权应用ID',
|
||||
dataIndex: 'appid',
|
||||
key: 'appid',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权用户ID',
|
||||
dataIndex: 'accountUserid',
|
||||
key: 'accountUserid',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '授权账户角色',
|
||||
dataIndex: 'accountRole',
|
||||
key: 'accountRole',
|
||||
width: 160,
|
||||
render: (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
ADVERTISER: '客户',
|
||||
CUSTOMER_ADMIN: '普通版工作台-管理员',
|
||||
CUSTOMER_OPERATOR: '普通版工作台-协作者',
|
||||
AGENT: '代理商',
|
||||
CHILD_AGENT: '二级代理商',
|
||||
PLATFORM_ROLE_STAR: '星图账户',
|
||||
PLATFORM_ROLE_SHOP_ACCOUNT: '抖音店铺账户',
|
||||
PLATFORM_ROLE_QIANCHUAN_AGENT: '千川代理商',
|
||||
PLATFORM_ROLE_STAR_AGENT: '星图代理商',
|
||||
PLATFORM_ROLE_AWEME: '抖音号',
|
||||
PLATFORM_ROLE_STAR_MCN: '星图MCN机构',
|
||||
PLATFORM_ROLE_STAR_ISV: '星图服务商',
|
||||
AGENT_SYSTEM_ACCOUNT: '代理商系统账户',
|
||||
PLATFORM_ROLE_LOCAL_AGENT: '本地推代理商',
|
||||
PLATFORM_ROLE_YUNTU_BRAND_ISV_ADMIN: '云图品牌服务商管理员',
|
||||
PLATFORM_ROLE_LIFE: '抖音来客账户',
|
||||
PLATFORM_ROLE_ENTERPRISE_BP_ADMIN: '升级版工作台管理员',
|
||||
PLATFORM_ROLE_ENTERPRISE_BP_OPERATOR: '升级版工作台协作者',
|
||||
};
|
||||
return roleMap[role] || role;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '授权账户用户名',
|
||||
dataIndex: 'accountUsername',
|
||||
key: 'accountUsername',
|
||||
width: 120,
|
||||
render: (text: string) => <span style={{ color: text ? '#1e293b' : '#94a3b8' }}>{text || '-'}</span>,
|
||||
},
|
||||
]}
|
||||
loading={oauthLoading}
|
||||
pagination={{
|
||||
current: oauthPage,
|
||||
pageSize: oauthPageSize,
|
||||
total: oauthTotal,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
onChange: (page, size) => {
|
||||
setOauthPage(page);
|
||||
setOauthPageSize(size);
|
||||
loadOAuthList(page, size);
|
||||
},
|
||||
}}
|
||||
rowKey="id"
|
||||
size="small"
|
||||
scroll={{ x: 'max-content' }}
|
||||
onRow={(record) => ({
|
||||
onClick: () => {
|
||||
const id = String(record.id);
|
||||
const newOauthItems = [...selectedOauthItems];
|
||||
newOauthItems[index] = { value: id, label: String(record.accountId)+'-'+(record.accountName || '-') };
|
||||
setSelectedOauthItems(newOauthItems);
|
||||
const newOauthSelectOpens = [...oauthSelectOpens];
|
||||
newOauthSelectOpens[index] = false;
|
||||
setOauthSelectOpens(newOauthSelectOpens);
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
backgroundColor: oauthItem?.value === String(record.id) ? '#e6f7ff' : undefined,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
open={oauthSelectOpens[index]}
|
||||
onOpenChange={(open) => {
|
||||
const newOauthSelectOpens = [...oauthSelectOpens];
|
||||
newOauthSelectOpens[index] = open;
|
||||
setOauthSelectOpens(newOauthSelectOpens);
|
||||
if (open) {
|
||||
loadOAuthList(1, oauthPageSize);
|
||||
}
|
||||
}}
|
||||
labelInValue
|
||||
fieldNames={{ label: 'accountUserid', value: 'id' }}
|
||||
/>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block' }}>
|
||||
粘贴账户ID(每行一个或用逗号分隔)
|
||||
</Typography.Text>
|
||||
<Input.TextArea
|
||||
value={accountIdInputs[index]}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
const newAccountIdInputs = [...accountIdInputs];
|
||||
newAccountIdInputs[index] = value;
|
||||
setAccountIdInputs(newAccountIdInputs);
|
||||
const ids = value.split(/[\n,]/)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
const uniqueIds = [...new Set(ids)];
|
||||
const textAccounts = uniqueIds.map(id => ({ accountId: id }));
|
||||
const seen = new Set<string>();
|
||||
const finalAccounts = textAccounts.filter(a => {
|
||||
if (seen.has(a.accountId)) return false;
|
||||
seen.add(a.accountId);
|
||||
return true;
|
||||
});
|
||||
const newAccountIdLists = [...accountIdLists];
|
||||
newAccountIdLists[index] = finalAccounts;
|
||||
setAccountIdLists(newAccountIdLists);
|
||||
}}
|
||||
placeholder="粘贴账户ID,每行一个或用逗号分隔,例如:
|
||||
10001,10002,10003
|
||||
10004"
|
||||
rows={4}
|
||||
rows={3}
|
||||
style={{ borderRadius: 8 }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
type="dashed"
|
||||
block
|
||||
onClick={() => {
|
||||
setSelectedOauthItems([...selectedOauthItems, undefined]);
|
||||
setAccountIdInputs([...accountIdInputs, '']);
|
||||
setAccountIdLists([...accountIdLists, []]);
|
||||
setOauthSelectOpens([...oauthSelectOpens, false]);
|
||||
}}
|
||||
style={{ borderRadius: 8, marginBottom: 16 }}
|
||||
/>
|
||||
>
|
||||
+ 新增授权账户组
|
||||
</Button>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div style={{
|
||||
@@ -1610,9 +1669,9 @@ const GeneratedRecord: React.FC = () => {
|
||||
<Button
|
||||
onClick={() => {
|
||||
setUploadConfigModalVisible(false);
|
||||
setAccountIdList([]);
|
||||
setAccountIdInput('');
|
||||
setSelectedOauthItems(undefined);
|
||||
setAccountIdLists([[]]);
|
||||
setAccountIdInputs(['']);
|
||||
setSelectedOauthItems([undefined]);
|
||||
setMaterialFileNames(new Map());
|
||||
setUnifiedFileName('');
|
||||
}}
|
||||
@@ -1624,7 +1683,7 @@ const GeneratedRecord: React.FC = () => {
|
||||
type="primary"
|
||||
onClick={handleStartBatchUpload}
|
||||
loading={uploading}
|
||||
disabled={uploading || accountIdList.length === 0}
|
||||
disabled={uploading || accountIdLists.every(list => list.length === 0)}
|
||||
style={{ borderRadius: 8 }}
|
||||
>
|
||||
{uploading ? '上传中...' : '开始上传'}
|
||||
@@ -2072,15 +2131,16 @@ const GeneratedRecord: React.FC = () => {
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
{/* <div>
|
||||
<div>
|
||||
<Button
|
||||
|
||||
style={{ width: '100%', borderRadius: 8, marginTop: 20, color: '#4c49cc' }}
|
||||
type="primary"
|
||||
onClick={handleSinglePushToMedia}
|
||||
style={{ width: '100%', borderRadius: 8, marginTop: 20 }}
|
||||
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
|
||||
>
|
||||
|
||||
推送媒体后台
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user