index.vue 4.34 KB
<!--
 * @Date: 2026-01-29 22:25:57
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-01-29 22:54:41
 * @FilePath: /manulife-weapp/src/pages/avatar/index.vue
 * @Description: 修改头像页面
-->
<template>
  <view class="min-h-screen bg-white flex flex-col">
    <!-- NavHeader -->
    <NavHeader title="修改头像" />
    <!-- Main Content -->
    <view class="flex-1 flex flex-col items-center pt-[120rpx]">
      <view class="relative mb-[60rpx]" @tap="onChangeAvatar">
        <nut-avatar size="large" class="!w-[240rpx] !h-[240rpx] shadow-lg border-4 border-gray-100">
          <img :src="avatarUrl" />
        </nut-avatar>
        <view class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-black/30 rounded-full p-[16rpx]">
          <IconFont name="photograph" color="#fff" size="24" />
        </view>
      </view>

      <text class="text-gray-500 text-[32rpx] mb-[12rpx]">点击更换头像</text>
    </view>

    <!-- Footer Buttons -->
    <view class="px-[40rpx] pb-[80rpx] w-full">
      <view class="flex gap-[32rpx]">
        <nut-button plain type="primary" block class="flex-1 !rounded-[48rpx]" @click="onCancel">取消</nut-button>
        <nut-button type="primary" block class="flex-1 !rounded-[48rpx]" @click="onSave">保存</nut-button>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { useGo } from '@/hooks/useGo'
import IconFont from '@/components/IconFont.vue'
import NavHeader from '@/components/NavHeader.vue'
import Taro from '@tarojs/taro'
import defaultAvatar from '@/assets/images/icon/avatar.svg'
import { uploadImageToQiniuAPI } from '@/api/common'
import { updateProfileAPI } from '@/api/user'

const go = useGo()
const avatarUrl = ref(defaultAvatar)
const tempAvatarUrl = ref('') // 临时存储上传后的头像URL
const uploading = ref(false) // 上传状态

/**
 * @description 更换头像
 */
const onChangeAvatar = () => {
  Taro.chooseImage({
    count: 1,
    sizeType: ['compressed'], // 使用压缩图,减少上传时间
    sourceType: ['album', 'camera'],
    success: async (res) => {
      const tempFile = res.tempFiles[0]

      // 检查文件大小(5MB限制)
      if (tempFile.size > 5 * 1024 * 1024) {
        Taro.showToast({
          title: '图片大小不能超过5MB',
          icon: 'none',
        })
        return
      }

      // 显示上传进度
      Taro.showLoading({ title: '上传中...', mask: true })
      uploading.value = true

      try {
        // 上传到七牛云
        const uploadRes = await uploadImageToQiniuAPI({
          filePath: tempFile.path
        })

        Taro.hideLoading()
        uploading.value = false

        if (uploadRes.code === 1) {
          // 上传成功,更新头像显示
          avatarUrl.value = uploadRes.data.src
          tempAvatarUrl.value = uploadRes.data.src
          Taro.showToast({
            title: '上传成功',
            icon: 'success'
          })
        } else {
          Taro.showToast({
            title: uploadRes.msg || '上传失败',
            icon: 'none'
          })
        }
      } catch (err) {
        Taro.hideLoading()
        uploading.value = false
        console.error('上传头像失败:', err)
        Taro.showToast({
          title: '上传失败,请稍后重试',
          icon: 'none'
        })
      }
    }
  })
}

/**
 * @description 取消修改
 */
const onCancel = () => {
  Taro.navigateBack()
}

/**
 * @description 保存头像
 */
const onSave = async () => {
  // 如果没有上传新头像,直接返回
  if (!tempAvatarUrl.value) {
    Taro.showToast({
      title: '请先选择头像',
      icon: 'none'
    })
    return
  }

  // 保存到服务器
  Taro.showLoading({ title: '保存中...', mask: true })

  try {
    const res = await updateProfileAPI({
      avatar_url: tempAvatarUrl.value
    })

    Taro.hideLoading()

    if (res.code === 1) {
      Taro.showToast({
        title: '保存成功',
        icon: 'success'
      })
      setTimeout(() => {
        Taro.navigateBack()
      }, 1500)
    } else {
      Taro.showToast({
        title: res.msg || '保存失败',
        icon: 'none'
      })
    }
  } catch (err) {
    Taro.hideLoading()
    console.error('保存头像失败:', err)
    Taro.showToast({
      title: '保存失败,请稍后重试',
      icon: 'none'
    })
  }
}
</script>