fix: 同步顶部账号头像展示
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-30 17:23:22 +08:00
parent b5f02fff61
commit 89e7bdbed6
3 changed files with 26 additions and 2 deletions

View File

@@ -87,3 +87,4 @@ SpacetimeDB 正式表 `user_account` 需要增加 `avatar_url: Option<String>`
4. 昵称编辑成功后,资料卡与顶部账号入口同步新昵称。 4. 昵称编辑成功后,资料卡与顶部账号入口同步新昵称。
5. 非法头像文件不会进入裁剪流程。 5. 非法头像文件不会进入裁剪流程。
6. 裁剪保存成功后,资料卡头像展示裁剪后的图片。 6. 裁剪保存成功后,资料卡头像展示裁剪后的图片。
7. 桌面右上角账号入口与“我的”资料卡共用 `avatarUrl`,有已保存头像时展示头像图片,缺失时才回退到首字头像。

View File

@@ -5,6 +5,7 @@ import userEvent from '@testing-library/user-event';
import { useState } from 'react'; import { useState } from 'react';
import { afterEach, expect, test, vi } from 'vitest'; import { afterEach, expect, test, vi } from 'vitest';
import type { AuthUser } from '../../services/authService';
import { AuthUiContext } from '../auth/AuthUiContext'; import { AuthUiContext } from '../auth/AuthUiContext';
import { import {
RpgEntryHomeView, RpgEntryHomeView,
@@ -219,6 +220,7 @@ function renderProfileView(
profileDashboardOverrides: Partial< profileDashboardOverrides: Partial<
NonNullable<RpgEntryHomeViewProps['profileDashboard']> NonNullable<RpgEntryHomeViewProps['profileDashboard']>
> = {}, > = {},
userOverrides: Partial<AuthUser> = {},
) { ) {
return render( return render(
<AuthUiContext.Provider <AuthUiContext.Provider
@@ -233,6 +235,7 @@ function renderProfileView(
loginMethod: 'password', loginMethod: 'password',
bindingStatus: 'active', bindingStatus: 'active',
wechatBound: false, wechatBound: false,
...userOverrides,
}, },
canAccessProtectedData: true, canAccessProtectedData: true,
openLoginModal: vi.fn(), openLoginModal: vi.fn(),
@@ -448,6 +451,18 @@ test('profile total play time card always uses hours', () => {
expect(within(playTimeCard).queryByText('90分')).toBeNull(); expect(within(playTimeCard).queryByText('90分')).toBeNull();
}); });
test('desktop account entry uses saved avatar image when available', () => {
mockDesktopLayout();
const avatarUrl = 'data:image/png;base64,AAAA';
renderProfileView(vi.fn(), {}, { avatarUrl });
const accountEntry = screen.getByRole('button', { name: //u });
const avatarImage = accountEntry.querySelector('img');
expect(avatarImage?.getAttribute('src')).toBe(avatarUrl);
expect(within(accountEntry).queryByText('测')).toBeNull();
});
test('wallet ledger modal shows empty and error states', async () => { test('wallet ledger modal shows empty and error states', async () => {
const user = userEvent.setup(); const user = userEvent.setup();
mockGetRpgProfileWalletLedger.mockResolvedValueOnce({ entries: [] }); mockGetRpgProfileWalletLedger.mockResolvedValueOnce({ entries: [] });

View File

@@ -3575,13 +3575,21 @@ export function RpgEntryHomeView({
className="platform-desktop-search flex items-center gap-3 px-3 py-2.5 text-left" className="platform-desktop-search flex items-center gap-3 px-3 py-2.5 text-left"
> >
<span <span
className="flex h-11 w-11 items-center justify-center rounded-full text-base font-black text-white" className="flex h-11 w-11 items-center justify-center overflow-hidden rounded-full text-base font-black text-white"
style={{ style={{
background: 'var(--platform-profile-avatar-fill)', background: 'var(--platform-profile-avatar-fill)',
boxShadow: 'var(--platform-profile-avatar-shadow)', boxShadow: 'var(--platform-profile-avatar-shadow)',
}} }}
> >
{avatarLabel} {avatarUrl ? (
<img
src={avatarUrl}
alt=""
className="h-full w-full object-cover"
/>
) : (
avatarLabel
)}
</span> </span>
<span className="min-w-0"> <span className="min-w-0">
<span className="block truncate text-sm font-semibold text-[var(--platform-text-strong)]"> <span className="block truncate text-sm font-semibold text-[var(--platform-text-strong)]">