156 lines
3.9 KiB
Vue
156 lines
3.9 KiB
Vue
<template>
|
|
<div class="avatar-display" :class="{ 'avatar-display-completed': completed }">
|
|
<template v-if="names.length === 0">
|
|
<span class="avatar-empty">-</span>
|
|
</template>
|
|
<template v-else-if="names.length === 1">
|
|
<div class="avatar-single-wrap">
|
|
<div class="avatar-circle" :style="{ background: getColor(names[0]), width: size + 'px', height: size + 'px' }">
|
|
<span class="avatar-text" :style="{ fontSize: fontSize + 'px' }">{{ names[0].charAt(0) }}</span>
|
|
</div>
|
|
<span v-if="showName" class="avatar-name">{{ names[0] }}</span>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="names.length <= maxShow">
|
|
<div
|
|
v-for="(name, idx) in names"
|
|
:key="idx"
|
|
class="avatar-circle"
|
|
:style="{ background: getColor(name), width: size + 'px', height: size + 'px', marginLeft: idx > 0 ? overlap + 'px' : '0', zIndex: idx + 1 }"
|
|
>
|
|
<span class="avatar-text" :style="{ fontSize: fontSize + 'px' }">{{ name.charAt(0) }}</span>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<div
|
|
v-for="(name, idx) in names.slice(0, maxShow - 1)"
|
|
:key="idx"
|
|
class="avatar-circle"
|
|
:style="{ background: getColor(name), width: size + 'px', height: size + 'px', marginLeft: idx > 0 ? overlap + 'px' : '0', zIndex: idx + 1 }"
|
|
>
|
|
<span class="avatar-text" :style="{ fontSize: fontSize + 'px' }">{{ name.charAt(0) }}</span>
|
|
</div>
|
|
<div
|
|
class="avatar-overflow"
|
|
:style="{ width: size + 'px', height: size + 'px', marginLeft: overlap + 'px', fontSize: overflowFontSize + 'px', zIndex: names.length }"
|
|
>
|
|
+{{ names.length - (maxShow - 1) }}
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed } from 'vue';
|
|
import { getAvatarColor } from '../types';
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
names: string;
|
|
size?: number;
|
|
maxShow?: number;
|
|
overlap?: number;
|
|
showName?: boolean;
|
|
completed?: boolean;
|
|
}>(),
|
|
{
|
|
size: 24,
|
|
maxShow: 5,
|
|
overlap: -8,
|
|
showName: true,
|
|
completed: false,
|
|
},
|
|
);
|
|
|
|
const fontSize = computed(() => Math.max(10, Math.round(props.size * 0.54)));
|
|
const overflowFontSize = computed(() => Math.max(8, Math.round(props.size * 0.42)));
|
|
|
|
const parsedNames = computed(() => {
|
|
if (!props.names) return [];
|
|
return props.names
|
|
.split(/[,,、]/)
|
|
.map((s) => s.trim())
|
|
.filter(Boolean);
|
|
});
|
|
|
|
const names = computed(() => parsedNames.value);
|
|
|
|
function getColor(name: string): string {
|
|
return getAvatarColor(name);
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.avatar-display {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.avatar-display-completed {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.avatar-single-wrap {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 2px 8px 2px 2px;
|
|
background: #f5f6f7;
|
|
border-radius: 14px;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.avatar-circle {
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
position: relative;
|
|
box-shadow: 0 0 0 2px #fff;
|
|
}
|
|
|
|
.avatar-text {
|
|
color: #fff;
|
|
font-weight: 500;
|
|
line-height: 1;
|
|
font-size: 14px !important;
|
|
}
|
|
|
|
.avatar-name {
|
|
color: #646a73;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.avatar-count {
|
|
font-size: 14px;
|
|
color: #646a73;
|
|
margin-left: 4px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.avatar-overflow {
|
|
border-radius: 50%;
|
|
background: #f0f0f0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #8c8c8c;
|
|
font-weight: 500;
|
|
position: relative;
|
|
box-shadow: 0 0 0 2px #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.avatar-empty {
|
|
color: #c0c4cc;
|
|
font-size: 14px;
|
|
}
|
|
</style>
|