import { useCallback, useEffect, useMemo, useState } from "react"; import { App, Button, Input, Modal, Select, Spin } from "antd"; import { Copy, Link2, RefreshCw, Share2, Unlink } from "lucide-react"; import { canvasThemes } from "@/lib/canvas-theme"; import { createCanvasShare, deleteCanvasShare, getCanvasShare, type CanvasShareStatus } from "@/services/api/canvas-share"; import { useThemeStore } from "@/stores/use-theme-store"; export function CanvasShareModal({ projectId, open, onClose, beforeCreate }: { projectId: string; open: boolean; onClose: () => void; beforeCreate: () => Promise }) { const { message, modal } = App.useApp(); const theme = canvasThemes[useThemeStore((state) => state.theme)]; const [share, setShare] = useState({ enabled: false }); const [expiresDays, setExpiresDays] = useState(0); const [loading, setLoading] = useState(false); const [submitting, setSubmitting] = useState(false); const shareUrl = useMemo(() => share.token ? `${window.location.origin}/share/canvas/${share.token}` : "", [share.token]); const load = useCallback(async () => { setLoading(true); try { const result = await getCanvasShare(projectId); setShare(result.share); } catch (error) { message.error(error instanceof Error ? error.message : "读取分享状态失败"); } finally { setLoading(false); } }, [message, projectId]); useEffect(() => { if (open) void load(); }, [load, open]); const copy = async (value = shareUrl) => { if (!value) return; await navigator.clipboard.writeText(value); message.success("分享链接已复制"); }; const create = async (rotate = false) => { setSubmitting(true); try { await beforeCreate(); const result = await createCanvasShare(projectId, { expiresDays, rotate }); setShare(result.share); const url = result.share.token ? `${window.location.origin}/share/canvas/${result.share.token}` : ""; await copy(url); } catch (error) { message.error(error instanceof Error ? error.message : "创建分享链接失败"); } finally { setSubmitting(false); } }; const revoke = () => modal.confirm({ title: "停止公开分享?", content: "现有分享链接会立即失效,原画布内容不会被删除。", okText: "停止分享", okButtonProps: { danger: true }, cancelText: "取消", onOk: async () => { await deleteCanvasShare(projectId); setShare({ enabled: false }); message.success("已停止分享"); }, }); return ( 分享画布} open={open} onCancel={onClose} footer={null} centered width={520} destroyOnHidden>

获得链接的人无需登录即可查看。访客可拖动画布节点并临时添加节点,但刷新后会恢复,不能修改原画布或执行生成。

{share.enabled && shareUrl ? (
} onClick={() => void copy()} aria-label="复制分享链接" />} />
{share.expiresAt ? `有效至 ${new Date(share.expiresAt).toLocaleString("zh-CN")}` : "长期有效,直至手动停止分享"}
) : (