276 lines
7.1 KiB
Vue
276 lines
7.1 KiB
Vue
<script setup lang="ts">
|
|
import type { FormInst, FormRules } from 'naive-ui'
|
|
import phoneImg from '@/assets/img/phone.png'
|
|
import wechatImg from '@/assets/img/wechat.png'
|
|
import { createDiscreteApi } from 'naive-ui'
|
|
import { ref } from 'vue'
|
|
|
|
const userStore = useUserStore()
|
|
const { message } = createDiscreteApi(['message'])
|
|
// 暴露方法给父组件
|
|
defineExpose({
|
|
showModal,
|
|
})
|
|
const regex = /^1[3-9]\d{9}$/
|
|
const rules: FormRules = {
|
|
phone: [
|
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
|
{ pattern: regex, message: '手机号格式不正确', trigger: 'blur' },
|
|
],
|
|
code: [
|
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
|
{ len: 6, message: '验证码长度为6位', trigger: 'blur' },
|
|
],
|
|
}
|
|
// 响应式状态
|
|
const isVisible = ref(false)
|
|
const formRef = ref<FormInst | null>(null)
|
|
type LoginMode = 'wechat' | 'phone'
|
|
const currentLoginMode = ref<LoginMode>('phone')
|
|
const codeButtonText = ref('获取验证码')
|
|
const codeButtonDisabled = ref(false)
|
|
|
|
const timerCode: Ref<ReturnType<typeof setInterval> | null> = ref(null)
|
|
interface UserData {
|
|
code: number
|
|
phone: number
|
|
}
|
|
interface ApiResponse<T> {
|
|
code: number
|
|
msg: string
|
|
token: string
|
|
}
|
|
const formData = ref({
|
|
|
|
})
|
|
interface LoginModeDescription {
|
|
title: string
|
|
des: string
|
|
src: string
|
|
}
|
|
|
|
type LoginModeDescriptions = Record<LoginMode, LoginModeDescription>
|
|
// 登录模式配置
|
|
const loginModeDescriptions: LoginModeDescriptions = {
|
|
phone: {
|
|
title: '登录',
|
|
des: '如未注册,验证后自动登录',
|
|
src: wechatImg,
|
|
},
|
|
wechat: {
|
|
title: '微信一键登录',
|
|
des: '关注后自动登录',
|
|
src: phoneImg,
|
|
},
|
|
}
|
|
|
|
function showModal() {
|
|
if (isVisible.value)
|
|
return
|
|
isVisible.value = true
|
|
}
|
|
|
|
function onCloseLogin() {
|
|
formData.value = {}
|
|
isVisible.value = false
|
|
}
|
|
|
|
// 切换登录方式
|
|
function toggleMode() {
|
|
return currentLoginMode.value === 'phone' ? 'wechat' : 'phone'
|
|
}
|
|
function setCurrentLoginMode() {
|
|
currentLoginMode.value = toggleMode()
|
|
}
|
|
interface UserToken {
|
|
token: string
|
|
}
|
|
// 登录
|
|
async function handleValidateClick(e: MouseEvent) {
|
|
e.preventDefault()
|
|
formRef.value?.validate(async (errors: any) => {
|
|
if (!errors) {
|
|
const res = await request.post<ApiResponse<UserData>>('/phoneLogin', {
|
|
...formData.value,
|
|
})
|
|
await userStore.setToken(res.token)
|
|
await userStore.getUserInfo()
|
|
// const res1 = await request.get<ApiResponse<UserToken>>('/getInfo', {
|
|
// token: res.token,
|
|
// })
|
|
// await userStore.getUserInfo()
|
|
// const res1 = await request.get('/system/user/selectUserById', {
|
|
// token: res.token,
|
|
// })
|
|
// debugger
|
|
// userStore.setUserInfo(res1.user)
|
|
onCloseLogin()
|
|
window.location.reload()
|
|
// window.location.href = '/'
|
|
}
|
|
else {
|
|
console.log(errors)
|
|
}
|
|
})
|
|
}
|
|
|
|
interface codeInterface {
|
|
phone: number
|
|
}
|
|
|
|
// 获取验证码
|
|
async function onGetCode() {
|
|
if (!regex.test(formData.value.phone))
|
|
return
|
|
try {
|
|
const response = await request.get<codeInterface>('/getCode', {
|
|
params: {
|
|
phone: formData.value.phone,
|
|
},
|
|
})
|
|
if (response.code === 200) {
|
|
codeButtonDisabled.value = true
|
|
let countdown = 60
|
|
codeButtonText.value = `${countdown}秒后重试`
|
|
|
|
if (timerCode.value)
|
|
clearInterval(timerCode.value)
|
|
|
|
timerCode.value = setInterval(() => {
|
|
countdown--
|
|
if (countdown <= 0) {
|
|
clearInterval(timerCode.value)
|
|
codeButtonDisabled.value = false
|
|
codeButtonText.value = '获取验证码'
|
|
}
|
|
else {
|
|
codeButtonText.value = `${countdown}秒后重试`
|
|
}
|
|
}, 1000)
|
|
}
|
|
else {
|
|
message.error(response.message || '获取验证码失败!')
|
|
}
|
|
}
|
|
catch (err) {
|
|
console.error(err)
|
|
}
|
|
}
|
|
function onCloseModel() {
|
|
formData.value = {}
|
|
if (timerCode.value) {
|
|
clearInterval(timerCode.value)
|
|
}
|
|
}
|
|
|
|
// 组件销毁时清理定时器
|
|
onUnmounted(() => {
|
|
formData.value = {}
|
|
if (timerCode.value) {
|
|
clearInterval(timerCode.value)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<NModal
|
|
v-model:show="isVisible"
|
|
:on-after-leave="onCloseModel"
|
|
preset="card"
|
|
style="width: 400px"
|
|
:mask-closable="false"
|
|
>
|
|
<div>
|
|
<!-- 关闭按钮 -->
|
|
<!-- <div
|
|
class="center fixed right-5 top-5 z-999 h-5 w-5 cursor-pointer b-white" @click="onCloseLogin(false)"
|
|
>
|
|
<NIcon size="30" class="text-white">
|
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"><g><g><polygon
|
|
points="405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798
|
|
375.202,405 405,375.202 285.798,256"
|
|
/></g></g></svg>
|
|
</NIcon>
|
|
</div> -->
|
|
|
|
<!-- 登录表单区域 -->
|
|
<div>
|
|
<h2 class="text-center text-xl text-gray-800 font-bold">
|
|
{{ loginModeDescriptions[currentLoginMode].title }}
|
|
</h2>
|
|
<p class="text-center text-sm text-gray-500 my-2">
|
|
{{ loginModeDescriptions[currentLoginMode].des }}
|
|
</p>
|
|
|
|
<!-- 手机号登录表单 -->
|
|
<n-form
|
|
v-if="currentLoginMode === 'phone'"
|
|
ref="formRef"
|
|
:model="formData"
|
|
:rules="rules"
|
|
label-placement="left"
|
|
label-width="70px"
|
|
size="large"
|
|
>
|
|
<n-form-item label="手机号" path="phone">
|
|
<n-input v-model:value="formData.phone" placeholder="输入手机号" />
|
|
</n-form-item>
|
|
|
|
<n-form-item label="密码" path="code">
|
|
<n-input-group>
|
|
<n-input v-model:value="formData.code" placeholder="输入密码" />
|
|
<n-button
|
|
type="primary"
|
|
:disabled="codeButtonDisabled"
|
|
ghost
|
|
@click="onGetCode($event)"
|
|
>
|
|
{{ codeButtonText }}
|
|
</n-button>
|
|
</n-input-group>
|
|
</n-form-item>
|
|
|
|
<n-form-item>
|
|
<n-button
|
|
type="primary"
|
|
block
|
|
attr-type="button"
|
|
@click="handleValidateClick"
|
|
>
|
|
登录
|
|
</n-button>
|
|
</n-form-item>
|
|
</n-form>
|
|
|
|
<!-- 微信登录区域 -->
|
|
<div v-else>
|
|
<div class="flex justify-center">
|
|
<WechatLoginQr />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 其他登录方式 -->
|
|
<div class="text-center">
|
|
<div class="text-sm text-gray-400">
|
|
其他登录方式
|
|
</div>
|
|
<div class="mt-2 flex items-center justify-center gap-6">
|
|
<div
|
|
class="cursor-pointer rounded-full p-2 transition-all hover:bg-gray-100"
|
|
@click="setCurrentLoginMode"
|
|
>
|
|
<img
|
|
:src="loginModeDescriptions[currentLoginMode].src"
|
|
alt=""
|
|
class="h-10 w-10"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</NModal>
|
|
</template>
|
|
|
|
<style scoped></style>
|