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,
|
AtxDevices,
|
||||||
} from '@/types/generated'
|
} from '@/types/generated'
|
||||||
import { setLanguage } from '@/i18n'
|
import { setLanguage } from '@/i18n'
|
||||||
|
import { useClipboard } from '@/composables/useClipboard'
|
||||||
import AppLayout from '@/components/AppLayout.vue'
|
import AppLayout from '@/components/AppLayout.vue'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
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 rustdeskPassword = ref<RustDeskPasswordResponse | null>(null)
|
||||||
const rustdeskLoading = ref(false)
|
const rustdeskLoading = ref(false)
|
||||||
const rustdeskCopied = ref<'id' | 'password' | null>(null)
|
const rustdeskCopied = ref<'id' | 'password' | null>(null)
|
||||||
|
const { copy: clipboardCopy } = useClipboard()
|
||||||
const rustdeskLocalConfig = ref({
|
const rustdeskLocalConfig = ref({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
rendezvous_server: '',
|
rendezvous_server: '',
|
||||||
@@ -860,11 +862,12 @@ async function regenerateRustdeskPassword() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyToClipboard(text: string, type: 'id' | 'password') {
|
async function copyToClipboard(text: string, type: 'id' | 'password') {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
const success = await clipboardCopy(text)
|
||||||
|
if (success) {
|
||||||
rustdeskCopied.value = type
|
rustdeskCopied.value = type
|
||||||
setTimeout(() => (rustdeskCopied.value = null), 2000)
|
setTimeout(() => (rustdeskCopied.value = null), 2000)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRustdeskServiceStatusText(status: string | undefined): string {
|
function getRustdeskServiceStatusText(status: string | undefined): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user