修复首页账户帮助菜单交互

统一我的和帮助菜单为点击触发

菜单项接入占位通知弹窗并提升弹层层级

调整泥点与升级入口为分段胶囊样式

补充首页菜单交互边界测试
This commit is contained in:
AIGameCreator App
2026-07-08 20:15:07 +08:00
parent d8dc11592c
commit 06e79ab2bc
3 changed files with 158 additions and 68 deletions
+96 -38
View File
@@ -16,7 +16,6 @@ import {
BookOpen,
CircleHelp,
Contact,
CreditCard,
FileText,
FolderKanban,
Gamepad2,
@@ -29,8 +28,8 @@ import {
Sparkles,
Upload,
User,
Wallet,
X,
Zap,
} from 'lucide-react';
import type {
@@ -2168,6 +2167,11 @@ export function WorkspaceLauncher({
const [walletLedgerStatus, setWalletLedgerStatus] =
useState('尚未读取明细');
const [accountMenuOpen, setAccountMenuOpen] = useState(false);
const [helpMenuOpen, setHelpMenuOpen] = useState(false);
const [launcherNotice, setLauncherNotice] = useState<{
title: string;
message: string;
} | null>(null);
const [showcaseResources, setShowcaseResources] = useState<
EditorShowcaseResource[]
>([]);
@@ -2702,6 +2706,15 @@ export function WorkspaceLauncher({
}
}
function showLauncherNotice(title: string) {
setAccountMenuOpen(false);
setHelpMenuOpen(false);
setLauncherNotice({
title,
message: `${title}正在接入中,当前版本会先保留入口。`,
});
}
function handleRecentWorkspaceRefresh() {
if (recentWorkspaces.length === 0 || recentWorkspaceRefreshing) {
return;
@@ -2985,7 +2998,12 @@ export function WorkspaceLauncher({
<button
type="button"
className="launcher-config-button"
onClick={() => setAccountMenuOpen((open) => !open)}
onClick={() => {
setHelpMenuOpen(false);
setWalletPanelOpen(false);
setAccountMenuOpen((open) => !open);
}}
aria-expanded={accountMenuOpen}
aria-label="我的"
title="我的"
>
@@ -3000,21 +3018,21 @@ export function WorkspaceLauncher({
<button
type="button"
role="menuitem"
onClick={() => setWalletPanelOpen(true)}
onClick={() => showLauncherNotice('会员与升级')}
>
</button>
<button
type="button"
role="menuitem"
onClick={() => setLauncherView('guide')}
onClick={() => showLauncherNotice('使用指南')}
>
使
</button>
<button
type="button"
role="menuitem"
onClick={() => setLauncherView('contact')}
onClick={() => showLauncherNotice('反馈联系')}
>
</button>
@@ -3031,39 +3049,47 @@ export function WorkspaceLauncher({
aria-label="帮助"
title="帮助"
className={
['guide', 'contact', 'news'].includes(launcherView)
helpMenuOpen || ['guide', 'contact', 'news'].includes(launcherView)
? 'launcher-nav-active'
: ''
}
aria-expanded={helpMenuOpen}
onClick={() => {
setAccountMenuOpen(false);
setWalletPanelOpen(false);
setHelpMenuOpen((open) => !open);
}}
>
<CircleHelp size={17} aria-hidden="true" />
</button>
<div className="launcher-help-menu" role="menu">
<button
type="button"
role="menuitem"
onClick={() => setLauncherView('guide')}
>
<BookOpen size={14} aria-hidden="true" />
使
</button>
<button
type="button"
role="menuitem"
onClick={() => setLauncherView('contact')}
>
<Contact size={14} aria-hidden="true" />
</button>
<button
type="button"
role="menuitem"
onClick={() => setLauncherView('news')}
>
<Bell size={14} aria-hidden="true" />
</button>
</div>
{helpMenuOpen ? (
<div className="launcher-help-menu" role="menu">
<button
type="button"
role="menuitem"
onClick={() => showLauncherNotice('使用指南')}
>
<BookOpen size={14} aria-hidden="true" />
使
</button>
<button
type="button"
role="menuitem"
onClick={() => showLauncherNotice('联系我们')}
>
<Contact size={14} aria-hidden="true" />
</button>
<button
type="button"
role="menuitem"
onClick={() => showLauncherNotice('最新动态')}
>
<Bell size={14} aria-hidden="true" />
</button>
</div>
) : null}
</div>
<button
type="button"
@@ -3101,9 +3127,13 @@ export function WorkspaceLauncher({
<button
type="button"
aria-label="泥点余额"
onClick={() => setWalletPanelOpen((open) => !open)}
onClick={() => {
setAccountMenuOpen(false);
setHelpMenuOpen(false);
setWalletPanelOpen((open) => !open);
}}
>
<Wallet size={14} aria-hidden="true" />
<Zap size={12} aria-hidden="true" />
{formatMudPoints(profileDashboard?.walletBalance)}
</button>
{walletPanelOpen ? (
@@ -3129,9 +3159,8 @@ export function WorkspaceLauncher({
</div>
) : null}
</div>
<button type="button" onClick={() => setWalletPanelOpen(true)}>
<CreditCard size={14} aria-hidden="true" />
<button type="button" onClick={() => showLauncherNotice('升级')}>
</button>
</div>
</div>
@@ -3631,6 +3660,35 @@ export function WorkspaceLauncher({
{runtimeConfigOpen ? (
<RuntimeConfigDialog onClose={() => setRuntimeConfigOpen(false)} />
) : null}
{launcherNotice ? (
<div
className="launcher-dialog-backdrop"
role="presentation"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
setLauncherNotice(null);
}
}}
>
<section
aria-labelledby="launcher-notice-title"
aria-modal="true"
className="launcher-dialog"
role="dialog"
onKeyDown={(event) =>
closeDialogOnEscape(event, () => setLauncherNotice(null))
}
>
<h2 id="launcher-notice-title">{launcherNotice.title}</h2>
<p>{launcherNotice.message}</p>
<div className="launcher-dialog-actions">
<button type="button" onClick={() => setLauncherNotice(null)}>
</button>
</div>
</section>
</div>
) : null}
{pendingNonEmptyProject ? (
<div
className="launcher-dialog-backdrop"
+33 -29
View File
@@ -150,6 +150,7 @@ textarea {
.launcher-sidebar {
position: sticky;
top: 0;
z-index: 40;
display: flex;
flex-direction: column;
align-items: center;
@@ -158,6 +159,7 @@ textarea {
padding: 16px 0 18px;
border-right: 1px solid #eceef2;
background: #fff;
overflow: visible;
}
.launcher-brand {
@@ -186,6 +188,7 @@ textarea {
.launcher-sidebar-bottom {
position: relative;
z-index: 60;
display: grid;
justify-items: center;
gap: 13px;
@@ -230,30 +233,18 @@ textarea {
bottom: -4px;
left: 36px;
display: grid;
min-width: 138px;
gap: 4px;
padding: 7px;
min-width: 150px;
gap: 5px;
padding: 8px;
border: 1px solid #e5e7eb;
border-radius: 10px;
border-radius: 8px;
background: #fff;
box-shadow: 0 14px 34px rgb(15 23 42 / 14%);
opacity: 0;
pointer-events: none;
transform: translateX(-4px);
transition:
opacity 0.16s ease,
transform 0.16s ease;
z-index: 5;
z-index: 90;
}
.launcher-help-nav:hover .launcher-help-menu,
.launcher-help-nav:focus-within .launcher-help-menu {
opacity: 1;
pointer-events: auto;
transform: translateX(0);
}
.launcher-sidebar .launcher-help-menu button {
.launcher-sidebar .launcher-help-menu button,
.launcher-sidebar .launcher-account-popover button {
display: flex;
align-items: center;
justify-content: flex-start;
@@ -269,7 +260,9 @@ textarea {
}
.launcher-sidebar .launcher-help-menu button:hover,
.launcher-sidebar .launcher-help-menu button:focus-visible {
.launcher-sidebar .launcher-help-menu button:focus-visible,
.launcher-sidebar .launcher-account-popover button:hover,
.launcher-sidebar .launcher-account-popover button:focus-visible {
background: #f3f4f6;
}
@@ -333,7 +326,7 @@ textarea {
right: 22px;
display: flex;
align-items: center;
gap: 7px;
gap: 0;
z-index: 2;
}
@@ -345,11 +338,11 @@ textarea {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
height: 24px;
padding: 0 8px;
border: 1px solid #e5e7eb;
border-radius: 7px;
gap: 4px;
height: 26px;
padding: 0 9px;
border: 0;
border-radius: 0;
background: #fff;
color: #1f2937;
font-size: 12px;
@@ -360,8 +353,9 @@ textarea {
align-items: center;
overflow: visible;
border: 1px solid #e5e7eb;
border-radius: 7px;
border-radius: 8px;
background: #fff;
box-shadow: 0 1px 2px rgb(15 23 42 / 5%);
}
.launcher-wallet-group > button,
@@ -370,8 +364,14 @@ textarea {
border-radius: 0;
}
.launcher-wallet-group .launcher-wallet-anchor > button {
border-radius: 8px 0 0 8px;
font-weight: 500;
}
.launcher-wallet-group > button {
border-left: 1px solid #e5e7eb;
border-radius: 0 8px 8px 0;
}
.launcher-wallet-anchor {
@@ -393,7 +393,7 @@ textarea {
background: #fff;
box-shadow: 0 16px 38px rgb(15 23 42 / 14%);
color: #111827;
z-index: 9;
z-index: 90;
}
.launcher-account-nav .launcher-account-popover {
@@ -401,6 +401,9 @@ textarea {
right: auto;
bottom: -4px;
left: 36px;
width: 218px;
gap: 6px;
padding: 10px;
}
.launcher-account-popover strong,
@@ -423,7 +426,7 @@ textarea {
height: 30px;
padding: 0 8px;
border: 0;
background: #f8fafc;
background: transparent;
color: #111827;
}
@@ -1483,6 +1486,7 @@ textarea {
.launcher-dialog-backdrop {
position: fixed;
inset: 0;
z-index: 200;
display: grid;
padding: 24px;
background: rgb(0 0 0 / 56%);
@@ -798,7 +798,7 @@ describe('AI 游戏创作 App 界面边界', () => {
).not.toBeNull();
expect(
within(screen.getByLabelText('账户资产')).getByRole('button', {
name: '充值',
name: '升级',
}),
).not.toBeNull();
expect(
@@ -831,6 +831,34 @@ describe('AI 游戏创作 App 界面边界', () => {
).toContain('/tmp/authorized-game');
});
it('opens account and help menus by click and routes placeholder items to one notice dialog', () => {
renderLauncherAt('/?launcher');
expect(
screen.queryByRole('menuitem', { name: '联系我们' }),
).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '帮助' }));
expect(
screen.getByRole('menuitem', { name: '联系我们' }),
).not.toBeNull();
fireEvent.click(screen.getByRole('menuitem', { name: '联系我们' }));
expect(screen.getByRole('dialog', { name: '联系我们' })).not.toBeNull();
expect(
screen.getByText('联系我们正在接入中,当前版本会先保留入口。'),
).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '知道了' }));
fireEvent.click(screen.getByRole('button', { name: '我的' }));
expect(
screen.getByRole('menuitem', { name: '会员与升级' }),
).not.toBeNull();
fireEvent.click(screen.getByRole('menuitem', { name: '会员与升级' }));
expect(screen.getByRole('dialog', { name: '会员与升级' })).not.toBeNull();
expect(
screen.getByText('会员与升级正在接入中,当前版本会先保留入口。'),
).not.toBeNull();
});
it('opens the developer agent chat entry and persists selected agent messages', async () => {
const persistedMessages: Array<{
role: 'user' | 'assistant' | 'tool';