feat: 新增依赖
This commit is contained in:
@@ -2,9 +2,13 @@ import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import pinia from "./store";
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
import "./style.css";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(ElementPlus);
|
||||
app.use(router);
|
||||
app.use(pinia);
|
||||
app.mount("#app");
|
||||
|
||||
@@ -1,58 +1,92 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gradient-to-br from-purple-50 to-blue-50 flex items-center justify-center p-4">
|
||||
<div
|
||||
class="min-h-screen bg-gray-50 flex items-center justify-center p-4"
|
||||
>
|
||||
<div class="w-full max-w-md">
|
||||
<!-- 登录卡片 -->
|
||||
<div class="bg-white rounded-2xl shadow-xl p-8">
|
||||
<!-- 头部 -->
|
||||
<div class="bg-white rounded-lg shadow-sm border p-8 w-full max-w-md">
|
||||
<!-- 头部区域 -->
|
||||
<div class="text-center mb-8">
|
||||
<div
|
||||
class="w-16 h-16 bg-gradient-to-r from-purple-500 to-blue-500 rounded-full mx-auto mb-4 flex items-center justify-center">
|
||||
<div class="w-8 h-8 bg-white rounded-full flex items-center justify-center">
|
||||
<div class="w-4 h-4 bg-gradient-to-r from-purple-500 to-blue-500 rounded-full"></div>
|
||||
<!-- Logo -->
|
||||
<div class="mb-6">
|
||||
<div class="w-16 h-16 bg-blue-600 rounded-xl flex items-center justify-center mx-auto">
|
||||
<span class="text-white font-bold text-2xl">F</span>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-gray-800 mb-2">欢迎来到 Fellou</h1>
|
||||
<p class="text-gray-500 text-sm">您的隐私对我们很重要,我们确保您的数据安全可靠</p>
|
||||
|
||||
<!-- 标题 -->
|
||||
<h1 class="text-2xl font-bold text-gray-900 mb-2">欢迎回来</h1>
|
||||
<p class="text-gray-600">登录您的账户</p>
|
||||
</div>
|
||||
|
||||
<!-- 登录方式切换 -->
|
||||
<div class="flex mb-6">
|
||||
<button @click="loginType = 'code'" :class="[
|
||||
'flex-1 py-2 px-4 text-sm font-medium border-b-2 transition-colors',
|
||||
loginType === 'code'
|
||||
? 'text-blue-600 border-blue-600'
|
||||
: 'text-gray-500 border-transparent hover:text-gray-700'
|
||||
]">
|
||||
<div class="flex mb-8 bg-gray-50 rounded-lg p-1">
|
||||
<button
|
||||
@click="loginType = 'code'"
|
||||
:class="[
|
||||
'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all',
|
||||
loginType === 'code'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-700'
|
||||
]"
|
||||
>
|
||||
验证码登录
|
||||
</button>
|
||||
<button @click="loginType = 'password'" :class="[
|
||||
'flex-1 py-2 px-4 text-sm font-medium border-b-2 transition-colors',
|
||||
loginType === 'password'
|
||||
? 'text-blue-600 border-blue-600'
|
||||
: 'text-gray-500 border-transparent hover:text-gray-700'
|
||||
]">
|
||||
<button
|
||||
@click="loginType = 'password'"
|
||||
:class="[
|
||||
'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all',
|
||||
loginType === 'password'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-700'
|
||||
]"
|
||||
>
|
||||
密码登录
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" @submit.prevent="handleLogin"
|
||||
class="space-y-4">
|
||||
<el-form
|
||||
ref="loginFormRef"
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
@submit.prevent="handleLogin"
|
||||
class="space-y-3"
|
||||
>
|
||||
<!-- 邮箱输入框 -->
|
||||
<el-form-item prop="email" class="mb-4">
|
||||
<el-input v-model="loginForm.email" type="email" placeholder="请输入邮箱" size="large" class="w-full"
|
||||
:prefix-icon="User" @blur="validateField('email')" />
|
||||
<el-form-item prop="email">
|
||||
<el-input
|
||||
v-model="loginForm.email"
|
||||
type="email"
|
||||
placeholder="请输入邮箱"
|
||||
size="default"
|
||||
class="w-full"
|
||||
:prefix-icon="User"
|
||||
@blur="validateField('email')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 验证码登录 -->
|
||||
<div v-if="loginType === 'code'" class="space-y-4">
|
||||
<el-form-item prop="verifyCode" class="mb-4">
|
||||
<div class="flex space-x-2">
|
||||
<el-input v-model="loginForm.verifyCode" placeholder="请输入4位验证码" size="large" class="flex-1"
|
||||
:prefix-icon="Shield" maxlength="4" @blur="validateField('verifyCode')" />
|
||||
<el-button type="primary" size="large" :disabled="!isEmailValid || countdown > 0"
|
||||
@click="sendVerifyCode" class="px-6">
|
||||
{{ countdown > 0 ? `${countdown}s` : '发送验证码' }}
|
||||
<div v-if="loginType === 'code'">
|
||||
<el-form-item prop="verifyCode">
|
||||
<div class="flex gap-2">
|
||||
<el-input
|
||||
v-model="loginForm.verifyCode"
|
||||
placeholder="请输入验证码"
|
||||
size="default"
|
||||
class="flex-1"
|
||||
:prefix-icon="Timer"
|
||||
maxlength="4"
|
||||
@blur="validateField('verifyCode')"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
:disabled="!isEmailValid || countdown > 0"
|
||||
@click="sendVerifyCode"
|
||||
class="min-w-[80px]"
|
||||
>
|
||||
{{ countdown > 0 ? `${countdown}s` : "获取" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -60,12 +94,21 @@
|
||||
|
||||
<!-- 密码登录 -->
|
||||
<div v-if="loginType === 'password'">
|
||||
<el-form-item prop="password" class="mb-4">
|
||||
<el-input v-model="loginForm.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码"
|
||||
size="large" class="w-full" :prefix-icon="Lock" @blur="validateField('password')">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="请输入密码"
|
||||
size="default"
|
||||
class="w-full"
|
||||
:prefix-icon="Lock"
|
||||
@blur="validateField('password')"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon @click="showPassword = !showPassword"
|
||||
class="cursor-pointer text-gray-400 hover:text-gray-600">
|
||||
<el-icon
|
||||
@click="showPassword = !showPassword"
|
||||
class="cursor-pointer text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<View v-if="showPassword" />
|
||||
<Hide v-else />
|
||||
</el-icon>
|
||||
@@ -74,30 +117,56 @@
|
||||
</el-form-item>
|
||||
|
||||
<!-- 忘记密码 -->
|
||||
<div class="text-right mb-4">
|
||||
<el-link type="primary" :underline="false" @click="handleForgotPassword">
|
||||
<div class="text-right mb-2">
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="handleForgotPassword"
|
||||
class="text-xs"
|
||||
>
|
||||
忘记密码?
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协议同意 -->
|
||||
<el-form-item prop="agreed" class="mb-6">
|
||||
<el-checkbox v-model="loginForm.agreed" @change="validateField('agreed')">
|
||||
<span class="text-sm text-gray-600">
|
||||
我已阅读并同意 Fellou
|
||||
<el-link type="primary" :underline="false" @click="handlePrivacyPolicy">隐私政策</el-link>
|
||||
<el-form-item prop="agreed">
|
||||
<el-checkbox
|
||||
v-model="loginForm.agreed"
|
||||
@change="validateField('agreed')"
|
||||
class="text-xs"
|
||||
>
|
||||
<span class="text-gray-600">
|
||||
我已阅读并同意
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="handlePrivacyPolicy"
|
||||
class="text-xs"
|
||||
>隐私政策</el-link
|
||||
>
|
||||
和
|
||||
<el-link type="primary" :underline="false" @click="handleServiceTerms">平台服务条款</el-link>
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="handleServiceTerms"
|
||||
class="text-xs"
|
||||
>服务条款</el-link
|
||||
>
|
||||
</span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" :loading="loading" @click="handleLogin"
|
||||
class="w-full bg-gradient-to-r from-purple-600 to-blue-600 border-0 hover:from-purple-700 hover:to-blue-700">
|
||||
{{ loading ? '登录中...' : '注册 / 登录' }}
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
:loading="loading"
|
||||
@click="handleLogin"
|
||||
class="w-full"
|
||||
>
|
||||
{{ loading ? "登录中..." : "登录" }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -107,155 +176,154 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { User, Lock, Shield, View, Hide } from '@element-plus/icons-vue'
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { User, Lock, Timer, View, Hide } from "@element-plus/icons-vue";
|
||||
|
||||
// 登录类型
|
||||
const loginType = ref('code')
|
||||
const loginType = ref("code");
|
||||
|
||||
// 表单引用
|
||||
const loginFormRef = ref()
|
||||
const loginFormRef = ref();
|
||||
|
||||
// 表单数据
|
||||
const loginForm = reactive({
|
||||
email: '',
|
||||
password: '',
|
||||
verifyCode: '',
|
||||
agreed: false
|
||||
})
|
||||
email: "",
|
||||
password: "",
|
||||
verifyCode: "",
|
||||
agreed: false,
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const loginRules = reactive({
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
{ required: true, message: "请输入邮箱", trigger: "blur" },
|
||||
{ type: "email", message: "请输入正确的邮箱格式", trigger: "blur" },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码长度不能少于6位", trigger: "blur" },
|
||||
],
|
||||
verifyCode: [
|
||||
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||
{ len: 4, message: '验证码必须为4位', trigger: 'blur' }
|
||||
{ required: true, message: "请输入验证码", trigger: "blur" },
|
||||
{ len: 4, message: "验证码必须为4位", trigger: "blur" },
|
||||
],
|
||||
agreed: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请同意隐私政策和服务条款'))
|
||||
callback(new Error("请同意隐私政策和服务条款"));
|
||||
} else {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
})
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
const showPassword = ref(false)
|
||||
const countdown = ref(0)
|
||||
const loading = ref(false);
|
||||
const showPassword = ref(false);
|
||||
const countdown = ref(0);
|
||||
|
||||
// 计算属性
|
||||
const isEmailValid = computed(() => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return emailRegex.test(loginForm.email)
|
||||
})
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(loginForm.email);
|
||||
});
|
||||
|
||||
// 验证码倒计时
|
||||
const startCountdown = () => {
|
||||
countdown.value = 60
|
||||
countdown.value = 60;
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 发送验证码
|
||||
const sendVerifyCode = async () => {
|
||||
if (!isEmailValid.value) {
|
||||
ElMessage.error('请先输入正确的邮箱地址')
|
||||
return
|
||||
ElMessage.error("请先输入正确的邮箱地址");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 这里调用发送验证码的API
|
||||
ElMessage.success('验证码已发送到您的邮箱')
|
||||
startCountdown()
|
||||
ElMessage.success("验证码已发送到您的邮箱");
|
||||
startCountdown();
|
||||
} catch (error) {
|
||||
ElMessage.error('验证码发送失败,请重试')
|
||||
ElMessage.error("验证码发送失败,请重试");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 验证单个字段
|
||||
const validateField = (field) => {
|
||||
loginFormRef.value?.validateField(field)
|
||||
}
|
||||
loginFormRef.value?.validateField(field);
|
||||
};
|
||||
|
||||
// 处理登录
|
||||
const handleLogin = async () => {
|
||||
if (!loginFormRef.value) return
|
||||
if (!loginFormRef.value) return;
|
||||
|
||||
try {
|
||||
const valid = await loginFormRef.value.validate()
|
||||
if (!valid) return
|
||||
const valid = await loginFormRef.value.validate();
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
|
||||
// 根据登录类型构建请求数据
|
||||
const loginData = {
|
||||
email: loginForm.email,
|
||||
agreed: loginForm.agreed
|
||||
}
|
||||
agreed: loginForm.agreed,
|
||||
};
|
||||
|
||||
if (loginType.value === 'password') {
|
||||
loginData.password = loginForm.password
|
||||
if (loginType.value === "password") {
|
||||
loginData.password = loginForm.password;
|
||||
} else {
|
||||
loginData.verifyCode = loginForm.verifyCode
|
||||
loginData.verifyCode = loginForm.verifyCode;
|
||||
}
|
||||
|
||||
// 这里调用登录API
|
||||
console.log('登录数据:', loginData)
|
||||
console.log("登录数据:", loginData);
|
||||
|
||||
// 模拟登录请求
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
ElMessage.success('登录成功')
|
||||
ElMessage.success("登录成功");
|
||||
|
||||
// 登录成功后的跳转逻辑
|
||||
// router.push('/home')
|
||||
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
ElMessage.error('登录失败,请重试')
|
||||
console.error("登录失败:", error);
|
||||
ElMessage.error("登录失败,请重试");
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 忘记密码
|
||||
const handleForgotPassword = () => {
|
||||
ElMessage.info('忘记密码功能开发中')
|
||||
}
|
||||
ElMessage.info("忘记密码功能开发中");
|
||||
};
|
||||
|
||||
// 隐私政策
|
||||
const handlePrivacyPolicy = () => {
|
||||
ElMessage.info('隐私政策页面开发中')
|
||||
}
|
||||
ElMessage.info("隐私政策页面开发中");
|
||||
};
|
||||
|
||||
// 服务条款
|
||||
const handleServiceTerms = () => {
|
||||
ElMessage.info('服务条款页面开发中')
|
||||
}
|
||||
ElMessage.info("服务条款页面开发中");
|
||||
};
|
||||
|
||||
// 页面加载时自动聚焦到邮箱输入框
|
||||
onMounted(() => {
|
||||
// 可以在这里添加自动聚焦逻辑
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -292,4 +360,4 @@ onMounted(() => {
|
||||
.el-button--primary.bg-gradient-to-r:hover {
|
||||
background: linear-gradient(to right, #7c3aed, #1d4ed8) !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user