import { Button } from '@/components/ui/button'; import { Form } from '@/components/ui/form'; import { zodResolver } from '@hookform/resolvers/zod'; import React, { useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import ClothingCard from './components/ClothingCard'; import { api, type Body_async_cloud_change_bg_v3_api_v3_cloud_batch_change_bg_post, type Body_async_cloud_gen_images_v3_api_v3_cloud_batch_edit_images_post, type Body_local_cloud_async_change_bg_api_v2_cloud_batch_change_bg_post, } from '@/api'; import { type BgPaddingMultiValue } from '@/components/block/BgPaddingMultiSelect'; // 五级联动的 tag schema const tagSchema = z.object({ scenes: z.array(z.array(z.string())).optional(), }); const formSchema = z.object({ clothing_images: z.array(z.instanceof(File)).min(1, '请上传至少一张服装图片').max(5, '最多上传5个商品'), tags: z.array(tagSchema), changeBg: z.boolean().optional(), }); interface TagForm { scenes?: string[][]; paddingList?: BgPaddingMultiValue; bgMode?: 'custom' | 'scene'; } interface FormValues { clothing_images: File[]; tags: TagForm[]; changeBg?: boolean; } const defaultValues: FormValues = { clothing_images: [], tags: [], changeBg: false, }; const TryOnPage: React.FC = () => { const [scenesOptions, setScenesOptions] = useState([]); const [loading, setLoading] = useState(false); const [result, setResult] = useState(null); const fileInputRef = useRef(null); const [imagePreviews, setImagePreviews] = useState([]); // 表单 const form = useForm({ resolver: zodResolver(formSchema), defaultValues, }); // 拉取标签数据 useEffect(() => { api.TagService.fetchStyleAvailableTagsApiTagStyleTagListGet().then(data => setScenesOptions(data.data || [])); }, []); const tags = form.watch('tags'); // 图片上传和标签组同步(append 新卡片) const handleImageChange = (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); if (!files.length) return; const oldFiles = form.getValues('clothing_images'); if (oldFiles.length + files.length > 5) { alert('最多只能上传5个商品'); if (fileInputRef.current) fileInputRef.current.value = ''; return; } // 追加到已有图片和标签 const oldTags = form.getValues('tags'); const newFiles = [...oldFiles, ...files]; const newTags = [...oldTags, ...files.map(() => ({ scenes: [] as string[][] }))]; form.setValue('clothing_images', newFiles); setImagePreviews(newFiles.map(file => URL.createObjectURL(file))); form.setValue('tags', newTags); // 清空 input 以便连续选择同一图片 if (fileInputRef.current) fileInputRef.current.value = ''; }; // 删除图片及对应标签组 const handleRemoveImage = (idx: number) => { const files = form.getValues('clothing_images'); const tags = form.getValues('tags'); const newFiles = files.filter((_, i) => i !== idx); const newTags = tags.filter((_, i) => i !== idx); form.setValue('clothing_images', newFiles); setImagePreviews(newFiles.map(file => URL.createObjectURL(file))); form.setValue('tags', newTags); }; // 场景多选 const handleScenesChange = (idx: number, scenes: string[][]) => { const newTags = [...form.getValues('tags')]; newTags[idx] = { ...newTags[idx], scenes }; form.setValue('tags', newTags); }; // 自定义上传 const handlePaddingListChange = (idx: number, paddingList: BgPaddingMultiValue) => { const newTags = [...form.getValues('tags')]; newTags[idx] = { ...newTags[idx], paddingList }; form.setValue('tags', newTags); }; // 切换背景模式 const handleBgModeChange = (idx: number, bgMode: 'custom' | 'scene') => { const newTags = [...form.getValues('tags')]; newTags[idx] = { ...newTags[idx], bgMode }; form.setValue('tags', newTags); }; // 递归查找 prompt function findPromptByPath(tree: any[], path: string[]): string { let node: any = null; let nodes: any[] = tree; for (const t of path) { node = nodes.find((n: any) => n.title === t); if (!node) return ''; nodes = node.children || []; } return node && node.prompt ? node.prompt : ''; } // 提交 const onSubmit = async (values: FormValues) => { setLoading(true); setResult(null); const results: any[] = []; console.log(values); try { for (let i = 0; i < values.clothing_images.length; i++) { const tag = values.tags[i]; const bgMode = tag.bgMode || 'scene'; if (bgMode === 'custom') { // 自定义模式 const paddingList = tag.paddingList || []; for (const padding of paddingList) { const formData: Body_async_cloud_change_bg_v3_api_v3_cloud_batch_change_bg_post = { clothing_images: [values.clothing_images[i]], padding_images: [padding.file], bg_prompts: padding.text, }; const res = await api.ImageGenerateService.asyncCloudChangeBgV3ApiV3CloudBatchChangeBgPost({ formData }); results.push(res); } } else { // 场景模式 const scenesArr: string[][] = tag.scenes || []; for (const path of scenesArr) { const scene = { title: path.join('/'), prompt: findPromptByPath(scenesOptions, path), }; const formData: Body_local_cloud_async_change_bg_api_v2_cloud_batch_change_bg_post = { clothing_images: [values.clothing_images[i]], scenes_list: JSON.stringify([{ title: scene.title, prompt: scene.prompt }]), }; const res = await api.ImageGenerateService.localCloudAsyncChangeBgApiV2CloudBatchChangeBgPost({ formData }); results.push(res); } } } setResult(results); } catch (e: any) { setResult({ error: e?.message || '请求失败' }); } finally { setLoading(false); } }; const clothing_images = form.watch('clothing_images'); return (

换装体验

{/* 卡片式图片+表单 */}
{clothing_images && clothing_images.length > 0 && clothing_images.map((_, idx) => { const tag: TagForm = tags[idx] || { scenes: [] }; const fileName = clothing_images[idx]?.name; return ( handleRemoveImage(idx)} scenesOptions={scenesOptions} scenes={tag.scenes || []} onScenesChange={scenes => handleScenesChange(idx, scenes)} fileName={fileName} paddingList={tag.paddingList || []} onPaddingListChange={paddingList => handlePaddingListChange(idx, paddingList)} bgMode={tag.bgMode || 'scene'} onChangeBgMode={bgMode => handleBgModeChange(idx, bgMode)} /> ); })}
{/* 结果展示 */} {result && (

结果

{JSON.stringify(result, null, 2)}
)}
); }; export default TryOnPage;