vue3 项目,想要使用 js 实现点击触发跳转时,复制口令到剪贴板,注意这里,仅仅是复制到剪贴板。
注意事项:
click、touchstart)事件的同步代码中调用,如果在 setTimeout、promise、事件监听等 内或者后面都会导致执行失败/**
* 复制文本到剪贴板
* 注意:必须在用户手势(如 click、touchstart)的同步调用链中执行,不能在异步回调中调用!
* @param {string} text - 要复制的文本
* @returns {Promise<boolean>} - 是否复制成功
*/
export function copyToClipboard(text) {
return new Promise((resolve) => {
// 优先使用现代 Clipboard API
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
navigator.clipboard.writeText(text)
.then(() => {
console.log('[copyToClipboard] Clipboard API 复制成功')
resolve(true)
})
.catch((err) => {
console.warn('[copyToClipboard] Clipboard API 失败:', err.message)
const success = fallbackCopy(text)
console.log('[copyToClipboard] 降级方案结果:', success)
resolve(success)
})
} else {
console.log('[copyToClipboard] Clipboard API 不可用,使用降级方案')
const success = fallbackCopy(text)
console.log('[copyToClipboard] 降级方案结果:', success)
resolve(success)
}
})
}
/**
* 降级复制方案(使用 textarea + execCommand,增强 iOS 兼容性)
* @param {string} text - 要复制的文本
* @returns {boolean} - 是否复制成功
*/
function fallbackCopy(text) {
const textarea = document.createElement('textarea')
textarea.value = text
// 关键:设置 contentEditable 为 true,提高 iOS 选中兼容性
textarea.contentEditable = 'true'
textarea.style.cssText = `
position: fixed;
top: 0;
left: 0;
opacity: 0;
pointer-events: none;
width: 100px;
height: 100px;
z-index: -1;
`
document.body.appendChild(textarea)
// iOS 兼容性处理
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent)
let success = false
try {
// 聚焦元素(iOS 需要)
textarea.focus()
if (isIOS) {
const range = document.createRange()
range.selectNodeContents(textarea)
const selection = window.getSelection()
selection.removeAllRanges()
selection.addRange(range)
textarea.setSelectionRange(0, text.length)
} else {
textarea.select()
textarea.setSelectionRange(0, text.length)
}
success = document.execCommand('copy')
console.log('[fallbackCopy] execCommand 复制结果:', success)
} catch (err) {
console.error('[fallbackCopy] execCommand 异常:', err)
success = false
} finally {
document.body.removeChild(textarea)
}
return success
}
/**
* 通过 scheme 打开 APP
* @param {string} scheme - 完整的 scheme URL
* @returns {void}
*/
export function openAppByScheme(scheme) {
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent)
if (isIOS) {
// iOS 使用 iframe 方式,避免页面跳转
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = scheme
document.body.appendChild(iframe)
setTimeout(() => {
document.body.removeChild(iframe)
}, 100)
} else {
// Android 直接跳转
window.location.href = scheme
}
}
/**
* 复制口令并打开 APP
* @param {string} jumpCommand - 口令内容
* @param {string} schemePrefix - scheme 前缀,默认为抖音极速版
* @param {object} options - 配置选项
* @param {number} options.delay - 复制后延迟打开的时间(ms),默认 300ms
* @returns {Promise<void>}
*/
export async function copyAndOpenApp(jumpCommand, schemePrefix = DEFAULT_SCHEME_PREFIX, options = {}) {
const { delay = 300 } = options
if (!jumpCommand) {
console.warn('[copyAndOpenApp] jumpCommand 为空,跳过复制')
return
}
// 复制口令
const copied = await copyToClipboard(jumpCommand)
if (!copied) {
console.warn('[copyAndOpenApp] 复制失败,但仍尝试打开 APP')
}
// 拼接 scheme 并打开 APP
const scheme = `${schemePrefix}search?keyword=${encodeURIComponent(jumpCommand)}`
// 延迟打开,确保复制操作完成
setTimeout(() => {
openAppByScheme(scheme)
}, delay)
}
/**
* 复制口令到剪贴板
* @param {string} jumpCommand - 口令内容
*/
export async function copyCommand(jumpCommand) {
if (!jumpCommand) {
console.warn('[copyCommand] jumpCommand 为空,跳过复制')
return
}
// 复制口令
const copied = await copyToClipboard(jumpCommand)
if (!copied) {
console.warn('[copyCommand] 复制失败,但仍尝试打开 APP')
}
}以上方法,如果仅复制口令,则使用 copyCommand,如果还要实现复制口令并打开,则使用 copyAndOpenApp。
常见报错: