index.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <template>
  2. <view class="ss-upload-avatar">
  3. <view class="avatar-container">
  4. <!-- 直接在头像图片上使用微信原生头像选择功能 -->
  5. <button
  6. class="avatar-btn"
  7. open-type="chooseAvatar"
  8. @chooseavatar="onChooseAvatar"
  9. >
  10. <image class="avatar-img" :src="innerUrl" mode="aspectFill" />
  11. </button>
  12. <text class="avatar-tips">点击图像修改</text>
  13. </view>
  14. <!-- 裁剪器(隐藏,通过 ref 调用) -->
  15. <up-cropper
  16. ref="cropperRef"
  17. :canChangeSize="false"
  18. areaWidth="300rpx"
  19. areaHeight="300rpx"
  20. exportWidth="260rpx"
  21. exportHeight="260rpx"
  22. @confirm="onCropConfirm"
  23. />
  24. </view>
  25. </template>
  26. <script setup>
  27. import { ref, watch, computed } from 'vue'
  28. import upload from '@/utils/upload.js'
  29. import { getImageUrl } from '@/utils/util.js'
  30. /* -------------------- 接口 -------------------- */
  31. const props = defineProps({
  32. modelValue: { type: String, default: '' }
  33. })
  34. const emit = defineEmits(['update:modelValue', 'updated'])
  35. /* -------------------- 响应式 -------------------- */
  36. // 存储服务器路径(用于提交给后端)
  37. const serverPath = ref(props.modelValue)
  38. // 计算显示URL(用于回显)
  39. const innerUrl = computed(() => {
  40. if (!serverPath.value) return '/static/images/yishuzhao_nv.svg'
  41. // 如果是临时路径(微信临时文件)或本地静态资源,直接显示
  42. if (serverPath.value.startsWith('http') ||
  43. serverPath.value.startsWith('wxfile://') ||
  44. serverPath.value.startsWith('/static/')) {
  45. return serverPath.value
  46. }
  47. // 如果是服务器路径,通过 getImageUrl 转换
  48. return getImageUrl(serverPath.value)
  49. })
  50. const cropperRef = ref(null)
  51. watch(() => props.modelValue, v => serverPath.value = v)
  52. /* -------- 微信头像选择回调 -------- */
  53. async function onChooseAvatar(e) {
  54. console.log('微信头像选择:', e)
  55. const avatarUrl = e.detail.avatarUrl
  56. if (avatarUrl) {
  57. // 先显示临时图片(即时反馈)
  58. serverPath.value = avatarUrl
  59. // 上传到服务器
  60. try {
  61. const path = await upload.uploadAvatar(avatarUrl)
  62. console.log('上传成功,服务器路径:', path)
  63. // 更新为服务器返回的 path(只保存 path,不保存完整URL)
  64. serverPath.value = path
  65. emit('update:modelValue', path)
  66. emit('updated', path)
  67. uni.showToast({ title: '头像上传成功', icon: 'success' })
  68. } catch (error) {
  69. console.error('上传失败:', error)
  70. // 上传失败,恢复原头像
  71. serverPath.value = props.modelValue
  72. uni.showToast({ title: '上传失败,请重试', icon: 'none' })
  73. }
  74. } else {
  75. uni.showToast({ title: '获取头像失败', icon: 'error' })
  76. }
  77. }
  78. /* -------- 裁剪完成 -------- */
  79. async function onCropConfirm(e) {
  80. const tempPath = e.path
  81. // 先显示临时图片
  82. serverPath.value = tempPath
  83. // 上传到服务器
  84. try {
  85. const path = await upload.uploadAvatar(tempPath)
  86. console.log('裁剪并上传成功,服务器路径:', path)
  87. // 更新为服务器返回的 path
  88. serverPath.value = path
  89. emit('update:modelValue', path)
  90. emit('updated', path)
  91. uni.showToast({ title: '裁剪并上传成功', icon: 'success' })
  92. } catch (error) {
  93. console.error('上传失败:', error)
  94. // 上传失败,恢复原头像
  95. serverPath.value = props.modelValue
  96. uni.showToast({ title: '上传失败,请重试', icon: 'none' })
  97. }
  98. }
  99. </script>
  100. <style lang="scss" scoped>
  101. .ss-upload-avatar {
  102. display: flex;
  103. align-items: center;
  104. .avatar-container {
  105. display: flex;
  106. // flex-direction: column;
  107. align-items: center;
  108. gap: 20rpx;
  109. }
  110. .avatar-btn {
  111. // 重置按钮样式,让它看起来就像图片
  112. background: none;
  113. border: none;
  114. padding: 0;
  115. margin: 0;
  116. border-radius: 0;
  117. &::after {
  118. border: none;
  119. }
  120. }
  121. .avatar-img {
  122. width: 160rpx;
  123. height: 160rpx;
  124. border-radius: 50%;
  125. border: 2rpx solid #eee;
  126. background-color: #f5f5f5;
  127. display: block; // 确保图片正常显示
  128. }
  129. .avatar-tips {
  130. font-size: 32rpx;
  131. color: #666;
  132. text-align: center;
  133. }
  134. }
  135. </style>