nsgm-cli 2.1.14 → 2.1.16

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 (69) hide show
  1. package/README.md +49 -26
  2. package/client/components/ClientProviders.tsx +29 -0
  3. package/client/components/LanguageSwitcher.tsx +59 -0
  4. package/client/components/SSRSafeAntdProvider.tsx +24 -0
  5. package/client/components/SuppressHydrationWarnings.tsx +55 -0
  6. package/client/layout/index.tsx +82 -126
  7. package/client/styled/common.ts +0 -1
  8. package/client/styled/layout/index.ts +218 -104
  9. package/client/styled/template/manage.ts +88 -1
  10. package/client/utils/i18n.ts +68 -0
  11. package/client/utils/menu.tsx +42 -36
  12. package/client/utils/navigation.ts +58 -0
  13. package/client/utils/suppressWarnings.ts +32 -0
  14. package/eslint.config.js +17 -20
  15. package/generation/client/redux/reducers.ts +1 -1
  16. package/generation/client/utils/menu.tsx +36 -30
  17. package/generation/env +3 -0
  18. package/generation/next.config.js +7 -3
  19. package/generation/package.json +5 -2
  20. package/generation/tsconfig.json +6 -19
  21. package/lib/cli/commands/create.js +1 -1
  22. package/lib/cli/commands/delete.js +1 -1
  23. package/lib/cli/commands/init.js +6 -6
  24. package/lib/cli/utils/prompt.d.ts +1 -1
  25. package/lib/cli/utils/prompt.js +76 -117
  26. package/lib/constants.d.ts +6 -1
  27. package/lib/constants.js +12 -2
  28. package/lib/generate.js +1 -0
  29. package/lib/generate_create.js +14 -11
  30. package/lib/generate_delete.js +86 -9
  31. package/lib/generate_init.d.ts +6 -0
  32. package/lib/generate_init.js +125 -5
  33. package/lib/generators/file-generator.d.ts +48 -0
  34. package/lib/generators/file-generator.js +455 -0
  35. package/lib/generators/i18n-generator.d.ts +51 -0
  36. package/lib/generators/i18n-generator.js +320 -0
  37. package/lib/generators/page-generator.d.ts +6 -2
  38. package/lib/generators/page-generator.js +182 -156
  39. package/lib/generators/resolver-generator.d.ts +6 -4
  40. package/lib/generators/resolver-generator.js +114 -75
  41. package/lib/generators/service-generator.d.ts +4 -0
  42. package/lib/generators/service-generator.js +120 -6
  43. package/lib/tsconfig.build.tsbuildinfo +1 -1
  44. package/next-i18next.config.js +18 -0
  45. package/next.config.js +55 -16
  46. package/package.json +7 -2
  47. package/pages/_app.tsx +84 -35
  48. package/pages/_document.tsx +39 -2
  49. package/pages/_error.tsx +66 -0
  50. package/pages/index.tsx +46 -29
  51. package/pages/login.tsx +58 -33
  52. package/pages/template/manage.tsx +95 -109
  53. package/public/locales/en-US/common.json +48 -0
  54. package/public/locales/en-US/home.json +57 -0
  55. package/public/locales/en-US/layout.json +22 -0
  56. package/public/locales/en-US/login.json +13 -0
  57. package/public/locales/en-US/template.json +42 -0
  58. package/public/locales/ja-JP/common.json +48 -0
  59. package/public/locales/ja-JP/home.json +57 -0
  60. package/public/locales/ja-JP/layout.json +22 -0
  61. package/public/locales/ja-JP/login.json +13 -0
  62. package/public/locales/ja-JP/template.json +42 -0
  63. package/public/locales/zh-CN/common.json +48 -0
  64. package/public/locales/zh-CN/home.json +57 -0
  65. package/public/locales/zh-CN/layout.json +22 -0
  66. package/public/locales/zh-CN/login.json +13 -0
  67. package/public/locales/zh-CN/template.json +42 -0
  68. package/server/utils/validation.js +163 -0
  69. package/types/i18next.d.ts +10 -0
package/README.md CHANGED
@@ -31,22 +31,26 @@ NSGM CLI is a comprehensive full-stack development framework that combines the p
31
31
  ## 🎯 Key Features
32
32
 
33
33
  ### 🧙‍♂️ **Intelligent CLI Wizard**
34
+
34
35
  - **Smart Mode Detection**: Automatically switches between interactive and command-line modes
35
36
  - **Beginner-Friendly**: Step-by-step guided setup for newcomers
36
37
  - **Expert-Efficient**: Quick command-line shortcuts for experienced developers
37
38
 
38
39
  ### ⚡ **Rapid Development**
40
+
39
41
  - **Code Generation**: Automatic CRUD operations, API endpoints, and database schemas
40
42
  - **Hot Reload**: Instant development feedback
41
43
  - **Type Safety**: Full TypeScript support throughout the stack
42
44
 
43
45
  ### 🔒 **Production-Ready Security**
46
+
44
47
  - **CSRF Protection**: Built-in cross-site request forgery prevention
45
48
  - **Password Encryption**: bcrypt-based secure authentication
46
49
  - **Session Management**: Robust user session handling
47
50
  - **CSP Headers**: Content Security Policy implementation
48
51
 
49
52
  ### 🏗️ **Modern Tech Stack**
53
+
50
54
  - **Frontend**: Next.js 13+, React 18+, Styled-components, Redux Toolkit
51
55
  - **Backend**: Express.js, GraphQL, REST APIs
52
56
  - **Database**: MySQL with native drivers
@@ -87,6 +91,7 @@ nsgm init my-awesome-app
87
91
  ```
88
92
 
89
93
  The wizard will guide you through:
94
+
90
95
  - ✅ Project name and directory
91
96
  - ✅ Database configuration
92
97
  - ✅ Security settings
@@ -99,9 +104,15 @@ cd your-project-name
99
104
 
100
105
  # Copy environment template
101
106
  cp .env.example .env
107
+ ```
102
108
 
109
+ **Default Login**: `admin/admin123`
110
+
111
+ **To change password** (optional):
112
+
113
+ ```bash
103
114
  # Generate secure password hash
104
- npm run generate-password your-secure-password
115
+ npm run generate-password yourNewPassword
105
116
 
106
117
  # Edit .env file with generated hash
107
118
  nano .env
@@ -118,23 +129,24 @@ npm run dev
118
129
  ```
119
130
 
120
131
  Your application will be available at `http://localhost:3000` with:
132
+
121
133
  - 🎛️ Admin dashboard with CRUD interface
122
134
  - 📊 Data import/export functionality
123
135
  - 🗑️ Batch operations support
124
- - 🔐 Secure login system
136
+ - 🔐 Secure login system (Default: admin/admin123)
125
137
 
126
138
  ## 🛠️ CLI Commands
127
139
 
128
140
  ### Core Commands
129
141
 
130
- | Command | Description | Mode | Example |
131
- |---------|-------------|------|---------|
132
- | `nsgm init` | Initialize new project | Interactive/CLI | `nsgm init blog-app` |
133
- | `nsgm create` | Generate controller with CRUD | Interactive/CLI | `nsgm create user` |
134
- | `nsgm delete` | Remove controller and files | Interactive/CLI | `nsgm delete product` |
135
- | `nsgm dev` | Start development server | CLI | `nsgm dev` |
136
- | `nsgm build` | Build for production | CLI | `nsgm build` |
137
- | `nsgm start` | Start production server | CLI | `nsgm start` |
142
+ | Command | Description | Mode | Example |
143
+ | ------------- | ----------------------------- | --------------- | --------------------- |
144
+ | `nsgm init` | Initialize new project | Interactive/CLI | `nsgm init blog-app` |
145
+ | `nsgm create` | Generate controller with CRUD | Interactive/CLI | `nsgm create user` |
146
+ | `nsgm delete` | Remove controller and files | Interactive/CLI | `nsgm delete product` |
147
+ | `nsgm dev` | Start development server | CLI | `nsgm dev` |
148
+ | `nsgm build` | Build for production | CLI | `nsgm build` |
149
+ | `nsgm start` | Start production server | CLI | `nsgm start` |
138
150
 
139
151
  ### Advanced Commands
140
152
 
@@ -157,6 +169,7 @@ npm run test:coverage # Test coverage report
157
169
  Each controller created with `nsgm create` includes:
158
170
 
159
171
  ### 🔧 **Backend Components**
172
+
160
173
  - **GraphQL Schema**: Typed queries and mutations
161
174
  - **GraphQL Resolvers**: Business logic implementation
162
175
  - **REST API Endpoints**: RESTful service layer
@@ -164,6 +177,7 @@ Each controller created with `nsgm create` includes:
164
177
  - **Data Validation**: Input sanitization and validation
165
178
 
166
179
  ### 🎯 **Frontend Components**
180
+
167
181
  - **React Components**: Modern functional components with hooks
168
182
  - **Styled Components**: CSS-in-JS styling
169
183
  - **Redux Integration**: State management
@@ -171,6 +185,7 @@ Each controller created with `nsgm create` includes:
171
185
  - **Data Tables**: Sortable, filterable data grids
172
186
 
173
187
  ### 📊 **CRUD Operations**
188
+
174
189
  - **Create**: Add new records with validation
175
190
  - **Read**: List, search, and pagination
176
191
  - **Update**: Edit existing records
@@ -211,7 +226,7 @@ your-project/
211
226
  # .env file
212
227
  NODE_ENV=development
213
228
  LOGIN_USERNAME=admin
214
- LOGIN_PASSWORD_HASH=your_generated_hash
229
+ LOGIN_PASSWORD_HASH=your_generated_hash # Default: admin123
215
230
  DATABASE_URL=mysql://user:password@localhost:3306/dbname
216
231
 
217
232
  # Optional
@@ -229,18 +244,22 @@ module.exports = {
229
244
  port: process.env.DB_PORT || 3306,
230
245
  user: process.env.DB_USER || 'root',
231
246
  password: process.env.DB_PASSWORD || '',
232
- database: process.env.DB_NAME || 'nsgm_db'
233
- }
247
+ database: process.env.DB_NAME || 'nsgm_db',
248
+ },
234
249
  }
235
250
  ```
236
251
 
237
252
  ## 🔒 Security Setup
238
253
 
239
- ### Password Generation
254
+ ### Default Authentication
255
+
256
+ **Default Login Credentials**: `admin/admin123`
257
+
258
+ ### Custom Password Setup (Optional)
240
259
 
241
260
  ```bash
242
- # Generate secure hash
243
- npm run generate-password your-secure-password
261
+ # Generate secure hash for custom password
262
+ npm run generate-password yourNewPassword
244
263
 
245
264
  # Add to .env file
246
265
  LOGIN_PASSWORD_HASH=your_generated_hash_here
@@ -255,13 +274,15 @@ NSGM CLI includes built-in CSRF protection:
255
274
  app.use(csrfProtection)
256
275
 
257
276
  // Custom CSP headers
258
- app.use(createCSPMiddleware({
259
- directives: {
260
- defaultSrc: ["'self'"],
261
- styleSrc: ["'self'", "'unsafe-inline'"],
262
- scriptSrc: ["'self'"]
263
- }
264
- }))
277
+ app.use(
278
+ createCSPMiddleware({
279
+ directives: {
280
+ defaultSrc: ["'self'"],
281
+ styleSrc: ["'self'", "'unsafe-inline'"],
282
+ scriptSrc: ["'self'"],
283
+ },
284
+ })
285
+ )
265
286
  ```
266
287
 
267
288
  ## 🧪 Testing
@@ -312,7 +333,7 @@ router.get('/stats', (req, res) => {
312
333
  res.json({
313
334
  totalPosts: 42,
314
335
  totalUsers: 15,
315
- lastUpdate: new Date()
336
+ lastUpdate: new Date(),
316
337
  })
317
338
  })
318
339
 
@@ -341,7 +362,7 @@ module.exports = {
341
362
  createdAt: Date!
342
363
  updatedAt: Date!
343
364
  }
344
- `
365
+ `,
345
366
  }
346
367
  ```
347
368
 
@@ -403,6 +424,7 @@ npm run tsbuild
403
424
  ### Common Issues
404
425
 
405
426
  **Port already in use**
427
+
406
428
  ```bash
407
429
  # Kill process on port 3000
408
430
  lsof -ti:3000 | xargs kill -9
@@ -412,6 +434,7 @@ PORT=3001 npm run dev
412
434
  ```
413
435
 
414
436
  **Database connection failed**
437
+
415
438
  ```bash
416
439
  # Check MySQL service
417
440
  sudo systemctl status mysql
@@ -421,6 +444,7 @@ cat .env | grep DB_
421
444
  ```
422
445
 
423
446
  **Permission denied**
447
+
424
448
  ```bash
425
449
  # Fix npm permissions
426
450
  sudo chown -R $(whoami) ~/.npm
@@ -449,4 +473,3 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
449
473
  [GitHub](https://github.com/erishen/nsgm) • [npm](https://www.npmjs.com/package/nsgm-cli) • [Issues](https://github.com/erishen/nsgm/issues)
450
474
 
451
475
  </div>
452
-
@@ -0,0 +1,29 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { ThemeProvider } from 'styled-components'
3
+ import { GlobalStyle } from '@/styled/common'
4
+
5
+ interface ClientProvidersProps {
6
+ children: React.ReactNode
7
+ theme: any
8
+ whiteColor?: boolean
9
+ }
10
+
11
+ const ClientProviders: React.FC<ClientProvidersProps> = ({ children, theme, whiteColor = true }) => {
12
+ const [isClient, setIsClient] = useState(false)
13
+
14
+ useEffect(() => {
15
+ // 使用更安全的客户端检测
16
+ setIsClient(true)
17
+ }, [])
18
+
19
+ // 在服务端渲染时,使用一个占位符来保持结构一致性
20
+ // 但不渲染可能引起 useLayoutEffect 警告的组件
21
+ return (
22
+ <ThemeProvider theme={theme}>
23
+ {isClient && <GlobalStyle whiteColor={whiteColor} />}
24
+ {children}
25
+ </ThemeProvider>
26
+ )
27
+ }
28
+
29
+ export default ClientProviders
@@ -0,0 +1,59 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { Select } from 'antd'
3
+ import { useRouter } from 'next/router'
4
+ import { GlobalOutlined } from '@ant-design/icons'
5
+
6
+ const { Option } = Select
7
+
8
+ interface LanguageSwitcherProps {
9
+ style?: React.CSSProperties
10
+ size?: 'small' | 'middle' | 'large'
11
+ }
12
+
13
+ const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({ style, size = 'middle' }) => {
14
+ const router = useRouter()
15
+ const [mounted, setMounted] = useState(false)
16
+ const [currentLocale, setCurrentLocale] = useState('zh-CN')
17
+
18
+ useEffect(() => {
19
+ setMounted(true)
20
+ // 只在客户端获取当前语言
21
+ if (typeof window !== 'undefined' && router.locale) {
22
+ setCurrentLocale(router.locale)
23
+ }
24
+ }, [router.locale])
25
+
26
+ const languages = [
27
+ { code: 'zh-CN', name: '中文', flag: '🇨🇳' },
28
+ { code: 'en-US', name: 'English', flag: '🇺🇸' },
29
+ { code: 'ja-JP', name: '日本語', flag: '🇯🇵' },
30
+ ]
31
+
32
+ const handleLanguageChange = (locale: string) => {
33
+ if (mounted && typeof window !== 'undefined') {
34
+ const { pathname, asPath, query } = router
35
+ router.push({ pathname, query }, asPath, { locale })
36
+ }
37
+ }
38
+
39
+ return (
40
+ <Select
41
+ value={currentLocale}
42
+ onChange={handleLanguageChange}
43
+ style={{ minWidth: 120, ...style }}
44
+ size={size}
45
+ suffixIcon={<GlobalOutlined />}
46
+ placeholder="Language"
47
+ disabled={!mounted}
48
+ >
49
+ {languages.map((language) => (
50
+ <Option key={language.code} value={language.code}>
51
+ <span style={{ marginRight: 8 }}>{language.flag}</span>
52
+ {language.name}
53
+ </Option>
54
+ ))}
55
+ </Select>
56
+ )
57
+ }
58
+
59
+ export default LanguageSwitcher
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ import { ConfigProvider } from 'antd'
3
+
4
+ interface SSRSafeAntdProviderProps {
5
+ children: React.ReactNode
6
+ locale?: any
7
+ }
8
+
9
+ const SSRSafeAntdProvider: React.FC<SSRSafeAntdProviderProps> = ({ children, locale }) => {
10
+ // 在服务端渲染时,我们仍然使用 ConfigProvider,但使用简化的配置
11
+ return (
12
+ <ConfigProvider
13
+ locale={locale}
14
+ theme={{
15
+ // 确保服务端渲染的一致性
16
+ cssVar: false,
17
+ }}
18
+ >
19
+ {children}
20
+ </ConfigProvider>
21
+ )
22
+ }
23
+
24
+ export default SSRSafeAntdProvider
@@ -0,0 +1,55 @@
1
+ import { useEffect } from 'react'
2
+
3
+ // 全局抑制 useLayoutEffect 警告的函数
4
+ const suppressUseLayoutEffectWarnings = () => {
5
+ if (typeof window === 'undefined' && process.env.NODE_ENV === 'development') {
6
+ const originalError = console.error
7
+ const originalWarn = console.warn
8
+
9
+ console.error = (...args) => {
10
+ const errorMessage = args[0]
11
+ if (
12
+ typeof errorMessage === 'string' &&
13
+ (errorMessage.includes('useLayoutEffect does nothing on the server') ||
14
+ errorMessage.includes('Warning: useLayoutEffect does nothing on the server'))
15
+ ) {
16
+ return
17
+ }
18
+ originalError.apply(console, args)
19
+ }
20
+
21
+ console.warn = (...args) => {
22
+ const warnMessage = args[0]
23
+ if (
24
+ typeof warnMessage === 'string' &&
25
+ (warnMessage.includes('useLayoutEffect does nothing on the server') ||
26
+ warnMessage.includes('Warning: useLayoutEffect does nothing on the server'))
27
+ ) {
28
+ return
29
+ }
30
+ originalWarn.apply(console, args)
31
+ }
32
+
33
+ // 返回清理函数
34
+ return () => {
35
+ console.error = originalError
36
+ console.warn = originalWarn
37
+ }
38
+ }
39
+ return undefined
40
+ }
41
+
42
+ // 在模块加载时立即执行
43
+ suppressUseLayoutEffectWarnings()
44
+
45
+ const SuppressHydrationWarnings = () => {
46
+ useEffect(() => {
47
+ // 在客户端也抑制这些警告(以防万一)
48
+ const cleanup = suppressUseLayoutEffectWarnings()
49
+ return cleanup
50
+ }, [])
51
+
52
+ return null
53
+ }
54
+
55
+ export default SuppressHydrationWarnings
@@ -1,13 +1,23 @@
1
1
  import React, { useEffect, useState } from 'react'
2
- import { Layout, Menu, Breadcrumb, Image, Dropdown, Space, Tooltip } from 'antd'
3
- import { Container } from '@/styled/layout'
4
- import styled from 'styled-components'
2
+ import { Layout, Menu, Dropdown, Space } from 'antd'
3
+ import {
4
+ Container,
5
+ FlexLayout,
6
+ StyledSider,
7
+ SideMenu,
8
+ ContentLayout,
9
+ StyledHeader,
10
+ StyledBreadcrumb,
11
+ StyledContent,
12
+ } from '@/styled/layout'
5
13
  import { useRouter } from 'next/router'
6
14
  import _ from 'lodash'
7
- import menuConfig from '@/utils/menu'
8
- import { logout } from '@/utils/sso'
15
+ import menuConfig, { getMenuConfig } from '@/utils/menu'
9
16
  import getConfig from 'next/config'
10
- import { LogoutOutlined, SettingOutlined, BellOutlined, UserOutlined } from '@ant-design/icons'
17
+ import { LogoutOutlined } from '@ant-design/icons'
18
+ import LanguageSwitcher from '@/components/LanguageSwitcher'
19
+ import { useTranslation } from 'next-i18next'
20
+ import { navigateToLogin } from '@/utils/navigation'
11
21
 
12
22
  interface SubMenuItem {
13
23
  key: string
@@ -23,96 +33,10 @@ interface MenuItem {
23
33
  subMenus?: SubMenuItem[]
24
34
  }
25
35
 
26
- const { Header, Content, Sider } = Layout
27
-
28
36
  const nextConfig = getConfig()
29
37
  const { publicRuntimeConfig } = nextConfig
30
38
  const { prefix } = publicRuntimeConfig
31
39
 
32
- // styled-components
33
- const FlexLayout = styled(Layout)`
34
- display: flex;
35
- flex: 1;
36
- `
37
- const StyledSider = styled(Sider)`
38
- display: flex;
39
- flex-direction: column;
40
- box-shadow: 2px 0 8px -4px rgba(0, 0, 0, 0.1);
41
- z-index: 5;
42
- position: relative;
43
- `
44
-
45
- const SideMenu = styled(Menu)`
46
- height: 100%;
47
- border-right: 0;
48
- padding: 8px 0;
49
- `
50
-
51
- const ContentLayout = styled(Layout)`
52
- display: flex;
53
- flex-direction: column;
54
- flex: 1;
55
- background: #f5f7fa;
56
- position: relative;
57
- z-index: 1;
58
- `
59
- const StyledHeader = styled(Header)`
60
- display: flex;
61
- align-items: center;
62
- padding: 0 24px;
63
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
64
- z-index: 11;
65
-
66
- .logo {
67
- margin-right: 24px;
68
- }
69
-
70
- .main-menu {
71
- flex: 1;
72
- }
73
-
74
- .user-actions {
75
- display: flex;
76
- align-items: center;
77
-
78
- .action-icon {
79
- font-size: 18px;
80
- color: rgba(255, 255, 255, 0.85);
81
- cursor: pointer;
82
- padding: 0 8px;
83
- transition: color 0.3s;
84
-
85
- &:hover {
86
- color: #fff;
87
- }
88
- }
89
-
90
- .user-dropdown {
91
- cursor: pointer;
92
- padding: 0 8px;
93
-
94
- .username {
95
- color: rgba(255, 255, 255, 0.85);
96
- margin-left: 8px;
97
- }
98
- }
99
- }
100
- `
101
- const StyledBreadcrumb = styled(Breadcrumb)`
102
- margin: 16px 24px;
103
- font-size: 14px;
104
- `
105
- const StyledContent = styled(Content)`
106
- margin: 0 24px 24px;
107
- padding: 24px;
108
- background: #fff;
109
- border-radius: 4px;
110
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
111
- min-height: calc(100vh - 180px);
112
- position: relative;
113
- z-index: 1;
114
- `
115
-
116
40
  const getLocationKey = () => {
117
41
  const result = {
118
42
  topMenu: '1',
@@ -169,7 +93,7 @@ const getLocationKey = () => {
169
93
  }
170
94
 
171
95
  const routerPush = (router: any, url: string) => {
172
- if (router && url) {
96
+ if (router && url && typeof window !== 'undefined') {
173
97
  if (prefix && url.indexOf(prefix) === -1) {
174
98
  url = prefix + url
175
99
  }
@@ -178,10 +102,35 @@ const routerPush = (router: any, url: string) => {
178
102
  }
179
103
 
180
104
  const LayoutComponent = ({ user, children }) => {
105
+ const { t } = useTranslation(['layout', 'common'])
181
106
  const router = useRouter()
182
107
  const [topMenuKey, setTopMenuKey] = useState('1')
183
108
  const [sliderMenuKey, setSliderMenuKey] = useState('1')
184
109
  const [collapsed, setCollapsed] = useState(false)
110
+ const [mounted, setMounted] = useState(false)
111
+
112
+ useEffect(() => {
113
+ setMounted(true)
114
+ }, [])
115
+
116
+ // 使用翻译后的菜单配置
117
+ const translatedMenuConfig = getMenuConfig(t)
118
+
119
+ // 自定义退出登录函数,保持语言设置
120
+ const handleLogout = () => {
121
+ if (!mounted || typeof window === 'undefined') return
122
+
123
+ // 删除登录相关的 cookie
124
+ const deleteCookie = (name: string) => {
125
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
126
+ }
127
+
128
+ deleteCookie('_cas_nsgm')
129
+ deleteCookie('_cas_nsgm_user')
130
+
131
+ // 跳转到登录页面,保持当前语言
132
+ navigateToLogin(router)
133
+ }
185
134
 
186
135
  useEffect(() => {
187
136
  const { topMenu, slideMenu } = getLocationKey()
@@ -192,7 +141,7 @@ const LayoutComponent = ({ user, children }) => {
192
141
  const menuItems: any = []
193
142
  const menuItemsVertical: any = []
194
143
 
195
- _.each(menuConfig, (item) => {
144
+ _.each(translatedMenuConfig, (item) => {
196
145
  const { key, text, url, icon, subMenus } = item
197
146
 
198
147
  if (key && text && url) {
@@ -200,13 +149,15 @@ const LayoutComponent = ({ user, children }) => {
200
149
  label: text,
201
150
  key,
202
151
  onClick: () => {
203
- routerPush(router, url)
204
- setTopMenuKey(key)
152
+ if (mounted) {
153
+ routerPush(router, url)
154
+ setTopMenuKey(key)
205
155
 
206
- if (subMenus) {
207
- setSliderMenuKey('1')
208
- } else {
209
- setSliderMenuKey('0')
156
+ if (subMenus) {
157
+ setSliderMenuKey('1')
158
+ } else {
159
+ setSliderMenuKey('0')
160
+ }
210
161
  }
211
162
  },
212
163
  }
@@ -225,13 +176,15 @@ const LayoutComponent = ({ user, children }) => {
225
176
  key: `slider_${subKey}`,
226
177
  label: subText,
227
178
  onClick: () => {
228
- routerPush(router, subUrl)
179
+ if (mounted) {
180
+ routerPush(router, subUrl)
229
181
 
230
- const subKeyArr = subKey.split('_')
231
- const subKeyArrLen = subKeyArr.length
182
+ const subKeyArr = subKey.split('_')
183
+ const subKeyArrLen = subKeyArr.length
232
184
 
233
- if (subKeyArrLen >= 1) setTopMenuKey(subKeyArr[0])
234
- if (subKeyArrLen >= 2) setSliderMenuKey(subKeyArr[1])
185
+ if (subKeyArrLen >= 1) setTopMenuKey(subKeyArr[0])
186
+ if (subKeyArrLen >= 2) setSliderMenuKey(subKeyArr[1])
187
+ }
235
188
  },
236
189
  }
237
190
 
@@ -260,9 +213,11 @@ const LayoutComponent = ({ user, children }) => {
260
213
  icon,
261
214
  key: `slider_${key}_0`,
262
215
  onClick: () => {
263
- routerPush(router, url)
264
- setTopMenuKey(key)
265
- setSliderMenuKey('0')
216
+ if (mounted) {
217
+ routerPush(router, url)
218
+ setTopMenuKey(key)
219
+ setSliderMenuKey('0')
220
+ }
266
221
  },
267
222
  }
268
223
 
@@ -276,7 +231,7 @@ const LayoutComponent = ({ user, children }) => {
276
231
  <Container>
277
232
  <StyledHeader>
278
233
  <div className="logo">
279
- <Image width={120} src={`${prefix}/images/zhizuotu_1.png`} preview={false} />
234
+ <span className="logo-text">NSGM</span>
280
235
  </div>
281
236
  <Menu
282
237
  theme="dark"
@@ -288,39 +243,40 @@ const LayoutComponent = ({ user, children }) => {
288
243
  />
289
244
  <div className="user-actions">
290
245
  <Space size={20} align="center">
291
- <Tooltip title="通知">
246
+ <LanguageSwitcher size="small" />
247
+ {/* <Tooltip title="通知">
292
248
  <BellOutlined className="action-icon" />
293
249
  </Tooltip>
294
250
  <Tooltip title="设置">
295
251
  <SettingOutlined className="action-icon" />
296
- </Tooltip>
252
+ </Tooltip> */}
297
253
  <Dropdown
298
254
  menu={{
299
255
  items: [
300
- {
301
- key: '1',
302
- icon: <UserOutlined />,
303
- label: '个人中心',
304
- },
305
- {
306
- key: '2',
307
- icon: <SettingOutlined />,
308
- label: '账户设置',
309
- },
256
+ // {
257
+ // key: '1',
258
+ // icon: <UserOutlined />,
259
+ // label: t('layout:layout.userActions.profile'),
260
+ // },
261
+ // {
262
+ // key: '2',
263
+ // icon: <SettingOutlined />,
264
+ // label: t('layout:layout.userActions.settings'),
265
+ // },
310
266
  {
311
267
  type: 'divider',
312
268
  },
313
269
  {
314
270
  key: '3',
315
271
  icon: <LogoutOutlined />,
316
- label: '退出登录',
317
- onClick: () => logout(),
272
+ label: t('layout:layout.userActions.logout'),
273
+ onClick: () => handleLogout(),
318
274
  },
319
275
  ],
320
276
  }}
321
277
  >
322
278
  <Space className="user-dropdown">
323
- <span className="username">{user?.displayName || '用户'}</span>
279
+ <span className="username">{user?.displayName || t('layout:layout.userActions.user')}</span>
324
280
  </Space>
325
281
  </Dropdown>
326
282
  </Space>
@@ -346,10 +302,10 @@ const LayoutComponent = ({ user, children }) => {
346
302
  />
347
303
  </div>
348
304
  </StyledSider>
349
- <ContentLayout className="content-layout">
305
+ <ContentLayout collapsed={collapsed} className="content-layout">
350
306
  <StyledBreadcrumb
351
307
  items={_.compact(
352
- _.flatMap(menuConfig, (item, index) => {
308
+ _.flatMap(translatedMenuConfig, (item, index) => {
353
309
  const { key, text, subMenus } = item
354
310
 
355
311
  if (subMenus) {