mcwl-pc/app/components/LoginModal.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>