登录注册
|
|
@ -0,0 +1,5 @@
|
||||||
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
VITE_API_BASE_URL = 'http://test-admin.haituaigc.com'
|
||||||
|
|
||||||
|
VITE_API_CHAT_URL = 'http://openai-test.haituaigc.com'
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
VITE_API_BASE_URL = 'http://test-admin.haituaigc.com'
|
||||||
|
|
||||||
|
VITE_API_CHAT_URL = 'http://openai-test.haituaigc.com'
|
||||||
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,371 @@
|
||||||
|
<template>
|
||||||
|
<div class="loginBox">
|
||||||
|
<div class="banner">
|
||||||
|
<h2>您好!欢迎使用海兔AIGC!</h2>
|
||||||
|
<p>请输入您的相关信息进行登录!</p>
|
||||||
|
</div>
|
||||||
|
<ul class="tabBox">
|
||||||
|
<li :class="{ active: loginType == 'tel' }" @click="loginType = 'tel'"><i class="icon-code"></i>短信验证</li>
|
||||||
|
<li :class="{ active: loginType == 'email' }" @click="loginType = 'email'"><i class="icon-email"></i>邮箱验证</li>
|
||||||
|
<li :class="{ active: loginType == 'password' }" @click="loginType = 'password'"><i class="icon-pwd"></i>密码登录
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="formBox" v-if="loginType === 'tel'">
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">手机号码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field type="tel" :clearable="true" class="txt" v-model="tel" placeholder="请输入手机号码" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">验证码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field type="digit" :clearable="true" class="stxt" v-model="msgCode" placeholder="请输入验证码" />
|
||||||
|
<van-button class="sbtn" type="primary" :disabled="telCountDown !== getCodeTime" @click="getTelCode">{{
|
||||||
|
telBtnTxt }}</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formBox" v-if="loginType === 'email'">
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">邮箱</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field :clearable="true" class="txt" v-model="email" placeholder="请输入邮箱" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">验证码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field :clearable="true" class="stxt" v-model="emailCode" placeholder="请输入验证码" />
|
||||||
|
<van-button class="sbtn" type="primary" :disabled="emailCountDown !== getCodeTime"
|
||||||
|
@click="getEmailCode">{{ emailBtnTxt }}</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formBox" v-if="loginType === 'password'">
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">手机号/邮箱</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field :clearable="true" class="txt" v-model="account" placeholder="请输入手机号/邮箱" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">密码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field type="password" class="pwdInput" v-model="password" placeholder="请输入密码" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="submitBtn">
|
||||||
|
<van-button type="primary" @click="onLogin">登录</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useAuthModal } from '@/stores/authModal';
|
||||||
|
import { useUserInfo } from '@/stores/userInfo';
|
||||||
|
import { showToast } from 'vant';
|
||||||
|
import http from '@/io/http';
|
||||||
|
import { localCache } from '@/io/cache';
|
||||||
|
import md5 from 'md5';
|
||||||
|
|
||||||
|
const authModal = useAuthModal();
|
||||||
|
const userInfo = useUserInfo();
|
||||||
|
|
||||||
|
const loginType = ref('tel');
|
||||||
|
const tel = ref();
|
||||||
|
const msgCode = ref();
|
||||||
|
const email = ref();
|
||||||
|
const emailCode = ref();
|
||||||
|
const account = ref();
|
||||||
|
const password = ref();
|
||||||
|
|
||||||
|
const getCodeTime = 60;
|
||||||
|
const telBtnTxt = ref('获取验证码');
|
||||||
|
const telCountDown = ref(getCodeTime);
|
||||||
|
const telTimer = ref(0);
|
||||||
|
const emailBtnTxt = ref('获取验证码');
|
||||||
|
const emailCountDown = ref(getCodeTime);
|
||||||
|
const emailTimer = ref(0);
|
||||||
|
|
||||||
|
const telReg = /^1[3-9]\d{9}$/;
|
||||||
|
const emailReg = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
|
||||||
|
const isGetTelCode = ref(false);
|
||||||
|
const isGetEmailCode = ref(false);
|
||||||
|
|
||||||
|
|
||||||
|
const getTelCode = () => {
|
||||||
|
if (!telReg.test(tel.value)) {
|
||||||
|
showToast('请输入正确的手机号!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(isGetTelCode.value){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isGetTelCode.value = true;
|
||||||
|
http('/api/sms/phone', { phone: tel.value, type: 'login' }, 'POST').then(res => {
|
||||||
|
reRenderTelGetcodebtn();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
}).finally(()=>{
|
||||||
|
isGetTelCode.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEmailCode = () => {
|
||||||
|
if (!emailReg.test(email.value)) {
|
||||||
|
showToast('请输入正确的邮箱地址!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isGetEmailCode.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isGetEmailCode.value = true;
|
||||||
|
|
||||||
|
http('/api/sms/email', { email: email.value, type: 'login' }).then(res => {
|
||||||
|
reRenderEmailGetcodebtn();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
}).finally(() => {
|
||||||
|
isGetEmailCode.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const reRenderTelGetcodebtn = () => {
|
||||||
|
let time = --telCountDown.value;
|
||||||
|
telBtnTxt.value = `${time}s后重新获取`;
|
||||||
|
if (time == 0) {
|
||||||
|
clearTimeout(telTimer.value);
|
||||||
|
telBtnTxt.value = '获取验证码';
|
||||||
|
telCountDown.value = getCodeTime;
|
||||||
|
} else {
|
||||||
|
telTimer.value = setTimeout(reRenderTelGetcodebtn, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const reRenderEmailGetcodebtn = () => {
|
||||||
|
let time = --emailCountDown.value;
|
||||||
|
emailBtnTxt.value = `${time}s后重新获取`;
|
||||||
|
if (time == 0) {
|
||||||
|
clearTimeout(emailTimer.value);
|
||||||
|
emailBtnTxt.value = '获取验证码';
|
||||||
|
emailCountDown.value = getCodeTime;
|
||||||
|
} else {
|
||||||
|
emailTimer.value = setTimeout(reRenderEmailGetcodebtn, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
if (loginType == 'tel') {
|
||||||
|
if (!telReg.test(tel.value)) {
|
||||||
|
showToast('请输入正确的手机号!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!msgCode.value) {
|
||||||
|
showToast('请输入短信验证码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loginType == 'email') {
|
||||||
|
if (!emailReg.test(email.value)) {
|
||||||
|
showToast('请输入正确的邮箱地址!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!emailCode.value) {
|
||||||
|
showToast('请输入邮箱验证码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLogin = () => {
|
||||||
|
if (!validate()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let formData = {};
|
||||||
|
switch (loginType.value) {
|
||||||
|
case 'tel':
|
||||||
|
formData.username = tel.value;
|
||||||
|
formData.code = msgCode.value;
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
formData.username = email.value;
|
||||||
|
formData.code = emailCode.value;
|
||||||
|
break;
|
||||||
|
case 'password':
|
||||||
|
formData.username = account.value;
|
||||||
|
formData.password = md5(password.value).toLocaleLowerCase();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
http('/api/auth/login', formData).then(res => {
|
||||||
|
let nowTime = Date.now();
|
||||||
|
let overTime = nowTime + res.data.expires_in * 1000;
|
||||||
|
let authData = { ...res.data, expires_time: overTime };
|
||||||
|
localCache.set('auth', authData);
|
||||||
|
|
||||||
|
authModal.hideAuthModal();
|
||||||
|
getUserInfo();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserInfo = () => {
|
||||||
|
http('/api/user/profile', {}, 'get').then(res => {
|
||||||
|
localCache.set('userInfo', res.data);
|
||||||
|
userInfo.updateUserInfo(res.data);
|
||||||
|
// window.location.reload();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loginBox {
|
||||||
|
position: relative;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 .6rem .6rem .6rem;
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: .3rem;
|
||||||
|
margin-bottom: .1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: .23rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabBox {
|
||||||
|
height: .5rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: .3rem 0;
|
||||||
|
box-sizing: content-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: .48rem;
|
||||||
|
border: .01rem solid #414548;
|
||||||
|
opacity: 0.4;
|
||||||
|
border-radius: .02rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: .23rem;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
color: #FFF;
|
||||||
|
border-color: #3662FE;
|
||||||
|
|
||||||
|
i.icon-code {
|
||||||
|
background-image: url('../../assets/images/icon_msg_h.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon-email {
|
||||||
|
background-image: url('../../assets/images/icon_email_h.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon-pwd {
|
||||||
|
background-image: url('../../assets/images/icon_pwd_h.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: .1rem;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
width: .3rem;
|
||||||
|
height: .3rem;
|
||||||
|
|
||||||
|
&.icon-code {
|
||||||
|
background-image: url('../../assets/images/icon_msg.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-email {
|
||||||
|
background-image: url('../../assets/images/icon_email.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-pwd {
|
||||||
|
background-image: url('../../assets/images/icon_pwd.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formBox {
|
||||||
|
.item {
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
line-height: .66rem;
|
||||||
|
font-size: .28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.val {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.txt,
|
||||||
|
.stxt,
|
||||||
|
.pwdInput {
|
||||||
|
border: .01rem solid rgba($color: #414548, $alpha: 0.4);
|
||||||
|
border-radius: .02rem;
|
||||||
|
background: none;
|
||||||
|
color: #FFF;
|
||||||
|
|
||||||
|
:deep(input) {
|
||||||
|
background: none;
|
||||||
|
color: #FFF;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.van-cell:after){
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbtn {
|
||||||
|
width: 4rem;
|
||||||
|
margin-left: .1rem;
|
||||||
|
font-size: .23rem;
|
||||||
|
|
||||||
|
&:disabled{
|
||||||
|
opacity: 1;
|
||||||
|
color: #FFF;
|
||||||
|
border: .02rem solid #FFF;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitBtn {
|
||||||
|
margin-top: 1rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
<template>
|
||||||
|
<div class="registerBox">
|
||||||
|
<div class="banner">
|
||||||
|
<h2>您好!欢迎使用海兔AIGC!</h2>
|
||||||
|
<p>请输入您的相关信息进行注册!</p>
|
||||||
|
</div>
|
||||||
|
<div class="formBox">
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">手机号/邮箱</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field :clearable="true" class="txt" v-model="account" placeholder="请输入手机号/邮箱" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">验证码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field :clearable="true" type="digit" class="stxt" v-model="code" placeholder="请输入验证码" />
|
||||||
|
<van-button class="sbtn" type="primary" :disabled="countDown !== getCodeTime" @click="getCode">{{ btnTxt
|
||||||
|
}}</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">密码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field type="password" class="pwdInput" v-model="password" placeholder="请输入密码" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="label">再次输入密码</div>
|
||||||
|
<div class="val">
|
||||||
|
<van-field type="password" class="pwdInput" v-model="checkPassword" placeholder="请再次输入密码" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="submitBtn">
|
||||||
|
<van-button type="primary" @click="onRegister">注册</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import http from '@/io/http';
|
||||||
|
import { showToast } from 'vant';
|
||||||
|
import { localCache } from '@/io/cache';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useAuthModal } from '@/stores/authModal';
|
||||||
|
import md5 from 'md5';
|
||||||
|
|
||||||
|
const authModal = useAuthModal();
|
||||||
|
const getCodeTime = 60;
|
||||||
|
const account = ref();
|
||||||
|
const code = ref();
|
||||||
|
const password = ref();
|
||||||
|
const checkPassword = ref();
|
||||||
|
const btnTxt = ref('获取验证码');
|
||||||
|
const countDown = ref(getCodeTime);
|
||||||
|
const timer = ref(0);
|
||||||
|
|
||||||
|
const getCode = () => {
|
||||||
|
let telReg = /^1[3-9]\d{9}$/;
|
||||||
|
let emailReg = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
if (!(telReg.test(account.value) || emailReg.test(account.value))) {
|
||||||
|
showToast('请输入正确的手机号或邮箱!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
http('/api/sms/send', { receiver: account.value, type: 'register' }).then(res => {
|
||||||
|
reRenderGetcodebtn();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const reRenderGetcodebtn = () => {
|
||||||
|
let time = --countDown.value;
|
||||||
|
btnTxt.value = `${time}s后重新获取`;
|
||||||
|
if (time == 0) {
|
||||||
|
clearTimeout(timer.value);
|
||||||
|
btnTxt.value = '获取验证码';
|
||||||
|
countDown.value = getCodeTime;
|
||||||
|
} else {
|
||||||
|
timer.value = setTimeout(reRenderGetcodebtn, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
let telReg = /^1[3-9]\d{9}$/;
|
||||||
|
let emailReg = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
if (!(telReg.test(account.value) || emailReg.test(account.value))) {
|
||||||
|
showToast('请输入正确的手机号或邮箱!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!code.value) {
|
||||||
|
showToast('请输入验证码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!password.value) {
|
||||||
|
showToast('请输入密码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checkPassword.value) {
|
||||||
|
showToast('请再次输入确认密码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (checkPassword.value !== password.value) {
|
||||||
|
showToast('两次密码不一致,请重新输入!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRegister = () => {
|
||||||
|
if (!validate()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
username: account.value,
|
||||||
|
code: code.value,
|
||||||
|
password: md5(password.value).toLocaleLowerCase(),
|
||||||
|
password_confirmation: md5(checkPassword.value).toLocaleLowerCase()
|
||||||
|
};
|
||||||
|
http('/api/auth/register', params).then(res => {
|
||||||
|
showToast('注册成功!请登录!');
|
||||||
|
localCache.set('auth', res.data);
|
||||||
|
authModal.setAuthModalType('login');
|
||||||
|
}).catch(err => {
|
||||||
|
showToast(err.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.registerBox {
|
||||||
|
position: relative;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 .6rem .6rem .6rem;
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: .3rem;
|
||||||
|
margin-bottom: .1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: .23rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formBox {
|
||||||
|
.item {
|
||||||
|
margin-top: 0.28rem;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
line-height: .5rem;
|
||||||
|
font-size: .28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.val {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.txt,
|
||||||
|
.stxt,
|
||||||
|
.pwdInput {
|
||||||
|
border: .01rem solid rgba($color: #414548, $alpha: 0.4);
|
||||||
|
border-radius: .02rem;
|
||||||
|
background: none;
|
||||||
|
color: #FFF;
|
||||||
|
|
||||||
|
:deep(input) {
|
||||||
|
background: none;
|
||||||
|
color: #FFF;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.van-cell:after){
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbtn {
|
||||||
|
width: 4rem;
|
||||||
|
margin-left: .1rem;
|
||||||
|
font-size: .23rem;
|
||||||
|
|
||||||
|
&:disabled{
|
||||||
|
opacity: 1;
|
||||||
|
color: #FFF;
|
||||||
|
border: .02rem solid #FFF;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitBtn {
|
||||||
|
margin-top: .7rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<van-popup v-model:show="authModal.isShow" closeable style="background: none;width:6.93rem;">
|
||||||
|
<div class="formBox" v-if="authModal.authModalType == 'login'">
|
||||||
|
<div class="actionFlag" @click="onMethodChange('register')"><span>注册</span></div>
|
||||||
|
<Login/>
|
||||||
|
</div>
|
||||||
|
<div class="formBox" v-if="authModal.authModalType == 'register'">
|
||||||
|
<div class="actionFlag" @click="onMethodChange('login')"><span>登录</span></div>
|
||||||
|
<Register/>
|
||||||
|
</div>
|
||||||
|
</van-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useAuthModal } from '@/stores/authModal';
|
||||||
|
import { useUserInfo } from '@/stores/userInfo';
|
||||||
|
import { localCache } from '@/io/cache';
|
||||||
|
import http from '@/io/http';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const authModal = useAuthModal();
|
||||||
|
const userInfo = useUserInfo();
|
||||||
|
|
||||||
|
const onMethodChange = (val) => {
|
||||||
|
authModal.setAuthModalType(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
authModal.setAuthModalType('login');
|
||||||
|
authModal.hideAuthModal();
|
||||||
|
};
|
||||||
|
const handleOk = () => {
|
||||||
|
authModal.setAuthModalType('login');
|
||||||
|
authModal.hideAuthModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.formBox{
|
||||||
|
position: relative;
|
||||||
|
line-height: 1;
|
||||||
|
padding-top: .5rem;
|
||||||
|
color: #FFF;
|
||||||
|
width: 100%;
|
||||||
|
background: #161718;
|
||||||
|
border-radius: .06rem;
|
||||||
|
.actionFlag{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
width: 1.45rem;
|
||||||
|
height: 1.4rem;
|
||||||
|
border-radius: .1rem 0 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #FFF;
|
||||||
|
cursor: pointer;
|
||||||
|
span{
|
||||||
|
font-size: 0.26rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
top: .22rem;
|
||||||
|
left: .16rem;
|
||||||
|
}
|
||||||
|
&::before{
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
top: -0.75rem;
|
||||||
|
left: -0.7rem;
|
||||||
|
width: 1.7rem;
|
||||||
|
height: 1.7rem;
|
||||||
|
background: #304FED;
|
||||||
|
border-radius: 10px;
|
||||||
|
transform: rotateZ(45deg);
|
||||||
|
box-shadow: 0 0 20px rgba($color: #304FED, $alpha: 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -16,6 +16,11 @@ const http = (url = '', data = {}, type = 'POST', otherConfig = {}) => {
|
||||||
promise = axios({
|
promise = axios({
|
||||||
method: type,
|
method: type,
|
||||||
url,
|
url,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
'Accept': 'application/json; charset=UTF-8',
|
||||||
|
'isLoading': config.isLoading,
|
||||||
|
},
|
||||||
params: data,
|
params: data,
|
||||||
timeout: config.timeout
|
timeout: config.timeout
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,35 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { localCache } from './cache';
|
import { localCache } from './cache';
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import hostAPI from '@/config/host.config'
|
|
||||||
import { showToast } from 'vant';
|
import { showToast } from 'vant';
|
||||||
|
import { useAuthModal } from '@/stores/authModal';
|
||||||
|
import hostAPI from '@/config/host.config'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
let _token = '';
|
let _token = '';
|
||||||
let requestCount = 0;//请求数量
|
let requestCount = 0;//请求数量
|
||||||
|
|
||||||
|
const authModal = useAuthModal();
|
||||||
|
|
||||||
|
const showLoading = ()=>{
|
||||||
|
if (requestCount === 0) {
|
||||||
|
let renderDom = document.createElement('div');
|
||||||
|
renderDom.style.cssText = 'width:100%; height: 100%; position: absolute;left:0;top:0;z-index:9999;background-color: rgba(0,0,0,0.5);display: flex;justify-content: center; align-items: center;'
|
||||||
|
renderDom.setAttribute('id', 'requestLoading');
|
||||||
|
renderDom.innerHTML = 'loading...';
|
||||||
|
document.body.appendChild(renderDom);
|
||||||
|
}
|
||||||
|
requestCount++;
|
||||||
|
}
|
||||||
|
const hideLoading = ()=>{
|
||||||
|
requestCount--;
|
||||||
|
if (requestCount === 0) {
|
||||||
|
document.body.removeChild(document.getElementById('requestLoading'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// axios.defaults.baseURL = '//' + window.location.host;
|
// axios.defaults.baseURL = '//' + window.location.host;
|
||||||
axios.defaults.baseURL = hostAPI;
|
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL;
|
||||||
|
|
||||||
// 请求超时时间为10秒
|
// 请求超时时间为10秒
|
||||||
axios.defaults.timeout = 10000;
|
axios.defaults.timeout = 10000;
|
||||||
|
|
@ -23,7 +43,6 @@ export default function () {
|
||||||
// 请求发送拦截,并在头部加入token
|
// 请求发送拦截,并在头部加入token
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
function (config) {
|
function (config) {
|
||||||
|
|
||||||
let nextToken = localCache.get('auth') && localCache.get('auth').token;
|
let nextToken = localCache.get('auth') && localCache.get('auth').token;
|
||||||
let toeknType = localCache.get('auth') && localCache.get('auth').token_type;
|
let toeknType = localCache.get('auth') && localCache.get('auth').token_type;
|
||||||
if (config.url.indexOf('/auth/login') > -1) {
|
if (config.url.indexOf('/auth/login') > -1) {
|
||||||
|
|
@ -39,11 +58,11 @@ export default function () {
|
||||||
if (_token) {
|
if (_token) {
|
||||||
config.headers['Authorization'] = `${toeknType} ${_token}`; // 存在将token写入请求头
|
config.headers['Authorization'] = `${toeknType} ${_token}`; // 存在将token写入请求头
|
||||||
}
|
}
|
||||||
// if (config.headers.isLoading !== false) showLoading();
|
if (config.headers.isLoading !== false) showLoading();
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
// if (error.config.headers.isLoading !== false) hideLoading();
|
if (error.config.headers.isLoading !== false) hideLoading();
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -58,18 +77,19 @@ export default function () {
|
||||||
showToast('账号过期或异地登录, 请重新登录');
|
showToast('账号过期或异地登录, 请重新登录');
|
||||||
localCache.remove('auth');
|
localCache.remove('auth');
|
||||||
localCache.remove('userInfo');
|
localCache.remove('userInfo');
|
||||||
window.location.pathname='/home';
|
authModal.setAuthModalType('login');
|
||||||
|
authModal.showAuthModal();
|
||||||
// router.replace({name: 'Home', params: {auth: 'over'}});
|
// router.replace({name: 'Home', params: {auth: 'over'}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast('后台' + response.data.status + '错误返回格式不正确');
|
showToast('后台' + response.data.status + '错误返回格式不正确');
|
||||||
}
|
}
|
||||||
// if (response.config.headers.isLoading !== false) hideLoading();
|
if (response.config.headers.isLoading !== false) hideLoading();
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
// if (error.config.headers.isLoading !== false) hideLoading();
|
if (error.config.headers.isLoading !== false) hideLoading();
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
switch (error.response.data.status) {
|
switch (error.response.data.status) {
|
||||||
case 401:
|
case 401:
|
||||||
|
|
@ -77,7 +97,8 @@ export default function () {
|
||||||
showToast('账号过期或异地登录, 请重新登录');
|
showToast('账号过期或异地登录, 请重新登录');
|
||||||
localCache.remove('auth');
|
localCache.remove('auth');
|
||||||
localCache.remove('userInfo');
|
localCache.remove('userInfo');
|
||||||
window.location.pathname='/home';
|
authModal.setAuthModalType('login');
|
||||||
|
authModal.showAuthModal();
|
||||||
break;
|
break;
|
||||||
case 500:
|
case 500:
|
||||||
// 500 服务端请求错误
|
// 500 服务端请求错误
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import { localCache } from '@/io/cache'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||||
|
timeout: 200000,
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
const token = localCache.get('auth') && localCache.get('auth').token;
|
||||||
|
if (token) {
|
||||||
|
config.headers[
|
||||||
|
'Authorization'
|
||||||
|
] = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
if (config.requestBaseUrl == 'chat') {
|
||||||
|
config.baseURL = import.meta.env.VITE_API_CHAT_URL
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
service.interceptors.response.use(
|
||||||
|
|
||||||
|
response => {
|
||||||
|
const { requestBaseUrl } = response.config
|
||||||
|
const res = response.data
|
||||||
|
if (requestBaseUrl == 'chat') {
|
||||||
|
return res
|
||||||
|
} else {
|
||||||
|
if (res.status != 0) {
|
||||||
|
message.error(res.msg || 'Error')
|
||||||
|
return Promise.reject(res.message || 'Error')
|
||||||
|
} else {
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
const res = error.response.data
|
||||||
|
const { requestBaseUrl } = error.config
|
||||||
|
if (requestBaseUrl == 'chat') {
|
||||||
|
if (res.errcode == 401) {
|
||||||
|
// localCache.remove('auth');
|
||||||
|
// localCache.remove('userInfo');
|
||||||
|
}
|
||||||
|
message.error(res.errmsg || 'Error')
|
||||||
|
return Promise.reject(res)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
message.error(res.errmsg || 'Error')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default service
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
<div class="logo">logo</div>
|
<div class="logo">logo</div>
|
||||||
<div class="title">首页</div>
|
<div class="title">首页</div>
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<div class="navBtn" @click="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
<div class="navBtn" @click.stop="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
||||||
<div class="menuBox" v-if="showMenu">
|
<div class="menuBox" @click.stop="" v-if="showMenu">
|
||||||
<template v-if="isLogin">
|
<template v-if="userInfo.userData && userInfo.userData.id">
|
||||||
<div class="menuHeader">
|
<div class="menuHeader">
|
||||||
<div class="userInfo">
|
<div class="userInfo">
|
||||||
<div class="avatar"><img src="" alt=""></div>
|
<div class="avatar"><img :src="userInfo.userData.avatar || defaultAvatar"></div>
|
||||||
<div class="name">大王叫我来巡山大王叫我来巡山</div>
|
<div class="name">{{ userInfo.userData.name || userInfo.userData.phone || userInfo.userData.email }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navIcon" @click="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
<div class="navIcon" @click="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div class="menuHeader">
|
<div class="menuHeader">
|
||||||
<div class="userInfo">
|
<div class="userInfo">
|
||||||
<div class="avatar"><img src="" alt=""></div>
|
<div class="avatar"><img src="" alt=""></div>
|
||||||
<div class="name">大王叫我来巡山大王叫我来巡山</div>
|
<div class="name">未登录</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navIcon" @click="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
<div class="navIcon" @click="toggleMenu"><van-icon name="wap-nav" size=".37rem" /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Auth />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -49,36 +50,53 @@ import { localCache } from '@/io/cache';
|
||||||
import http from '@/io/http';
|
import http from '@/io/http';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { showToast } from 'vant';
|
import { showToast } from 'vant';
|
||||||
|
import Auth from '@/components/auth/index.vue';
|
||||||
|
import { useAuthModal } from '@/stores/authModal';
|
||||||
|
|
||||||
|
|
||||||
const userInfo = useUserInfo();
|
const userInfo = useUserInfo();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const authModal = useAuthModal();
|
||||||
|
|
||||||
const isLogin = ref(false);
|
|
||||||
const showMenu = ref(false);
|
const showMenu = ref(false);
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
document.body.addEventListener('click', ()=>{
|
||||||
|
showMenu.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
let userInfoData = localCache.get('userInfo') || {};
|
||||||
|
if (userInfoData.id) {
|
||||||
|
userInfo.updateUserInfo(userInfoData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const toggleMenu = ()=>{
|
const toggleMenu = ()=>{
|
||||||
console.log(showMenu.value);
|
|
||||||
showMenu.value = !showMenu.value;
|
showMenu.value = !showMenu.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const onLogin = ()=>{
|
const onLogin = ()=>{
|
||||||
|
showMenu.value = false;
|
||||||
|
authModal.setAuthModalType('login');
|
||||||
|
authModal.showAuthModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRegister = ()=>{
|
const onRegister = ()=>{
|
||||||
|
showMenu.value = false;
|
||||||
|
authModal.setAuthModalType('register');
|
||||||
|
authModal.showAuthModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLogout = ()=>{
|
const onLogout = ()=>{
|
||||||
|
showMenu.value = false;
|
||||||
http('/api/auth/logout').then(res => {
|
http('/api/auth/logout').then(res => {
|
||||||
localCache.remove('userInfo');
|
localCache.remove('userInfo');
|
||||||
localCache.remove('auth');
|
localCache.remove('auth');
|
||||||
|
userInfo.updateUserInfo();
|
||||||
showToast('已退出登录!');
|
showToast('已退出登录!');
|
||||||
isLogin.value = false;
|
// window.location.replace('/');
|
||||||
window.location.replace('/');
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
showToast(err.message);
|
showToast(err.message);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import httpConfig from "./io/httpConfig";
|
||||||
import 'vant/es/toast/style';
|
import 'vant/es/toast/style';
|
||||||
|
|
||||||
|
|
||||||
httpConfig();
|
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
|
|
@ -22,4 +21,5 @@ registerSvgIcon(app);
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
|
httpConfig();
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
移动端
|
移动端首页
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { onMounted } from 'vue';
|
||||||
import { showToast } from 'vant';
|
import { showToast } from 'vant';
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
showToast('页面初始化');
|
// showToast('页面初始化');
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -18,7 +18,6 @@ onMounted(()=>{
|
||||||
.page{
|
.page{
|
||||||
width: 7.5rem;
|
width: 7.5rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
background: #CCC;
|
|
||||||
font-size: .22rem;
|
font-size: .22rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -24,9 +24,7 @@ export default defineConfig({
|
||||||
}),
|
}),
|
||||||
Components({
|
Components({
|
||||||
resolvers: [
|
resolvers: [
|
||||||
VantResolver({
|
VantResolver(),
|
||||||
importStyle: false, // css in js
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
||||||