feat: 用户信息修改
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
# 已切换为真实后端接口
|
# 后端服务地址(开发环境)
|
||||||
VITE_USE_MOCK=false
|
# 头像等静态资源通过此地址访问,生产环境留空即使用相对路径
|
||||||
|
VITE_BACKEND_URL=http://localhost:8080
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ import {
|
|||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { useAccountStore } from '@/stores'
|
import { useAccountStore } from '@/stores'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { getAvatarUrl } from '@/utils'
|
||||||
import AvatarCropper from './AvatarCropper.vue'
|
import AvatarCropper from './AvatarCropper.vue'
|
||||||
|
|
||||||
/** 编辑资料表单数据 */
|
/** 编辑资料表单数据 */
|
||||||
@@ -306,11 +307,11 @@ const hasAvatar = computed(() => {
|
|||||||
return !!(av && av.length > 0)
|
return !!(av && av.length > 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 头像显示源:裁剪预览 > 移除状态 > store 数据 */
|
/** 头像显示源:裁剪预览 > 移除状态 > store 数据(已拼接后端域名) */
|
||||||
const avatarDisplaySrc = computed(() => {
|
const avatarDisplaySrc = computed(() => {
|
||||||
if (avatarRemoved.value) return ''
|
if (avatarRemoved.value) return ''
|
||||||
if (croppedPreviewUrl.value) return croppedPreviewUrl.value
|
if (croppedPreviewUrl.value) return croppedPreviewUrl.value
|
||||||
return accountInfo.value?.avatar || ''
|
return getAvatarUrl(accountInfo.value?.avatar)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 根据生日计算年龄(周岁) */
|
/** 根据生日计算年龄(周岁) */
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
>
|
>
|
||||||
<div class="table-card__chair-seat">
|
<div class="table-card__chair-seat">
|
||||||
<img
|
<img
|
||||||
:src="room.players[idx].avatar"
|
:src="getAvatarUrl(room.players[idx].avatar)"
|
||||||
:alt="room.players[idx].nickname"
|
:alt="room.players[idx].nickname"
|
||||||
class="table-card__chair-avatar"
|
class="table-card__chair-avatar"
|
||||||
/>
|
/>
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { RoomItem, RoomPlayer, RoomStatus } from '@/api/modules/game'
|
import type { RoomItem, RoomPlayer, RoomStatus } from '@/api/modules/game'
|
||||||
|
import { getAvatarUrl } from '@/utils'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
room: RoomItem
|
room: RoomItem
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ import { ElMessage } from 'element-plus'
|
|||||||
import { Setting, EditPen, ArrowRight, Check, DocumentCopy, SwitchButton } from '@element-plus/icons-vue'
|
import { Setting, EditPen, ArrowRight, Check, DocumentCopy, SwitchButton } from '@element-plus/icons-vue'
|
||||||
import { useAccountStore } from '@/stores'
|
import { useAccountStore } from '@/stores'
|
||||||
import { UserStatus, type UserStatusType } from '@/api/modules/user'
|
import { UserStatus, type UserStatusType } from '@/api/modules/user'
|
||||||
|
import { getAvatarUrl } from '@/utils'
|
||||||
import EditProfileDialog from './EditProfileDialog.vue'
|
import EditProfileDialog from './EditProfileDialog.vue'
|
||||||
|
|
||||||
/** 在线状态选项 */
|
/** 在线状态选项 */
|
||||||
@@ -170,7 +171,7 @@ const currentStatus = computed<StatusOption>(() => {
|
|||||||
return backendStatus ? (statusMap.get(backendStatus) ?? STATUS_OPTIONS[0]!) : STATUS_OPTIONS[0]!
|
return backendStatus ? (statusMap.get(backendStatus) ?? STATUS_OPTIONS[0]!) : STATUS_OPTIONS[0]!
|
||||||
})
|
})
|
||||||
|
|
||||||
const userAvatar = computed(() => accountInfo.value?.avatar || '')
|
const userAvatar = computed(() => getAvatarUrl(accountInfo.value?.avatar))
|
||||||
const displayName = computed(() => accountInfo.value?.nickName || '未登录')
|
const displayName = computed(() => accountInfo.value?.nickName || '未登录')
|
||||||
const signatureText = computed(() => accountInfo.value?.signature || '这个人很懒,什么都没写...')
|
const signatureText = computed(() => accountInfo.value?.signature || '这个人很懒,什么都没写...')
|
||||||
|
|
||||||
|
|||||||
33
src/utils/avatar.ts
Normal file
33
src/utils/avatar.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 头像 URL 工具
|
||||||
|
*
|
||||||
|
* 将后端返回的相对头像路径拼接为完整可访问的 URL。
|
||||||
|
* 开发环境使用 VITE_BACKEND_URL(如 http://localhost:8080),
|
||||||
|
* 生产环境使用相对路径(前后端同域)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 后端基础 URL(Vite 环境变量,开发时为 http://localhost:8080,生产为空) */
|
||||||
|
const BACKEND_BASE = import.meta.env.VITE_BACKEND_URL || ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的头像展示 URL
|
||||||
|
*
|
||||||
|
* - 空值/undefined 返回空字符串,前端显示默认头像
|
||||||
|
* - data URI(注册默认头像)直接返回
|
||||||
|
* - 已是完整 URL(http/https 开头)直接返回
|
||||||
|
* - 相对路径(如上传的自定义头像)拼接后端域名
|
||||||
|
*
|
||||||
|
* @param avatarPath - 后端返回的头像路径,如 "/uploads/avatars/xxx.jpg" 或 "data:image/svg+xml;base64,..."
|
||||||
|
* @returns 可直接用于 <img src> 的完整 URL
|
||||||
|
*/
|
||||||
|
export function getAvatarUrl(avatarPath: string | null | undefined): string {
|
||||||
|
if (!avatarPath) return ''
|
||||||
|
|
||||||
|
// data URI 或完整 URL,直接返回
|
||||||
|
if (avatarPath.startsWith('data:') || avatarPath.startsWith('http://') || avatarPath.startsWith('https://')) {
|
||||||
|
return avatarPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// 相对路径拼接后端域名
|
||||||
|
return BACKEND_BASE + avatarPath
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './eventBus.ts'
|
export * from './eventBus.ts'
|
||||||
export * from './useDebouncedRef.ts'
|
export * from './useDebouncedRef.ts'
|
||||||
|
export * from './avatar.ts'
|
||||||
|
|||||||
Reference in New Issue
Block a user