feat(mobile): 重构移动端首页与个人中心

This commit is contained in:
yml
2026-11-12 22:48:18 +08:00
parent a4441bba9a
commit adc5a905da
11 changed files with 1902 additions and 232 deletions
+65 -36
View File
@@ -1,8 +1,13 @@
import 'antd-mobile/es/global'
import { TabBar } from 'antd-mobile'
import { AppOutline, MessageOutline, UnorderedListOutline, UserOutline } from 'antd-mobile-icons'
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom'
import {
AddOutline,
AppOutline,
MessageOutline,
UnorderedListOutline,
UserOutline,
} from 'antd-mobile-icons'
import { Link, Outlet, useLocation } from 'react-router-dom'
import { BrandLogo } from '@/components/BrandLogo'
import { useChatUnreadCount } from '@/features/messages/api'
@@ -11,63 +16,87 @@ import { useSessionStore } from '@/stores/session'
const tabs = [
{ key: '/m', title: '首页', icon: <AppOutline /> },
{ key: '/m/orders', title: '订单', icon: <UnorderedListOutline /> },
{ key: '/m/seller/listings/new', title: '发布', icon: <AddOutline />, primary: true },
{ key: '/m/messages', title: '消息', icon: <MessageOutline /> },
{ key: '/m/profile', title: '我的', icon: <UserOutline /> },
]
function activeTab(pathname: string) {
if (pathname.startsWith('/m/orders') || pathname.startsWith('/m/chats')) return '/m/orders'
if (pathname.startsWith('/m/messages')) return '/m/messages'
if (pathname.startsWith('/m/orders') || pathname.startsWith('/m/crash/orders')) {
return '/m/orders'
}
if (pathname.startsWith('/m/messages') || pathname.startsWith('/m/chats')) {
return '/m/messages'
}
if (pathname.startsWith('/m/profile')) return '/m/profile'
if (pathname.startsWith('/m/seller/listings/new')) return '/m/seller/listings/new'
return '/m'
}
export function MobileLayout() {
const location = useLocation()
const navigate = useNavigate()
const user = useSessionStore(state => state.user)
const chatUnread = useChatUnreadCount(Boolean(user))
const unreadBadge = chatUnread.data && chatUnread.data > 0 ? chatUnread.data : undefined
const hideTabbar =
const unreadCount = chatUnread.data ?? 0
const active = activeTab(location.pathname)
const hideChrome =
/^\/m\/listings\/[^/]+$/.test(location.pathname) ||
/^\/m\/orders\/[^/]+$/.test(location.pathname) ||
/^\/m\/messages\/[^/]+$/.test(location.pathname)
/^\/m\/messages\/[^/]+$/.test(location.pathname) ||
/^\/m\/crash\/[^/]+$/.test(location.pathname) ||
location.pathname === '/m/login' ||
location.pathname === '/m/register' ||
location.pathname === '/m/add-home-guide'
return (
<div className={`mobile-shell${hideTabbar ? ' mobile-shell--no-tabbar' : ''}`}>
<header className="mobile-header">
<Link to="/m" className="mobile-header__brand" aria-label="解忧商行移动端首页">
<BrandLogo height={30} />
</Link>
<div className="mobile-header__right">
{user ? (
<Link to="/m/profile" className="mobile-header__user">
{user.nickname || '我的'}
<div className={`mobile-shell${hideChrome ? ' mobile-shell--detail' : ''}`}>
{hideChrome ? null : (
<header className="mobile-header">
<Link to="/m" className="mobile-header__brand" aria-label="解忧商行移动端首页">
<BrandLogo height={30} />
</Link>
<div className="mobile-header__right">
<Link to="/m/messages" className="mobile-header__icon" aria-label="消息中心">
<MessageOutline />
{unreadCount > 0 ? (
<em className="mobile-header__badge">{unreadCount > 99 ? '99+' : unreadCount}</em>
) : null}
</Link>
) : (
<Link to="/m/login" className="mobile-header__login">
登录
<Link to={user ? '/m/profile' : '/m/login'} className="mobile-header__user">
{user ? user.nickname || '我的' : '登录'}
</Link>
)}
</div>
</header>
</div>
</header>
)}
<main className="mobile-content">
<Outlet />
</main>
<div className="mobile-tabbar" hidden={hideTabbar}>
<TabBar activeKey={activeTab(location.pathname)} onChange={key => navigate(key)} safeArea>
{tabs.map(tab => (
<TabBar.Item
key={tab.key}
icon={tab.icon}
title={tab.title}
badge={tab.key === '/m/messages' ? unreadBadge : undefined}
/>
))}
</TabBar>
</div>
{hideChrome ? null : (
<nav className="mobile-tabbar" aria-label="移动端主导航">
{tabs.map(tab => {
const selected = active === tab.key
return (
<Link
key={tab.key}
to={tab.key}
className={`mobile-tab${selected ? ' is-active' : ''}${
tab.primary ? ' is-primary' : ''
}`}
>
<span className="mobile-tab__icon">
{tab.icon}
{tab.key === '/m/messages' && unreadCount > 0 ? (
<em className="mobile-tab__badge">{unreadCount > 99 ? '99+' : unreadCount}</em>
) : null}
</span>
<span className="mobile-tab__title">{tab.title}</span>
</Link>
)
})}
</nav>
)}
</div>
)
}