foreign-admin-ui/src/views/user/Login.vue

287 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="user-card">
<UserHeader />
<UserText :user-text="userText" @back="handleBack" />
<UserTabs v-model="activeTab" :hide-tab="hideTab" :tab-list="tabList" />
<el-form class="user-form" ref="loginFormRef" :model="loginForm" :rules="loginRules" @submit.prevent="handleLogin">
<Transition name="form-switch" mode="out-in">
<el-form-item v-if="activeTab === 'email'" key="form-email-item" prop="email">
<el-input v-model="loginForm.email" placeholder="请输入邮箱" clearable size="large" />
</el-form-item>
<el-form-item
class="phone-item"
prop="phone"
v-else-if="activeTab === 'phone'"
key="form-phone-item"
>
<el-select
v-model="loginForm.countryCode"
:teleported="false"
value-key="code"
placeholder="国家"
size="large"
>
<el-option
v-for="item in countryOptions"
:key="item.code"
:label="item.code"
:value="item.code"
>
<div class="country-option">
<span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span>
</div>
</el-option>
</el-select>
<el-input v-model="loginForm.phone" placeholder="请输入手机号" clearable size="large" @keyup.enter.prevent="handleLogin"/>
</el-form-item>
<el-form-item v-else-if="activeTab === 'password'" key="form-password-item" prop="password">
<el-input
ref="passwordInputRef"
v-model="loginForm.password"
placeholder="请输入密码"
type="password"
show-password
clearable
size="large"
/>
<router-link to="/user/forgot-password" class="forgot-password">忘记密码?</router-link>
</el-form-item>
</Transition>
<el-form-item class="form-submit">
<el-button type="primary" @click="handleLogin" :loading="activeTab === 'password' && buttonLoading">登录</el-button>
</el-form-item>
<router-link to="/user/register" class="to-register">没有账号?去注册</router-link>
</el-form>
</div>
</template>
<script lang="ts" setup>
defineOptions({
name: 'Login'
})
// type类型
import type { FormInstance, FormRules } from 'element-plus'
import { useRouter } from 'vue-router'
// 组件
import UserHeader from './components/Header.vue'
import UserText from './components/Text.vue'
import UserTabs from './components/Tabs.vue'
//公共方法/store/配置文件
import { useUserStore } from '@/stores/modules/user'
import { useButtonLoading } from '@/composables/useLoading'
import { setStorage, getStorage } from '@/utils/storage'
import { md5Encrypt } from '@/utils/crypto'
//用户校验规则
import { countryOptions } from '@/views/user/config/mock'
import { passwordRules, emailRules, phoneRules, codeRules } from './config/rules'
const router = useRouter()
// 按钮 loading
const { buttonLoading, startButtonLoading, stopButtonLoading } = useButtonLoading()
//userText 实例
const userText = ref({
backText: '',
title: '',
text: '初次登录时,您的电子邮箱或手机号码将被用于注册。',
account: '',
})
//tabs 实例
const tabList = ref([
{ label: '邮箱', name: 'email' },
{ label: '手机', name: 'phone' },
])
const activeTab = ref<string>('email')
const hideTab = ref<boolean>(false)
// login form 实例
const userStore = useUserStore()
const loginFormRef = ref<FormInstance>()
const passwordInputRef = ref()
const loginForm = ref({
email: '',
countryCode: '+86',
phone: '',
password: '',
code: '123456',
})
const loginRules = ref<FormRules>({
email: emailRules().email,
phone: phoneRules(loginForm.value).phone,
code: codeRules().code,
password: passwordRules(loginForm.value).password,
})
//方法
/**
* 生成时间戳拼接6位随机数字的字符串
* @returns {string} 格式示例1710688800000123456
*/
function generateTimestampWithRandomNum() {
// 1. 获取当前时间戳(毫秒级)
const timestamp = Date.now()
// 2. 生成6位随机数字范围000000 ~ 999999
// Math.random()生成0~1的随机数乘以1000000后取整不足6位补0
const random6Num = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, '0')
// 3. 拼接并返回
return timestamp + random6Num
}
// 登录
const handleLogin = async () => {
console.log('触发登录');
if (!loginFormRef.value) return
try {
const currentTab = activeTab.value
//验证邮箱/手机号 → 切换到密码页
if (currentTab === 'email' || currentTab === 'phone') {
//校验当前账号字段email/phone
await loginFormRef.value.validateField(currentTab)
//存储账号类型和账号值(方便后续登录取值)
const loginValue =
currentTab === 'phone'
? loginForm.value.countryCode + loginForm.value[currentTab]
: loginForm.value[currentTab]
setStorage('loginType', currentTab, 'session') // 存储账号类型email/phone
setStorage('login', loginValue, 'session') // 存储账号值
//切换到密码页,更新提示
hideTab.value = true
userText.value = {
backText: '返回',
title: '欢迎回来!',
text: '请输入您的登录密码',
account: loginValue,
}
activeTab.value = 'password'
if (currentTab === 'phone' && loginForm.value.email !== '') {
loginForm.value.email = ''
}
// 切换到密码页后自动聚焦密码输入框
setTimeout(() => {
passwordInputRef.value?.focus()
},400)
}
// 验证密码+验证码 → 执行登录
else if (currentTab === 'password') {
// 校验密码+验证码
await loginFormRef.value.validateField(['password', 'code'])
// 开启 loading
startButtonLoading()
try {
// 生成device_no并保存到变量关键修改1
const deviceNo = generateTimestampWithRandomNum()
// 登录参数
const loginParams = {
email: loginForm.value.email,
mobile: loginForm.value.countryCode + loginForm.value.phone,
login_type: getStorage('loginType', 'session') === 'email' ? 1 : 2,
code: loginForm.value.code,
password: loginForm.value.password,
device_no: deviceNo,
}
await userStore.login(loginParams)
setStorage('device_no', deviceNo)
} finally {
// 关闭 loading
stopButtonLoading()
}
}
} catch (error) {
console.error('表单校验失败:', error)
}
}
//返回登录页
const handleBack = () => {
if (activeTab.value === 'password') {
// 从sessionStorage取账号类型而非用password作为key
const loginType = getStorage('loginType', 'session') || 'email'
// 清空密码、验证码字段,避免残留
loginForm.value.password = ''
loginForm.value.code = ''
// 清空表单校验提示
loginFormRef.value?.clearValidate(['password', 'code'])
// 重置文本和tab状态
hideTab.value = false
userText.value = {
backText: '',
title: '',
text: '初次登录时,您的电子邮箱或手机号码将被用于注册。',
account: '',
}
activeTab.value = loginType as string
} else {
// 非password页仅重置为email可选也可保留当前tab
activeTab.value = 'email'
// 清空当前账号字段和校验提示
loginForm.value.email = ''
loginForm.value.phone = ''
loginFormRef.value?.clearValidate(['email', 'phone'])
}
}
</script>
<style scoped lang="scss">
.user-form {
.phone-item {
:deep(.el-form-item__content) {
flex-wrap: nowrap;
}
.el-select {
width: 120px;
margin-right: 10px;
.country-option {
width: 120px;
display: flex;
justify-content: space-between;
}
:deep(.el-select-dropdown__item) {
padding: 0 16px 0 16px;
}
}
}
.forgot-password {
font-size: 14px;
color: #409eff;
text-decoration: none;
}
.form-submit {
margin-top: 18px;
:deep(.el-form-item__content) {
width: 100%;
}
}
.to-register {
display: flex;
justify-content: center;
text-decoration: none;
color: #409eff;
margin-top: 24px;
}
}
</style>