mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-11-03 22:57:09 +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