226 lines
5.1 KiB
Vue
226 lines
5.1 KiB
Vue
<template>
|
||
<view class="avatar-wrapper" :style="wrapperStyle">
|
||
<view class="avatar-circle" :style="avatarStyle">
|
||
<image class="avatar-image" :src="avatarImage" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="level-badge" v-if="showLevel && level" :style="badgeStyle">
|
||
<text class="level-text" :style="badgeTextStyle">LV{{ level }}</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref, watch, onMounted } from 'vue';
|
||
|
||
// 定义 props
|
||
const props = defineProps({
|
||
// 用户ID(优先使用)
|
||
userId: {
|
||
type: [String, Number],
|
||
default: ''
|
||
},
|
||
// 用户昵称(作为备用)
|
||
nickname: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 自定义头像URL(来自OSS或已解析的预签名URL)
|
||
avatarUrl: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 头像尺寸(单位:rpx)
|
||
size: {
|
||
type: Number,
|
||
default: 100
|
||
},
|
||
// 是否显示等级徽章
|
||
showLevel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 等级
|
||
level: {
|
||
type: [String, Number],
|
||
default: 0
|
||
},
|
||
// 边框宽度(单位:rpx)
|
||
borderWidth: {
|
||
type: Number,
|
||
default: 4
|
||
},
|
||
// 是否需要缓存(true=用户自己的头像需要缓存,false=好友头像不缓存)
|
||
enableCache: {
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
});
|
||
|
||
// 本地头像路径
|
||
const localAvatarUrl = ref('');
|
||
|
||
// 获取用户自己的头像(直接使用后端返回的URL)
|
||
const fetchOwnAvatar = () => {
|
||
if (!props.avatarUrl) {
|
||
localAvatarUrl.value = '';
|
||
return;
|
||
}
|
||
// 直接使用后端返回的 URL
|
||
localAvatarUrl.value = props.avatarUrl;
|
||
};
|
||
|
||
// 获取好友头像(直接使用传入的URL)
|
||
const fetchFriendAvatar = () => {
|
||
localAvatarUrl.value = props.avatarUrl || '';
|
||
};
|
||
|
||
// 根据enableCache决定使用哪种获取方式
|
||
const fetchAvatar = () => {
|
||
if (props.enableCache) {
|
||
fetchOwnAvatar();
|
||
} else {
|
||
fetchFriendAvatar();
|
||
}
|
||
};
|
||
|
||
// 监听avatarUrl变化
|
||
watch(() => props.avatarUrl, () => {
|
||
fetchAvatar();
|
||
});
|
||
|
||
// 监听enableCache变化
|
||
watch(() => props.enableCache, () => {
|
||
fetchAvatar();
|
||
});
|
||
|
||
// 组件挂载时获取头像
|
||
onMounted(() => {
|
||
if (props.avatarUrl) {
|
||
fetchAvatar();
|
||
}
|
||
});
|
||
|
||
// 根据用户ID或昵称生成默认头像路径(保证同一用户头像一致)
|
||
const getDefaultAvatar = () => {
|
||
const avatarCount = 7; // static/avatar 中有7张图片
|
||
let index = 1; // 默认使用第一张
|
||
|
||
if (props.userId) {
|
||
// 优先使用用户ID生成索引
|
||
index = (parseInt(props.userId) || 0) % avatarCount + 1;
|
||
} else if (props.nickname) {
|
||
// 如果没有ID,使用昵称的字符码生成索引
|
||
let hash = 0;
|
||
for (let i = 0; i < props.nickname.length; i++) {
|
||
hash = props.nickname.charCodeAt(i) + ((hash << 5) - hash);
|
||
}
|
||
index = Math.abs(hash) % avatarCount + 1;
|
||
}
|
||
|
||
return `/static/avatar/${index}.jpeg`;
|
||
};
|
||
|
||
// 头像图片源(优先使用头像URL,否则使用默认头像)
|
||
const avatarImage = computed(() => {
|
||
if (localAvatarUrl.value) {
|
||
return localAvatarUrl.value;
|
||
}
|
||
return getDefaultAvatar();
|
||
});
|
||
|
||
// 包装器样式
|
||
const wrapperStyle = computed(() => ({
|
||
position: 'relative',
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
flexShrink: 0
|
||
}));
|
||
|
||
// 头像圆圈样式
|
||
const avatarStyle = computed(() => {
|
||
// 根据尺寸调整阴影大小
|
||
const shadowSize = props.size >= 160 ? '0 8rpx 32rpx rgba(0, 0, 0, 0.2)' : '0 4rpx 16rpx rgba(0, 0, 0, 0.2)';
|
||
return {
|
||
width: `${props.size}rpx`,
|
||
height: `${props.size}rpx`,
|
||
borderWidth: `${props.borderWidth}rpx`,
|
||
boxShadow: shadowSize
|
||
};
|
||
});
|
||
|
||
// 徽章样式(根据头像大小自适应)
|
||
const badgeStyle = computed(() => {
|
||
// 根据头像大小调整徽章样式
|
||
if (props.size >= 160) {
|
||
// 大头像(profile页面)
|
||
return {
|
||
top: '-10rpx',
|
||
right: '-10rpx',
|
||
borderRadius: '20rpx',
|
||
padding: '6rpx 16rpx',
|
||
border: '3rpx solid rgba(255, 255, 255, 0.8)',
|
||
boxShadow: '0 4rpx 12rpx rgba(0, 0, 0, 0.3)'
|
||
};
|
||
} else {
|
||
// 小头像(好友列表等)
|
||
const offset = -props.size * 0.18; // 徽章偏移量
|
||
return {
|
||
top: `${offset}rpx`,
|
||
right: `${offset}rpx`,
|
||
borderRadius: '10rpx',
|
||
padding: '0 8rpx',
|
||
border: '2rpx solid rgba(255, 255, 255, 0.8)',
|
||
boxShadow: '0 2rpx 8rpx rgba(0, 0, 0, 0.3)'
|
||
};
|
||
}
|
||
});
|
||
|
||
// 徽章文字样式(根据头像大小自适应)
|
||
const badgeTextStyle = computed(() => {
|
||
// 根据头像大小调整文字大小
|
||
const fontSize = props.size >= 160 ? 22 : 18;
|
||
return {
|
||
fontSize: `${fontSize}rpx`
|
||
};
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.avatar-wrapper {
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.avatar-circle {
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-style: solid;
|
||
border-color: rgba(255, 255, 255, 0.3);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.avatar-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.level-badge {
|
||
position: absolute;
|
||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||
}
|
||
|
||
.level-text {
|
||
font-weight: bold;
|
||
color: #e6e6e6;
|
||
line-height: 1;
|
||
}
|
||
</style>
|