65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
import { h } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
|
||
/**
|
||
* 将文本写入剪贴板。优先 Clipboard API,HTTP 等非安全上下文回退 execCommand。
|
||
*/
|
||
export async function copyTextToClipboard(text: string): Promise<boolean> {
|
||
if (navigator.clipboard?.writeText) {
|
||
try {
|
||
await navigator.clipboard.writeText(text)
|
||
return true
|
||
} catch {
|
||
// 权限拒绝或异步后丢失用户手势时,尝试 legacy 方式
|
||
}
|
||
}
|
||
|
||
try {
|
||
const textarea = document.createElement('textarea')
|
||
textarea.value = text
|
||
textarea.setAttribute('readonly', '')
|
||
textarea.style.cssText = 'position:fixed;left:-9999px;top:0;opacity:0'
|
||
document.body.appendChild(textarea)
|
||
textarea.focus()
|
||
textarea.select()
|
||
const ok = document.execCommand('copy')
|
||
document.body.removeChild(textarea)
|
||
return ok
|
||
} catch {
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 复制文本;失败时弹窗展示内容供手动复制(适用于 HTTP 测试环境等受限场景)。
|
||
*/
|
||
export async function copyTextWithFallback(
|
||
text: string,
|
||
options?: { successMessage?: string; fallbackTitle?: string }
|
||
): Promise<void> {
|
||
const ok = await copyTextToClipboard(text)
|
||
if (ok) {
|
||
ElMessage.success(options?.successMessage ?? '已复制到剪贴板')
|
||
return
|
||
}
|
||
|
||
await ElMessageBox.alert(
|
||
h('textarea', {
|
||
value: text,
|
||
readonly: true,
|
||
style:
|
||
'width:100%;min-height:88px;padding:8px 12px;border:1px solid var(--el-border-color);border-radius:8px;font-size:14px;line-height:1.5;resize:none;',
|
||
onVnodeMounted(vnode) {
|
||
const el = vnode.el as HTMLTextAreaElement | null
|
||
el?.focus()
|
||
el?.select()
|
||
}
|
||
}),
|
||
options?.fallbackTitle ?? '请手动复制短信内容',
|
||
{
|
||
confirmButtonText: '关闭',
|
||
customClass: 'copy-text-fallback-dialog'
|
||
}
|
||
)
|
||
}
|