PhoneSettingPage.vue 7.14 KB
<!--
 * @Date: 2025-06-13 11:23:38
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-06-13 11:26:53
 * @FilePath: /mlaj/src/views/profile/settings/PhoneSettingPage.vue
 * @Description: 手机号修改页面
-->
<template>
  <AppLayout title="">
    <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen">
      <div class="px-4 py-6">
        <FrostedGlass class="rounded-xl overflow-hidden">
          <div class="p-4">
            <div class="space-y-4">
              <!-- 当前手机号显示 -->
              <div class="mb-6">
                <label class="block text-sm font-medium text-gray-700 mb-2">当前手机号</label>
                <div class="w-full px-4 py-3 bg-gray-100 border border-gray-300 rounded-lg text-gray-600">
                  {{ currentPhone || '未设置' }}
                </div>
              </div>

              <!-- 新手机号输入 -->
              <div class="mb-4">
                <label for="phone" class="block text-sm font-medium text-gray-700 mb-2">
                  新手机号 <span class="text-red-500">*</span>
                </label>
                <input
                  id="phone"
                  v-model="formData.phone"
                  autocomplete="tel"
                  type="tel"
                  required
                  pattern="^1[3-9]\d{9}$"
                  maxlength="11"
                  placeholder="请输入新的手机号"
                  @input="formData.phone = formData.phone.replace(/\D/g, '')"
                  @blur="validatePhone"
                  class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
                />
                <div v-if="phoneError" class="text-red-500 text-sm mt-1">{{ phoneError }}</div>
              </div>

              <!-- 验证码输入 -->
              <div class="mb-6">
                <label for="verificationCode" class="block text-sm font-medium text-gray-700 mb-2">
                  验证码 <span class="text-red-500">*</span>
                </label>
                <div class="flex space-x-2">
                  <input
                    id="verificationCode"
                    v-model="formData.verificationCode"
                    type="text"
                    required
                    maxlength="6"
                    placeholder="请输入验证码"
                    class="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
                  />
                  <button
                    type="button"
                    :disabled="countdown > 0 || !isPhoneValid"
                    @click="sendVerificationCode"
                    class="px-4 py-3 border border-transparent text-sm font-medium rounded-lg text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap"
                  >
                    {{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
                  </button>
                </div>
              </div>

              <!-- 保存按钮 -->
              <van-button
                @click="handlePhoneChange"
                type="primary"
                block
                round
                :loading="loading"
              >
                保存修改
              </van-button>
            </div>
          </div>
        </FrostedGlass>
      </div>
    </div>
  </AppLayout>
</template>

<script setup>
import { ref, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router';
import AppLayout from '@/components/layout/AppLayout.vue';
import FrostedGlass from '@/components/ui/FrostedGlass.vue';
import { getUserInfoAPI, updateUserInfoAPI } from '@/api/users';
import { smsAPI } from '@/api/common';
import { showToast } from 'vant';
import { useTitle } from '@vueuse/core';
import { useAuth } from '@/contexts/auth';

const $route = useRoute();
useTitle($route.meta.title);

// 获取用户认证状态
const { currentUser } = useAuth();

// 表单数据
const formData = ref({
  phone: '',
  verificationCode: ''
});

// 当前手机号
const currentPhone = ref('');

// 状态管理
const loading = ref(false);
const countdown = ref(0);
const phoneError = ref('');

// 手机号验证
const isPhoneValid = computed(() => {
  const phoneRegex = /^1[3-9]\d{9}$/;
  return phoneRegex.test(formData.value.phone);
});

/**
 * 验证手机号格式
 */
const validatePhone = () => {
  if (!formData.value.phone) {
    phoneError.value = '请输入手机号';
    return false;
  }
  if (!isPhoneValid.value) {
    phoneError.value = '请输入正确的手机号格式';
    return false;
  }
  if (formData.value.phone === currentPhone.value) {
    phoneError.value = '新手机号不能与当前手机号相同';
    return false;
  }
  phoneError.value = '';
  return true;
};

/**
 * 发送验证码
 */
const sendVerificationCode = async () => {
  if (!validatePhone()) {
    return;
  }

  try {
    // 调用发送验证码API
    const { code } = await smsAPI({ mobile: formData.value.phone });
    if (code) {
      showToast('验证码已发送');
      // 开始倒计时
      countdown.value = 60;
      const timer = setInterval(() => {
        countdown.value--;
        if (countdown.value <= 0) {
          clearInterval(timer);
        }
      }, 1000);
    }
  } catch (error) {
    console.error('发送验证码失败:', error);
    showToast('发送验证码失败,请稍后重试');
  }
};

/**
 * 处理手机号修改
 */
const handlePhoneChange = async () => {
  if (!validatePhone()) {
    return;
  }

  if (!formData.value.verificationCode) {
    showToast('请输入验证码');
    return;
  }

  if (formData.value.verificationCode.length !== 6) {
    showToast('请输入6位验证码');
    return;
  }

  loading.value = true;

  try {
    const { code, data } = await updateUserInfoAPI({ 
      mobile: formData.value.phone,
      sms_code: formData.value.verificationCode
    });
    
    if (code) {
      // 更新auth上下文中的用户信息
      currentUser.value = {
        ...currentUser.value,
        mobile: formData.value.phone
      };
      // 更新localStorage中的用户信息
      localStorage.setItem('currentUser', JSON.stringify(currentUser.value));
      
      // 更新当前显示的手机号
      currentPhone.value = formData.value.phone;
      
      // 清空表单
      formData.value.phone = '';
      formData.value.verificationCode = '';
      
      showToast('手机号修改成功');
    }
  } catch (error) {
    console.error('手机号修改失败:', error);
    showToast('手机号修改失败,请重试');
  } finally {
    loading.value = false;
  }
};

// 获取用户信息
onMounted(async () => {
  try {
    const response = await getUserInfoAPI();
    if (response.data) {
      currentPhone.value = response.data.user.mobile || '';
    }
  } catch (error) {
    console.error('获取用户信息失败:', error);
  }
});
</script>

<style scoped>
/* 自定义样式 */
.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>