2026-05-26 18:46:23 +08:00

370 lines
12 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="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>