Files
medical-mall/pages/user/bind-email.uvue

545 lines
11 KiB
Plaintext
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="bind-email-page">
<view class="header">
<view class="back-btn" @click="goBack">
<text class="back-icon"></text>
</view>
<text class="header-title">绑定邮箱</text>
</view>
<view class="content">
<view v-if="userInfo.email" class="already-bound">
<text class="bound-icon">✓</text>
<text class="bound-title">已绑定邮箱</text>
<text class="bound-email">{{ userInfo.email }}</text>
<text class="bound-time">绑定时间:{{ formatTime(userInfo.emailBoundTime) }}</text>
</view>
<form @submit="onSubmit" v-if="!userInfo.email || isChanging">
<!-- 邮箱输入 -->
<view class="form-item">
<text class="label">邮箱地址</text>
<input
class="input"
type="email"
placeholder="请输入邮箱地址"
v-model="form.email"
:disabled="loading"
required
/>
</view>
<!-- 验证码 -->
<view class="form-item">
<text class="label">验证码</text>
<view class="code-input-wrapper">
<input
class="code-input"
type="number"
placeholder="请输入验证码"
v-model="form.code"
:disabled="loading"
maxlength="6"
required
/>
<button
class="get-code-btn"
@click="getCode"
:disabled="!canGetCode || countdown > 0"
>
{{ countdown > 0 ? `${countdown}s后重新获取` : '获取验证码' }}
</button>
</view>
</view>
<!-- 提交按钮 -->
<button
class="submit-btn"
form-type="submit"
:disabled="loading || !isFormValid"
:loading="loading"
>
{{ loading ? '处理中...' : userInfo.email ? '更换邮箱' : '绑定邮箱' }}
</button>
</form>
<view v-if="userInfo.email && !isChanging" class="action-buttons">
<button class="change-btn" @click="startChange">更换邮箱</button>
<button class="unbind-btn" @click="unbindEmail">解绑邮箱</button>
</view>
</view>
<!-- 成功提示 -->
<view v-if="showSuccess" class="success-modal" @click="hideSuccess">
<view class="success-content" @click.stop>
<text class="success-icon">✓</text>
<text class="success-title">{{ successTitle }}</text>
<text class="success-text">{{ successMessage }}</text>
<button class="success-btn" @click="hideSuccess">确定</button>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, computed, onMounted } from 'vue'
//import supa from '@/components/supadb/aksupainstance.uts'
// 用户信息
const userInfo = ref({
email: '',
emailBoundTime: null as string | null
})
const form = ref({
email: '',
code: ''
})
const loading = ref<boolean>(false)
const countdown = ref<number>(0)
const isChanging = ref<boolean>(false)
const showSuccess = ref<boolean>(false)
const successTitle = ref<string>('')
const successMessage = ref<string>('')
// 表单验证
const isFormValid = computed((): boolean => {
const { email, code } = form.value
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email) && /^\d{6}$/.test(code)
})
// 是否可以获取验证码
const canGetCode = computed((): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(form.value.email)
})
// 加载用户信息
const loadUserInfo = () => {
const storedUserInfo = uni.getStorageSync('userInfo')
if (storedUserInfo) {
try {
const info = JSON.parse(storedUserInfo as string)
userInfo.value.email = info.email || ''
userInfo.value.emailBoundTime = info.emailBoundTime || null
} catch (e) {
console.error('Failed to parse user info', e)
}
}
}
// 获取验证码
const getCode = () => {
if (!canGetCode.value) {
uni.showToast({
title: '请输入正确的邮箱地址',
icon: 'none'
})
return
}
// 开始倒计时
countdown.value = 60
const timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(timer)
}
}, 1000)
// 模拟发送验证码
uni.showToast({
title: '验证码已发送到邮箱',
icon: 'success'
})
// 实际项目中这里应该调用发送邮件的API
// const response = await sendEmailCode(form.value.email)
}
// 提交表单
const onSubmit = async () => {
if (!isFormValid.value) {
uni.showToast({
title: '请填写完整且正确的信息',
icon: 'none'
})
return
}
loading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000))
// 更新本地用户信息
const storedUserInfo = uni.getStorageSync('userInfo')
let userInfoData = storedUserInfo ? JSON.parse(storedUserInfo as string) : {}
userInfoData.email = form.value.email
userInfoData.emailBoundTime = new Date().toISOString()
uni.setStorageSync('userInfo', JSON.stringify(userInfoData))
userInfo.value.email = form.value.email
userInfo.value.emailBoundTime = userInfoData.emailBoundTime
// 显示成功消息
successTitle.value = isChanging.value ? '更换成功' : '绑定成功'
successMessage.value = `邮箱 ${form.value.email} 已成功${isChanging.value ? '更换' : '绑定'}`
showSuccess.value = true
// 重置表单
form.value = { email: '', code: '' }
isChanging.value = false
} catch (error: any) {
console.error('绑定邮箱失败:', error)
uni.showToast({
title: error.message || '操作失败',
icon: 'none'
})
} finally {
loading.value = false
}
}
// 开始更换邮箱
const startChange = () => {
isChanging.value = true
form.value.email = userInfo.value.email
}
// 解绑邮箱
const unbindEmail = () => {
uni.showModal({
title: '解绑邮箱',
content: '确定要解绑邮箱吗?解绑后可能影响账号安全',
success: async (res) => {
if (res.confirm) {
loading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000))
// 更新本地用户信息
const storedUserInfo = uni.getStorageSync('userInfo')
let userInfoData = storedUserInfo ? JSON.parse(storedUserInfo as string) : {}
userInfoData.email = ''
userInfoData.emailBoundTime = null
uni.setStorageSync('userInfo', JSON.stringify(userInfoData))
userInfo.value.email = ''
userInfo.value.emailBoundTime = null
uni.showToast({
title: '解绑成功',
icon: 'success'
})
} catch (error: any) {
console.error('解绑失败:', error)
uni.showToast({
title: error.message || '解绑失败',
icon: 'none'
})
} finally {
loading.value = false
}
}
}
})
}
// 格式化时间显示
const formatTime = (time: string | null): string => {
if (!time) return '未知'
const date = new Date(time)
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
}
// 导航函数
const goBack = () => {
uni.navigateBack()
}
const hideSuccess = () => {
showSuccess.value = false
}
// 生命周期
onMounted(() => {
loadUserInfo()
})
</script>
<style>
.bind-email-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.header {
background-color: #ffffff;
padding: 15px;
display: flex;
align-items: center;
border-bottom: 1px solid #e5e5e5;
}
.back-btn {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
.back-icon {
font-size: 24px;
color: #333;
}
.header-title {
font-size: 18px;
font-weight: bold;
color: #333;
flex: 1;
}
.content {
padding: 20px;
}
.already-bound {
background-color: #ffffff;
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
text-align: center;
}
.bound-icon {
display: block;
width: 60px;
height: 60px;
line-height: 60px;
background-color: #4cd964;
color: #ffffff;
font-size: 30px;
border-radius: 50%;
margin: 0 auto 15px;
}
.bound-title {
display: block;
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.bound-email {
display: block;
font-size: 16px;
color: #007aff;
margin-bottom: 10px;
font-weight: bold;
word-break: break-all;
}
.bound-time {
display: block;
font-size: 14px;
color: #999;
}
.form-item {
margin-bottom: 20px;
background-color: #ffffff;
border-radius: 10px;
padding: 15px;
}
.label {
display: block;
font-size: 16px;
color: #333;
margin-bottom: 10px;
font-weight: bold;
}
.input {
width: 100%;
height: 44px;
border: 1px solid #ddd;
border-radius: 8px;
padding: 0 15px;
font-size: 16px;
box-sizing: border-box;
}
.input:focus {
border-color: #007aff;
outline: none;
}
.input:disabled {
background-color: #f9f9f9;
color: #999;
}
.code-input-wrapper {
display: flex;
gap: 10px;
}
.code-input {
flex: 1;
height: 44px;
border: 1px solid #ddd;
border-radius: 8px;
padding: 0 15px;
font-size: 16px;
box-sizing: border-box;
}
.get-code-btn {
width: 120px;
height: 44px;
background-color: #007aff;
color: #ffffff;
border-radius: 8px;
font-size: 14px;
border: none;
}
.get-code-btn:disabled {
background-color: #cccccc;
}
.submit-btn {
width: 100%;
height: 50px;
background-color: #007aff;
color: #ffffff;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
border: none;
margin-top: 30px;
}
.submit-btn:disabled {
background-color: #cccccc;
}
.action-buttons {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 20px;
}
.change-btn {
width: 100%;
height: 50px;
background-color: #ffffff;
color: #007aff;
border: 2px solid #007aff;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
}
.unbind-btn {
width: 100%;
height: 50px;
background-color: #ffffff;
color: #ff3b30;
border: 2px solid #ff3b30;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
}
/* 成功提示模态框 */
.success-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.success-content {
width: 280px;
background-color: #ffffff;
border-radius: 15px;
padding: 30px 20px;
text-align: center;
}
.success-icon {
display: block;
width: 60px;
height: 60px;
line-height: 60px;
background-color: #4cd964;
color: #ffffff;
font-size: 30px;
border-radius: 50%;
margin: 0 auto 20px;
}
.success-title {
display: block;
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.success-text {
display: block;
font-size: 14px;
color: #666;
margin-bottom: 20px;
line-height: 1.4;
}
.success-btn {
width: 100%;
height: 44px;
background-color: #007aff;
color: #ffffff;
border-radius: 22px;
font-size: 16px;
border: none;
}
/* 响应式优化 */
@media screen and (min-width: 768px) {
.bind-email-page {
max-width: 500px;
margin: 0 auto;
border-left: 1px solid #e5e5e5;
border-right: 1px solid #e5e5e5;
}
.content {
padding: 40px;
}
}
</style>