feat: 新增依赖

This commit is contained in:
duanshuwen
2025-09-23 18:57:14 +08:00
parent c77c5350aa
commit 089a86c66b
8 changed files with 1087 additions and 126 deletions

View File

@@ -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");

View File

@@ -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>