/** * 商品试算页(docs/v2.1/trial-page.md):采集后的主工作流。 * 01 商品信息 → 02 价格试算 → 03 俄文文案 → 04 图片与AI生图 → 05 入库与导出。 * 操作流水线对齐 v1 web 工具台;数据自动落库(products 表)。 */ import { useCallback, useEffect, useState } from 'react'; import { Link, useParams } from 'react-router'; import { Card, Menu, Space, Spin, Typography, message } from 'antd'; import { getProduct, listAssets, ProductDetail, ProductAsset, updateProduct } from '@/services/product'; import { apiErrorMessage } from '@/services/api'; import { STAGE_COLOR, STAGE_LABEL } from '../collection/CollectionPage'; import TrialInfoPanel from './TrialInfoPanel'; import TrialPricingPanel from './TrialPricingPanel'; import TrialSuitePanel from './TrialSuitePanel'; import TrialExportPanel from './TrialExportPanel'; import CopyPanel from '../product/CopyPanel'; const { Title, Text } = Typography; const SECTIONS = [ { id: 'section-info', label: '商品信息' }, { id: 'section-pricing', label: '价格试算' }, { id: 'section-copy', label: '俄文文案' }, { id: 'section-collect', label: '采集图片' }, { id: 'section-plan', label: '出图方案' }, { id: 'section-result', label: '生成结果' }, { id: 'section-export', label: '入库与导出' }, ]; export default function TrialPage() { const { id } = useParams(); const [product, setProduct] = useState(null); const [assets, setAssets] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [active, setActive] = useState(SECTIONS[0].id); const load = useCallback(async () => { if (!id) return; setLoading(true); try { const [p, a] = await Promise.all([getProduct(id), listAssets(id)]); setProduct(p); setAssets(a); } catch (e) { message.error(apiErrorMessage(e)); } finally { setLoading(false); } }, [id]); useEffect(() => { load(); }, [load]); // 滚动监听:高亮当前区域 useEffect(() => { const onScroll = () => { let current = SECTIONS[0].id; for (const s of SECTIONS) { const el = document.getElementById(s.id); if (el && el.getBoundingClientRect().top <= 90) current = s.id; } setActive(current); }; window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); const save = useCallback( async (partial: Partial) => { if (!id || !product) return; setSaving(true); try { const updated = await updateProduct(id, partial); setProduct(updated); } catch (e) { message.error(apiErrorMessage(e)); } finally { setSaving(false); } }, [id, product], ); if (loading || !product) { return (
); } const raw = (product.raw ?? {}) as Record; const titleZh = ((raw.title_zh as string) ?? (raw.title as string) ?? '').trim(); return (
{titleZh || product.name || '(未命名商品)'} {STAGE_LABEL[product.stage] || product.stage} {saving ? '保存中…' : '已自动保存'}
{product.name && product.name !== titleZh && ( 俄文:{product.name} )} 进入商品编辑页 →
{/* key 随商品切换:面板本地状态(表单/方案/勾选)一次性初始化,避免保存回显互相覆盖 */}
{/* 4/5/6 三张卡片由 TrialSuitePanel 渲染,锚点在面板内部 */}
{/* 右侧区域导航 */}
({ key: s.id, label: s.label }))} onClick={({ key }) => { document.getElementById(key)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }} />
); }