“历史素材”
This commit is contained in:
+101
-101
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-B8KgQUTE.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BRGpXFCL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-JhRVnnL-.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import React from 'react';
|
||||
import { Button, DatePicker, Input, Space } from 'antd';
|
||||
import {
|
||||
CheckOutlined,
|
||||
ClearOutlined,
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
SearchOutlined,
|
||||
UploadOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
interface UnifiedFilterBarProps {
|
||||
// 搜索
|
||||
searchValue: string;
|
||||
searchPlaceholder?: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
onSearch: () => void;
|
||||
// 日期
|
||||
showDate?: boolean;
|
||||
dateValue?: Dayjs | null;
|
||||
onDateChange?: (date: Dayjs | null) => void;
|
||||
datePlaceholder?: string;
|
||||
// 批量操作
|
||||
batchCount: number;
|
||||
totalCount: number;
|
||||
onSelectAll: () => void;
|
||||
onClearSelection: () => void;
|
||||
onDelete: () => void;
|
||||
onDownload?: () => void;
|
||||
onPush?: () => void;
|
||||
pushLoading?: boolean;
|
||||
pushText?: string;
|
||||
// 右侧额外操作
|
||||
extraActions?: React.ReactNode;
|
||||
}
|
||||
|
||||
const btnBase: React.CSSProperties = {
|
||||
borderRadius: 8,
|
||||
fontWeight: 600,
|
||||
height: 36,
|
||||
};
|
||||
|
||||
const btnSecondary: React.CSSProperties = {
|
||||
...btnBase,
|
||||
background: '#f8f9fc',
|
||||
border: '1px solid #e2e8f0',
|
||||
color: '#334155',
|
||||
};
|
||||
|
||||
const btnDanger: React.CSSProperties = {
|
||||
...btnBase,
|
||||
background: 'linear-gradient(135deg, #ef4444, #dc2626)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
};
|
||||
|
||||
const btnPrimary: React.CSSProperties = {
|
||||
...btnBase,
|
||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
};
|
||||
|
||||
const UnifiedFilterBar: React.FC<UnifiedFilterBarProps> = ({
|
||||
searchValue,
|
||||
searchPlaceholder = '搜索...',
|
||||
onSearchChange,
|
||||
onSearch,
|
||||
showDate = false,
|
||||
dateValue,
|
||||
onDateChange,
|
||||
datePlaceholder = '选择日期',
|
||||
batchCount,
|
||||
totalCount,
|
||||
onSelectAll,
|
||||
onClearSelection,
|
||||
onDelete,
|
||||
onDownload,
|
||||
onPush,
|
||||
pushLoading = false,
|
||||
pushText = '推送至账户',
|
||||
extraActions,
|
||||
}) => {
|
||||
const allSelected = batchCount === totalCount && totalCount > 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
marginBottom: 16,
|
||||
padding: '12px 16px',
|
||||
borderRadius: 12,
|
||||
background: '#fff',
|
||||
border: '1px solid #f0f0f5',
|
||||
}}
|
||||
>
|
||||
{/* 左侧:搜索 + 日期 */}
|
||||
<Space size={8} wrap>
|
||||
<Input.Search
|
||||
placeholder={searchPlaceholder}
|
||||
allowClear
|
||||
value={searchValue}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
onSearchChange(val);
|
||||
if (!val) onSearch();
|
||||
}}
|
||||
onSearch={onSearch}
|
||||
style={{ width: 240, borderRadius: 8 }}
|
||||
enterButton
|
||||
/>
|
||||
{showDate && (
|
||||
<>
|
||||
<DatePicker
|
||||
value={dateValue || undefined}
|
||||
onChange={(date) => onDateChange?.(date)}
|
||||
format="YYYY-MM-DD"
|
||||
placeholder={datePlaceholder}
|
||||
style={{ width: 160, borderRadius: 8 }}
|
||||
allowClear
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
|
||||
{/* 右侧:批量操作 + 额外操作 */}
|
||||
<Space size={8} wrap>
|
||||
{/* 全选 / 取消选择 */}
|
||||
<Button
|
||||
icon={allSelected ? <ClearOutlined /> : <CheckOutlined />}
|
||||
onClick={allSelected ? onClearSelection : onSelectAll}
|
||||
style={btnSecondary}
|
||||
>
|
||||
{allSelected ? '取消全选' : '全选'}
|
||||
</Button>
|
||||
|
||||
{/* 删除 */}
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={onDelete}
|
||||
disabled={batchCount === 0}
|
||||
style={batchCount === 0 ? { ...btnSecondary, opacity: 0.5 } : btnDanger}
|
||||
>
|
||||
删除 {batchCount > 0 && `(${batchCount})`}
|
||||
</Button>
|
||||
|
||||
{/* 下载 */}
|
||||
{onDownload && (
|
||||
<Button
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={onDownload}
|
||||
disabled={batchCount === 0}
|
||||
style={batchCount === 0 ? { ...btnSecondary, opacity: 0.5 } : btnPrimary}
|
||||
>
|
||||
下载 {batchCount > 0 && `(${batchCount})`}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 推送 */}
|
||||
{onPush && (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UploadOutlined />}
|
||||
onClick={onPush}
|
||||
loading={pushLoading}
|
||||
disabled={batchCount === 0}
|
||||
style={batchCount === 0 ? { ...btnBase, opacity: 0.5 } : btnPrimary}
|
||||
>
|
||||
{pushLoading ? '推送中...' : `${pushText} ${batchCount > 0 ? `(${batchCount})` : ''}`}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 额外操作按钮 */}
|
||||
{extraActions}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedFilterBar;
|
||||
@@ -7,6 +7,9 @@ import dayjs from 'dayjs';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 统一按钮样式
|
||||
const btnBase: React.CSSProperties = { borderRadius: 8, fontWeight: 600 };
|
||||
|
||||
const toCamelCase = (str: string) => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
|
||||
interface ConsumptionRecord {
|
||||
@@ -164,13 +167,24 @@ const ConsumePage: React.FC = () => {
|
||||
<Typography.Text strong style={{ fontSize: 16 }}>消耗记录</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
marginBottom: 16,
|
||||
padding: '12px 16px',
|
||||
borderRadius: 12,
|
||||
background: '#fff',
|
||||
border: '1px solid #f0f0f5',
|
||||
}}>
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
<Input
|
||||
placeholder="广告主ID"
|
||||
value={advertiserId}
|
||||
onChange={(e) => setAdvertiserId(e.target.value)}
|
||||
style={{ width: 180 }}
|
||||
style={{ width: 180, borderRadius: 8 }}
|
||||
allowClear
|
||||
onPressEnter={() => { setCurrentPage(1); loadData(1, pageSize); }}
|
||||
/>
|
||||
@@ -183,25 +197,32 @@ const ConsumePage: React.FC = () => {
|
||||
setConsumeDateRange(undefined);
|
||||
}
|
||||
}}
|
||||
style={{ borderRadius: 8 }}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="medium"
|
||||
onClick={handleSearch}
|
||||
style={{ ...btnBase, borderColor: 'transparent' }}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Button
|
||||
icon={<SyncOutlined />}
|
||||
onClick={handleSync}
|
||||
loading={syncLoading}
|
||||
style={{ borderRadius: 8 }}
|
||||
style={btnBase}
|
||||
>
|
||||
拉取消耗
|
||||
</Button>
|
||||
<Button icon={<SettingOutlined />} onClick={() => setShowModal(true)} style={{ borderRadius: 8 }}>自定义表头</Button>
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => setShowModal(true)}
|
||||
style={{ ...btnBase, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#334155' }}
|
||||
>
|
||||
自定义表头
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
DeleteOutlined,
|
||||
LoadingOutlined,
|
||||
UserOutlined,
|
||||
CheckOutlined,
|
||||
ClearOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { PrivatePortraitLibraryPanel } from '../components/privatePortrait';
|
||||
@@ -909,225 +911,86 @@ const GeneratedRecord: React.FC = () => {
|
||||
}, [filterType, filterMedia]);
|
||||
return (
|
||||
<div className="content_box" >
|
||||
{/* 操作栏:筛选 + 推送按钮 */}
|
||||
{/* 顶部:标签切换 + 批量操作按钮 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
marginBottom: 16,
|
||||
padding: '12px 16px',
|
||||
borderRadius: 12,
|
||||
background: '#fff',
|
||||
border: '1px solid #f0f0f5',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 12,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
|
||||
<FilterOutlined style={{ color: '#090a0cff', fontSize: 14 }} />
|
||||
<Space size={8} wrap={true}>
|
||||
<Button
|
||||
type={filterType === 'project' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('project');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'project'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'project' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'project' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<FolderOpenOutlined />}
|
||||
>
|
||||
项目记录
|
||||
</Button>
|
||||
<Button
|
||||
type={filterType === 'creation' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('creation');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'creation'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'creation' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'creation' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<FileTextOutlined />}
|
||||
>
|
||||
创作记录
|
||||
</Button>
|
||||
<Button
|
||||
type={filterType === 'hot_opening_replicate' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('hot_opening_replicate');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'hot_opening_replicate'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'hot_opening_replicate' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'hot_opening_replicate' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<StarOutlined />}
|
||||
>
|
||||
爆款复刻
|
||||
</Button>
|
||||
<Button
|
||||
type={filterType === 'shot_replicate' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('shot_replicate');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'shot_replicate'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'shot_replicate' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'shot_replicate' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<PlayCircleOutlined />}
|
||||
>
|
||||
拆镜复刻
|
||||
</Button>
|
||||
<Button
|
||||
type={filterType === 'private_portrait' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('private_portrait');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'private_portrait'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'private_portrait' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'private_portrait' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<UserOutlined />}
|
||||
>
|
||||
私域素材库
|
||||
</Button>
|
||||
<Button
|
||||
type={filterType === 'upload_resource' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterType('upload_resource');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterType === 'upload_resource'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterType === 'upload_resource' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterType === 'upload_resource' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<UploadOutlined />}
|
||||
>
|
||||
历史素材
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Tabs
|
||||
activeKey={filterType}
|
||||
onChange={(key) => {
|
||||
setFilterType(key as typeof filterType);
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
items={[
|
||||
{ key: 'project', label: <span><FolderOpenOutlined /> 项目记录</span> },
|
||||
{ key: 'creation', label: <span><FileTextOutlined /> 创作记录</span> },
|
||||
{ key: 'hot_opening_replicate', label: <span><StarOutlined /> 爆款复刻</span> },
|
||||
{ key: 'shot_replicate', label: <span><PlayCircleOutlined /> 拆镜复刻</span> },
|
||||
{ key: 'private_portrait', label: <span><UserOutlined /> 私域素材库</span> },
|
||||
{ key: 'upload_resource', label: <span><UploadOutlined /> 历史素材</span> },
|
||||
]}
|
||||
size="middle"
|
||||
/>
|
||||
{filterType !== 'private_portrait' && filterType !== 'upload_resource' && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
|
||||
{/* 多选模式按钮 */}
|
||||
{isSelectionMode ? (
|
||||
<Space size={8} wrap={true}>
|
||||
<Button
|
||||
onClick={handleSelectAll}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: '#f8f9fc',
|
||||
border: '1px solid #e2e8f0',
|
||||
color: '#222222ff',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) ? '取消全选' : '全选'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: '#f8f9fc',
|
||||
border: '1px solid #e2e8f0',
|
||||
color: '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
取消选择
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleDeleteSelected()}
|
||||
style={{ borderRadius: 8, background: 'linear-gradient(135deg, #bf0b0a 0%, #ff8165 100%)', fontWeight: 600, padding: '8px 24px', color: '#fff' }}
|
||||
>
|
||||
删除 ({selectedItems.size})
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleDownloadSelected()}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
||||
color: '#fff',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
下载 ({selectedItems.size})
|
||||
</Button>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
{isSelectionMode ? (
|
||||
<Space size={8}>
|
||||
<Button
|
||||
icon={selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? <ClearOutlined /> : <CheckOutlined />}
|
||||
onClick={selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? () => { setSelectedItems(new Set()); } : handleSelectAll}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#334155' }}
|
||||
>
|
||||
{selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? '取消全选' : '全选'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => { setIsSelectionMode(false); setSelectedItems(new Set()); }}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#64748b' }}
|
||||
>
|
||||
取消选择
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={handleDeleteSelected}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #ef4444, #dc2626)', border: 'none', color: '#fff' }}
|
||||
>
|
||||
删除 {selectedItems.size > 0 && `(${selectedItems.size})`}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={handleDownloadSelected}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', border: 'none', color: '#fff' }}
|
||||
>
|
||||
下载 {selectedItems.size > 0 && `(${selectedItems.size})`}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UploadOutlined />}
|
||||
onClick={handleBatchUploadSelected}
|
||||
loading={uploading}
|
||||
disabled={uploading || selectedItems.size === 0}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', border: 'none', color: '#fff' }}
|
||||
>
|
||||
{uploading ? '推送中...' : `推送至账户 ${selectedItems.size > 0 ? `(${selectedItems.size})` : ''}`}
|
||||
</Button>
|
||||
</Space>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleBatchUploadSelected}
|
||||
loading={uploading}
|
||||
disabled={uploading || selectedItems.size === 0}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
||||
color: '#fff',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<UploadOutlined />}
|
||||
onClick={() => setIsSelectionMode(true)}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}
|
||||
>
|
||||
{uploading ? '推送中...' : `推送至账户 (${selectedItems.size})`}
|
||||
批量操作
|
||||
</Button>
|
||||
</Space>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UploadOutlined />}
|
||||
onClick={() => setIsSelectionMode(true)}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
border: 'none',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
批量操作
|
||||
</Button>
|
||||
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{filterType === 'private_portrait' ? (
|
||||
@@ -1136,122 +999,75 @@ const GeneratedRecord: React.FC = () => {
|
||||
<UploadResourceHistoryPanel />
|
||||
) : (
|
||||
<>
|
||||
{/* Second row filter: 视频 / 图片 */}
|
||||
{/* 统一筛选栏:搜索 + 日期 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
marginBottom: 24,
|
||||
marginBottom: 16,
|
||||
padding: '12px 16px',
|
||||
borderRadius: 12,
|
||||
background: '#fff',
|
||||
border: '1px solid #f0f0f5',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Typography.Text style={{ color: '#94a3b8', fontSize: 14 }}>媒体类型:</Typography.Text>
|
||||
<Space size={8} wrap={true}>
|
||||
<Button
|
||||
type={filterMedia === 'video' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterMedia('video');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterMedia === 'video'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterMedia === 'video' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterMedia === 'video' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<VideoCameraOutlined />}
|
||||
>
|
||||
视频
|
||||
</Button>
|
||||
<Button
|
||||
type={filterMedia === 'image' ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
setFilterMedia('image');
|
||||
setIsSelectionMode(false);
|
||||
setSelectedItems(new Set());
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: filterMedia === 'image'
|
||||
? 'linear-gradient(135deg, #6366f1, #8b5cf6)'
|
||||
: '#f8f9fc',
|
||||
border: filterMedia === 'image' ? 'none' : '1px solid #e2e8f0',
|
||||
color: filterMedia === 'image' ? '#fff' : '#64748b',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
icon={<PictureOutlined />}
|
||||
>
|
||||
图片
|
||||
</Button>
|
||||
<DatePicker
|
||||
picker="date"
|
||||
value={selectedDate ? dayjs(selectedDate) : undefined}
|
||||
onChange={(date, dateString) => handleDateChange(dateString || '')}
|
||||
format="YYYY-MM-DD"
|
||||
style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
|
||||
placeholder="选择日期"
|
||||
/>
|
||||
{selectedDate && (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => handleDateChange('')}
|
||||
style={{ color: '#94a3b8', fontSize: 12 }}
|
||||
>
|
||||
清除
|
||||
</Button>
|
||||
)}
|
||||
<Input.Search
|
||||
placeholder="搜索提示词"
|
||||
allowClear
|
||||
value={searchKeyword}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setSearchKeyword(val);
|
||||
if (!val) {
|
||||
setPagebreak(prev => ({ ...prev, page: 1 }));
|
||||
loadRecordList();
|
||||
}
|
||||
}}
|
||||
onSearch={() => {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<Typography.Text style={{ color: '#94a3b8', fontSize: 14, marginRight: 4 }}>媒体类型:</Typography.Text>
|
||||
<Button
|
||||
type={filterMedia === 'video' ? 'primary' : 'default'}
|
||||
onClick={() => { setFilterMedia('video'); setIsSelectionMode(false); setSelectedItems(new Set()); }}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: filterMedia === 'video' ? 'transparent' : '#e2e8f0', color: filterMedia === 'video' ? '#fff' : '#64748b' }}
|
||||
icon={<VideoCameraOutlined />}
|
||||
>
|
||||
视频
|
||||
</Button>
|
||||
<Button
|
||||
type={filterMedia === 'image' ? 'primary' : 'default'}
|
||||
onClick={() => { setFilterMedia('image'); setIsSelectionMode(false); setSelectedItems(new Set()); }}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: filterMedia === 'image' ? 'transparent' : '#e2e8f0', color: filterMedia === 'image' ? '#fff' : '#64748b' }}
|
||||
icon={<PictureOutlined />}
|
||||
>
|
||||
图片
|
||||
</Button>
|
||||
</div>
|
||||
<Space size={8} wrap>
|
||||
<DatePicker
|
||||
picker="date"
|
||||
value={selectedDate ? dayjs(selectedDate) : undefined}
|
||||
onChange={(date, dateString) => handleDateChange(dateString || '')}
|
||||
format="YYYY-MM-DD"
|
||||
style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
|
||||
placeholder="选择日期"
|
||||
allowClear
|
||||
/>
|
||||
<Input.Search
|
||||
placeholder="搜索提示词"
|
||||
allowClear
|
||||
value={searchKeyword}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setSearchKeyword(val);
|
||||
if (!val) {
|
||||
setPagebreak(prev => ({ ...prev, page: 1 }));
|
||||
loadRecordList();
|
||||
}}
|
||||
style={{ width: 240, borderRadius: 8 }}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<Button
|
||||
icon={<ClockCircleOutlined />}
|
||||
onClick={handleOpenUploadHistory}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 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)';
|
||||
}}
|
||||
>
|
||||
查询推送任务历史
|
||||
</Button>
|
||||
}
|
||||
}}
|
||||
onSearch={() => {
|
||||
setPagebreak(prev => ({ ...prev, page: 1 }));
|
||||
loadRecordList();
|
||||
}}
|
||||
style={{ width: 240, borderRadius: 8 }}
|
||||
enterButton
|
||||
/>
|
||||
<Button
|
||||
icon={<ClockCircleOutlined />}
|
||||
onClick={handleOpenUploadHistory}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', color: '#fff' }}
|
||||
>
|
||||
推送任务历史
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
{/* Content area */}
|
||||
{loading ? (
|
||||
@@ -1408,18 +1224,20 @@ const GeneratedRecord: React.FC = () => {
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 8,
|
||||
left: 8,
|
||||
width: 20,
|
||||
height: 20,
|
||||
top: 6,
|
||||
left: 6,
|
||||
width: 22,
|
||||
height: 22,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isSelectedItem ? '#10b981' : 'rgba(255,255,255,0.9)',
|
||||
border: isSelectedItem ? '2px solid #10b981' : '2px solid #d1d5db',
|
||||
backgroundColor: isSelectedItem ? '#6366f1' : 'rgba(255,255,255,0.92)',
|
||||
border: isSelectedItem ? '2px solid #6366f1' : '2px solid #cbd5e1',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
zIndex: 10,
|
||||
boxShadow: isSelectedItem ? '0 2px 8px rgba(99,102,241,0.4)' : '0 1px 3px rgba(0,0,0,0.1)',
|
||||
transition: 'all 0.15s ease',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -1517,10 +1335,11 @@ const GeneratedRecord: React.FC = () => {
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
border: '3px solid #10b981',
|
||||
border: '3px solid #6366f1',
|
||||
borderRadius: 4,
|
||||
pointerEvents: 'none',
|
||||
zIndex: 5,
|
||||
boxShadow: 'inset 0 0 0 1px rgba(99,102,241,0.2)',
|
||||
}} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -383,13 +383,24 @@ const MaterialListPage: React.FC = () => {
|
||||
<FolderOpenOutlined style={{ color: '#6366f1', fontSize: 16 }} />
|
||||
<Typography.Text strong style={{ fontSize: 16 }}>素材列表</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'top', marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
marginBottom: 16,
|
||||
padding: '12px 16px',
|
||||
borderRadius: 12,
|
||||
background: '#fff',
|
||||
border: '1px solid #f0f0f5',
|
||||
}}>
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
<Input
|
||||
placeholder="广告主ID"
|
||||
value={searchParams.advertiser_id}
|
||||
onChange={(e) => setSearchParams(prev => ({ ...prev, advertiser_id: e.target.value }))}
|
||||
style={{ width: 160 }}
|
||||
style={{ width: 150, borderRadius: 8 }}
|
||||
allowClear
|
||||
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
|
||||
/>
|
||||
@@ -397,7 +408,7 @@ const MaterialListPage: React.FC = () => {
|
||||
placeholder="素材ID"
|
||||
value={searchParams.material_id}
|
||||
onChange={(e) => setSearchParams(prev => ({ ...prev, material_id: e.target.value }))}
|
||||
style={{ width: 160 }}
|
||||
style={{ width: 150, borderRadius: 8 }}
|
||||
allowClear
|
||||
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
|
||||
/>
|
||||
@@ -405,7 +416,7 @@ const MaterialListPage: React.FC = () => {
|
||||
placeholder="上传ID"
|
||||
value={searchParams.upload_id}
|
||||
onChange={(e) => setSearchParams(prev => ({ ...prev, upload_id: e.target.value }))}
|
||||
style={{ width: 160 }}
|
||||
style={{ width: 150, borderRadius: 8 }}
|
||||
allowClear
|
||||
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
|
||||
/>
|
||||
@@ -413,7 +424,7 @@ const MaterialListPage: React.FC = () => {
|
||||
placeholder="文件名"
|
||||
value={searchParams.file_name}
|
||||
onChange={(e) => setSearchParams(prev => ({ ...prev, file_name: e.target.value }))}
|
||||
style={{ width: 140 }}
|
||||
style={{ width: 140, borderRadius: 8 }}
|
||||
allowClear
|
||||
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
|
||||
/>
|
||||
@@ -421,7 +432,7 @@ const MaterialListPage: React.FC = () => {
|
||||
placeholder="资源类型"
|
||||
value={searchParams.resource_type}
|
||||
onChange={(value) => setSearchParams(prev => ({ ...prev, resource_type: value }))}
|
||||
style={{ width: 120 }}
|
||||
style={{ width: 110, borderRadius: 8 }}
|
||||
allowClear
|
||||
options={[
|
||||
{ value: 'image', label: '图片' },
|
||||
@@ -431,21 +442,16 @@ const MaterialListPage: React.FC = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: 'transparent' }}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Button
|
||||
icon={<RobotOutlined />}
|
||||
onClick={() => navigate('/generated?filterType=private_portrait&portraitTab=aigc_virtual')}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
fontSize: 14,
|
||||
borderColor: '#8b5cf6',
|
||||
color: '#7c3aed',
|
||||
background: '#f5f3ff',
|
||||
}}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: '#8b5cf6', color: '#7c3aed', background: '#f5f3ff' }}
|
||||
>
|
||||
私域虚拟人像库
|
||||
</Button>
|
||||
@@ -454,16 +460,9 @@ const MaterialListPage: React.FC = () => {
|
||||
loading={pushTemplatesLoading}
|
||||
onClick={handleOpenPushModal}
|
||||
disabled={selectedRows.size === 0}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
fontSize: 14,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 14px rgba(99,102,241,0.3)',
|
||||
}}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', color: '#fff' }}
|
||||
>
|
||||
推送前测 ({selectedRows.size})
|
||||
推送前测 {selectedRows.size > 0 && `(${selectedRows.size})`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user