nsgm-cli 2.1.13 → 2.1.15

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 (147) hide show
  1. package/README.md +394 -156
  2. package/client/components/Button.tsx +3 -5
  3. package/client/components/ClientProviders.tsx +29 -0
  4. package/client/components/LanguageSwitcher.tsx +59 -0
  5. package/client/components/SSRSafeAntdProvider.tsx +24 -0
  6. package/client/components/SuppressHydrationWarnings.tsx +55 -0
  7. package/client/components/__tests__/Button.test.tsx +10 -10
  8. package/client/layout/index.tsx +153 -185
  9. package/client/redux/reducers.ts +1 -1
  10. package/client/redux/store.ts +2 -1
  11. package/client/redux/template/manage/actions.ts +77 -88
  12. package/client/redux/template/manage/reducers.ts +25 -37
  13. package/client/redux/template/manage/types.ts +1 -1
  14. package/client/service/template/manage.ts +20 -21
  15. package/client/styled/common.ts +12 -14
  16. package/client/styled/layout/index.ts +234 -120
  17. package/client/styled/template/manage.ts +102 -14
  18. package/client/utils/common.ts +23 -21
  19. package/client/utils/cookie.ts +18 -19
  20. package/client/utils/fetch.ts +64 -100
  21. package/client/utils/i18n.ts +68 -0
  22. package/client/utils/menu.tsx +42 -23
  23. package/client/utils/navigation.ts +58 -0
  24. package/client/utils/sso.ts +74 -84
  25. package/client/utils/suppressWarnings.ts +32 -0
  26. package/eslint.config.js +53 -19
  27. package/generation/README.md +25 -6
  28. package/generation/__tests__/example.test.js +41 -0
  29. package/generation/client/redux/reducers.ts +1 -1
  30. package/generation/client/utils/menu.tsx +36 -23
  31. package/generation/env +3 -0
  32. package/generation/env.example +3 -0
  33. package/generation/eslint.config.js +112 -0
  34. package/generation/gitignore +6 -1
  35. package/generation/jest.config.js +40 -0
  36. package/generation/next.config.js +7 -3
  37. package/generation/package.json +28 -4
  38. package/generation/tsconfig.json +6 -19
  39. package/jest.config.js +23 -6
  40. package/lib/args.js +9 -1
  41. package/lib/cli/app.d.ts +28 -0
  42. package/lib/cli/app.js +99 -0
  43. package/lib/cli/commands/build.d.ts +2 -0
  44. package/lib/cli/commands/build.js +29 -0
  45. package/lib/cli/commands/create.d.ts +2 -0
  46. package/lib/cli/commands/create.js +113 -0
  47. package/lib/cli/commands/delete.d.ts +3 -0
  48. package/lib/cli/commands/delete.js +151 -0
  49. package/lib/cli/commands/export.d.ts +2 -0
  50. package/lib/cli/commands/export.js +42 -0
  51. package/lib/cli/commands/help.d.ts +2 -0
  52. package/lib/cli/commands/help.js +42 -0
  53. package/lib/cli/commands/init.d.ts +2 -0
  54. package/lib/cli/commands/init.js +115 -0
  55. package/lib/cli/commands/server.d.ts +3 -0
  56. package/lib/cli/commands/server.js +26 -0
  57. package/lib/cli/commands/upgrade.d.ts +2 -0
  58. package/lib/cli/commands/upgrade.js +38 -0
  59. package/lib/cli/commands/version.d.ts +2 -0
  60. package/lib/cli/commands/version.js +24 -0
  61. package/lib/cli/index.d.ts +16 -0
  62. package/lib/cli/index.js +33 -0
  63. package/lib/cli/parser.d.ts +22 -0
  64. package/lib/cli/parser.js +115 -0
  65. package/lib/cli/registry.d.ts +33 -0
  66. package/lib/cli/registry.js +81 -0
  67. package/lib/cli/types/project.d.ts +10 -0
  68. package/lib/cli/types/project.js +2 -0
  69. package/lib/cli/types.d.ts +31 -0
  70. package/lib/cli/types.js +20 -0
  71. package/lib/cli/utils/console.d.ts +62 -0
  72. package/lib/cli/utils/console.js +148 -0
  73. package/lib/cli/utils/index.d.ts +2 -0
  74. package/lib/cli/utils/index.js +7 -0
  75. package/lib/cli/utils/prompt.d.ts +83 -0
  76. package/lib/cli/utils/prompt.js +327 -0
  77. package/lib/constants.d.ts +65 -0
  78. package/lib/constants.js +177 -0
  79. package/lib/generate.d.ts +25 -3
  80. package/lib/generate.js +98 -621
  81. package/lib/generate_create.d.ts +9 -0
  82. package/lib/generate_create.js +329 -0
  83. package/lib/generate_delete.d.ts +8 -0
  84. package/lib/generate_delete.js +233 -0
  85. package/lib/generate_init.d.ts +56 -0
  86. package/lib/generate_init.js +612 -0
  87. package/lib/generators/base-generator.d.ts +47 -0
  88. package/lib/generators/base-generator.js +92 -0
  89. package/lib/generators/file-generator.d.ts +48 -0
  90. package/lib/generators/file-generator.js +455 -0
  91. package/lib/generators/generator-factory.d.ts +20 -0
  92. package/lib/generators/generator-factory.js +25 -0
  93. package/lib/generators/i18n-generator.d.ts +51 -0
  94. package/lib/generators/i18n-generator.js +320 -0
  95. package/lib/generators/page-generator.d.ts +45 -0
  96. package/lib/generators/page-generator.js +578 -0
  97. package/lib/generators/resolver-generator.d.ts +14 -0
  98. package/lib/generators/resolver-generator.js +342 -0
  99. package/lib/generators/schema-generator.d.ts +7 -0
  100. package/lib/generators/schema-generator.js +57 -0
  101. package/lib/generators/service-generator.d.ts +11 -0
  102. package/lib/generators/service-generator.js +233 -0
  103. package/lib/generators/sql-generator.d.ts +8 -0
  104. package/lib/generators/sql-generator.js +52 -0
  105. package/lib/index.d.ts +1 -1
  106. package/lib/index.js +14 -173
  107. package/lib/server/csrf.js +9 -16
  108. package/lib/server/db.js +6 -7
  109. package/lib/server/graphql.js +5 -6
  110. package/lib/server/plugins/date.js +1 -1
  111. package/lib/server/utils/graphql-cache.js +3 -3
  112. package/lib/tsconfig.build.tsbuildinfo +1 -1
  113. package/lib/utils/project-config.d.ts +5 -0
  114. package/lib/utils/project-config.js +145 -0
  115. package/lib/utils.js +1 -1
  116. package/next-i18next.config.js +18 -0
  117. package/next.config.js +61 -18
  118. package/package.json +16 -8
  119. package/pages/_app.tsx +77 -33
  120. package/pages/_document.tsx +78 -21
  121. package/pages/_error.tsx +66 -0
  122. package/pages/index.tsx +109 -47
  123. package/pages/login.tsx +66 -41
  124. package/pages/template/manage.tsx +162 -151
  125. package/public/fonts/font-awesome.min.css +4 -0
  126. package/public/fonts/fontawesome-webfont.woff +0 -0
  127. package/public/fonts/fontawesome-webfont.woff2 +0 -0
  128. package/public/locales/en-US/common.json +48 -0
  129. package/public/locales/en-US/home.json +57 -0
  130. package/public/locales/en-US/layout.json +22 -0
  131. package/public/locales/en-US/login.json +13 -0
  132. package/public/locales/en-US/template.json +42 -0
  133. package/public/locales/ja-JP/common.json +48 -0
  134. package/public/locales/ja-JP/home.json +57 -0
  135. package/public/locales/ja-JP/layout.json +22 -0
  136. package/public/locales/ja-JP/login.json +13 -0
  137. package/public/locales/ja-JP/template.json +42 -0
  138. package/public/locales/zh-CN/common.json +48 -0
  139. package/public/locales/zh-CN/home.json +57 -0
  140. package/public/locales/zh-CN/layout.json +22 -0
  141. package/public/locales/zh-CN/login.json +13 -0
  142. package/public/locales/zh-CN/template.json +42 -0
  143. package/public/slbhealthcheck.html +1 -1
  144. package/server/apis/template.js +0 -2
  145. package/server/utils/validation.js +163 -0
  146. package/types/i18next.d.ts +10 -0
  147. package/generation/eslintrc.js +0 -16
@@ -1,22 +1,85 @@
1
- import Document, {
2
- Html,
3
- Head,
4
- Main,
5
- NextScript,
6
- DocumentContext
7
- } from 'next/document'
1
+ import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document'
8
2
  import React from 'react'
9
3
  import { ServerStyleSheet } from 'styled-components'
10
4
 
11
- const MyDocument = () => {
5
+ const MyDocument = (props) => {
6
+ // 从 props 中获取语言信息,如果没有则默认为 zh-CN
7
+ const locale = props.locale || 'zh-CN'
8
+
12
9
  return (
13
- <Html>
10
+ <Html lang={locale}>
14
11
  <title>NSGM CLI</title>
15
- <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,minimal-ui" />
16
- <meta name="apple-mobile-web-app-capable" content="yes" />
12
+ <meta
13
+ name="viewport"
14
+ content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,minimal-ui"
15
+ />
16
+ <meta name="mobile-web-app-capable" content="yes" />
17
17
  <meta charSet="utf-8" />
18
18
  <Head>
19
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
19
+ {/* 直接使用现有的 FontAwesome CSS 文件 */}
20
+ <link rel="stylesheet" href="/fonts/font-awesome.min.css" />
21
+
22
+ {/* 预加载字体文件以改善性能 */}
23
+ <link
24
+ rel="preload"
25
+ href="/fonts/fontawesome-webfont.woff2?v=4.7.0"
26
+ as="font"
27
+ type="font/woff2"
28
+ crossOrigin="anonymous"
29
+ />
30
+ <link
31
+ rel="preload"
32
+ href="/fonts/fontawesome-webfont.woff?v=4.7.0"
33
+ as="font"
34
+ type="font/woff"
35
+ crossOrigin="anonymous"
36
+ />
37
+
38
+ {/* 确保字体正确显示的额外样式 */}
39
+ <style>{`
40
+ .fa {
41
+ font-family: FontAwesome !important;
42
+ font-style: normal !important;
43
+ font-weight: normal !important;
44
+ text-decoration: inherit;
45
+ line-height: 1;
46
+ -webkit-font-smoothing: antialiased;
47
+ -moz-osx-font-smoothing: grayscale;
48
+ }
49
+ `}</style>
50
+
51
+ {/* 抑制服务端渲染的 useLayoutEffect 警告 */}
52
+ <script
53
+ dangerouslySetInnerHTML={{
54
+ __html: `
55
+ if (typeof window === 'undefined') {
56
+ global.window = {};
57
+ }
58
+
59
+ // 在服务端抑制 useLayoutEffect 警告
60
+ if (typeof console !== 'undefined' && process.env.NODE_ENV === 'development') {
61
+ const originalError = console.error;
62
+ const originalWarn = console.warn;
63
+
64
+ console.error = function(...args) {
65
+ const message = args[0];
66
+ if (typeof message === 'string' && message.includes('useLayoutEffect does nothing on the server')) {
67
+ return;
68
+ }
69
+ originalError.apply(console, args);
70
+ };
71
+
72
+ console.warn = function(...args) {
73
+ const message = args[0];
74
+ if (typeof message === 'string' && message.includes('useLayoutEffect does nothing on the server')) {
75
+ return;
76
+ }
77
+ originalWarn.apply(console, args);
78
+ };
79
+ }
80
+ `,
81
+ }}
82
+ />
20
83
  </Head>
21
84
  <body>
22
85
  <Main />
@@ -29,27 +92,21 @@ const MyDocument = () => {
29
92
  // MyDocument.renderDocument = Document.renderDocument
30
93
 
31
94
  MyDocument.getInitialProps = async (ctx: DocumentContext) => {
32
- // console.log('document getInitialProps')
33
-
34
95
  const sheet = new ServerStyleSheet()
35
96
  const originalRenderPage = ctx.renderPage
36
97
 
37
98
  try {
38
99
  ctx.renderPage = () =>
39
100
  originalRenderPage({
40
- enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />)
101
+ enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
41
102
  })
42
103
 
43
104
  const initialProps = await Document?.getInitialProps(ctx)
44
105
 
45
106
  return {
46
107
  ...initialProps,
47
- styles: (
48
- <>
49
- {initialProps.styles}
50
- {sheet.getStyleElement()}
51
- </>
52
- )
108
+ locale: ctx.locale || 'zh-CN',
109
+ styles: [initialProps.styles, sheet.getStyleElement()],
53
110
  }
54
111
  } finally {
55
112
  sheet.seal()
@@ -0,0 +1,66 @@
1
+ import React from 'react'
2
+ import { Result, Button } from 'antd'
3
+ import { NextPageContext } from 'next'
4
+
5
+ interface ErrorProps {
6
+ statusCode?: number
7
+ }
8
+
9
+ function ErrorPage({ statusCode }: ErrorProps) {
10
+ const getErrorMessage = () => {
11
+ if (statusCode === 404) {
12
+ return '页面未找到'
13
+ } else if (statusCode === 500) {
14
+ return '服务器错误'
15
+ }
16
+ return '发生了未知错误'
17
+ }
18
+
19
+ const getErrorTitle = () => {
20
+ if (statusCode === 404) {
21
+ return '404'
22
+ } else if (statusCode === 500) {
23
+ return '500'
24
+ }
25
+ return '错误'
26
+ }
27
+
28
+ return (
29
+ <div
30
+ style={{
31
+ minHeight: '100vh',
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ padding: '20px',
36
+ }}
37
+ >
38
+ <Result
39
+ status={statusCode === 404 ? '404' : '500'}
40
+ title={getErrorTitle()}
41
+ subTitle={getErrorMessage()}
42
+ extra={
43
+ <Button
44
+ type="primary"
45
+ onClick={() => {
46
+ if (typeof window !== 'undefined') {
47
+ window.location.href = '/'
48
+ }
49
+ }}
50
+ >
51
+ 返回首页
52
+ </Button>
53
+ }
54
+ />
55
+ </div>
56
+ )
57
+ }
58
+
59
+ ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
60
+ const statusCode = res ? res.statusCode : err ? err.statusCode : 404
61
+ return { statusCode }
62
+ }
63
+
64
+ ErrorPage.displayName = 'ErrorPage'
65
+
66
+ export default ErrorPage
package/pages/index.tsx CHANGED
@@ -1,104 +1,157 @@
1
- import MarkdownIt from 'markdown-it'
2
1
  import _ from 'lodash'
3
2
  import { Container } from '../client/styled/common'
4
- import getConfig from 'next/config'
5
3
  import React from 'react'
6
4
  import { Card, Typography, Divider, Row, Col, Tag } from 'antd'
7
5
  import { CodeOutlined, BookOutlined, DatabaseOutlined, SettingOutlined } from '@ant-design/icons'
6
+ import { useTranslation } from 'next-i18next'
7
+ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
8
8
 
9
9
  const { Title, Paragraph, Text } = Typography
10
10
 
11
- const md = new MarkdownIt({
12
- html: true,
13
- linkify: true,
14
- typographer: true
15
- })
11
+ const Page = () => {
12
+ const { t } = useTranslation(['common', 'home'])
16
13
 
17
- const nextConfig = getConfig()
18
- const { publicRuntimeConfig } = nextConfig
19
- const { env } = publicRuntimeConfig
20
-
21
-
22
- const Page = ({ html }) => {
23
14
  return (
24
15
  <Container>
25
16
  <Typography style={{ padding: '24px' }}>
26
- <Title level={1}>NSGM CLI</Title>
17
+ <Row justify="space-between" align="middle" style={{ marginBottom: '24px' }}>
18
+ <Col>
19
+ <Title level={1}>{t('home:page.title')}</Title>
20
+ </Col>
21
+ </Row>
22
+
27
23
  <Paragraph>
28
24
  <Row gutter={[16, 16]}>
29
- <Col><Tag color="blue">Next</Tag></Col>
30
- <Col><Tag color="purple">Styled-components</Tag></Col>
31
- <Col><Tag color="magenta">Graphql</Tag></Col>
32
- <Col><Tag color="green">Mysql</Tag></Col>
25
+ <Col>
26
+ <Tag color="blue">Next</Tag>
27
+ </Col>
28
+ <Col>
29
+ <Tag color="purple">Styled-components</Tag>
30
+ </Col>
31
+ <Col>
32
+ <Tag color="magenta">Graphql</Tag>
33
+ </Col>
34
+ <Col>
35
+ <Tag color="green">Mysql</Tag>
36
+ </Col>
33
37
  </Row>
34
38
  </Paragraph>
35
- <Paragraph>
36
- 全栈架构,代码模板生成,快速开发
37
- </Paragraph>
39
+ <Paragraph>{t('home:page.description')}</Paragraph>
38
40
 
39
41
  <Card style={{ marginBottom: '24px' }}>
40
42
  <Row gutter={[24, 16]}>
41
43
  <Col xs={24} md={8}>
42
- <Card type="inner" title={<><DatabaseOutlined /> 数据库配置</>}>
43
- 数据库采用 Mysql, 配置见 mysql.config.js
44
+ <Card
45
+ type="inner"
46
+ title={
47
+ <>
48
+ <DatabaseOutlined /> {t('home:page.sections.database.title')}
49
+ </>
50
+ }
51
+ >
52
+ {t('home:page.sections.database.description')}
44
53
  </Card>
45
54
  </Col>
46
55
  <Col xs={24} md={8}>
47
- <Card type="inner" title={<><SettingOutlined /> 项目配置</>}>
48
- 项目配置见 project.config.js
56
+ <Card
57
+ type="inner"
58
+ title={
59
+ <>
60
+ <SettingOutlined /> {t('home:page.sections.project.title')}
61
+ </>
62
+ }
63
+ >
64
+ {t('home:page.sections.project.description')}
49
65
  </Card>
50
66
  </Col>
51
67
  <Col xs={24} md={8}>
52
- <Card type="inner" title={<><CodeOutlined /> 框架配置</>}>
53
- Next 框架配置见 next.config.js
68
+ <Card
69
+ type="inner"
70
+ title={
71
+ <>
72
+ <CodeOutlined /> {t('home:page.sections.framework.title')}
73
+ </>
74
+ }
75
+ >
76
+ {t('home:page.sections.framework.description')}
54
77
  </Card>
55
78
  </Col>
56
79
  </Row>
57
80
  </Card>
58
81
 
59
- <Title level={2}><BookOutlined /> 命令</Title>
82
+ <Title level={2}>
83
+ <BookOutlined /> {t('home:page.commands.title')}
84
+ </Title>
60
85
  <Divider />
61
86
 
62
87
  <Row gutter={[16, 16]}>
63
88
  <Col xs={24} md={8}>
64
89
  <Card hoverable>
65
- <Title level={4}>项目管理</Title>
90
+ <Title level={4}>{t('home:page.commands.categories.projectManagement.title')}</Title>
66
91
  <ul>
67
- <li><Text strong>nsgm init</Text> - 初始化项目</li>
68
- <li><Text strong>nsgm upgrade</Text> - 升级项目基础文件</li>
92
+ <li>
93
+ <Text strong>nsgm init</Text> - {t('home:page.commands.categories.projectManagement.items.init')}
94
+ </li>
95
+ <li>
96
+ <Text strong>nsgm upgrade</Text> -{' '}
97
+ {t('home:page.commands.categories.projectManagement.items.upgrade')}
98
+ </li>
69
99
  </ul>
70
100
  </Card>
71
101
  </Col>
72
102
  <Col xs={24} md={8}>
73
103
  <Card hoverable>
74
- <Title level={4}>模板操作</Title>
104
+ <Title level={4}>{t('home:page.commands.categories.templateOperations.title')}</Title>
75
105
  <ul>
76
- <li><Text strong>nsgm create</Text> - 创建模板页面</li>
77
- <li><Text strong>nsgm delete</Text> - 删除模板页面</li>
78
- <li><Text strong>nsgm deletedb</Text> - 删除模板页面及数据库表</li>
106
+ <li>
107
+ <Text strong>nsgm create</Text> - {t('home:page.commands.categories.templateOperations.items.create')}
108
+ </li>
109
+ <li>
110
+ <Text strong>nsgm delete</Text> - {t('home:page.commands.categories.templateOperations.items.delete')}
111
+ </li>
112
+ <li>
113
+ <Text strong>nsgm deletedb</Text> -{' '}
114
+ {t('home:page.commands.categories.templateOperations.items.deletedb')}
115
+ </li>
79
116
  </ul>
80
117
  </Card>
81
118
  </Col>
82
119
  <Col xs={24} md={8}>
83
120
  <Card hoverable>
84
- <Title level={4}>运行与构建</Title>
121
+ <Title level={4}>{t('home:page.commands.categories.runBuild.title')}</Title>
85
122
  <ul>
86
- <li><Text strong>nsgm dev</Text> - 开发模式</li>
87
- <li><Text strong>nsgm start</Text> - 生产模式</li>
88
- <li><Text strong>nsgm build</Text> - 编译</li>
89
- <li><Text strong>nsgm export</Text> - 导出静态页面</li>
123
+ <li>
124
+ <Text strong>nsgm dev</Text> - {t('home:page.commands.categories.runBuild.items.dev')}
125
+ </li>
126
+ <li>
127
+ <Text strong>nsgm start</Text> - {t('home:page.commands.categories.runBuild.items.start')}
128
+ </li>
129
+ <li>
130
+ <Text strong>nsgm build</Text> - {t('home:page.commands.categories.runBuild.items.build')}
131
+ </li>
132
+ <li>
133
+ <Text strong>nsgm export</Text> - {t('home:page.commands.categories.runBuild.items.export')}
134
+ </li>
90
135
  </ul>
91
136
  </Card>
92
137
  </Col>
93
138
  </Row>
94
139
 
95
- <Title level={2} style={{ marginTop: '24px' }}>参数</Title>
140
+ <Title level={2} style={{ marginTop: '24px' }}>
141
+ {t('home:page.parameters.title')}
142
+ </Title>
96
143
  <Divider />
97
144
  <Card>
98
145
  <ul>
99
- <li><Text strong>dictionary:</Text> 在 export/init 的时候使用, 默认 webapp, 譬如: nsgm export/init dictionary=webapp 或者 nsgm export/init webapp</li>
100
- <li><Text strong>controller:</Text> 在 create/delete 的时候使用, 必须有。譬如:nsgm create/delete math</li>
101
- <li><Text strong>action:</Text> 在 create/delete 的时候使用, 默认 manage, 跟在 controller 后面, 譬如 nsgm create/delete math test</li>
146
+ <li>
147
+ <Text strong>dictionary:</Text> {t('home:page.parameters.items.dictionary')}
148
+ </li>
149
+ <li>
150
+ <Text strong>controller:</Text> {t('home:page.parameters.items.controller')}
151
+ </li>
152
+ <li>
153
+ <Text strong>action:</Text> {t('home:page.parameters.items.action')}
154
+ </li>
102
155
  </ul>
103
156
  </Card>
104
157
  </Typography>
@@ -106,8 +159,17 @@ const Page = ({ html }) => {
106
159
  )
107
160
  }
108
161
 
109
- Page.getInitialProps = () => {
110
- return {}
162
+ export const getServerSideProps = async ({ locale }) => {
163
+ // 确保 locale 有默认值,避免 serverSideTranslations 报错
164
+ const currentLocale = locale || 'zh-CN'
165
+
166
+ return {
167
+ props: {
168
+ ...(await serverSideTranslations(currentLocale, ['common', 'home', 'layout'])),
169
+ },
170
+ }
111
171
  }
112
172
 
113
- export default Page
173
+ Page.displayName = 'HomePage'
174
+
175
+ export default Page
package/pages/login.tsx CHANGED
@@ -2,15 +2,20 @@ import MarkdownIt from 'markdown-it'
2
2
  import _ from 'lodash'
3
3
  import { LoginContainer } from '../client/styled/common'
4
4
  // import getConfig from 'next/config'
5
- import React, { useState } from 'react'
5
+ import React, { useState, useEffect } from 'react'
6
6
  import { Input, Button, Form, Typography, message } from 'antd'
7
7
  import { UserOutlined, LockOutlined } from '@ant-design/icons'
8
8
  import { directLogin } from '../client/utils/sso'
9
+ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
10
+ import { useTranslation } from 'next-i18next'
11
+ import { useRouter } from 'next/router'
12
+ import LanguageSwitcher from '@/components/LanguageSwitcher'
13
+ import { navigateToHome } from '@/utils/navigation'
9
14
 
10
15
  const md = new MarkdownIt({
11
16
  html: true,
12
17
  linkify: true,
13
- typographer: true
18
+ typographer: true,
14
19
  })
15
20
 
16
21
  // const nextConfig = getConfig()
@@ -19,48 +24,56 @@ const md = new MarkdownIt({
19
24
 
20
25
  const renderArr: any = []
21
26
 
22
- renderArr.push('Login')
27
+ renderArr.push('NSGM')
23
28
 
24
29
  const Page = ({ html }) => {
25
- const [userName, setUserName] = useState("")
26
- const [userPassword, setUserPassword] = useState("")
30
+ const { t } = useTranslation(['login'])
31
+ const router = useRouter()
32
+ const [userName, setUserName] = useState('')
33
+ const [userPassword, setUserPassword] = useState('')
34
+ const [mounted, setMounted] = useState(false)
35
+
36
+ useEffect(() => {
37
+ setMounted(true)
38
+ }, [])
27
39
 
28
40
  const createMarkup = () => {
29
41
  return {
30
- __html: html
42
+ __html: html,
31
43
  }
32
44
  }
33
45
 
34
46
  const doLogin = () => {
35
- if (typeof window !== "undefined") {
36
- if (userName === '') {
37
- message.error('请输入用户名');
38
- return;
39
- }
40
- if (userPassword === '') {
41
- message.error('请输入密码');
42
- return;
47
+ if (!mounted || typeof window === 'undefined') return
48
+
49
+ if (userName === '') {
50
+ message.error(t('login:login.errors.usernameRequired'))
51
+ return
52
+ }
53
+ if (userPassword === '') {
54
+ message.error(t('login:login.errors.passwordRequired'))
55
+ return
56
+ }
57
+
58
+ const result = directLogin(userName, userPassword, (user) => {
59
+ if (user && mounted) {
60
+ // 跳转到首页,保持当前语言设置,强制添加语言前缀避免自动检测
61
+ navigateToHome(router, true)
43
62
  }
63
+ })
44
64
 
45
- const result = directLogin(userName, userPassword, (user) => {
46
- if (user) {
47
- window.location.href = window.location.origin;
48
- }
49
- });
50
-
51
- // 检查是否是 Promise
52
- if (result && typeof (result as any).then === 'function') {
53
- (result as Promise<any>).then(loginResult => {
54
- if (!loginResult.success) {
55
- message.error(loginResult.message);
56
- }
57
- });
58
- } else {
59
- // 直接返回的结果
60
- const syncResult = result as { success: boolean; message?: string };
61
- if (!syncResult.success) {
62
- message.error(syncResult.message || '登录失败');
65
+ // 检查是否是 Promise
66
+ if (result && typeof (result as any).then === 'function') {
67
+ ;(result as Promise<any>).then((loginResult) => {
68
+ if (!loginResult.success) {
69
+ message.error(loginResult.message)
63
70
  }
71
+ })
72
+ } else {
73
+ // 直接返回的结果
74
+ const syncResult = result as { success: boolean; message?: string }
75
+ if (!syncResult.success) {
76
+ message.error(syncResult.message || t('login:login.errors.loginFailed'))
64
77
  }
65
78
  }
66
79
  }
@@ -75,13 +88,18 @@ const Page = ({ html }) => {
75
88
 
76
89
  return (
77
90
  <LoginContainer>
91
+ <div style={{ position: 'absolute', top: '20px', right: '20px' }}>
92
+ <LanguageSwitcher />
93
+ </div>
78
94
  <div dangerouslySetInnerHTML={createMarkup()} />
79
- <Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 24 }}>系统登录</Typography.Title>
95
+ <Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 24 }}>
96
+ {t('login:login.title')}
97
+ </Typography.Title>
80
98
  <Form layout="vertical" style={{ width: '100%' }}>
81
99
  <Form.Item>
82
100
  <Input
83
101
  prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
84
- placeholder="用户名"
102
+ placeholder={t('login:login.username')}
85
103
  size="large"
86
104
  value={userName}
87
105
  onChange={doChangeName}
@@ -91,7 +109,7 @@ const Page = ({ html }) => {
91
109
  <Form.Item>
92
110
  <Input.Password
93
111
  prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
94
- placeholder="密码"
112
+ placeholder={t('login:login.password')}
95
113
  size="large"
96
114
  value={userPassword}
97
115
  onChange={doChangePassword}
@@ -100,7 +118,7 @@ const Page = ({ html }) => {
100
118
  </Form.Item>
101
119
  <Form.Item>
102
120
  <Button type="primary" onClick={doLogin} size="large" block>
103
- 登录
121
+ {t('login:login.loginButton')}
104
122
  </Button>
105
123
  </Form.Item>
106
124
  </Form>
@@ -108,17 +126,24 @@ const Page = ({ html }) => {
108
126
  )
109
127
  }
110
128
 
111
- Page.getInitialProps = () => {
129
+ export const getServerSideProps = async ({ locale }) => {
130
+ // 确保 locale 有默认值,避免 serverSideTranslations 报错
131
+ const currentLocale = locale || 'zh-CN'
132
+
133
+ // 处理 markdown 内容
112
134
  let html = ''
113
135
  _.each(renderArr, (item) => {
114
136
  html += md.render(item)
115
137
  })
116
138
 
117
- console.log("Generated HTML:", html);
118
-
119
139
  return {
120
- html
140
+ props: {
141
+ html,
142
+ ...(await serverSideTranslations(currentLocale, ['common', 'layout', 'login'])),
143
+ },
121
144
  }
122
145
  }
123
146
 
124
- export default Page
147
+ Page.displayName = 'LoginPage'
148
+
149
+ export default Page