/** * 分享工具函数 * 支持移动浏览器原生分享 API 和 WhatsApp 分享 */ interface ShareData { title?: string text?: string url?: string files?: File[] } /** * 检查是否支持 Web Share API */ export function isWebShareSupported(): boolean { return typeof navigator !== 'undefined' && 'share' in navigator } /** * 使用 Web Share API 进行分享(移动浏览器原生分享) * @param data 分享数据 */ async function shareWithWebAPI(data: ShareData): Promise { if (!isWebShareSupported()) { throw new Error('Web Share API is not supported') } try { // Web Share API 只支持 title, text, url 和 files const shareData: ShareData = {} if (data.title) shareData.title = data.title if (data.text) shareData.text = data.text if (data.url) shareData.url = data.url if (data.files && data.files.length > 0) shareData.files = data.files await navigator.share(shareData) } catch (error: any) { // 用户取消分享时,会抛出 AbortError,这是正常情况 if (error.name !== 'AbortError') { console.error('分享失败:', error) throw error } } } /** * 分享到 WhatsApp(回退方案) * @param text 分享的文本内容 * @param url 分享的链接(可选) */ export function shareToWhatsApp(text: string, url?: string): void { const shareText = url ? `${text} ${url}` : text const encodedText = encodeURIComponent(shareText) const whatsappScheme = `whatsapp://send?text=${encodedText}` const whatsappWebUrl = `https://wa.me/?text=${encodedText}` const isMobile = typeof navigator !== 'undefined' && /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent) if (typeof window === 'undefined') return if (isMobile) { // 优先尝试调用已安装的 WhatsApp 应用,失败后回退到 Web 版本 let didFallback = false const fallbackTimer = window.setTimeout(() => { didFallback = true window.location.href = whatsappWebUrl }, 1500) try { window.location.href = whatsappScheme } catch (error) { window.clearTimeout(fallbackTimer) window.location.href = whatsappWebUrl } // 某些浏览器会在成功唤起 App 时终止脚本,无需额外处理 return } // 桌面端或不支持 scheme 的环境直接打开 WhatsApp Web window.open(whatsappWebUrl, '_blank') } /** * 通用分享函数(优先使用 Web Share API,不支持则回退到 WhatsApp) * @param options 分享选项 */ export async function share(options: { title?: string text?: string url?: string files?: File[] fallbackToWhatsApp?: boolean }): Promise { const { title, text, url, files, fallbackToWhatsApp = true } = options // 如果支持 Web Share API,优先使用 if (isWebShareSupported() && !files) { try { await shareWithWebAPI({ title, text, url }) return } catch (error) { // 如果分享失败且允许回退,则使用 WhatsApp if (fallbackToWhatsApp) { const shareText = [title, text, url].filter(Boolean).join('\n\n') shareToWhatsApp(shareText) return } throw error } } // 如果不支持 Web Share API 或需要分享文件,使用 WhatsApp if (fallbackToWhatsApp) { const shareText = [title, text, url].filter(Boolean).join('\n\n') shareToWhatsApp(shareText) } else { throw new Error('Web Share API is not supported and fallback is disabled') } } /** * 分享当前页面(优先使用原生分享,不支持则回退到 WhatsApp) * @param title 分享的标题 * @param description 分享的描述(可选) */ export async function shareCurrentPage(title: string, description?: string): Promise { const currentUrl = window.location.href await share({ title, text: description, url: currentUrl, fallbackToWhatsApp: true }) } /** * 分享图片(优先使用原生分享,不支持则回退到 WhatsApp) * @param imageUrl 图片链接 * @param title 分享的标题 * @param description 分享的描述(可选) */ export async function shareImage(imageUrl: string, title: string, description?: string): Promise { const currentUrl = window.location.href const text = description ? `${description}\n\n查看图片: ${imageUrl}` : `查看图片: ${imageUrl}` await share({ title, text, url: currentUrl, fallbackToWhatsApp: true }) } /** * 分享图片文件(使用 Web Share API,支持直接分享图片文件) * @param imageFile 图片文件 * @param title 分享的标题(可选) * @param text 分享的文本(可选) */ export async function shareImageFile(imageFile: File, title?: string, text?: string): Promise { if (!isWebShareSupported()) { throw new Error('WEB_SHARE_UNSUPPORTED') } if (typeof navigator.canShare === 'function' && !navigator.canShare({ files: [imageFile] })) { throw new Error('WEB_SHARE_FILE_UNSUPPORTED') } await shareWithWebAPI({ title, text, files: [imageFile] }) } /** * 将远程图片转换为 File 对象,便于提前缓存后再触发分享 * @param imageUrl 图片链接 * @param fileName 文件名(可选) */ export async function createImageFileFromUrl(imageUrl: string, fileName?: string): Promise { const response = await fetch(imageUrl) const blob = await response.blob() const name = fileName || imageUrl.split('/').pop() || 'share-image.jpg' return new File([blob], name, { type: blob.type || 'image/jpeg' }) } /** * 分享当前页面到 WhatsApp(兼容旧版本,推荐使用 shareCurrentPage) * @param title 分享的标题 * @param description 分享的描述(可选) */ export async function shareCurrentPageToWhatsApp(title: string, description?: string): Promise { await shareCurrentPage(title, description) } /** * 分享图片到 WhatsApp(兼容旧版本,推荐使用 shareImage) * @param imageUrl 图片链接 * @param title 分享的标题 * @param description 分享的描述(可选) */ export async function shareImageToWhatsApp(imageUrl: string, title: string, description?: string): Promise { await shareImage(imageUrl, title, description) }