@chenhui996/gg-cli 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/template/operations-tem/.editorconfig +16 -0
  2. package/dist/template/operations-tem/.env.development +4 -0
  3. package/dist/template/operations-tem/.env.production +4 -0
  4. package/dist/template/operations-tem/.env.test +4 -0
  5. package/dist/template/operations-tem/.prettierignore +34 -0
  6. package/dist/template/operations-tem/.prettierrc +14 -0
  7. package/dist/template/operations-tem/README.md +54 -17
  8. package/dist/template/operations-tem/docs/Git/345/274/200/345/217/221/350/247/204/350/214/203.md +105 -0
  9. package/dist/template/operations-tem/docs/React/345/274/200/345/217/221/350/247/204/350/214/203.md +81 -0
  10. package/dist/template/operations-tem/docs/TypeScript/345/274/200/345/217/221/350/247/204/350/214/203.md +119 -0
  11. package/dist/template/operations-tem/docs//345/211/215/347/253/257/346/227/245/345/277/227/344/270/216/347/233/221/346/216/247/345/237/213/347/202/271/350/247/204/350/214/203.md +136 -0
  12. package/dist/template/operations-tem/docs//345/215/225/345/205/203/346/265/213/350/257/225/350/247/204/350/214/203.md +131 -0
  13. package/dist/template/operations-tem/docs//351/241/271/347/233/256/347/233/256/345/275/225/347/273/223/346/236/204/350/247/204/350/214/203.md +93 -0
  14. package/dist/template/operations-tem/package.json +20 -3
  15. package/dist/template/operations-tem/src/api/user.ts +2 -2
  16. package/dist/template/operations-tem/src/components/ErrorBoundary/index.tsx +82 -0
  17. package/dist/template/operations-tem/src/layouts/BasicLayout.tsx +39 -48
  18. package/dist/template/operations-tem/src/main.tsx +11 -11
  19. package/dist/template/operations-tem/src/pages/404.test.tsx +20 -0
  20. package/dist/template/operations-tem/src/pages/dashboard/index.tsx +1 -1
  21. package/dist/template/operations-tem/src/pages/home/index.tsx +61 -32
  22. package/dist/template/operations-tem/src/router/index.tsx +27 -21
  23. package/dist/template/operations-tem/src/setupTests.ts +1 -0
  24. package/dist/template/operations-tem/src/store/useCounterStore.ts +6 -6
  25. package/dist/template/operations-tem/src/style.less +3 -3
  26. package/dist/template/operations-tem/src/utils/request/index.ts +5 -5
  27. package/dist/template/operations-tem/src/vite-env.d.ts +12 -0
  28. package/dist/template/operations-tem/tsconfig.app.json +7 -1
  29. package/dist/template/operations-tem/vite.config.ts +61 -2
  30. package/dist/template/zhiguan/.editorconfig +16 -0
  31. package/dist/template/zhiguan/.env +1 -0
  32. package/dist/template/zhiguan/.env.development +4 -0
  33. package/dist/template/zhiguan/.env.production +4 -0
  34. package/dist/template/zhiguan/.env.test +4 -0
  35. package/dist/template/zhiguan/.prettierignore +34 -0
  36. package/dist/template/zhiguan/.prettierrc +14 -0
  37. package/dist/template/zhiguan/README.md +183 -0
  38. package/dist/template/zhiguan/docs/Git/345/274/200/345/217/221/350/247/204/350/214/203.md +105 -0
  39. package/dist/template/zhiguan/docs/React/345/274/200/345/217/221/350/247/204/350/214/203.md +81 -0
  40. package/dist/template/zhiguan/docs/TypeScript/345/274/200/345/217/221/350/247/204/350/214/203.md +119 -0
  41. package/dist/template/zhiguan/docs//345/211/215/347/253/257/346/227/245/345/277/227/344/270/216/347/233/221/346/216/247/345/237/213/347/202/271/350/247/204/350/214/203.md +136 -0
  42. package/dist/template/zhiguan/docs//345/215/225/345/205/203/346/265/213/350/257/225/350/247/204/350/214/203.md +131 -0
  43. package/dist/template/zhiguan/docs//351/241/271/347/233/256/347/233/256/345/275/225/347/273/223/346/236/204/350/247/204/350/214/203.md +93 -0
  44. package/dist/template/zhiguan/eslint.config.js +27 -0
  45. package/dist/template/zhiguan/index.html +13 -0
  46. package/dist/template/zhiguan/package.json +60 -0
  47. package/dist/template/zhiguan/public/favicon.svg +1 -0
  48. package/dist/template/zhiguan/public/icons.svg +24 -0
  49. package/dist/template/zhiguan/src/api/user.ts +21 -0
  50. package/dist/template/zhiguan/src/assets/Frame 20.png +0 -0
  51. package/dist/template/zhiguan/src/assets/react.svg +1 -0
  52. package/dist/template/zhiguan/src/components/Chart/index.tsx +22 -0
  53. package/dist/template/zhiguan/src/components/ErrorBoundary/index.tsx +82 -0
  54. package/dist/template/zhiguan/src/layouts/BasicLayout.tsx +174 -0
  55. package/dist/template/zhiguan/src/main.tsx +38 -0
  56. package/dist/template/zhiguan/src/pages/404.test.tsx +20 -0
  57. package/dist/template/zhiguan/src/pages/404.tsx +32 -0
  58. package/dist/template/zhiguan/src/pages/about/index.tsx +8 -0
  59. package/dist/template/zhiguan/src/pages/calendar/index.tsx +8 -0
  60. package/dist/template/zhiguan/src/pages/dashboard/index.tsx +72 -0
  61. package/dist/template/zhiguan/src/pages/home/index.less +59 -0
  62. package/dist/template/zhiguan/src/pages/home/index.tsx +217 -0
  63. package/dist/template/zhiguan/src/pages/settings/index.tsx +8 -0
  64. package/dist/template/zhiguan/src/pages/workspace/index.tsx +8 -0
  65. package/dist/template/zhiguan/src/router/index.tsx +81 -0
  66. package/dist/template/zhiguan/src/setupTests.ts +1 -0
  67. package/dist/template/zhiguan/src/store/useCounterStore.ts +24 -0
  68. package/dist/template/zhiguan/src/style.less +3 -0
  69. package/dist/template/zhiguan/src/utils/request/index.ts +108 -0
  70. package/dist/template/zhiguan/src/vite-env.d.ts +12 -0
  71. package/dist/template/zhiguan/tsconfig.app.json +34 -0
  72. package/dist/template/zhiguan/tsconfig.json +7 -0
  73. package/dist/template/zhiguan/tsconfig.node.json +26 -0
  74. package/dist/template/zhiguan/vite.config.ts +77 -0
  75. package/package.json +1 -1
  76. package/dist/template/operations-tem/package-lock.json +0 -4672
@@ -0,0 +1,131 @@
1
+ # 前端单元测试开发规范
2
+
3
+ 单元测试是保障前端代码质量、支持安全重构的重要手段。本文档规定了团队内前端项目的测试工具选型、目录结构以及测试代码的编写规范。
4
+
5
+ ## 1. 测试工具选型
6
+
7
+ 在基于 Vite 的前端项目中,我们**全面弃用 Jest,转而使用 Vitest 作为核心测试框架**。
8
+
9
+ ### 为什么选择 Vitest?
10
+ 1. **与 Vite 完美融合**:Vitest 直接复用项目的 `vite.config.ts`(包括 alias、插件等),实现了零配置开箱即用,避免了在 Jest 中配置复杂的 Babel/TypeScript 转换器。
11
+ 2. **极速运行**:得益于 Vite 的原生 ESM 支持和底层的 esbuild/oxc,Vitest 的启动和运行速度远超 Jest。
12
+ 3. **API 兼容**:Vitest 提供了与 Jest 几乎 100% 兼容的 API(如 `describe`, `it`, `expect`),现有的 Jest 经验可以无缝迁移。
13
+
14
+ ### 核心测试栈:
15
+ - **测试框架**: [Vitest](https://vitest.dev/) (替代 Jest)
16
+ - **组件测试库**: [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) (用于渲染和交互测试)
17
+ - **环境模拟**: `jsdom` 或 `happy-dom` (模拟浏览器 DOM 环境)
18
+
19
+ ---
20
+
21
+ ## 2. 测试文件命名与目录规范
22
+
23
+ ### 2.1 命名规范
24
+ 测试文件必须以 `.test.ts` 或 `.test.tsx` 结尾(如果包含 JSX 代码必须用 `.tsx`)。
25
+
26
+ ### 2.2 目录存放方式
27
+ 推荐**就近存放**原则。测试文件应与其被测试的代码文件放在同一目录下,方便查找和维护。
28
+
29
+ ```text
30
+ src/
31
+ ├── utils/
32
+ │ ├── math.ts
33
+ │ └── math.test.ts # 工具函数的测试文件
34
+ ├── components/
35
+ │ └── Button/
36
+ │ ├── index.tsx
37
+ │ ├── index.less
38
+ │ └── index.test.tsx # UI 组件的测试文件
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 3. 编写规范
44
+
45
+ ### 3.1 测试结构 (Arrange - Act - Assert)
46
+ 所有的测试用例都必须遵循著名的 **AAA 模式**:
47
+ - **Arrange (准备)**:初始化测试数据、渲染组件、设置 Mock 函数。
48
+ - **Act (执行)**:触发用户交互(点击、输入)或调用函数。
49
+ - **Assert (断言)**:验证期望的结果(如:函数返回值、DOM 元素的变化)。
50
+
51
+ ### 3.2 纯函数测试规范
52
+ 纯函数(如工具类、数据转换逻辑)是最容易且最应该被测试的代码。
53
+
54
+ ```typescript
55
+ // utils/math.test.ts
56
+ import { describe, it, expect } from 'vitest';
57
+ import { add } from './math';
58
+
59
+ describe('math 工具函数', () => {
60
+ it('应当正确计算两个正数的和', () => {
61
+ // Arrange
62
+ const a = 1;
63
+ const b = 2;
64
+
65
+ // Act
66
+ const result = add(a, b);
67
+
68
+ // Assert
69
+ expect(result).toBe(3);
70
+ });
71
+ });
72
+ ```
73
+
74
+ ### 3.3 React 组件测试规范 (React Testing Library)
75
+ 组件测试的原则是:**测试组件的行为,而不是其内部实现**。像真实用户一样去交互和断言,不要去测试组件的内部 state。
76
+
77
+ ```tsx
78
+ // components/Button/index.test.tsx
79
+ import { describe, it, expect, vi } from 'vitest';
80
+ import { render, screen, fireEvent } from '@testing-library/react';
81
+ import { Button } from './index';
82
+
83
+ describe('Button 组件', () => {
84
+ it('正常渲染标签文字', () => {
85
+ // Arrange
86
+ render(<Button label="提交" onClick={() => {}} />);
87
+
88
+ // Act & Assert
89
+ // 像用户一样通过可见文本寻找元素
90
+ expect(screen.getByText('提交')).toBeInTheDocument();
91
+ });
92
+
93
+ it('点击时应触发 onClick 回调', () => {
94
+ // Arrange
95
+ // 使用 vi.fn() 创建一个 Mock 函数记录调用
96
+ const handleClick = vi.fn();
97
+ render(<Button label="提交" onClick={handleClick} />);
98
+
99
+ // Act
100
+ fireEvent.click(screen.getByText('提交'));
101
+
102
+ // Assert
103
+ expect(handleClick).toHaveBeenCalledTimes(1);
104
+ });
105
+ });
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 4. Mock 规范
111
+
112
+ 在前端测试中,经常需要 Mock 网络请求或复杂的第三方依赖。
113
+
114
+ - **Mock 函数**:使用 `vi.fn()` 替代 `jest.fn()`。
115
+ - **Mock 模块**:使用 `vi.mock('module-name')` 替代 `jest.mock()`。
116
+ - **Mock 定时器**:使用 `vi.useFakeTimers()`。
117
+
118
+ ---
119
+
120
+ ## 5. 测试覆盖率 (Coverage) 要求
121
+
122
+ 虽然不强求 100% 的覆盖率(会带来过高的维护成本),但应遵守以下底线:
123
+
124
+ 1. **核心业务逻辑 (utils/store/hooks)**:包含大量计算、格式化、状态流转的纯函数,要求语句覆盖率达到 **80%** 以上。
125
+ 2. **公共 UI 组件 (components)**:要求进行基本的渲染测试和交互测试。
126
+ 3. **页面级组件 (pages)**:不强制要求编写详细的单元测试,推荐使用 E2E 测试工具(如 Cypress/Playwright)进行核心链路覆盖。
127
+
128
+ 运行覆盖率命令:
129
+ ```bash
130
+ npm run test:coverage
131
+ ```
@@ -0,0 +1,93 @@
1
+ # 项目目录结构规范
2
+
3
+ 本文档定义了前端工程的标准目录结构,旨在保持项目架构的清晰一致,降低新成员的理解成本,并为大型应用的扩展奠定基础。
4
+
5
+ ## 1. 根目录结构
6
+
7
+ 项目根目录存放工程化配置、环境变量文件以及源代码目录。
8
+
9
+ ```text
10
+ ├── .env.development # 本地开发环境变量配置
11
+ ├── .env.test # 测试环境变量配置
12
+ ├── .env.production # 生产环境变量配置
13
+ ├── .eslintrc.cjs # ESLint 逻辑检查配置
14
+ ├── .prettierrc # Prettier 代码格式化配置
15
+ ├── .editorconfig # 跨编辑器基础配置规范
16
+ ├── package.json # 项目依赖及 npm scripts
17
+ ├── tsconfig.json # TypeScript 基础配置
18
+ ├── tsconfig.app.json # TypeScript 针对 src 目录的配置
19
+ ├── tsconfig.node.json # TypeScript 针对 vite.config.ts 等 Node 环境的配置
20
+ ├── vite.config.ts # Vite 构建工具配置
21
+ ├── index.html # 应用挂载的 HTML 模板
22
+ └── src/ # 源代码主目录
23
+ ```
24
+
25
+ ## 2. `src/` 源代码主目录结构
26
+
27
+ 所有业务代码、静态资源和配置文件都应放置在 `src` 目录下,并严格按照功能模块进行划分。
28
+
29
+ ```text
30
+ src/
31
+ ├── api/ # API 接口请求定义
32
+ ├── assets/ # 静态资源文件
33
+ ├── components/ # 业务无关的全局公共组件
34
+ ├── hooks/ # 全局自定义 React Hooks
35
+ ├── layouts/ # 页面布局组件
36
+ ├── pages/ # 页面级组件 (路由视图)
37
+ ├── router/ # 路由配置表
38
+ ├── store/ # 全局状态管理 (Zustand)
39
+ ├── types/ # 全局 TypeScript 类型定义
40
+ ├── utils/ # 全局公共工具函数
41
+ ├── App.tsx # 根组件,承载路由等全局 Context
42
+ ├── main.tsx # 应用入口文件,负责渲染 React 树
43
+ ├── index.css # 全局样式文件
44
+ └── vite-env.d.ts # Vite 环境变量类型声明
45
+ ```
46
+
47
+ ---
48
+
49
+ ## 3. 详细目录说明与规范
50
+
51
+ ### 3.1 `api/` (接口定义层)
52
+ - 存放所有的接口请求函数。
53
+ - 按照业务模块或后端微服务划分文件,例如 `user.ts`、`order.ts`。
54
+ - **规范**:接口函数的命名应见名知意(如 `getUserList`, `createOrder`),并且必须定义入参和返回值的 TypeScript 类型。
55
+
56
+ ### 3.2 `assets/` (静态资源)
57
+ - 存放不需要经过 Webpack/Vite 编译的静态资源,如图片 (`images/`)、SVG 图标 (`icons/`)、字体文件等。
58
+ - **规范**:较大的静态资源建议上传至 CDN,小图片或图标可以直接放在此处。
59
+
60
+ ### 3.3 `components/` (公共组件库)
61
+ - 存放**跨页面复用**的、**业务无关**的 UI 组件或业务组件(如封装的通用图表组件 `Chart/`、通用的文件上传组件)。
62
+ - **规范**:每个组件应拥有自己的文件夹,内部包含 `index.tsx` (组件逻辑) 和 `index.less` (组件样式)。
63
+
64
+ ### 3.4 `hooks/` (自定义 Hooks)
65
+ - 存放全局复用的自定义 React Hooks,例如 `useDebounce.ts`、`useWindowSize.ts`。
66
+ - **规范**:文件命名必须以 `use` 开头,采用 **camelCase (小驼峰)**。
67
+
68
+ ### 3.5 `layouts/` (布局组件)
69
+ - 存放页面的整体布局组件,例如包含侧边栏、顶栏和底部版权信息的 `BasicLayout.tsx`,或者登录页专用的 `BlankLayout.tsx`。
70
+ - 布局组件通常被用作路由配置中的父级路由组件 (`<Outlet />`)。
71
+
72
+ ### 3.6 `pages/` (页面级视图)
73
+ - 存放与路由直接对应的页面组件。
74
+ - 按照功能模块划分文件夹,如 `home/`、`dashboard/`、`about/`。
75
+ - **规范**:
76
+ - 如果一个页面较复杂,可以在其文件夹下建立私有的 `components/` 目录存放仅该页面使用的子组件。
77
+ - 入口文件统一命名为 `index.tsx`,配合 `index.less` 使用。
78
+
79
+ ### 3.7 `router/` (路由配置)
80
+ - 存放应用的路由配置表。
81
+ - **规范**:基于 React Router v7 的 Data API 模式配置路由树。按业务模块拆分路由配置,避免单一文件过长。
82
+
83
+ ### 3.8 `store/` (全局状态管理)
84
+ - 存放基于 Zustand 的全局状态。
85
+ - **规范**:按业务领域拆分 Store 文件,如 `useUserStore.ts`、`useAppStore.ts`。不要将本应属于组件内部的局部状态放入全局 Store。
86
+
87
+ ### 3.9 `types/` (全局类型声明)
88
+ - 存放全局公用的 TypeScript 接口 (`interface`) 和类型别名 (`type`)。
89
+ - **规范**:例如定义后端的通用分页返回结构、全局共享的枚举值等。命名约定为 `*.d.ts` 或 `*.ts`。
90
+
91
+ ### 3.10 `utils/` (工具函数)
92
+ - 存放纯 JavaScript/TypeScript 的工具函数,如日期格式化 (`date.ts`)、正则校验 (`regex.ts`)、本地存储封装 (`storage.ts`)。
93
+ - **重点**:`request/` 目录用于存放 Axios 的二次封装,包括拦截器配置、错误统一处理等。
@@ -4,9 +4,16 @@
4
4
  "version": "0.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "vite",
8
- "build": "tsc -b && vite build",
7
+ "dev": "vite --mode development",
8
+ "build:test": "tsc -b && vite build --mode test",
9
+ "build:prod": "tsc -b && vite build --mode production",
10
+ "build": "npm run build:prod",
11
+ "test": "vitest",
12
+ "test:ui": "vitest --ui",
13
+ "test:coverage": "vitest run --coverage",
14
+ "analyze": "cross-env ANALYZE=true npm run build:prod",
9
15
  "lint": "eslint .",
16
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,less,css,json,md}\"",
10
17
  "preview": "vite preview"
11
18
  },
12
19
  "dependencies": {
@@ -25,19 +32,29 @@
25
32
  "@babel/core": "^7.29.0",
26
33
  "@eslint/js": "^9.39.4",
27
34
  "@rolldown/plugin-babel": "^0.2.0",
35
+ "@testing-library/dom": "^10.4.1",
36
+ "@testing-library/jest-dom": "^6.9.1",
37
+ "@testing-library/react": "^16.3.2",
28
38
  "@types/babel__core": "^7.20.5",
29
39
  "@types/node": "^24.12.0",
30
40
  "@types/react": "^19.2.14",
31
41
  "@types/react-dom": "^19.2.3",
32
42
  "@vitejs/plugin-react": "^6.0.0",
43
+ "@vitest/coverage-v8": "^4.1.4",
33
44
  "babel-plugin-react-compiler": "^1.0.0",
45
+ "cross-env": "^10.1.0",
34
46
  "eslint": "^9.39.4",
35
47
  "eslint-plugin-react-hooks": "^7.0.1",
36
48
  "eslint-plugin-react-refresh": "^0.5.2",
37
49
  "globals": "^17.4.0",
50
+ "jsdom": "^29.0.2",
38
51
  "less": "^4.6.4",
52
+ "prettier": "^3.8.1",
53
+ "rollup-plugin-visualizer": "^7.0.1",
54
+ "terser": "^5.46.1",
39
55
  "typescript": "~5.9.3",
40
56
  "typescript-eslint": "^8.56.1",
41
- "vite": "^8.0.0"
57
+ "vite": "^8.0.0",
58
+ "vitest": "^4.1.4"
42
59
  }
43
60
  }
@@ -13,9 +13,9 @@ export const userService = {
13
13
  getUserInfo: (userId: number) => {
14
14
  return request.get<UserInfo>(`/users/${userId}`);
15
15
  },
16
-
16
+
17
17
  // 更新用户信息
18
18
  updateUserInfo: (userId: number, data: Partial<UserInfo>) => {
19
19
  return request.put<UserInfo>(`/users/${userId}`, data);
20
- }
20
+ },
21
21
  };
@@ -0,0 +1,82 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ import { Result, Button } from 'antd';
3
+
4
+ interface Props {
5
+ children?: ReactNode;
6
+ fallback?: ReactNode;
7
+ }
8
+
9
+ interface State {
10
+ hasError: boolean;
11
+ error?: Error;
12
+ }
13
+
14
+ /**
15
+ * 全局错误边界组件 (Error Boundary)
16
+ * 用于捕获 React 组件树中的渲染错误,防止整个应用白屏崩溃
17
+ * 同时可在此处进行错误日志上报
18
+ */
19
+ export class ErrorBoundary extends Component<Props, State> {
20
+ public state: State = {
21
+ hasError: false,
22
+ };
23
+
24
+ public static getDerivedStateFromError(error: Error): State {
25
+ // 更新 state 以致于下一次渲染能够显示降级后的 UI
26
+ return { hasError: true, error };
27
+ }
28
+
29
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
30
+ // 在这里你可以将错误日志上报给服务器
31
+ console.error('Uncaught error in React component tree:', error, errorInfo);
32
+
33
+ // 示例:如果在生产环境,调用监控 SDK
34
+ // if (import.meta.env.PROD) {
35
+ // logService.error('REACT_RENDER_ERROR', { error, errorInfo });
36
+ // }
37
+ }
38
+
39
+ private handleReset = () => {
40
+ // 重置错误状态,尝试恢复组件渲染
41
+ this.setState({ hasError: false, error: undefined });
42
+ // 也可以选择刷新整个页面
43
+ // window.location.reload();
44
+ };
45
+
46
+ public render() {
47
+ if (this.state.hasError) {
48
+ // 渲染自定义的降级 UI,或者使用默认的 500 页面
49
+ if (this.props.fallback) {
50
+ return this.props.fallback;
51
+ }
52
+
53
+ return (
54
+ <Result
55
+ status="500"
56
+ title="抱歉,页面加载出错了"
57
+ subTitle={
58
+ import.meta.env.DEV
59
+ ? this.state.error?.message
60
+ : "我们正在努力修复此问题,请稍后再试。"
61
+ }
62
+ extra={
63
+ <Button type="primary" onClick={this.handleReset}>
64
+ 重试
65
+ </Button>
66
+ }
67
+ style={{
68
+ height: '100%',
69
+ display: 'flex',
70
+ flexDirection: 'column',
71
+ justifyContent: 'center',
72
+ background: '#fff',
73
+ borderRadius: 8,
74
+ margin: 24,
75
+ }}
76
+ />
77
+ );
78
+ }
79
+
80
+ return this.props.children;
81
+ }
82
+ }
@@ -1,13 +1,5 @@
1
- import { Outlet, Link, useLocation, useNavigate } from "react-router-dom";
2
- import {
3
- Layout,
4
- Menu,
5
- Avatar,
6
- Badge,
7
- Space,
8
- Typography,
9
- Button,
10
- } from "antd";
1
+ import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom';
2
+ import { Layout, Menu, Avatar, Badge, Space, Typography, Button } from 'antd';
11
3
  import {
12
4
  MessageOutlined,
13
5
  AppstoreOutlined,
@@ -16,7 +8,7 @@ import {
16
8
  SettingOutlined,
17
9
  BellOutlined,
18
10
  UserOutlined,
19
- } from "@ant-design/icons";
11
+ } from '@ant-design/icons';
20
12
 
21
13
  const { Header, Content, Sider } = Layout;
22
14
  const { Text } = Typography;
@@ -30,30 +22,30 @@ const { Text } = Typography;
30
22
  */
31
23
  const MENU_ITEMS = [
32
24
  {
33
- key: "/",
25
+ key: '/',
34
26
  icon: <MessageOutlined />,
35
- label: "聊天",
27
+ label: '聊天',
36
28
  },
37
29
  {
38
- key: "/dashboard",
30
+ key: '/dashboard',
39
31
  icon: <AppstoreOutlined />,
40
- label: "功能大厅",
32
+ label: '功能大厅',
41
33
  },
42
34
  {
43
- key: "/workspace", // 暂未实现
35
+ key: '/workspace', // 暂未实现
44
36
  icon: <FolderOutlined />,
45
- label: "工作空间",
37
+ label: '工作空间',
46
38
  },
47
39
  {
48
- key: "/calendar", // 暂未实现
40
+ key: '/calendar', // 暂未实现
49
41
  icon: <CalendarOutlined />,
50
- label: "日历",
42
+ label: '日历',
51
43
  },
52
44
  ];
53
45
 
54
46
  /**
55
47
  * 基础布局组件 (BasicLayout)
56
- *
48
+ *
57
49
  * 整体布局结构:
58
50
  * - Sider (左侧侧边栏)
59
51
  * - Logo
@@ -62,7 +54,7 @@ const MENU_ITEMS = [
62
54
  * - Layout (右侧主体)
63
55
  * - Header (顶部导航)
64
56
  * - Content (路由出口 Outlet)
65
- *
57
+ *
66
58
  * @returns {JSX.Element} 布局组件
67
59
  */
68
60
  export default function BasicLayout() {
@@ -75,17 +67,14 @@ export default function BasicLayout() {
75
67
  const renderUserProfile = () => (
76
68
  <div
77
69
  style={{
78
- padding: "16px",
79
- display: "flex",
80
- alignItems: "center",
81
- justifyContent: "space-between",
70
+ padding: '16px',
71
+ display: 'flex',
72
+ alignItems: 'center',
73
+ justifyContent: 'space-between',
82
74
  }}
83
75
  >
84
76
  <Space>
85
- <Avatar
86
- src="https://api.dicebear.com/7.x/miniavs/svg?seed=1"
87
- icon={<UserOutlined />}
88
- />
77
+ <Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" icon={<UserOutlined />} />
89
78
  <Text strong style={{ fontSize: 14 }}>
90
79
  国泰海通-张三
91
80
  </Text>
@@ -95,37 +84,39 @@ export default function BasicLayout() {
95
84
  );
96
85
 
97
86
  return (
98
- <Layout style={{ minHeight: "100vh", position: "relative" }}>
87
+ <Layout style={{ minHeight: '100vh', position: 'relative' }}>
99
88
  {/* 左侧侧边栏 */}
100
89
  <Sider
101
90
  width={240}
102
91
  theme="light"
103
92
  style={{
104
- borderRight: "1px solid #f0f0f0",
93
+ borderRight: '1px solid #f0f0f0',
105
94
  }}
106
95
  >
107
- <div style={{ display: "flex", flexDirection: "column", height: '100%', position: 'relative' }}>
96
+ <div
97
+ style={{ display: 'flex', flexDirection: 'column', height: '100%', position: 'relative' }}
98
+ >
108
99
  {/* Logo 区域 */}
109
100
  <div
110
101
  style={{
111
102
  height: 64,
112
- display: "flex",
113
- alignItems: "center",
103
+ display: 'flex',
104
+ alignItems: 'center',
114
105
  paddingLeft: 24,
115
106
  fontSize: 20,
116
- fontWeight: "bold",
117
- color: "#333",
107
+ fontWeight: 'bold',
108
+ color: '#333',
118
109
  }}
119
110
  >
120
111
  PILOTCORE
121
112
  </div>
122
113
 
123
114
  {/* 菜单区域 - flex: 1 撑满剩余空间 */}
124
- <div style={{ flex: 1, overflowY: "auto" }}>
115
+ <div style={{ flex: 1, overflowY: 'auto' }}>
125
116
  <Menu
126
117
  mode="inline"
127
118
  selectedKeys={[location.pathname]}
128
- style={{ borderRight: 0, paddingBottom: 16, borderBottom: "1px solid #f0f0f0" }}
119
+ style={{ borderRight: 0, paddingBottom: 16, borderBottom: '1px solid #f0f0f0' }}
129
120
  items={MENU_ITEMS.map((item) => ({
130
121
  ...item,
131
122
  label: <Link to={item.key}>{item.label}</Link>,
@@ -134,7 +125,7 @@ export default function BasicLayout() {
134
125
  </div>
135
126
 
136
127
  {/* 底部用户信息 */}
137
- <div style={{ position: "absolute", bottom: 0, width: '100%' }}>
128
+ <div style={{ position: 'absolute', bottom: 0, width: '100%' }}>
138
129
  {renderUserProfile()}
139
130
  </div>
140
131
  </div>
@@ -145,11 +136,11 @@ export default function BasicLayout() {
145
136
  {/* 顶部导航栏 */}
146
137
  <Header
147
138
  style={{
148
- padding: "0 24px",
149
- display: "flex",
150
- justifyContent: "flex-end",
151
- alignItems: "center",
152
- backgroundColor: "transparent", // 透明背景融入整体
139
+ padding: '0 24px',
140
+ display: 'flex',
141
+ justifyContent: 'flex-end',
142
+ alignItems: 'center',
143
+ backgroundColor: 'transparent', // 透明背景融入整体
153
144
  }}
154
145
  >
155
146
  <Space size={24}>
@@ -159,9 +150,9 @@ export default function BasicLayout() {
159
150
  icon={<BellOutlined />}
160
151
  shape="circle"
161
152
  style={{
162
- border: "none",
163
- background: "#fff",
164
- boxShadow: "0 2px 8px rgba(0,0,0,0.05)",
153
+ border: 'none',
154
+ background: '#fff',
155
+ boxShadow: '0 2px 8px rgba(0,0,0,0.05)',
165
156
  }}
166
157
  />
167
158
  </Badge>
@@ -171,7 +162,7 @@ export default function BasicLayout() {
171
162
  {/* 内容区域 */}
172
163
  <Content
173
164
  style={{
174
- margin: "0 24px 24px",
165
+ margin: '0 24px 24px',
175
166
  minHeight: 280,
176
167
  }}
177
168
  >
@@ -1,14 +1,14 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import { RouterProvider } from 'react-router-dom'
4
- import { ConfigProvider } from 'antd'
5
- import zhCN from 'antd/locale/zh_CN'
6
- import { router } from './router'
7
- import 'antd/dist/reset.css'
8
- import './style.less'
1
+ import { createRoot } from 'react-dom/client';
2
+ import { RouterProvider } from 'react-router-dom';
3
+ import { ConfigProvider } from 'antd';
4
+ import zhCN from 'antd/locale/zh_CN';
5
+ import { router } from '@/router';
6
+ import { ErrorBoundary } from '@/components/ErrorBoundary';
7
+ import 'antd/dist/reset.css';
8
+ import './style.less';
9
9
 
10
10
  createRoot(document.getElementById('root')!).render(
11
- <StrictMode>
11
+ <ErrorBoundary>
12
12
  <ConfigProvider
13
13
  locale={zhCN}
14
14
  theme={{
@@ -34,5 +34,5 @@ createRoot(document.getElementById('root')!).render(
34
34
  >
35
35
  <RouterProvider router={router} />
36
36
  </ConfigProvider>
37
- </StrictMode>,
38
- )
37
+ </ErrorBoundary>,
38
+ );
@@ -0,0 +1,20 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import NotFound from './404';
4
+ import { BrowserRouter } from 'react-router-dom';
5
+
6
+ describe('NotFound (404) 页面组件', () => {
7
+ it('应当正确渲染 404 状态码和提示信息', () => {
8
+ // Arrange: 渲染组件 (需要包裹在 Router 中因为内部使用了 useNavigate)
9
+ render(
10
+ <BrowserRouter>
11
+ <NotFound />
12
+ </BrowserRouter>
13
+ );
14
+
15
+ // Assert: 验证页面中是否出现了 404 和相关提示文字
16
+ expect(screen.getByText('404')).toBeInTheDocument();
17
+ expect(screen.getByText('抱歉,您访问的页面不存在。')).toBeInTheDocument();
18
+ expect(screen.getByRole('button', { name: '返回首页' })).toBeInTheDocument();
19
+ });
20
+ });
@@ -1,5 +1,5 @@
1
1
  import { Card, Row, Col } from 'antd';
2
- import Chart from '../../components/Chart';
2
+ import Chart from '@/components/Chart';
3
3
  import type { EChartsOption } from 'echarts';
4
4
 
5
5
  export default function Dashboard() {