<template> <div class="container"> <div clas...

Created on: May 25, 2026

Answered using GPT-5.5 Thinking by Chat01

Question

<template> <div class="container"> <div class="login-box"> <div class="login-title"> <div> <span>有本通讯录</span> </div> <div> <span>系统登录</span> </div> </div> <div class="login-content"> <span>用户名:</span> <el-input type="text" class="login-input" v-model="user_name" @keydown.enter="passwordFocus" placeholder="请输入用户名"></el-input> </div> <div class="login-content"> <span>密码:</span> <el-input type="password" class="login-input" id='password' v-model="password" @keydown.enter="performLogin" placeholder="请输入密码" show-password></el-input> </div>
text
<div class="login-button"> <el-button type="primary" @click="performLogin">登录</el-button> <el-button type="warning" @click="registerVisible=true">注册</el-button> </div> </div> <el-dialog v-model="registerVisible" title="用户注册" width="400px" :close-on-click-modal="false" @close="resetRegisterForm"> <el-form :model="registerForm" label-width="80px"> <el-form-item label="用户名"> <el-input v-model="registerForm.user_name" placeholder="请输入用户名"></el-input> </el-form-item> <el-form-item label="密码"> <el-input v-model="registerForm.password" placeholder="请输入密码" type="password" show-password></el-input> </el-form-item> <el-form-item label="确认密码"> <el-input v-model="registerForm.confirmPassword" placeholder="请再次输入密码" type="password" show-password></el-input> </el-form-item> <el-form-item label="姓名"> <el-input v-model="registerForm.name" placeholder="请输入姓名"></el-input> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button type="warning" @click="performRegister">注册</el-button> <el-button @click="registerVisible=false">取消</el-button> </span> </template> </el-dialog>
</div> </template> <script setup> import {reactive, ref} from 'vue' import {ElMessage} from "element-plus" import axios from 'axios' import {logininfoStore} from "../store/logininfo.js"; import {useRouter} from "vue-router"; import {PartlyCloudy} from "@element-plus/icons-vue"; const logininfo=logininfoStore() const router = useRouter(); const user_name=ref('') const password=ref('') function passwordFocus(){ document.getElementById('password').focus(); } function performLogin(){ const url='http://abtest.wyk.plus/api/UserLogin?user_name='+user_name.value+'&password='+password.value axios.post(url).then(response=>{ console.log(response.data) if(response.data.code===0){ logininfo.set(JSON.parse(response.data.data)); console.log(JSON.parse(response.data.data)); ElMessage.success('登录成功') router.push('/home'); }else{ ElMessage.error(response.data.message||'登录失败,请检查用户名和密码!') } }).catch(error=>{ ElMessage.error('登录请求失败,请稍后重试!') }) } const registerVisible=ref(false); const registerForm=reactive({ user_name:'', password:'', confirmPassword:'', name:'' }) function resetRegisterForm(){ registerForm.user_name='' registerForm.password='' registerForm.confirmPassword='' registerForm.name='' } function performRegister(){ if(!registerForm.user_name||!registerForm.password||!registerForm.name){ ElMessage.warning('请填写完整信息!') return } if(registerForm.password!==registerForm.confirmPassword){ ElMessage.warning('两次输入的密码不一致!') return } const url='http://abtest.wyk.plus/api/Register'; const params={ user_name:registerForm.user_name, password:registerForm.password, name:registerForm.name, } axios.post(url,params).then(response=>{ if(response.data.code===0){ ElMessage.success('注册成功,请登录') registerVisible.value = false }else{ ElMessage.error(response.data.message||'注册失败!'); } }).catch(error=>{ ElMessage.error('注册请求失败,请稍后尝试!') }) } </script> <style scoped> .container { width: 100%; height: 100vh; position: fixed; background-color: #215bb3; } .login-box{ background-color: #ffffffbd; border: 1px solid rgb(88, 148, 238); border-radius: 5px; width: 600px; height: 280px; left: 50%; top: 50%; transform: translate(-50%,-50%); position: fixed; } .login-title{ display: block; text-align: center; color: #aaaaed; font-size: 24px; margin-top: 20px; } .login-content{ margin-top: 20px; } .login-content span{ display: inline-block; width: 40%; text-align: right; margin-right: 10px; //background-color:greenyellow; } .login-input{ display: inline-block; width: 200px; max-width: 200px; //background: #c52626; } .login-input :deep(.el-input__wrapper){ box-shadow: none; height: 35px; } .login-button{ margin-top: 20px; text-align: center; } button{ margin-left: 20px; } </style>

请提出几个美化方案,必须具有3d动态效果,然后给出每个方案的完整的代码

Thought

我会给你 3 个可直接替换的 Vue 单文件组件方案:都保留登录/注册逻辑,并分别加入不同风格的 3D 动态背景、卡片悬浮和按钮动效。

Thought for 14s

Answer

下面给你 3 个可直接替换的完整 .vue 单文件组件方案。三套都包含 3D 动态背景、登录卡片悬浮、按钮动态效果、注册弹窗美化,并保留你原来的登录/注册逻辑。


方案一:星空玻璃拟态 3D 登录页

特点:深蓝星空、玻璃卡片、3D 行星环绕、卡片悬浮旋转。

vue
<template> <div class="container"> <div class="star-layer"></div> <div class="star-layer star-layer-2"></div> <div class="planet planet-one"> <span></span> </div> <div class="planet planet-two"> <span></span> </div> <div class="login-scene"> <div class="login-box"> <div class="login-title"> <div class="brand">有本通讯录</div> <div class="subtitle">系统登录</div> </div> <div class="login-content"> <span>用户名:</span> <el-input type="text" class="login-input" v-model="user_name" @keydown.enter="passwordFocus" placeholder="请输入用户名" /> </div> <div class="login-content"> <span>密码:</span> <el-input ref="passwordInput" type="password" class="login-input" v-model="password" @keydown.enter="performLogin" placeholder="请输入密码" show-password /> </div> <div class="login-button"> <el-button type="primary" class="main-btn" @click="performLogin">登录</el-button> <el-button type="warning" class="sub-btn" @click="registerVisible = true">注册</el-button> </div> </div> </div> <el-dialog v-model="registerVisible" title="用户注册" width="400px" :close-on-click-modal="false" class="register-dialog" @close="resetRegisterForm" > <el-form :model="registerForm" label-width="80px"> <el-form-item label="用户名"> <el-input v-model="registerForm.user_name" placeholder="请输入用户名" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="registerForm.password" placeholder="请输入密码" type="password" show-password /> </el-form-item> <el-form-item label="确认密码"> <el-input v-model="registerForm.confirmPassword" placeholder="请再次输入密码" type="password" show-password /> </el-form-item> <el-form-item label="姓名"> <el-input v-model="registerForm.name" placeholder="请输入姓名" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button type="warning" @click="performRegister">注册</el-button> <el-button @click="registerVisible = false">取消</el-button> </span> </template> </el-dialog> </div> </template> <script setup> import { reactive, ref } from 'vue' import { ElMessage } from 'element-plus' import axios from 'axios' import { logininfoStore } from '../store/logininfo.js' import { useRouter } from 'vue-router' const logininfo = logininfoStore() const router = useRouter() const user_name = ref('') const password = ref('') const passwordInput = ref(null) function passwordFocus() { passwordInput.value?.focus?.() } function parseUserData(data) { if (typeof data === 'string') { return JSON.parse(data) } return data } function performLogin() { axios .post('http://abtest.wyk.plus/api/UserLogin', null, { params: { user_name: user_name.value, password: password.value, }, }) .then((response) => { if (response.data.code === 0) { const userData = parseUserData(response.data.data) logininfo.set(userData) ElMessage.success('登录成功') router.push('/home') } else { ElMessage.error(response.data.message || '登录失败,请检查用户名和密码!') } }) .catch(() => { ElMessage.error('登录请求失败,请稍后重试!') }) } const registerVisible = ref(false) const registerForm = reactive({ user_name: '', password: '', confirmPassword: '', name: '', }) function resetRegisterForm() { registerForm.user_name = '' registerForm.password = '' registerForm.confirmPassword = '' registerForm.name = '' } function performRegister() { if (!registerForm.user_name || !registerForm.password || !registerForm.name) { ElMessage.warning('请填写完整信息!') return } if (registerForm.password !== registerForm.confirmPassword) { ElMessage.warning('两次输入的密码不一致!') return } axios .post('http://abtest.wyk.plus/api/Register', { user_name: registerForm.user_name, password: registerForm.password, name: registerForm.name, }) .then((response) => { if (response.data.code === 0) { ElMessage.success('注册成功,请登录') registerVisible.value = false } else { ElMessage.error(response.data.message || '注册失败!') } }) .catch(() => { ElMessage.error('注册请求失败,请稍后尝试!') }) } </script> <style scoped> .container { width: 100%; height: 100vh; position: fixed; overflow: hidden; background: radial-gradient(circle at 20% 20%, rgba(100, 180, 255, 0.45), transparent 30%), radial-gradient(circle at 80% 80%, rgba(174, 93, 255, 0.35), transparent 35%), linear-gradient(135deg, #06142e, #10194a 50%, #050816); perspective: 1200px; } .star-layer, .star-layer-2 { position: absolute; inset: 0; background-image: radial-gradient(circle, rgba(255, 255, 255, 0.9) 1px, transparent 1px), radial-gradient(circle, rgba(135, 206, 250, 0.8) 1px, transparent 1px); background-size: 80px 80px, 140px 140px; animation: starMove 22s linear infinite; } .star-layer-2 { opacity: 0.45; animation-duration: 38s; transform: translateZ(-300px) scale(1.6); } @keyframes starMove { from { background-position: 0 0, 20px 20px; } to { background-position: 800px 600px, -500px 300px; } } .planet { position: absolute; width: 170px; height: 170px; border-radius: 50%; transform-style: preserve-3d; animation: floatPlanet 8s ease-in-out infinite; } .planet span { position: absolute; inset: 20px; border-radius: 50%; background: linear-gradient(135deg, #7dd3fc, #8b5cf6); box-shadow: 0 0 40px rgba(125, 211, 252, 0.6); } .planet::before { content: ""; position: absolute; left: -18px; top: 70px; width: 210px; height: 34px; border: 3px solid rgba(255, 255, 255, 0.38); border-radius: 50%; transform: rotateX(70deg) rotateZ(-18deg); } .planet-one { left: 9%; top: 13%; } .planet-two { right: 10%; bottom: 9%; transform: scale(0.75); animation-delay: -3s; } .planet-two span { background: linear-gradient(135deg, #fbbf24, #fb7185); } @keyframes floatPlanet { 0%, 100% { transform: translateY(0) rotateY(0deg); } 50% { transform: translateY(-28px) rotateY(24deg); } } .login-scene { width: 100%; height: 100%; display: grid; place-items: center; transform-style: preserve-3d; } .login-box { width: 620px; min-height: 330px; padding: 34px 40px; box-sizing: border-box; position: relative; border-radius: 28px; border: 1px solid rgba(255, 255, 255, 0.34); background: linear-gradient(135deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0.08)); backdrop-filter: blur(22px); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.35), inset 0 0 40px rgba(255, 255, 255, 0.08); transform-style: preserve-3d; animation: cardFloat 5.5s ease-in-out infinite; } .login-box::before { content: ""; position: absolute; inset: 0; border-radius: 28px; background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.36), transparent); transform: translateX(-120%); animation: lightSweep 4.5s ease-in-out infinite; } .login-box:hover { transform: rotateX(7deg) rotateY(-8deg) translateZ(35px); } @keyframes cardFloat { 0%, 100% { transform: translateY(0) rotateX(0deg) rotateY(0deg); } 50% { transform: translateY(-18px) rotateX(4deg) rotateY(-4deg); } } @keyframes lightSweep { 0% { transform: translateX(-130%); } 45%, 100% { transform: translateX(130%); } } .login-title { position: relative; z-index: 1; text-align: center; margin-bottom: 30px; color: #fff; } .brand { font-size: 34px; font-weight: 800; letter-spacing: 6px; text-shadow: 0 0 22px rgba(125, 211, 252, 0.85); } .subtitle { margin-top: 8px; font-size: 16px; letter-spacing: 8px; color: rgba(255, 255, 255, 0.72); } .login-content { position: relative; z-index: 1; margin-top: 20px; display: flex; align-items: center; justify-content: center; } .login-content span { width: 90px; color: #eef7ff; text-align: right; margin-right: 12px; font-weight: 600; } .login-input { width: 260px; } .login-input :deep(.el-input__wrapper) { height: 42px; border-radius: 16px; background: rgba(255, 255, 255, 0.18); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22), 0 10px 24px rgba(0, 0, 0, 0.18); } .login-input :deep(.el-input__inner) { color: #fff; } .login-input :deep(.el-input__inner::placeholder) { color: rgba(255, 255, 255, 0.62); } .login-button { position: relative; z-index: 1; margin-top: 30px; text-align: center; } .login-button .el-button { width: 110px; height: 42px; border-radius: 18px; border: none; font-weight: 700; transition: 0.3s; } .main-btn { background: linear-gradient(135deg, #38bdf8, #6366f1); box-shadow: 0 12px 30px rgba(56, 189, 248, 0.36); } .sub-btn { background: linear-gradient(135deg, #f59e0b, #fb7185); box-shadow: 0 12px 30px rgba(251, 113, 133, 0.35); } .login-button .el-button:hover { transform: translateY(-5px) translateZ(25px) scale(1.05); } .dialog-footer { display: flex; justify-content: flex-end; gap: 12px; } :global(.register-dialog .el-dialog) { border-radius: 20px; overflow: hidden; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(18px); } </style>

方案二:赛博霓虹 3D 网格登录页

特点:霓虹蓝紫色、3D 地面网格、浮动立方体、科技感强。

vue
<template> <div class="container"> <div class="cyber-grid"></div> <div class="cube cube-1"> <i></i><i></i><i></i><i></i><i></i><i></i> </div> <div class="cube cube-2"> <i></i><i></i><i></i><i></i><i></i><i></i> </div> <div class="cube cube-3"> <i></i><i></i><i></i><i></i><i></i><i></i> </div> <div class="login-wrapper"> <div class="login-box"> <div class="corner corner-lt"></div> <div class="corner corner-rb"></div> <div class="login-title"> <div class="brand">有本通讯录</div> <div class="subtitle">SYSTEM LOGIN</div> </div> <div class="login-content"> <span>用户名</span> <el-input type="text" class="login-input" v-model="user_name" @keydown.enter="passwordFocus" placeholder="请输入用户名" /> </div> <div class="login-content"> <span>密码</span> <el-input ref="passwordInput" type="password" class="login-input" v-model="password" @keydown.enter="performLogin" placeholder="请输入密码" show-password /> </div> <div class="login-button"> <el-button type="primary" class="main-btn" @click="performLogin">登录</el-button> <el-button type="warning" class="sub-btn" @click="registerVisible = true">注册</el-button> </div> </div> </div> <el-dialog v-model="registerVisible" title="用户注册" width="400px" :close-on-click-modal="false" class="register-dialog" @close="resetRegisterForm" > <el-form :model="registerForm" label-width="80px"> <el-form-item label="用户名"> <el-input v-model="registerForm.user_name" placeholder="请输入用户名" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="registerForm.password" placeholder="请输入密码" type="password" show-password /> </el-form-item> <el-form-item label="确认密码"> <el-input v-model="registerForm.confirmPassword" placeholder="请再次输入密码" type="password" show-password /> </el-form-item> <el-form-item label="姓名"> <el-input v-model="registerForm.name" placeholder="请输入姓名" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button type="warning" @click="performRegister">注册</el-button> <el-button @click="registerVisible = false">取消</el-button> </span> </template> </el-dialog> </div> </template> <script setup> import { reactive, ref } from 'vue' import { ElMessage } from 'element-plus' import axios from 'axios' import { logininfoStore } from '../store/logininfo.js' import { useRouter } from 'vue-router' const logininfo = logininfoStore() const router = useRouter() const user_name = ref('') const password = ref('') const passwordInput = ref(null) function passwordFocus() { passwordInput.value?.focus?.() } function parseUserData(data) { if (typeof data === 'string') { return JSON.parse(data) } return data } function performLogin() { axios .post('http://abtest.wyk.plus/api/UserLogin', null, { params: { user_name: user_name.value, password: password.value, }, }) .then((response) => { if (response.data.code === 0) { const userData = parseUserData(response.data.data) logininfo.set(userData) ElMessage.success('登录成功') router.push('/home') } else { ElMessage.error(response.data.message || '登录失败,请检查用户名和密码!') } }) .catch(() => { ElMessage.error('登录请求失败,请稍后重试!') }) } const registerVisible = ref(false) const registerForm = reactive({ user_name: '', password: '', confirmPassword: '', name: '', }) function resetRegisterForm() { registerForm.user_name = '' registerForm.password = '' registerForm.confirmPassword = '' registerForm.name = '' } function performRegister() { if (!registerForm.user_name || !registerForm.password || !registerForm.name) { ElMessage.warning('请填写完整信息!') return } if (registerForm.password !== registerForm.confirmPassword) { ElMessage.warning('两次输入的密码不一致!') return } axios .post('http://abtest.wyk.plus/api/Register', { user_name: registerForm.user_name, password: registerForm.password, name: registerForm.name, }) .then((response) => { if (response.data.code === 0) { ElMessage.success('注册成功,请登录') registerVisible.value = false } else { ElMessage.error(response.data.message || '注册失败!') } }) .catch(() => { ElMessage.error('注册请求失败,请稍后尝试!') }) } </script> <style scoped> .container { position: fixed; width: 100%; height: 100vh; overflow: hidden; background: radial-gradient(circle at 50% 0%, rgba(0, 255, 255, 0.22), transparent 36%), linear-gradient(160deg, #050014, #071a3d 55%, #02040f); perspective: 1000px; } .cyber-grid { position: absolute; left: -20%; bottom: -12%; width: 140%; height: 58%; background-image: linear-gradient(rgba(0, 255, 255, 0.32) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 255, 0.32) 1px, transparent 1px); background-size: 48px 48px; transform: rotateX(68deg); transform-origin: bottom; animation: gridMove 1.8s linear infinite; box-shadow: 0 -20px 90px rgba(0, 255, 255, 0.18); } .cyber-grid::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, transparent, rgba(5, 0, 20, 0.9)); } @keyframes gridMove { from { background-position: 0 0; } to { background-position: 0 48px; } } .cube { position: absolute; width: 86px; height: 86px; transform-style: preserve-3d; animation: cubeSpin 9s linear infinite; } .cube i { position: absolute; width: 86px; height: 86px; border: 1px solid rgba(0, 255, 255, 0.8); background: rgba(0, 255, 255, 0.06); box-shadow: inset 0 0 24px rgba(0, 255, 255, 0.28), 0 0 24px rgba(0, 255, 255, 0.24); } .cube i:nth-child(1) { transform: translateZ(43px); } .cube i:nth-child(2) { transform: rotateY(180deg) translateZ(43px); } .cube i:nth-child(3) { transform: rotateY(90deg) translateZ(43px); } .cube i:nth-child(4) { transform: rotateY(-90deg) translateZ(43px); } .cube i:nth-child(5) { transform: rotateX(90deg) translateZ(43px); } .cube i:nth-child(6) { transform: rotateX(-90deg) translateZ(43px); } .cube-1 { top: 14%; left: 10%; } .cube-2 { right: 13%; top: 20%; transform: scale(0.7); animation-delay: -3s; } .cube-3 { left: 22%; bottom: 17%; transform: scale(0.55); animation-delay: -5s; } @keyframes cubeSpin { 0% { transform: rotateX(0deg) rotateY(0deg) translateY(0); } 50% { transform: rotateX(180deg) rotateY(180deg) translateY(-28px); } 100% { transform: rotateX(360deg) rotateY(360deg) translateY(0); } } .login-wrapper { width: 100%; height: 100%; display: grid; place-items: center; transform-style: preserve-3d; } .login-box { width: 610px; min-height: 340px; padding: 38px 46px; box-sizing: border-box; position: relative; border-radius: 18px; background: rgba(3, 10, 32, 0.72); border: 1px solid rgba(0, 255, 255, 0.42); box-shadow: 0 0 30px rgba(0, 255, 255, 0.22), 0 0 70px rgba(124, 58, 237, 0.28), 26px 26px 0 rgba(0, 255, 255, 0.05); transform-style: preserve-3d; animation: cardCyber 4.6s ease-in-out infinite; } .login-box::before { content: ""; position: absolute; inset: 10px; border-radius: 14px; border: 1px dashed rgba(255, 255, 255, 0.18); pointer-events: none; } .login-box:hover { transform: rotateX(9deg) rotateY(9deg) translateZ(40px); } @keyframes cardCyber { 0%, 100% { transform: translateY(0) rotateX(0deg); } 50% { transform: translateY(-14px) rotateX(5deg); } } .corner { position: absolute; width: 70px; height: 70px; } .corner-lt { left: -2px; top: -2px; border-left: 4px solid #22d3ee; border-top: 4px solid #22d3ee; box-shadow: -4px -4px 18px rgba(34, 211, 238, 0.8); } .corner-rb { right: -2px; bottom: -2px; border-right: 4px solid #a855f7; border-bottom: 4px solid #a855f7; box-shadow: 4px 4px 18px rgba(168, 85, 247, 0.8); } .login-title { text-align: center; margin-bottom: 34px; color: #fff; } .brand { font-size: 36px; font-weight: 900; letter-spacing: 5px; color: #e0ffff; text-shadow: 0 0 8px #22d3ee, 0 0 22px #22d3ee, 0 0 40px #a855f7; } .subtitle { margin-top: 8px; font-size: 13px; letter-spacing: 9px; color: #67e8f9; } .login-content { position: relative; margin-top: 20px; display: flex; justify-content: center; align-items: center; } .login-content span { width: 90px; color: #67e8f9; font-weight: 700; text-align: right; margin-right: 14px; letter-spacing: 2px; } .login-input { width: 270px; } .login-input :deep(.el-input__wrapper) { height: 44px; border-radius: 6px; background: rgba(0, 255, 255, 0.07); box-shadow: inset 0 0 0 1px rgba(34, 211, 238, 0.45), 0 0 24px rgba(34, 211, 238, 0.16); } .login-input :deep(.el-input__inner) { color: #e0ffff; } .login-input :deep(.el-input__inner::placeholder) { color: rgba(224, 255, 255, 0.48); } .login-button { margin-top: 34px; text-align: center; } .login-button .el-button { width: 112px; height: 44px; margin: 0 12px; border-radius: 6px; border: none; font-weight: 800; letter-spacing: 3px; transition: 0.3s; } .main-btn { background: linear-gradient(135deg, #06b6d4, #2563eb); box-shadow: 0 0 24px rgba(34, 211, 238, 0.52); } .sub-btn { background: linear-gradient(135deg, #a855f7, #ec4899); box-shadow: 0 0 24px rgba(236, 72, 153, 0.52); } .login-button .el-button:hover { transform: translateY(-6px) translateZ(35px); filter: brightness(1.18); } .dialog-footer { display: flex; justify-content: flex-end; gap: 12px; } :global(.register-dialog .el-dialog) { border-radius: 16px; background: rgba(5, 14, 36, 0.95); border: 1px solid rgba(34, 211, 238, 0.45); box-shadow: 0 0 45px rgba(34, 211, 238, 0.25); } :global(.register-dialog .el-dialog__title), :global(.register-dialog .el-form-item__label) { color: #e0ffff; } </style>

方案三:清新蓝白 3D 浮岛登录页

特点:蓝白渐变、3D 浮岛、柔和圆球动态、整体更适合通讯录/管理系统。

vue
<template> <div class="container"> <div class="cloud cloud-1"></div> <div class="cloud cloud-2"></div> <div class="cloud cloud-3"></div> <div class="floating-island"> <div class="island-top"></div> <div class="island-side"></div> </div> <div class="bubble bubble-1"></div> <div class="bubble bubble-2"></div> <div class="bubble bubble-3"></div> <div class="bubble bubble-4"></div> <div class="login-stage"> <div class="login-box"> <div class="login-title"> <div class="logo-circle">讯</div> <div> <div class="brand">有本通讯录</div> <div class="subtitle">系统登录</div> </div> </div> <div class="login-content"> <span>用户名:</span> <el-input type="text" class="login-input" v-model="user_name" @keydown.enter="passwordFocus" placeholder="请输入用户名" /> </div> <div class="login-content"> <span>密码:</span> <el-input ref="passwordInput" type="password" class="login-input" v-model="password" @keydown.enter="performLogin" placeholder="请输入密码" show-password /> </div> <div class="login-button"> <el-button type="primary" class="main-btn" @click="performLogin">登录</el-button> <el-button type="warning" class="sub-btn" @click="registerVisible = true">注册</el-button> </div> </div> </div> <el-dialog v-model="registerVisible" title="用户注册" width="400px" :close-on-click-modal="false" class="register-dialog" @close="resetRegisterForm" > <el-form :model="registerForm" label-width="80px"> <el-form-item label="用户名"> <el-input v-model="registerForm.user_name" placeholder="请输入用户名" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="registerForm.password" placeholder="请输入密码" type="password" show-password /> </el-form-item> <el-form-item label="确认密码"> <el-input v-model="registerForm.confirmPassword" placeholder="请再次输入密码" type="password" show-password /> </el-form-item> <el-form-item label="姓名"> <el-input v-model="registerForm.name" placeholder="请输入姓名" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button type="warning" @click="performRegister">注册</el-button> <el-button @click="registerVisible = false">取消</el-button> </span> </template> </el-dialog> </div> </template> <script setup> import { reactive, ref } from 'vue' import { ElMessage } from 'element-plus' import axios from 'axios' import { logininfoStore } from '../store/logininfo.js' import { useRouter } from 'vue-router' const logininfo = logininfoStore() const router = useRouter() const user_name = ref('') const password = ref('') const passwordInput = ref(null) function passwordFocus() { passwordInput.value?.focus?.() } function parseUserData(data) { if (typeof data === 'string') { return JSON.parse(data) } return data } function performLogin() { axios .post('http://abtest.wyk.plus/api/UserLogin', null, { params: { user_name: user_name.value, password: password.value, }, }) .then((response) => { if (response.data.code === 0) { const userData = parseUserData(response.data.data) logininfo.set(userData) ElMessage.success('登录成功') router.push('/home') } else { ElMessage.error(response.data.message || '登录失败,请检查用户名和密码!') } }) .catch(() => { ElMessage.error('登录请求失败,请稍后重试!') }) } const registerVisible = ref(false) const registerForm = reactive({ user_name: '', password: '', confirmPassword: '', name: '', }) function resetRegisterForm() { registerForm.user_name = '' registerForm.password = '' registerForm.confirmPassword = '' registerForm.name = '' } function performRegister() { if (!registerForm.user_name || !registerForm.password || !registerForm.name) { ElMessage.warning('请填写完整信息!') return } if (registerForm.password !== registerForm.confirmPassword) { ElMessage.warning('两次输入的密码不一致!') return } axios .post('http://abtest.wyk.plus/api/Register', { user_name: registerForm.user_name, password: registerForm.password, name: registerForm.name, }) .then((response) => { if (response.data.code === 0) { ElMessage.success('注册成功,请登录') registerVisible.value = false } else { ElMessage.error(response.data.message || '注册失败!') } }) .catch(() => { ElMessage.error('注册请求失败,请稍后尝试!') }) } </script> <style scoped> .container { position: fixed; width: 100%; height: 100vh; overflow: hidden; background: radial-gradient(circle at 18% 18%, rgba(255, 255, 255, 0.95), transparent 18%), radial-gradient(circle at 80% 30%, rgba(186, 230, 253, 0.9), transparent 22%), linear-gradient(135deg, #dbeafe, #93c5fd 48%, #2563eb); perspective: 1200px; } .cloud { position: absolute; width: 220px; height: 70px; border-radius: 999px; background: rgba(255, 255, 255, 0.78); filter: blur(1px); animation: cloudMove 16s ease-in-out infinite; } .cloud::before, .cloud::after { content: ""; position: absolute; border-radius: 50%; background: inherit; } .cloud::before { width: 95px; height: 95px; left: 35px; top: -42px; } .cloud::after { width: 120px; height: 120px; right: 28px; top: -58px; } .cloud-1 { top: 13%; left: 8%; } .cloud-2 { top: 22%; right: 12%; transform: scale(0.78); animation-delay: -5s; } .cloud-3 { bottom: 15%; left: 13%; transform: scale(0.65); animation-delay: -8s; } @keyframes cloudMove { 0%, 100% { transform: translateX(0) translateY(0); } 50% { transform: translateX(35px) translateY(-12px); } } .floating-island { position: absolute; width: 420px; height: 180px; right: 9%; bottom: 11%; transform-style: preserve-3d; animation: islandFloat 5.2s ease-in-out infinite; } .island-top { position: absolute; width: 420px; height: 120px; border-radius: 50%; background: linear-gradient(135deg, #bfdbfe, #eff6ff); box-shadow: inset 0 12px 30px rgba(255, 255, 255, 0.8), 0 30px 50px rgba(30, 64, 175, 0.24); transform: rotateX(68deg); } .island-side { position: absolute; left: 58px; top: 70px; width: 300px; height: 90px; border-radius: 0 0 150px 150px; background: linear-gradient(180deg, #60a5fa, #1d4ed8); filter: drop-shadow(0 28px 30px rgba(30, 64, 175, 0.26)); } @keyframes islandFloat { 0%, 100% { transform: translateY(0) rotateY(-10deg); } 50% { transform: translateY(-24px) rotateY(10deg); } } .bubble { position: absolute; border-radius: 50%; background: radial-gradient(circle at 30% 25%, #fff, rgba(255, 255, 255, 0.18) 38%, rgba(59, 130, 246, 0.22)); box-shadow: inset -12px -12px 28px rgba(59, 130, 246, 0.2); animation: bubbleFloat 7s ease-in-out infinite; } .bubble-1 { width: 82px; height: 82px; left: 19%; bottom: 18%; } .bubble-2 { width: 54px; height: 54px; right: 26%; top: 14%; animation-delay: -2s; } .bubble-3 { width: 110px; height: 110px; right: 18%; bottom: 32%; animation-delay: -4s; } .bubble-4 { width: 42px; height: 42px; left: 36%; top: 18%; animation-delay: -6s; } @keyframes bubbleFloat { 0%, 100% { transform: translateY(0) translateZ(0) scale(1); } 50% { transform: translateY(-32px) translateZ(80px) scale(1.08); } } .login-stage { width: 100%; height: 100%; display: grid; place-items: center; transform-style: preserve-3d; } .login-box { width: 610px; min-height: 350px; padding: 36px 44px; position: relative; box-sizing: border-box; border-radius: 32px; background: linear-gradient(145deg, rgba(255, 255, 255, 0.92), rgba(219, 234, 254, 0.78)); border: 1px solid rgba(255, 255, 255, 0.9); box-shadow: 0 28px 60px rgba(30, 64, 175, 0.28), inset 8px 8px 18px rgba(255, 255, 255, 0.75), inset -10px -10px 24px rgba(96, 165, 250, 0.18); transform-style: preserve-3d; animation: loginFloat 4.8s ease-in-out infinite; } .login-box::after { content: ""; position: absolute; left: 38px; right: 38px; bottom: -26px; height: 36px; border-radius: 50%; background: rgba(30, 64, 175, 0.18); filter: blur(14px); transform: translateZ(-60px); } .login-box:hover { transform: rotateX(8deg) rotateY(-8deg) translateZ(50px); } @keyframes loginFloat { 0%, 100% { transform: translateY(0) rotateX(0deg); } 50% { transform: translateY(-18px) rotateX(4deg); } } .login-title { display: flex; align-items: center; justify-content: center; gap: 18px; margin-bottom: 30px; } .logo-circle { width: 72px; height: 72px; border-radius: 22px; display: grid; place-items: center; color: white; font-size: 32px; font-weight: 900; background: linear-gradient(135deg, #3b82f6, #06b6d4); box-shadow: 0 14px 28px rgba(37, 99, 235, 0.35), inset 0 4px 10px rgba(255, 255, 255, 0.3); animation: logoSwing 3.2s ease-in-out infinite; } @keyframes logoSwing { 0%, 100% { transform: rotateY(0deg) translateZ(0); } 50% { transform: rotateY(22deg) translateZ(24px); } } .brand { color: #1e3a8a; font-size: 34px; font-weight: 900; letter-spacing: 4px; } .subtitle { margin-top: 6px; color: #3b82f6; font-size: 15px; letter-spacing: 8px; } .login-content { margin-top: 20px; display: flex; justify-content: center; align-items: center; } .login-content span { width: 90px; color: #1e40af; font-weight: 700; text-align: right; margin-right: 14px; } .login-input { width: 270px; } .login-input :deep(.el-input__wrapper) { height: 44px; border-radius: 18px; background: rgba(255, 255, 255, 0.88); box-shadow: inset 4px 4px 10px rgba(147, 197, 253, 0.28), inset -4px -4px 10px rgba(255, 255, 255, 0.9), 0 12px 24px rgba(59, 130, 246, 0.12); } .login-button { margin-top: 32px; text-align: center; } .login-button .el-button { width: 112px; height: 44px; margin: 0 12px; border: none; border-radius: 18px; font-weight: 800; transition: 0.28s; } .main-btn { background: linear-gradient(135deg, #2563eb, #06b6d4); box-shadow: 0 14px 26px rgba(37, 99, 235, 0.32); } .sub-btn { background: linear-gradient(135deg, #f59e0b, #f97316); box-shadow: 0 14px 26px rgba(249, 115, 22, 0.28); } .login-button .el-button:hover { transform: translateY(-6px) translateZ(34px) scale(1.05); } .dialog-footer { display: flex; justify-content: flex-end; gap: 12px; } :global(.register-dialog .el-dialog) { border-radius: 24px; background: rgba(255, 255, 255, 0.96); box-shadow: 0 28px 70px rgba(37, 99, 235, 0.28); } </style>

这 3 个方案里,方案一更梦幻高级方案二更科技炫酷方案三更清新适合管理系统。我更推荐你用 方案三,因为通讯录系统看起来会更干净、亲和,也不会太花哨。

Share this Q&A