| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- <template>
- <view class="ss-upload-avatar">
- <view class="avatar-container">
- <!-- 直接在头像图片上使用微信原生头像选择功能 -->
- <button
- class="avatar-btn"
- open-type="chooseAvatar"
- @chooseavatar="onChooseAvatar"
- >
- <image class="avatar-img" :src="innerUrl" mode="aspectFill" />
- </button>
-
- <text class="avatar-tips">点击图像修改</text>
- </view>
- <!-- 裁剪器(隐藏,通过 ref 调用) -->
- <up-cropper
- ref="cropperRef"
- :canChangeSize="false"
- areaWidth="300rpx"
- areaHeight="300rpx"
- exportWidth="260rpx"
- exportHeight="260rpx"
- @confirm="onCropConfirm"
- />
- </view>
- </template>
- <script setup>
- import { ref, watch, computed } from 'vue'
- import upload from '@/utils/upload.js'
- import { getImageUrl } from '@/utils/util.js'
- /* -------------------- 接口 -------------------- */
- const props = defineProps({
- modelValue: { type: String, default: '' }
- })
- const emit = defineEmits(['update:modelValue', 'updated'])
- /* -------------------- 响应式 -------------------- */
- // 存储服务器路径(用于提交给后端)
- const serverPath = ref(props.modelValue)
- // 计算显示URL(用于回显)
- const innerUrl = computed(() => {
- if (!serverPath.value) return '/static/images/yishuzhao_nv.svg'
- // 如果是临时路径(微信临时文件)或本地静态资源,直接显示
- if (serverPath.value.startsWith('http') ||
- serverPath.value.startsWith('wxfile://') ||
- serverPath.value.startsWith('/static/')) {
- return serverPath.value
- }
- // 如果是服务器路径,通过 getImageUrl 转换
- return getImageUrl(serverPath.value)
- })
- const cropperRef = ref(null)
- watch(() => props.modelValue, v => serverPath.value = v)
- /* -------- 微信头像选择回调 -------- */
- async function onChooseAvatar(e) {
- console.log('微信头像选择:', e)
- const avatarUrl = e.detail.avatarUrl
- if (avatarUrl) {
- // 先显示临时图片(即时反馈)
- serverPath.value = avatarUrl
- // 上传到服务器
- try {
- const path = await upload.uploadAvatar(avatarUrl)
- console.log('上传成功,服务器路径:', path)
- // 更新为服务器返回的 path(只保存 path,不保存完整URL)
- serverPath.value = path
- emit('update:modelValue', path)
- emit('updated', path)
- uni.showToast({ title: '头像上传成功', icon: 'success' })
- } catch (error) {
- console.error('上传失败:', error)
- // 上传失败,恢复原头像
- serverPath.value = props.modelValue
- uni.showToast({ title: '上传失败,请重试', icon: 'none' })
- }
- } else {
- uni.showToast({ title: '获取头像失败', icon: 'error' })
- }
- }
- /* -------- 裁剪完成 -------- */
- async function onCropConfirm(e) {
- const tempPath = e.path
- // 先显示临时图片
- serverPath.value = tempPath
- // 上传到服务器
- try {
- const path = await upload.uploadAvatar(tempPath)
- console.log('裁剪并上传成功,服务器路径:', path)
- // 更新为服务器返回的 path
- serverPath.value = path
- emit('update:modelValue', path)
- emit('updated', path)
- uni.showToast({ title: '裁剪并上传成功', icon: 'success' })
- } catch (error) {
- console.error('上传失败:', error)
- // 上传失败,恢复原头像
- serverPath.value = props.modelValue
- uni.showToast({ title: '上传失败,请重试', icon: 'none' })
- }
- }
- </script>
- <style lang="scss" scoped>
- .ss-upload-avatar {
- display: flex;
- align-items: center;
-
- .avatar-container {
- display: flex;
- // flex-direction: column;
- align-items: center;
- gap: 20rpx;
- }
-
- .avatar-btn {
- // 重置按钮样式,让它看起来就像图片
- background: none;
- border: none;
- padding: 0;
- margin: 0;
- border-radius: 0;
-
- &::after {
- border: none;
- }
- }
-
- .avatar-img {
- width: 160rpx;
- height: 160rpx;
- border-radius: 50%;
- border: 2rpx solid #eee;
- background-color: #f5f5f5;
- display: block; // 确保图片正常显示
- }
-
- .avatar-tips {
- font-size: 32rpx;
- color: #666;
- text-align: center;
- }
- }
- </style>
|