| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- <template>
- <view class="user-avatar" :style="wrapStyle" @click="$emit('click')">
- <image v-if="src" class="user-avatar-img" :src="src" mode="aspectFill" :style="imgStyle" />
- <view v-else class="user-avatar-placeholder" :class="'gradient-' + gradientIndex" :style="placeholderStyle">
- <text class="user-avatar-text" :style="textStyle">{{ displayText }}</text>
- </view>
- </view>
- </template>
- <script setup>
- import { computed } from 'vue'
- /**
- * 文字提取:中文 ≤2 全量,>2 取最后两位;英文取前两词首字母大写,一词取前两位
- */
- function getAvatarText(name) {
- const n = String(name || '').trim()
- if (!n) return '?'
- // 是否主要为中文(CJK):非空字符里 CJK 占多数则按中文处理
- const cjkCount = (n.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length
- const letterCount = n.replace(/\s/g, '').length
- const isCJK = letterCount > 0 && cjkCount >= letterCount / 2
- if (isCJK) {
- if (n.length <= 2) return n
- return n.slice(-2)
- }
- // 英文/其他:按空格分词
- const words = n.split(/\s+/).filter(Boolean)
- if (words.length >= 2) {
- const a = (words[0][0] || '').toUpperCase()
- const b = (words[1][0] || '').toUpperCase()
- return (a + b) || '?'
- }
- if (words.length === 1 && words[0].length >= 2) return words[0].slice(0, 2).toUpperCase()
- if (words.length === 1 && words[0].length === 1) return words[0].toUpperCase()
- return '?'
- }
- /** 渐变色库 [左上深, 右下浅],135deg,共 20 组 */
- const AVATAR_GRADIENT_PAIRS = [
- ['#1e3a8a', '#93c5fd'], ['#166534', '#86efac'], ['#c2410c', '#fdba74'], ['#b91c1c', '#fca5a5'], ['#5b21b6', '#c4b5fd'],
- ['#0f766e', '#5eead4'], ['#3730a3', '#a5b4fc'], ['#0d9488', '#2dd4bf'], ['#b45309', '#fcd34d'], ['#be123c', '#fda4af'],
- ['#0369a1', '#7dd3fc'], ['#4d7c0f', '#bef264'], ['#86198f', '#e879f9'], ['#475569', '#cbd5e1'], ['#047857', '#6ee7b7'],
- ['#6d28d9', '#ddd6fe'], ['#1e40af', '#93c5fd'], ['#ea580c', '#fed7aa'], ['#0e7490', '#99f6e4'], ['#881337', '#fbcfe8'],
- ]
- /**
- * 根据 id 或 name 哈希得到固定索引 0~19,同一 id/name 对应同一种渐变
- * id 为空时用 name,确保无 id 时也有稳定渐变
- */
- function getGradientIndex(id, name) {
- const s = String(id || '').trim() || String(name || '').trim()
- let hash = 0
- for (let i = 0; i < s.length; i++) {
- hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0
- }
- return Math.abs(hash) % AVATAR_GRADIENT_PAIRS.length
- }
- const props = defineProps({
- /** 显示名称,用于占位文字 */
- name: { type: String, default: '' },
- /** 用户唯一标识,用于占位背景色 */
- id: { type: String, default: '' },
- /** 头像图片地址,有则显示图片,无则显示占位 */
- src: { type: String, default: '' },
- /** 尺寸数值,默认 48 */
- size: { type: Number, default: 48 },
- /** 尺寸单位:'px' | 'rpx',默认 px;rpx 便于在 uni-app 中与页面 rpx 一致 */
- unit: { type: String, default: 'px' },
- })
- defineEmits(['click'])
- const displayText = computed(() => getAvatarText(props.name))
- const gradientIndex = computed(() => getGradientIndex(props.id, props.name))
- const su = computed(() => props.unit || 'px')
- const wrapStyle = computed(() => {
- const u = su.value
- const w = props.size + u
- return {
- width: w,
- height: w,
- borderRadius: '50%',
- }
- })
- const imgStyle = computed(() => {
- const u = su.value
- const w = props.size + u
- return {
- width: w,
- height: w,
- borderRadius: '50%',
- }
- })
- const placeholderStyle = computed(() => {
- const u = su.value
- const w = props.size + u
- return {
- width: w,
- height: w,
- borderRadius: '50%',
- }
- })
- const textStyle = computed(() => ({
- fontSize: (props.size * 0.4) + su.value,
- }))
- </script>
- <style scoped>
- .user-avatar {
- overflow: hidden;
- flex-shrink: 0;
- }
- .user-avatar-img {
- display: block;
- }
- .user-avatar-placeholder {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .user-avatar-text {
- color: #ffffff;
- font-weight: 500;
- font-family: 'PingFang SC', 'Roboto', -apple-system, sans-serif;
- }
- </style>
- <style>
- /* 无 scoped,避免编译后动态 class 与 data-v 不匹配导致渐变不生效;用 .user-avatar 限定避免污染;135deg 左上深→右下浅 */
- .user-avatar .user-avatar-placeholder.gradient-0 { background-color: #1e3a8a; background-image: linear-gradient(135deg, #1e3a8a, #93c5fd); }
- .user-avatar .user-avatar-placeholder.gradient-1 { background-color: #166534; background-image: linear-gradient(135deg, #166534, #86efac); }
- .user-avatar .user-avatar-placeholder.gradient-2 { background-color: #c2410c; background-image: linear-gradient(135deg, #c2410c, #fdba74); }
- .user-avatar .user-avatar-placeholder.gradient-3 { background-color: #b91c1c; background-image: linear-gradient(135deg, #b91c1c, #fca5a5); }
- .user-avatar .user-avatar-placeholder.gradient-4 { background-color: #5b21b6; background-image: linear-gradient(135deg, #5b21b6, #c4b5fd); }
- .user-avatar .user-avatar-placeholder.gradient-5 { background-color: #0f766e; background-image: linear-gradient(135deg, #0f766e, #5eead4); }
- .user-avatar .user-avatar-placeholder.gradient-6 { background-color: #3730a3; background-image: linear-gradient(135deg, #3730a3, #a5b4fc); }
- .user-avatar .user-avatar-placeholder.gradient-7 { background-color: #0d9488; background-image: linear-gradient(135deg, #0d9488, #2dd4bf); }
- .user-avatar .user-avatar-placeholder.gradient-8 { background-color: #b45309; background-image: linear-gradient(135deg, #b45309, #fcd34d); }
- .user-avatar .user-avatar-placeholder.gradient-9 { background-color: #be123c; background-image: linear-gradient(135deg, #be123c, #fda4af); }
- .user-avatar .user-avatar-placeholder.gradient-10 { background-color: #0369a1; background-image: linear-gradient(135deg, #0369a1, #7dd3fc); }
- .user-avatar .user-avatar-placeholder.gradient-11 { background-color: #4d7c0f; background-image: linear-gradient(135deg, #4d7c0f, #bef264); }
- .user-avatar .user-avatar-placeholder.gradient-12 { background-color: #86198f; background-image: linear-gradient(135deg, #86198f, #e879f9); }
- .user-avatar .user-avatar-placeholder.gradient-13 { background-color: #475569; background-image: linear-gradient(135deg, #475569, #cbd5e1); }
- .user-avatar .user-avatar-placeholder.gradient-14 { background-color: #047857; background-image: linear-gradient(135deg, #047857, #6ee7b7); }
- .user-avatar .user-avatar-placeholder.gradient-15 { background-color: #6d28d9; background-image: linear-gradient(135deg, #6d28d9, #ddd6fe); }
- .user-avatar .user-avatar-placeholder.gradient-16 { background-color: #1e40af; background-image: linear-gradient(135deg, #1e40af, #93c5fd); }
- .user-avatar .user-avatar-placeholder.gradient-17 { background-color: #ea580c; background-image: linear-gradient(135deg, #ea580c, #fed7aa); }
- .user-avatar .user-avatar-placeholder.gradient-18 { background-color: #0e7490; background-image: linear-gradient(135deg, #0e7490, #99f6e4); }
- .user-avatar .user-avatar-placeholder.gradient-19 { background-color: #881337; background-image: linear-gradient(135deg, #881337, #fbcfe8); }
- </style>
|