index.vue 6.22 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" style="object-fit: cover; width: 100%; height: 100%;" />
        </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 IconFont from '@/components/IconFont.vue'
import NavHeader from '@/components/NavHeader.vue'
import Taro, { useLoad } from '@tarojs/taro'
import defaultAvatar from '@/assets/images/icon/avatar.svg'
import { updateProfileAPI, getProfileAPI } from '@/api/user'
import BASE_URL from '@/utils/config'

const avatarUrl = ref(defaultAvatar)
const tempAvatarData = ref(null) // 临时存储上传后的完整 avatar 对象

/**
 * @description 获取用户当前头像
 * @description 页面加载时调用,如果用户已有头像则显示
 */
const fetchCurrentAvatar = async () => {
  try {
    const res = await getProfileAPI()
    if (res.code === 1 && res.data?.user?.avatar?.src) {
      // 用户已有头像,显示当前头像
      avatarUrl.value = res.data.user.avatar.src
    }
  } catch (err) {
    console.error('获取用户头像失败:', err)
    // 失败时使用默认头像,不显示错误提示
  }
}

/**
 * @description 页面加载时获取用户当前头像
 */
useLoad(() => {
  fetchCurrentAvatar()
})

/**
 * @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 })

      // 参考老来赛:直接用 Taro.uploadFile 上传到服务器
      Taro.uploadFile({
        url: BASE_URL + '/admin/?m=srv&a=upload&image_audit=1',
        filePath: tempFile.path,
        name: 'file',
        success: (uploadRes) => {
          Taro.hideLoading()

          const data = JSON.parse(uploadRes.data)

          // 检查是否为审核不通过
          if (data.data && data.data.audit_code == -1) {
            Taro.showModal({
              title: '温馨提示',
              content: data.msg || '图片审核不通过',
              showCancel: false
            })
            return
          }

          if (data.code === 0) {  // 注意:老来赛后端 code=0 表示成功
            avatarUrl.value = data.data.src
            // 保存完整的 avatar 对象,用于后续更新
            // 根据实际接口返回结构映射字段
            tempAvatarData.value = {
              name: data.data.file?.name || data.data.title || '',
              hash: data.data.res?.hash || '',
              src: data.data.src || '',
              height: String(data.data.height || data.data.res?.image_info?.height || '0'),
              width: String(data.data.width || data.data.res?.image_info?.width || '0'),
              size: data.data.file?.size || data.data.res?.filesize || 0
            }
            console.log('上传成功,avatar 对象:', tempAvatarData.value)
            Taro.showToast({
              title: '上传成功',
              icon: 'success'
            })
          } else {
            Taro.showToast({
              title: data.msg || '上传失败',
              icon: 'none'
            })
          }
        },
        fail: () => {
          Taro.hideLoading()
          Taro.showToast({
            title: '上传失败,请稍后重试',
            icon: 'none'
          })
        }
      })
    }
  })
}

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

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

  // 检查 avatar 对象完整性
  if (!tempAvatarData.value.src) {
    console.error('avatar 对象缺少 src 字段')
    Taro.showToast({
      title: '上传数据异常,请重试',
      icon: 'none'
    })
    return
  }

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

  try {
    // ✅ 修复:传递完整的 avatar 对象(符合 API 规范)
    const res = await updateProfileAPI({
      avatar: tempAvatarData.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>