feat(admin): 对齐旧站后台布局与导航
This commit is contained in:
@@ -563,6 +563,7 @@ export function AppRouter() {
|
||||
<Route path="withdrawals" element={<AdminWithdrawalsPage />} />
|
||||
<Route path="disputes" element={<AdminDisputesPage />} />
|
||||
<Route path="finance" element={<AdminFinancePage />} />
|
||||
<Route path="finance/dashboard" element={<AdminFinancePage />} />
|
||||
<Route path="finance/disbursements" element={<AdminFinanceDisbursementsPage />} />
|
||||
<Route path="finance/operating-expenses" element={<AdminOperatingExpensesPage />} />
|
||||
<Route path="announcements" element={<AdminAnnouncementsPage />} />
|
||||
@@ -591,6 +592,7 @@ export function AppRouter() {
|
||||
<Route path="chat-quick-replies" element={<AdminQuickRepliesPage />} />
|
||||
<Route path="tracking-links" element={<AdminTrackingLinksPage />} />
|
||||
<Route path="chat-support-groups" element={<AdminSupportGroupsPage />} />
|
||||
<Route path="support-groups" element={<AdminSupportGroupsPage />} />
|
||||
<Route path="distribution/sites" element={<AdminDistributionSitesPage />} />
|
||||
<Route
|
||||
path="distribution/dashboard"
|
||||
@@ -621,6 +623,10 @@ export function AppRouter() {
|
||||
element={<AdminDistributionInvitationPage />}
|
||||
/>
|
||||
<Route path="distribution/qrcodes" element={<AdminDistributionQrCodesPage />} />
|
||||
<Route
|
||||
path="distribution/qrcode-pool"
|
||||
element={<AdminDistributionQrCodesPage />}
|
||||
/>
|
||||
<Route path="distribution/rules" element={<AdminDistributionRulesPage />} />
|
||||
<Route
|
||||
path="*"
|
||||
|
||||
@@ -61,7 +61,7 @@ export function AdminBackupsPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 系统" title="备份管理" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 系统" title="数据备份" />
|
||||
<Alert type="warning" showIcon message="当前账号没有备份查看权限(backup:view)" />
|
||||
</div>
|
||||
)
|
||||
@@ -78,7 +78,7 @@ export function AdminBackupsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 系统"
|
||||
title="备份管理"
|
||||
title="数据备份"
|
||||
description="查看数据库全量/增量备份状态,并维护定时备份计划。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -215,7 +215,7 @@ export function AdminChatsPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 客服" title="客服会话" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 客服" title="客服群聊" />
|
||||
<Alert type="warning" showIcon message="当前账号没有聊天查看权限(chat:view)" />
|
||||
</div>
|
||||
)
|
||||
@@ -225,7 +225,7 @@ export function AdminChatsPage() {
|
||||
<div className="admin-page admin-chat-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 客服"
|
||||
title="客服会话"
|
||||
title="客服群聊"
|
||||
description="按阶段筛选会话、查看用户消息并直接回复,支持转接与备注。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -172,7 +172,7 @@ export function AdminCrashCategoriesPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="摸红分类" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="撞车分类" />
|
||||
<Alert type="warning" showIcon message="当前账号没有摸红商品权限" />
|
||||
</div>
|
||||
)
|
||||
@@ -182,7 +182,7 @@ export function AdminCrashCategoriesPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 摸红"
|
||||
title="摸红分类"
|
||||
title="撞车分类"
|
||||
description="维护撞车商城的商品分类与排序。"
|
||||
meta={<Tag color="geekblue">共 {categoriesQuery.data?.length ?? 0} 个分类</Tag>}
|
||||
extra={
|
||||
|
||||
@@ -35,8 +35,8 @@ export function AdminCrashConfigPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="摸红配置" />
|
||||
<Alert type="warning" showIcon message="当前账号没有摸红配置权限" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="撞车配置" />
|
||||
<Alert type="warning" showIcon message="当前账号没有撞车配置权限" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export function AdminCrashConfigPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 摸红"
|
||||
title="摸红配置"
|
||||
title="撞车配置"
|
||||
description="维护撞车订单默认固定二维码与订单一键复制模板。"
|
||||
extra={
|
||||
<Button
|
||||
|
||||
@@ -210,8 +210,8 @@ export function AdminCrashOrdersPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="摸红订单" />
|
||||
<Alert type="warning" showIcon message="当前账号没有摸红订单权限" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="撞车订单" />
|
||||
<Alert type="warning" showIcon message="当前账号没有撞车订单权限" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -220,7 +220,7 @@ export function AdminCrashOrdersPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 摸红"
|
||||
title="摸红订单"
|
||||
title="撞车订单"
|
||||
description="处理撞车商城订单:接单、完成、取消与退款。"
|
||||
meta={<Tag color="geekblue">共 {ordersQuery.data?.total ?? 0} 单</Tag>}
|
||||
extra={
|
||||
|
||||
@@ -245,8 +245,8 @@ export function AdminCrashProductsPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="摸红商品" />
|
||||
<Alert type="warning" showIcon message="当前账号没有摸红商品权限" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 摸红" title="撞车商品" />
|
||||
<Alert type="warning" showIcon message="当前账号没有撞车商品权限" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -255,7 +255,7 @@ export function AdminCrashProductsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 摸红"
|
||||
title="摸红商品"
|
||||
title="撞车商品"
|
||||
description="维护撞车商城商品、价格、库存与固定二维码。"
|
||||
meta={<Tag color="geekblue">共 {productsQuery.data?.total ?? 0} 个商品</Tag>}
|
||||
extra={
|
||||
|
||||
@@ -173,7 +173,7 @@ export function AdminDisputesPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 交易"
|
||||
title="申诉仲裁"
|
||||
title="仲裁中心"
|
||||
description="核对申诉描述、证据与结账快照,给出可追溯的仲裁结果。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -504,7 +504,7 @@ export function AdminDistributionQrCodesPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 分销"
|
||||
title="二维码池"
|
||||
title="分站二维码池"
|
||||
description="每个分站只从自己的码池发码;池空等待补码,不会混发主站或其他分站二维码。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -165,7 +165,7 @@ export function AdminFinanceDetailsPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader title="资金明细" eyebrow="JIEYOU OPS · 财务" />
|
||||
<AdminPageHeader title="财务明细" eyebrow="JIEYOU OPS · 财务" />
|
||||
<Alert type="warning" showIcon message="当前账号没有资金查看权限" />
|
||||
</div>
|
||||
)
|
||||
@@ -175,7 +175,7 @@ export function AdminFinanceDetailsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 财务"
|
||||
title="资金明细"
|
||||
title="财务明细"
|
||||
description="按订单维度核对实付、退款、号主结算与平台净额。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
@@ -256,7 +256,7 @@ export function AdminFinanceDetailsPage() {
|
||||
type="error"
|
||||
showIcon
|
||||
className="admin-table-alert"
|
||||
message="资金明细加载失败"
|
||||
message="财务明细加载失败"
|
||||
description={errorText(detailsQuery.error)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -273,7 +273,7 @@ export function AdminFinanceDetailsPage() {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextSize)
|
||||
}}
|
||||
emptyDescription="所选区间还没有资金明细"
|
||||
emptyDescription="所选区间还没有财务明细"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -265,7 +265,7 @@ export function AdminFinancePage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 财务"
|
||||
title="财务看板"
|
||||
title="财务仪表盘"
|
||||
description="按时间区间核对流水、退款、平台收入、号主结算与出款压力。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -240,7 +240,7 @@ export function AdminInvitationsPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 运营" title="邀请管理" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 运营" title="邀请返利管理" />
|
||||
<Alert type="warning" showIcon message="当前账号没有邀请数据查看权限" />
|
||||
</div>
|
||||
)
|
||||
@@ -252,7 +252,7 @@ export function AdminInvitationsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 运营"
|
||||
title="邀请管理"
|
||||
title="邀请返利管理"
|
||||
description="维护邀请返利比例,查看邀请关系与返利流水;比例按百分比录入,提交时转 0–1 小数。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -366,8 +366,8 @@ export function AdminManualEntriesPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 客服" title="手工录单" />
|
||||
<Alert type="warning" showIcon message="当前账号没有手工录单查看权限" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 客服" title="手动录单" />
|
||||
<Alert type="warning" showIcon message="当前账号没有手动录单查看权限" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -376,7 +376,7 @@ export function AdminManualEntriesPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 客服"
|
||||
title="手工录单"
|
||||
title="手动录单"
|
||||
description="客服在系统外成单后录入编号,用于对账与客服绩效统计。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -270,7 +270,7 @@ export function AdminRolesPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 权限"
|
||||
title="角色权限"
|
||||
title="角色管理"
|
||||
description="维护后台角色与权限矩阵;系统内置角色不可删除。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -328,7 +328,7 @@ export function AdminStaffAccountsPage() {
|
||||
if (!canManage) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 系统" title="管理员账号" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 系统" title="管理员管理" />
|
||||
<Alert type="warning" showIcon message="当前账号没有管理员账号管理权限" />
|
||||
</div>
|
||||
)
|
||||
@@ -338,7 +338,7 @@ export function AdminStaffAccountsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 系统"
|
||||
title="管理员账号"
|
||||
title="管理员管理"
|
||||
description="维护后台账号、启用状态、角色绑定与密码重置。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -238,7 +238,7 @@ export function AdminSupportGroupsPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 客服"
|
||||
title="客服群组"
|
||||
title="客服分组"
|
||||
description="维护客服分组、在线成员与排序,用于会话分配与订单客服群。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -322,8 +322,8 @@ export function AdminTrackingLinksPage() {
|
||||
if (!canManage) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 运营" title="推广追踪" />
|
||||
<Alert type="warning" showIcon message="当前账号没有推广追踪管理权限" />
|
||||
<AdminPageHeader eyebrow="JIEYOU OPS · 运营" title="推广链接管理" />
|
||||
<Alert type="warning" showIcon message="当前账号没有推广链接管理管理权限" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -332,7 +332,7 @@ export function AdminTrackingLinksPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 运营"
|
||||
title="推广追踪"
|
||||
title="推广链接管理"
|
||||
description="管理推广短链、渠道成本与注册下单转化数据。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
|
||||
@@ -125,7 +125,7 @@ export function AdminWalletLedgerPage() {
|
||||
if (!canView) {
|
||||
return (
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader title="钱包流水" eyebrow="JIEYOU OPS · 财务" />
|
||||
<AdminPageHeader title="资金流水" eyebrow="JIEYOU OPS · 财务" />
|
||||
<Alert type="warning" showIcon message="当前账号没有资金查看权限" />
|
||||
</div>
|
||||
)
|
||||
@@ -135,7 +135,7 @@ export function AdminWalletLedgerPage() {
|
||||
<div className="admin-page">
|
||||
<AdminPageHeader
|
||||
eyebrow="JIEYOU OPS · 财务"
|
||||
title="钱包流水"
|
||||
title="资金流水"
|
||||
description="按用户、订单与业务类型核对每一笔余额变动。"
|
||||
meta={
|
||||
<Space size={8} wrap>
|
||||
@@ -197,7 +197,7 @@ export function AdminWalletLedgerPage() {
|
||||
type="error"
|
||||
showIcon
|
||||
className="admin-table-alert"
|
||||
message="钱包流水加载失败"
|
||||
message="资金流水加载失败"
|
||||
description={errorText(ledgerQuery.error)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -214,7 +214,7 @@ export function AdminWalletLedgerPage() {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextSize)
|
||||
}}
|
||||
emptyDescription="还没有钱包流水"
|
||||
emptyDescription="还没有资金流水"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -169,6 +169,13 @@ export async function fetchAdminMe() {
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function updateAdminSupportStatus(status: 'online' | 'offline' | 'busy') {
|
||||
const { data } = await adminClient.put<
|
||||
ApiResponse<{ updated: boolean; support_status: string }>
|
||||
>('/admin/me/support-status', { status })
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function logoutAdmin() {
|
||||
await adminClient.post('/admin/auth/logout')
|
||||
}
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
import { useState, type ReactNode } from 'react'
|
||||
import { useEffect, useState, type ReactNode } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import {
|
||||
App,
|
||||
Avatar,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Dropdown,
|
||||
Layout,
|
||||
Menu,
|
||||
Space,
|
||||
Tag,
|
||||
Typography,
|
||||
type MenuProps,
|
||||
} from 'antd'
|
||||
import {
|
||||
AccountBookOutlined,
|
||||
ApartmentOutlined,
|
||||
AppstoreOutlined,
|
||||
BankOutlined,
|
||||
BellOutlined,
|
||||
CloudServerOutlined,
|
||||
ControlOutlined,
|
||||
CreditCardOutlined,
|
||||
CustomerServiceOutlined,
|
||||
DashboardOutlined,
|
||||
FileDoneOutlined,
|
||||
FileSearchOutlined,
|
||||
FileTextOutlined,
|
||||
FormOutlined,
|
||||
GiftOutlined,
|
||||
LineChartOutlined,
|
||||
LinkOutlined,
|
||||
LogoutOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
MessageOutlined,
|
||||
MoneyCollectOutlined,
|
||||
NotificationOutlined,
|
||||
ProfileOutlined,
|
||||
QrcodeOutlined,
|
||||
RollbackOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SafetyOutlined,
|
||||
SettingOutlined,
|
||||
ShareAltOutlined,
|
||||
ShopOutlined,
|
||||
ShoppingCartOutlined,
|
||||
SoundOutlined,
|
||||
SwapOutlined,
|
||||
TeamOutlined,
|
||||
UserSwitchOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { BrandLogo } from '@/components/BrandLogo'
|
||||
import { fetchAdminNotificationUnreadCount, updateAdminSupportStatus } from '@/features/admin/api'
|
||||
import { getAdminBasePath, getAdminLoginPath } from '@/shared/api/adminClient'
|
||||
import { useAdminSessionStore } from '@/stores/adminSession'
|
||||
|
||||
@@ -38,6 +61,7 @@ const { Sider, Header, Content } = Layout
|
||||
interface NavItem {
|
||||
key: string
|
||||
label: string
|
||||
icon?: ReactNode
|
||||
permission?: string
|
||||
}
|
||||
|
||||
@@ -53,131 +77,270 @@ const allNavGroups: NavGroup[] = [
|
||||
key: 'workspace',
|
||||
label: '工作台',
|
||||
icon: <DashboardOutlined />,
|
||||
children: [{ key: '/', label: '仪表盘', permission: 'dashboard:view' }],
|
||||
},
|
||||
{
|
||||
key: 'trade',
|
||||
label: '交易',
|
||||
icon: <ShoppingCartOutlined />,
|
||||
children: [
|
||||
{ key: '/orders', label: '订单管理', permission: 'order:list' },
|
||||
{ key: '/orders/refund-review', label: '退款审核', permission: 'order:close' },
|
||||
{ key: '/pickup', label: '线下提号', permission: 'order:pickup' },
|
||||
{ key: '/disputes', label: '申诉仲裁', permission: 'dispute:view' },
|
||||
{ key: '/', label: '仪表盘', icon: <DashboardOutlined />, permission: 'dashboard:view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'listings',
|
||||
label: '商品',
|
||||
icon: <ShopOutlined />,
|
||||
key: 'business',
|
||||
label: '业务管理',
|
||||
icon: <AppstoreOutlined />,
|
||||
children: [
|
||||
{ key: '/listings', label: '商品管理', permission: 'listing:view' },
|
||||
{ key: '/listings/review', label: '商品审核', permission: 'listing:approve' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'users',
|
||||
label: '用户',
|
||||
icon: <TeamOutlined />,
|
||||
children: [{ key: '/users', label: '用户管理', permission: 'user:view' }],
|
||||
},
|
||||
{
|
||||
key: 'finance',
|
||||
label: '财务',
|
||||
icon: <AccountBookOutlined />,
|
||||
children: [
|
||||
{ key: '/finance', label: '财务看板', permission: 'wallet:view' },
|
||||
{ key: '/finance/details', label: '资金明细', permission: 'wallet:view' },
|
||||
{ key: '/finance/disbursements', label: '资金出款', permission: 'wallet:view' },
|
||||
{ key: '/payments', label: '支付流水', permission: 'wallet:view' },
|
||||
{ key: '/wallet-ledger', label: '钱包流水', permission: 'wallet:view' },
|
||||
{ key: '/users', label: '用户管理', icon: <TeamOutlined />, permission: 'user:view' },
|
||||
{
|
||||
key: '/finance/operating-expenses',
|
||||
label: '运营开支',
|
||||
permission: 'finance:operating_expense',
|
||||
key: '/orders',
|
||||
label: '订单管理',
|
||||
icon: <FileTextOutlined />,
|
||||
permission: 'order:list',
|
||||
},
|
||||
{
|
||||
key: '/orders/refund-review',
|
||||
label: '退款审核',
|
||||
icon: <RollbackOutlined />,
|
||||
permission: 'order:close',
|
||||
},
|
||||
{ key: '/pickup', label: '线下提号', icon: <SwapOutlined />, permission: 'order:pickup' },
|
||||
{
|
||||
key: '/listings',
|
||||
label: '商品管理',
|
||||
icon: <ShopOutlined />,
|
||||
permission: 'listing:view',
|
||||
},
|
||||
{
|
||||
key: '/listings/review',
|
||||
label: '商品审核',
|
||||
icon: <FileDoneOutlined />,
|
||||
permission: 'listing:approve',
|
||||
},
|
||||
{
|
||||
key: '/crash/categories',
|
||||
label: '撞车分类',
|
||||
icon: <GiftOutlined />,
|
||||
permission: 'mohong:product_view',
|
||||
},
|
||||
{
|
||||
key: '/crash/products',
|
||||
label: '撞车商品',
|
||||
icon: <GiftOutlined />,
|
||||
permission: 'mohong:product_view',
|
||||
},
|
||||
{
|
||||
key: '/crash/orders',
|
||||
label: '撞车订单',
|
||||
icon: <ProfileOutlined />,
|
||||
permission: 'mohong:order_view',
|
||||
},
|
||||
{
|
||||
key: '/crash/config',
|
||||
label: '撞车配置',
|
||||
icon: <ControlOutlined />,
|
||||
permission: 'mohong:config',
|
||||
},
|
||||
{ key: '/withdrawals', label: '提现审核', permission: 'withdrawal:list' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'distribution',
|
||||
label: '分销',
|
||||
label: '分销管理',
|
||||
icon: <ShareAltOutlined />,
|
||||
children: [
|
||||
{ key: '/distribution/dashboard', label: '分销看板', permission: 'distribution:site_view' },
|
||||
{ key: '/distribution/sites', label: '站点管理', permission: 'distribution:site_view' },
|
||||
{ key: '/distribution/orders', label: '分销订单', permission: 'distribution:order_view' },
|
||||
{
|
||||
key: '/distribution/sites',
|
||||
label: '分销站点管理',
|
||||
icon: <ApartmentOutlined />,
|
||||
permission: 'distribution:site_view',
|
||||
},
|
||||
{
|
||||
key: '/distribution/qrcode-pool',
|
||||
label: '分站二维码池',
|
||||
icon: <QrcodeOutlined />,
|
||||
permission: 'qrcode:view',
|
||||
},
|
||||
{
|
||||
key: '/distribution/rules',
|
||||
label: '分销规则',
|
||||
icon: <SettingOutlined />,
|
||||
permission: 'distribution:rule_view',
|
||||
},
|
||||
{
|
||||
key: '/distribution/orders',
|
||||
label: '分销订单',
|
||||
icon: <ProfileOutlined />,
|
||||
permission: 'distribution:order_view',
|
||||
},
|
||||
{
|
||||
key: '/distribution/settlements',
|
||||
label: '结算批次',
|
||||
label: '结算对账',
|
||||
icon: <AccountBookOutlined />,
|
||||
permission: 'distribution:settlement_view',
|
||||
},
|
||||
{ key: '/distribution/users', label: '分销用户', permission: 'distribution:site_view' },
|
||||
{
|
||||
key: '/distribution/announcements',
|
||||
label: '站点公告',
|
||||
permission: 'distribution:announcement_view',
|
||||
key: '/invitations',
|
||||
label: '邀请返利管理',
|
||||
icon: <GiftOutlined />,
|
||||
permission: 'distribution:site_view',
|
||||
},
|
||||
{ key: '/distribution/listings', label: '分销商品', permission: 'distribution:account_view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'ad-tracking',
|
||||
label: '推广追踪',
|
||||
icon: <LineChartOutlined />,
|
||||
children: [
|
||||
{
|
||||
key: '/distribution/payment-channels',
|
||||
label: '支付通道',
|
||||
permission: 'distribution:payment_view',
|
||||
key: '/tracking-links',
|
||||
label: '推广链接管理',
|
||||
icon: <LinkOutlined />,
|
||||
permission: 'ad_tracking:manage',
|
||||
},
|
||||
{ key: '/distribution/wallet', label: '站点钱包', permission: 'distribution:settlement_view' },
|
||||
{ key: '/distribution/rules', label: '规则版本', permission: 'distribution:rule_view' },
|
||||
{ key: '/distribution/qrcodes', label: '二维码池', permission: 'qrcode:view' },
|
||||
{ key: '/distribution/sms', label: '站点短信', permission: 'distribution:site_view' },
|
||||
{ key: '/distribution/invitation', label: '邀请返利', permission: 'distribution:site_view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'crash',
|
||||
label: '摸红',
|
||||
icon: <GiftOutlined />,
|
||||
children: [
|
||||
{ key: '/crash/categories', label: '分类管理', permission: 'mohong:product_view' },
|
||||
{ key: '/crash/products', label: '商品管理', permission: 'mohong:product_view' },
|
||||
{ key: '/crash/orders', label: '订单管理', permission: 'mohong:order_view' },
|
||||
{ key: '/crash/config', label: '摸红配置', permission: 'mohong:config' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'operations',
|
||||
label: '运营',
|
||||
icon: <BellOutlined />,
|
||||
children: [
|
||||
{ key: '/announcements', label: '公告管理', permission: 'announcement:view' },
|
||||
{ key: '/tracking-links', label: '推广追踪', permission: 'ad_tracking:manage' },
|
||||
{ key: '/notifications', label: '通知中心', permission: 'notification:view' },
|
||||
{ key: '/push', label: '推送通知', permission: 'system_config:view' },
|
||||
{ key: '/invitations', label: '邀请管理', permission: 'distribution:site_view' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'support',
|
||||
label: '客服',
|
||||
key: 'support-risk',
|
||||
label: '客服与风控',
|
||||
icon: <CustomerServiceOutlined />,
|
||||
children: [
|
||||
{ key: '/chat-support-groups', label: '客服群组', permission: 'chat:manage' },
|
||||
{ key: '/chats', label: '客服会话', permission: 'chat:view' },
|
||||
{ key: '/qrcode-pool', label: '二维码池', permission: 'qrcode:view' },
|
||||
{ key: '/manual-entries', label: '手工录单', permission: 'cs_manual_entry:view' },
|
||||
{ key: '/chat-quick-replies', label: '快捷回复', permission: 'chat:view' },
|
||||
{ key: '/chat-welcome', label: '欢迎语配置', permission: 'system_config:view' },
|
||||
{
|
||||
key: '/disputes',
|
||||
label: '仲裁中心',
|
||||
icon: <SafetyOutlined />,
|
||||
permission: 'dispute:view',
|
||||
},
|
||||
{ key: '/chats', label: '客服群聊', icon: <MessageOutlined />, permission: 'chat:view' },
|
||||
{
|
||||
key: '/qrcode-pool',
|
||||
label: '二维码池',
|
||||
icon: <QrcodeOutlined />,
|
||||
permission: 'qrcode:view',
|
||||
},
|
||||
{
|
||||
key: '/support-groups',
|
||||
label: '客服分组',
|
||||
icon: <TeamOutlined />,
|
||||
permission: 'chat:manage',
|
||||
},
|
||||
{
|
||||
key: '/manual-entries',
|
||||
label: '手动录单',
|
||||
icon: <FormOutlined />,
|
||||
permission: 'cs_manual_entry:view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'system',
|
||||
label: '系统',
|
||||
key: 'finance',
|
||||
label: '财务管理',
|
||||
icon: <AccountBookOutlined />,
|
||||
children: [
|
||||
{
|
||||
key: '/finance/dashboard',
|
||||
label: '财务仪表盘',
|
||||
icon: <DashboardOutlined />,
|
||||
permission: 'wallet:view',
|
||||
},
|
||||
{
|
||||
key: '/finance/details',
|
||||
label: '财务明细',
|
||||
icon: <FileTextOutlined />,
|
||||
permission: 'wallet:view',
|
||||
},
|
||||
{
|
||||
key: '/finance/disbursements',
|
||||
label: '资金出款',
|
||||
icon: <BankOutlined />,
|
||||
permission: 'wallet:view',
|
||||
},
|
||||
{
|
||||
key: '/finance/operating-expenses',
|
||||
label: '运营开支',
|
||||
icon: <MoneyCollectOutlined />,
|
||||
permission: 'finance:operating_expense',
|
||||
},
|
||||
{
|
||||
key: '/wallet-ledger',
|
||||
label: '资金流水',
|
||||
icon: <AccountBookOutlined />,
|
||||
permission: 'wallet:view',
|
||||
},
|
||||
{
|
||||
key: '/payments',
|
||||
label: '支付流水',
|
||||
icon: <CreditCardOutlined />,
|
||||
permission: 'wallet:view',
|
||||
},
|
||||
{
|
||||
key: '/withdrawals',
|
||||
label: '提现审核',
|
||||
icon: <MoneyCollectOutlined />,
|
||||
permission: 'withdrawal:list',
|
||||
},
|
||||
{
|
||||
key: '/payment-configs',
|
||||
label: '支付配置',
|
||||
icon: <CreditCardOutlined />,
|
||||
permission: 'payment_config:list',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'content-config',
|
||||
label: '内容与配置',
|
||||
icon: <SettingOutlined />,
|
||||
children: [
|
||||
{ key: '/system-configs', label: '系统配置', permission: 'system_config:view' },
|
||||
{ key: '/roles', label: '角色权限', permission: 'role:manage' },
|
||||
{ key: '/admin-users', label: '管理员账号', permission: 'admin_user:manage' },
|
||||
{ key: '/payment-configs', label: '支付配置', permission: 'payment_config:list' },
|
||||
{ key: '/backups', label: '备份管理', permission: 'backup:view' },
|
||||
{ key: '/audit-logs', label: '审计日志', permission: 'audit_log:view' },
|
||||
{
|
||||
key: '/announcements',
|
||||
label: '公告管理',
|
||||
icon: <SoundOutlined />,
|
||||
permission: 'announcement:view',
|
||||
},
|
||||
{
|
||||
key: '/notifications',
|
||||
label: '通知中心',
|
||||
icon: <BellOutlined />,
|
||||
permission: 'notification:view',
|
||||
},
|
||||
{
|
||||
key: '/push',
|
||||
label: '推送通知',
|
||||
icon: <NotificationOutlined />,
|
||||
permission: 'system_config:view',
|
||||
},
|
||||
{
|
||||
key: '/system-configs',
|
||||
label: '系统配置',
|
||||
icon: <ControlOutlined />,
|
||||
permission: 'system_config:view',
|
||||
},
|
||||
{
|
||||
key: '/backups',
|
||||
label: '数据备份',
|
||||
icon: <CloudServerOutlined />,
|
||||
permission: 'backup:view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'system-auth',
|
||||
label: '系统权限',
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
children: [
|
||||
{
|
||||
key: '/audit-logs',
|
||||
label: '审计日志',
|
||||
icon: <FileSearchOutlined />,
|
||||
permission: 'audit_log:view',
|
||||
},
|
||||
{
|
||||
key: '/admin-users',
|
||||
label: '管理员管理',
|
||||
icon: <UserSwitchOutlined />,
|
||||
permission: 'admin_user:manage',
|
||||
},
|
||||
{
|
||||
key: '/roles',
|
||||
label: '角色管理',
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
permission: 'role:manage',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -186,35 +349,37 @@ const sectionMeta: Record<string, { title: string; description: string }> = {
|
||||
'/orders': { title: '订单管理', description: '查看全量订单、交接记录与结账状态。' },
|
||||
'/orders/refund-review': { title: '退款审核', description: '审核退款申请并核对资金影响。' },
|
||||
'/pickup': { title: '线下提号', description: '管理员线下提号,站内结算或线下打款确认。' },
|
||||
'/disputes': { title: '申诉仲裁', description: '处理订单争议、证据与仲裁结果。' },
|
||||
'/disputes': { title: '仲裁中心', description: '处理订单争议、证据与仲裁结果。' },
|
||||
'/listings': { title: '商品管理', description: '查看商品、账号资产与上下架状态。' },
|
||||
'/listings/review': { title: '商品审核', description: '审核待上架商品与账号截图。' },
|
||||
'/users': { title: '用户管理', description: '查看用户、实名状态与交易记录。' },
|
||||
'/finance': { title: '财务看板', description: '查看收入、退款、结算与资金趋势。' },
|
||||
'/finance/details': { title: '资金明细', description: '按订单核对实付、退款、号主结算与平台净额。' },
|
||||
'/finance': { title: '财务仪表盘', description: '查看收入、退款、结算与资金趋势。' },
|
||||
'/finance/dashboard': { title: '财务仪表盘', description: '查看收入、退款、结算与资金趋势。' },
|
||||
'/finance/details': { title: '财务明细', description: '按订单核对实付、退款、号主结算与平台净额。' },
|
||||
'/payments': { title: '支付流水', description: '核对渠道支付单、回调状态与失败原因。' },
|
||||
'/wallet-ledger': { title: '钱包流水', description: '按用户、订单与业务类型核对余额变动。' },
|
||||
'/wallet-ledger': { title: '资金流水', description: '按用户、订单与业务类型核对余额变动。' },
|
||||
'/finance/disbursements': { title: '资金出款', description: '查看代管出款、提现与线下出款明细。' },
|
||||
'/finance/operating-expenses': { title: '运营开支', description: '登记与核对日常运营支出。' },
|
||||
'/withdrawals': { title: '提现审核', description: '审核提现申请与打款确认。' },
|
||||
'/distribution/sites': { title: '分销站点', description: '管理分站、规则、结算与站点钱包。' },
|
||||
'/distribution/sites': { title: '分销站点管理', description: '管理分站、规则、结算与站点钱包。' },
|
||||
'/distribution/dashboard': { title: '分销看板', description: '查看站点收号、订单、利润与近 7 天趋势。' },
|
||||
'/distribution/orders': { title: '分销订单', description: '核对分销订单快照与冷却期释放状态。' },
|
||||
'/distribution/settlements': { title: '结算批次', description: '生成人工批次并核对、释放站内份额。' },
|
||||
'/distribution/settlements': { title: '结算对账', description: '生成人工批次并核对、释放站内份额。' },
|
||||
'/distribution/users': { title: '分销用户', description: '查看站点注册客户、下单客户与消费贡献。' },
|
||||
'/distribution/announcements': { title: '站点公告', description: '维护分站自己的公告中心与弹窗公告。' },
|
||||
'/distribution/listings': { title: '分销商品', description: '查看收货商品并控制账号展示与加价。' },
|
||||
'/distribution/payment-channels': { title: '支付通道', description: '为分站绑定主站支付商户配置。' },
|
||||
'/distribution/wallet': { title: '站点钱包', description: '查看分站余额、结算进度与提现记录。' },
|
||||
'/distribution/rules': { title: '规则版本', description: '维护全局与站点级分账规则版本。' },
|
||||
'/distribution/qrcodes': { title: '二维码池', description: '按站点维护发布群二维码池与发放状态。' },
|
||||
'/distribution/rules': { title: '分销规则', description: '维护全局与站点级分账规则版本。' },
|
||||
'/distribution/qrcodes': { title: '分站二维码池', description: '按站点维护发布群二维码池与发放状态。' },
|
||||
'/distribution/qrcode-pool': { title: '分站二维码池', description: '按站点维护发布群二维码池与发放状态。' },
|
||||
'/distribution/sms': { title: '站点短信', description: '为分站配置独立短信签名、模板与密钥。' },
|
||||
'/distribution/invitation': { title: '邀请返利', description: '配置分站邀请好友的返利比例。' },
|
||||
'/announcements': { title: '公告管理', description: '维护主站与分站公告。' },
|
||||
'/roles': { title: '角色权限', description: '维护后台角色并分配资源级操作权限。' },
|
||||
'/admin-users': { title: '管理员账号', description: '维护后台账号、启用状态与角色绑定。' },
|
||||
'/roles': { title: '角色管理', description: '维护后台角色并分配资源级操作权限。' },
|
||||
'/admin-users': { title: '管理员管理', description: '维护后台账号、启用状态与角色绑定。' },
|
||||
'/manual-entries': {
|
||||
title: '手工录单',
|
||||
title: '手动录单',
|
||||
description: '录入系统外成单编号,用于对账与客服绩效统计。',
|
||||
},
|
||||
'/chat-quick-replies': {
|
||||
@@ -222,25 +387,29 @@ const sectionMeta: Record<string, { title: string; description: string }> = {
|
||||
description: '维护客服会话常用话术与全局可见范围。',
|
||||
},
|
||||
'/tracking-links': {
|
||||
title: '推广追踪',
|
||||
title: '推广链接管理',
|
||||
description: '管理推广短链、渠道成本与注册下单转化。',
|
||||
},
|
||||
'/crash/categories': { title: '摸红分类', description: '维护撞车商城商品分类与排序。' },
|
||||
'/crash/products': { title: '摸红商品', description: '维护撞车商城商品、价格与固定二维码。' },
|
||||
'/crash/orders': { title: '摸红订单', description: '处理撞车订单的接单、完成、取消与退款。' },
|
||||
'/crash/config': { title: '摸红配置', description: '维护默认二维码与订单一键复制模板。' },
|
||||
'/crash/categories': { title: '撞车分类', description: '维护撞车商城商品分类与排序。' },
|
||||
'/crash/products': { title: '撞车商品', description: '维护撞车商城商品、价格与固定二维码。' },
|
||||
'/crash/orders': { title: '撞车订单', description: '处理撞车订单的接单、完成、取消与退款。' },
|
||||
'/crash/config': { title: '撞车配置', description: '维护默认二维码与订单一键复制模板。' },
|
||||
'/notifications': { title: '通知中心', description: '查看后台系统通知并批量标记已读。' },
|
||||
'/push': { title: '推送通知', description: '维护推送渠道与通知规则。' },
|
||||
'/invitations': {
|
||||
title: '邀请管理',
|
||||
title: '邀请返利管理',
|
||||
description: '维护邀请返利比例,查看邀请关系与返利流水。',
|
||||
},
|
||||
'/chat-support-groups': {
|
||||
title: '客服群组',
|
||||
title: '客服分组',
|
||||
description: '维护客服群与坐席分配,支撑会话分流。',
|
||||
},
|
||||
'/support-groups': {
|
||||
title: '客服分组',
|
||||
description: '维护客服群与坐席分配,支撑会话分流。',
|
||||
},
|
||||
'/chats': {
|
||||
title: '客服会话',
|
||||
title: '客服群聊',
|
||||
description: '按阶段筛选会话、回复用户并转接客服。',
|
||||
},
|
||||
'/chat-welcome': {
|
||||
@@ -253,22 +422,55 @@ const sectionMeta: Record<string, { title: string; description: string }> = {
|
||||
},
|
||||
'/system-configs': { title: '系统配置', description: '维护首页、发布、订单与支付配置。' },
|
||||
'/payment-configs': { title: '支付配置', description: '维护主站支付商户、渠道与回调参数。' },
|
||||
'/backups': { title: '备份管理', description: '查看备份健康状态并维护定时备份计划。' },
|
||||
'/backups': { title: '数据备份', description: '查看备份健康状态并维护定时备份计划。' },
|
||||
'/audit-logs': { title: '审计日志', description: '查看高风险操作与变更记录。' },
|
||||
}
|
||||
|
||||
const MENU_ALIASES: Record<string, string> = {
|
||||
'/finance': '/finance/dashboard',
|
||||
'/chat-support-groups': '/support-groups',
|
||||
'/distribution/qrcodes': '/distribution/qrcode-pool',
|
||||
}
|
||||
|
||||
const SUPPORT_STATUS_LABELS: Record<string, string> = {
|
||||
online: '在线',
|
||||
busy: '忙碌',
|
||||
offline: '离线',
|
||||
}
|
||||
|
||||
export function AdminLayout() {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const admin = useAdminSessionStore(state => state.admin)
|
||||
const logout = useAdminSessionStore(state => state.logout)
|
||||
const { message } = App.useApp()
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
const [openKeys, setOpenKeys] = useState<string[]>([])
|
||||
const [supportStatus, setSupportStatus] = useState<'online' | 'offline' | 'busy'>(
|
||||
admin?.support_status ?? 'offline'
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (admin?.support_status) setSupportStatus(admin.support_status)
|
||||
}, [admin?.support_status])
|
||||
|
||||
const canViewNotifications = Boolean(
|
||||
admin?.permissions?.includes('*') || admin?.permissions?.includes('notification:view')
|
||||
)
|
||||
|
||||
const { data: unreadCount = 0 } = useQuery({
|
||||
queryKey: ['admin-notification-unread'],
|
||||
queryFn: fetchAdminNotificationUnreadCount,
|
||||
enabled: canViewNotifications,
|
||||
refetchInterval: 60_000,
|
||||
})
|
||||
|
||||
const basePath = getAdminBasePath()
|
||||
const relativePath =
|
||||
location.pathname.startsWith(basePath) && location.pathname !== basePath
|
||||
? location.pathname.slice(basePath.length)
|
||||
: '/'
|
||||
const selectedKey = MENU_ALIASES[relativePath] ?? relativePath
|
||||
|
||||
const can = (permission?: string) => {
|
||||
if (!permission) return true
|
||||
@@ -290,19 +492,42 @@ export function AdminLayout() {
|
||||
children: group.children.map(item => ({
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
icon: item.icon,
|
||||
})),
|
||||
}))
|
||||
|
||||
const activeGroupKey = visibleGroups.find(group =>
|
||||
group.children.some(item => item.key === selectedKey)
|
||||
)?.key
|
||||
|
||||
useEffect(() => {
|
||||
if (activeGroupKey) setOpenKeys([activeGroupKey])
|
||||
}, [activeGroupKey])
|
||||
|
||||
const currentLabel =
|
||||
visibleGroups.flatMap(group => group.children).find(item => item.key === relativePath)?.label ||
|
||||
sectionMeta[relativePath]?.title ||
|
||||
(relativePath === '/' ? '仪表盘' : '功能建设中')
|
||||
visibleGroups.flatMap(group => group.children).find(item => item.key === selectedKey)?.label ||
|
||||
sectionMeta[selectedKey]?.title ||
|
||||
(selectedKey === '/' ? '仪表盘' : '功能建设中')
|
||||
|
||||
const onLogout = async () => {
|
||||
await logout()
|
||||
navigate(getAdminLoginPath(), { replace: true })
|
||||
}
|
||||
|
||||
const adminName = admin?.nickname || admin?.username || '管理员'
|
||||
const adminRoleText = admin?.roles?.[0]?.name || '管理员'
|
||||
|
||||
const handleStatusChange = async (status: 'online' | 'offline' | 'busy') => {
|
||||
if (status === supportStatus) return
|
||||
try {
|
||||
await updateAdminSupportStatus(status)
|
||||
setSupportStatus(status)
|
||||
message.success(`已切换为${SUPPORT_STATUS_LABELS[status]}`)
|
||||
} catch {
|
||||
message.error('状态更新失败')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout className="admin-shell">
|
||||
<Sider
|
||||
@@ -324,8 +549,12 @@ export function AdminLayout() {
|
||||
theme="light"
|
||||
inlineCollapsed={collapsed}
|
||||
items={menuItems}
|
||||
selectedKeys={[relativePath]}
|
||||
defaultOpenKeys={visibleGroups.map(group => group.key)}
|
||||
selectedKeys={[selectedKey]}
|
||||
openKeys={openKeys}
|
||||
onOpenChange={keys => {
|
||||
const opened = keys.find(key => !openKeys.includes(key))
|
||||
setOpenKeys(opened ? [opened] : [])
|
||||
}}
|
||||
onClick={({ key }) => {
|
||||
if (key.startsWith('/')) {
|
||||
navigate(key === '/' ? basePath : `${basePath}${key}`)
|
||||
@@ -335,8 +564,59 @@ export function AdminLayout() {
|
||||
|
||||
{!collapsed ? (
|
||||
<div className="admin-sider__foot">
|
||||
<Typography.Text type="secondary">解忧商行 · 运营后台</Typography.Text>
|
||||
<Typography.Text type="secondary">Phase 2 · 交易与商品</Typography.Text>
|
||||
{canViewNotifications ? (
|
||||
<Link className="admin-sider__notice" to={`${basePath}/notifications`}>
|
||||
<BellOutlined />
|
||||
<span>通知中心</span>
|
||||
{unreadCount > 0 ? (
|
||||
<Tag color="red">{unreadCount > 99 ? '99+' : unreadCount}</Tag>
|
||||
) : null}
|
||||
</Link>
|
||||
) : null}
|
||||
|
||||
<Dropdown
|
||||
placement="topLeft"
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'online', label: '在线' },
|
||||
{ key: 'busy', label: '忙碌' },
|
||||
{ key: 'offline', label: '离线' },
|
||||
],
|
||||
selectedKeys: [supportStatus],
|
||||
onClick: ({ key }) =>
|
||||
void handleStatusChange(key as 'online' | 'offline' | 'busy'),
|
||||
}}
|
||||
>
|
||||
<button type="button" className="admin-sider__status">
|
||||
<span className={`admin-status-dot is-${supportStatus}`} />
|
||||
{SUPPORT_STATUS_LABELS[supportStatus]}
|
||||
</button>
|
||||
</Dropdown>
|
||||
|
||||
<Dropdown
|
||||
placement="topLeft"
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'account',
|
||||
label: `${adminName} · ${admin?.username || ''}`,
|
||||
disabled: true,
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{ key: 'logout', label: '退出后台', icon: <LogoutOutlined />, onClick: onLogout },
|
||||
],
|
||||
}}
|
||||
>
|
||||
<button type="button" className="admin-sider__account">
|
||||
<Avatar size={28} className="admin-avatar">
|
||||
{adminName.slice(0, 1)}
|
||||
</Avatar>
|
||||
<span className="admin-sider__account-text">
|
||||
<strong>{adminName}</strong>
|
||||
<small>{adminRoleText}</small>
|
||||
</span>
|
||||
</button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
</Sider>
|
||||
@@ -358,31 +638,6 @@ export function AdminLayout() {
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Space size={16}>
|
||||
{admin?.roles?.[0] ? <Tag className="admin-role-tag">{admin.roles[0].name}</Tag> : null}
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'account',
|
||||
label: `${admin?.nickname || admin?.username || '管理员'} · ${admin?.username || ''}`,
|
||||
disabled: true,
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{ key: 'logout', label: '退出后台', icon: <LogoutOutlined />, onClick: onLogout },
|
||||
],
|
||||
}}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<Button type="text" className="admin-account">
|
||||
<Avatar size={30} className="admin-avatar">
|
||||
{(admin?.nickname || admin?.username || '管').slice(0, 1)}
|
||||
</Avatar>
|
||||
<span className="admin-account__name">{admin?.nickname || admin?.username}</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</Header>
|
||||
|
||||
<Content className="admin-content">
|
||||
|
||||
@@ -66,11 +66,101 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 16px 20px;
|
||||
padding: 14px 16px 16px;
|
||||
border-top: 1px solid var(--line);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.admin-sider__notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 7px 8px;
|
||||
border-radius: 6px;
|
||||
color: var(--ink-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.admin-sider__notice:hover {
|
||||
background: #f1ece0;
|
||||
color: var(--brand-ink);
|
||||
}
|
||||
|
||||
.admin-sider__notice .ant-tag {
|
||||
margin-inline-end: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.admin-sider__status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 7px 8px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--ink-secondary);
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-sider__status:hover {
|
||||
background: #f1ece0;
|
||||
}
|
||||
|
||||
.admin-status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #9ca3af;
|
||||
}
|
||||
|
||||
.admin-status-dot.is-online {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.admin-status-dot.is-busy {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.admin-status-dot.is-offline {
|
||||
background: #9ca3af;
|
||||
}
|
||||
|
||||
.admin-sider__account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
padding: 10px 8px 2px;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-sider__account-text {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.admin-sider__account-text strong {
|
||||
color: var(--brand-ink);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.admin-sider__account-text small {
|
||||
color: var(--ink-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
min-width: 0;
|
||||
background: var(--paper);
|
||||
|
||||
Reference in New Issue
Block a user