前台素材云页面样式统一修改

This commit is contained in:
2026-07-11 11:31:30 +08:00
parent f5f4be2986
commit 37588d0f05
4 changed files with 374 additions and 349 deletions
@@ -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;
+28 -7
View File
@@ -7,6 +7,9 @@ import dayjs from 'dayjs';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
// 统一按钮样式
const btnBase: React.CSSProperties = { borderRadius: 8, fontWeight: 600 };
const toCamelCase = (str: string) => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); const toCamelCase = (str: string) => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
interface ConsumptionRecord { interface ConsumptionRecord {
@@ -164,13 +167,24 @@ const ConsumePage: React.FC = () => {
<Typography.Text strong style={{ fontSize: 16 }}></Typography.Text> <Typography.Text strong style={{ fontSize: 16 }}></Typography.Text>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, marginBottom: 16 }}> <div style={{
<div style={{ display: 'flex', gap: 12 }}> 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 <Input
placeholder="广告主ID" placeholder="广告主ID"
value={advertiserId} value={advertiserId}
onChange={(e) => setAdvertiserId(e.target.value)} onChange={(e) => setAdvertiserId(e.target.value)}
style={{ width: 180 }} style={{ width: 180, borderRadius: 8 }}
allowClear allowClear
onPressEnter={() => { setCurrentPage(1); loadData(1, pageSize); }} onPressEnter={() => { setCurrentPage(1); loadData(1, pageSize); }}
/> />
@@ -183,25 +197,32 @@ const ConsumePage: React.FC = () => {
setConsumeDateRange(undefined); setConsumeDateRange(undefined);
} }
}} }}
style={{ borderRadius: 8 }}
/> />
<Button <Button
type="primary" type="primary"
size="medium"
onClick={handleSearch} onClick={handleSearch}
style={{ ...btnBase, borderColor: 'transparent' }}
> >
</Button> </Button>
</div> </div>
<div style={{ display: 'flex', gap: 12 }}> <div style={{ display: 'flex', gap: 8 }}>
<Button <Button
icon={<SyncOutlined />} icon={<SyncOutlined />}
onClick={handleSync} onClick={handleSync}
loading={syncLoading} loading={syncLoading}
style={{ borderRadius: 8 }} style={btnBase}
> >
</Button> </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>
</div> </div>
+137 -318
View File
@@ -17,6 +17,8 @@ import {
DeleteOutlined, DeleteOutlined,
LoadingOutlined, LoadingOutlined,
UserOutlined, UserOutlined,
CheckOutlined,
ClearOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { PrivatePortraitLibraryPanel } from '../components/privatePortrait'; import { PrivatePortraitLibraryPanel } from '../components/privatePortrait';
@@ -909,225 +911,86 @@ const GeneratedRecord: React.FC = () => {
}, [filterType, filterMedia]); }, [filterType, filterMedia]);
return ( return (
<div className="content_box" > <div className="content_box" >
{/* 操作栏:筛选 + 推送按钮 */} {/* 顶部:标签切换 + 批量操作按钮 */}
<div style={{ <div style={{
display: 'flex', display: 'flex',
flexWrap: 'wrap', flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
gap: 12, gap: 12,
marginBottom: 16, marginBottom: 12,
padding: '12px 16px',
borderRadius: 12,
background: '#fff',
border: '1px solid #f0f0f5',
justifyContent: 'space-between',
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}> <Tabs
<FilterOutlined style={{ color: '#090a0cff', fontSize: 14 }} /> activeKey={filterType}
<Space size={8} wrap={true}> onChange={(key) => {
<Button setFilterType(key as typeof filterType);
type={filterType === 'project' ? 'primary' : 'default'} setIsSelectionMode(false);
onClick={() => { setSelectedItems(new Set());
setFilterType('project'); }}
setIsSelectionMode(false); items={[
setSelectedItems(new Set()); { key: 'project', label: <span><FolderOpenOutlined /> </span> },
}} { key: 'creation', label: <span><FileTextOutlined /> </span> },
style={{ { key: 'hot_opening_replicate', label: <span><StarOutlined /> </span> },
borderRadius: 8, { key: 'shot_replicate', label: <span><PlayCircleOutlined /> </span> },
background: filterType === 'project' { key: 'private_portrait', label: <span><UserOutlined /> </span> },
? 'linear-gradient(135deg, #6366f1, #8b5cf6)' { key: 'upload_resource', label: <span><UploadOutlined /> </span> },
: '#f8f9fc', ]}
border: filterType === 'project' ? 'none' : '1px solid #e2e8f0', size="middle"
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>
{filterType !== 'private_portrait' && filterType !== 'upload_resource' && ( {filterType !== 'private_portrait' && filterType !== 'upload_resource' && (
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{/* 多选模式按钮 */} {isSelectionMode ? (
{isSelectionMode ? ( <Space size={8}>
<Space size={8} wrap={true}> <Button
<Button icon={selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? <ClearOutlined /> : <CheckOutlined />}
onClick={handleSelectAll} onClick={selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? () => { setSelectedItems(new Set()); } : handleSelectAll}
style={{ style={{ borderRadius: 8, fontWeight: 600, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#334155' }}
borderRadius: 8, >
background: '#f8f9fc', {selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) && recordlist.length > 0 ? '取消全选' : '全选'}
border: '1px solid #e2e8f0', </Button>
color: '#222222ff', <Button
fontWeight: 600, onClick={() => { setIsSelectionMode(false); setSelectedItems(new Set()); }}
}} style={{ borderRadius: 8, fontWeight: 600, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#64748b' }}
> >
{selectedItems.size === recordlist.reduce((sum: number, group: any) => sum + group.items.length, 0) ? '取消全选' : '全选'}
</Button> </Button>
<Button <Button
onClick={() => { danger
setIsSelectionMode(false); icon={<DeleteOutlined />}
setSelectedItems(new Set()); onClick={handleDeleteSelected}
}} style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #ef4444, #dc2626)', border: 'none', color: '#fff' }}
style={{ >
borderRadius: 8, {selectedItems.size > 0 && `(${selectedItems.size})`}
background: '#f8f9fc', </Button>
border: '1px solid #e2e8f0', <Button
color: '#64748b', icon={<DownloadOutlined />}
fontWeight: 600, onClick={handleDownloadSelected}
}} style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', border: 'none', color: '#fff' }}
> >
{selectedItems.size > 0 && `(${selectedItems.size})`}
</Button> </Button>
<Button <Button
onClick={() => handleDeleteSelected()} type="primary"
style={{ borderRadius: 8, background: 'linear-gradient(135deg, #bf0b0a 0%, #ff8165 100%)', fontWeight: 600, padding: '8px 24px', color: '#fff' }} icon={<UploadOutlined />}
> onClick={handleBatchUploadSelected}
({selectedItems.size}) loading={uploading}
</Button> disabled={uploading || selectedItems.size === 0}
<Button style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', border: 'none', color: '#fff' }}
onClick={() => handleDownloadSelected()} >
style={{ {uploading ? '推送中...' : `推送至账户 ${selectedItems.size > 0 ? `(${selectedItems.size})` : ''}`}
borderRadius: 8, </Button>
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', </Space>
color: '#fff', ) : (
fontWeight: 600,
}}
>
({selectedItems.size})
</Button>
<Button <Button
type="primary" type="primary"
onClick={handleBatchUploadSelected} icon={<UploadOutlined />}
loading={uploading} onClick={() => setIsSelectionMode(true)}
disabled={uploading || selectedItems.size === 0} style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}
style={{
borderRadius: 8,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
color: '#fff',
fontWeight: 600,
}}
> >
{uploading ? '推送中...' : `推送至账户 (${selectedItems.size})`}
</Button> </Button>
</Space> )}
) : ( </div>
<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' ? ( {filterType === 'private_portrait' ? (
@@ -1136,122 +999,75 @@ const GeneratedRecord: React.FC = () => {
<UploadResourceHistoryPanel /> <UploadResourceHistoryPanel />
) : ( ) : (
<> <>
{/* Second row filter: 视频 / 图片 */} {/* 统一筛选栏:搜索 + 日期 */}
<div style={{ <div style={{
display: 'flex', display: 'flex',
flexWrap: 'wrap', flexWrap: 'wrap',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
gap: 12, gap: 12,
marginBottom: 24, marginBottom: 16,
padding: '12px 16px', padding: '12px 16px',
borderRadius: 12, borderRadius: 12,
background: '#fff', background: '#fff',
border: '1px solid #f0f0f5', border: '1px solid #f0f0f5',
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Typography.Text style={{ color: '#94a3b8', fontSize: 14 }}></Typography.Text> <Typography.Text style={{ color: '#94a3b8', fontSize: 14, marginRight: 4 }}></Typography.Text>
<Space size={8} wrap={true}> <Button
<Button type={filterMedia === 'video' ? 'primary' : 'default'}
type={filterMedia === 'video' ? 'primary' : 'default'} onClick={() => { setFilterMedia('video'); setIsSelectionMode(false); setSelectedItems(new Set()); }}
onClick={() => { style={{ borderRadius: 8, fontWeight: 600, borderColor: filterMedia === 'video' ? 'transparent' : '#e2e8f0', color: filterMedia === 'video' ? '#fff' : '#64748b' }}
setFilterMedia('video'); icon={<VideoCameraOutlined />}
setIsSelectionMode(false); >
setSelectedItems(new Set());
}} </Button>
style={{ <Button
borderRadius: 8, type={filterMedia === 'image' ? 'primary' : 'default'}
background: filterMedia === 'video' onClick={() => { setFilterMedia('image'); setIsSelectionMode(false); setSelectedItems(new Set()); }}
? 'linear-gradient(135deg, #6366f1, #8b5cf6)' style={{ borderRadius: 8, fontWeight: 600, borderColor: filterMedia === 'image' ? 'transparent' : '#e2e8f0', color: filterMedia === 'image' ? '#fff' : '#64748b' }}
: '#f8f9fc', icon={<PictureOutlined />}
border: filterMedia === 'video' ? 'none' : '1px solid #e2e8f0', >
color: filterMedia === 'video' ? '#fff' : '#64748b',
fontWeight: 600, </Button>
}} </div>
icon={<VideoCameraOutlined />} <Space size={8} wrap>
> <DatePicker
picker="date"
</Button> value={selectedDate ? dayjs(selectedDate) : undefined}
<Button onChange={(date, dateString) => handleDateChange(dateString || '')}
type={filterMedia === 'image' ? 'primary' : 'default'} format="YYYY-MM-DD"
onClick={() => { style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
setFilterMedia('image'); placeholder="选择日期"
setIsSelectionMode(false); allowClear
setSelectedItems(new Set()); />
}} <Input.Search
style={{ placeholder="搜索提示词"
borderRadius: 8, allowClear
background: filterMedia === 'image' value={searchKeyword}
? 'linear-gradient(135deg, #6366f1, #8b5cf6)' onChange={(e) => {
: '#f8f9fc', const val = e.target.value;
border: filterMedia === 'image' ? 'none' : '1px solid #e2e8f0', setSearchKeyword(val);
color: filterMedia === 'image' ? '#fff' : '#64748b', if (!val) {
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={() => {
setPagebreak(prev => ({ ...prev, page: 1 })); setPagebreak(prev => ({ ...prev, page: 1 }));
loadRecordList(); loadRecordList();
}} }
style={{ width: 240, borderRadius: 8 }} }}
/> onSearch={() => {
</Space> setPagebreak(prev => ({ ...prev, page: 1 }));
</div> loadRecordList();
<Button }}
icon={<ClockCircleOutlined />} style={{ width: 240, borderRadius: 8 }}
onClick={handleOpenUploadHistory} enterButton
style={{ />
borderRadius: 8, <Button
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', icon={<ClockCircleOutlined />}
border: 'none', onClick={handleOpenUploadHistory}
color: '#ffffff', style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', color: '#fff' }}
fontWeight: 600, >
boxShadow: '0 4px 15px rgba(102, 126, 234, 0.4)',
transition: 'all 0.3s ease', </Button>
}} </Space>
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>
</div> </div>
{/* Content area */} {/* Content area */}
{loading ? ( {loading ? (
@@ -1408,18 +1224,20 @@ const GeneratedRecord: React.FC = () => {
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: 8, top: 6,
left: 8, left: 6,
width: 20, width: 22,
height: 20, height: 22,
borderRadius: '50%', borderRadius: '50%',
backgroundColor: isSelectedItem ? '#10b981' : 'rgba(255,255,255,0.9)', backgroundColor: isSelectedItem ? '#6366f1' : 'rgba(255,255,255,0.92)',
border: isSelectedItem ? '2px solid #10b981' : '2px solid #d1d5db', border: isSelectedItem ? '2px solid #6366f1' : '2px solid #cbd5e1',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
cursor: 'pointer', cursor: 'pointer',
zIndex: 10, 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) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -1517,10 +1335,11 @@ const GeneratedRecord: React.FC = () => {
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
border: '3px solid #10b981', border: '3px solid #6366f1',
borderRadius: 4, borderRadius: 4,
pointerEvents: 'none', pointerEvents: 'none',
zIndex: 5, zIndex: 5,
boxShadow: 'inset 0 0 0 1px rgba(99,102,241,0.2)',
}} /> }} />
)} )}
</div> </div>
+23 -24
View File
@@ -383,13 +383,24 @@ const MaterialListPage: React.FC = () => {
<FolderOpenOutlined style={{ color: '#6366f1', fontSize: 16 }} /> <FolderOpenOutlined style={{ color: '#6366f1', fontSize: 16 }} />
<Typography.Text strong style={{ fontSize: 16 }}></Typography.Text> <Typography.Text strong style={{ fontSize: 16 }}></Typography.Text>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'top', marginBottom: 16 }}> <div style={{
<div style={{ display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}> 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 <Input
placeholder="广告主ID" placeholder="广告主ID"
value={searchParams.advertiser_id} value={searchParams.advertiser_id}
onChange={(e) => setSearchParams(prev => ({ ...prev, advertiser_id: e.target.value }))} onChange={(e) => setSearchParams(prev => ({ ...prev, advertiser_id: e.target.value }))}
style={{ width: 160 }} style={{ width: 150, borderRadius: 8 }}
allowClear allowClear
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }} onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
/> />
@@ -397,7 +408,7 @@ const MaterialListPage: React.FC = () => {
placeholder="素材ID" placeholder="素材ID"
value={searchParams.material_id} value={searchParams.material_id}
onChange={(e) => setSearchParams(prev => ({ ...prev, material_id: e.target.value }))} onChange={(e) => setSearchParams(prev => ({ ...prev, material_id: e.target.value }))}
style={{ width: 160 }} style={{ width: 150, borderRadius: 8 }}
allowClear allowClear
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }} onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
/> />
@@ -405,7 +416,7 @@ const MaterialListPage: React.FC = () => {
placeholder="上传ID" placeholder="上传ID"
value={searchParams.upload_id} value={searchParams.upload_id}
onChange={(e) => setSearchParams(prev => ({ ...prev, upload_id: e.target.value }))} onChange={(e) => setSearchParams(prev => ({ ...prev, upload_id: e.target.value }))}
style={{ width: 160 }} style={{ width: 150, borderRadius: 8 }}
allowClear allowClear
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }} onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
/> />
@@ -413,7 +424,7 @@ const MaterialListPage: React.FC = () => {
placeholder="文件名" placeholder="文件名"
value={searchParams.file_name} value={searchParams.file_name}
onChange={(e) => setSearchParams(prev => ({ ...prev, file_name: e.target.value }))} onChange={(e) => setSearchParams(prev => ({ ...prev, file_name: e.target.value }))}
style={{ width: 140 }} style={{ width: 140, borderRadius: 8 }}
allowClear allowClear
onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }} onPressEnter={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
/> />
@@ -421,7 +432,7 @@ const MaterialListPage: React.FC = () => {
placeholder="资源类型" placeholder="资源类型"
value={searchParams.resource_type} value={searchParams.resource_type}
onChange={(value) => setSearchParams(prev => ({ ...prev, resource_type: value }))} onChange={(value) => setSearchParams(prev => ({ ...prev, resource_type: value }))}
style={{ width: 120 }} style={{ width: 110, borderRadius: 8 }}
allowClear allowClear
options={[ options={[
{ value: 'image', label: '图片' }, { value: 'image', label: '图片' },
@@ -431,21 +442,16 @@ const MaterialListPage: React.FC = () => {
<Button <Button
type="primary" type="primary"
onClick={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }} onClick={() => { setCurrentPage(1); loadMaterialList(1, pageSize); }}
style={{ borderRadius: 8, fontWeight: 600, borderColor: 'transparent' }}
> >
</Button> </Button>
</div> </div>
<div style={{ display: 'flex', gap: 12 }}> <div style={{ display: 'flex', gap: 8 }}>
<Button <Button
icon={<RobotOutlined />} icon={<RobotOutlined />}
onClick={() => navigate('/generated?filterType=private_portrait&portraitTab=aigc_virtual')} onClick={() => navigate('/generated?filterType=private_portrait&portraitTab=aigc_virtual')}
style={{ style={{ borderRadius: 8, fontWeight: 600, borderColor: '#8b5cf6', color: '#7c3aed', background: '#f5f3ff' }}
borderRadius: 12,
fontSize: 14,
borderColor: '#8b5cf6',
color: '#7c3aed',
background: '#f5f3ff',
}}
> >
</Button> </Button>
@@ -454,16 +460,9 @@ const MaterialListPage: React.FC = () => {
loading={pushTemplatesLoading} loading={pushTemplatesLoading}
onClick={handleOpenPushModal} onClick={handleOpenPushModal}
disabled={selectedRows.size === 0} disabled={selectedRows.size === 0}
style={{ style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', color: '#fff' }}
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)',
}}
> >
({selectedRows.size}) {selectedRows.size > 0 && `(${selectedRows.size})`}
</Button> </Button>
</div> </div>
</div> </div>