Files
device-voice-h5/pages/bind/bind.vue
sexygoat 3f997063f4
All checks were successful
构建并部署前端到生产环境 / build-and-deploy (push) Successful in 1m37s
fix: 优化体验
2026-05-09 16:14:06 +08:00

221 lines
5.0 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>
<view class="page">
<!-- 自定义导航栏 -->
<view v-if="!fromLogin" class="custom-navbar">
<view class="navbar-back" @tap="goBack">
<image class="back-icon" src="../../static/left.png" mode="aspectFit" />
</view>
<view class="navbar-title">绑定手机号</view>
<view class="navbar-placeholder"></view>
</view>
<view class="container" :style="{ paddingTop: fromLogin ? '0' : '0' }">
<view class="card flex-col-g20">
<view class="flex-row-g20">
<label for="">手机号:</label>
<up-input placeholder="请输入绑定的手机号" border="surround" v-model="bind.phone" />
</view>
<view class="flex-row-g20">
<label for="">验证码:</label>
<up-input placeholder="验证码" v-model="bind.code">
<template #suffix>
<up-button @tap="getCode" :text="codeText" type="success" :disabled="cooldown > 0"></up-button>
</template>
</up-input>
</view>
<view class="btn">
<up-button type="primary" @tap="bindPhone">绑定手机号</up-button>
</view>
<!-- 如果从登录页面跳转过来显示退出按钮 -->
<view class="btn" v-if="fromLogin">
<up-button type="error" @tap="logout">退出</up-button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { authApi } from '@/api/index.js';
import { useUserStore } from '@/store/index.js';
const POST_BIND_RELOGIN_NOTICE_KEY = 'postBindReloginNotice';
const userStore = useUserStore();
let bind = reactive({
phone: '',
code: ''
});
let cooldown = ref(0);
let codeText = ref('获取验证码');
let timer = null;
let fromLogin = ref(false);
onMounted(() => {
// 获取页面参数,判断是否从登录页面跳转过来
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options;
fromLogin.value = options.fromLogin === 'true';
});
const prepareManualRelogin = () => {
const preservedIdentifier = userStore.state.identifier || uni.getStorageSync('identifier') || '';
userStore.clearUser();
if (preservedIdentifier) {
userStore.setIdentifier(preservedIdentifier);
}
uni.setStorageSync(POST_BIND_RELOGIN_NOTICE_KEY, '1');
// #ifdef H5
if (typeof sessionStorage !== 'undefined') {
sessionStorage.removeItem('assetToken');
}
// #endif
};
const getCode = async () => {
if (!bind.phone) {
uni.showToast({ title: '请输入手机号', icon: 'none' });
return;
}
if (!/^1[3-9]\d{9}$/.test(bind.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' });
return;
}
try {
const data = await authApi.sendCode(bind.phone, 'bind_phone');
cooldown.value = data.cooldown_seconds || 60;
codeText.value = `${cooldown.value}s`;
timer = setInterval(() => {
cooldown.value--;
if (cooldown.value <= 0) {
clearInterval(timer);
codeText.value = '获取验证码';
} else {
codeText.value = `${cooldown.value}s`;
}
}, 1000);
uni.showToast({ title: '验证码已发送', icon: 'success' });
} catch (e) {
console.error('发送验证码失败', e);
}
};
const bindPhone = async () => {
if (!bind.phone || !bind.code) {
uni.showToast({ title: '手机号和验证码都不能为空', icon: 'none' });
return;
}
try {
await authApi.bindPhone(bind.phone, bind.code);
if (fromLogin.value) {
prepareManualRelogin();
uni.reLaunch({ url: '/pages/login/login' });
return;
}
uni.showToast({ title: '绑定成功', icon: 'success' });
setTimeout(() => {
// 从其他页面过来的,返回上一页
uni.navigateBack();
}, 1500);
} catch (e) {
console.error('绑定手机号失败', e);
}
};
const logout = () => {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.clearStorageSync();
uni.reLaunch({ url: '/pages/login/login' });
}
}
});
};
const goBack = () => {
uni.navigateTo({
url: '/pages/index/index'
})
};
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: var(--bg-secondary);
max-width: 750rpx;
margin: 0 auto;
width: 100%;
box-sizing: border-box;
}
.custom-navbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
margin-bottom: 24rpx;
background: var(--bg-primary);
border-bottom: 1rpx solid var(--border-light);
padding: 0 24rpx;
position: sticky;
top: 0;
z-index: 100;
.navbar-back {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx;
cursor: pointer;
.back-icon {
width: 40rpx;
height: 40rpx;
}
.back-text {
font-size: 28rpx;
color: var(--text-primary);
}
}
.navbar-title {
font-size: 32rpx;
font-weight: 600;
color: var(--text-primary);
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.navbar-placeholder {
width: 100rpx;
}
}
.container {
padding: var(--space-md);
}
</style>