add bind mobile, bind email, bind wechat function is ready

This commit is contained in:
RockYang 2024-08-14 15:56:50 +08:00
parent 43843b92f2
commit aa42d38387
18 changed files with 386 additions and 65 deletions

View File

@ -329,8 +329,10 @@ func (h *UserHandler) CLogin(c *gin.Context) {
// CLoginCallback 第三方登录回调 // CLoginCallback 第三方登录回调
func (h *UserHandler) CLoginCallback(c *gin.Context) { func (h *UserHandler) CLoginCallback(c *gin.Context) {
loginType := h.GetTrim(c, "login_type") loginType := c.Query("login_type")
code := h.GetTrim(c, "code") code := c.Query("code")
userId := h.GetInt(c, "user_id", 0)
action := c.Query("action")
var res types.BizVo var res types.BizVo
apiURL := fmt.Sprintf("%s/api/clogin/info", h.App.Config.ApiConfig.ApiURL) apiURL := fmt.Sprintf("%s/api/clogin/info", h.App.Config.ApiConfig.ApiURL)
@ -355,11 +357,34 @@ func (h *UserHandler) CLoginCallback(c *gin.Context) {
// login successfully // login successfully
data := res.Data.(map[string]interface{}) data := res.Data.(map[string]interface{})
session := gin.H{}
var user model.User var user model.User
tx := h.DB.Debug().Where("openid", data["openid"]).First(&user) if action == "bind" && userId > 0 {
if tx.Error != nil { // user not exist, create new user err = h.DB.Where("openid", data["openid"]).First(&user).Error
// 检测最大注册人数 if err == nil {
resp.ERROR(c, "该微信已经绑定其他账号,请先解绑")
return
}
err = h.DB.Where("id", userId).First(&user).Error
if err != nil {
resp.ERROR(c, "绑定用户不存在")
return
}
err = h.DB.Model(&user).UpdateColumn("openid", data["openid"]).Error
if err != nil {
resp.ERROR(c, "更新用户信息失败,"+err.Error())
return
}
resp.SUCCESS(c, gin.H{"token": ""})
return
}
session := gin.H{}
tx := h.DB.Where("openid", data["openid"]).First(&user)
if tx.Error != nil {
// create new user
var totalUser int64 var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser) h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum { if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
@ -534,7 +559,7 @@ func (h *UserHandler) UpdatePass(c *gin.Context) {
resp.SUCCESS(c) resp.SUCCESS(c)
} }
// ResetPass 重置密码 // ResetPass 找回密码
func (h *UserHandler) ResetPass(c *gin.Context) { func (h *UserHandler) ResetPass(c *gin.Context) {
var data struct { var data struct {
Username string `json:"username"` Username string `json:"username"`
@ -572,10 +597,10 @@ func (h *UserHandler) ResetPass(c *gin.Context) {
} }
} }
// BindUsername 重置账 // BindMobile 绑定手机
func (h *UserHandler) BindUsername(c *gin.Context) { func (h *UserHandler) BindMobile(c *gin.Context) {
var data struct { var data struct {
Username string `json:"username"` Mobile string `json:"mobile"`
Code string `json:"code"` Code string `json:"code"`
} }
if err := c.ShouldBindJSON(&data); err != nil { if err := c.ShouldBindJSON(&data); err != nil {
@ -584,7 +609,7 @@ func (h *UserHandler) BindUsername(c *gin.Context) {
} }
// 检查验证码 // 检查验证码
key := CodeStorePrefix + data.Username key := CodeStorePrefix + data.Mobile
code, err := h.redis.Get(c, key).Result() code, err := h.redis.Get(c, key).Result()
if err != nil || code != data.Code { if err != nil || code != data.Code {
resp.ERROR(c, "验证码错误") resp.ERROR(c, "验证码错误")
@ -593,19 +618,54 @@ func (h *UserHandler) BindUsername(c *gin.Context) {
// 检查手机号是否被其他账号绑定 // 检查手机号是否被其他账号绑定
var item model.User var item model.User
res := h.DB.Where("username = ?", data.Username).First(&item) res := h.DB.Where("mobile", data.Mobile).First(&item)
if res.Error == nil { if res.Error == nil {
resp.ERROR(c, "该账号已经被其他账号绑定") resp.ERROR(c, "该手机号已经绑定了其他账号,请更换手机号")
return return
} }
user, err := h.GetLoginUser(c) userId := h.GetLoginUserId(c)
if err != nil {
resp.NotAuth(c)
return
}
err = h.DB.Model(&user).UpdateColumn("username", data.Username).Error err = h.DB.Model(&item).Where("id", userId).UpdateColumn("mobile", data.Mobile).Error
if err != nil {
resp.ERROR(c, err.Error())
return
}
_ = h.redis.Del(c, key) // 删除短信验证码
resp.SUCCESS(c)
}
// BindEmail 绑定邮箱
func (h *UserHandler) BindEmail(c *gin.Context) {
var data struct {
Email string `json:"email"`
Code string `json:"code"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
// 检查验证码
key := CodeStorePrefix + data.Email
code, err := h.redis.Get(c, key).Result()
if err != nil || code != data.Code {
resp.ERROR(c, "验证码错误")
return
}
// 检查手机号是否被其他账号绑定
var item model.User
res := h.DB.Where("email", data.Email).First(&item)
if res.Error == nil {
resp.ERROR(c, "该邮箱地址已经绑定了其他账号,请更邮箱地址")
return
}
userId := h.GetLoginUserId(c)
err = h.DB.Model(&item).Where("id", userId).UpdateColumn("email", data.Email).Error
if err != nil { if err != nil {
resp.ERROR(c, err.Error()) resp.ERROR(c, err.Error())
return return

View File

@ -231,7 +231,8 @@ func main() {
group.GET("profile", h.Profile) group.GET("profile", h.Profile)
group.POST("profile/update", h.ProfileUpdate) group.POST("profile/update", h.ProfileUpdate)
group.POST("password", h.UpdatePass) group.POST("password", h.UpdatePass)
group.POST("bind/username", h.BindUsername) group.POST("bind/mobile", h.BindMobile)
group.POST("bind/email", h.BindEmail)
group.POST("resetPass", h.ResetPass) group.POST("resetPass", h.ResetPass)
group.GET("clogin", h.CLogin) group.GET("clogin", h.CLogin)
group.GET("clogin/callback", h.CLoginCallback) group.GET("clogin/callback", h.CLoginCallback)

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4125778 */ font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1723434190230') format('woff2'), src: url('iconfont.woff2?t=1723593727785') format('woff2'),
url('iconfont.woff?t=1723434190230') format('woff'), url('iconfont.woff?t=1723593727785') format('woff'),
url('iconfont.ttf?t=1723434190230') format('truetype'); url('iconfont.ttf?t=1723593727785') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-email:before {
content: "\e670";
}
.icon-mobile:before {
content: "\e79a";
}
.icon-drag:before { .icon-drag:before {
content: "\e8ec"; content: "\e8ec";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,20 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "15838472",
"name": "email",
"font_class": "email",
"unicode": "e670",
"unicode_decimal": 58992
},
{
"icon_id": "6151052",
"name": "mobile-alt",
"font_class": "mobile",
"unicode": "e79a",
"unicode_decimal": 59290
},
{ {
"icon_id": "15617554", "icon_id": "15617554",
"name": "drag", "name": "drag",

Binary file not shown.

View File

@ -0,0 +1,108 @@
<template>
<el-dialog
v-model="showDialog"
:close-on-click-modal="true"
style="max-width: 400px"
@close="close"
:title="title"
>
<div class="form">
<div class="text-center" v-if="email !== ''">当前已绑定邮箱{{ email }}</div>
<el-form label-position="top">
<el-form-item label="邮箱地址">
<el-input v-model="form.email"/>
</el-form-item>
<el-form-item label="验证码">
<el-row :gutter="0">
<el-col :span="16">
<el-input v-model="form.code" maxlength="6"/>
</el-col>
<el-col :span="8" style="padding-left: 10px">
<send-msg :receiver="form.email" type="email"/>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="save">
提交绑定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {computed, ref, watch} from "vue";
import SendMsg from "@/components/SendMsg.vue";
import {ElMessage} from "element-plus";
import {httpPost} from "@/utils/http";
import {checkSession, removeUserInfo} from "@/store/cache";
const props = defineProps({
show: Boolean,
});
const showDialog = computed(() => {
return props.show
})
const title = ref('绑定邮箱')
const email = ref('')
const form = ref({
email: '',
code: ''
})
watch(showDialog, (val) => {
if (val) {
form.value.code = ''
form.value.email = ''
checkSession().then(user => {
email.value = user.email
})
}
})
const emits = defineEmits(['hide']);
const save = () => {
if (form.value.code === '') {
return ElMessage.error("请输入验证码");
}
httpPost('/api/user/bind/email', form.value).then(() => {
removeUserInfo()
ElMessage.success("绑定成功")
emits('hide')
}).catch(e => {
ElMessage.error("绑定失败:" + e.message);
})
}
const close = function () {
emits('hide');
}
</script>
<style lang="stylus" scoped>
.form {
.text-center {
text-align center
padding-bottom 15px
font-size 14px
color #a1a1a1
font-weight 700
}
.el-form-item__content {
.el-row {
width 100%
}
}
}
</style>

View File

@ -2,24 +2,24 @@
<el-dialog <el-dialog
v-model="showDialog" v-model="showDialog"
:close-on-click-modal="true" :close-on-click-modal="true"
style="max-width: 600px" style="max-width: 400px"
:before-close="close" @close="close"
:title="title" :title="title"
> >
<div class="form"> <div class="form">
<div class="text-center">当前已绑手机号{{ mobile }}</div> <div class="text-center" v-if="mobile !== ''">当前已绑手机号{{ mobile }}</div>
<el-form :model="form" label-width="120px"> <el-form label-position="top">
<el-form-item label="手机号"> <el-form-item label="手机号">
<el-input v-model="form.mobile"/> <el-input v-model="form.mobile"/>
</el-form-item> </el-form-item>
<el-form-item label="验证码"> <el-form-item label="验证码">
<el-row :gutter="20"> <el-row :gutter="0">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="form.code" maxlength="6"/> <el-input v-model="form.code" maxlength="6"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8" style="padding-left: 10px">
<send-msg size="" :receiver="form.username" type="mobile"/> <send-msg :receiver="form.mobile" type="mobile"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -37,12 +37,11 @@
</template> </template>
<script setup> <script setup>
import {computed, ref} from "vue"; import {computed, ref, watch} from "vue";
import SendMsg from "@/components/SendMsg.vue"; import SendMsg from "@/components/SendMsg.vue";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {httpPost} from "@/utils/http"; import {httpPost} from "@/utils/http";
import {validateEmail, validateMobile} from "@/utils/validate"; import {checkSession, removeUserInfo} from "@/store/cache";
import {checkSession} from "@/store/cache";
const props = defineProps({ const props = defineProps({
show: Boolean, show: Boolean,
@ -59,33 +58,37 @@ const form = ref({
code: '' code: ''
}) })
checkSession().then(user => { watch(showDialog, (val) => {
if (val) {
form.value = {
mobile: '',
code: ''
}
checkSession().then(user => {
mobile.value = user.mobile mobile.value = user.mobile
})
}
}) })
const emits = defineEmits(['hide']); const emits = defineEmits(['hide']);
const save = () => { const save = () => {
if (!validateMobile(form.value.mobile) && !validateEmail(form.value.mobile)) {
return ElMessage.error("请输入合法的手机号/邮箱地址")
}
if (form.value.code === '') { if (form.value.code === '') {
return ElMessage.error("请输入验证码"); return ElMessage.error("请输入验证码");
} }
httpPost('/api/user/bind/username', form.value).then(() => { httpPost('/api/user/bind/mobile', form.value).then(() => {
ElMessage.success({ removeUserInfo()
message: '绑定成功', ElMessage.success("绑定成功")
duration: 1000, emits('hide')
onClose: () => emits('hide', false)
})
}).catch(e => { }).catch(e => {
ElMessage.error("绑定失败:" + e.message); ElMessage.error("绑定失败:" + e.message);
}) })
} }
const close = function () { const close = function () {
emits('hide', false); emits('hide');
} }
</script> </script>

View File

@ -66,7 +66,7 @@
<div class="c-login" v-if="wechatLoginURL !== ''"> <div class="c-login" v-if="wechatLoginURL !== ''">
<div class="text">其他登录方式</div> <div class="text">其他登录方式</div>
<div class="login-type"> <div class="login-type">
<a class="wechat-login" :href="wechatLoginURL"><i class="iconfont icon-wechat"></i></a> <a class="wechat-login" :href="wechatLoginURL" @click="setRoute(router.currentRoute.value.path)"><i class="iconfont icon-wechat"></i></a>
</div> </div>
</div> </div>
</el-col> </el-col>
@ -249,6 +249,8 @@ import {arrayContains} from "@/utils/libs";
import {getSystemInfo} from "@/store/cache"; import {getSystemInfo} from "@/store/cache";
import Captcha from "@/components/Captcha.vue"; import Captcha from "@/components/Captcha.vue";
import ResetPass from "@/components/ResetPass.vue"; import ResetPass from "@/components/ResetPass.vue";
import {setRoute} from "@/store/system";
import {useRouter} from "vue-router";
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const props = defineProps({ const props = defineProps({
@ -282,9 +284,10 @@ const emits = defineEmits(['hide', 'success']);
const action = ref("login") const action = ref("login")
const enableVerify = ref(false) const enableVerify = ref(false)
const showResetPass = ref(false) const showResetPass = ref(false)
const router = useRouter()
onMounted(() => { onMounted(() => {
const returnURL = `${location.protocol}//${location.host}/login/callback` const returnURL = `${location.protocol}//${location.host}/login/callback?action=login`
httpGet("/api/user/clogin?return_url="+returnURL).then(res => { httpGet("/api/user/clogin?return_url="+returnURL).then(res => {
wechatLoginURL.value = res.data.url wechatLoginURL.value = res.data.url
}).catch(e => { }).catch(e => {

View File

@ -2,7 +2,6 @@
<el-dialog <el-dialog
v-model="showDialog" v-model="showDialog"
:close-on-click-modal="true" :close-on-click-modal="true"
:show-close="mobile !== ''"
:before-close="close" :before-close="close"
:width="450" :width="450"
:title="title" :title="title"

View File

@ -58,7 +58,7 @@ const doSendMsg = (data) => {
canSend.value = false canSend.value = false
httpPost('/api/sms/code', {receiver: props.receiver, key: data.key, dots: data.dots, x:data.x}).then(() => { httpPost('/api/sms/code', {receiver: props.receiver, key: data.key, dots: data.dots, x:data.x}).then(() => {
showMessageOK('验证码发送成功') showMessageOK('验证码发送成功')
let time = 120 let time = 60
btnText.value = time btnText.value = time
const handler = setInterval(() => { const handler = setInterval(() => {
time = time - 1 time = time - 1

View File

@ -0,0 +1,93 @@
<template>
<el-dialog
v-model="showDialog"
:close-on-click-modal="true"
style="max-width: 400px"
@close="close"
:title="title"
>
<div class="third-login" v-loading="loading">
<div class="item" v-if="wechatBindURL !== ''">
<a class="link" :href="wechatBindURL"><i class="iconfont icon-wechat"></i></a>
<span class="text ok" v-if="openid !== ''">已绑定</span>
<span class="text" v-else>未绑定</span>
</div>
</div>
</el-dialog>
</template>
<script setup>
import {computed, ref, watch} from "vue";
import {httpGet} from "@/utils/http";
import {checkSession} from "@/store/cache";
import {showMessageError} from "@/utils/dialog";
const props = defineProps({
show: Boolean,
});
const emits = defineEmits(['hide']);
const showDialog = computed(() => {
return props.show
})
const title = ref('绑定第三方登录')
const openid = ref('')
const wechatBindURL = ref('')
const loading = ref(true)
watch(showDialog, (val) => {
if (val) {
checkSession().then(user => {
openid.value = user.openid
})
const returnURL = `${location.protocol}//${location.host}/login/callback?action=bind`
httpGet("/api/user/clogin?return_url="+returnURL).then(res => {
wechatBindURL.value = res.data.url
loading.value = false
}).catch(e => {
showMessageError(e.message)
})
}
})
const close = function () {
emits('hide');
}
</script>
<style lang="stylus" scoped>
.third-login {
display flex
justify-content center
min-height 100px
.item {
display flex
flex-flow column
align-items center
.link {
display flex
.iconfont {
font-size 30px
cursor pointer
background #e9f1f6
padding 10px
border-radius 50%
}
margin-bottom 10px
}
.text {
font-size 14px
}
.icon-wechat,.ok {
color: #0bc15f;
}
}
}
</style>

View File

@ -6,6 +6,7 @@
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import Storage from "good-storage"; import Storage from "good-storage";
import {useRouter} from "vue-router";
const MOBILE_THEME = process.env.VUE_APP_KEY_PREFIX + "MOBILE_THEME" const MOBILE_THEME = process.env.VUE_APP_KEY_PREFIX + "MOBILE_THEME"
const ADMIN_THEME = process.env.VUE_APP_KEY_PREFIX + "ADMIN_THEME" const ADMIN_THEME = process.env.VUE_APP_KEY_PREFIX + "ADMIN_THEME"
@ -63,3 +64,11 @@ export function FormatFileSize(bytes) {
const i = Math.floor(Math.log(bytes) / Math.log(k)); const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
} }
export function setRoute(path) {
Storage.set(process.env.VUE_APP_KEY_PREFIX + 'ROUTE_',path)
}
export function getRoute() {
return Storage.get(process.env.VUE_APP_KEY_PREFIX + 'ROUTE_')
}

View File

@ -114,7 +114,7 @@ onMounted(() => {
}).catch(() => { }).catch(() => {
}) })
const returnURL = `${location.protocol}//${location.host}/login/callback` const returnURL = `${location.protocol}//${location.host}/login/callback?action=login`
httpGet("/api/user/clogin?return_url="+returnURL).then(res => { httpGet("/api/user/clogin?return_url="+returnURL).then(res => {
wechatLoginURL.value = res.data.url wechatLoginURL.value = res.data.url
}).catch(e => { }).catch(e => {

View File

@ -39,9 +39,10 @@ import {useRouter} from "vue-router"
import {ElMessage, ElMessageBox} from "element-plus"; import {ElMessage, ElMessageBox} from "element-plus";
import {httpGet} from "@/utils/http"; import {httpGet} from "@/utils/http";
import {setUserToken} from "@/store/session"; import {setUserToken} from "@/store/session";
import {isMobile} from "@/utils/libs";
import Clipboard from "clipboard"; import Clipboard from "clipboard";
import {showMessageError, showMessageOK} from "@/utils/dialog"; import {showMessageError, showMessageOK} from "@/utils/dialog";
import {getRoute} from "@/store/system";
import {checkSession, removeUserInfo} from "@/store/cache";
const winHeight = ref(window.innerHeight) const winHeight = ref(window.innerHeight)
const loading = ref(true) const loading = ref(true)
@ -52,12 +53,25 @@ const password = ref('')
const code = router.currentRoute.value.query.code const code = router.currentRoute.value.query.code
const action = router.currentRoute.value.query.action
if (code === "") { if (code === "") {
ElMessage.error({message: "登录失败code 参数不能为空",duration: 2000, onClose: () => router.push("/")}) ElMessage.error({message: "登录失败code 参数不能为空",duration: 2000, onClose: () => router.push("/")})
} else { } else {
checkSession().then(user => {
// bind user
doLogin(user.id)
}).catch(() => {
doLogin(0)
})
}
const doLogin = (userId) => {
// //
httpGet("/api/user/clogin/callback",{login_type: "wx",code: code}).then(res => { httpGet("/api/user/clogin/callback",{login_type: "wx",code: code, action:action, user_id: userId}).then(res => {
removeUserInfo()
if (res.data.token) {
setUserToken(res.data.token) setUserToken(res.data.token)
}
if (res.data.username) { if (res.data.username) {
username.value = res.data.username username.value = res.data.username
password.value = res.data.password password.value = res.data.password
@ -96,11 +110,7 @@ onUnmounted(() => {
const finishLogin = () => { const finishLogin = () => {
show.value = false show.value = false
if (isMobile()) { router.push(getRoute())
router.push('/mobile')
} else {
router.push('/chat')
}
} }
</script> </script>

View File

@ -7,13 +7,19 @@
<el-row class="user-opt" :gutter="20"> <el-row class="user-opt" :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button> <el-button type="primary" @click="showBindEmailDialog = true">绑定邮箱</el-button>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-button type="primary" @click="showBindMobileDialog = true">绑定手机</el-button> <el-button type="primary" @click="showBindMobileDialog = true">绑定手机</el-button>
</el-col> </el-col>
<el-col :span="12">
<el-button type="primary" @click="showThirdLoginDialog = true">第三方登录</el-button>
</el-col>
<el-col :span="12">
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button>
</el-col>
<el-col :span="24"> <el-col :span="24">
<el-button type="success" @click="showRedeemVerifyDialog = true">兑换码核销 <el-button type="primary" @click="showRedeemVerifyDialog = true">卡密兑换
</el-button> </el-button>
</el-col> </el-col>
@ -95,6 +101,9 @@
<bind-mobile v-if="isLogin" :show="showBindMobileDialog" @hide="showBindMobileDialog = false"/> <bind-mobile v-if="isLogin" :show="showBindMobileDialog" @hide="showBindMobileDialog = false"/>
<bind-email v-if="isLogin" :show="showBindEmailDialog" @hide="showBindEmailDialog = false"/>
<third-login v-if="isLogin" :show="showThirdLoginDialog" @hide="showThirdLoginDialog = false"/>
<redeem-verify v-if="isLogin" :show="showRedeemVerifyDialog" @hide="redeemCallback"/> <redeem-verify v-if="isLogin" :show="showRedeemVerifyDialog" @hide="redeemCallback"/>
<el-dialog <el-dialog
@ -149,6 +158,8 @@ import {removeUserToken} from "@/store/session";
import UserOrder from "@/components/UserOrder.vue"; import UserOrder from "@/components/UserOrder.vue";
import CountDown from "@/components/CountDown.vue"; import CountDown from "@/components/CountDown.vue";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import BindEmail from "@/components/BindEmail.vue";
import ThirdLogin from "@/components/ThirdLogin.vue";
const list = ref([]) const list = ref([])
const showPayDialog = ref(false) const showPayDialog = ref(false)
@ -156,9 +167,11 @@ const vipImg = ref("/images/vip.png")
const enableReward = ref(false) // const enableReward = ref(false) //
const rewardImg = ref('/images/reward.png') const rewardImg = ref('/images/reward.png')
const qrcode = ref("") const qrcode = ref("")
const showPasswordDialog = ref(false); const showPasswordDialog = ref(false)
const showBindMobileDialog = ref(false); const showBindMobileDialog = ref(false)
const showRedeemVerifyDialog = ref(false); const showBindEmailDialog = ref(false)
const showRedeemVerifyDialog = ref(false)
const showThirdLoginDialog = ref(false)
const text = ref("") const text = ref("")
const user = ref(null) const user = ref(null)
const isLogin = ref(false) const isLogin = ref(false)