feat(web): 初始化前端项目
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
printWidth: 100,
|
||||||
|
tabWidth: 4,
|
||||||
|
useTabs: false,
|
||||||
|
semi: true,
|
||||||
|
singleQuote: true,
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
jsxSingleQuote: false,
|
||||||
|
trailingComma: 'es5',
|
||||||
|
bracketSpacing: true,
|
||||||
|
bracketSameLine: false,
|
||||||
|
arrowParens: 'always',
|
||||||
|
endOfLine: 'lf',
|
||||||
|
embeddedLanguageFormatting: 'auto',
|
||||||
|
};
|
||||||
@@ -12,18 +12,66 @@
|
|||||||
padding: 24px 20px;
|
padding: 24px 20px;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
transition: padding 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 折叠态收窄水平留白,与宽度动画同步 */
|
||||||
|
.ant-layout-sider-collapsed .sidebar-header {
|
||||||
|
padding: 20px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
|
position: relative;
|
||||||
|
height: 31px;
|
||||||
|
line-height: 31px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: white;
|
color: white;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 全称与缩写叠加存放,折叠时交叉淡入淡出 */
|
||||||
|
.logo-full,
|
||||||
|
.logo-short {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition:
|
||||||
|
opacity 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-short {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-layout-sider-collapsed .logo-full {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-layout-sider-collapsed .logo-short {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 副标题折叠时渐隐并收起占位高度 */
|
||||||
.logo-subtitle {
|
.logo-subtitle {
|
||||||
|
max-height: 18px;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 1;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: rgba(255, 255, 255, 0.8);
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
transition:
|
||||||
|
opacity 0.2s ease,
|
||||||
|
max-height 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-layout-sider-collapsed .logo-subtitle {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-header {
|
.main-header {
|
||||||
@@ -38,19 +86,19 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
height: 64px;
|
height: 50px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-left {
|
.header-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapse-trigger,
|
.collapse-trigger,
|
||||||
.mobile-menu-trigger {
|
.mobile-menu-trigger {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -58,8 +106,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 32px;
|
width: 28px;
|
||||||
height: 32px;
|
height: 28px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,32 +116,34 @@
|
|||||||
background: #f9fafb;
|
background: #f9fafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title-group {
|
.page-breadcrumb {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #1f2937;
|
|
||||||
line-height: 1.2;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-subtitle {
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6b7280;
|
line-height: 1;
|
||||||
font-style: italic;
|
white-space: nowrap;
|
||||||
line-height: 1.2;
|
}
|
||||||
|
|
||||||
|
.page-breadcrumb .ant-breadcrumb-link {
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-crumb {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-crumb .anticon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-breadcrumb .ant-breadcrumb-separator {
|
||||||
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
padding: 24px;
|
padding: 20px;
|
||||||
background: #f3f4f6;
|
background: #f3f4f6;
|
||||||
min-height: calc(100vh - 64px);
|
min-height: calc(100vh - 50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -104,12 +154,4 @@
|
|||||||
.main-header {
|
.main-header {
|
||||||
padding: 0 16px !important;
|
padding: 0 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-subtitle {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Outlet, useLocation } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
import { Drawer, Layout } from 'antd';
|
import { Breadcrumb, Drawer, Layout } from 'antd';
|
||||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
||||||
import SidebarMenu from './SidebarMenu';
|
import SidebarMenu from './SidebarMenu';
|
||||||
import { getPageInfo } from './menuConfig';
|
import { getBreadcrumbItems } from './menuConfig';
|
||||||
import './MainLayout.css';
|
import './MainLayout.css';
|
||||||
|
|
||||||
const { Header, Sider, Content } = Layout;
|
const { Header, Sider, Content } = Layout;
|
||||||
@@ -14,7 +14,18 @@ const MainLayout = () => {
|
|||||||
const [isMobile, setIsMobile] = useState(false);
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const pageInfo = useMemo(() => getPageInfo(location.pathname), [location.pathname]);
|
const breadcrumbItems = useMemo(
|
||||||
|
() =>
|
||||||
|
getBreadcrumbItems(location.pathname).map((crumb) => ({
|
||||||
|
title: (
|
||||||
|
<span className="breadcrumb-crumb">
|
||||||
|
{crumb.icon}
|
||||||
|
<span>{crumb.label}</span>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
[location.pathname]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkMobile = () => {
|
const checkMobile = () => {
|
||||||
@@ -45,8 +56,11 @@ const MainLayout = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="sidebar-header">
|
<div className="sidebar-header">
|
||||||
<div className="logo">{collapsed ? 'A' : 'Atlas Kit'}</div>
|
<div className="logo">
|
||||||
{!collapsed && <div className="logo-subtitle">商品工作台</div>}
|
<span className="logo-full">Atlas Kit</span>
|
||||||
|
<span className="logo-short">A</span>
|
||||||
|
</div>
|
||||||
|
<div className="logo-subtitle">商品工作台</div>
|
||||||
</div>
|
</div>
|
||||||
<SidebarMenu collapsed={collapsed} />
|
<SidebarMenu collapsed={collapsed} />
|
||||||
</Sider>
|
</Sider>
|
||||||
@@ -81,14 +95,18 @@ const MainLayout = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div className="collapse-trigger" onClick={() => setCollapsed(!collapsed)}>
|
<div
|
||||||
|
className="collapse-trigger"
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
>
|
||||||
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="page-title-group">
|
<Breadcrumb
|
||||||
<div className="page-title">{pageInfo.title}</div>
|
className="page-breadcrumb"
|
||||||
<div className="page-subtitle">{pageInfo.subtitle}</div>
|
separator=">"
|
||||||
</div>
|
items={breadcrumbItems}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,20 @@
|
|||||||
.sidebar-menu .ant-menu-item .ant-menu-title-content {
|
.sidebar-menu .ant-menu-item .ant-menu-title-content {
|
||||||
font-size: 15px !important;
|
font-size: 15px !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
/* antd 默认只过渡 color,会覆盖下面的 width/opacity 过渡;必须 !important 提权 */
|
||||||
|
transition:
|
||||||
|
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
width 0.2s ease,
|
||||||
|
opacity 0.2s ease,
|
||||||
|
margin 0.3s,
|
||||||
|
color 0.3s !important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
/* 显式宽度让折叠时 width→0 可插值,配合裁剪+渐隐平滑收起(auto→0 无法动画) */
|
||||||
|
width: 100%;
|
||||||
|
opacity: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu .ant-menu-item:hover {
|
.sidebar-menu .ant-menu-item:hover {
|
||||||
@@ -58,6 +70,14 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
/* 折叠时渐隐并收起占位高度(!important 防止被 antd 默认 transition 覆盖) */
|
||||||
|
max-height: 40px;
|
||||||
|
opacity: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
transition:
|
||||||
|
opacity 0.2s ease,
|
||||||
|
max-height 0.2s ease,
|
||||||
|
padding 0.2s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu .ant-menu-item-icon {
|
.sidebar-menu .ant-menu-item-icon {
|
||||||
@@ -67,7 +87,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu .ant-menu-item:hover .ant-menu-item-icon {
|
/* hover 放大只作用于非选中项;选中项图标固定 translateX(8px),hover 时不得变 transform 导致跳动 */
|
||||||
|
.sidebar-menu .ant-menu-item:hover:not(.ant-menu-item-selected) .ant-menu-item-icon {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +109,14 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分组标题折叠时渐隐并完全收起,不占纵向空间 */
|
||||||
|
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item-group-title {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item {
|
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -107,14 +136,17 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 折叠时去掉左侧 3px 选中边框(会挤偏图标),选中态用高亮背景区分 */
|
/* 折叠时去掉左侧 3px 选中边框(会挤偏图标);只过渡宽度,避免 none 突变 */
|
||||||
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item-selected {
|
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item-selected {
|
||||||
border-left: none !important;
|
border-left-width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 折叠时隐藏 label 占位(antd 折叠动画保留 opacity 占位,会把图标挤离中心) */
|
/* 折叠时菜单文字平滑收起:width 100%→0 插值 + 裁剪 + 渐隐;
|
||||||
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item .ant-menu-title-content {
|
不用 display:none(无法过渡,文字瞬间消失) */
|
||||||
display: none !important;
|
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item .ant-menu-title-content,
|
||||||
|
.sidebar-menu.ant-menu-vertical .ant-menu-item .ant-menu-title-content {
|
||||||
|
width: 0 !important;
|
||||||
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 兼容:部分版本折叠态用 vertical 类渲染,同样居中 */
|
/* 兼容:部分版本折叠态用 vertical 类渲染,同样居中 */
|
||||||
@@ -124,10 +156,6 @@
|
|||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu.ant-menu-vertical .ant-menu-item .ant-menu-title-content {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 折叠态 flex 子元素居中(display:flex 已生效,缺 justify-content) */
|
/* 折叠态 flex 子元素居中(display:flex 已生效,缺 justify-content) */
|
||||||
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item {
|
.sidebar-menu.ant-menu-inline-collapsed .ant-menu-item {
|
||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const SidebarMenu = ({ collapsed, onMenuClick }: SidebarMenuProps) => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// 分组标题保持常驻,折叠时的隐藏由 CSS 过渡完成,避免文字瞬间消失
|
||||||
const menuItems: MenuProps['items'] = useMemo(() => {
|
const menuItems: MenuProps['items'] = useMemo(() => {
|
||||||
const children = routeMenuConfig.map((config) => ({
|
const children = routeMenuConfig.map((config) => ({
|
||||||
key: config.key,
|
key: config.key,
|
||||||
@@ -23,12 +24,12 @@ const SidebarMenu = ({ collapsed, onMenuClick }: SidebarMenuProps) => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: 'group' as const,
|
type: 'group' as const,
|
||||||
label: collapsed ? '' : '主要功能',
|
label: '主要功能',
|
||||||
className: 'menu-section-title',
|
className: 'menu-section-title',
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [collapsed]);
|
}, []);
|
||||||
|
|
||||||
const selectedKeys = useMemo(() => {
|
const selectedKeys = useMemo(() => {
|
||||||
const path = location.pathname;
|
const path = location.pathname;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CalculatorOutlined,
|
CalculatorOutlined,
|
||||||
|
CodeSandboxOutlined,
|
||||||
|
EditOutlined,
|
||||||
GlobalOutlined,
|
GlobalOutlined,
|
||||||
InboxOutlined,
|
InboxOutlined,
|
||||||
PictureOutlined,
|
PictureOutlined,
|
||||||
@@ -12,14 +14,10 @@ export interface RouteMenuConfig {
|
|||||||
path: string;
|
path: string;
|
||||||
/** 菜单键值(与 path 相同) */
|
/** 菜单键值(与 path 相同) */
|
||||||
key: string;
|
key: string;
|
||||||
/** 菜单图标 */
|
/** 菜单图标(菜单与页头面包屑共用) */
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
/** 菜单标签 */
|
/** 菜单标签 */
|
||||||
label: string;
|
label: string;
|
||||||
/** 页面标题 */
|
|
||||||
title: string;
|
|
||||||
/** 页面副标题 */
|
|
||||||
subtitle: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 左侧一级菜单,顺序与 docs/requirements/account-store-watermark.md 的导航约定一致 */
|
/** 左侧一级菜单,顺序与 docs/requirements/account-store-watermark.md 的导航约定一致 */
|
||||||
@@ -29,52 +27,46 @@ export const routeMenuConfig: RouteMenuConfig[] = [
|
|||||||
key: '/online-products',
|
key: '/online-products',
|
||||||
icon: <GlobalOutlined />,
|
icon: <GlobalOutlined />,
|
||||||
label: '在线商品',
|
label: '在线商品',
|
||||||
title: '在线商品',
|
|
||||||
subtitle: '按 SKU 查看平台已上架商品与同步状态',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/collection',
|
path: '/collection',
|
||||||
key: '/collection',
|
key: '/collection',
|
||||||
icon: <InboxOutlined />,
|
icon: <InboxOutlined />,
|
||||||
label: '采集箱',
|
label: '采集箱',
|
||||||
title: '采集箱',
|
|
||||||
subtitle: '查看已采集的商品,进入编辑上架',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/shops',
|
path: '/shops',
|
||||||
key: '/shops',
|
key: '/shops',
|
||||||
icon: <ShopOutlined />,
|
icon: <ShopOutlined />,
|
||||||
label: '店铺管理',
|
label: '店铺管理',
|
||||||
title: '店铺管理',
|
|
||||||
subtitle: '绑定平台店铺授权与水印',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/watermarks',
|
path: '/watermarks',
|
||||||
key: '/watermarks',
|
key: '/watermarks',
|
||||||
icon: <PictureOutlined />,
|
icon: <PictureOutlined />,
|
||||||
label: '水印管理',
|
label: '水印管理',
|
||||||
title: '水印管理',
|
|
||||||
subtitle: '维护水印模板并绑定到店铺',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/pricing',
|
path: '/pricing',
|
||||||
key: '/pricing',
|
key: '/pricing',
|
||||||
icon: <CalculatorOutlined />,
|
icon: <CalculatorOutlined />,
|
||||||
label: '定价工具',
|
label: '定价工具',
|
||||||
title: '定价工具',
|
|
||||||
subtitle: '独立试算与物流计价规则',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** 根据路径获取页面标题信息 */
|
export interface BreadcrumbCrumb {
|
||||||
export const getPageInfo = (path: string): { title: string; subtitle: string } => {
|
icon: ReactNode;
|
||||||
if (path === '/' || path === '') {
|
label: string;
|
||||||
const first = routeMenuConfig[0];
|
|
||||||
return { title: first.title, subtitle: first.subtitle };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 页头面包屑:一级菜单页显示单级;商品 AI 编辑页不在菜单中,显示「商品 > 商品编辑」两级 */
|
||||||
|
export const getBreadcrumbItems = (path: string): BreadcrumbCrumb[] => {
|
||||||
if (path.startsWith('/products/')) {
|
if (path.startsWith('/products/')) {
|
||||||
return { title: '商品编辑', subtitle: '商品 AI 编辑页:SPU/SKU、媒体、定价与发布' };
|
return [
|
||||||
|
{ icon: <CodeSandboxOutlined />, label: '商品' },
|
||||||
|
{ icon: <EditOutlined />, label: '商品编辑' },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
const config = routeMenuConfig.find((item) => path.startsWith(item.path));
|
const config = routeMenuConfig.find((item) => path.startsWith(item.path)) ?? routeMenuConfig[0];
|
||||||
return config ? { title: config.title, subtitle: config.subtitle } : getPageInfo('/');
|
return [{ icon: config.icon, label: config.label }];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ import App from './App';
|
|||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ const LoginPage = () => {
|
|||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="password" label="密码" rules={[{ required: true, message: '请输入密码' }]}>
|
<Form.Item
|
||||||
|
name="password"
|
||||||
|
label="密码"
|
||||||
|
rules={[{ required: true, message: '请输入密码' }]}
|
||||||
|
>
|
||||||
<Input.Password
|
<Input.Password
|
||||||
prefix={<LockOutlined style={{ color: 'var(--text-secondary)' }} />}
|
prefix={<LockOutlined style={{ color: 'var(--text-secondary)' }} />}
|
||||||
placeholder="请输入密码"
|
placeholder="请输入密码"
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ apiClient.interceptors.response.use(
|
|||||||
window.location.assign('/login');
|
window.location.assign('/login');
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
|
|||||||
Reference in New Issue
Block a user