AvatarSettingPage.vue 5.16 KB
<!--
 * @Date: 2025-03-24 13:04:21
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-11-11 17:09:16
 * @FilePath: /mlaj/src/views/profile/settings/AvatarSettingPage.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="flex flex-col items-center">
              <div class="mb-6">
                <van-image
                  round
                  :src="
                    userAvatar || 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
                  "
                  alt="用户头像"
                  class="rounded-full border-4 border-white shadow-lg"
                  width="8rem"
                  height="8rem"
                  fit="cover"
                />
              </div>
              <p class="text-sm text-gray-500 mb-4">支持 jpg、png 格式,大小不超过 2MB</p>
              <van-uploader
                :after-read="handleAvatarChange"
                :max-size="2 * 1024 * 1024"
                accept="image/jpeg,image/png"
                :preview-image="false"
              >
                <van-button
                  block
                  type="primary"
                  class="max-w-xs"
                >
                  选择新头像
                </van-button>
              </van-uploader>
            </div>
          </div>
        </FrostedGlass>
      </div>
    </div>
  </AppLayout>
</template>

<script setup>
import { ref, onMounted } from "vue";
import AppLayout from "@/components/layout/AppLayout.vue";
import FrostedGlass from "@/components/ui/FrostedGlass.vue";
import { getUserInfoAPI, updateUserInfoAPI } from "@/api/users";
import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common';
import { showToast, showLoadingToast } from 'vant';
import { qiniuFileHash } from '@/utils/qiniuFileHash';
import { useTitle } from '@vueuse/core';
import { useAuth } from '@/contexts/auth';

const $route = useRoute();
const { currentUser } = useAuth();
useTitle($route.meta.title);

// 用户头像
const userAvatar = ref("");

// 获取用户信息
onMounted(async () => {
  try {
    const response = await getUserInfoAPI();
    if (response.data) {
      userAvatar.value = response.data.user.avatar;
    }
  } catch (error) {
    console.error("获取用户信息失败:", error);
  }
});

/**
 * 获取文件哈希(与七牛云ETag一致)
 * @param {File} file 文件对象
 * @returns {Promise<string>} 哈希字符串
 * 注释:使用 qiniuFileHash 进行计算,替代浏览器MD5方案。
 */
const getFileMD5 = async (file) => {
    return await qiniuFileHash(file)
}

// 上传到七牛云
const uploadToQiniu = async (file, token, fileName) => {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('token', token)
  formData.append('key', fileName)

  const config = {
    headers: { 'Content-Type': 'multipart/form-data' }
  }

  // 根据协议选择上传地址
  const qiniuUploadUrl = window.location.protocol === 'https:'
    ? 'https://up.qbox.me'
    : 'http://upload.qiniu.com'

  return await qiniuUploadAPI(qiniuUploadUrl, formData, config)
}

// 处理头像上传
const handleAvatarChange = async (file) => {
  // const toast = showLoadingToast({
  //   message: '上传中...',
  //   forbidClick: true,
  // });

  try {
    // 获取MD5值
    const md5 = await getFileMD5(file.file)

    // 获取七牛token
    const tokenResult = await qiniuTokenAPI({
      name: file.file.name,
      hash: md5
    })

    // 文件已存在,直接使用
    if (tokenResult.data) {
      userAvatar.value = tokenResult.data.src;
      // 更新用户信息
      await updateUserInfoAPI({ avatar: tokenResult.data.src });
      showToast('头像上传成功');
      return;
    }

    // 新文件上传
    if (tokenResult.token) {
      const suffix = /.[^.]+$/.exec(file.file.name) || ''
      const fileName = `mlaj/upload/avatar/${currentUser.value.mobile}/${md5}${suffix}`

      const { filekey, image_info } = await uploadToQiniu(
        file.file,
        tokenResult.token,
        fileName
      )

      if (filekey) {
        // 保存文件信息
        const { data } = await saveFileAPI({
          name: file.file.name,
          filekey,
          hash: md5,
          height: image_info?.height,
          width: image_info?.width
        })

        if (data) {
          userAvatar.value = data.src;
          // 更新用户信息
          await updateUserInfoAPI({ avatar: data.src });
          // 更新auth上下文中的用户信息
          currentUser.value = {
            ...currentUser.value,
            avatar: userAvatar.value
          };
          // 更新localStorage中的用户信息
          localStorage.setItem('currentUser', JSON.stringify(currentUser.value));
          showToast('头像上传成功');
        }
      }
    }
  } catch (error) {
    console.error('头像上传失败:', error)
    showToast('头像上传失败,请重试')
  } finally {
    // toast.close();
  }
};
</script>