如何封装一个 AI 图片生成组件,支持选择模型、输入 prompt、展示图像并支持本地保存?
以下是封装一个高可复用 AI 图片生成组件的完整方案,基于 React + TypeScript 实现,包含模型选择、Prompt 输入、实时展示和本地保存功能:
一、组件架构设计
typescript
interface ImageGenConfig {
model: string; // 选择的模型ID
prompt: string; // 用户输入的提示词
negativePrompt?: string; // 可选负面提示词
width: number; // 生成图片宽度
height: number; // 生成图片高度
}
type ImageGenStatus = 'idle' | 'generating' | 'success' | 'error';二、核心功能实现
1. 模型选择模块
tsx
const ModelSelector = ({ models, onChange }: {
models: Array<{ id: string; name: string }>;
onChange: (modelId: string) => void;
}) => (
<select
onChange={(e) => onChange(e.target.value)}
className="model-selector"
>
{models.map(model => (
<option key={model.id} value={model.id}>
{model.name}
{model.id.includes('xl') && ' (高清)'}
</option>
))}
</select>
);2. Prompt 输入增强
tsx
const PromptInput = ({ onGenerate }: {
onGenerate: (config: ImageGenConfig) => void;
}) => {
const [prompt, setPrompt] = useState('');
const [negativePrompt, setNegativePrompt] = useState('');
const handleSubmit = () => {
onGenerate({
model: 'sd-xl-1.0',
prompt,
negativePrompt,
width: 1024,
height: 1024
});
};
return (
<div className="prompt-editor">
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="描述你想要生成的图像..."
/>
<AdvancedOptions>
<input
value={negativePrompt}
onChange={(e) => setNegativePrompt(e.target.value)}
placeholder="不希望出现的元素"
/>
</AdvancedOptions>
<button onClick={handleSubmit}>生成</button>
</div>
);
};3. 图片生成与展示
tsx
const ImageDisplay = ({ imageData, status }: {
imageData: string | null;
status: ImageGenStatus;
}) => {
if (status === 'generating') {
return <ProgressBar />;
}
return imageData ? (
<div className="image-container">
<img
src={`data:image/png;base64,${imageData}`}
alt="生成的AI图片"
onLoad={() => URL.revokeObjectURL(imageData)}
/>
<ImageToolbar imageData={imageData} />
</div>
) : (
<Placeholder />
);
};4. 本地保存功能
tsx
const saveImage = (base64Data: string, fileName: string) => {
const link = document.createElement('a');
link.href = `data:image/png;base64,${base64Data}`;
link.download = `${fileName}_${Date.now()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 在组件中使用
<button onClick={() => saveImage(imageData!, prompt.substring(0, 20))}>
保存图片
</button>三、状态管理与 API 集成
1. 封装 AI 服务调用
typescript
const generateImage = async (
config: ImageGenConfig,
onProgress?: (progress: number) => void
): Promise<string> => {
const response = await fetch('/api/generate-image', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
if (!response.ok) throw new Error('生成失败');
const reader = response.body?.getReader();
const chunks: Uint8Array[] = [];
let receivedLength = 0;
const contentLength = parseInt(response.headers.get('Content-Length') || '0');
while (reader) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
receivedLength += value.length;
onProgress?.(Math.round((receivedLength / contentLength) * 100));
}
const blob = new Blob(chunks, { type: 'image/png' });
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve((reader.result as string).split(',')[1]);
reader.readAsDataURL(blob);
});
};2. 组件状态整合
tsx
const AIImageGenerator = () => {
const [config, setConfig] = useState<ImageGenConfig>(defaultConfig);
const [imageData, setImageData] = useState<string | null>(null);
const [status, setStatus] = useState<ImageGenStatus>('idle');
const handleGenerate = async () => {
setStatus('generating');
try {
const data = await generateImage(config, (progress) => {
console.log(`生成进度: ${progress}%`);
});
setImageData(data);
setStatus('success');
} catch (error) {
setStatus('error');
console.error(error);
}
};
return (
<div className="ai-image-generator">
<ModelSelector
models={availableModels}
onChange={(model) => setConfig({...config, model})}
/>
<PromptInput onGenerate={handleGenerate} />
<ImageDisplay imageData={imageData} status={status} />
</div>
);
};四、增强功能实现
1. 生成历史记录
tsx
const [history, setHistory] = useState<Array<{
config: ImageGenConfig;
imageData: string;
timestamp: number;
}>>([]);
const addToHistory = (item: typeof history[0]) => {
setHistory(prev => [item, ...prev.slice(0, 9)]);
};2. 响应式布局
css
.image-container {
position: relative;
max-width: 100%;
aspect-ratio: 1/1;
background: #f0f0f0;
}
@media (min-width: 768px) {
.ai-image-generator {
grid-template-columns: 300px 1fr;
}
}3. 安全防护
typescript
// 在提交前过滤敏感词
const filterPrompt = (prompt: string) => {
const blockedTerms = ['暴力', '仇恨言论'];
return blockedTerms.some(term => prompt.includes(term))
? null
: prompt;
};五、组件使用示例
tsx
// 在父组件中
const App = () => {
const availableModels = [
{ id: 'sd-xl-1.0', name: 'Stable Diffusion XL' },
{ id: 'karlo-v1', name: 'Kakao Karlo' }
];
return (
<AIImageGenerator
models={availableModels}
apiKey={import.meta.env.VITE_API_KEY}
/>
);
};题目要点:
- 分层设计:分离模型选择、Prompt输入、图片展示为独立子组件
- 类型安全:使用 TypeScript 严格定义接口和状态类型
- 性能优化:流式接收图片数据并显示进度
- 可扩展性:通过配置对象支持未来新增生成参数
- 用户体验:包含生成状态反馈和历史记录功能
- 安全性:前端基础内容过滤+后端二次验证