fix(admin): copy buttons dead on HTTP origin — execCommand fallback

navigator.clipboard 仅安全上下文(HTTPS/localhost)可用,测试服务器经
http://IP:32005 访问时为 undefined,四处复制按钮(公开令牌/分享链接/
嵌入代码/重置密码)点击即抛 TypeError 且无提示。统一 utils/clipboard
copyText:API 优先、失败降级隐藏 textarea + execCommand。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-23 17:01:54 +08:00
parent cd8a3696e2
commit aaea76b0e3
6 changed files with 103 additions and 8 deletions
@@ -53,6 +53,7 @@
viewerBaseFromEnv,
type QrDraw
} from '@/utils/albums/shareLink'
import { copyText } from '@/utils/clipboard'
const props = defineProps<{
album: Api.Album.Detail
@@ -104,8 +105,11 @@
)
async function copyLink(): Promise<void> {
await navigator.clipboard.writeText(viewerUrl.value)
ElMessage.success(t('albumsManage.list.copied'))
if (await copyText(viewerUrl.value)) {
ElMessage.success(t('albumsManage.list.copied'))
} else {
ElMessage.error(t('setting.actions.copyFailed'))
}
}
</script>
@@ -61,6 +61,7 @@
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import { buildEmbedSnippet, embedOriginFromEnv } from '@/utils/system/embedSlots'
import { copyText } from '@/utils/clipboard'
const props = defineProps<{
/** 目标展示位(publicToken 拼入 data-slot;settings 生成显式 data-* 覆盖) */
@@ -81,7 +82,10 @@
}
async function copySnippet(): Promise<void> {
await navigator.clipboard.writeText(snippet.value)
ElMessage.success(t('embedSlotsManage.code.copied'))
if (await copyText(snippet.value)) {
ElMessage.success(t('embedSlotsManage.code.copied'))
} else {
ElMessage.error(t('setting.actions.copyFailed'))
}
}
</script>
@@ -91,6 +91,7 @@
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import { useResetPassword } from './composables/useResetPassword'
import { copyText } from '@/utils/clipboard'
import type { ResetPasswordMode } from '@/utils/system/userAdmin'
const props = defineProps<{
@@ -129,7 +130,10 @@
}
async function copyPassword(): Promise<void> {
await navigator.clipboard.writeText(flow.password.value)
ElMessage.success(t('usersManage.reset.copied'))
if (await copyText(flow.password.value)) {
ElMessage.success(t('usersManage.reset.copied'))
} else {
ElMessage.error(t('setting.actions.copyFailed'))
}
}
</script>