diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 018ace80d..e372072ea 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -953,6 +953,45 @@ function buildAutoModeFallbackMessage(params: { ].join("\n"); } +function hasStructuredAgentSections(message?: string): boolean { + if (!message) return false; + return /(?:###\s*需求理解|【\s*需求理解\s*】|###\s*图片分析|【\s*图片分析\s*】|###\s*执行计划|【\s*执行计划\s*】)/.test( + message, + ); +} + +function ensureStructuredAgentFriendlyMessage(params: { + message?: string; + text: string; + hasImage: boolean; + fromHistory: boolean; +}): string { + const existingMessage = String(params.message || "").trim(); + if (hasStructuredAgentSections(existingMessage)) { + return existingMessage; + } + + const fallback = buildAutoModeFallbackMessage({ + text: params.text, + hasImage: params.hasImage, + fromHistory: params.fromHistory, + }); + + if (!existingMessage) { + return fallback; + } + + const normalizedSupplement = existingMessage.replace(/\s+/g, " ").trim(); + if (!normalizedSupplement) { + return fallback; + } + + return fallback.replace( + "### 执行计划\n", + `### 执行计划\n- 补充说明:${normalizedSupplement}\n`, + ); +} + function extractMessageImageUrls(parts: any[]): string[] { return (parts || []) .map((part: any) => { @@ -1766,6 +1805,7 @@ export async function POST(request: Request) { let smartAutoHistoryImages: string[] = []; // 从历史对话中提取的图片URL(支持多张) let smartAutoNeedsClarification = false; // 是否需要先追问用户 let smartAutoClarificationQuestion: string | undefined; + let preferredAgentModelId: string | undefined; const deepThinkingOn = deepThinkingEnabled !== false; let shouldRunAgentConversation = finalModelId === "auto"; let shouldKeepSelectedModel = false; @@ -1829,8 +1869,9 @@ export async function POST(request: Request) { } else if (isFixedVisualModel) { shouldRunAgentConversation = true; shouldKeepSelectedModel = true; + preferredAgentModelId = selectedModelConfig.id; console.log( - `[Smart Auto Mode] 🎯 Agent conversation enabled for fixed visual model: ${selectedModelConfig.displayName} (${selectedModelProviderType || "unknown"})` + `[Smart Auto Mode] 🎯 Agent conversation enabled for fixed visual model: ${selectedModelConfig.displayName} (${selectedModelProviderType || "unknown"}), preferredAgentModelId=${preferredAgentModelId}` ); } } else if (isFixedVisualModel || isAiModelAgentAuto(selectedModelConfig)) { @@ -2002,6 +2043,7 @@ export async function POST(request: Request) { imageFromHistory: smartAutoHistoryImages.length > 0, historyMessages, context: autoContextText || undefined, + preferredModelId: preferredAgentModelId, }), "runAutoAgentGraph", SMART_AUTO_GRAPH_TIMEOUT_MS, @@ -2180,6 +2222,7 @@ export async function POST(request: Request) { imageFromHistory: autoFromHistoryForFallback || smartAutoHistoryImages.length > 0, historyMessages: [], context: undefined, + preferredModelId: preferredAgentModelId, }), "fallbackAnalyzeIntent", SMART_AUTO_FALLBACK_TIMEOUT_MS, @@ -2222,6 +2265,7 @@ export async function POST(request: Request) { text: autoTextForFallback, imageUrls: summaryImageUrls, context: typeof autoContext === "string" ? autoContext : undefined, + preferredModelId: preferredAgentModelId, }), "fallbackVisualSummary", SMART_AUTO_FALLBACK_TIMEOUT_MS, @@ -2357,6 +2401,23 @@ export async function POST(request: Request) { console.log("[Smart Auto Mode] 🧩 Backfilled conversational reply with fallback"); } + if (shouldRunAgentConversation && !smartAutoNeedsClarification) { + const latestUserText = message.parts + .filter((part): part is { type: "text"; text: string } => part.type === "text") + .map((part) => part.text) + .join(" "); + const hasCurrentImageInput = message.parts.some((part: any) => + part?.type === "file" || part?.type === "image" || part?.type === "image_url" + ); + const hasHistoryImageInput = smartAutoHistoryImages.length > 0; + smartAutoFriendlyMessage = ensureStructuredAgentFriendlyMessage({ + message: smartAutoFriendlyMessage, + text: latestUserText, + hasImage: hasCurrentImageInput || hasHistoryImageInput, + fromHistory: !hasCurrentImageInput && hasHistoryImageInput, + }); + } + const hasUserCustomImageParams = hasUserCustomizedImageParams(requestBody.imageParams); const hasUserCustomVideoParams = hasUserCustomizedVideoParams(requestBody.videoParams); const shouldApplyAgentImageParams = @@ -2437,6 +2498,7 @@ export async function POST(request: Request) { text: latestUserText, imageUrls: summaryImageUrls, context: typeof autoContext === "string" ? autoContext : undefined, + preferredModelId: preferredAgentModelId, }), "enrichImageAnalysisSummary", timeoutMs, diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index aff4c905a..cce29ba19 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -666,7 +666,7 @@ export default function CanvasEditorPage({ params }: PageProps) { "absolute top-4 right-4 bottom-4 z-10 w-[460px] max-w-[calc(100vw-1rem)] transition-all duration-300 ease-in-out", isPanelOpen ? "translate-x-0 opacity-100" : "translate-x-[120%] opacity-0 pointer-events-none" )}> -
+
{panelMode === 'chat' ? ( // AI 对话内容 - 使用 key 强制在 id 变化时重新挂载
diff --git a/components/angle-edit-dialog.tsx b/components/angle-edit-dialog.tsx index 1055afe79..e8c660ac8 100644 --- a/components/angle-edit-dialog.tsx +++ b/components/angle-edit-dialog.tsx @@ -188,7 +188,7 @@ export function AngleEditDialog({ return ( - +
已生成文件列表 diff --git a/components/canvas/RealtimeCanvasDialog.tsx b/components/canvas/RealtimeCanvasDialog.tsx index de76a6e43..6df3d35c2 100644 --- a/components/canvas/RealtimeCanvasDialog.tsx +++ b/components/canvas/RealtimeCanvasDialog.tsx @@ -27,21 +27,14 @@ import { Paintbrush, Square, Circle as CircleIcon, + Slash, Upload, RefreshCw, - Send, Sparkles, Trash2, - Wifi, - WifiOff, - Minimize2, - Maximize2, Download, - Image as ImageIcon, - MoreHorizontal, Undo2, Redo2, - X, } from "lucide-react"; import { toast } from "sonner"; @@ -76,6 +69,15 @@ type RealtimeCanvasDialogProps = { const FALLBACK_PROMPT = "将草图渲染为高质感产品设计图,保持结构和主轮廓一致。"; const CANVAS_BACKGROUND = "#ffffff"; +const BRUSH_COLOR_PRESETS = [ + "#18181b", + "#ef4444", + "#f59e0b", + "#22c55e", + "#3b82f6", + "#a855f7", +] as const; +const CANVAS_BACKGROUND_PRESETS = ["#ffffff", "#f4f4f5", "#fdf2f8", "#ecfeff", "#f0fdf4"] as const; const defaultRealtimeConfig: RealtimeConfig = { transport: { @@ -112,14 +114,15 @@ function buildImageDataUrl(image: any) { return ""; } -function isShapeMode(mode: "brush" | "eraser" | "rect" | "circle") { - return mode === "rect" || mode === "circle"; +function isShapeMode(mode: "brush" | "eraser" | "rect" | "circle" | "line") { + return mode === "rect" || mode === "circle" || mode === "line"; } async function canvasToJpegDataUri( canvas: HTMLCanvasElement, maxInputResolution: number, - jpegQuality: number + jpegQuality: number, + backgroundColor: string ) { const sourceWidth = canvas.width; const sourceHeight = canvas.height; @@ -136,7 +139,7 @@ async function canvasToJpegDataUri( throw new Error("无法初始化导出画布"); } - exportCtx.fillStyle = CANVAS_BACKGROUND; + exportCtx.fillStyle = backgroundColor; exportCtx.fillRect(0, 0, exportWidth, exportHeight); exportCtx.drawImage(canvas, 0, 0, exportWidth, exportHeight); @@ -159,15 +162,14 @@ export function RealtimeCanvasDialog({ open, onOpenChange, initialImageUrl, - onApplyResult, }: RealtimeCanvasDialogProps) { const canvasRef = useRef(null); + const previewCanvasRef = useRef(null); const canvasWrapRef = useRef(null); const connectionRef = useRef(null); const pointerDownRef = useRef(false); const lastPointRef = useRef<{ x: number; y: number } | null>(null); const shapeStartPointRef = useRef<{ x: number; y: number } | null>(null); - const shapeSnapshotRef = useRef(null); const pendingFlushTimerRef = useRef(null); const flushInFlightRef = useRef(false); const dirtyRef = useRef(false); @@ -178,6 +180,7 @@ export function RealtimeCanvasDialog({ const metricsQueueRef = useRef([]); const sessionIdRef = useRef(`rt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`); const hasLoadedInitialImageRef = useRef(false); + const canvasBackgroundRef = useRef(CANVAS_BACKGROUND); // History Management const historyRef = useRef([]); @@ -192,8 +195,9 @@ export function RealtimeCanvasDialog({ const [statusMessage, setStatusMessage] = useState("等待连接"); const [prompt, setPrompt] = useState(FALLBACK_PROMPT); const [previewUrl, setPreviewUrl] = useState(""); - const [isApplying, setIsApplying] = useState(false); - const [brushMode, setBrushMode] = useState<"brush" | "eraser" | "rect" | "circle">("brush"); + const [brushMode, setBrushMode] = useState<"brush" | "eraser" | "rect" | "circle" | "line">("brush"); + const [brushColor, setBrushColor] = useState(BRUSH_COLOR_PRESETS[0]); + const [canvasBackground, setCanvasBackground] = useState(canvasBackgroundRef.current); const [brushSize, setBrushSize] = useState(6); const [adaptiveThrottleMs, setAdaptiveThrottleMs] = useState( defaultRealtimeConfig.transport.throttleInterval @@ -244,9 +248,14 @@ export function RealtimeCanvasDialog({ historyRef.current = historyRef.current.slice(0, historyIndexRef.current + 1); } - const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - historyRef.current.push(imageData); - historyIndexRef.current = historyRef.current.length - 1; + try { + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + historyRef.current.push(imageData); + historyIndexRef.current = historyRef.current.length - 1; + } catch (error) { + console.warn("[RealtimeCanvas] saveHistory skipped:", error); + return; + } // Limit history size if (historyRef.current.length > 50) { @@ -279,7 +288,8 @@ export function RealtimeCanvasDialog({ const imageDataUri = await canvasToJpegDataUri( canvas, config.image.maxInputResolution, - config.image.jpegQuality + config.image.jpegQuality, + canvasBackgroundRef.current ); dirtyRef.current = false; @@ -334,6 +344,14 @@ export function RealtimeCanvasDialog({ [adaptiveThrottleMs, flushFrame] ); + const clearPreviewCanvas = useCallback(() => { + const previewCanvas = previewCanvasRef.current; + if (!previewCanvas) return; + const previewCtx = previewCanvas.getContext("2d"); + if (!previewCtx) return; + previewCtx.clearRect(0, 0, previewCanvas.width, previewCanvas.height); + }, []); + const undo = useCallback(() => { if (historyIndexRef.current <= 0) return; @@ -373,7 +391,7 @@ export function RealtimeCanvasDialog({ if (!ctx) return; ctx.save(); ctx.globalCompositeOperation = "source-over"; - ctx.fillStyle = CANVAS_BACKGROUND; + ctx.fillStyle = canvasBackgroundRef.current; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); @@ -412,18 +430,28 @@ export function RealtimeCanvasDialog({ const initializeCanvas = useCallback(() => { const canvas = canvasRef.current; + const previewCanvas = previewCanvasRef.current; const wrap = canvasWrapRef.current; - if (!canvas || !wrap) return; + if (!canvas || !previewCanvas || !wrap) return; const rect = wrap.getBoundingClientRect(); const dpr = Math.max(1, window.devicePixelRatio || 1); canvas.width = Math.max(640, Math.floor(rect.width * dpr)); canvas.height = Math.max(480, Math.floor(rect.height * dpr)); canvas.style.width = `${Math.floor(rect.width)}px`; canvas.style.height = `${Math.floor(rect.height)}px`; + previewCanvas.width = canvas.width; + previewCanvas.height = canvas.height; + previewCanvas.style.width = canvas.style.width; + previewCanvas.style.height = canvas.style.height; const ctx = canvas.getContext("2d"); + const previewCtx = previewCanvas.getContext("2d"); if (!ctx) return; + if (!previewCtx) return; ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.scale(dpr, dpr); + previewCtx.setTransform(1, 0, 0, 1, 0, 0); + previewCtx.scale(dpr, dpr); + previewCtx.clearRect(0, 0, previewCanvas.width, previewCanvas.height); resetCanvas(); hasLoadedInitialImageRef.current = false; }, [resetCanvas]); @@ -452,7 +480,7 @@ export function RealtimeCanvasDialog({ ctx.lineWidth = brushSize * 1.45; } else { ctx.globalCompositeOperation = "source-over"; - ctx.strokeStyle = "#18181b"; + ctx.strokeStyle = brushColor; ctx.lineWidth = brushSize; } ctx.lineCap = "round"; @@ -463,14 +491,17 @@ export function RealtimeCanvasDialog({ ctx.stroke(); ctx.restore(); }, - [brushMode, brushSize] + [brushMode, brushColor, brushSize] ); const drawShape = useCallback( - (from: { x: number; y: number }, to: { x: number; y: number }) => { - const canvas = canvasRef.current; - if (!canvas) return; - const ctx = canvas.getContext("2d"); + ( + from: { x: number; y: number }, + to: { x: number; y: number }, + targetCtx?: CanvasRenderingContext2D | null + ) => { + const defaultCtx = canvasRef.current?.getContext("2d"); + const ctx = targetCtx || defaultCtx; if (!ctx) return; const minX = Math.min(from.x, to.x); @@ -480,7 +511,7 @@ export function RealtimeCanvasDialog({ ctx.save(); ctx.globalCompositeOperation = "source-over"; - ctx.strokeStyle = "#18181b"; + ctx.strokeStyle = brushColor; ctx.lineWidth = Math.max(1, brushSize * 0.9); ctx.lineCap = "round"; ctx.lineJoin = "round"; @@ -495,28 +526,34 @@ export function RealtimeCanvasDialog({ ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2); ctx.stroke(); + } else if (brushMode === "line") { + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } ctx.restore(); }, - [brushMode, brushSize] + [brushMode, brushColor, brushSize] ); const handlePointerDown = useCallback( (event: ReactPointerEvent) => { + event.preventDefault(); const point = getCanvasPoint(event); if (!point) return; pointerDownRef.current = true; lastPointRef.current = point; if (isShapeMode(brushMode)) { - const canvas = canvasRef.current; - const ctx = canvas?.getContext("2d", { willReadFrequently: true }); shapeStartPointRef.current = point; - shapeSnapshotRef.current = - canvas && ctx ? ctx.getImageData(0, 0, canvas.width, canvas.height) : null; + clearPreviewCanvas(); + } else { + drawStroke(point, point); + scheduleFlush(); } event.currentTarget.setPointerCapture(event.pointerId); }, - [brushMode, getCanvasPoint] + [brushMode, clearPreviewCanvas, drawStroke, getCanvasPoint, scheduleFlush] ); const handlePointerLeave = useCallback(() => { @@ -527,11 +564,11 @@ export function RealtimeCanvasDialog({ pointerDownRef.current = false; lastPointRef.current = null; shapeStartPointRef.current = null; - shapeSnapshotRef.current = null; + clearPreviewCanvas(); if (wasDrawing) { scheduleFlush(true, false); } - }, [scheduleFlush]); + }, [clearPreviewCanvas, scheduleFlush]); const handlePointerEnter = useCallback(() => { if (cursorRef.current) { @@ -541,6 +578,7 @@ export function RealtimeCanvasDialog({ const handlePointerMove = useCallback( (event: ReactPointerEvent) => { + event.preventDefault(); // Update cursor const point = getCanvasPoint(event); if (point && cursorRef.current) { @@ -558,11 +596,11 @@ export function RealtimeCanvasDialog({ if (!pointerDownRef.current) return; if (!point || !lastPointRef.current) return; if (isShapeMode(brushMode) && shapeStartPointRef.current) { - const canvas = canvasRef.current; - const ctx = canvas?.getContext("2d"); - if (ctx && shapeSnapshotRef.current) { - ctx.putImageData(shapeSnapshotRef.current, 0, 0); - drawShape(shapeStartPointRef.current, point); + const previewCanvas = previewCanvasRef.current; + const previewCtx = previewCanvas?.getContext("2d"); + if (previewCanvas && previewCtx) { + previewCtx.clearRect(0, 0, previewCanvas.width, previewCanvas.height); + drawShape(shapeStartPointRef.current, point, previewCtx); } } else { drawStroke(lastPointRef.current, point); @@ -575,20 +613,16 @@ export function RealtimeCanvasDialog({ const handlePointerUp = useCallback( (event: ReactPointerEvent) => { + event.preventDefault(); pointerDownRef.current = false; const endPoint = getCanvasPoint(event); const shapeStart = shapeStartPointRef.current; if (isShapeMode(brushMode) && endPoint && shapeStart) { - const canvas = canvasRef.current; - const ctx = canvas?.getContext("2d"); - if (ctx && shapeSnapshotRef.current) { - ctx.putImageData(shapeSnapshotRef.current, 0, 0); - drawShape(shapeStart, endPoint); - } + drawShape(shapeStart, endPoint); + clearPreviewCanvas(); } lastPointRef.current = null; shapeStartPointRef.current = null; - shapeSnapshotRef.current = null; try { event.currentTarget.releasePointerCapture(event.pointerId); } catch { @@ -597,9 +631,13 @@ export function RealtimeCanvasDialog({ saveHistory(); // Save state after stroke scheduleFlush(true, false); }, - [brushMode, drawShape, getCanvasPoint, saveHistory, scheduleFlush] + [brushMode, clearPreviewCanvas, drawShape, getCanvasPoint, saveHistory, scheduleFlush] ); + useEffect(() => { + clearPreviewCanvas(); + }, [brushMode, clearPreviewCanvas]); + // Shortcuts useEffect(() => { if (!open) return; @@ -625,43 +663,6 @@ export function RealtimeCanvasDialog({ return () => window.removeEventListener('keydown', handleKeyDown); }, [open, undo, redo]); - const uploadPreviewToCanvas = useCallback(async () => { - if (!previewUrl) { - toast.error("请先完成一帧实时预览"); - return; - } - - setIsApplying(true); - try { - const blob = await fetch(previewUrl).then((res) => res.blob()); - const formData = new FormData(); - const filename = `realtime-canvas-${Date.now()}.jpg`; - formData.append("file", new File([blob], filename, { type: blob.type || "image/jpeg" })); - - const response = await fetch("/api/files/upload", { - method: "POST", - body: formData, - }); - const result = await response.json().catch(() => ({})); - - if (!response.ok || !result?.url) { - throw new Error(result?.error || "上传到画布失败"); - } - - onApplyResult({ - imageUrl: String(result.url), - prompt: prompt.trim() || FALLBACK_PROMPT, - }); - - toast.success("已发送到画布"); - onOpenChange(false); - } catch (error) { - toast.error(error instanceof Error ? error.message : "发送到画布失败"); - } finally { - setIsApplying(false); - } - }, [onApplyResult, onOpenChange, previewUrl, prompt]); - useEffect(() => { if (!open) return; initializeCanvas(); @@ -848,9 +849,10 @@ export function RealtimeCanvasDialog({ const clearSketch = useCallback(() => { resetCanvas(); + clearPreviewCanvas(); dirtyRef.current = true; scheduleFlush(true, false); - }, [resetCanvas, scheduleFlush]); + }, [clearPreviewCanvas, resetCanvas, scheduleFlush]); const handleSketchUpload = useCallback( async (event: ChangeEvent) => { @@ -901,15 +903,59 @@ export function RealtimeCanvasDialog({ [saveHistory, scheduleFlush] ); + const handleCanvasBackgroundChange = useCallback( + (nextColor: string) => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + const snapshot = document.createElement("canvas"); + snapshot.width = canvas.width; + snapshot.height = canvas.height; + const snapshotCtx = snapshot.getContext("2d"); + if (!snapshotCtx) return; + snapshotCtx.drawImage(canvas, 0, 0); + + ctx.save(); + ctx.globalCompositeOperation = "source-over"; + ctx.fillStyle = nextColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(snapshot, 0, 0); + ctx.restore(); + + canvasBackgroundRef.current = nextColor; + setCanvasBackground(nextColor); + saveHistory(); + scheduleFlush(true); + }, + [saveHistory, scheduleFlush] + ); + + const handleDownloadCanvas = useCallback(async () => { + const canvas = canvasRef.current; + if (!canvas) return; + try { + const dataUrl = await canvasToJpegDataUri(canvas, 2048, 0.92, canvasBackgroundRef.current); + const anchor = document.createElement("a"); + anchor.href = dataUrl; + anchor.download = `realtime-canvas-${Date.now()}.jpg`; + anchor.click(); + toast.success("已保存当前画布"); + } catch (error) { + toast.error(error instanceof Error ? error.message : "保存失败"); + } + }, []); + return ( 实时画布 - 左侧绘制草图,右侧实时预览,并可将结果发送到无限画布。 + 左侧绘制草图,右侧实时预览。 {/* Top Header - Minimal */} -
+
Realtime Canvas
@@ -918,17 +964,6 @@ export function RealtimeCanvasDialog({ {statusBadge.label}
- -
- -
{/* Main Canvas Area - Split View */} @@ -952,6 +987,10 @@ export function RealtimeCanvasDialog({ onPointerEnter={handlePointerEnter} onPointerLeave={handlePointerLeave} /> + {/* Custom Brush Cursor */}
- {/* Floating Toolbar - Left (Tools) */} -
-
- - - -
+ {/* Floating Toolbar - Top (Lovart-like) */} +
+
+ + + + + - - - - -
- -
- {brushSize}px - setBrushSize(val)} - className="h-24 w-4" - /> -
+
-
- - +
+ {BRUSH_COLOR_PRESETS.map((color) => ( +
+
+ +
+ +
+ setBrushSize(val)} + /> +
+ +
+ +
+ {CANVAS_BACKGROUND_PRESETS.map((color) => ( +
+ +
+ + + + + + + + +
+
{/* Floating Toolbar - Bottom (Prompt) */} @@ -1134,28 +1268,6 @@ export function RealtimeCanvasDialog({
- {/* Floating Toolbar - Right (Actions) */} -
-
- - - -
-
); diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index 0cfe31ac3..4e507fe4e 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -1906,7 +1906,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { {/* 文字编辑面板(显示在图片节点旁边) */} {isSelected && textEditPanelOpen && data.src && typeof document !== "undefined" && createPortal(
- +
{allComposerModels.map((model) => ( - + 账户管理 @@ -2264,7 +2264,7 @@ export function LovartHome({
- +
升级您的套餐 diff --git a/components/image-params-bar.tsx b/components/image-params-bar.tsx index 9166b6a40..4095edbfc 100644 --- a/components/image-params-bar.tsx +++ b/components/image-params-bar.tsx @@ -8,7 +8,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { Check, SlidersHorizontal } from "lucide-react"; +import { Check, Link2, Scan, SlidersHorizontal, Sparkles } from "lucide-react"; import { cn } from "@/lib/utils"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; @@ -72,6 +72,12 @@ const IMAGE_SIZE_OPTIONS = [ { value: "4K", label: "4K", disabled: false }, ] as const; +const IMAGE_SIZE_PIXEL_MAP: Record = { + "1K": 1024, + "2K": 2048, + "4K": 4096, +}; + export function ImageParamsBar({ modelName = "Gemini 图片生成", initialParams, @@ -145,6 +151,58 @@ export function ImageParamsBar({ const showPromptExtend = isAlibabaImage || imageGenConfig?.supportsPromptExtend; const showNegativePrompt = isAlibabaImage || imageGenConfig?.supportsNegativePrompt; + const getComputedOutputSize = (aspectRatio: string, imageSize: string) => { + const size = IMAGE_SIZE_PIXEL_MAP[imageSize] || IMAGE_SIZE_PIXEL_MAP["2K"]; + if (aspectRatio === "auto" || aspectRatio === "1:1") { + return { width: size, height: size }; + } + + const [wRaw, hRaw] = aspectRatio.split(":").map((item) => Number(item)); + if (!wRaw || !hRaw) { + return { width: size, height: size }; + } + + if (wRaw >= hRaw) { + return { + width: size, + height: Math.max(1, Math.round((size * hRaw) / wRaw)), + }; + } + + return { + width: Math.max(1, Math.round((size * wRaw) / hRaw)), + height: size, + }; + }; + + const renderRatioGlyph = (ratioValue: string) => { + if (ratioValue === "auto") { + return ; + } + + const [wRaw, hRaw] = ratioValue.split(":").map((item) => Number(item)); + if (!wRaw || !hRaw) { + return ; + } + const maxSide = 22; + let width = maxSide; + let height = maxSide; + if (wRaw >= hRaw) { + width = maxSide; + height = Math.max(8, Math.round((maxSide * hRaw) / wRaw)); + } else { + height = maxSide; + width = Math.max(8, Math.round((maxSide * wRaw) / hRaw)); + } + + return ( + + ); + }; + // 🆕 监听 initialParams 变化,确保状态同步 useEffect(() => { console.log("[ImageParamsBar] Mount/Update - initialParams:", initialParams); @@ -176,71 +234,119 @@ export function ImageParamsBar({ // 内联模式:只返回参数按钮,嵌入工具栏 if (inline) { + const ratioLabel = params.aspectRatio === "auto" ? "智能比例" : params.aspectRatio; + const sizeLabel = params.imageSize === "4K" ? "超清 4K" : `高清 ${params.imageSize}`; + const outputSize = getComputedOutputSize(params.aspectRatio, params.imageSize); + return (
- {/* 图片比例 */} - - + + - - -
- {ASPECT_RATIO_OPTIONS.map((option) => ( - - ))} -
-
-
+ + +
+
+

选择比例

+
+ {ASPECT_RATIO_OPTIONS.map((option) => ( + + ))} +
+
- {/* 分辨率 */} - - - - - - {IMAGE_SIZE_OPTIONS.map((option) => ( - updateParam("imageSize", option.value)} - className="focus-visible:ring-0 focus-visible:ring-offset-0 focus:outline-none focus:ring-0" - > - - {option.label} - - ))} - - +
+

选择分辨率

+
+ {IMAGE_SIZE_OPTIONS.filter((option) => !option.disabled).map((option) => ( + + ))} +
+
+ +
+

尺寸

+
+
+
+ W + {outputSize.width} +
+
+ +
+
+ H + {outputSize.height} +
+
+ PX +
+
+
+
+ {showImageCountSelector && ( @@ -264,10 +370,10 @@ export function ImageParamsBar({ {(showPromptExtend || showNegativePrompt) && ( - + + + +
+ + +
+ +
+
+
+
+ 一页理解这套落地方案 +
+
+ 详情页不是介绍产品,而是完成“心理决策”。我们的落地路径:
+ 定位 → 素材 → 需求分级 → 买点转译 → 结构化编排 → FABE写作 → 指标验证 → 资产沉淀 +
+
+ 从卖点到买点 + 从参数到情绪价值 + 从堆砌到结构 +
+
+
+
+ 培训建议节奏:
+ ① 先拿一个SKU现场演示(全程 60–90min)
+ ② 让学员分组填表(定位卡/原话池/需求分级)
+ ③ 最后用同一模板输出前3屏 + FABE 1组卖点块 +
+
+ 常见误区:过度文艺化、形式主义、堆抽象词、先写再找证据。 +
+
+
+ +
+
+ 关键产出物(必做) + 定位卡 / 原话素材池 / 需求分级表 / 买点转译表 / 详情页结构图 / FABE卖点块 +
+
+ 团队协同(最省沟通) + 文案定“买点与顺序”,设计按ABC做视觉,运营用数据复盘与AB测试 +
+
+ 上线验收(看这4个数) + 停留时长、前3屏跳出、收藏率、咨询关键词变化(再看转化) +
+
+ + +
+
+ + 落地方法论:详情页构建四步法 +
+
+ +
+
+ 优先级 +

结构化需求排序

+
+
    +
  • 识别品类三大核心需求
  • +
  • 按必备型→期待型→魅力型排序
  • +
  • 删除无差异与反向需求
  • +
  • 避免抄袭竞品,回归用户真实动机
  • +
+
+ 核心理念:详情页不是功能说明书,是用户决策路径图 +
+
+ + +
+
+ 讲故事 +

内容化表达

+
+
    +
  • 用场景替代参数(如"搬家不费力"而非"轻便")
  • +
  • 用情绪替代功能(如"安心入睡"而非"防臭材料")
  • +
  • 用故事感替代罗列(用户能代入、能共鸣)
  • +
  • 避免专业术语,用口语化利益表达
  • +
+
+ 核心理念:好详情页 = 一场说服用户的对话 +
+
+ + +
+
+ 标签化 +

心智命名与锚定

+
+
    +
  • 命名传递价值:"安安裤" > "减敏内裤"
  • +
  • 用心理账户转移:"4000元=十年安心"
  • +
  • 打造专属词:"官炉锅"构建尊贵感
  • +
  • 让产品成为用户身份的符号
  • +
+
+ 核心理念:心智标签是长期复购的护城河 +
+
+ + +
+
+ 数据驱动 +

精细化运营

+
+
    +
  • 基于人群画像定制内容
  • +
  • AB测试不同场景表达
  • +
  • 监控转化漏斗与停留时长
  • +
  • 持续迭代需求洞察
  • +
+
+ 核心理念:最终目标:从流量运营转向用户价值运营 +
+
+
+
+
+ + + + +
+

核心方法论:从功能罗列到价值叙事

+ +
+
+ 传统误区:商家仅罗列参数与功能,忽视用户真实动机,陷入价格战,无法建立品牌忠诚度。 +
+
+ 思维跃迁:从"产品是什么"转向"用户为何买",关注情绪价值、安全感与问题解决。 +
+
+ 内容化趋势:抖音等平台淘汰低质卖点页,内容化、场景化表达成为获取流量的唯一路径。 +
+
+ + +
+

五类需求模型(Kano模型)

+
+
+

必备型

+

缺失则不买
例:电饭锅能煮饭、书架能放书

+
+
+

期待型

+

越优越买
例:米饭口感、书架承重更强

+
+
+

魅力型

+

锦上添花
例:袜子淡淡香味、书架带隐藏灯带

+
+
+

无差异/反向型

+

有无无影响 / 设计反效果
需删除,不占篇幅

+
+
+
+ 提示:需求随技术演进动态迁移,需持续识别核心层级。家具类:必备(能放/能坐/能收纳)→期待(更稳/更省空间/更美观)→魅力(智能感应/隐藏收纳/氛围灯)。 +
+
+ + +
+

痛点拆解法(场景案例)

+
+
+

挡风被

+

冻不着(保暖)+不着(抗风)+湿不着(防泼水)

+
+
+

拖鞋

+

舒适(足弓)+干净(防臭)+防滑(纹路)

+
+
+

体检仪

+

怕死的有钱人→购买"对未知的掌控感"

+
+
+
+

家具类痛点拆解案例

+
+
+ 收纳书架:乱不着(分区收纳)+倒不着(稳固承重)+丑不着(简约设计) +
+
+ 办公书桌:累不着(人体工学)+挤不着(大桌面)+吵不着(静音抽屉) +
+
+ 儿童书桌:伤不着(圆角安全)+歪不着(可调高度)+脏不着(易清洁) +
+
+ 客厅边柜:乱不着(隐藏收纳)+占不着(窄身设计)+搭不着(风格适配) +
+
+
+
+ 提示:卖点必须嵌入具体生活场景才有转化力。家具类要拆解"空间痛点"(挤/乱/丑/不安全)而非只写"材质工艺"。 +
+
+ + +
+

策略分野:创新型 vs 成熟型产品

+
+
+

创新型产品(获客)

+
    +
  • 用户无搜索习惯,依赖兴趣内容引爆
  • +
  • 策略三步法:强化场景→解决痛点→消除顾虑
  • +
  • 案例:家用体检仪,不是"设备",而是"健康掌控器"
  • +
  • 警惕替代品威胁(如手机散热取代散热器)
  • +
+
+ 家具案例:智能升降桌,不是"桌子",而是"久坐救星"。用"站着办公不腰痛"替代"电机升降"。 +
+
+
+

成熟型产品(人群)

+
    +
  • 用户有明确意图,需建立心智差异
  • +
  • 关键动作:需求梳理→卖点包装→心智命名
  • +
  • 案例:儿童内裤,不是"减敏",而是"安安裤"(孩子爱穿,妈妈安心)
  • +
  • 命名要传递身份感:"官炉锅">"古法铁锅"
  • +
+
+ 家具案例:实木书架,不是"书架",而是"书香柜"(传递文化感)。用"4000元=十年陪伴"做心理账户转移。 +
+
+
+
+ 核心差异:创新型用情绪价值替代技术参数;成熟型用心理账户转移提升感知价值。家具类:新品类(如智能家具)走创新路线,传统品类(如书桌书架)走成熟路线。 +
+
+ + +
+

角色分高洞察(用户≠买家)

+
+

康复仪案例

+
    +
  • 使用者是父母,购买者是子女
  • +
  • 卖点应聚焦"节省子女照护时间"而非"仪器功能"
  • +
  • 翻译技术语言:"磁吸充电"→"接单不断电"
  • +
  • 价值翻译:"4000元体检仪=一部手机换十年安心"
  • +
+
+
+

家具类角色洞察案例

+
+
+ 儿童书桌:使用者是孩子,购买者是父母。卖点聚焦"让孩子坐得直、不近视"而非"材质工艺"。翻译:"可调高度"→"长高也能用";"圆角设计"→"磕碰不受伤"。 +
+
+ 老人椅:使用者是老人,购买者是子女。卖点聚焦"让父母起身不费力"而非"功能参数"。翻译:"助力起身"→"不用扶也能站";"3000元=父母十年安全"。 +
+
+ 办公桌(企业采购):使用者是员工,购买者是HR/行政。卖点聚焦"提升办公效率、减少健康投诉"而非"材质"。翻译:"人体工学"→"久坐不累";"5000元=10人一年健康成本"。 +
+
+
+
+ 提示:洞察购买决策链,才能精准触达动机。家具类B2C要区分"自用"(体验优先)和"送人"(情感价值优先)。 +
+
+
+ + +
+

消费者导向详情页SOP:从品类到心智的完整路径

+ + +
+
+ S1 +

SOP 1:品类需求分析(需求源于品类,而非产品)

+
+
+
+

执行步骤

+
    +
  1. 列出该品类下用户的所有使用场景(不要只写产品功能)
  2. +
  3. 识别每个场景下的真实动机(为什么在这个场景下需要它)
  4. +
  5. 对比竞品:他们写的是"产品是什么"还是"品类需求是什么"
  6. +
  7. 确定:我们的详情页要解决品类下的哪个核心需求
  8. +
+
+
+

家具案例:收纳书架

+
+
+ 错误写法:实木材质、三层设计、承重50kg
+ (只写产品参数) +
+
+ 正确写法:品类需求=书房整洁+快速找书+展示收藏
+ (先写品类需求,再写产品如何满足) +
+
+
+
+
+ 检查清单:能否用一句话回答"这个品类解决用户的什么问题"?如果不能,说明还在写产品,没写品类。 +
+
+ + +
+
+ S2 +

SOP 2:隐性需求挖掘(表面需求→深层诉求)

+
+
+
+

执行步骤

+
    +
  1. 列出表面需求(用户说出来的,如"要能放书")
  2. +
  3. 追问3次"为什么":为什么需要这个?背后是什么情绪?
  4. +
  5. 识别深层诉求(没说出来的,如"不想被说乱"、"想显得有文化")
  6. +
  7. 转化逻辑:技术参数→表面需求→深层诉求→情感绑定
  8. +
+
+
+

家具案例:儿童书桌

+
+
+ 表面需求:能写作业、能放书
+ 深层诉求:孩子坐得直、不近视、主动学习
+ 转化逻辑:可调高度→坐姿正确→不近视→妈妈安心 +
+
+ 洞察:妈妈不关心"E1级板材",关心"孩子是否愿意主动用"。 +
+
+
+
+
+ 检查清单:每个卖点是否都能回答"用户真正想要的是什么"?如果只能回答"产品有什么",说明没挖到深层诉求。 +
+
+ + +
+
+ S3 +

SOP 3:痛点重构(不同角色的痛点不同)

+
+
+
+

执行步骤

+
    +
  1. 识别使用者痛点(直接使用产品的人)
  2. +
  3. 识别购买者痛点(实际付费的人,可能不同)
  4. +
  5. 对比:哪个痛点更驱动购买决策?
  6. +
  7. 重构卖点:聚焦购买者的痛点,而非使用者的痛点
  8. +
+
+
+

家具案例:老人椅

+
+
+ 使用者(老人)痛点:关节疼痛、起身困难
+ 购买者(子女)痛点:愧疚感、时间压力、担心突发
+ 核心卖点:聚焦"让父母起身不费力"而非"理疗功能" +
+
+ 洞察:真正的痛点是子女的愧疚与时间压力,而非老人的身体症状。 +
+
+
+
+
+ 检查清单:卖点是否面向购买决策者?如果只写使用者的痛点,可能错失真正的购买动机。 +
+
+ + +
+
+ S4 +

SOP 4:需求权重重构(不同场景下优先级不同)

+
+
+
+

执行步骤

+
    +
  1. 列出产品的所有使用场景(至少3个)
  2. +
  3. 对每个场景,重新排序需求优先级
  4. +
  5. 对比:同一产品在不同场景下,哪个需求最重要?
  6. +
  7. 确定主场景:详情页聚焦哪个场景?其他场景如何处理?
  8. +
+
+
+

家具案例:沙发

+
+
+ 客厅场景:舒适 > 美观 > 耐用 > 收纳
+ 小户型场景:省空间 > 多功能 > 舒适 > 美观
+ 老人使用场景:易起身 > 安全 > 舒适 > 美观 +
+
+ 洞察:同一产品,不同场景下需求优先级需动态调整。详情页要明确主场景。 +
+
+
+
+
+ 检查清单:是否出现"适合所有场景"这种表述?(出现=没做场景区分)详情页必须明确主场景,其他场景可放在中后段。 +
+
+ + +
+
+ S5 +

SOP 5:竞对分析破局(拒绝模仿,找到差异化)

+
+
+
+

执行步骤

+
    +
  1. 拆解竞品TOP3详情页结构(只拆结构,不抄句子)
  2. +
  3. 识别竞品的"动机三角":为什么买?为什么选你?为什么复购?
  4. +
  5. 找出竞品忽略的真实动机(他们写参数,我们写什么?)
  6. +
  7. 地域/人群洞察:有没有未被满足的地域性需求?
  8. +
  9. 功能到情感翻译:把技术参数翻译成情绪语言
  10. +
+
+
+

家具案例:南方市场书桌

+
+
+ 竞品写法:实木材质、环保工艺、承重强
+ 南方真实痛点:潮湿发霉、回南天变形、桌面起泡
+ 破局写法:防潮不发霉+回南天不变形+桌面不起泡
+ 技术对应:防潮涂层+实木烘干+防水面板 +
+
+ 洞察:未被满足的地域性需求,是蓝海市场的黄金切入点。 +
+
+
+
+
+

功能到情感的翻译表(家具类)

+
+
技术参数 → 感知利益
纳米涂层 → "一擦即净"
防滑纹路 → "赤脚不滑倒"
+
技术参数 → 感知利益
抑菌材质 → "全家安心用"
圆角设计 → "磕碰不受伤"
+
技术参数 → 感知利益
可调高度 → "长高也能用"
隐藏收纳 → "看着不乱"
+
+
+
+ 检查清单:是否直接复制了竞品话术?(复制=没有差异化)真正的差异化来自对需求的深度洞察,而非页面设计的微调。 +
+
+ + +
+
+ S6 +

SOP 6:心智资产延伸(围绕核心关键词建立品牌生态)

+
+
+
+

执行步骤

+
    +
  1. 提炼一个核心关键词(如"安"、"省"、"美")
  2. +
  3. 围绕关键词,延伸3-5个产品线(同一心智,不同产品)
  4. +
  5. 确认:每个产品都能强化这个心智
  6. +
  7. 目标:降低用户认知成本,提升复购率30%+
  8. +
+
+
+

家具案例:围绕"安"建立心智

+
+
+ 核心关键词:"安"(安心、安全、安稳)
+ 产品延伸:
+ • 儿童书桌 → "安学"(安心学习)
+ • 老人椅 → "安养"(安心养老)
+ • 收纳柜 → "安放"(安心收纳) +
+
+ 目标:用户看到"安"字,就想到我们的品牌。单一卖点升级为品牌符号。 +
+
+
+
+
+ 检查清单:产品线是否围绕同一个心智关键词?如果每个产品打不同的心智,用户认知成本高,复购率低。 +
+
+ + +
+
+ S7 +

SOP 7:情感溢价高于功能溢价(消费者为情绪买单)

+
+
+
+

执行步骤

+
    +
  1. 列出产品的所有功能卖点
  2. +
  3. 对每个卖点,写出对应的情感价值
  4. +
  5. 对比:功能溢价 vs 情感溢价,哪个更能支撑价格?
  6. +
  7. 重构:把功能卖点包装成情感卖点
  8. +
+
+
+

家具案例:情感溢价对比

+
+
+ 沙发:功能溢价(舒适坐感)
+ → 情感溢价("承载幸福时光的中心")

+ 儿童床:功能溢价(安全耐用)
+ → 情感溢价("陪伴孩子安心入睡的城堡")

+ 餐桌:功能溢价(用餐功能)
+ → 情感溢价("连接家人情感的纽带") +
+
+ 核心观点:消费者为情绪买单,而非技术参数。 +
+
+
+
+
+ 检查清单:详情页中功能描述占比是否超过50%?(超过=情感溢价不足)建议功能:情感=3:7,让用户为情绪买单。 +
+
+ +
+ 总结:详情页的本质是消费者心理的翻译器——用结构化思维拆解需求,用情感语言包装卖点,才能让技术真正打动人心。这7个SOP可以按顺序执行,也可以根据产品特点选择性使用。建议每个SKU都走一遍SOP 1-5,SOP 6-7用于品牌长期建设。 +
+
+ + +
+

产品卖点提炼与用户洞察:基于真实场景的差异化表达

+ + +
+

超级心智卖点构建路径(三步法)

+
+ +
+
+ 1 +

强需求洞察

+
+
+
+ 锁定痛点:聚焦目标人群的核心痛点,区分基础需求与进阶需求 +
+
+ 家具案例:办公椅
+ • 基础需求:能坐
+ • 进阶需求:久坐不累、不腰痛
+ • 高价值细分:程序员/设计师的"久坐救星" +
+
+
+ + +
+
+ 2 +

价值重心聚焦

+
+
+
+ 浓缩承诺:将宽泛功能转化为具体可感知的承诺,技术术语生活化 +
+
+ 家具案例:收纳柜
+ • 宽泛功能:能收纳
+ • 具体承诺:"一秒找到,不再翻箱倒柜"
+ • 生活化表达:分区设计→"不用翻也能找到" +
+
+
+ + +
+
+ 3 +

差异化表达

+
+
+
+ 占领心智:创造专属词汇,借助背书、命名创新实现品类区隔 +
+
+ 家具案例:智能床垫
+ • 专属词:"云暖锁温"(替代"恒温技术")
+ • 命名创新:"安睡舱"(替代"床垫")
+ • 品类区隔:不是床垫,是"睡眠系统" +
+
+
+
+
+ 执行顺序:先做需求洞察(锁定痛点)→再做价值聚焦(浓缩承诺)→最后做差异化表达(占领心智)。三步缺一不可。 +
+
+ + +
+

核心需求三维拆解(体感/社交/实用)

+
+ +
+

体感需求(脚感体验)

+
+
+ 关键要素:舒适、软弹、支撑、包裹 +
+
+ 家具案例:沙发
+ • 舒适:坐感柔软不塌陷
+ • 支撑:久坐腰不累
+ • 包裹:贴合身体曲线
+ • 决定因素:填充物配比+框架结构 +
+
+
+ + +
+

社交需求(形象管理)

+
+
+ 心理诉求:"体面"=好看+好搭+易打理 +
+
+ 家具案例:餐桌
+ • 好看:设计简约有质感
+ • 好搭:适配多种装修风格
+ • 易打理:一擦即净不显脏
+ • 场景关联:朋友聚会不尴尬 +
+
+
+ + +
+

实用需求(长期价值)

+
+
+ 本质:耐穿=省钱,影响预算有限群体 +
+
+ 家具案例:书架
+ • 耐承重:放满书也不变形
+ • 耐磨:用十年也不显旧
+ • 复合价值:承重+防潮+易清洁
+ • 理性决策:性价比优先 +
+
+
+
+
+ 执行要点:每条核心卖点至少覆盖2个维度。家具类:体感(舒适/支撑)→社交(好看/好搭)→实用(耐用/省空间)。根据人群调整权重:年轻人重体感+社交,中年人重实用+体感。 +
+
+ + +
+

详情页设计五大法则(黄金五屏原则)

+
+ +
+

法则1:超级卖点重点说

+
+
+ 聚焦单一:在有限时间内传递一个清晰的核心概念 +
+
+ 家具案例:儿童书桌
+ • 核心概念:"安全"
+ • 每屏从不同角度诠释:
+ - 屏1:圆角不受伤
+ - 屏2:材质无毒害
+ - 屏3:结构稳固不倾倒
+ • 统一主题贯穿页面 +
+
+
+ + +
+

法则2:优势差异重复说

+
+
+ 强化记忆:通过多维度反复强化同一核心价值 +
+
+ 家具案例:办公椅
+ • 核心价值:"超稳"
+ • 多维度强化:
+ - 结构:五爪底座更稳
+ - 材质:加厚钢管更稳
+ - 测试:承重200kg更稳
+ • 符号化元素持续曝光 +
+
+
+ + +
+

法则3:页面逻辑重序说

+
+
+ 决策优先:按用户决策优先级安排内容顺序 +
+
+ 家具案例:智能床垫
+ • 传统顺序:功能→安全
+ • 新锐顺序:安全→功能
+ • 决策逻辑:先解决"敢不敢用",再解决"好不好用"
+ • 内部协作:各渠道传播一致性 +
+
+
+
+
+

黄金五屏原则执行清单

+
+
+ 屏1-2:核心卖点(聚焦单一)
+ 屏3-4:优势差异(重复强化)
+ 屏5:证据背书(建立信任) +
+
+ 检查点:是否每屏都围绕同一核心?
+ 检查点:是否按决策优先级排序?
+ 检查点:是否建立内部共识? +
+
+
+
+ 核心原则:一个核心,贯穿始终。重复是记忆之母,顺序决定转化率。 +
+
+ + +
+

FABE模型应用实践(特征→优势→利益→证据)

+
+ +
+

F - 特征(客观属性)

+
+
+ 定义:描述客观存在的产品属性,如材质、工艺、结构 +
+
+ 语言:使用中性词而非形容词,如"高频震动技术" +
+
+ 家具案例:实木书架
+ • 特征:E1级板材+加厚背板+圆角设计
+ • 表达:明确物理存在点,避免模糊
+ • 核心问题:是什么?——产品本身的技术事实 +
+
+
+ + +
+

A - 优势(比较优势)

+
+
+ 来源:从特性延伸出比较性优势,如"清洁效率提升4倍" +
+
+ 强调:优于同行或前代产品的具体表现 +
+
+ 家具案例:办公椅
+ • 优势:比普通椅子多30%支撑面积
+ • 表达:保持与特性的直接因果关系
+ • 核心问题:更好在哪?——技术带来的性能提升 +
+
+
+ + +
+

B - 利益(用户收益)

+
+
+ 转化:将优势转化为用户可感知的生命改善 +
+
+ 场景结合:结合使用场景讲述结果 +
+
+ 家具案例:沙发
+ • 利益:久坐不累、腰不痛
+ • 场景:下班回家躺下瞬间放松
+ • 人群差异:年轻人重"拍得美",中年人重"坐得舒服"
+ • 核心问题:对你有什么用?——情感与功能的双重满足 +
+
+
+ + +
+

E - 证据(可信背书)

+
+
+ 方式:提供第三方验证增强可信度 +
+
+ 选择:选择与品牌形象匹配的奖项来源 +
+
+ 家具案例:儿童书桌
+ • 证据:检测报告+专家背书+KOL测评
+ • 佐证:实验室数据+用户评价+安全认证
+ • 匹配:选择与"安全"匹配的权威机构
+ • 核心问题:为什么可信?——用事实消除疑虑 +
+
+
+
+
+

FABE完整案例:收纳书架

+
+
+ F(特征):三层分区设计+隐藏收纳格+加厚背板 +
+
+ A(优势):比普通书架多30%收纳空间,承重提升50% +
+
+ B(利益):不用整理也能一直整洁,一秒找到不再翻箱倒柜 +
+
+ E(证据):承重测试视频+收纳对比图+用户好评1000+条 +
+
+
+
+ 执行要点:FABE必须形成闭环。先写B(利益先行),再写F(事实支撑),再写A(优势解释),最后写E(证据收尾)。顺序不能乱。 +
+
+ +
+ 总结:产品卖点的本质是用户心智的占领,需以真实场景为起点,通过三维需求拆解、人群适配、FABE闭环与黄金五屏法则,实现从功能到价值的精准转化。建议每个SKU都按"超级心智卖点构建路径"走一遍,再用"FABE模型"输出每个核心卖点。 +
+
+ + +
+

文案创作的三大维度:关联想象与语言雕琢的实战方法

+ + +
+

核心发展脉络(三大维度)

+
+ +
+
+ 情感锚点 +

建立认知关联

+
+
+
+ 将产品特性与用户熟悉的物件、情感或外貌建立联想,突破参数局限,增强画面感与共鸣力 +
+
+ 家具案例:沙发
+ • 参数:高回弹海绵
+ • 关联:像云朵一样柔软
+ • 画面感:躺下就像被云朵包裹 +
+
+
+ + +
+
+ 风格适配 +

语言精雕细琢

+
+
+
+ 根据品牌定位选择短文案凝练或长文案叙事,实现表达的惊艳与效率平衡 +
+
+ 家具案例:书桌
+ • 短文案:静心办公,不被打扰
+ • 长文案:在安静的角落,找回专注的自己...
+ • 适配:高溢价用长文案,快消用短文案 +
+
+
+ + +
+
+ 避免形式主义 +

协同优化策略

+
+
+
+ 文案需兼顾情绪感染力与沟通本质,拒绝堆砌形容词与谐音梗 +
+
+ 家具案例:收纳柜
+ • 错误:超强收纳,巨能装,超好用
+ • 正确:不用整理也能一直整洁
+ • 原则:情绪+沟通,拒绝堆砌 +
+
+
+
+
+ 执行顺序:先建立认知关联(让用户理解)→再精雕语言(让表达惊艳)→最后协同优化(确保沟通效率)。三大维度缺一不可。 +
+
+ + +
+

关联想象类型(三类路径)

+
+ +
+

物件类比

+
+
+ 用熟悉的物件具象化抽象属性 +
+
+ 家具案例:
+ • 抽象:柔软舒适
+ • 类比:像云朵一样柔软(用云朵具象化"柔软")
+ • 类比:像棉花糖一样蓬松(用棉花糖具象化"蓬松")
+ • 类比:像蜂蜜一样温润(用蜂蜜具象化"温润")
+ • 效果:从抽象转向可感知,突破参数局限 +
+
+
+ + +
+

情感联想

+
+
+ 将产品特性与情感体验挂钩 +
+
+ 家具案例:
+ • 特性:温暖色调
+ • 联想:像家的温暖(将"温暖"与"家"挂钩)
+ • 联想:像拥抱一样安心(将"安心"与"拥抱"挂钩)
+ • 联想:像初恋的笑容一样治愈(将"治愈"与"笑容"挂钩)
+ • 效果:增强情感共鸣,突破参数局限 +
+
+
+ + +
+

外貌形容

+
+
+ 用具体的外貌特征描述产品 +
+
+ 家具案例:
+ • 描述:简约设计
+ • 形容:线条流畅如流水(用流水形容线条)
+ • 形容:质感温润如玉(用玉形容质感)
+ • 形容:你看起来很有品味,跟我的书桌一样(用"你"建立关联)
+ • 效果:画面感更强,增强共鸣力 +
+
+
+
+
+ 核心原则:从抽象属性转向可感知体验,是提升共鸣的核心。家具类:用"像XX一样"的句式,让用户瞬间理解。记住:物件类比(用熟悉物件)→情感联想(与情感挂钩)→外貌形容(用具体特征),三类路径缺一不可。 +
+
+ + +
+

短文案与长文案的选择策略

+
+ +
+
+ 记忆点设计 +

短文案提炼原则

+
+
+
    +
  • 采用四字、六字、八字结构
  • +
  • 注重节奏感与语义凝练
  • +
  • 避免冗余表达,提升传播效率
  • +
+
+ 家具案例:
+ • 四字:收纳不费力
+ • 六字:一秒找到不再翻箱倒柜
+ • 八字:不用整理也能一直整洁
+ • 原则:精简不是简单,而是语言密度的极致压缩 +
+
+
+ + +
+
+ 高溢价产品 +

长文案适用场景

+
+
+
    +
  • 售价千元以上的产品
  • +
  • 小众生活方式品牌
  • +
  • 需构建价值认同的品类
  • +
+
+ 家具案例:
+ • 适用:售价千元的懒人沙发(高溢价产品)
+ • 适用:设计师款沙发(小众生活方式品牌)
+ • 适用:实木餐桌(需构建价值认同的品类)
+ • 原则:功能同质化时,长文案是塑造差异化价值的唯一路径
+ • 策略:用长文案构建价值认同,而非只写功能参数 +
+
+
+
+
+ 选择标准:仅在利润空间充足时使用长文案。评估用户接受度与转化预期,拒绝为形式牺牲沟通效率。好的文案是策略的产物,不是艺术的炫耀。 +
+
+ + +
+

成本意识与ROI(理性评估)

+
+
+

评估维度

+
    +
  • 产品利润空间是否充足?
  • +
  • 用户是否有时间阅读长文案?
  • +
  • 转化预期是否值得投入?
  • +
  • 沟通效率是否被牺牲?
  • +
+
+
+

家具案例:决策矩阵

+
+
+ 高溢价产品(>3000元):
+ • 利润空间:充足 ✓
+ • 用户接受度:高 ✓
+ • 建议:用长文案 +
+
+ 快消产品(<1000元):
+ • 利润空间:有限 ✗
+ • 用户接受度:低 ✗
+ • 建议:用短文案 +
+
+
+
+
+ 核心原则:好的文案是策略的产物,不是艺术的炫耀。评估ROI后再决定用长文案还是短文案。 +
+
+ + +
+

常见误区与警示

+
+ +
+
+ 信息模糊 +

过度文艺化

+
+
+
    +
  • 堆砌抽象词汇,缺乏认知锚点
  • +
  • 追求诗意却丢失产品核心
  • +
  • 用户无法理解,传播失效
  • +
+
+ 家具案例:
+ • 错误:诗意生活,优雅如诗
+ • 正确:收纳不费力,一秒找到
+ • 原则:文案不是诗歌,沟通效率优先 +
+
+
+ + +
+
+ 无效创作 +

形式主义陷阱

+
+
+
    +
  • 滥用谐音梗与网络梗
  • +
  • 为炫技牺牲清晰表达
  • +
  • 脱离品牌定位与用户语境
  • +
+
+ 家具案例:
+ • 错误:收纳"柜"绝了(谐音梗)
+ • 正确:收纳不费力,一秒找到
+ • 原则:创意必须服务于目标 +
+
+
+
+
+ 红线警示:文案不是诗歌,沟通效率优先于文学性。创意必须服务于目标,而非自我表达。如果用户看不懂,再好的创意也是无效的。 +
+
+ + +
+

实战应用清单(家具类)

+
+
+

关联想象应用

+
+
+ 沙发:像云朵一样柔软 → 像拥抱一样安心 +
+
+ 书桌:像图书馆一样安静 → 像工作室一样专业 +
+
+ 书架:像博物馆一样有序 → 像书房一样有文化 +
+
+ 床垫:像云端一样舒适 → 像摇篮一样安心 +
+
+
+
+

短文案模板(家具类)

+
+
+ 四字:收纳不费力、一秒找到、久坐不累 +
+
+ 六字:不用整理也能一直整洁、长高也能用不担心 +
+
+ 八字:不用翻箱倒柜也能一秒找到、不用扶也能轻松起身 +
+
+
+
+
+ 执行建议:先建立认知关联(让用户理解)→再选择短/长文案(根据产品定位)→最后检查是否过度文艺化(确保沟通效率)。文案创作需以用户认知为起点,以沟通效率为终点。 +
+
+ + +
+

下半年执行策略(分品牌制定差异化文案策略)

+
+
+

策略制定维度

+
    +
  • 按品牌定位选择短/长文案
  • +
  • 按产品类型选择关联想象方式
  • +
  • 按用户群体选择情感锚点
  • +
  • 按利润空间选择文案投入
  • +
+
+
+

家具类分品牌策略

+
+
+ 高端品牌:长文案+情感联想+外貌形容 +
+
+ 快消品牌:短文案+物件类比+记忆点设计 +
+
+ 目标:强化实战落地能力,提升转化力 +
+
+
+
+
+ 执行建议:分品牌制定差异化文案策略,避免一刀切。高端品牌用长文案构建价值认同,快消品牌用短文案提升传播效率。 +
+
+ +
+ 总结:文案创作需以用户认知为起点,以沟通效率为终点。关联想象与语言雕琢是提升转化力的双引擎。下半年将分品牌制定差异化文案策略,强化实战落地能力。家具类:高溢价产品用长文案(构建价值认同),快消产品用短文案(提升传播效率)。记住:精简不是简单,而是对语言密度的极致压缩;好的文案是策略的产物,不是艺术的炫耀。 +
+
+ + +
+

详情页结构化转译方法论:基于用户任务与情绪价值的设计

+ + +
+

核心发展脉络(三大原则)

+
+ +
+
+ 123层级 +

结构化拆解

+
+
+
+ 将需求拆解为123层级,创建可复用的页面骨架,避免定制化碎片 +
+
+ 家具案例:书架
+ • 1级:核心卖点(收纳不费力)
+ • 2级:支撑卖点(分区设计+承重强)
+ • 3级:细节说明(尺寸+安装+材质)
+ • 目标:构建可复用的页面骨架 +
+
+
+ + +
+
+ 心智强化 +

新质词贯穿

+
+
+
+ 用新质词作为关键词锚点,统一卖点表达,反复强化消费者认知 +
+
+ 家具案例:儿童书桌
+ • 新质词:"安学"(替代"安全学习")
+ • 贯穿:屏1"安学设计"→屏2"安学材质"→屏3"安学认证"
+ • 效果:提升记忆与转化一致性 +
+
+
+ + +
+
+ 感性打断 +

节奏控制

+
+
+
+ 避免纯理性信息堆叠,用B类视觉创造沉浸体验,形成"感性-理性-感性"节奏 +
+
+ 家具案例:沙发
+ • 屏1:B类(家庭团聚场景)
+ • 屏2:A类(材质工艺)
+ • 屏3:B类(放松休息场景)
+ • 效果:延长停留时长 +
+
+
+
+
+ 执行顺序:先做结构化拆解(建立骨架)→再用新质词贯穿(统一表达)→最后控制节奏(感性打断)。三大原则缺一不可。 +
+
+ + +
+

ABC视觉体系(三类视觉内容)

+
+ +
+

A类调性(技术公信)

+
+
+ 内容:实验室场景、权威背书、奖项认证 +
+
+ 目标:构建专业感,支撑理性决策 +
+
+ 家具案例:
+ • 实验室:甲醛检测场景
+ • 权威:设计师推荐、环保认证
+ • 奖项:设计大奖、品质认证
+ • 使用:首屏或关键转折点 +
+
+
+ + +
+

B类场景(情绪共鸣)

+
+
+ 内容:模拟真实生活场景,触发情绪 +
+
+ 目标:驱动感性决策,提升代入感 +
+
+ 家具案例:
+ • 场景:孩子安心学习、家庭团聚
+ • 情绪:安心、温馨、放松
+ • 效果:让用户感觉"被看见"
+ • 使用:穿插在理性信息之间 +
+
+
+ + +
+

C类细节(理性佐证)

+
+
+ 内容:材质分析、工艺细节、安装指南、功能参数 +
+
+ 目标:巩固信任,防止比价 +
+
+ 家具案例:
+ • 材质:E1级板材+加厚背板
+ • 工艺:圆角打磨+防潮处理
+ • 参数:尺寸+承重+安装说明
+ • 作用:FABE的佐证层 +
+
+
+
+
+ 执行要点:每屏都要有情绪钩子(B类)与理性支撑(A/C类)。避免纯理性堆叠,用B类视觉打断,形成"感性-理性-感性"节奏。 +
+
+ + +
+

买点思维模型(卖点 vs 买点)

+
+ +
+

本质差异

+
+
+ 卖点:产品特性("我有多好")
+ 买点:用户任务("你能得到什么") +
+
+ 家具案例:
+ • 卖点:E1级板材、加厚背板
+ • 买点:不用整理也能一直整洁
+ • 驱动:感性驱动 > 理性分析
+ • 转变:从"我哪里好"到"你能得到什么" +
+
+
+ + +
+

FABE法则应用(结构变体)

+
+
+ 优先级:先感性(B),再F、A(小屏),最后E +
+
+ 家具案例:收纳书架
+ • B(利益):不用整理也能一直整洁
+ • F(特征):三层分区设计
+ • A(优势):比普通书架多30%空间
+ • E(证据):收纳测试视频+对比图
+ • 原则:让每屏都有情绪钩子与理性支撑 +
+
+
+
+
+

跨职能协同(认知墙突破)

+
+
+ 策划:主导框架设计,理解用户动机
+ 设计:执行视觉转译,理解ABC体系 +
+
+ 运营:理解视觉结构,推动标准化落地
+ 目标:打破部门壁垒,实现内容-视觉-策略三位一体 +
+
+
+
+ 核心原则:避免拼贴式设计,理解文案底层逻辑。让每屏都有情绪钩子(B类)与理性支撑(A/C类)。 +
+
+ + +
+

执行策略与落地(三款策略)

+
+ +
+
+ 打造高度 +

形象款

+
+
+
+ 策略:技术深度与情绪感染力,强化A类调性和B类场景 +
+
+ 家具案例:设计师款沙发
+ • 用途:品牌升级、高端定位
+ • 重点:A类(权威背书)+ B类(生活方式)
+ • 原则:不求量,但求质与势 +
+
+
+ + +
+
+ 批量上新 +

动销款

+
+
+
+ 策略:简单结构,确保基础信息完整,标准化模块复用率>80% +
+
+ 家具案例:常规书架
+ • 用途:批量上新、快速响应
+ • 重点:C类(基础信息)+ 标准化模块
+ • 原则:效率优先,结构可复制 +
+
+
+ + +
+
+ 情感引爆 +

爆款款

+
+
+
+ 策略:强化B类场景和用户共鸣,嵌入可分享的社交语言 +
+
+ 家具案例:网红书桌
+ • 用途:情感引爆、刺激收藏加购
+ • 重点:B类(场景共鸣)+ 社交语言
+ • 原则:停留时长决定转化率 +
+
+
+
+
+ 选择标准:根据产品定位选择策略。形象款(品牌高度)→动销款(批量效率)→爆款款(情感引爆)。不同策略对应不同的ABC组合。 +
+
+ + +
+

实战应用清单(家具类)

+
+
+

ABC组合示例

+
+
+ 屏1:B类(家庭团聚场景)
+ 屏2:A类(材质工艺+权威认证)
+ 屏3:B类(放松休息场景)
+ 屏4:C类(尺寸+安装+参数)
+ 屏5:B类(生活方式场景) +
+
+ 节奏:感性-理性-感性-理性-感性 +
+
+
+
+

新质词贯穿示例

+
+
+ 新质词:"安学"(儿童书桌)
+ • 屏1:安学设计,圆角不受伤
+ • 屏2:安学材质,无毒无害
+ • 屏3:安学认证,权威检测
+ • 屏4:安学陪伴,孩子爱用 +
+
+ 效果:提升记忆与转化一致性 +
+
+
+
+
+ 执行建议:先做结构化拆解(123层级)→再用新质词贯穿(统一表达)→最后控制节奏(感性打断)。让每屏都有情绪钩子(B类)与理性支撑(A/C类)。 +
+
+ +
+ 总结:详情页设计已从功能展示进化为用户心智工程。唯有以买点思维为轴心、ABC视觉为骨架、跨职能协同为引擎,方能实现从"看"到"买"的高效转化。家具类:形象款强化A+B类,动销款标准化C类,爆款款强化B类场景。 +
+
+ + +
+

01|准备阶段:做对输入,输出才会对

+
+
+
    + +
+
+
+

本阶段交付

+
《定位卡》1页
+
《原话素材池》≥30条
+
《竞品结构拆解》TOP3
+ +
+ 讲师提醒:先让大家承认“我们不是在写文案”,而是在“设计决策路径”。 +
+
+
+
+ + +
+

02|需求拆解:把功能变成“用户要的结果”

+
+
+
    +
    +
    +

    本阶段交付

    +
    《卖点→买点转译清单》
    +
    《五类需求分级表》只保留3–5个核心需求
    +
    《三维需求覆盖表》每条卖点≥2维
    + +
    + 红线:如果任何一句话回答不了“所以呢?对我有什么用?”,就不许进详情页。 +
    +
    +
    +
    + + +
    +

    03|卖点提炼:从“参数”到“情绪价值”

    +
    +
    +
      +
      +
      +

      本阶段交付

      +
      《痛点三连问》结果句(每条卖点1句)
      +
      《技术→结果→情绪》转译表
      +
      《唯一心智标签》1个词 + 3种表达
      + +
      + 讲师提醒:不要先写“形容词”,先写“用户结果”,再补“情绪词”。 +
      +
      +
      +
      + + +
      +

      04|页面结构:用结构化转译把卖点变成“路径”

      +
      +
      +
        +
        +
        +

        本阶段交付

        +
        《前3屏脚本》B→买点→证据
        +
        《ABC视觉分工表》文案/设计/运营
        +
        《内容节奏》避免理性堆叠
        + +
        + 常见错法:把A类理性(材质工艺)放在第一屏,导致用户没共鸣就跳出。 +
        +
        +
        +
        + + +
        +

        05|写作产出:FABE模块化 + 语言精雕(短、准、可验证)

        +
        +
        +
          +
          +
          +

          本阶段交付

          +
          每个核心卖点 1个FABE模块
          +
          标题:4–8字 + 副标题:1句话结果
          +
          证据清单:图/测/对比/口碑/资质
          + +
          + 一句话标准:好文案 = 读完更快理解 + 更愿意相信 + 更想下单。 +
          +
          +
          +
          + + +
          +

          06|验证迭代:把一次优化变成“长期战斗力”

          +
          +
          +
            +
            +
            +

            本阶段交付

            +
            《AB测试计划》一次只改1件事
            +
            《复盘报告》四指标 + 结论
            +
            《内容资产库》模板化可复用
            + +
            + 建议机制:每周固定一次“详情页复盘会”,把咨询关键词和跳出点当作需求。 +
            +
            +
            +
            + + +
            +

            表格模板(可复制到表格/Notion/飞书)

            + +
            +
            +

            模板1|定位卡(1页)

            +
            产品:________ +主购买者:________ 主使用者:________ +主场景:________ 触发时刻:________ +用户怕什么(风险):________ +用户想要的结果:________ +一句话定位(对谁/在何时/解决什么):________
            +
            + +
            +

            模板2|原话素材池(30条起)

            +
            原话|情绪词|触发场景|真实诉求|可用作哪一屏(B/A/C)|证据线索 +例:桌面太乱看着烦|烦|书桌|想更清爽|B|对比图 +例:书架倒了书全掉|怕|书房|要更稳|A|承重测试 +例:孩子写作业趴着|急|儿童房|要坐得直|B|对比图 +例:收纳柜占地方|烦|客厅|要省空间|B|占地对比
            +
            + +
            +

            模板3|卖点→买点转译

            +
            卖点(我有什么)|用户任务(我想在___也能___)|买点句(不用___也能___)|三维覆盖(体验/社交/实用)|证据 +例:可伸缩|我想换桌也能放得下|不用量尺寸也能刚刚好|体验+实用|实拍适配 +例:实木材质|我想用十年也不坏|不用换也能一直用|实用+社交|材质证明 +例:圆角设计|我想孩子用也不受伤|不用担心磕碰也能安全|体验+实用|安全测试 +例:隐藏收纳|我想客厅看着不乱|不用藏也能整洁|体验+社交|实拍对比
            +
            + +
            +

            模板4|FABE卖点块(标准化输出)

            +
            B(利益先行):________(对我有什么用) +F(事实支撑):________(客观是什么) +A(优势解释):________(为什么更好) +E(证据收尾):________(凭什么信) + +标题(4–8字):________ +副标题(1句话结果):________ + +【家具案例:收纳书架】 +B:不用整理也能一直整洁 +F:三层分区+隐藏收纳格 +A:比普通书架多30%收纳空间 +E:实拍对比图+收纳测试视频 +标题:收纳不费力 +副标题:三层分区设计,书和杂物各归其位
            +
            +
            + +
            + 落地建议:先把这4个模板变成团队统一表格(或飞书多维表)。每个SKU都按同一套“输入—输出”走一遍,团队能力会快速复制。 +
            +
            + + +
            + +
            + + + +
            +
            + + 详情页排版演示:人体工学椅(13屏完整转化流) +
            + + +
            +

            第 1 屏:[KV] "第二脊椎™"人体工学椅 - 动态追踪腰部支撑系统

            +

            SOP应用:首屏吸睛定调。必须展示极具冲击力的产品图(Hero Image),并配以“Slogan + 核心利益点(The One)”,一句话告诉用户“这是什么”以及“为什么它值得看下去”。

            +
            +
            +

            专为久坐程序员研发

            +

            让你的腰这辈子不再酸痛。

            +
            [此处展示产品45度角高清渲染图,突出腰托结构]
            +
            +
            +
            吸睛定调KV展示
            +
            + + +
            +

            第 2 屏:每天坐8小时,你的腰还好吗?(核心痛点)

            +

            SOP应用:场景唤醒(Awaken Pain)。不直接卖产品,先描述“久坐腰断”的具体感受,唤起用户的生理痛苦记忆,建立共情。

            +
            +
            +

            B (Benefit)

            • 彻底告别“下班腰直不起来”的痛苦,重获健康的腰椎曲线。
            • 让你从“忍痛工作”变成“享受工作”。
            +

            A (Advantage)

            • 普通椅子:坐姿僵硬,腰部悬空,压力全在腰椎。
            • 现状:90%的程序员都有腰肌劳损隐患。
            +
            +

            E (Evidence)

            • [展示一张脊椎受力热力图,对比普通坐姿与健康坐姿的压力分布]
            +
            +
            痛点唤醒场景一:办公
            +
            + + +
            +

            第 3 屏:午休只能趴着睡?手麻脖子酸?(隐性痛点)

            +

            SOP应用:痛点叠加(Pain Stacking)。挖掘用户除“坐”以外的第二大痛点——“午休”。将隐性需求显性化,增加购买理由。

            +
            +
            +

            B (Benefit)

            • 无需折叠床,一秒切换“头等舱”午休模式。
            • 睡醒满血复活,下午工作效率翻倍。
            +

            A (Advantage)

            • 趴睡危害:压迫眼球、胃部胀气、手臂发麻。
            • 普通工学椅:后仰角度小,只能半躺,根本睡不着。
            +
            +
            +
            痛点挖掘场景二:午休
            +
            + + +
            +

            第 4 屏:什么是“第二脊椎”?(核心方案)

            +

            SOP应用:概念提出(The Concept)。抛出核心解决方案,解释为什么它能解决上述痛点。用通俗易懂的“技术转译”解释原理。

            +
            +
            +
            仿生脊椎动态支撑系统不是固定不动的靠背,而是一套会“思考”的支撑系统。
            +
            +

            技术原理

            • 智能感应:通过重力感应底盘,自动识别你的体重和坐姿。
            • 动态追踪:椅背像脊椎一样灵活,时刻贴合你的背部曲线。
            +
            +
            +
            +
            核心方案心智命名
            +
            + + +
            +

            第 5 屏:你动它也动,像影子一样追着你的腰 (核心收益1)

            +

            SOP应用:卖点展开(Active Support)。针对 Screen 2 的办公痛点,给出具体的解决方案和利益点。

            +
            +
            +

            B (Benefit)

            • 前倾码字时,腰托自动前顶,给腰部强力支撑。
            • 后仰思考时,椅背自动跟随,不留一丝悬空缝隙。
            +

            A (Advantage)

            • 普通椅子:你动它不动,稍微换个姿势腰就空了。
            • 本产品:无论怎么动,腰部始终有“依靠”。
            +
            +

            E (Evidence)

            • [GIF演示:模特在椅子上频繁变换坐姿,腰托始终紧贴腰部]
            +
            +
            核心收益动态支撑
            +
            + + +
            +

            第 6 屏:一秒切换“零重力”午休舱 (核心收益2)

            +

            SOP应用:卖点展开(Deep Relaxation)。针对 Screen 3 的午休痛点,展示“躺平”的能力。

            +
            +
            +

            B (Benefit)

            • 135°黄金仰角,让全身血液回流,心脏负荷降至最低。
            • 伸缩脚托托住双腿,彻底释放腿部压力,拒绝静脉曲张。
            +

            F (Feature)

            • 三档锁定:90°办公 / 110°手游 / 135°午休。
            • 隐藏式伸缩脚托:用时拉出,不用收起,不占空间。
            +
            +
            +
            核心收益午休神器
            +
            + + +
            +

            第 7 屏:5D机械臂扶手,手游/码字/阅读全适配 (功能细节1)

            +

            SOP应用:场景适配(Scenario Adaptation)。将枯燥的参数(5D)转化为具体的使用场景。

            +
            +
            +
            F (Feature): 5D多维调节上下/前后/左右/旋转/上扬,总有一个角度适合你。
            +
            +

            可视化场景

            • 手游模式:扶手上扬,稳稳托住手肘,打排位手不酸。
            • 码字模式:扶手内收,手臂自然下垂,肩膀不耸肩。
            • 阅读模式:扶手加宽,手机/Kindle随心放。
            +
            +
            +
            +
            C级信息细节体验
            +
            + + +
            +

            第 8 屏:4点精调系统,身高155-190cm全家通吃 (功能细节2)

            +

            SOP应用:全员适配(Universal Fit)。解决“我能坐吗?我女朋友能坐吗?”的适用性疑虑。

            +
            +

            F (Feature) + B (Benefit)

            • 头枕升降/旋转:精准承托颈椎,无论高矮都能护颈。
            • 椅背升降:腰托位置随身高调节,对准腰椎第3-4节黄金受力点。
            • 坐深调节:腿长腿短都能坐满椅面,大腿不悬空,膝盖不压迫。
            +
            +
            C级信息适用性
            +
            + + +
            +

            第 9 屏:德国Wintex网布,坐十年也不塌陷 (材质科技)

            +

            SOP应用:品质体感(Quality & Touch)。对比材质差异,建立高品质认知。

            +
            +
            +

            B (Benefit)

            • 夏天久坐不闷汗,像穿了透气跑鞋一样干爽。
            • 高弹支撑,久坐不塌陷,十年如一日的Q弹。
            +

            A (Advantage)

            • 普通海绵:夏天热得像蒸桑拿,坐半年就塌成坑。
            • 国产网布:太硬勒大腿,太软没支撑。
            +
            +
            +
            材质工艺高品质
            +
            + + +
            +

            第 10 屏:恐爆症?看看我们的暴力测试 (安全信任)

            +

            SOP应用:消除恐惧(Eliminate Fear)。针对用户对气压杆爆炸的深层恐惧,用硬核测试建立安全壁垒。

            +
            +

            F (Feature) 硬核配置

            • 韩国KGS四级气压杆:管壁加厚2mm,氮气纯度99.9%,永不爆炸。
            • 加厚防爆钢板:底盘内置3mm钢板,给屁股穿上“防弹衣”。
            +

            E (Evidence)

            • [视频:200kg铁球高空坠落撞击测试,椅子完好无损]
            • [视频:气压杆升降12万次疲劳测试]
            +
            +
            安全信任消除顾虑
            +
            + + +
            +

            第 11 屏:骨科医生眼里的“护腰神器” (权威背书)

            +

            SOP应用:建立权威(Establish Authority)。借用第三方权威背书,提升产品的专业度。

            +
            +

            权威认证

            • SGS / BIFMA 双重认证:通过美标、欧标严苛测试。
            • 人体工学协会推荐:荣获“年度最佳人体工学设计奖”。
            +

            专家证言

            “一把好椅子,应该是主动适应人的,而不是人去适应椅子。这款产品的动态追腰设计,非常符合脊椎的生理曲度。” —— 某三甲医院康复科主任

            +
            +
            权威背书信任状
            +
            + + +
            +

            第 12 屏:法拉利设计美学,是椅子更是艺术品 (美学/口碑)

            +

            SOP应用:情感共鸣(Emotional Resonance)。从功能上升到审美,满足用户的“悦己”需求。

            +
            +

            F (Feature)

            • 极简流线型设计:灵感源自超跑座椅,线条流畅,科技感十足。
            • 三种高定配色:深空灰 / 极夜黑 / 冰川白,完美融入各种家居风格。
            +

            E (Evidence) 用户晒图

            • “颜值太高了,放在书房瞬间提升了整个房间的档次。” —— 设计师用户
            • “不仅好坐,还好看,朋友来家里都问链接。”
            +
            +
            魅力属性颜值正义
            +
            + + +
            +

            第 13 屏:10年质保,只换不修 (转化收口)

            +

            SOP应用:临门一脚(Final Push)。用超级承诺消除最后的决策风险,并提供限时福利促单。

            +
            +
            +

            无忧承诺

            • 10年质保,核心部件只换不修。
            • 30天免费试坐,不满意包运费退回。
            +

            限时福利

            • 前100名下单,赠送价值199元定制午休毯。
            • 赠送价值99元头枕套。
            +
            +
            +
            +

            日均成本不到 0.3 元

            +

            投资一次,守护未来 3650 天的脊椎健康。

            +
            + +
            +
            + +