Merge branch 'main' of gitee.com:wg123/video-gen into main
This commit is contained in:
@@ -193,8 +193,8 @@ class HotOpeningTaskCreate(BaseModel):
|
||||
|
||||
material_video_url: str = Field(..., min_length=1, description="素材视频链接,参考素材,1份。由项目已有上传接口返回,本接口不负责上传,不做后端素材校验")
|
||||
material_image_url: str = Field(..., min_length=1, description="素材图片链接,新产品图片,1份。由项目已有上传接口返回,本接口不负责上传,不做后端素材校验")
|
||||
source_project_name: str = Field(..., min_length=1, max_length=120, description="视频素材内容项目名称")
|
||||
target_project_name: str = Field(..., min_length=1, max_length=120, description="生成项目名称")
|
||||
source_project_name: str = Field(..., min_length=1, max_length=20, description="视频素材内容项目名称")
|
||||
target_project_name: str = Field(..., min_length=1, max_length=20, description="生成项目名称")
|
||||
core_content_point: str = Field(..., min_length=1, max_length=50, description="生成的项目核心内容点,最多50字")
|
||||
idempotency_key: str | None = Field(None, max_length=64, description="创建总任务幂等键。只用于 module_generation_projects,不用于 ChatGenerationTask")
|
||||
|
||||
@@ -224,8 +224,8 @@ class HotOpeningMaterialUpdateRequest(BaseModel):
|
||||
|
||||
material_video_url: str | None = Field(None, min_length=1, description="素材视频链接,未传则沿用旧值")
|
||||
material_image_url: str | None = Field(None, min_length=1, description="素材图片链接,未传则沿用旧值")
|
||||
source_project_name: str | None = Field(None, min_length=1, max_length=120, description="视频素材内容项目名称,未传则沿用旧值")
|
||||
target_project_name: str | None = Field(None, min_length=1, max_length=120, description="生成项目名称,未传则沿用旧值")
|
||||
source_project_name: str | None = Field(None, min_length=1, max_length=20, description="视频素材内容项目名称,未传则沿用旧值")
|
||||
target_project_name: str | None = Field(None, min_length=1, max_length=20, description="生成项目名称,未传则沿用旧值")
|
||||
core_content_point: str | None = Field(None, min_length=1, max_length=50, description="生成项目核心内容点,最多50字,未传则沿用旧值")
|
||||
|
||||
@field_validator("material_video_url", "material_image_url", "source_project_name", "target_project_name", "core_content_point", mode="before")
|
||||
@@ -250,8 +250,8 @@ class HotOpeningStepUpdate(BaseModel):
|
||||
|
||||
material_video_url: str | None = Field(None, description="修改第1步素材视频链接")
|
||||
material_image_url: str | None = Field(None, description="修改第1步素材图片链接")
|
||||
source_project_name: str | None = Field(None, max_length=120, description="修改第1步视频素材内容项目名称")
|
||||
target_project_name: str | None = Field(None, max_length=120, description="修改第1步生成项目名称")
|
||||
source_project_name: str | None = Field(None, max_length=20, description="修改第1步视频素材内容项目名称")
|
||||
target_project_name: str | None = Field(None, max_length=20, description="修改第1步生成项目名称")
|
||||
core_content_point: str | None = Field(None, max_length=50, description="修改第1步生成项目核心内容点,最多50字")
|
||||
prompt: str | None = Field(None, description="修改第2步图片提词或第4步视频最终提词")
|
||||
prompt_schema: dict[str, Any] | None = Field(None, description="修改第4步视频提词 JSON schema。只对视频提词步骤有意义")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import certifi
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from sqlalchemy import select
|
||||
@@ -156,7 +157,13 @@ def _get_alipay_client(app_id: str, private_key: str, public_key: str, gateway:
|
||||
config.alipay_public_key = public_key
|
||||
config.sign_type = "RSA2"
|
||||
config.charset = "utf-8"
|
||||
|
||||
config.cert_path = certifi.where()
|
||||
logger.info(
|
||||
f"Initializing Alipay client: app_id={app_id}, gateway={config.server_url}, "
|
||||
f"public_key={config.alipay_public_key}, "
|
||||
f"private_key={config.app_private_key}, "
|
||||
f"cert_path={config.cert_path}"
|
||||
)
|
||||
try:
|
||||
_alipay_client = DefaultAlipayClient(config)
|
||||
_alipay_client_app_id = app_id
|
||||
|
||||
@@ -1 +1,20 @@
|
||||
{}
|
||||
{
|
||||
"src/pages/GenerateConver.tsx": {
|
||||
"description": "ai创建"
|
||||
},
|
||||
"src/pages/GeneratedRecord.tsx": {
|
||||
"description": "生成历史"
|
||||
},
|
||||
"src/pages/GeneratePage.tsx": {
|
||||
"description": "生成记录"
|
||||
},
|
||||
"src/pages/ProjectsPage.tsx": {
|
||||
"description": "我的项目"
|
||||
},
|
||||
"src/pages/RemoveLens.tsx": {
|
||||
"description": "拆镜复刻"
|
||||
},
|
||||
"src/pages/InitialReplication.tsx": {
|
||||
"description": "爆款开头复刻"
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import InitialInfo from './pages/InitialInfo';
|
||||
import RemoveLens from './pages/RemoveLens';
|
||||
import GeneratedRecord from './pages/GeneratedRecord';
|
||||
import AuthorizationPage from './pages/AuthorizationPage';
|
||||
import RemoveInfo from './pages/RemoveInfo';
|
||||
|
||||
|
||||
|
||||
@@ -96,6 +97,7 @@ const App = () => {
|
||||
<Route path="initial" element={<InitialReplication />} />
|
||||
<Route path="initial/:creatID/initialinfo" element={<InitialInfo />} />
|
||||
<Route path="removelens" element={<RemoveLens />} />
|
||||
<Route path="removelens/:creatID/removeinfo" element={<RemoveInfo />} />
|
||||
<Route path="generated" element={<GeneratedRecord />} />
|
||||
<Route path="authorization" element={<AuthorizationPage />} />
|
||||
|
||||
|
||||
@@ -1066,10 +1066,10 @@ const GeneratedRecord: React.FC = () => {
|
||||
<div>
|
||||
<Button
|
||||
|
||||
style={{width:'100%', borderRadius: 8 ,marginTop:20,color:'#4c49cc'}}
|
||||
style={{ width: '100%', borderRadius: 8, marginTop: 20, color: '#4c49cc' }}
|
||||
>
|
||||
|
||||
推送巨量引擎后台
|
||||
推送媒体后台
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ function InitialInfo() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ height: '94vh', background: '#f8fafc' }}>
|
||||
<div style={{ height: 'calc(94vh)', padding: 24, }}>
|
||||
<div style={{ height: 'calc(94vh)', }}>
|
||||
<div style={{ height: '100%', display: 'flex', justifyContent: 'space-between', gap: '2%' }}>
|
||||
<div style={{ width: '70%', background: '#fff', borderRadius: 12, overflowY: 'auto' }}>
|
||||
<div style={{ borderBottom: '1px solid #e2e8f0', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px', boxSizing: 'border-box' }}>
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Button, Table, Tag, Drawer, Input, Upload, message } from 'antd';
|
||||
import { ArrowLeftOutlined, PlayCircleOutlined, XOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import type { UploadFile } from 'antd';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
function RemoveInfo() {
|
||||
const { creatID } = useParams<{ creatID: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [drawerVisible, setDrawerVisible] = useState(false);
|
||||
const [currentSegment, setCurrentSegment] = useState<number | null>(null);
|
||||
const [productName, setProductName] = useState('');
|
||||
const [productSellingPoint, setProductSellingPoint] = useState('');
|
||||
const [productImage, setProductImage] = useState('');
|
||||
const [detailImage, setDetailImage] = useState('');
|
||||
|
||||
const handleGenerate = (segmentId: number) => {
|
||||
setCurrentSegment(segmentId);
|
||||
setDrawerVisible(true);
|
||||
};
|
||||
|
||||
const handleCloseDrawer = () => {
|
||||
setDrawerVisible(false);
|
||||
setCurrentSegment(null);
|
||||
setProductName('');
|
||||
setProductSellingPoint('');
|
||||
setProductImage('');
|
||||
setDetailImage('');
|
||||
};
|
||||
|
||||
const handleProductImageChange: any = (info: any) => {
|
||||
if (info.fileList.length > 0) {
|
||||
const file = info.fileList[0];
|
||||
if (file.originFileObj) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
setProductImage(e.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file.originFileObj);
|
||||
}
|
||||
} else {
|
||||
setProductImage('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDetailImageChange: any = (info: any) => {
|
||||
if (info.fileList.length > 0) {
|
||||
const file = info.fileList[0];
|
||||
if (file.originFileObj) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
setDetailImage(e.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file.originFileObj);
|
||||
}
|
||||
} else {
|
||||
setDetailImage('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleManualGenerate = () => {
|
||||
// 必填校验
|
||||
if (!productImage) {
|
||||
message.warning('请上传产品图');
|
||||
return;
|
||||
}
|
||||
if (!productName.trim()) {
|
||||
message.warning('请输入产品名称');
|
||||
return;
|
||||
}
|
||||
if (!productSellingPoint.trim()) {
|
||||
message.warning('请输入产品卖点');
|
||||
return;
|
||||
}
|
||||
|
||||
// 输出内容
|
||||
console.log('手动生成 - 片段', currentSegment);
|
||||
console.log('产品图:', productImage);
|
||||
console.log('细节图:', detailImage);
|
||||
console.log('产品名称:', productName);
|
||||
console.log('产品卖点:', productSellingPoint);
|
||||
|
||||
message.success(`手动生成成功!片段: ${currentSegment}`);
|
||||
};
|
||||
|
||||
const mockData = {
|
||||
productName: '返回',
|
||||
uploadTime: '2026-06-09 09:01:16',
|
||||
sellingPoints: ['一键匹配', '连麦聊天'],
|
||||
audience: '123123',
|
||||
audienceAnalysis: '123123',
|
||||
videoUrl: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=320&h=180&fit=crop',
|
||||
segments: [
|
||||
{
|
||||
key: '1',
|
||||
id: 1,
|
||||
timeRange: '00:00 - 00:03',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=120&h=80&fit=crop',
|
||||
content: '11111',
|
||||
lines: 'qqqqqqqqqqq',
|
||||
contentStrategy: '展示礼盒'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
id: 2,
|
||||
timeRange: '00:03 - 00:06',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=120&h=80&fit=crop',
|
||||
content: '1231231231',
|
||||
lines: 'qqqqqqqqqqq',
|
||||
contentStrategy: '开箱展示'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
id: 3,
|
||||
timeRange: '00:06 - 00:09',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=120&h=80&fit=crop',
|
||||
content: '123123',
|
||||
lines: 'qqqqqqqqqqq',
|
||||
contentStrategy: '取出产品'
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '片段',
|
||||
width: 100,
|
||||
render: (text: any, record: any) => (
|
||||
<div>
|
||||
<div style={{ fontSize: 16, fontWeight: 600, color: '#333' }}>片段{record.id}</div>
|
||||
<div style={{ fontSize: 12, color: '#999' }}>{record.timeRange}</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '片段视频',
|
||||
width: 120,
|
||||
render: (text: any, record: any) => (
|
||||
<div style={{ position: 'relative', width: 120, height: 80, borderRadius: 6, overflow: 'hidden' }}>
|
||||
<img
|
||||
src={record.thumbnail}
|
||||
alt={`片段${record.id}`}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 28,
|
||||
height: 28,
|
||||
background: 'rgba(0,0,0,0.6)',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<PlayCircleOutlined style={{ fontSize: 16, color: '#fff' }} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '画面内容',
|
||||
width: 250,
|
||||
|
||||
render: (text: any, record: any) => (
|
||||
<div style={{ fontSize: 14, color: '#333', lineHeight: 1.6 }}>
|
||||
{record.content}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '台词',
|
||||
width: 250,
|
||||
|
||||
render: (text: any, record: any) => (
|
||||
<div style={{ fontSize: 14, color: '#333', lineHeight: 1.6 }}>
|
||||
{record.lines}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '内容策略',
|
||||
width: 120,
|
||||
align: 'left' as const,
|
||||
render: (text: any, record: any) => (
|
||||
<div style={{ fontSize: 14, color: '#333', fontWeight: 500 }}>
|
||||
{record.contentStrategy || '-'}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '素材',
|
||||
width: 140,
|
||||
align: 'center' as const,
|
||||
render: (text: any, record: any) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{ fontSize: 12, color: '#999', padding: '12px 24px', border: '1px dashed #ddd', borderRadius: 4 }}>
|
||||
等待生成
|
||||
</div>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => handleGenerate(record.id)}
|
||||
style={{ color: '#656efa', fontSize: 12, padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
|
||||
>
|
||||
视频生成
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ minHeight: '94vh', background: '#f5f5f5' }}>
|
||||
<div style={{ background: '#fff', padding: '16px 24px 0 24px', boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate(-1)}
|
||||
style={{ fontSize: 16, color: '#666' }}
|
||||
/>
|
||||
<span style={{ fontSize: 18, fontWeight: 600 }}>{mockData.productName}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ }}>
|
||||
<div style={{ background: '#fff', borderRadius: 12, padding: '10px 20px 20px 20px', marginBottom: 20 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 30, marginBottom: 12 }}>
|
||||
<div style={{ position: 'relative', width: 280, height: 160, borderRadius: 8, overflow: 'hidden', flexShrink: 0 }}>
|
||||
<img
|
||||
src={mockData.videoUrl}
|
||||
alt="视频缩略图"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 48,
|
||||
height: 48,
|
||||
background: 'rgba(0,0,0,0.6)',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<PlayCircleOutlined style={{ fontSize: 28, color: '#fff' }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginBottom: 16 }}>视频总结</h2>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<span style={{ color: '#999', fontSize: 12 }}>上传时间: {mockData.uploadTime}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style={{ display: 'flex', marginBottom: 12 }}>
|
||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12 }}>产品名称:</span>
|
||||
<span style={{ color: '#333', fontWeight: 500 }}>{mockData.productName}</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', marginBottom: 12 }}>
|
||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12 }}>卖点词:</span>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
{mockData.sellingPoints.map((point, index) => (
|
||||
<Tag key={index} color="purple" style={{ fontSize: 12 }}>
|
||||
{point}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', marginBottom: 12 }}>
|
||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12 }}>受众群体:</span>
|
||||
<span style={{ color: '#333' }}>{mockData.audience}</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12 }}>受众分析:</span>
|
||||
<span style={{ color: '#333' }}>{mockData.audienceAnalysis}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ background: '#fff', borderRadius: 12, overflow: 'hidden' }}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={mockData.segments}
|
||||
pagination={false}
|
||||
bordered={false}
|
||||
rowKey="key"
|
||||
scroll={{ y: 520 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
title={
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
|
||||
<span>
|
||||
<span style={{ color: '#6366f1' }}>智能视频复刻</span>
|
||||
<span style={{ color: '#6366f1' }}>-片段{currentSegment}</span>
|
||||
</span>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<XOutlined />}
|
||||
onClick={handleCloseDrawer}
|
||||
style={{ padding: 0 }}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={handleCloseDrawer}
|
||||
open={drawerVisible}
|
||||
width={480}
|
||||
bodyStyle={{ padding: '24px' }}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<div>
|
||||
<label style={{ fontWeight: 400, color: '#333', marginBottom: 12, display: 'block' }}>
|
||||
产品白底图 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||
</label>
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
onChange={handleProductImageChange}
|
||||
maxCount={1}
|
||||
accept="image/*"
|
||||
style={{ width: 140, height: 140 }}
|
||||
>
|
||||
{!productImage && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
|
||||
<PlusOutlined style={{ fontSize: 24, color: '#999' }} />
|
||||
<span style={{ fontSize: 12, color: '#999' }}>产品图 *</span>
|
||||
</div>
|
||||
)}
|
||||
</Upload>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
onChange={handleDetailImageChange}
|
||||
maxCount={1}
|
||||
accept="image/*"
|
||||
style={{ width: 140, height: 140 }}
|
||||
>
|
||||
{!detailImage && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
|
||||
<PlusOutlined style={{ fontSize: 24, color: '#999' }} />
|
||||
<span style={{ fontSize: 12, color: '#999' }}>细节图</span>
|
||||
</div>
|
||||
)}
|
||||
</Upload>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style={{ fontWeight: 500, color: '#333', marginBottom: 12, display: 'block' }}>
|
||||
产品名称
|
||||
</label>
|
||||
<Input
|
||||
value={productName}
|
||||
onChange={(e) => setProductName(e.target.value)}
|
||||
placeholder="请输入产品名称"
|
||||
style={{ height: 48, borderRadius: 8 }}
|
||||
maxLength={10}
|
||||
showCount
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style={{ fontWeight: 500, color: '#333', marginBottom: 12, display: 'block' }}>
|
||||
产品卖点
|
||||
</label>
|
||||
<TextArea
|
||||
value={productSellingPoint}
|
||||
onChange={(e) => setProductSellingPoint(e.target.value)}
|
||||
placeholder="请输入产品卖点"
|
||||
style={{ borderRadius: 8 }}
|
||||
maxLength={100}
|
||||
showCount
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 16, marginTop: 24 }}>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={handleManualGenerate}
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 48,
|
||||
borderRadius: 8,
|
||||
borderColor: '#6366f1',
|
||||
color: '#6366f1',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
手动生成
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default RemoveInfo;
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user