拆镜复刻跳转修改
This commit is contained in:
+41
-41
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-ZwwuCCw2.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DTCz7Wbv.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -501,8 +501,11 @@ export async function createShotReplication(params: any): Promise<any> {
|
||||
return api.post('/shot-replications/task-sets', params);
|
||||
}
|
||||
// 获取镜头复刻任务列表
|
||||
export async function getShotReplicationList(page: number, page_size: number): Promise<any> {
|
||||
return api.get(`/shot-replications/task-sets?page=${page}&page_size=${page_size}`);
|
||||
export async function getShotReplicationList(page: number, page_size: number, keyword?: string): Promise<any> {
|
||||
const url = keyword
|
||||
? `/shot-replications/task-sets?page=${page}&page_size=${page_size}&keyword=${encodeURIComponent(keyword)}`
|
||||
: `/shot-replications/task-sets?page=${page}&page_size=${page_size}`;
|
||||
return api.get(url);
|
||||
}
|
||||
// 获取镜头复刻任务详情
|
||||
export async function getShotReplicationDetail(taskSetId: string): Promise<any> {
|
||||
|
||||
@@ -552,23 +552,27 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
|
||||
<Text style={{ marginLeft: 8, color: '#64748b' }}>正在抽取每秒帧...</Text>
|
||||
</div>
|
||||
) : frames.length > 0 ? (
|
||||
frames.map((frame) => (
|
||||
<button
|
||||
key={frame.second}
|
||||
type="button"
|
||||
onClick={() => handleFrameClick(frame.second)}
|
||||
title={`${frame.second}s${frame.fallback ? `,实际取帧 ${frame.captureSecond?.toFixed(1)}s` : ''}`}
|
||||
style={{
|
||||
minWidth: 0,
|
||||
height: 86,
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
background: 'transparent',
|
||||
overflow: 'hidden',
|
||||
cursor: disabledByDuration ? 'not-allowed' : 'pointer',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
frames.map((frame) => {
|
||||
const isSelected = frame.second >= range[0] && frame.second < range[1];
|
||||
return (
|
||||
<button
|
||||
key={frame.second}
|
||||
type="button"
|
||||
onClick={() => handleFrameClick(frame.second)}
|
||||
title={`${frame.second}s${frame.fallback ? `,实际取帧 ${frame.captureSecond?.toFixed(1)}s` : ''}`}
|
||||
style={{
|
||||
minWidth: 0,
|
||||
height: 86,
|
||||
borderTop: isSelected ? '5px solid #6366f1' : 'none',
|
||||
borderBottom: isSelected ? '5px solid #6366f1' : 'none',
|
||||
padding: 0,
|
||||
background: 'transparent',
|
||||
overflow: 'hidden',
|
||||
cursor: disabledByDuration ? 'not-allowed' : 'pointer',
|
||||
position: 'relative',
|
||||
// borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{frame.status === 'success' && frame.image ? (
|
||||
<img src={frame.image} alt={`${frame.second}s`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
|
||||
) : frame.status === 'loading' ? (
|
||||
@@ -594,7 +598,8 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
|
||||
{frame.second}s
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div style={{ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#64748b' }}>
|
||||
暂无帧预览,请确认视频是否加载完成
|
||||
@@ -603,7 +608,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
|
||||
|
||||
</div>
|
||||
|
||||
<div style={{ position: 'absolute', top: -6, left: 10, right: 0, padding: '0 8px 0' }}>
|
||||
<div style={{ position: 'absolute', top: -6, left: 6, right: 0, }}>
|
||||
<Slider
|
||||
range
|
||||
min={0}
|
||||
|
||||
@@ -1070,42 +1070,7 @@ const AIChatPage: React.FC = () => {
|
||||
<div style={{ flex: 1 }}>
|
||||
{/* 时间戳和参数信息 */}
|
||||
|
||||
<div style={{ margin: 4, fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
|
||||
<span>{msg.createdAt?.replace('T', ' ').split('.')[0]}</span>
|
||||
{/* 引擎标签 */}
|
||||
<span >
|
||||
{/* <SettingsOutlined style={{ fontSize: 12 }} /> */}
|
||||
{msg.engineSnapshot.name}
|
||||
</span>
|
||||
{/* 参数标签 */}
|
||||
<span >
|
||||
{/* <LayoutGridOutlined style={{ fontSize: 12 }} /> */}
|
||||
{msg.genType === 'image'
|
||||
? `${msg.imageProportion || ''} · ${msg.imagePx || ''} · ${msg.imageSize || ''}`
|
||||
: `${msg.duration || ''}s · ${msg.aspectRatio || ''} · ${msg.resolution || ''}`
|
||||
}
|
||||
</span>
|
||||
<span style={{ marginLeft: 8 }}>消耗积分:{msg.creditsCost}</span>
|
||||
{msg.mediaReferences && msg.mediaReferences.length > 0 && (
|
||||
<span
|
||||
style={{ marginLeft: 20, color: '#6366f1', cursor: 'pointer' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
setAttachmentPopupPosition({
|
||||
x: rect.left,
|
||||
y: rect.top - 10
|
||||
});
|
||||
setAttachmentPopupMessageId(msg.id);
|
||||
setAttachmentPopupVisible(true);
|
||||
}}
|
||||
>
|
||||
附件详情
|
||||
</span>
|
||||
|
||||
)}
|
||||
</div>
|
||||
{/* 消息气泡 */}
|
||||
<div
|
||||
style={{
|
||||
@@ -1114,13 +1079,17 @@ const AIChatPage: React.FC = () => {
|
||||
borderRadius: '16px 16px 16px 4px',
|
||||
padding: '12px 16px',
|
||||
width: '70%',
|
||||
maxWidth: 800,
|
||||
minWidth: 500,
|
||||
boxSizing: 'border-box',
|
||||
boxShadow: '0 4px 20px rgba(99, 102, 241, 0.08), 0 1px 3px rgba(0,0,0,0.04)',
|
||||
position: 'relative',
|
||||
border: '1px solid rgba(99, 102, 241, 0.06)',
|
||||
}}
|
||||
>
|
||||
<div style={{ margin: 4, fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
|
||||
<span>{msg.createdAt?.replace('T', ' ').split('.')[0]}</span>
|
||||
|
||||
</div>
|
||||
{/* 删除按钮 - 右上角 */}
|
||||
<div style={{ position: 'absolute', top: 8, right: 8, zIndex: 100 }}>
|
||||
<Popconfirm
|
||||
@@ -1173,7 +1142,7 @@ const AIChatPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* 文本内容 */}
|
||||
<div
|
||||
{/* <div
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
@@ -1204,7 +1173,7 @@ const AIChatPage: React.FC = () => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
{/* 默认显示:一行省略 */}
|
||||
默认显示:一行省略
|
||||
<div style={{
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
@@ -1215,7 +1184,7 @@ const AIChatPage: React.FC = () => {
|
||||
{msg.originalPrompt}
|
||||
</div>
|
||||
|
||||
{/* 鼠标移入显示:完整内容 */}
|
||||
鼠标移入显示:完整内容
|
||||
<div style={{
|
||||
width: '100%',
|
||||
maxHeight: 200,
|
||||
@@ -1225,7 +1194,7 @@ const AIChatPage: React.FC = () => {
|
||||
}}>
|
||||
{msg.originalPrompt}
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* 根据 status 显示不同内容 */}
|
||||
{/* 生成中 - 显示加载动画 */}
|
||||
@@ -1304,14 +1273,14 @@ const AIChatPage: React.FC = () => {
|
||||
)}
|
||||
{/* 已完成 - 显示媒体内容 */}
|
||||
{msg.status === 'completed' && (
|
||||
<div style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: 8, marginBottom: 10, marginTop: 12, width: '100%', }}>
|
||||
<div style={{ display: 'flex', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: 8, marginBottom: 10, marginTop: 12, width: '100%', }}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setPreviewUrl(msg.genType === 'image' ? msg.imageUrl : msg.videoUrl);
|
||||
setPreviewType(msg.genType === 'image' ? 'image' : 'video');
|
||||
setPreviewVisible(true);
|
||||
}}
|
||||
style={{ cursor: 'pointer', overflow: 'hidden', borderRadius: 8, position: 'relative', height: 200 }}
|
||||
style={{ width: '50%', cursor: 'pointer', overflow: 'hidden', borderRadius: 8, position: 'relative', height: 200 }}
|
||||
>
|
||||
{msg.genType === 'image' ? (
|
||||
<img
|
||||
@@ -1353,6 +1322,46 @@ const AIChatPage: React.FC = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ width: '50%', }}>
|
||||
<p style={{ marginBottom: 20 ,height: 100, overflow: 'auto', padding: 0, margin: 0 }}>
|
||||
{msg.originalPrompt}
|
||||
</p>
|
||||
<div style={{fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
|
||||
{/* 引擎标签 */}
|
||||
<span >
|
||||
{/* <SettingsOutlined style={{ fontSize: 12 }} /> */}
|
||||
{msg.engineSnapshot.name}
|
||||
</span>
|
||||
{/* 参数标签 */}
|
||||
<span >
|
||||
{/* <LayoutGridOutlined style={{ fontSize: 12 }} /> */}
|
||||
{msg.genType === 'image'
|
||||
? `${msg.imageProportion || ''} · ${msg.imagePx || ''} · ${msg.imageSize || ''}`
|
||||
: `${msg.duration || ''}s · ${msg.aspectRatio || ''} · ${msg.resolution || ''}`
|
||||
}
|
||||
</span>
|
||||
<span style={{ marginLeft: 8 }}>消耗积分:{msg.creditsCost}</span>
|
||||
{msg.mediaReferences && msg.mediaReferences.length > 0 && (
|
||||
<span
|
||||
style={{ marginLeft: 20, color: '#6366f1', cursor: 'pointer' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
setAttachmentPopupPosition({
|
||||
x: rect.left,
|
||||
y: rect.top - 10
|
||||
});
|
||||
setAttachmentPopupMessageId(msg.id);
|
||||
setAttachmentPopupVisible(true);
|
||||
}}
|
||||
>
|
||||
附件详情
|
||||
</span>
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ function InitialInfo() {
|
||||
const [taskDetail, setTaskDetail] = useState<any>({});
|
||||
// API 返回的步骤数据
|
||||
const [apiSteps, setApiSteps] = useState<any[]>([]);
|
||||
// 当前展开的步骤
|
||||
const [activeKey, setActiveKey] = useState<string[]>([]);
|
||||
|
||||
//
|
||||
const baseSteps = [
|
||||
@@ -76,6 +78,21 @@ function InitialInfo() {
|
||||
engineId: apiSteps[index]?.input?.payload?.videoConfig?.engineId || '',
|
||||
}));
|
||||
|
||||
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
|
||||
useEffect(() => {
|
||||
for (let i = steps.length - 1; i >= 0; i--) {
|
||||
if (steps[i].status === 'completed' || steps[i].status === 'failed') {
|
||||
setActiveKey([String(steps[i].childId)]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setActiveKey([]);
|
||||
}, [apiSteps]);
|
||||
|
||||
// console.log('steps', steps);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -367,7 +384,7 @@ function InitialInfo() {
|
||||
}
|
||||
const agincreatevideo = () => {
|
||||
let params = {
|
||||
engine_id: steps[2].engineId,
|
||||
engine_id: steps[3].engineId,
|
||||
}
|
||||
|
||||
|
||||
@@ -553,13 +570,14 @@ function InitialInfo() {
|
||||
</div>
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '8px 16px 16px' }}>
|
||||
<Collapse
|
||||
defaultActiveKey={['']}
|
||||
activeKey={activeKey}
|
||||
onChange={setActiveKey}
|
||||
ghost
|
||||
bordered={false}
|
||||
style={{ background: 'transparent' }}
|
||||
expandIconPlacement="end"
|
||||
items={steps.map((step) => ({
|
||||
key: String(step.id),
|
||||
key: String(step.childId),
|
||||
label: (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function VideoFrameExtractor() {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
@@ -81,8 +82,21 @@ export default function VideoFrameExtractor() {
|
||||
idempotency_key: `shot_${Date.now()}`,
|
||||
};
|
||||
|
||||
await createShotReplication(params);
|
||||
message.success('任务创建成功');
|
||||
createShotReplication(params).then((res) => {
|
||||
// message.success('任务创建成功');
|
||||
getShotReplicationList(1, 20, searchKeyword).then((res) => {
|
||||
// console.log('获取列表成功', res.items[0].id);
|
||||
message.loading('创建中...', 3);
|
||||
|
||||
setTimeout(() => {
|
||||
navigate(`/removelens/${res.items[0].id}/removeinfo`);
|
||||
}, 3000);
|
||||
|
||||
// setTableData(res.items || []);
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
cleanupResources();
|
||||
} catch (err) {
|
||||
message.error('任务创建失败,请重试');
|
||||
@@ -91,9 +105,9 @@ export default function VideoFrameExtractor() {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchList = async (page: number, size: number) => {
|
||||
const fetchList = async (page: number, size: number, keyword?: string) => {
|
||||
try {
|
||||
const res = await getShotReplicationList(page, size);
|
||||
const res = await getShotReplicationList(page, size, keyword);
|
||||
setTableData(res.items || []);
|
||||
setTotal(res.total || 0);
|
||||
setCurrentPage(page);
|
||||
@@ -104,12 +118,16 @@ export default function VideoFrameExtractor() {
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number, size: number) => {
|
||||
fetchList(page, size);
|
||||
fetchList(page, size, searchKeyword);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
fetchList(1, 10, searchKeyword);
|
||||
};
|
||||
|
||||
const handleOpenModal = () => {
|
||||
setIsModalOpen(true);
|
||||
fetchList(1, 10);
|
||||
fetchList(1, 10, searchKeyword);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -127,7 +145,7 @@ export default function VideoFrameExtractor() {
|
||||
|
||||
|
||||
<div style={{ maxWidth: 1200, margin: '50px auto', position: 'relative', zIndex: 10 }}>
|
||||
<div style={{ marginBottom: 50,height: 160, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div style={{ marginBottom: 50, height: 160, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
|
||||
<div>
|
||||
@@ -440,13 +458,16 @@ export default function VideoFrameExtractor() {
|
||||
width={800}
|
||||
footer={null}
|
||||
styles={{
|
||||
body: { background: 'linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%)' },
|
||||
body: { background: 'linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%)' },
|
||||
header: { background: '#fff', borderBottom: '1px solid rgba(99, 102, 241, 0.1)', padding: '20px 24px' },
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16, gap: 8 }}>
|
||||
<Input
|
||||
placeholder="搜索产品名称"
|
||||
value={searchKeyword}
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
onPressEnter={handleSearch}
|
||||
style={{
|
||||
width: 200,
|
||||
borderRadius: 10,
|
||||
@@ -456,6 +477,7 @@ export default function VideoFrameExtractor() {
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSearch}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
height: 40,
|
||||
|
||||
@@ -52,10 +52,13 @@ function InitialInfo() {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
// 任务详情数据
|
||||
const [taskDetail, setTaskDetail] = useState<any>({});
|
||||
// API 返回的步骤数据
|
||||
const [apiSteps, setApiSteps] = useState<any[]>([]);
|
||||
// 当前展开的步骤
|
||||
const [activeKey, setActiveKey] = useState<string[]>([]);
|
||||
|
||||
//
|
||||
const baseSteps = [
|
||||
@@ -75,6 +78,17 @@ function InitialInfo() {
|
||||
engineId: apiSteps[index]?.input?.payload?.videoConfig?.engineId || '',
|
||||
}));
|
||||
|
||||
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
|
||||
useEffect(() => {
|
||||
for (let i = steps.length - 1; i >= 0; i--) {
|
||||
if (steps[i].status === 'completed' || steps[i].status === 'failed') {
|
||||
setActiveKey([String(steps[i].childId)]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setActiveKey([]);
|
||||
}, [apiSteps]);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -82,8 +96,8 @@ function InitialInfo() {
|
||||
|
||||
|
||||
// 获取列表数据的函数
|
||||
const fetchList = (page: number, size: number) => {
|
||||
getShotReplicationList(page, size).then((res: any) => {
|
||||
const fetchList = (page: number, size: number, keyword?: string) => {
|
||||
getShotReplicationList(page, size, keyword).then((res: any) => {
|
||||
if (res.items) {
|
||||
setTableData(res.items);
|
||||
}
|
||||
@@ -98,12 +112,17 @@ function InitialInfo() {
|
||||
const handlePageChange = (page: number, size: number) => {
|
||||
setCurrentPage(page);
|
||||
setPageSize(size);
|
||||
fetchList(page, size);
|
||||
fetchList(page, size, searchKeyword);
|
||||
};
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
fetchList(1, 20, searchKeyword);
|
||||
};
|
||||
|
||||
// 获取复刻列表数据
|
||||
useEffect(() => {
|
||||
fetchList(currentPage, pageSize);
|
||||
fetchList(currentPage, pageSize, searchKeyword);
|
||||
}, []);
|
||||
|
||||
// 获取引擎列表
|
||||
@@ -371,7 +390,7 @@ function InitialInfo() {
|
||||
}
|
||||
const agincreatevideo = () => {
|
||||
let params = {
|
||||
engine_id: steps[2].engineId,
|
||||
engine_id: steps[3].engineId,
|
||||
}
|
||||
|
||||
|
||||
@@ -422,7 +441,7 @@ function InitialInfo() {
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate(-1)}
|
||||
onClick={() => navigate('/removelens')}
|
||||
style={{
|
||||
color: '#64748b',
|
||||
borderRadius: 10,
|
||||
@@ -551,13 +570,14 @@ function InitialInfo() {
|
||||
</div>
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '8px 16px 16px' }}>
|
||||
<Collapse
|
||||
defaultActiveKey={['']}
|
||||
activeKey={activeKey}
|
||||
onChange={setActiveKey}
|
||||
ghost
|
||||
bordered={false}
|
||||
style={{ background: 'transparent' }}
|
||||
expandIconPlacement="end"
|
||||
items={steps.map((step) => ({
|
||||
key: String(step.id),
|
||||
key: String(step.childId),
|
||||
label: (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
@@ -1199,6 +1219,9 @@ function InitialInfo() {
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16, gap: 8 }}>
|
||||
<Input
|
||||
placeholder="搜索产品名称"
|
||||
value={searchKeyword}
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
onPressEnter={handleSearch}
|
||||
style={{
|
||||
width: 200,
|
||||
borderRadius: 10,
|
||||
@@ -1208,6 +1231,7 @@ function InitialInfo() {
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSearch}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
height: 40,
|
||||
|
||||
Reference in New Issue
Block a user