133 lines
2.7 KiB
Vue
133 lines
2.7 KiB
Vue
<template>
|
||
<div
|
||
class="safe-avatar"
|
||
:style="{
|
||
width: size + 'px',
|
||
height: size + 'px'
|
||
}"
|
||
>
|
||
<img
|
||
v-if="!imageError && src"
|
||
:src="src"
|
||
:alt="alt"
|
||
@error="handleImageError"
|
||
@load="handleImageLoad"
|
||
crossorigin="anonymous"
|
||
referrerpolicy="no-referrer"
|
||
/>
|
||
<div v-else class="avatar-placeholder">
|
||
<span class="avatar-text">{{ avatarText }}</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, watch } from 'vue'
|
||
|
||
interface Props {
|
||
src?: string
|
||
alt?: string
|
||
size?: number
|
||
name?: string
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
src: '',
|
||
alt: '头像',
|
||
size: 32,
|
||
name: '用户'
|
||
})
|
||
|
||
const imageError = ref(false)
|
||
|
||
// 调试:监听src变化
|
||
watch(() => props.src, (newSrc) => {
|
||
console.log('🖼️ SafeAvatar - 头像URL变化:', newSrc)
|
||
console.log('🖼️ SafeAvatar - 头像URL类型:', typeof newSrc)
|
||
console.log('🖼️ SafeAvatar - 头像URL长度:', newSrc?.length)
|
||
// 重置错误状态
|
||
imageError.value = false
|
||
}, { immediate: true })
|
||
|
||
// 根据用户名生成头像文字
|
||
const avatarText = computed(() => {
|
||
if (props.name) {
|
||
// 如果是中文名,取最后一个字
|
||
if (/[\u4e00-\u9fa5]/.test(props.name)) {
|
||
return props.name.slice(-1)
|
||
}
|
||
// 如果是英文名,取首字母
|
||
return props.name.charAt(0).toUpperCase()
|
||
}
|
||
return '用'
|
||
})
|
||
|
||
const handleImageError = (event: Event) => {
|
||
console.error('🚨 SafeAvatar - 头像加载失败:', props.src, event)
|
||
imageError.value = true
|
||
}
|
||
|
||
const handleImageLoad = () => {
|
||
console.log('✅ SafeAvatar - 头像加载成功:', props.src)
|
||
imageError.value = false
|
||
}
|
||
</script>
|
||
|
||
export default {
|
||
name: 'SafeAvatar'
|
||
}
|
||
|
||
<style scoped>
|
||
.safe-avatar {
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f0f0f0;
|
||
}
|
||
|
||
.safe-avatar img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.avatar-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.avatar-text {
|
||
color: white;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 根据尺寸调整字体大小 */
|
||
.safe-avatar[style*="width: 24px"] .avatar-text {
|
||
font-size: 10px;
|
||
}
|
||
|
||
.safe-avatar[style*="width: 32px"] .avatar-text {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.safe-avatar[style*="width: 40px"] .avatar-text {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.safe-avatar[style*="width: 64px"] .avatar-text,
|
||
.safe-avatar[style*="width: 80px"] .avatar-text {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.safe-avatar[style*="width: 100px"] .avatar-text {
|
||
font-size: 24px;
|
||
}
|
||
</style>
|