diff --git a/video-gen-admin/src/pages/adminReplication/components/MediaPreview.tsx b/video-gen-admin/src/pages/adminReplication/components/MediaPreview.tsx index 8a9b9b3e..95355ed2 100644 --- a/video-gen-admin/src/pages/adminReplication/components/MediaPreview.tsx +++ b/video-gen-admin/src/pages/adminReplication/components/MediaPreview.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Alert, Button, Empty, Image, Space, Typography, message } from 'antd'; import { CopyOutlined, LinkOutlined } from '@ant-design/icons'; +import { copyToClipboard } from '../../../utils/clipboard'; const RAW_API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8000'; const RESOURCE_BASE = RAW_API_BASE.replace(/\/api\/?$/i, '').replace(/\/$/, ''); @@ -37,12 +38,8 @@ const MediaPreview: React.FC = ({ const copyUrl = async () => { if (!resolvedUrl) return; - try { - await navigator.clipboard.writeText(resolvedUrl); - message.success('资源地址已复制'); - } catch { - message.error('复制失败,请手动复制'); - } + const ok = await copyToClipboard(resolvedUrl); + message.success(ok ? '资源地址已复制' : '复制失败,请手动复制'); }; const tools = resolvedUrl ? ( diff --git a/video-gen-admin/src/utils/clipboard.ts b/video-gen-admin/src/utils/clipboard.ts new file mode 100644 index 00000000..c3b611e4 --- /dev/null +++ b/video-gen-admin/src/utils/clipboard.ts @@ -0,0 +1,23 @@ +/** 安全复制文本到剪贴板,兼容非 HTTPS 环境 */ +export async function copyToClipboard(text: string): Promise { + try { + if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') { + await navigator.clipboard.writeText(text); + return true; + } + // 降级方案:使用 textarea + execCommand + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + textarea.style.top = '0'; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + const succeeded = document.execCommand('copy'); + document.body.removeChild(textarea); + return succeeded; + } catch { + return false; + } +} diff --git a/video-gen-app/src/pages/GeneratePage.tsx b/video-gen-app/src/pages/GeneratePage.tsx index 1b931e13..23d0f26a 100644 --- a/video-gen-app/src/pages/GeneratePage.tsx +++ b/video-gen-app/src/pages/GeneratePage.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; +import { copyToClipboard } from "../utils/clipboard"; import { createPortal } from "react-dom"; import { Button, @@ -2741,10 +2742,7 @@ const GeneratePage: React.FC = () => { type="text" size="small" icon={} - onClick={() => { - navigator.clipboard.writeText(editedPrompt); - message.success("已复制"); - }} + onClick={async () => { const ok = await copyToClipboard(editedPrompt); message.success(ok ? '已复制' : '复制失败'); }} style={{ color: "#94a3b8" }} /> @@ -2886,10 +2884,7 @@ const GeneratePage: React.FC = () => { type="text" size="small" icon={} - onClick={() => { - navigator.clipboard.writeText(editedPrompt); - message.success("已复制"); - }} + onClick={async () => { const ok = await copyToClipboard(editedPrompt); message.success(ok ? '已复制' : '复制失败'); }} style={{ color: "#94a3b8" }} /> @@ -3683,10 +3678,7 @@ const GeneratePage: React.FC = () => { type="text" size="small" icon={} - onClick={() => { - navigator.clipboard.writeText(prompt); - message.success("已复制"); - }} + onClick={async () => { const ok = await copyToClipboard(prompt); message.success(ok ? '已复制' : '复制失败'); }} style={{ color: "#94a3b8" }} /> diff --git a/video-gen-app/src/pages/RecordsPage.tsx b/video-gen-app/src/pages/RecordsPage.tsx index 6769ca6d..184ed42f 100644 --- a/video-gen-app/src/pages/RecordsPage.tsx +++ b/video-gen-app/src/pages/RecordsPage.tsx @@ -33,6 +33,7 @@ import { useNavigate } from 'react-router-dom'; import { useAppStore } from '../store/useAppStore'; import type { GenerationStatus, AspectRatio, Resolution } from '../types'; import { formatDate } from '../utils/formatDate'; +import { copyToClipboard } from '../utils/clipboard'; const statusConfig: Record = { optimizing: { color: 'processing', text: '优化中', icon: }, @@ -300,7 +301,7 @@ const RecordsPage: React.FC = () => {