初始化移动端提交
This commit is contained in:
203
sheep/components/s-avatar/s-avatar.vue
Normal file
203
sheep/components/s-avatar/s-avatar.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<!--
|
||||
默认头像组件
|
||||
参考 zt-vue-element 的 CropperAvatar 实现
|
||||
支持默认头像逻辑:头像URL优先,否则显示用户昵称首字母
|
||||
-->
|
||||
<template>
|
||||
<view class="avatar-container" @click="handleClick">
|
||||
<!-- 有头像时显示图片 -->
|
||||
<image
|
||||
v-if="avatarUrl"
|
||||
:src="avatarUrl"
|
||||
:style="avatarStyle"
|
||||
class="avatar-image"
|
||||
mode="aspectFill"
|
||||
@error="handleImageError"
|
||||
/>
|
||||
|
||||
<!-- 无头像时显示首字母头像 -->
|
||||
<view
|
||||
v-else
|
||||
class="avatar-initial"
|
||||
:style="[avatarStyle, initialStyle]"
|
||||
>
|
||||
{{ initialText }}
|
||||
</view>
|
||||
|
||||
<!-- 编辑按钮 -->
|
||||
<view v-if="editable && !readonly" class="edit-mask">
|
||||
<text class="edit-icon">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
// 头像URL
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 用户昵称,用于生成首字母
|
||||
nickname: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 头像尺寸
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 80
|
||||
},
|
||||
// 是否可编辑
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否只读
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 背景色(用于首字母头像)
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#0055A2'
|
||||
},
|
||||
// 文字色(用于首字母头像)
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
// 自定义初始字符
|
||||
initial: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits(['click', 'error'])
|
||||
|
||||
// 响应式数据
|
||||
const avatarUrl = ref(props.src)
|
||||
const imageError = ref(false)
|
||||
|
||||
// 计算属性
|
||||
const avatarStyle = computed(() => {
|
||||
const size = typeof props.size === 'number' ? `${props.size}rpx` : props.size
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: '50%'
|
||||
}
|
||||
})
|
||||
|
||||
const initialStyle = computed(() => ({
|
||||
backgroundColor: props.bgColor,
|
||||
color: props.textColor,
|
||||
fontSize: `${Math.floor(props.size * 0.4)}rpx`,
|
||||
fontWeight: 'bold'
|
||||
}))
|
||||
|
||||
// 生成初始字符逻辑
|
||||
const initialText = computed(() => {
|
||||
// 如果提供了自定义初始字符,使用它
|
||||
if (props.initial) {
|
||||
return props.initial.charAt(0).toUpperCase()
|
||||
}
|
||||
|
||||
// 如果有昵称,取首字母
|
||||
if (props.nickname) {
|
||||
// 处理中文和英文
|
||||
const firstChar = props.nickname.charAt(0)
|
||||
// 如果是中文,直接使用
|
||||
if (/[\u4e00-\u9fa5]/.test(firstChar)) {
|
||||
return firstChar
|
||||
}
|
||||
// 如果是英文,转大写
|
||||
return firstChar.toUpperCase()
|
||||
}
|
||||
|
||||
// 默认返回用户图标
|
||||
return '用'
|
||||
})
|
||||
|
||||
// 监听src变化
|
||||
watch(() => props.src, (newSrc) => {
|
||||
avatarUrl.value = newSrc
|
||||
imageError.value = false
|
||||
}, { immediate: true })
|
||||
|
||||
// 方法
|
||||
const handleClick = () => {
|
||||
if (!props.readonly) {
|
||||
emit('click')
|
||||
}
|
||||
}
|
||||
|
||||
const handleImageError = () => {
|
||||
imageError.value = true
|
||||
avatarUrl.value = '' // 清空URL,显示首字母头像
|
||||
emit('error')
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
refresh: () => {
|
||||
avatarUrl.value = props.src
|
||||
imageError.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover .edit-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-initial {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--theme-primary, #0055A2);
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.edit-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
color: #ffffff;
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user