This commit is contained in:
parent
770c78733d
commit
8542a52f4b
|
@ -2,9 +2,14 @@ import { get, post, put, del } from '../utils/request'
|
||||||
|
|
||||||
// 获取课程列表
|
// 获取课程列表
|
||||||
export function getCourses(params) {
|
export function getCourses(params) {
|
||||||
return get('/courses', params)
|
return get('/bs/courses', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCoursesMethod(params) {
|
||||||
|
return get('/bs/courses', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取课程详情
|
// 获取课程详情
|
||||||
export function getCourseDetail(id) {
|
export function getCourseDetail(id) {
|
||||||
return get(`/courses/${id}`)
|
return get(`/courses/${id}`)
|
||||||
|
@ -49,11 +54,11 @@ export function toggleFavoriteCourse(id) {
|
||||||
export function searchCourses(params) {
|
export function searchCourses(params) {
|
||||||
// 处理数组参数
|
// 处理数组参数
|
||||||
let queryParams = { ...params }
|
let queryParams = { ...params }
|
||||||
|
|
||||||
// 如果有标签数组,转换为逗号分隔的字符串
|
// 如果有标签数组,转换为逗号分隔的字符串
|
||||||
if (params.tags && Array.isArray(params.tags) && params.tags.length > 0) {
|
if (params.tags && Array.isArray(params.tags) && params.tags.length > 0) {
|
||||||
queryParams.tags = params.tags.join(',')
|
queryParams.tags = params.tags.join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
return get('/courses/search', queryParams)
|
return get('/courses/search', queryParams)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,17 @@ const normalUser = {
|
||||||
role: 'user'
|
role: 'user'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function registerMethod(registerForm){
|
||||||
|
return post('/bs/user/register', registerForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendVerifyCodeMethod(email){
|
||||||
|
return post('/bs/user/getEmailCode', { email })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 用户登录
|
// 用户登录
|
||||||
export function login(username, password) {
|
export function login(username, password) {
|
||||||
// 模拟登录逻辑
|
// 模拟登录逻辑
|
||||||
|
@ -27,7 +38,7 @@ export function login(username, password) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 根据用户名判断返回管理员还是普通用户
|
// 根据用户名判断返回管理员还是普通用户
|
||||||
const userInfo = username === 'admin' ? adminUser : normalUser
|
const userInfo = username === 'admin' ? adminUser : normalUser
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
code: 200,
|
code: 200,
|
||||||
data: {
|
data: {
|
||||||
|
@ -43,4 +54,4 @@ export function login(username, password) {
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
export function updateUserProfile(userId, data) {
|
export function updateUserProfile(userId, data) {
|
||||||
return put(`/users/${userId}`, data)
|
return put(`/users/${userId}`, data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { useUserStore } from '../stores/user'
|
||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
// baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量获取 API 基础 URL
|
// baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量获取 API 基础 URL
|
||||||
baseURL: 'http://localhost:8101', // 从环境变量获取 API 基础 URL
|
//baseURL: 'http://localhost:8101', // 从环境变量获取 API 基础 URL
|
||||||
|
baseURL: 'http://localhost:8084', // 从环境变量获取 API 基础 URL
|
||||||
timeout: 15000, // 请求超时时间
|
timeout: 15000, // 请求超时时间
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ import {
|
||||||
QuestionFilled, School, DocumentChecked, DataAnalysis,
|
QuestionFilled, School, DocumentChecked, DataAnalysis,
|
||||||
Reading, Edit, User, Star
|
Reading, Edit, User, Star
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
|
import {getCoursesMethod} from "@/api/course.js";
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
@ -133,13 +134,13 @@ const features = [
|
||||||
]
|
]
|
||||||
|
|
||||||
// 更新课程数据
|
// 更新课程数据
|
||||||
const popularCourses = ref([
|
/*const popularCourses = ref([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'C++面向对象编程精讲',
|
title: 'C++面向对象编程精讲',
|
||||||
category: 'cpp',
|
category: 'cpp',
|
||||||
categoryName: 'C++编程',
|
categoryName: 'C++编程',
|
||||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
coverImg: 'http://localhost:9000/file/c6d07740-7306-4fc1-b1f8-670684a73ed9/img/31c258e7-3fd4-402c-8f2f-c9455c6cfdaf.png',
|
||||||
studentCount: 1234,
|
studentCount: 1234,
|
||||||
rating: 4.8,
|
rating: 4.8,
|
||||||
price: 0
|
price: 0
|
||||||
|
@ -165,7 +166,16 @@ const popularCourses = ref([
|
||||||
categoryName: '测试',
|
categoryName: '测试',
|
||||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
||||||
}
|
}
|
||||||
])
|
])*/
|
||||||
|
|
||||||
|
const popularCourses = ref([])
|
||||||
|
|
||||||
|
const getCourses = async () => {
|
||||||
|
var axiosResponse = await getCoursesMethod();
|
||||||
|
console.log(axiosResponse)
|
||||||
|
popularCourses.value = axiosResponse.data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const viewCourse = (courseId) => {
|
const viewCourse = (courseId) => {
|
||||||
router.push(`/course/detail/${courseId}`)
|
router.push(`/course/detail/${courseId}`)
|
||||||
|
@ -217,6 +227,9 @@ onMounted(() => {
|
||||||
startGuide()
|
startGuide()
|
||||||
localStorage.setItem('hasVisited', 'true')
|
localStorage.setItem('hasVisited', 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCourses()
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,15 @@
|
||||||
|
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<div class="form-tabs">
|
<div class="form-tabs">
|
||||||
<div
|
<div
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'login' }"
|
:class="{ active: activeTab === 'login' }"
|
||||||
@click="activeTab = 'login'"
|
@click="activeTab = 'login'"
|
||||||
>
|
>
|
||||||
登录
|
登录
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'register' }"
|
:class="{ active: activeTab === 'register' }"
|
||||||
@click="activeTab = 'register'"
|
@click="activeTab = 'register'"
|
||||||
>
|
>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-container" v-if="activeTab === 'login'">
|
<div class="form-container" v-if="activeTab === 'login'">
|
||||||
<el-form
|
<el-form
|
||||||
ref="loginFormRef"
|
ref="loginFormRef"
|
||||||
:model="loginForm"
|
:model="loginForm"
|
||||||
:rules="loginRules"
|
:rules="loginRules"
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<div class="custom-input" :class="{ focused: activeInput === 'username' }">
|
<div class="custom-input" :class="{ focused: activeInput === 'username' }">
|
||||||
<el-icon><User /></el-icon>
|
<el-icon><User /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="loginForm.username"
|
v-model="loginForm.username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
|
@ -64,14 +64,14 @@
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<div class="custom-input" :class="{ focused: activeInput === 'password' }">
|
<div class="custom-input" :class="{ focused: activeInput === 'password' }">
|
||||||
<el-icon><Lock /></el-icon>
|
<el-icon><Lock /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="loginForm.password"
|
v-model="loginForm.password"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
placeholder="请输入密码"
|
placeholder="请输入密码"
|
||||||
@focus="activeInput = 'password'"
|
@focus="activeInput = 'password'"
|
||||||
@blur="activeInput = ''"
|
@blur="activeInput = ''"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
class="password-toggle"
|
class="password-toggle"
|
||||||
@click="showPassword = !showPassword"
|
@click="showPassword = !showPassword"
|
||||||
>
|
>
|
||||||
|
@ -86,8 +86,8 @@
|
||||||
<el-button type="text" @click="forgotPassword">忘记密码?</el-button>
|
<el-button type="text" @click="forgotPassword">忘记密码?</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
class="submit-btn"
|
class="submit-btn"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="handleLogin"
|
@click="handleLogin"
|
||||||
|
@ -114,17 +114,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-container" v-else>
|
<div class="form-container" v-else>
|
||||||
<el-form
|
<el-form
|
||||||
ref="registerFormRef"
|
ref="registerFormRef"
|
||||||
:model="registerForm"
|
:model="registerForm"
|
||||||
:rules="registerRules"
|
:rules="registerRules"
|
||||||
class="register-form"
|
class="register-form"
|
||||||
>
|
>
|
||||||
<!-- 注册表单字段 -->
|
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<div class="custom-input">
|
<div class="custom-input">
|
||||||
<el-icon><User /></el-icon>
|
<el-icon><User /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="registerForm.username"
|
v-model="registerForm.username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="请设置用户名"
|
placeholder="请设置用户名"
|
||||||
|
@ -135,23 +134,43 @@
|
||||||
<el-form-item prop="email">
|
<el-form-item prop="email">
|
||||||
<div class="custom-input">
|
<div class="custom-input">
|
||||||
<el-icon><Message /></el-icon>
|
<el-icon><Message /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="registerForm.email"
|
v-model="registerForm.email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="请输入邮箱"
|
placeholder="请输入邮箱"
|
||||||
>
|
>
|
||||||
|
<el-button
|
||||||
|
class="verify-btn"
|
||||||
|
type="primary"
|
||||||
|
:disabled="cooldown > 0"
|
||||||
|
@click="sendVerifyCode"
|
||||||
|
>
|
||||||
|
{{ cooldown > 0 ? `${cooldown}s` : '发送验证码' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="verifyCode">
|
||||||
|
<div class="custom-input">
|
||||||
|
<el-icon><Key /></el-icon>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.verifyCode"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
maxlength="6"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<div class="custom-input">
|
<div class="custom-input">
|
||||||
<el-icon><Lock /></el-icon>
|
<el-icon><Lock /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="registerForm.password"
|
v-model="registerForm.password"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
placeholder="请设置密码"
|
placeholder="请设置密码"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
class="password-toggle"
|
class="password-toggle"
|
||||||
@click="showPassword = !showPassword"
|
@click="showPassword = !showPassword"
|
||||||
>
|
>
|
||||||
|
@ -164,7 +183,7 @@
|
||||||
<el-form-item prop="confirmPassword">
|
<el-form-item prop="confirmPassword">
|
||||||
<div class="custom-input">
|
<div class="custom-input">
|
||||||
<el-icon><Lock /></el-icon>
|
<el-icon><Lock /></el-icon>
|
||||||
<input
|
<input
|
||||||
v-model="registerForm.confirmPassword"
|
v-model="registerForm.confirmPassword"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
placeholder="请确认密码"
|
placeholder="请确认密码"
|
||||||
|
@ -172,8 +191,8 @@
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
class="submit-btn"
|
class="submit-btn"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="handleRegister"
|
@click="handleRegister"
|
||||||
|
@ -190,18 +209,22 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useUserStore } from '../stores/user'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import {
|
import {
|
||||||
User, Lock, View, Hide, Message,
|
User, Lock, View, Hide, Message,
|
||||||
ChatDotRound, Position
|
ChatDotRound, Position, Key
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
|
import { registerMethod, sendVerifyCodeMethod } from "@/api/user.js"
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
const activeTab = ref('login')
|
const activeTab = ref('login')
|
||||||
const showPassword = ref(false)
|
const showPassword = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const rememberMe = ref(false)
|
const rememberMe = ref(false)
|
||||||
const activeInput = ref('')
|
const activeInput = ref('')
|
||||||
|
const cooldown = ref(0)
|
||||||
|
|
||||||
const loginForm = reactive({
|
const loginForm = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
|
@ -211,11 +234,11 @@ const loginForm = reactive({
|
||||||
const registerForm = reactive({
|
const registerForm = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
email: '',
|
email: '',
|
||||||
|
verifyCode: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: ''
|
confirmPassword: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 登录表单验证规则
|
|
||||||
const loginRules = {
|
const loginRules = {
|
||||||
username: [
|
username: [
|
||||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
@ -227,7 +250,6 @@ const loginRules = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册表单验证规则
|
|
||||||
const registerRules = {
|
const registerRules = {
|
||||||
username: [
|
username: [
|
||||||
{ required: true, message: '请设置用户名', trigger: 'blur' },
|
{ required: true, message: '请设置用户名', trigger: 'blur' },
|
||||||
|
@ -237,59 +259,74 @@ const registerRules = {
|
||||||
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
||||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
|
verifyCode: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
|
{ len: 6, message: '验证码长度应为6位', trigger: 'blur' }
|
||||||
|
],
|
||||||
password: [
|
password: [
|
||||||
{ required: true, message: '请设置密码', trigger: 'blur' },
|
{ required: true, message: '请设置密码', trigger: 'blur' },
|
||||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
confirmPassword: [
|
confirmPassword: [
|
||||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||||
{
|
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||||
validator: (rule, value, callback) => {
|
|
||||||
if (value !== registerForm.password) {
|
|
||||||
callback(new Error('两次输入密码不一致'))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理登录
|
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
// 模拟登录请求
|
if (loginForm.username === 'admin' && loginForm.password === 'admin123') {
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
await userStore.mockAdminLogin()
|
||||||
ElMessage.success('登录成功')
|
ElMessage.success('登录成功')
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
} else {
|
||||||
|
ElMessage.error('用户名或密码错误')
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('登录失败')
|
ElMessage.error('登录失败: ' + error.message)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理注册
|
const handleRegister = () => {
|
||||||
const handleRegister = async () => {
|
if (registerForm.password !== registerForm.confirmPassword){
|
||||||
loading.value = true
|
ElMessage.error('两次密码不一致')
|
||||||
try {
|
return
|
||||||
// 模拟注册请求
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
ElMessage.success('注册成功')
|
|
||||||
activeTab.value = 'login'
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error('注册失败')
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerMethod(registerForm)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 忘记密码
|
|
||||||
const forgotPassword = () => {
|
const forgotPassword = () => {
|
||||||
ElMessage.info('忘记密码功能开发中')
|
ElMessage.info('忘记密码功能开发中')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sendVerifyCode = async () => {
|
||||||
|
if (!registerForm.email) {
|
||||||
|
ElMessage.warning('请先输入邮箱地址')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var axiosResponse = await sendVerifyCodeMethod(registerForm.email);
|
||||||
|
ElMessage.success('验证码已发送')
|
||||||
|
cooldown.value = 60
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
cooldown.value--
|
||||||
|
if (cooldown.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
console.log(axiosResponse)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('验证码发送失败: ' + error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -375,7 +412,7 @@ const forgotPassword = () => {
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.logo-wrapper {
|
.logo-wrapper {
|
||||||
.logo-text {
|
.logo-text {
|
||||||
.logo-title {
|
.logo-title {
|
||||||
|
@ -404,88 +441,86 @@ const forgotPassword = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-box {
|
.login-box {
|
||||||
width: 100%;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 460px;
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.form-tabs {
|
.form-container {
|
||||||
display: flex;
|
width: 100%;
|
||||||
position: relative;
|
}
|
||||||
margin-bottom: 30px;
|
|
||||||
border-bottom: 2px solid #eee;
|
|
||||||
|
|
||||||
.tab-item {
|
.login-form,
|
||||||
flex: 1;
|
.register-form {
|
||||||
text-align: center;
|
width: 100%;
|
||||||
padding: 12px;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #666;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #2563eb;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-line {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -2px;
|
|
||||||
height: 2px;
|
|
||||||
width: 50%;
|
|
||||||
background: #2563eb;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.register {
|
|
||||||
transform: translateX(100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-input {
|
.custom-input {
|
||||||
position: relative;
|
width: 100%;
|
||||||
background: #f3f4f6;
|
background: #f8fafc;
|
||||||
border-radius: 8px;
|
border: 2px solid transparent;
|
||||||
|
border-radius: 12px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s ease;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
&.focused {
|
&.focused {
|
||||||
background: white;
|
background: #fff;
|
||||||
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
|
border-color: #4080ff;
|
||||||
|
box-shadow: 0 0 0 4px rgba(64, 128, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-icon {
|
.el-icon {
|
||||||
font-size: 20px;
|
font-size: 18px;
|
||||||
color: #666;
|
color: #94a3b8;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #333;
|
color: #1e293b;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: #999;
|
color: #94a3b8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-toggle {
|
.password-toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 12px;
|
color: #94a3b8;
|
||||||
|
transition: color 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #2563eb;
|
color: #4080ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-left: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -494,28 +529,47 @@ const forgotPassword = () => {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 20px 0;
|
margin: 24px 0;
|
||||||
|
|
||||||
|
.el-checkbox {
|
||||||
|
.el-checkbox__label {
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
color: #4080ff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 42px;
|
height: 44px;
|
||||||
font-size: 15px;
|
font-size: 16px;
|
||||||
border-radius: 8px;
|
font-weight: 500;
|
||||||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
border-radius: 12px;
|
||||||
|
background: linear-gradient(135deg, #4080ff 0%, #2563eb 100%);
|
||||||
border: none;
|
border: none;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
|
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 30px 0;
|
margin: 32px 0;
|
||||||
|
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -524,25 +578,56 @@ const forgotPassword = () => {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
width: calc(50% - 30px);
|
width: calc(50% - 30px);
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: #ddd;
|
background: #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
&::before { left: 0; }
|
||||||
left: 0;
|
&::after { right: 0; }
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
span {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 0 10px;
|
padding: 0 12px;
|
||||||
color: #666;
|
color: #64748b;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-tabs {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 2px solid #e2e8f0;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #64748b;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #4080ff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-line {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
height: 2px;
|
||||||
|
background: #4080ff;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
|
||||||
|
&.register {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.social-login {
|
.social-login {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -589,7 +674,6 @@ const forgotPassword = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动画效果
|
|
||||||
@keyframes float {
|
@keyframes float {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
transform: translate(0, 0);
|
transform: translate(0, 0);
|
||||||
|
@ -608,11 +692,10 @@ const forgotPassword = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式设计
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.login-container {
|
.login-container {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|
||||||
.login-content {
|
.login-content {
|
||||||
.login-header {
|
.login-header {
|
||||||
.logo-wrapper {
|
.logo-wrapper {
|
||||||
|
@ -629,10 +712,10 @@ const forgotPassword = () => {
|
||||||
|
|
||||||
.login-box {
|
.login-box {
|
||||||
padding: 25px 20px;
|
padding: 25px 20px;
|
||||||
|
|
||||||
.custom-input {
|
.custom-input {
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
@ -646,4 +729,4 @@ const forgotPassword = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue