Files
O96u ac65c1eb9d refactor: 重构设置、API、图库与存储页面前端结构。
将操作日志并入设置侧栏,补齐检查更新提示与版本接口刷新;拆分各模块组件并修复图库搜索分页变量。
2026-09-01 17:15:02 +08:00

93 lines
2.1 KiB
Vue

<script setup lang="ts">
const props = defineProps<{
label?: string
value: string
iconOnly?: boolean
icon?: string
block?: boolean
successTitle?: string
variant?: 'solid' | 'outline' | 'soft' | 'subtle' | 'ghost' | 'link'
color?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral'
}>()
const emit = defineEmits<{
copied: []
}>()
const toast = useToast()
const { t } = useI18n()
const copying = ref(false)
function copyWithFallback(text: string): boolean {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.setAttribute('readonly', '')
textarea.style.position = 'fixed'
textarea.style.left = '-9999px'
textarea.style.top = '0'
document.body.appendChild(textarea)
textarea.focus()
textarea.select()
textarea.setSelectionRange(0, text.length)
let ok = false
try {
ok = document.execCommand('copy')
} catch {
ok = false
} finally {
document.body.removeChild(textarea)
}
return ok
}
function notifySuccess() {
toast.add({
title: props.successTitle ?? (props.label ? t('copy.copiedLabel', { label: props.label }) : t('copy.copied')),
color: 'success'
})
emit('copied')
}
async function copy() {
if (copying.value || !props.value) return
copying.value = true
try {
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(props.value)
notifySuccess()
return
} catch {
// 内网 http://IP 等非安全上下文会到这里,继续走回退
}
}
if (copyWithFallback(props.value)) {
notifySuccess()
} else {
toast.add({ title: t('copy.failedManual'), color: 'error' })
}
} finally {
copying.value = false
}
}
</script>
<template>
<UButton
size="xs"
:variant="variant ?? 'soft'"
:color="color"
:loading="copying"
:disabled="!value"
:label="iconOnly ? undefined : label"
:icon="icon"
:class="block ? 'flex-1 justify-center' : undefined"
:aria-label="iconOnly ? label : undefined"
:title="iconOnly ? label : undefined"
@click="copy"
/>
</template>