File size: 1,613 Bytes
8059bf0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | import { ref } from 'vue'
import { useAppStore } from '@/stores/app'
import { i18n } from '@/i18n'
const { t } = i18n.global
/**
* 检测是否支持 Clipboard API(需要安全上下文:HTTPS/localhost)
*/
function isClipboardSupported(): boolean {
return !!(navigator.clipboard && window.isSecureContext)
}
/**
* 降级方案:使用 textarea + execCommand
* 使用 textarea 而非 input,以正确处理多行文本
*/
function fallbackCopy(text: string): boolean {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.cssText = 'position:fixed;left:-9999px;top:-9999px'
document.body.appendChild(textarea)
textarea.select()
try {
return document.execCommand('copy')
} finally {
document.body.removeChild(textarea)
}
}
export function useClipboard() {
const appStore = useAppStore()
const copied = ref(false)
const copyToClipboard = async (
text: string,
successMessage?: string
): Promise<boolean> => {
if (!text) return false
let success = false
if (isClipboardSupported()) {
try {
await navigator.clipboard.writeText(text)
success = true
} catch {
success = fallbackCopy(text)
}
} else {
success = fallbackCopy(text)
}
if (success) {
copied.value = true
appStore.showSuccess(successMessage || t('common.copiedToClipboard'))
setTimeout(() => {
copied.value = false
}, 2000)
} else {
appStore.showError(t('common.copyFailed'))
}
return success
}
return { copied, copyToClipboard }
}
|