'use client'; import { useState, useEffect, useRef } from 'react'; import { X, Link2, Copy, Check, Loader2, Eye, MessageSquare, Wrench, ImageIcon, QrCode, ExternalLink, Trash2, } from 'lucide-react'; import { cn } from '@/lib/utils'; import { QRCodeSVG } from 'qrcode.react'; import { MessageSelector } from './MessageSelector'; // 消息接口 interface MessageForShare { id: string; role: 'user' | 'assistant'; content: string; } interface ShareModalProps { isOpen: boolean; onClose: () => void; conversationId: string; conversationTitle: string; messages?: MessageForShare[]; onPreview?: (url: string) => void; } interface ShareInfo { shareId: string; shareCode: string; shareUrl: string; title: string; viewCount?: number; createdAt?: string; } export function ShareModal({ isOpen, onClose, conversationId, conversationTitle, messages = [], onPreview, }: ShareModalProps) { // 表单状态 const [title, setTitle] = useState(conversationTitle); const [includeThinking, setIncludeThinking] = useState(true); const [includeToolCalls, setIncludeToolCalls] = useState(false); const [includeImages, setIncludeImages] = useState(true); const [selectedMessageIds, setSelectedMessageIds] = useState(null); // null 表示全部 // UI 状态 const [isCreating, setIsCreating] = useState(false); const [shareInfo, setShareInfo] = useState(null); const [copied, setCopied] = useState(false); const [error, setError] = useState(null); const [showQrCode, setShowQrCode] = useState(false); // 已有分享列表 const [existingShares, setExistingShares] = useState([]); const [loadingShares, setLoadingShares] = useState(false); const modalRef = useRef(null); // 重置状态 useEffect(() => { if (isOpen) { setTitle(conversationTitle); setShareInfo(null); setError(null); setCopied(false); setShowQrCode(false); setSelectedMessageIds(null); // 重置为全部 loadExistingShares(); } }, [isOpen, conversationTitle]); // 点击外部关闭 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(event.target as Node)) { onClose(); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen, onClose]); // ESC 关闭 useEffect(() => { const handleEsc = (event: KeyboardEvent) => { if (event.key === 'Escape') { onClose(); } }; if (isOpen) { document.addEventListener('keydown', handleEsc); } return () => { document.removeEventListener('keydown', handleEsc); }; }, [isOpen, onClose]); // 加载已有分享 const loadExistingShares = async () => { try { setLoadingShares(true); const response = await fetch(`/api/conversations/${conversationId}/share`); if (response.ok) { const data = await response.json(); setExistingShares(data.shares || []); } } catch (err) { console.error('Load shares error:', err); } finally { setLoadingShares(false); } }; // 创建分享 const handleCreateShare = async () => { // 验证:自定义选择时必须至少选择一条消息 if (selectedMessageIds !== null && selectedMessageIds.length === 0) { setError('请至少选择一轮对话进行分享'); return; } try { setIsCreating(true); setError(null); const response = await fetch(`/api/conversations/${conversationId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, includeThinking, includeToolCalls, includeImages, selectedMessageIds, // 新增:选择的消息ID,null 表示全部 }), }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || '创建分享失败'); } const data = await response.json(); setShareInfo(data); loadExistingShares(); } catch (err) { setError(err instanceof Error ? err.message : '创建分享失败'); } finally { setIsCreating(false); } }; // 复制链接 const handleCopy = async () => { if (!shareInfo?.shareUrl) return; try { await navigator.clipboard.writeText(shareInfo.shareUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error('Copy error:', err); } }; // 删除分享 const handleDeleteShare = async (shareId: string) => { if (!confirm('确定要删除此分享吗?')) return; try { const response = await fetch( `/api/conversations/${conversationId}/share?shareId=${shareId}`, { method: 'DELETE' } ); if (response.ok) { setExistingShares((prev) => prev.filter((s) => s.shareId !== shareId)); if (shareInfo?.shareId === shareId) { setShareInfo(null); } } } catch (err) { console.error('Delete share error:', err); } }; if (!isOpen) return null; return (
{/* 头部 */}

分享对话

{/* 内容 */}
{shareInfo ? ( // 分享成功后显示链接
分享链接已生成
{/* 链接复制区域 */}
{/* 二维码 */}
{showQrCode && (
)}
{/* 操作按钮 */}
) : ( // 创建分享表单
{/* 标题 */}
setTitle(e.target.value)} placeholder="输入分享标题" className="w-full px-3 py-2.5 bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded-lg text-sm text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:border-[var(--color-primary)]" />
{/* 消息选择器 */} {messages.length > 0 && ( )} {/* 内容选项 - 横向布局 */}
{/* 错误提示 */} {error && (
{error}
)} {/* 创建按钮 */}
)} {/* 已有分享列表 */} {existingShares.length > 0 && !shareInfo && (

已有分享 ({existingShares.length})

{existingShares.map((share) => (
{share.title}
{share.viewCount || 0} 次查看
))}
)}
); }