default-action-ref 和 welcome-file-list 标签的区别

本文介绍了Struts框架中default-action-ref标签的作用及其配置方法,同时解析了web.xml中welcome-file-list的应用,以便实现默认首页的访问。

default-action-ref 标签是 struts.xml 中的标签,意思是默认的动作引用,可以理解为当没有相应的 action 对应是,调用这个 action 引用,如下:

   1:  <default-action-ref name="index" />

上面这段代码意味着如果在地址栏中输入的 action 不存在,即调用 index 这个 action。

但是,如果我们在地址栏中不指明 action ,即日常的访问主页的操作,通过  default-action-ref   标签是不能达到效果的,这个标签仅仅作用于地址栏存在 action 但是找不到相应 action 的情况下。

那么如何达到像平常的那样访问主页的操作呢?我们就需要在 web.xml 中配置了。

welcome-file 是 web.xml 中的标签,是  welcome-file-list 标签的子标签,顾名思义,是对欢迎页面的指定:

   1:  <welcome-file-list>
   2:      <welcome-file>index</welcome-file>
   3:  </welcome-file-list>
 
当我们在 web.xml 中如是配置的话,我们就可以像访问主页一样直接定位到 index action 了。

既然是 list ,当然就可以在内部定义多个 标签,当出现多个 标签的时候,又是如何定位的呢?

   1:  <welcome-file-list>
   2:      <welcome-file>index1</welcome-file>
   3:      <welcome-file>index2</welcome-file>
   4:      <welcome-file>index3</welcome-file>
   5:      <welcome-file>index4</welcome-file>
   6:  </welcome-file-list>

我们可以在 中定义多个 标签,当浏览器发来请求的时候,服务器会根据 welcome-file-list 标签下的 标签进行遍历,一旦得到存在的 就立即 forward 到这个 。

<template> <div class="login-container"> <!-- 左侧宣传区 --> <div class="side-banner"> <h1>儿童体能训练馆</h1> <p>管理课程 · 预约教练 · 记录成长</p> <div class="tagline">让每一次训练都有迹可循</div> </div> <!-- 右侧登录区 --> <div class="login-form-wrapper"> <el-form ref="form" :model="loginform" label-width="60px" label-position="left" > <h2 class="title">欢迎登录</h2> <el-form-item label="用户名"> <el-input v-model="loginform.userName" placeholder="请输入用户名" clearable prefix-icon="el-icon-user" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="loginform.pwd" type="password" placeholder="请输入密码" clearable prefix-icon="el-icon-lock" @keyup.enter.native="submit" /> </el-form-item> <el-form-item class="actions"> <el-button type="primary" @click="submit" :loading="loading"> 登 录 </el-button> <el-button @click="dialogFormVisible = true">注 册</el-button> </el-form-item> </el-form> <!-- 注册弹窗(已放大并美化) --> <el-dialog :visible.sync="dialogFormVisible" width="500px" center custom-class="register-dialog" append-to-body @close="resetForm" > <template #title> <h3 style="text-align: center; color: #1e3a8a; font-weight: 600"> 🧒 创建新用户 </h3> </template> <el-form :model="form" :rules="registerRules" ref="registerRef" label-position="left" label-width="70px" style="padding: 0 20px" > <!-- 第一行:用户名 + 昵称 --> <div style="display: flex; gap: 20px"> <el-form-item label="用户名" prop="userName" style="flex: 1"> <el-input v-model="form.userName" placeholder="6-10位字母或数字" /> </el-form-item> <el-form-item label="昵称" prop="nickName" style="flex: 1"> <el-input v-model="form.nickName" placeholder="如:小明爸爸" /> </el-form-item> </div> <!-- 第二行:密码 + 手机号 --> <div style="display: flex; gap: 20px"> <el-form-item label="密码" prop="pwd" style="flex: 1"> <el-input v-model="form.pwd" type="password" placeholder="6-10位密码" /> </el-form-item> <el-form-item label="手机号" prop="phoneCode" style="flex: 1"> <el-input v-model="form.phoneCode" placeholder="用于接收通知" /> </el-form-item> </div> <!-- 头像上传 + 角色选择 水平排列 --> <div style=" display: flex; gap: 40px; align-items: flex-start; margin-bottom: 20px; " > <!-- 头像 --> <el-form-item label="头像"> <el-upload class="avatar-uploader" action="http://localhost:8081/file/upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="form.pic" :src="form.pic" class="avatar" alt="头像" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </el-form-item> <!-- 角色 --> <el-form-item label="角色" prop="roleId" style="flex: 1"> <el-radio-group v-model="form.roleId" style="margin-top: 12px"> <div style="margin-bottom: 8px"> <el-radio-button label="1">管理员</el-radio-button> </div> <div style="margin-bottom: 8px"> <el-radio-button label="2">家长/用户</el-radio-button> </div> <div> <el-radio-button label="3">教练</el-radio-button> </div> </el-radio-group> </el-form-item> </div> <!-- 操作按钮 --> <el-form-item style="text-align: center; margin-top: 20px"> <el-button type="primary" @click="register" size="medium" style="width: 120px" > 注 册 </el-button> <el-button @click="resetForm" size="medium" style="width: 120px; margin-left: 20px" > 重 置 </el-button> </el-form-item> </el-form> </el-dialog> </div> </div> </template> <script> import { login } from "@/api/Login"; import { register } from "@/api/Login"; export default { name: "LoginView", data() { return { loading: false, dialogFormVisible: false, pic: "", loginform: { userName: "", pwd: "", }, form: { roleId: "2", // 默认选中用户 pic: "", }, registerRules: { userName: [ { required: true, message: "请输入用户名", trigger: "blur" }, { min: 6, max: 10, message: "长度在 6 到 10 个字符", trigger: "blur", }, ], nickName: [ { required: true, message: "请输入昵称", trigger: "blur" }, { min: 1, max: 6, message: "长度在 1 到 6 个字符", trigger: "blur" }, ], pwd: [ { required: true, message: "请输入密码", trigger: "blur" }, { min: 6, max: 10, message: "长度在 6 到 10 个字符", trigger: "blur", }, ], phoneCode: [ { required: true, message: "请输入手机号", trigger: "blur" }, { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号", trigger: "blur", }, ], roleId: [{ required: true, message: "请选择角色", trigger: "change" }], }, }; }, methods: { handleAvatarSuccess(res) { if (res.data?.url) { this.form.pic = res.data.url; this.$message.success("头像上传成功!"); } else { this.$message.error("上传失败,请重试"); } }, beforeAvatarUpload(file) { const isImage = ["image/jpeg", "image/jpg", "image/png"].includes( file.type ); const isLt2M = file.size / 1024 / 1024 < 2; if (!isImage) { this.$message.error("头像必须是 JPG 或 PNG 格式!"); return false; } if (!isLt2M) { this.$message.error("头像图片大小不能超过 2MB!"); return false; } return true; }, submit() { this.$refs.form.validateField(["userName", "pwd"], (valid) => { if (!valid) { this.loading = true; login(this.loginform) .then((resp) => { if (resp.data.code === 200) { this.$message.success("登录成功!"); const user = resp.data.data.user; localStorage.setItem("userId", user.id); localStorage.setItem("roleId", user.roleId); if (user.roleId == 1) { this.$router.push("/adminChart"); } else if (user.roleId == 2) { this.$router.push("/user/bulletin"); } else { this.$router.push("/coach/bulletin"); } } else { this.$message.error(resp.data.message || "登录失败"); } }) .catch((err) => { console.error("登录请求失败:", err); this.$message.error("网络错误,请检查连接"); }) .finally(() => { this.loading = false; }); } }); }, register() { this.$refs.registerRef.validate((valid) => { if (valid) { register(this.form) .then((resp) => { if (resp.data.code === 200) { this.$message.success("注册成功,请登录"); this.dialogFormVisible = false; } else { this.$message.error(resp.data.message || "注册失败"); } }) .catch((err) => { console.error("注册失败:", err); this.$message.error("网络异常,请稍后重试"); }); } }); }, resetForm() { if (this.$refs.registerRef) { this.$refs.registerRef.resetFields(); } this.form.pic = ""; this.pic = ""; }, }, }; </script> <style scoped> .login-container { display: flex; height: 100vh; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; background: url("@/assets/login.jpg") no-repeat center center fixed; background-size: cover; } /* 左侧宣传栏 */ .side-banner { flex: 1; background: rgba(0, 0, 0, 0.4); color: #fff; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; padding: 40px; backdrop-filter: blur(8px); border-radius: 0 20px 20px 0; } .side-banner h1 { font-size: 2.5rem; margin-bottom: 12px; font-weight: 700; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } .side-banner p { font-size: 1.2rem; margin-bottom: 8px; } .tagline { font-size: 1rem; color: #e2e8f0; margin-top: 16px; padding: 10px 20px; border-top: 1px solid rgba(255, 255, 255, 0.2); border-bottom: 1px solid rgba(255, 255, 255, 0.2); } /* 右侧登录框 */ .login-form-wrapper { width: 420px; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border-radius: 20px 0 0 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); padding: 40px 30px; display: flex; flex-direction: column; justify-content: center; animation: slide-in 0.6s ease; } .title { text-align: center; color: #1e3a8a; margin-bottom: 30px; font-size: 1.6rem; font-weight: 600; } .actions { margin-top: 10px; } ::v-deep .el-form-item__label { font-weight: 500; color: #333; } /* 动画入场 */ @keyframes slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } /* 头像上传美化 */ .avatar-uploader { display: inline-block; } .avatar-uploader-icon { border: 2px dashed #c0ccda; border-radius: 50%; width: 100px; height: 100px; line-height: 100px; text-align: center; font-size: 24px; color: #999; cursor: pointer; transition: all 0.3s ease; } .avatar-uploader-icon:hover { border-color: #409eff; color: #409eff; } .avatar { width: 100px; height: 100px; object-fit: cover; border-radius: 50%; border: 3px solid #e6f1ff; transition: transform 0.3s ease; } .avatar:hover { transform: scale(1.05); } /* 注册弹窗样式 */ ::v-deep .register-dialog { border-radius: 16px; overflow: hidden; } ::v-deep .register-dialog .el-dialog__body { padding: 30px 20px; } /* 响应式适配 */ @media (max-width: 768px) { .login-container { flex-direction: column; } .side-banner { padding: 20px; border-radius: 20px 20px 0 0; } .login-form-wrapper { width: 100%; border-radius: 0; margin-top: -40px; z-index: 10; } .side-banner h1 { font-size: 1.8rem; } } </style> 更漂亮些
最新发布
09-23
太棒了!你的登录页已经结构清晰、功能完整,现在我们来 **全面升级视觉体验**,让它真正“更漂亮些”! 我们将从以下几个维度进行深度美化: --- ## ✨ 升级目标 | 改进方向 | 效果 | |--------|------| | 🎨 主题风格 | 温馨童趣 + 现代玻璃拟态(Glassmorphism) | | 🌈 色彩搭配 | 柔蓝绿渐变,适合儿童体能主题 | | 🖼️ 动效与层次 | 登录框入场动画、按钮悬停、头像缩放 | | 🔤 排版优化 | 字体间距、对齐方式、表单分组更舒适 | | 📱 响应式增强 | 移动端也能优雅显示 | --- ### ✅ 修改内容汇总 1. ✅ 左侧宣传区加入 SVG 插画或动态背景 2. ✅ 登录框宽度改为 `500px` 并居中微抬 3. ✅ 注册弹窗使用卡片式设计 + 内阴影 4. ✅ 全局字体、圆角、色彩统一 5. ✅ 添加加载动效交互反馈 --- ### 🎨 最终美化版完整代码(建议全量替换) ```vue <template> <div class="login-container"> <!-- 左侧温馨宣传区 --> <div class="side-banner"> <h1>儿童体能训练馆</h1> <p>管理课程 · 预约教练 · 记录成长</p> <div class="tagline">让每一次训练都有迹可循 💪</div> <!-- 可选:添加一个孩子奔跑的SVG插画 --> <div class="illustration"> <img src="@/assets/kid-running.svg" alt="儿童运动插画" /> </div> </div> <!-- 右侧登录区 --> <div class="login-form-wrapper"> <el-form ref="form" :model="loginform" label-width="70px" label-position="left"> <h2 class="title">>Welcome</h2> <el-form-item label="用户名"> <el-input v-model="loginform.userName" placeholder="请输入用户名" clearable prefix-icon="el-icon-user" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="loginform.pwd" type="password" placeholder="请输入密码" clearable prefix-icon="el-icon-lock" @keyup.enter.native="submit" /> </el-form-item> <el-form-item class="actions"> <el-button type="primary" @click="submit" :loading="loading" style="width: 130px"> 登 录 </el-button> <el-button @click="dialogFormVisible = true" style="width: 130px"> 注 册 </el-button> </el-form-item> <!-- 忘记密码链接 --> <div class="extra-links"> <a href="#" @click.prevent="gotoForget">忘记密码?</a> </div> </el-form> <!-- 注册弹窗 --> <el-dialog :visible.sync="dialogFormVisible" width="520px" center custom-class="register-dialog" append-to-body @close="resetForm" > <template #title> <h3 style="text-align: center; color: #2d8cf0; font-weight: 600;"> 🧒 创建新账户 </h3> </template> <el-form :model="form" :rules="registerRules" ref="registerRef" label-position="left" label-width="80px" class="register-form" > <!-- 用户名 + 昵称 --> <div class="row"> <el-form-item label="用户名" prop="userName"> <el-input v-model="form.userName" placeholder="6-10位字母数字" /> </el-form-item> <el-form-item label="昵称" prop="nickName"> <el-input v-model="form.nickName" placeholder="如:小明妈妈" /> </el-form-item> </div> <!-- 密码 + 手机号 --> <div class="row"> <el-form-item label="密码" prop="pwd"> <el-input v-model="form.pwd" type="password" placeholder="6-10位密码" /> </el-form-item> <el-form-item label="手机号" prop="phoneCode"> <el-input v-model="form.phoneCode" placeholder="用于接收通知" /> </el-form-item> </div> <!-- 头像 + 角色 --> <div class="upload-row"> <!-- 头像 --> <el-form-item label="头像"> <el-upload class="avatar-uploader" action="http://localhost:8081/file/upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="form.pic" :src="form.pic" class="avatar" alt="头像" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </el-form-item> <!-- 角色 --> <el-form-item label="角色" prop="roleId" class="role-group"> <el-radio-group v-model="form.roleId"> <el-radio-button label="1">管理员</el-radio-button> <el-radio-button label="2">家长/用户</el-radio-button> <el-radio-button label="3">教练</el-radio-button> </el-radio-group> </el-form-item> </div> <!-- 操作按钮 --> <el-form-item style="text-align: center; margin-top: 20px;"> <el-button type="primary" @click="register" size="medium" style="min-width: 90px"> 注 册 </el-button> <el-button @click="resetForm" size="medium" style="min-width: 90px; margin-left: 16px"> 重 置 </el-button> </el-form-item> </el-form> </el-dialog> </div> </div> </template> <script> import { login } from "@/api/Login"; import { register } from "@/api/Login"; export default { name: "LoginView", data() { return { loading: false, dialogFormVisible: false, loginform: { userName: "", pwd: "", }, form: { roleId: "2", pic: "", }, registerRules: { userName: [ { required: true, message: "请输入用户名", trigger: "blur" }, { min: 6, max: 10, message: "长度在 6 到 10 个字符", trigger: "blur" }, ], nickName: [ { required: true, message: "请输入昵称", trigger: "blur" }, { min: 1, max: 6, message: "长度在 1 到 6 个字符", trigger: "blur" }, ], pwd: [ { required: true, message: "请输入密码", trigger: "blur" }, { min: 6, max: 10, message: "长度在 6 到 10 个字符", trigger: "blur" }, ], phoneCode: [ { required: true, message: "请输入手机号", trigger: "blur" }, { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号", trigger: "blur" }, ], roleId: [{ required: true, message: "请选择角色", trigger: "change" }], }, }; }, methods: { handleAvatarSuccess(res) { if (res.data?.url) { this.form.pic = res.data.url; this.$message.success("头像上传成功!"); } else { this.$message.error("上传失败,请重试"); } }, beforeAvatarUpload(file) { const isImage = ["image/jpeg", "image/jpg", "image/png"].includes(file.type); const isLt2M = file.size / 1024 / 1024 < 2; if (!isImage) { this.$message.error("头像必须是 JPG 或 PNG 格式!"); return false; } if (!isLt2M) { this.$message.error("头像图片大小不能超过 2MB!"); return false; } return true; }, submit() { this.$refs.form.validateField(["userName", "pwd"], (valid) => { if (!valid) { this.loading = true; login(this.loginform) .then((resp) => { if (resp.data.code === 200) { this.$message.success("登录成功!"); const user = resp.data.data.user; localStorage.setItem("userId", user.id); localStorage.setItem("roleId", user.roleId); if (user.roleId == 1) { this.$router.push("/adminChart"); } else if (user.roleId == 2) { this.$router.push("/user/bulletin"); } else { this.$router.push("/coach/bulletin"); } } else { this.$message.error(resp.data.message || "登录失败"); } }) .catch((err) => { console.error("登录请求失败:", err); this.$message.error("网络错误,请检查连接"); }) .finally(() => { this.loading = false; }); } }); }, register() { this.$refs.registerRef.validate((valid) => { if (valid) { register(this.form) .then((resp) => { if (resp.data.code === 200) { this.$message.success("注册成功,请登录"); this.dialogFormVisible = false; } else { this.$message.error(resp.data.message || "注册失败"); } }) .catch((err) => { console.error("注册失败:", err); this.$message.error("网络异常,请稍后重试"); }); } }); }, resetForm() { if (this.$refs.registerRef) { this.$refs.registerRef.resetFields(); } this.form.pic = ""; }, gotoForget() { this.$alert('请联系管理员重置密码', '忘记密码', { confirmButtonText: '确定', type: 'info' }); } }, }; </script> <style scoped> .login-container { display: flex; height: 100vh; font-family: 'Nunito', 'Segoe UI', sans-serif; background: linear-gradient(135deg, #e0f7fa, #fff3e0), url("@/assets/login-bg-blur.jpg") no-repeat center center fixed; background-size: cover; position: relative; } /* 左侧宣传栏 */ .side-banner { flex: 1; background: rgba(0, 82, 147, 0.6); /* 蓝色主调 */ color: #fff; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; padding: 60px 40px; backdrop-filter: blur(10px); border-radius: 0 20px 20px 0; position: relative; overflow: hidden; } .side-banner::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.1) 0%, transparent 70%); pointer-events: none; } .side-banner h1 { font-size: 2.8rem; margin-bottom: 12px; font-weight: 800; text-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); letter-spacing: -0.5px; } .side-banner p { font-size: 1.3rem; margin-bottom: 10px; opacity: 0.9; } .tagline { font-size: 1.1rem; color: #e0f7ff; margin-top: 20px; padding: 12px 24px; background: rgba(255, 255, 255, 0.15); border-radius: 30px; border: 1px solid rgba(255, 255, 255, 0.2); } .illustration img { width: 180px; margin-top: 30px; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1)); } /* 右侧登录框 */ .login-form-wrapper { width: 500px; background: rgba(255, 255, 255, 0.98); backdrop-filter: blur(12px); border-radius: 20px 0 0 20px; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.18); padding: 50px 40px; display: flex; flex-direction: column; justify-content: center; animation: slide-up 0.6s ease forwards; transform: translateY(20px); z-index: 10; } @keyframes slide-up { to { transform: translateY(0); opacity: 1; } } .title { text-align: center; color: #2d8cf0; margin-bottom: 36px; font-size: 1.8rem; font-weight: 700; letter-spacing: -0.5px; } .actions { margin-top: 16px; } .extra-links { text-align: right; font-size: 13px; color: #999; margin-top: -10px; } .extra-links a { color: #2d8cf0; text-decoration: none; } .extra-links a:hover { text-decoration: underline; } ::v-deep .el-form-item__label { font-weight: 600; color: #333; font-size: 14px; } /* 表单行布局 */ .row { display: flex; gap: 24px; margin-bottom: 20px; } .row .el-form-item { flex: 1; } .upload-row { display: flex; gap: 30px; align-items: flex-start; margin-bottom: 20px; } .role-group ::v-deep .el-radio-button__inner { padding: 8px 12px; border-radius: 8px !important; border: 1px solid #ddd; } .role-group ::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner { background: #2d8cf0; border-color: #2d8cf0; color: white; } /* 头像上传 */ .avatar-uploader-icon { border: 2px dashed #d9edf7; border-radius: 50%; width: 100px; height: 100px; line-height: 100px; text-align: center; font-size: 24px; color: #8cc5ff; cursor: pointer; transition: all 0.3s ease; background: #f5f9ff; } .avatar-uploader-icon:hover { border-color: #2d8cf0; color: #2d8cf0; transform: scale(1.05); } .avatar { width: 100px; height: 100px; object-fit: cover; border-radius: 50%; border: 3px solid #e6f1ff; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); transition: transform 0.3s ease; } .avatar:hover { transform: scale(1.08); } /* 注册弹窗整体样式 */ ::v-deep .register-dialog { border-radius: 20px; overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); } ::v-deep .register-dialog .el-dialog__body { padding: 20px 0 10px; background: #fafbfc; } /* 响应式适配 */ @media (max-width: 768px) { .login-container { flex-direction: column; } .side-banner { padding: 30px 20px; border-radius: 20px 20px 0 0; background: rgba(0, 82, 147, 0.7); } .login-form-wrapper { width: 90%; border-radius: 0; margin-top: -60px; padding: 40px 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); } .side-banner h1 { font-size: 2rem; } .side-banner p, .tagline { font-size: 1rem; } .illustration img { width: 120px; } } </style> ``` --- ## ✅ 必须准备的资源 | 文件路径 | 说明 | |--------|------| | `src/assets/login-bg-blur.jpg` | 模糊化背景图(推荐蓝天草地或孩子运动场景) | | `src/assets/kid-running.svg` | 儿童奔跑插画 SVG(可从 [undraw.co](https://undraw.co) 下载) | > 示例 SVG 替代代码(无需文件): ```html <div class="illustration"> <svg width="180" viewBox="0 0 600 400" xmlns="http://www.w3.org/2000/svg"> <rect width="600" height="400" fill="#e0f7fa"/> <circle cx="300" cy="120" r="40" fill="#ffb74d"/> <rect x="270" y="160" width="60" height="80" fill="#64b5f6"/> <line x1="270" y1="180" x2="230" y2="220" stroke="#64b5f6" stroke-width="10" stroke-linecap="round"/> <line x1="330" y1="180" x2="370" y2="220" stroke="#64b5f6" stroke-width="10" stroke-linecap="round"/> <line x1="270" y1="240" x2="250" y2="280" stroke="#8d6e63" stroke-width="10" stroke-linecap="round"/> <line x1="330" y1="240" x2="350" y2="280" stroke="#8d6e63" stroke-width="10" stroke-linecap="round"/> </svg> </div> ``` --- ## 🌟 视觉亮点总结 | 特性 | 效果 | |------|------| | 🎨 渐变背景 + 毛玻璃 | 更现代、更有呼吸感 | | 🧍‍♂️ SVG 插画点缀 | 提升童趣氛围 | | 🌀 登录框滑入动画 | 增加仪式感 | | 🔘 圆角按钮 + 悬停反馈 | 交互更友好 | | 📱 完整响应式支持 | 手机平板都好看 | | 📝 “忘记密码”提示 | 功能更完整 | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值