feat(mobile): 重构移动端首页与个人中心
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user