| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- import { useState, useEffect } from "react";
- import { api } from "../api/client";
- import styles from "./PreviewPanel.module.css";
- interface Props {
- creativeId: string;
- creativeStatus: string;
- theme: Record<string, string>;
- }
- export default function PreviewPanel({ creativeId, creativeStatus, theme }: Props) {
- const [previewUrl, setPreviewUrl] = useState<string | null>(null);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState("");
- const [loaded, setLoaded] = useState(false);
- const canPreview = creativeStatus === "assets_ready" || creativeStatus === "built" || creativeStatus === "building";
- // 预览启动后 5s 超时
- useEffect(() => {
- if (!previewUrl || loaded) return;
- const timer = setTimeout(() => {
- if (!loaded) setError("预览加载超时,请检查控制台或重试");
- }, 10000);
- return () => clearTimeout(timer);
- }, [previewUrl, loaded]);
- // 退出页面时停止预览
- useEffect(() => {
- return () => {
- if (previewUrl) {
- api.stopPreview(creativeId).catch(() => {});
- }
- };
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
- async function handleStart() {
- setLoading(true);
- setError("");
- setLoaded(false);
- try {
- const res = await api.startPreview(creativeId, theme);
- setPreviewUrl(res.data.url);
- } catch (err: any) {
- setError(err.message);
- } finally {
- setLoading(false);
- }
- }
- async function handleStop() {
- try {
- await api.stopPreview(creativeId);
- } catch {}
- setPreviewUrl(null);
- setLoaded(false);
- setError("");
- }
- return (
- <div className={styles.wrapper}>
- <div className={styles.header}>
- <h3 className={styles.title}>实时预览</h3>
- {!previewUrl ? (
- <button
- onClick={handleStart}
- disabled={!canPreview || loading}
- className={styles.startBtn}
- >
- {loading ? "启动中…" : "▶ 开始预览"}
- </button>
- ) : (
- <button onClick={handleStop} className={styles.stopBtn}>
- 关闭预览
- </button>
- )}
- </div>
- {!canPreview && !previewUrl && (
- <p className={styles.hint}>上传素材后即可预览</p>
- )}
- {error && <p className={styles.error}>{error}</p>}
- {loading && (
- <div className={styles.loadingBox}>
- <span className={styles.spinner} />
- <span>Vite 开发服务器启动中,请稍候…</span>
- </div>
- )}
- {previewUrl && (
- <div className={styles.frameWrap}>
- <iframe
- src={previewUrl}
- className={styles.frame}
- title="广告预览"
- onLoad={() => setLoaded(true)}
- />
- </div>
- )}
- </div>
- );
- }
|