mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 16:41:52 +08:00
feat(web): 添加剪贴板复制兼容 HTTP 环境
- 新增 useClipboard composable,支持 execCommand 备用方案 - 修复 HTTP 环境下复制按钮无响应问题
This commit is contained in:
65
web/src/composables/useClipboard.ts
Normal file
65
web/src/composables/useClipboard.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function useClipboard() {
|
||||
const copied = ref(false)
|
||||
|
||||
// 检测是否支持原生 Clipboard API (需要安全上下文 + API 存在)
|
||||
function canUseClipboardApi(): boolean {
|
||||
return !!(
|
||||
typeof navigator !== 'undefined' &&
|
||||
navigator.clipboard &&
|
||||
typeof navigator.clipboard.writeText === 'function' &&
|
||||
window.isSecureContext
|
||||
)
|
||||
}
|
||||
|
||||
// Fallback: 使用 execCommand (兼容 HTTP 环境)
|
||||
function fallbackCopy(text: string): boolean {
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = text
|
||||
textarea.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none'
|
||||
document.body.appendChild(textarea)
|
||||
textarea.focus()
|
||||
textarea.select()
|
||||
|
||||
let success = false
|
||||
try {
|
||||
success = document.execCommand('copy')
|
||||
} catch {
|
||||
success = false
|
||||
}
|
||||
|
||||
document.body.removeChild(textarea)
|
||||
return success
|
||||
}
|
||||
|
||||
async function copy(text: string): Promise<boolean> {
|
||||
if (!text) return false
|
||||
|
||||
try {
|
||||
if (canUseClipboardApi()) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
} else {
|
||||
if (!fallbackCopy(text)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
copied.value = true
|
||||
setTimeout(() => (copied.value = false), 2000)
|
||||
return true
|
||||
} catch (e) {
|
||||
// Clipboard API 失败时尝试 fallback
|
||||
console.warn('Clipboard API failed, trying fallback:', e)
|
||||
if (fallbackCopy(text)) {
|
||||
copied.value = true
|
||||
setTimeout(() => (copied.value = false), 2000)
|
||||
return true
|
||||
}
|
||||
console.error('Copy failed:', e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return { copy, copied }
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import type {
|
||||
AtxDevices,
|
||||
} from '@/types/generated'
|
||||
import { setLanguage } from '@/i18n'
|
||||
import { useClipboard } from '@/composables/useClipboard'
|
||||
import AppLayout from '@/components/AppLayout.vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
@@ -173,6 +174,7 @@ const rustdeskStatus = ref<RustDeskStatusResponse | null>(null)
|
||||
const rustdeskPassword = ref<RustDeskPasswordResponse | null>(null)
|
||||
const rustdeskLoading = ref(false)
|
||||
const rustdeskCopied = ref<'id' | 'password' | null>(null)
|
||||
const { copy: clipboardCopy } = useClipboard()
|
||||
const rustdeskLocalConfig = ref({
|
||||
enabled: false,
|
||||
rendezvous_server: '',
|
||||
@@ -860,11 +862,12 @@ async function regenerateRustdeskPassword() {
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text: string, type: 'id' | 'password') {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
async function copyToClipboard(text: string, type: 'id' | 'password') {
|
||||
const success = await clipboardCopy(text)
|
||||
if (success) {
|
||||
rustdeskCopied.value = type
|
||||
setTimeout(() => (rustdeskCopied.value = null), 2000)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getRustdeskServiceStatusText(status: string | undefined): string {
|
||||
|
||||
Reference in New Issue
Block a user