370 lines
12 KiB
Vue
370 lines
12 KiB
Vue
<template>
|
||
<div class="login" style="padding-bottom: 50vw;">
|
||
<!-- Logo 区域 -->
|
||
<div class="logo" v-if="!isReg">
|
||
<img src="/img/login-logo.png" />
|
||
</div>
|
||
<div class="logo" v-else>
|
||
<div class="fonts">
|
||
<b>注册账号</b>
|
||
<p>欢迎注册泰古润</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="contaiar">
|
||
<div class="lg_box">
|
||
<!-- 登录方式切换 tabs -->
|
||
<van-tabs color="#841e36" line-height="1.07vw" line-width="12vw" v-model:active="active" v-if="!isReg">
|
||
<van-tab title="账号登录"></van-tab>
|
||
<van-tab title="手机号登录"></van-tab>
|
||
</van-tabs>
|
||
|
||
<!-- 表单区域 -->
|
||
<div class="form-content">
|
||
<!-- 手机号 -->
|
||
<div class="line">
|
||
<div class="title">
|
||
<img src="/img/login-i1.png" alt="">
|
||
<input type="tel" maxlength="11" :placeholder="active === 1 && !isReg ? '请输入手机号码作为账号' : '请输入手机号'"
|
||
v-model="formData.Phone" />
|
||
<van-icon v-if="formData.Phone" @click="formData.Phone = ''" name="cross" size="5vw"></van-icon>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 验证码 (手机号登录或注册时显示) -->
|
||
<div class="line" v-if="active === 1 || isReg">
|
||
<div class="title">
|
||
<img src="/img/login-i2.png" alt="">
|
||
<input type="text" placeholder="请输入验证码" v-model="formData.SmsCode" maxlength="6" />
|
||
<van-button class="hqyzm" @click="getSmsCode" :disabled="countdown.isCounting">
|
||
{{ countdown.isCounting ? `已发送(${countdown.seconds}s)` : '获取验证码' }}
|
||
</van-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 密码 (账号登录或注册时显示) -->
|
||
<div class="line" v-if="active === 0 || isReg">
|
||
<div class="title">
|
||
<img src="/img/login-i3.png" alt="">
|
||
<input :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" v-model="formData.Password"
|
||
@keydown.enter="handleSubmit" />
|
||
<van-icon @click="showPassword = !showPassword" :name="showPassword ? 'closed-eye' : 'eye-o'"
|
||
size="5vw"></van-icon>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 确认密码 (仅注册时显示) -->
|
||
<div class="line" v-if="isReg">
|
||
<div class="title">
|
||
<img src="/img/login-i4.png" alt="">
|
||
<input :type="showConfirmPassword ? 'text' : 'password'" placeholder="请确认密码" v-model="formData.RePassword"
|
||
@keydown.enter="handleSubmit" />
|
||
<van-icon @click="showConfirmPassword = !showConfirmPassword"
|
||
:name="showConfirmPassword ? 'closed-eye' : 'eye-o'" size="5vw"></van-icon>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 邀请人 (仅注册时显示) -->
|
||
<div class="line" v-if="isReg">
|
||
<div class="title">
|
||
<img src="/img/login-i5.png" alt="">
|
||
<input type="text" maxlength="11" placeholder="请填写邀请人账号(选填)" v-model="formData.Recommend" />
|
||
<van-icon v-if="formData.Recommend" @click="formData.Recommend = ''" name="cross" size="5vw"></van-icon>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 提交按钮 -->
|
||
<van-button class="submit" type="commit" @click="handleSubmit" :loading="loading" loading-text="提交中..">
|
||
{{ isReg ? '注册' : '登录' }}
|
||
</van-button>
|
||
</div>
|
||
|
||
<!-- 用户协议 -->
|
||
<van-checkbox icon-size="3.2vw" class="agreement" v-model="checked" checked-color="#841e36">
|
||
选中即代表同意<a @click.stop="showContract = true">《用户协议》</a>和<a @click.stop="showPolicy = true">《隐私政策》</a>
|
||
</van-checkbox>
|
||
|
||
<!-- 底部链接 -->
|
||
<div class="toLogin">
|
||
<div class="ct1" v-if="isReg" @click="isReg = false">
|
||
已有账号,立即登录
|
||
<van-icon name="arrow"></van-icon>
|
||
</div>
|
||
<div class="box ct2" v-else>
|
||
<div @click.stop="$navigate('/ResetPwd')">忘记密码</div>
|
||
<p>|</p>
|
||
<span @click="isReg = true">立即注册</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户协议弹窗 -->
|
||
<van-action-sheet v-model:show="showContract" safe-area-inset-bottom title="用户协议" closeable
|
||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||
<div class="w100 html" v-html="$datadic.getContent('code_yhxy')"></div>
|
||
</van-action-sheet>
|
||
|
||
<!-- 隐私政策弹窗 -->
|
||
<van-action-sheet v-model:show="showPolicy" safe-area-inset-bottom title="隐私政策" closeable
|
||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||
<div class="w100 html" v-html="$datadic.getContent('code_yszc')"></div>
|
||
</van-action-sheet>
|
||
</template>
|
||
|
||
<script>
|
||
import { useUserStore } from '@/stores/user'
|
||
|
||
export default {
|
||
name: 'Login',
|
||
data() {
|
||
return {
|
||
formData: {
|
||
Phone: '',
|
||
SmsCode: '',
|
||
Password: '',
|
||
RePassword: '',
|
||
Recommend: localStorage.getItem('recommend') || ''
|
||
},
|
||
isReg: false,
|
||
active: 0,
|
||
checked: false,
|
||
showPassword: false,
|
||
showConfirmPassword: false,
|
||
showContract: false,
|
||
showPolicy: false,
|
||
loading: false,
|
||
smsCode: '', // 接口返回的验证码
|
||
countdown: {
|
||
isCounting: false,
|
||
seconds: 0,
|
||
timer: null
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
init() {
|
||
// 防止刷新重复处理微信 code
|
||
if (sessionStorage.getItem('wx_code_processed') === '1') {
|
||
sessionStorage.removeItem('wx_code_processed')
|
||
return
|
||
}
|
||
// 已登录,直接跳转
|
||
if (this.$isLogin?.()) {
|
||
location.replace('#/')
|
||
return
|
||
}
|
||
// 小程序环境
|
||
if (this.$isWechatMini()) {
|
||
wx.miniProgram.reLaunch({ url: `/pages/user/login` })
|
||
return
|
||
}
|
||
// 微信公众号环境:有 code 则等待登录后绑定,无 code 则跳转授权
|
||
if (this.$isWechat()) {
|
||
if (sessionStorage.getItem('wx_callback_code')) {
|
||
// 有 code,等登录后用 token + code 绑定
|
||
} else {
|
||
this.checkWxLogin()
|
||
}
|
||
return
|
||
}
|
||
},
|
||
checkWxLogin() {
|
||
if (!this.$isWechat()) return
|
||
const REDIRECT_URI = encodeURIComponent('https://m.taigurun.cn/#/WxCallback')
|
||
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxdff8dd2032f1e054&redirect_uri=${REDIRECT_URI}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
|
||
},
|
||
validatePhone(phone) {
|
||
if (!phone) {
|
||
this.$showFailToast?.('请输入手机号')
|
||
return false
|
||
}
|
||
if (!this.$validPhone?.(phone)) {
|
||
this.$showFailToast?.('手机号格式错误')
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
validateAgreement() {
|
||
if (!this.checked) {
|
||
this.$showFailToast?.('请选中"同意《用户协议》和《隐私政策》"')
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
getSmsCode() {
|
||
if (!this.validatePhone(this.formData.Phone)) return
|
||
this.countdown.isCounting = true
|
||
this.countdown.seconds = 60
|
||
this.countdown.timer = setInterval(() => {
|
||
if (this.countdown.seconds > 0) {
|
||
this.countdown.seconds--
|
||
} else {
|
||
this.countdown.isCounting = false
|
||
clearInterval(this.countdown.timer)
|
||
}
|
||
}, 1000)
|
||
this.$post('/v1/client/AuthClient/sendcode', {
|
||
purpose: this.isReg ? 'register' : 'login',
|
||
cellphone: this.formData.Phone
|
||
}).then(data => {
|
||
if (data.status === 200) {
|
||
this.$showSuccessToast?.('发送成功')
|
||
this.smsCode = data.data
|
||
}
|
||
}).catch(() => {
|
||
this.countdown.isCounting = false
|
||
clearInterval(this.countdown.timer)
|
||
})
|
||
},
|
||
handleSubmit() {
|
||
if (!this.validatePhone(this.formData.Phone)) return
|
||
if (!this.validateAgreement()) return
|
||
if (this.isReg) {
|
||
this.handleRegister()
|
||
} else {
|
||
this.handleLogin()
|
||
}
|
||
},
|
||
handleLogin() {
|
||
if (this.active === 0) {
|
||
// 账号密码登录
|
||
if (!this.formData.Password) {
|
||
this.$showFailToast?.('请输入密码')
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.$post('/v1/client/AuthClient/loginpwd', {
|
||
cellphone: this.formData.Phone,
|
||
userpwd: this.formData.Password
|
||
}).then(data => {
|
||
this.loading = false
|
||
if (data.status !== 200) {
|
||
this.$showFailToast?.(data.message)
|
||
return
|
||
}
|
||
this.afterLogin(data.data.token)
|
||
}).catch(err => {
|
||
this.loading = false
|
||
this.$showFailToast?.(err.message || '登录失败')
|
||
})
|
||
} else {
|
||
// 手机验证码登录
|
||
if (!this.formData.SmsCode) {
|
||
this.$showFailToast?.('请输入验证码')
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.$post('/v1/client/AuthClient/logincode', {
|
||
cellphone: this.formData.Phone,
|
||
code: this.formData.SmsCode,
|
||
pcellphone: this.formData.Recommend
|
||
}).then(data => {
|
||
this.loading = false
|
||
if (data.status !== 200) {
|
||
this.$showFailToast?.(data.message)
|
||
return
|
||
}
|
||
this.afterLogin(data.data.token)
|
||
}).catch(err => {
|
||
this.loading = false
|
||
this.$showFailToast?.(err.message || '登录失败')
|
||
})
|
||
}
|
||
},
|
||
// 登录/注册成功后的统一处理:存 token、拉用户信息、绑定微信 openid
|
||
afterLogin(token) {
|
||
const userStore = useUserStore()
|
||
userStore.setToken(token)
|
||
userStore.fetchUserInfo()
|
||
this.bindWxOpenid(token)
|
||
},
|
||
// 用 token + code 换 openid 并绑定
|
||
bindWxOpenid(token) {
|
||
const code = sessionStorage.getItem('wx_callback_code')
|
||
if (!code) {
|
||
this.$showSuccessToast?.('登录成功')
|
||
location.replace('#/My')
|
||
return
|
||
}
|
||
fetch(`${import.meta.env.VITE_API_URL}/v1/client/AuthClient/loginwxweb`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`,
|
||
},
|
||
body: JSON.stringify(code),
|
||
}).then(res =>
|
||
res.json()
|
||
).then(res => {
|
||
// alert(JSON.stringify(res))
|
||
sessionStorage.removeItem('wx_callback_code')
|
||
if (res.message) {
|
||
localStorage.setItem('openid', res.message)
|
||
// alert('微信账号绑定成功,openid:' + res.message)
|
||
}
|
||
this.$showSuccessToast?.('登录成功')
|
||
location.replace('#/My')
|
||
}).catch(() => {
|
||
sessionStorage.removeItem('wx_callback_code')
|
||
this.$showSuccessToast?.('登录成功')
|
||
location.replace('#/My')
|
||
})
|
||
},
|
||
handleRegister() {
|
||
if (!this.formData.SmsCode) {
|
||
this.$showFailToast?.('请输入验证码')
|
||
return
|
||
}
|
||
if (!this.formData.Password) {
|
||
this.$showFailToast?.('请输入密码')
|
||
return
|
||
}
|
||
if (this.formData.Password !== this.formData.RePassword) {
|
||
this.$showFailToast?.('两次密码输入不一致')
|
||
return
|
||
}
|
||
if (this.formData.Password.length < 6) {
|
||
this.$showFailToast?.('密码至少6位')
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.$post('/v1/client/AuthClient/register', {
|
||
cellphone: this.formData.Phone,
|
||
userpwd: this.formData.Password,
|
||
pcellphone: this.formData.Recommend,
|
||
code: this.formData.SmsCode
|
||
}).then(data => {
|
||
this.loading = false
|
||
if (data.status !== 200) {
|
||
this.$showFailToast?.(data.message)
|
||
return
|
||
}
|
||
this.afterLogin(data.data.token)
|
||
}).catch(err => {
|
||
this.loading = false
|
||
this.$showFailToast?.(err.message || '注册失败')
|
||
})
|
||
}
|
||
},
|
||
mounted() {
|
||
this.init()
|
||
// 解析邀请链接参数
|
||
const params = new URLSearchParams(location.hash.split('?')[1] || '');
|
||
const inviteCode = params.get('invite');
|
||
if (inviteCode) {
|
||
this.formData.Recommend = inviteCode;
|
||
localStorage.setItem('recommend', inviteCode);
|
||
// 有邀请码时自动切换到注册模式
|
||
// this.isReg = true;
|
||
}
|
||
},
|
||
beforeUnmount() {
|
||
if (this.countdown.timer) {
|
||
clearInterval(this.countdown.timer)
|
||
}
|
||
}
|
||
}
|
||
</script>
|