You need to sign in or sign up before continuing.
index.vue 7.16 KB
<!--
 * @Date: 2026-01-29 22:25:57
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-02-11 14:06:51
 * @FilePath: /manulife-weapp/src/pages/avatar/index.vue
 * @Description: 修改头像页面
-->
<template>
  <view class="min-h-screen bg-[#F9FAFB] flex flex-col">
    <!-- NavHeader -->
    <NavHeader title="修改头像" />

    <!-- Main Content -->
    <view class="flex-1 flex flex-col items-center pt-[60rpx]">
      <!-- Avatar Card -->
      <view
        class="bg-white rounded-[32rpx] p-[60rpx] flex flex-col items-center shadow-sm mx-[40rpx] w-[calc(100%-80rpx)]">
        <view class="relative mb-[40rpx] group" @tap="onChangeAvatar">
          <!-- Avatar Container with Ring -->
          <view
            class="w-[240rpx] h-[240rpx] rounded-full p-[6rpx] bg-white shadow-lg border border-gray-100 flex items-center justify-center relative z-10">
            <nut-avatar size="large" class="!w-full !h-full !rounded-full overflow-hidden !bg-gray-50">
              <img :src="avatarUrl" class="w-full h-full object-cover" />
            </nut-avatar>
          </view>

          <!-- Edit Badge (Bottom Right) -->
          <view
            class="absolute bottom-[6rpx] right-[6rpx] w-[72rpx] h-[72rpx] bg-[#007AFF] text-white rounded-full flex items-center justify-center shadow-md border-[4rpx] border-white z-20 active:scale-95 transition-transform">
            <IconFont name="photograph" color="#fff" size="20" />
          </view>
        </view>

        <text class="text-gray-900 text-[34rpx] font-medium mb-[12rpx]">更换头像</text>
        <text class="text-gray-400 text-[26rpx]">点击上方图标选择新图片</text>
      </view>
    </view>

    <!-- Footer Buttons -->
    <view class="px-[40rpx] pb-[calc(40rpx+env(safe-area-inset-bottom))] w-full mb-[20rpx]">
      <view class="flex gap-[32rpx]">
        <nut-button plain type="default" block
          class="flex-1 !rounded-[48rpx] !h-[96rpx] !border-gray-200 !text-gray-600 !bg-white !text-[30rpx]"
          @click="onCancel">
          取消
        </nut-button>
        <nut-button type="primary" block
          class="flex-1 !rounded-[48rpx] !h-[96rpx] !bg-[#007AFF] !border-[#007AFF] shadow-lg shadow-blue-100 !text-[30rpx]"
          @click="onSave">
          保存
        </nut-button>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import IconFont from '@/components/icons/IconFont.vue'
import NavHeader from '@/components/navigation/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>