@chenhui996/gg-cli 1.0.2 → 1.0.4
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.
- package/dist/template/operations-tem/.editorconfig +16 -0
- package/dist/template/operations-tem/.env.development +4 -0
- package/dist/template/operations-tem/.env.production +4 -0
- package/dist/template/operations-tem/.env.test +4 -0
- package/dist/template/operations-tem/.prettierignore +34 -0
- package/dist/template/operations-tem/.prettierrc +14 -0
- package/dist/template/operations-tem/README.md +54 -17
- package/dist/template/operations-tem/docs/Git/345/274/200/345/217/221/350/247/204/350/214/203.md +105 -0
- package/dist/template/operations-tem/docs/React/345/274/200/345/217/221/350/247/204/350/214/203.md +81 -0
- package/dist/template/operations-tem/docs/TypeScript/345/274/200/345/217/221/350/247/204/350/214/203.md +119 -0
- 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
- 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
- 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
- package/dist/template/operations-tem/package-lock.json +2035 -294
- package/dist/template/operations-tem/package.json +20 -3
- package/dist/template/operations-tem/src/api/user.ts +2 -2
- package/dist/template/operations-tem/src/components/ErrorBoundary/index.tsx +82 -0
- package/dist/template/operations-tem/src/layouts/BasicLayout.tsx +39 -48
- package/dist/template/operations-tem/src/main.tsx +11 -11
- package/dist/template/operations-tem/src/pages/404.test.tsx +20 -0
- package/dist/template/operations-tem/src/pages/dashboard/index.tsx +1 -1
- package/dist/template/operations-tem/src/pages/home/index.tsx +61 -32
- package/dist/template/operations-tem/src/router/index.tsx +27 -21
- package/dist/template/operations-tem/src/setupTests.ts +1 -0
- package/dist/template/operations-tem/src/store/useCounterStore.ts +6 -6
- package/dist/template/operations-tem/src/style.less +3 -3
- package/dist/template/operations-tem/src/utils/request/index.ts +5 -5
- package/dist/template/operations-tem/src/vite-env.d.ts +12 -0
- package/dist/template/operations-tem/tsconfig.app.json +7 -1
- package/dist/template/operations-tem/vite.config.ts +61 -2
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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:
|
|
30
|
+
key: '/dashboard',
|
|
39
31
|
icon: <AppstoreOutlined />,
|
|
40
|
-
label:
|
|
32
|
+
label: '功能大厅',
|
|
41
33
|
},
|
|
42
34
|
{
|
|
43
|
-
key:
|
|
35
|
+
key: '/workspace', // 暂未实现
|
|
44
36
|
icon: <FolderOutlined />,
|
|
45
|
-
label:
|
|
37
|
+
label: '工作空间',
|
|
46
38
|
},
|
|
47
39
|
{
|
|
48
|
-
key:
|
|
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:
|
|
79
|
-
display:
|
|
80
|
-
alignItems:
|
|
81
|
-
justifyContent:
|
|
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:
|
|
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:
|
|
93
|
+
borderRight: '1px solid #f0f0f0',
|
|
105
94
|
}}
|
|
106
95
|
>
|
|
107
|
-
<div
|
|
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:
|
|
113
|
-
alignItems:
|
|
103
|
+
display: 'flex',
|
|
104
|
+
alignItems: 'center',
|
|
114
105
|
paddingLeft: 24,
|
|
115
106
|
fontSize: 20,
|
|
116
|
-
fontWeight:
|
|
117
|
-
color:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
149
|
-
display:
|
|
150
|
-
justifyContent:
|
|
151
|
-
alignItems:
|
|
152
|
-
backgroundColor:
|
|
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:
|
|
163
|
-
background:
|
|
164
|
-
boxShadow:
|
|
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:
|
|
165
|
+
margin: '0 24px 24px',
|
|
175
166
|
minHeight: 280,
|
|
176
167
|
}}
|
|
177
168
|
>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
+
});
|
|
@@ -21,9 +21,7 @@ const { Title, Text } = Typography;
|
|
|
21
21
|
*/
|
|
22
22
|
const WelcomeHeader = () => (
|
|
23
23
|
<div className="welcome-header">
|
|
24
|
-
<Title level={2}>
|
|
25
|
-
下午好,你有3件待办任务...
|
|
26
|
-
</Title>
|
|
24
|
+
<Title level={2}>下午好,你有3件待办任务...</Title>
|
|
27
25
|
</div>
|
|
28
26
|
);
|
|
29
27
|
|
|
@@ -42,25 +40,14 @@ const SearchSection = () => (
|
|
|
42
40
|
placeholder="输入您想查询的数据、指令或功能,例如:帮我分析一下NVDA的最新财报..."
|
|
43
41
|
className="search-input"
|
|
44
42
|
/>
|
|
45
|
-
<Button
|
|
46
|
-
type="primary"
|
|
47
|
-
shape="circle"
|
|
48
|
-
icon={<ArrowUpOutlined />}
|
|
49
|
-
className="search-btn"
|
|
50
|
-
/>
|
|
43
|
+
<Button type="primary" shape="circle" icon={<ArrowUpOutlined />} className="search-btn" />
|
|
51
44
|
</div>
|
|
52
|
-
|
|
45
|
+
|
|
53
46
|
{/* 快捷标签 */}
|
|
54
47
|
<div className="search-tags">
|
|
55
|
-
<Tag className="tag-item">
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<Tag className="tag-item">
|
|
59
|
-
查询头寸
|
|
60
|
-
</Tag>
|
|
61
|
-
<Tag className="tag-item">
|
|
62
|
-
压力测试
|
|
63
|
-
</Tag>
|
|
48
|
+
<Tag className="tag-item">分析财报</Tag>
|
|
49
|
+
<Tag className="tag-item">查询头寸</Tag>
|
|
50
|
+
<Tag className="tag-item">压力测试</Tag>
|
|
64
51
|
</div>
|
|
65
52
|
</div>
|
|
66
53
|
);
|
|
@@ -73,19 +60,52 @@ const SearchSection = () => (
|
|
|
73
60
|
const QuickActions = () => {
|
|
74
61
|
// 功能入口配置数据
|
|
75
62
|
const actions = [
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{
|
|
63
|
+
{
|
|
64
|
+
title: '工作空间',
|
|
65
|
+
icon: <AppstoreOutlined style={{ fontSize: 24, color: '#4F46E5' }} />,
|
|
66
|
+
color: '#EEF2FF',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
title: '规则配置',
|
|
70
|
+
icon: <SettingOutlined style={{ fontSize: 24, color: '#0EA5E9' }} />,
|
|
71
|
+
color: '#F0F9FF',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
title: '事后风控',
|
|
75
|
+
icon: <SafetyCertificateOutlined style={{ fontSize: 24, color: '#F59E0B' }} />,
|
|
76
|
+
color: '#FFFBEB',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: '交易审批',
|
|
80
|
+
icon: <AuditOutlined style={{ fontSize: 24, color: '#10B981' }} />,
|
|
81
|
+
color: '#ECFDF5',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: '证券池管理',
|
|
85
|
+
icon: <WalletOutlined style={{ fontSize: 24, color: '#3B82F6' }} />,
|
|
86
|
+
color: '#EFF6FF',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
title: '数据审批',
|
|
90
|
+
icon: <DatabaseOutlined style={{ fontSize: 24, color: '#06B6D4' }} />,
|
|
91
|
+
color: '#ECFEFF',
|
|
92
|
+
},
|
|
82
93
|
];
|
|
83
94
|
|
|
84
95
|
return (
|
|
85
96
|
<div style={{ maxWidth: 1000, margin: '0 auto 48px' }}>
|
|
86
|
-
<div
|
|
97
|
+
<div
|
|
98
|
+
style={{
|
|
99
|
+
display: 'flex',
|
|
100
|
+
justifyContent: 'space-between',
|
|
101
|
+
marginBottom: 16,
|
|
102
|
+
padding: '0 8px',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
87
105
|
<Text type="secondary">常用功能</Text>
|
|
88
|
-
<Button type="link" size="small" style={{ color: '#999' }}
|
|
106
|
+
<Button type="link" size="small" style={{ color: '#999' }}>
|
|
107
|
+
更多
|
|
108
|
+
</Button>
|
|
89
109
|
</div>
|
|
90
110
|
<Row gutter={[16, 16]}>
|
|
91
111
|
{actions.map((action) => (
|
|
@@ -139,7 +159,9 @@ const TaskList = () => {
|
|
|
139
159
|
|
|
140
160
|
return (
|
|
141
161
|
<div style={{ maxWidth: 1000, margin: '0 auto' }}>
|
|
142
|
-
<Text type="secondary" style={{ marginBottom: 16, display: 'block', paddingLeft: 8 }}
|
|
162
|
+
<Text type="secondary" style={{ marginBottom: 16, display: 'block', paddingLeft: 8 }}>
|
|
163
|
+
待办任务
|
|
164
|
+
</Text>
|
|
143
165
|
<List
|
|
144
166
|
dataSource={tasks}
|
|
145
167
|
renderItem={(item) => (
|
|
@@ -155,17 +177,24 @@ const TaskList = () => {
|
|
|
155
177
|
}}
|
|
156
178
|
>
|
|
157
179
|
<div style={{ display: 'flex', alignItems: 'center', flex: 1 }}>
|
|
158
|
-
<Text type="secondary" style={{ width: 60, marginRight: 16 }}>
|
|
180
|
+
<Text type="secondary" style={{ width: 60, marginRight: 16 }}>
|
|
181
|
+
{item.time}
|
|
182
|
+
</Text>
|
|
159
183
|
<Text style={{ fontSize: 15, fontWeight: 500 }}>{item.content}</Text>
|
|
160
184
|
</div>
|
|
161
|
-
|
|
185
|
+
|
|
162
186
|
<Space size={16}>
|
|
163
187
|
{item.tag && (
|
|
164
|
-
<Tag
|
|
188
|
+
<Tag
|
|
189
|
+
color={item.tagColor}
|
|
190
|
+
style={{ borderRadius: 4, border: 'none', marginRight: 0 }}
|
|
191
|
+
>
|
|
165
192
|
{item.tag}
|
|
166
193
|
</Tag>
|
|
167
194
|
)}
|
|
168
|
-
<Button type="link" size="small"
|
|
195
|
+
<Button type="link" size="small">
|
|
196
|
+
处理
|
|
197
|
+
</Button>
|
|
169
198
|
</Space>
|
|
170
199
|
</List.Item>
|
|
171
200
|
)}
|
|
@@ -2,23 +2,31 @@
|
|
|
2
2
|
* 路由配置 (React Router Data API)
|
|
3
3
|
* 采用对象配置模式,便于管理和扩展
|
|
4
4
|
*/
|
|
5
|
-
import { lazy, Suspense, type ComponentType, type LazyExoticComponent } from
|
|
6
|
-
import { createBrowserRouter } from
|
|
7
|
-
import { Spin } from
|
|
8
|
-
import BasicLayout from
|
|
5
|
+
import { lazy, Suspense, type ComponentType, type LazyExoticComponent } from 'react';
|
|
6
|
+
import { createBrowserRouter } from 'react-router-dom';
|
|
7
|
+
import { Spin } from 'antd';
|
|
8
|
+
import BasicLayout from '@/layouts/BasicLayout';
|
|
9
9
|
|
|
10
10
|
// 路由懒加载配置
|
|
11
|
-
const Home = lazy(() => import(
|
|
12
|
-
const About = lazy(() => import(
|
|
13
|
-
const Dashboard = lazy(() => import(
|
|
14
|
-
const Workspace = lazy(() => import(
|
|
15
|
-
const Calendar = lazy(() => import(
|
|
16
|
-
const Settings = lazy(() => import(
|
|
17
|
-
const NotFound = lazy(() => import(
|
|
11
|
+
const Home = lazy(() => import('@/pages/home'));
|
|
12
|
+
const About = lazy(() => import('@/pages/about'));
|
|
13
|
+
const Dashboard = lazy(() => import('@/pages/dashboard'));
|
|
14
|
+
const Workspace = lazy(() => import('@/pages/workspace'));
|
|
15
|
+
const Calendar = lazy(() => import('@/pages/calendar'));
|
|
16
|
+
const Settings = lazy(() => import('@/pages/settings'));
|
|
17
|
+
const NotFound = lazy(() => import('@/pages/404'));
|
|
18
18
|
|
|
19
19
|
// 全局 Loading 组件
|
|
20
20
|
const Loading = () => (
|
|
21
|
-
<div
|
|
21
|
+
<div
|
|
22
|
+
style={{
|
|
23
|
+
display: 'flex',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
height: '100%',
|
|
27
|
+
minHeight: 400,
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
22
30
|
<Spin size="large" />
|
|
23
31
|
</div>
|
|
24
32
|
);
|
|
@@ -32,11 +40,9 @@ function withSuspense(Component: LazyExoticComponent<ComponentType<any>>) {
|
|
|
32
40
|
);
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
43
|
export const router = createBrowserRouter([
|
|
38
44
|
{
|
|
39
|
-
path:
|
|
45
|
+
path: '/',
|
|
40
46
|
// 根布局:BasicLayout (包含侧边栏和 Header)
|
|
41
47
|
element: <BasicLayout />,
|
|
42
48
|
// 子路由:渲染在 BasicLayout 的 <Outlet /> 中
|
|
@@ -46,28 +52,28 @@ export const router = createBrowserRouter([
|
|
|
46
52
|
element: withSuspense(Home),
|
|
47
53
|
},
|
|
48
54
|
{
|
|
49
|
-
path:
|
|
55
|
+
path: 'dashboard',
|
|
50
56
|
element: withSuspense(Dashboard),
|
|
51
57
|
},
|
|
52
58
|
{
|
|
53
|
-
path:
|
|
59
|
+
path: 'about',
|
|
54
60
|
element: withSuspense(About),
|
|
55
61
|
},
|
|
56
62
|
{
|
|
57
|
-
path:
|
|
63
|
+
path: 'workspace',
|
|
58
64
|
element: withSuspense(Workspace),
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
|
-
path:
|
|
67
|
+
path: 'calendar',
|
|
62
68
|
element: withSuspense(Calendar),
|
|
63
69
|
},
|
|
64
70
|
{
|
|
65
|
-
path:
|
|
71
|
+
path: 'settings',
|
|
66
72
|
element: withSuspense(Settings),
|
|
67
73
|
},
|
|
68
74
|
// 404 页面配置
|
|
69
75
|
{
|
|
70
|
-
path:
|
|
76
|
+
path: '*',
|
|
71
77
|
element: withSuspense(NotFound),
|
|
72
78
|
},
|
|
73
79
|
],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { create } from 'zustand'
|
|
1
|
+
import { create } from 'zustand';
|
|
2
2
|
|
|
3
3
|
// 1. 定义状态接口
|
|
4
4
|
interface CounterState {
|
|
5
|
-
count: number
|
|
6
|
-
increment: () => void
|
|
7
|
-
decrement: () => void
|
|
5
|
+
count: number;
|
|
6
|
+
increment: () => void;
|
|
7
|
+
decrement: () => void;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 全局计数器状态 (Zustand Store)
|
|
12
12
|
* 这是一个简单的状态管理示例,演示如何使用 Zustand
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* @example
|
|
15
15
|
* // 在组件中使用
|
|
16
16
|
* const { count, increment } = useCounterStore()
|
|
@@ -21,4 +21,4 @@ export const useCounterStore = create<CounterState>()((set) => ({
|
|
|
21
21
|
// 修改状态的方法 (Action)
|
|
22
22
|
increment: () => set((state) => ({ count: state.count + 1 })),
|
|
23
23
|
decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
24
|
-
}))
|
|
24
|
+
}));
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
*{
|
|
2
|
-
|
|
3
|
-
}
|
|
1
|
+
* {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
}
|