mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-10 23:13:20 +08:00
Refactor background image rotation in login.vue
This commit is contained in:
@@ -32,9 +32,8 @@ const isPasswordVisible = ref(false)
|
|||||||
const errorMessage = ref('')
|
const errorMessage = ref('')
|
||||||
|
|
||||||
// 背景图片 URL 和预加载 URL
|
// 背景图片 URL 和预加载 URL
|
||||||
const backgroundImageUrl = ref('')
|
|
||||||
const backgroundImages = ref<string[]>([])
|
const backgroundImages = ref<string[]>([])
|
||||||
const isImageLoaded = ref(false)
|
const activeImageIndex = ref(0)
|
||||||
|
|
||||||
// 是否开启双重验证
|
// 是否开启双重验证
|
||||||
const isOTP = ref(false)
|
const isOTP = ref(false)
|
||||||
@@ -49,43 +48,11 @@ let intervalTimer: NodeJS.Timeout | null = null
|
|||||||
async function fetchBackgroundImage() {
|
async function fetchBackgroundImage() {
|
||||||
try {
|
try {
|
||||||
backgroundImages.value = await api.get('/login/wallpapers')
|
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]
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换背景图片函数
|
|
||||||
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 fetchOTP = debounce(async () => {
|
||||||
const userid = usernameInput.value?.value
|
const userid = usernameInput.value?.value
|
||||||
@@ -216,6 +183,13 @@ function login() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化背景图片轮循
|
||||||
|
function startBackgroundRotation() {
|
||||||
|
intervalTimer = setInterval(() => {
|
||||||
|
activeImageIndex.value = (activeImageIndex.value + 1) % backgroundImages.value.length
|
||||||
|
}, 5000) // 每5秒切换一次图片
|
||||||
|
}
|
||||||
|
|
||||||
// 自动登录
|
// 自动登录
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 从Vuex Store中获取token和remember状态
|
// 从Vuex Store中获取token和remember状态
|
||||||
@@ -228,8 +202,9 @@ onMounted(async () => {
|
|||||||
} else {
|
} else {
|
||||||
// 获取背景图片
|
// 获取背景图片
|
||||||
await fetchBackgroundImage()
|
await fetchBackgroundImage()
|
||||||
// 开始背景图片定时切换
|
if (backgroundImages.value.length > 1) {
|
||||||
startBackgroundImageRotation()
|
startBackgroundRotation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -240,79 +215,75 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 当前背景图片 -->
|
<!-- 当前背景图片 -->
|
||||||
<div class="absolute inset-0 w-full h-full overflow-hidden">
|
<div class="relative flex min-h-screen flex-col bg-gray-900 py-14">
|
||||||
<VImg
|
<div>
|
||||||
:src="backgroundImageUrl"
|
<div
|
||||||
:class="{ 'opacity-0': !isImageLoaded, 'opacity-100': isImageLoaded }"
|
v-for="(imageUrl, index) in backgroundImages"
|
||||||
class="absolute inset-0 transition-opacity duration-1000"
|
class="absolute-top-shift absolute inset-0 bg-cover bg-center transition-opacity duration-300 ease-in"
|
||||||
cover
|
:class="{ 'opacity-100': index === activeImageIndex, 'opacity-0': index !== activeImageIndex }"
|
||||||
position="center top"
|
>
|
||||||
/>
|
<VImg :src="imageUrl" class="absolute inset-0 transition-opacity duration-1000" cover position="center top" />
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0"
|
class="absolute inset-0"
|
||||||
style="background-image: linear-gradient(rgba(45, 55, 72, 33%) 0%, rgb(26, 32, 46) 100%)"
|
style="background-image: linear-gradient(rgba(45, 55, 72, 47%) 0%, rgb(26, 32, 46) 100%)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="auth-wrapper d-flex align-center justify-center pa-4">
|
<!-- 登录表单 -->
|
||||||
<VCard
|
<div class="auth-wrapper d-flex align-center justify-center pa-4">
|
||||||
class="auth-card px-7 py-3 w-full h-full rounded-lg"
|
<VCard class="auth-card px-7 py-3 w-full h-full rounded-lg opacity-85" max-width="24rem">
|
||||||
:class="{ 'opacity-85': isImageLoaded }"
|
<VCardItem class="justify-center">
|
||||||
max-width="24rem"
|
<template #prepend>
|
||||||
>
|
<div class="d-flex pe-0">
|
||||||
<VCardItem class="justify-center">
|
<VImg :src="logo" width="64" height="64" />
|
||||||
<template #prepend>
|
</div>
|
||||||
<div class="d-flex pe-0">
|
</template>
|
||||||
<VImg :src="logo" width="64" height="64" />
|
<VCardTitle class="font-weight-bold text-2xl text-uppercase"> MoviePilot </VCardTitle>
|
||||||
</div>
|
</VCardItem>
|
||||||
</template>
|
<VCardText>
|
||||||
|
<VForm ref="refForm" @submit.prevent="() => {}">
|
||||||
<VCardTitle class="font-weight-semibold text-2xl text-uppercase"> MoviePilot </VCardTitle>
|
<VRow>
|
||||||
</VCardItem>
|
<!-- username -->
|
||||||
|
<VCol cols="12">
|
||||||
<VCardText>
|
<VTextField
|
||||||
<VForm ref="refForm" @submit.prevent="() => {}">
|
ref="usernameInput"
|
||||||
<VRow>
|
v-model="form.username"
|
||||||
<!-- username -->
|
label="用户名"
|
||||||
<VCol cols="12">
|
type="text"
|
||||||
<VTextField
|
:rules="[requiredValidator]"
|
||||||
ref="usernameInput"
|
@input="fetchOTP"
|
||||||
v-model="form.username"
|
/>
|
||||||
label="用户名"
|
</VCol>
|
||||||
type="text"
|
<!-- password -->
|
||||||
:rules="[requiredValidator]"
|
<VCol cols="12">
|
||||||
@input="fetchOTP"
|
<VTextField
|
||||||
/>
|
v-model="form.password"
|
||||||
</VCol>
|
label="密码"
|
||||||
<!-- password -->
|
:type="isPasswordVisible ? 'text' : 'password'"
|
||||||
<VCol cols="12">
|
:append-inner-icon="isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||||
<VTextField
|
:rules="[requiredValidator]"
|
||||||
v-model="form.password"
|
@click:append-inner="isPasswordVisible = !isPasswordVisible"
|
||||||
label="密码"
|
/>
|
||||||
:type="isPasswordVisible ? 'text' : 'password'"
|
</VCol>
|
||||||
:append-inner-icon="isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
<VCol cols="12">
|
||||||
:rules="[requiredValidator]"
|
<VTextField v-if="isOTP" v-model="form.otp_password" label="双重验证码" type="input" />
|
||||||
@click:append-inner="isPasswordVisible = !isPasswordVisible"
|
<!-- remember me checkbox -->
|
||||||
/>
|
<div class="d-flex align-center justify-space-between flex-wrap">
|
||||||
</VCol>
|
<VCheckbox v-model="form.remember" label="保持登录" required />
|
||||||
<VCol cols="12">
|
</div>
|
||||||
<VTextField v-if="isOTP" v-model="form.otp_password" label="双重验证码" type="input" />
|
</VCol>
|
||||||
<!-- remember me checkbox -->
|
<VCol cols="12">
|
||||||
<div class="d-flex align-center justify-space-between flex-wrap">
|
<!-- login button -->
|
||||||
<VCheckbox v-model="form.remember" label="保持登录" required />
|
<VBtn block type="submit" @click="login"> 登录 </VBtn>
|
||||||
</div>
|
<div v-if="errorMessage" class="text-error mt-2 text-shadow">
|
||||||
</VCol>
|
{{ errorMessage }}
|
||||||
<VCol cols="12">
|
</div>
|
||||||
<!-- login button -->
|
</VCol>
|
||||||
<VBtn block type="submit" @click="login"> 登录 </VBtn>
|
</VRow>
|
||||||
<div v-if="errorMessage" class="text-error mt-2 text-shadow">
|
</VForm>
|
||||||
{{ errorMessage }}
|
</VCardText>
|
||||||
</div>
|
</VCard>
|
||||||
</VCol>
|
</div>
|
||||||
</VRow>
|
|
||||||
</VForm>
|
|
||||||
</VCardText>
|
|
||||||
</VCard>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -322,4 +293,8 @@ onUnmounted(() => {
|
|||||||
.v-card-item__prepend {
|
.v-card-item__prepend {
|
||||||
padding-inline-end: 0 !important;
|
padding-inline-end: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.absolute-top-shift {
|
||||||
|
inset-block-start: calc(-4rem - env(safe-area-inset-top));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user