From ae88da4c9f81b412162e998b348a3924cb290822 Mon Sep 17 00:00:00 2001 From: zichun Date: Fri, 8 May 2026 14:43:47 +0800 Subject: [PATCH] feat(usr): add NavOverlay component --- frontend/src/components/NavOverlay.tsx | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ frontend/src/test/NavOverlay.test.tsx | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 0 deletions(-) create mode 100644 frontend/src/components/NavOverlay.tsx create mode 100644 frontend/src/test/NavOverlay.test.tsx diff --git a/frontend/src/components/NavOverlay.tsx b/frontend/src/components/NavOverlay.tsx new file mode 100644 index 0000000..2a9d3b0 --- /dev/null +++ b/frontend/src/components/NavOverlay.tsx @@ -0,0 +1,86 @@ +import { useNavigate } from 'react-router-dom' +import { useAppDispatch } from '../store/hooks' +import { openTab } from '../store/slices/tabsSlice' + +const LEFT_MODULES = [ + '销售管理', 'DCS系统', '产品管理', '生产运营', '生产执行', '模具管理', + '采购管理', '材料库存', '成品库存', '外协管理', '物流管理', '质量管理', + '财务管理', '成本管理(专)', '成本管理', '设备管理', '人事行政', 'OA系统', + '基础设置', '系统设置', +] + +type NavItem = string | { label: string; go?: string; star?: boolean } + +const NAV_COLS: { title: string; items: NavItem[] }[] = [ + { title: '期初设置', items: ['客户期初', '供应商期初', '材料期初', '产品期初', '数据导入', '离线导出下载'] }, + { title: '用户管理', items: [{ label: '用户列表', go: 'userlist', star: true }, '系统权限', '系统权限稽查表', '权限组'] }, + { title: '系统参数', items: ['系统参数', '财务结账', '系统常量配置'] }, + { title: '计算方案', items: ['方案列表', '计算参数'] }, + { title: '日志', items: ['个性化模块', '操作日志', '异常清除KPI任务表', 'MYSQL监听器'] }, + { title: '开发平台', items: ['自定义开发范例', { label: '系统功能模块设置', star: true }, 'EBC流程清单', '功能模块界面设置', '增删改存业务处理'] }, + { title: 'API对接管理', items: ['调用第三方接口(TOKEN配置)', '调用第三方接口(接口定义)', '被第三方调用(生成token)', '数据同步', '被第三方调用(API定义)'] }, +] + +interface Props { onClose: () => void } + +export default function NavOverlay({ onClose }: Props) { + const navigate = useNavigate() + const dispatch = useAppDispatch() + + function handleUserList() { + dispatch(openTab({ id: 'userlist', title: '用户列表', path: '/usr/users', closable: true })) + navigate('/usr/users') + onClose() + } + + return ( +
+
e.stopPropagation()} style={{ display: 'flex', flex: 1 }}> + {/* Left sidebar */} +
+ {LEFT_MODULES.map(mod => ( +
+ {mod} +
+ ))} +
+ {/* Right grid */} +
+ {NAV_COLS.map(col => ( +
+

+ {col.title} +

+ {col.items.map(item => { + if (typeof item === 'string') { + return
{item}
+ } + return ( +
+ {item.label} + {item.star && } +
+ ) + })} +
+ ))} +
+
+
+ ) +} diff --git a/frontend/src/test/NavOverlay.test.tsx b/frontend/src/test/NavOverlay.test.tsx new file mode 100644 index 0000000..25e7093 --- /dev/null +++ b/frontend/src/test/NavOverlay.test.tsx @@ -0,0 +1,56 @@ +import { describe, it, expect, vi } from 'vitest' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { Provider } from 'react-redux' +import { MemoryRouter, Routes, Route } from 'react-router-dom' +import { configureStore } from '@reduxjs/toolkit' +import authReducer from '../store/slices/authSlice' +import tabsReducer from '../store/slices/tabsSlice' +import NavOverlay from '../components/NavOverlay' + +function makeStore() { + return configureStore({ reducer: { auth: authReducer, tabs: tabsReducer } }) +} + +function renderOverlay(onClose = vi.fn()) { + return render( + + + + } /> + UserList} /> + + + + ) +} + +describe('NavOverlay', () => { + it('renders_systemModules', () => { + renderOverlay() + expect(screen.getByText('系统设置')).toBeInTheDocument() + expect(screen.getByText('用户管理')).toBeInTheDocument() + }) + + it('clickUserList_navigatesAndClosesOverlay', async () => { + const onClose = vi.fn() + renderOverlay(onClose) + await userEvent.click(screen.getByText('用户列表')) + expect(screen.getByText('UserList')).toBeInTheDocument() + expect(onClose).toHaveBeenCalledTimes(1) + }) + + it('clickBackground_callsOnClose', async () => { + const onClose = vi.fn() + render( + + + + + + ) + // Click the outer overlay div (background) + await userEvent.click(document.querySelector('[data-testid="nav-bg"]')!) + expect(onClose).toHaveBeenCalledTimes(1) + }) +}) -- libgit2 0.22.2