diff --git a/src/@core/utils/navigator.ts b/src/@core/utils/navigator.ts index e3f3782f..f60a0841 100644 --- a/src/@core/utils/navigator.ts +++ b/src/@core/utils/navigator.ts @@ -21,6 +21,8 @@ export async function copyToClipboard(content: string) { const input = document.createElement('textarea') input.value = content document.body.appendChild(input) + // 阻止事件冒泡到其他元素,确保 focusin 事件只在 textarea 元素上处理,不会影响其他元素 + input.addEventListener('focusin', e => e.stopPropagation()) input.select() document.execCommand('copy') document.body.removeChild(input) diff --git a/src/components/cards/PluginCard.vue b/src/components/cards/PluginCard.vue index 7a1e274e..6dcf9749 100644 --- a/src/components/cards/PluginCard.vue +++ b/src/components/cards/PluginCard.vue @@ -61,6 +61,9 @@ const pluginInfoDialog = ref(false) // 进度框文本 const progressText = ref('正在更新插件...') +// 用户头像是否加载完成 +const isAvatarLoaded = ref(false) + // 插件数据页面配置项 let pluginPageItems = ref([]) @@ -215,6 +218,14 @@ const iconPath: Ref = computed(() => { return `./plugin_icon/${props.plugin?.plugin_icon}` }) +// 插件作者头像路径 +const authorPath: Ref = computed(() => { + // 网络图片则使用代理后返回 + return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent( + props.plugin?.author_url + '.png', + )}` +}) + // 重置插件 async function resetPlugin() { const isConfirmed = await createConfirm({ @@ -436,8 +447,10 @@ watch( - - + + + + {{ props.plugin?.plugin_author }} @@ -513,4 +526,17 @@ watch( content: ''; inset: 0; } + +.author-info { + display: flex; + align-items: center; +} + +.author-avatar { + border-radius: 50%; + block-size: 24px; + inline-size: 24px; + margin-inline-end: 8px; + object-fit: cover; +} diff --git a/src/pages/login.vue b/src/pages/login.vue index f22a74f7..2b2985ab 100644 --- a/src/pages/login.vue +++ b/src/pages/login.vue @@ -31,13 +31,9 @@ const isPasswordVisible = ref(false) // 错误信息 const errorMessage = ref('') -// 背景图片 +// 背景图片 URL 和预加载 URL const backgroundImageUrl = ref('') - -// 所有的背景图片 const backgroundImages = ref([]) - -// 背景图片加载状态 const isImageLoaded = ref(false) // 是否开启双重验证 @@ -54,7 +50,6 @@ async function fetchBackgroundImage() { try { backgroundImages.value = await api.get('/login/wallpapers') if (backgroundImages.value && backgroundImages.value.length > 0) { - // 随机打乱排序 backgroundImages.value.sort(() => Math.random() - 0.5) backgroundImageUrl.value = backgroundImages.value[0] } @@ -63,6 +58,34 @@ async function fetchBackgroundImage() { } } +// 切换背景图片函数 +function startBackgroundImageRotation() { + let currentIndex = 0 + + intervalTimer = setInterval(() => { + if (backgroundImages.value.length > 1) { + // 更新下一张图片索引 + const nextIndex = (currentIndex + 1) % backgroundImages.value.length + const nextImageUrl = backgroundImages.value[nextIndex] + // 使用预加载机制确保下一张图片已加载完成 + const img = new Image() + img.src = nextImageUrl + img.onload = () => { + // 开始淡入过渡 + isImageLoaded.value = false + // 延迟一小段时间触发淡入效果 + setTimeout(() => { + // 更新当前索引并切换背景图片 URL + currentIndex = nextIndex + backgroundImageUrl.value = nextImageUrl + // 切换完成后显示图片 + isImageLoaded.value = true + }, 1000) + } + } + }, 5000) +} + // 查询是否开启双重验证 const fetchOTP = debounce(async () => { const userid = usernameInput.value?.value @@ -205,14 +228,8 @@ onMounted(async () => { } else { // 获取背景图片 await fetchBackgroundImage() - - // 每隔5秒更换一次背景图片 - intervalTimer = setInterval(() => { - if (backgroundImages.value.length > 0) { - const index = Math.floor(Math.random() * backgroundImages.value.length) - backgroundImageUrl.value = backgroundImages.value[index] - } - }, 5000) + // 开始背景图片定时切换 + startBackgroundImageRotation() } }) @@ -222,19 +239,21 @@ onUnmounted(() => {