mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-13 14:57:14 +08:00
refactor: 调整默认头像规则,由基于性别的固定头像调整为基于昵称展示(背景颜色基于昵称计算随机)
This commit is contained in:
82
src/components/Avatar/index.vue
Normal file
82
src/components/Avatar/index.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<a-avatar v-if="src" :size="size">
|
||||||
|
<img :src="src" :alt="alt" />
|
||||||
|
<template v-if="trigger" #trigger-icon><slot name="trigger-icon"></slot></template>
|
||||||
|
</a-avatar>
|
||||||
|
<a-avatar
|
||||||
|
v-else-if="name || text"
|
||||||
|
:size="size"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: avatarColor,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span v-if="name">{{ avatarName }}</span>
|
||||||
|
<span v-else>{{ text }}</span>
|
||||||
|
<template v-if="trigger" #trigger-icon><slot name="trigger-icon"></slot></template>
|
||||||
|
</a-avatar>
|
||||||
|
<a-avatar v-else :size="size">
|
||||||
|
<img :src="Unknown" :alt="alt" />
|
||||||
|
<template v-if="trigger" #trigger-icon><slot name="trigger-icon"></slot></template>
|
||||||
|
</a-avatar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Unknown from '@/assets/images/avatar/unknown.png'
|
||||||
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
|
defineOptions({ name: 'Avatar' })
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
color: '#168CFF',
|
||||||
|
size: 20,
|
||||||
|
alt: 'avatar',
|
||||||
|
trigger: false
|
||||||
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
src?: string
|
||||||
|
name?: string
|
||||||
|
text?: string
|
||||||
|
color?: string
|
||||||
|
size?: string | number
|
||||||
|
alt?: string
|
||||||
|
trigger?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 英文开头:取传入字符串的前面两个字符,例如:Charles => Ch
|
||||||
|
* 超过 4 位:取字符串第一个字符,例如:系统管理员 => 系
|
||||||
|
* 其他:取传入字符串的最后两个字符,例如:张三 => 张三;王多鱼:多鱼
|
||||||
|
*/
|
||||||
|
const avatarName = computed(() => {
|
||||||
|
const name = props.name
|
||||||
|
if (!name) return ''
|
||||||
|
if (name[0].match(Regexp.OnlyEn)) {
|
||||||
|
const nameArr = name.split(' ')
|
||||||
|
if (nameArr.length > 1 && nameArr[1][0].match(Regexp.OnlyEn)) return `${nameArr[0][0]}${nameArr[1][0]}`
|
||||||
|
return name.substring(0, 2)
|
||||||
|
}
|
||||||
|
if (name.length > 4) return name[0]
|
||||||
|
return name.substring(name.length - 2, name.length)
|
||||||
|
})
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'#168CFF',
|
||||||
|
'#7BC616',
|
||||||
|
'#14C9C9',
|
||||||
|
'#FF7D00',
|
||||||
|
'#FFC72E'
|
||||||
|
]
|
||||||
|
const avatarColor = computed(() => {
|
||||||
|
const hash = (s) => {
|
||||||
|
let hash = 0
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
hash = (hash << 5) - hash + s.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return Math.abs(hash)
|
||||||
|
}
|
||||||
|
return colors[hash(props.name || props.text) % (colors.length)]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-space fill>
|
<a-space fill>
|
||||||
<a-avatar :size="24" shape="circle">
|
<Avatar :src="props.avatar" :name="props.name" :size="24" />
|
||||||
<img :src="props.avatar" alt="avatar" />
|
|
||||||
</a-avatar>
|
|
||||||
<a-link v-if="props.isLink" @click="emit('click')">
|
<a-link v-if="props.isLink" @click="emit('click')">
|
||||||
<a-typography-paragraph
|
<a-typography-paragraph
|
||||||
class="link-text"
|
class="link-text"
|
||||||
@@ -15,7 +13,17 @@
|
|||||||
{{ props.name }}
|
{{ props.name }}
|
||||||
</a-typography-paragraph>
|
</a-typography-paragraph>
|
||||||
</a-link>
|
</a-link>
|
||||||
<span v-else>{{ props.name }}</span>
|
<span v-else>
|
||||||
|
<a-typography-paragraph
|
||||||
|
:ellipsis="{
|
||||||
|
rows: 1,
|
||||||
|
showTooltip: true,
|
||||||
|
css: true,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ props.name }}
|
||||||
|
</a-typography-paragraph>
|
||||||
|
</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@@ -48,10 +48,8 @@
|
|||||||
<a-dropdown trigger="hover">
|
<a-dropdown trigger="hover">
|
||||||
<a-row align="center" :wrap="false" class="user">
|
<a-row align="center" :wrap="false" class="user">
|
||||||
<!-- 管理员头像 -->
|
<!-- 管理员头像 -->
|
||||||
<a-avatar :size="32">
|
<Avatar :src="userStore.avatar" :name="userStore.nickname" :size="32" />
|
||||||
<img :src="userStore.avatar" alt="avatar" />
|
<span class="username">{{ userStore.nickname }}</span>
|
||||||
</a-avatar>
|
|
||||||
<span class="username">{{ userStore.name }}</span>
|
|
||||||
<icon-down />
|
<icon-down />
|
||||||
</a-row>
|
</a-row>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
@@ -15,7 +15,6 @@ import {
|
|||||||
} from '@/apis'
|
} from '@/apis'
|
||||||
import { clearToken, getToken, setToken } from '@/utils/auth'
|
import { clearToken, getToken, setToken } from '@/utils/auth'
|
||||||
import { resetHasRouteFlag } from '@/router/permission'
|
import { resetHasRouteFlag } from '@/router/permission'
|
||||||
import getAvatar from '@/utils/avatar'
|
|
||||||
|
|
||||||
const storeSetup = () => {
|
const storeSetup = () => {
|
||||||
const userInfo = reactive<UserInfo>({
|
const userInfo = reactive<UserInfo>({
|
||||||
@@ -33,7 +32,8 @@ const storeSetup = () => {
|
|||||||
roles: [],
|
roles: [],
|
||||||
permissions: []
|
permissions: []
|
||||||
})
|
})
|
||||||
const name = computed(() => userInfo.nickname)
|
const nickname = computed(() => userInfo.nickname)
|
||||||
|
const username = computed(() => userInfo.username)
|
||||||
const avatar = computed(() => userInfo.avatar)
|
const avatar = computed(() => userInfo.avatar)
|
||||||
|
|
||||||
const token = ref(getToken() || '')
|
const token = ref(getToken() || '')
|
||||||
@@ -100,7 +100,7 @@ const storeSetup = () => {
|
|||||||
const getInfo = async () => {
|
const getInfo = async () => {
|
||||||
const res = await getUserInfoApi()
|
const res = await getUserInfoApi()
|
||||||
Object.assign(userInfo, res.data)
|
Object.assign(userInfo, res.data)
|
||||||
userInfo.avatar = getAvatar(res.data.avatar, res.data.gender)
|
userInfo.avatar = res.data.avatar
|
||||||
if (res.data.roles && res.data.roles.length) {
|
if (res.data.roles && res.data.roles.length) {
|
||||||
roles.value = res.data.roles
|
roles.value = res.data.roles
|
||||||
permissions.value = res.data.permissions
|
permissions.value = res.data.permissions
|
||||||
@@ -109,7 +109,8 @@ const storeSetup = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
userInfo,
|
userInfo,
|
||||||
name,
|
nickname,
|
||||||
|
username,
|
||||||
avatar,
|
avatar,
|
||||||
token,
|
token,
|
||||||
roles,
|
roles,
|
||||||
|
2
src/types/auto-imports.d.ts
vendored
2
src/types/auto-imports.d.ts
vendored
@@ -82,6 +82,6 @@ declare global {
|
|||||||
// for type re-export
|
// for type re-export
|
||||||
declare global {
|
declare global {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||||
import('vue')
|
import('vue')
|
||||||
}
|
}
|
||||||
|
1
src/types/components.d.ts
vendored
1
src/types/components.d.ts
vendored
@@ -7,6 +7,7 @@ export {}
|
|||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
||||||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
||||||
Chart: typeof import('./../components/Chart/index.vue')['default']
|
Chart: typeof import('./../components/Chart/index.vue')['default']
|
||||||
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
|
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
|
||||||
|
@@ -2,11 +2,9 @@
|
|||||||
<a-card class="card" :bordered="false">
|
<a-card class="card" :bordered="false">
|
||||||
<a-row align="center" wrap :gutter="[{ xs: 0, sm: 14, md: 14, lg: 14, xl: 14, xxl: 14 }, 16]" class="content">
|
<a-row align="center" wrap :gutter="[{ xs: 0, sm: 14, md: 14, lg: 14, xl: 14, xxl: 14 }, 16]" class="content">
|
||||||
<a-space size="medium">
|
<a-space size="medium">
|
||||||
<a-avatar :size="68">
|
<Avatar :src="userStore.avatar" :name="userStore.nickname" :size="68" />
|
||||||
<img :src="userStore.avatar" alt="avatar" />
|
|
||||||
</a-avatar>
|
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<p class="hello">{{ goodTimeText() }}!{{ userStore.name }}</p>
|
<p class="hello">{{ goodTimeText() }}!{{ userStore.nickname }}</p>
|
||||||
<p>北海虽赊,扶摇可接;东隅已逝,桑榆非晚。</p>
|
<p>北海虽赊,扶摇可接;东隅已逝,桑榆非晚。</p>
|
||||||
</div>
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
@@ -11,10 +11,9 @@
|
|||||||
:on-before-upload="onBeforeUpload"
|
:on-before-upload="onBeforeUpload"
|
||||||
>
|
>
|
||||||
<template #upload-button>
|
<template #upload-button>
|
||||||
<a-avatar :size="100">
|
<Avatar :src="avatarList[0].url" :name="userStore.nickname" :size="100" trigger>
|
||||||
<template #trigger-icon><icon-camera /></template>
|
<template #trigger-icon><icon-camera /></template>
|
||||||
<img v-if="avatarList.length" :src="avatarList[0].url" alt="avatar" />
|
</Avatar>
|
||||||
</a-avatar>
|
|
||||||
</template>
|
</template>
|
||||||
</a-upload>
|
</a-upload>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabled-tools="['size']"
|
:disabled-tools="['size']"
|
||||||
:disabled-column-keys="['username']"
|
:disabled-column-keys="['nickname']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
<template #top>
|
<template #top>
|
||||||
@@ -40,9 +40,8 @@
|
|||||||
<template #default>导出</template>
|
<template #default>导出</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #username="{ record }">
|
<template #nickname="{ record }">
|
||||||
<GiCellAvatar :avatar="getAvatar(record.avatar, record.gender)" :name="record.username" is-link
|
<GiCellAvatar :avatar="record.avatar" :name="record.nickname" />
|
||||||
@click="onDetail(record)" />
|
|
||||||
</template>
|
</template>
|
||||||
<template #gender="{ record }">
|
<template #gender="{ record }">
|
||||||
<GiCellGender :gender="record.gender" />
|
<GiCellGender :gender="record.gender" />
|
||||||
@@ -59,6 +58,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
|
<a-link v-permission="['system:user:list']" @click="onDetail(record)">详情</a-link>
|
||||||
<a-link v-permission="['system:user:update']" @click="onUpdate(record)">修改</a-link>
|
<a-link v-permission="['system:user:update']" @click="onUpdate(record)">修改</a-link>
|
||||||
<a-link
|
<a-link
|
||||||
v-permission="['system:user:delete']"
|
v-permission="['system:user:delete']"
|
||||||
@@ -103,7 +103,6 @@ import type { Columns, Options } from '@/components/GiForm'
|
|||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import { useDownload, useTable } from '@/hooks'
|
import { useDownload, useTable } from '@/hooks'
|
||||||
import { isMobile } from '@/utils'
|
import { isMobile } from '@/utils'
|
||||||
import getAvatar from '@/utils/avatar'
|
|
||||||
import has from '@/utils/has'
|
import has from '@/utils/has'
|
||||||
import { DisEnableStatusList } from '@/constant/common'
|
import { DisEnableStatusList } from '@/constant/common'
|
||||||
|
|
||||||
@@ -170,15 +169,15 @@ const columns: TableInstanceColumns[] = [
|
|||||||
fixed: !isMobile() ? 'left' : undefined
|
fixed: !isMobile() ? 'left' : undefined
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '用户名',
|
title: '昵称',
|
||||||
dataIndex: 'username',
|
dataIndex: 'nickname',
|
||||||
slotName: 'username',
|
slotName: 'nickname',
|
||||||
width: 140,
|
minWidth: 140,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
fixed: !isMobile() ? 'left' : undefined
|
fixed: !isMobile() ? 'left' : undefined
|
||||||
},
|
},
|
||||||
{ title: '昵称', dataIndex: 'nickname', width: 120, ellipsis: true, tooltip: true },
|
{ title: '用户名', dataIndex: 'username', slotName: 'username', minWidth: 140, ellipsis: true, tooltip: true },
|
||||||
{ title: '状态', slotName: 'status', align: 'center', width: 80 },
|
{ title: '状态', slotName: 'status', align: 'center', width: 80 },
|
||||||
{ title: '性别', slotName: 'gender', align: 'center', width: 100 },
|
{ title: '性别', slotName: 'gender', align: 'center', width: 100 },
|
||||||
{ title: '所属部门', dataIndex: 'deptName', ellipsis: true, tooltip: true, width: 180 },
|
{ title: '所属部门', dataIndex: 'deptName', ellipsis: true, tooltip: true, width: 180 },
|
||||||
@@ -194,7 +193,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
slotName: 'action',
|
slotName: 'action',
|
||||||
width: 150,
|
width: 190,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: !isMobile() ? 'right' : undefined,
|
fixed: !isMobile() ? 'right' : undefined,
|
||||||
show: has.hasPermOr(['system:user:update', 'system:user:delete', 'system:user:resetPwd'])
|
show: has.hasPermOr(['system:user:update', 'system:user:delete', 'system:user:resetPwd'])
|
||||||
|
Reference in New Issue
Block a user