nsgm-cli 2.1.5 → 2.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nsgm-cli",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "A CLI tool to run Next/Style-components and Graphql/Mysql fullstack project",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -96,8 +96,8 @@
96
96
  "mysql2": "3.14.2",
97
97
  "next": "15.4.4",
98
98
  "rc-util": "5.44.4",
99
- "react": "^19",
100
- "react-dom": "^19",
99
+ "react": "18.3.1",
100
+ "react-dom": "18.3.1",
101
101
  "react-redux": "9.2.0",
102
102
  "redux": "5.0.1",
103
103
  "replace": "1.2.2",
@@ -128,8 +128,8 @@
128
128
  "@types/mysql": "2.15.27",
129
129
  "@types/next": "9.0.0",
130
130
  "@types/node": "^24",
131
- "@types/react": "^19",
132
- "@types/react-dom": "^19",
131
+ "@types/react": "18.3.23",
132
+ "@types/react-dom": "18.3.7",
133
133
  "@types/react-redux": "7.1.34",
134
134
  "@types/shelljs": "0.8.17",
135
135
  "@types/styled-components": "5.1.34",
package/pages/index.tsx CHANGED
@@ -3,6 +3,10 @@ import _ from 'lodash'
3
3
  import { Container } from '../client/styled/common'
4
4
  import getConfig from 'next/config'
5
5
  import React from 'react'
6
+ import { Card, Typography, Divider, Row, Col, Tag } from 'antd'
7
+ import { CodeOutlined, BookOutlined, DatabaseOutlined, SettingOutlined } from '@ant-design/icons'
8
+
9
+ const { Title, Paragraph, Text } = Typography
6
10
 
7
11
  const md = new MarkdownIt({
8
12
  html: true,
@@ -14,56 +18,96 @@ const nextConfig = getConfig()
14
18
  const { publicRuntimeConfig } = nextConfig
15
19
  const { env } = publicRuntimeConfig
16
20
 
17
- const renderArr: any = []
18
-
19
- renderArr.push('# NSGM CLI ' + env)
20
- renderArr.push('- 技术栈: [Next](https://github.com/vercel/next.js), [Styled-components](https://github.com/styled-components/styled-components), [Graphql](https://graphql.org/), [Mysql](https://www.mysql.com/)')
21
- renderArr.push('- 全栈架构,代码模板生成,快速开发')
22
- renderArr.push('- 数据库采用 Mysql, 配置见 mysql.config.js')
23
- renderArr.push('- 项目配置见 project.config.js')
24
- renderArr.push('- Next 框架配置见 next.config.js')
25
-
26
- renderArr.push('## 命令')
27
- renderArr.push('- nsgm init 初始化项目')
28
- renderArr.push('- nsgm upgrade 升级项目基础文件')
29
- renderArr.push('- nsgm create 创建模板页面')
30
- renderArr.push('- nsgm delete 删除模板页面')
31
- renderArr.push('- nsgm deletedb 删除模板页面及数据库表')
32
- renderArr.push('- nsgm dev 开发模式')
33
- renderArr.push('- nsgm start 生产模式')
34
- renderArr.push('- nsgm build 编译')
35
- renderArr.push('- nsgm export 导出静态页面')
36
21
 
37
- renderArr.push('## 参数')
38
- renderArr.push('- dictionary: 在 export/init 的时候使用, 默认 webapp, 譬如: nsgm export/init dictionary=webapp 或者 nsgm export/init webapp')
39
- renderArr.push('- controller: 在 create/delete 的时候使用, 必须有。譬如:nsgm create/delete math')
40
- renderArr.push('- action: create/delete 的时候使用, 默认 manage, 跟在 controller 后面, 譬如 nsgm create/delete math test')
22
+ const Page = ({ html }) => {
23
+ return (
24
+ <Container>
25
+ <Typography style={{ padding: '24px' }}>
26
+ <Title level={1}>NSGM CLI</Title>
27
+ <Paragraph>
28
+ <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>
33
+ </Row>
34
+ </Paragraph>
35
+ <Paragraph>
36
+ 全栈架构,代码模板生成,快速开发
37
+ </Paragraph>
41
38
 
39
+ <Card style={{ marginBottom: '24px' }}>
40
+ <Row gutter={[24, 16]}>
41
+ <Col xs={24} md={8}>
42
+ <Card type="inner" title={<><DatabaseOutlined /> 数据库配置</>}>
43
+ 数据库采用 Mysql, 配置见 mysql.config.js
44
+ </Card>
45
+ </Col>
46
+ <Col xs={24} md={8}>
47
+ <Card type="inner" title={<><SettingOutlined /> 项目配置</>}>
48
+ 项目配置见 project.config.js
49
+ </Card>
50
+ </Col>
51
+ <Col xs={24} md={8}>
52
+ <Card type="inner" title={<><CodeOutlined /> 框架配置</>}>
53
+ Next 框架配置见 next.config.js
54
+ </Card>
55
+ </Col>
56
+ </Row>
57
+ </Card>
42
58
 
43
- const Page = ({ html }) => {
59
+ <Title level={2}><BookOutlined /> 命令</Title>
60
+ <Divider />
44
61
 
45
- const createMarkup = () => {
46
- return {
47
- __html: html
48
- }
49
- }
62
+ <Row gutter={[16, 16]}>
63
+ <Col xs={24} md={8}>
64
+ <Card hoverable>
65
+ <Title level={4}>项目管理</Title>
66
+ <ul>
67
+ <li><Text strong>nsgm init</Text> - 初始化项目</li>
68
+ <li><Text strong>nsgm upgrade</Text> - 升级项目基础文件</li>
69
+ </ul>
70
+ </Card>
71
+ </Col>
72
+ <Col xs={24} md={8}>
73
+ <Card hoverable>
74
+ <Title level={4}>模板操作</Title>
75
+ <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>
79
+ </ul>
80
+ </Card>
81
+ </Col>
82
+ <Col xs={24} md={8}>
83
+ <Card hoverable>
84
+ <Title level={4}>运行与构建</Title>
85
+ <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>
90
+ </ul>
91
+ </Card>
92
+ </Col>
93
+ </Row>
50
94
 
51
- return (
52
- <Container>
53
- <div dangerouslySetInnerHTML={createMarkup()} />
95
+ <Title level={2} style={{ marginTop: '24px' }}>参数</Title>
96
+ <Divider />
97
+ <Card>
98
+ <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>
102
+ </ul>
103
+ </Card>
104
+ </Typography>
54
105
  </Container>
55
106
  )
56
107
  }
57
108
 
58
109
  Page.getInitialProps = () => {
59
- let html = ''
60
- _.each(renderArr, (item, index) => {
61
- html += md.render(item)
62
- })
63
-
64
- return {
65
- html
66
- }
110
+ return {}
67
111
  }
68
112
 
69
113
  export default Page
package/pages/login.tsx CHANGED
@@ -4,6 +4,8 @@ import { LoginContainer } from '../client/styled/common'
4
4
  // import getConfig from 'next/config'
5
5
  import React, { useState } from 'react'
6
6
  import { handleXSS } from '../client/utils/common'
7
+ import { Input, Button, Form, Typography, message } from 'antd'
8
+ import { UserOutlined, LockOutlined } from '@ant-design/icons'
7
9
 
8
10
  const md = new MarkdownIt({
9
11
  html: true,
@@ -31,11 +33,18 @@ const Page = ({ html }) => {
31
33
 
32
34
  const doLogin = () => {
33
35
  if (typeof window !== "undefined") {
34
- let locationHref = window.location.origin + "?ticket=XXX"
35
- if (userName !== '') {
36
- locationHref += "&name=" + btoa(handleXSS(userName + "," + userPassword))
37
- window.location.href = locationHref
36
+ if (userName === '') {
37
+ message.error('请输入用户名');
38
+ return;
39
+ }
40
+ if (userPassword === '') {
41
+ message.error('请输入密码');
42
+ return;
38
43
  }
44
+
45
+ let locationHref = window.location.origin + "?ticket=XXX"
46
+ locationHref += "&name=" + btoa(handleXSS(userName + "," + userPassword))
47
+ window.location.href = locationHref
39
48
  }
40
49
  }
41
50
 
@@ -50,17 +59,34 @@ const Page = ({ html }) => {
50
59
  return (
51
60
  <LoginContainer>
52
61
  <div dangerouslySetInnerHTML={createMarkup()} />
53
- <input type="text" style={{ opacity: 0, position: "absolute", width: 0, height: 0 }}></input>
54
- <input type="password" style={{ opacity: 0, position: "absolute", width: 0, height: 0 }}></input>
55
- <div className='row'>
56
- <input type="text" name="user-name" className='user-input' autoComplete='off' onChange={doChangeName} value={userName}></input>
57
- </div>
58
- <div className='row'>
59
- <input type="password" name="user-password" className='user-input' autoComplete='off' onChange={doChangePassword} value={userPassword}></input>
60
- </div>
61
- <div className='row right'>
62
- <button onClick={doLogin}>login</button>
63
- </div>
62
+ <Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 24 }}>系统登录</Typography.Title>
63
+ <Form layout="vertical" style={{ width: '100%' }}>
64
+ <Form.Item>
65
+ <Input
66
+ prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
67
+ placeholder="用户名"
68
+ size="large"
69
+ value={userName}
70
+ onChange={doChangeName}
71
+ autoComplete="off"
72
+ />
73
+ </Form.Item>
74
+ <Form.Item>
75
+ <Input.Password
76
+ prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
77
+ placeholder="密码"
78
+ size="large"
79
+ value={userPassword}
80
+ onChange={doChangePassword}
81
+ autoComplete="off"
82
+ />
83
+ </Form.Item>
84
+ <Form.Item>
85
+ <Button type="primary" onClick={doLogin} size="large" block>
86
+ 登录
87
+ </Button>
88
+ </Form.Item>
89
+ </Form>
64
90
  </LoginContainer>
65
91
  )
66
92
  }
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react'
2
2
  import { ConfigProvider, Table, Modal, Button, Input, Space, Upload, message } from 'antd'
3
3
  import { Container, SearchRow, ModalContainer } from '../../client/styled/template/manage'
4
+ import styled from 'styled-components'
4
5
  import { useDispatch, useSelector } from 'react-redux'
5
6
  import { getTemplate, addTemplate, modTemplate, delTemplate, updateSSRTemplate, searchTemplate, batchDelTemplate } from '../../client/redux/template/manage/actions'
6
7
  import { getTemplateService } from '../../client/service/template/manage'
@@ -16,12 +17,56 @@ import { saveAs } from 'file-saver'
16
17
  const pageSize = 100
17
18
  const dateFormat = 'YYYY-MM-DD'
18
19
  const currentDate = dayjs().format(dateFormat)
19
- console.log('currentDate', currentDate)
20
20
 
21
21
  const keyTitles = {
22
22
  name: '名称'
23
23
  }
24
24
 
25
+ // styled-components
26
+ const StyledButton = styled(Button) <{ $primary?: boolean; $export?: boolean; $import?: boolean; $danger?: boolean }>`
27
+ display: flex;
28
+ align-items: center;
29
+ border-radius: 6px;
30
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
31
+ ${props => props.$export && `
32
+ background-color: #f6ffed;
33
+ color: #52c41a;
34
+ border-color: #b7eb8f;
35
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
36
+ transition: all 0.3s ease;
37
+ `}
38
+ ${props => props.$import && `
39
+ background-color: #e6f7ff;
40
+ color: #1890ff;
41
+ border-color: #91d5ff;
42
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
43
+ transition: all 0.3s ease;
44
+ `}
45
+ ${props => props.$danger && `
46
+ background-color: #fff1f0;
47
+ border-color: #ffa39e;
48
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
49
+ transition: all 0.3s ease;
50
+ `}
51
+ `
52
+ const StyledInput = styled(Input)`
53
+ width: 200px;
54
+ border-radius: 6px;
55
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
56
+ `
57
+ const StyledTable = styled(Table)`
58
+ margin-top: 16px;
59
+ border-radius: 8px;
60
+ overflow: hidden;
61
+ `
62
+ const ModalTitle = styled.div`
63
+ color: #1890ff;
64
+ font-weight: 500;
65
+ `
66
+ const ModalInput = styled(Input)`
67
+ border-radius: 4px;
68
+ `
69
+
25
70
  const Page = ({ template }) => {
26
71
  const dispatch = useDispatch()
27
72
  const [isModalVisiable, setIsModalVisible] = useState(false)
@@ -36,14 +81,12 @@ const Page = ({ template }) => {
36
81
 
37
82
  const state = useSelector((state: RootState) => state)
38
83
  const { templateManage }: any = state
39
- console.log('templateManage', templateManage)
40
84
 
41
85
  if (!templateManage.firstLoadFlag) {
42
86
  template = templateManage.template
43
87
  }
44
88
 
45
89
  const { totalCounts, items: templateItems } = _.cloneDeep(template)
46
- console.log('template', template)
47
90
 
48
91
  _.each(templateItems, (item, index) => {
49
92
  const { id } = item
@@ -58,7 +101,9 @@ const Page = ({ template }) => {
58
101
  key: 'id',
59
102
  sorter: (a: any, b: any) => a.id - b.id,
60
103
  sortDirections: ['descend', 'ascend'],
61
- showSorterTooltip: false
104
+ showSorterTooltip: false,
105
+ width: '15%',
106
+ align: 'center'
62
107
  },
63
108
  {
64
109
  title: keyTitles.name,
@@ -66,23 +111,25 @@ const Page = ({ template }) => {
66
111
  key: 'name',
67
112
  sorter: (a: any, b: any) => a.name.length - b.name.length,
68
113
  sortDirections: ['descend', 'ascend'],
69
- showSorterTooltip: false
114
+ showSorterTooltip: false,
115
+ width: '60%',
116
+ ellipsis: true
70
117
  },
71
118
  {
72
119
  title: '操作',
73
120
  dataIndex: '',
121
+ width: '25%',
122
+ align: 'center',
74
123
  render: (_: any, record: any) => {
75
124
  return (
76
- <Space>
77
- <Button onClick={() => {
78
- console.log('record', record)
125
+ <Space size="small">
126
+ <Button type="primary" size="small" onClick={() => {
79
127
  updateTemplate(record)
80
- }}>修改</Button>
81
- <Button onClick={() => {
82
- console.log('record', record)
128
+ }} style={{ borderRadius: '4px' }}>修改</Button>
129
+ <Button danger size="small" onClick={() => {
83
130
  const { id } = record
84
131
  deleteTemplate(id)
85
- }}>删除</Button>
132
+ }} style={{ borderRadius: '4px' }}>删除</Button>
86
133
  </Space>
87
134
  )
88
135
  }
@@ -91,7 +138,7 @@ const Page = ({ template }) => {
91
138
 
92
139
  const rowSelection = {
93
140
  onChange: (selectedRowKeys: any, selectedRows: any) => {
94
- console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
141
+ //
95
142
  setBatchDelIds(selectedRowKeys)
96
143
  }
97
144
  }
@@ -138,7 +185,7 @@ const Page = ({ template }) => {
138
185
  const modalObj = {
139
186
  name: handleXSS(modalName)
140
187
  }
141
- console.log('handleOk', modalObj)
188
+ //
142
189
 
143
190
  const checkResult = checkModalObj(modalObj)
144
191
 
@@ -192,8 +239,8 @@ const Page = ({ template }) => {
192
239
  wb.xlsx.writeBuffer().then((data) => {
193
240
  const blob = new Blob([data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
194
241
  saveAs(blob, "Template.xlsx")
195
- }).catch((error) => {
196
- console.error('导出失败', error?.message)
242
+ }).catch(() => {
243
+ // 导出失败
197
244
  })
198
245
  } else {
199
246
  message.info("没有数据无需导出")
@@ -204,9 +251,7 @@ const Page = ({ template }) => {
204
251
  name: 'file',
205
252
  action: '/rest/template/import',
206
253
  onChange(info: any) {
207
- if (info.file.status !== 'uploading') {
208
- console.log(info.file, info.fileList)
209
- }
254
+ //
210
255
 
211
256
  if (info.file.status === 'done') {
212
257
  message.success(`${info.file.name} 文件上传成功`)
@@ -236,43 +281,86 @@ const Page = ({ template }) => {
236
281
 
237
282
  return (
238
283
  <Container>
284
+ <div className="page-title">模板管理</div>
239
285
  <ConfigProvider locale={locale}>
240
286
  <SearchRow>
241
- <Space>
242
- <Button type="primary" onClick={createTemplate}>
243
- 新增
244
- </Button>
245
- <Input value={searchName} placeholder="" onChange={(e) => setSearchName(e.target.value)} />
246
- <Button type="primary" onClick={doSearch}>
247
- 搜索
248
- </Button>
249
- <Button type="primary" onClick={exportTemplate}>
250
- 导出
251
- </Button>
252
- <Upload {...uploadProps}>
253
- <Button icon={<UploadOutlined rev={undefined} />}>导入</Button>
254
- </Upload>
255
- <Button type="primary" onClick={batchDeleteTemplate}>
256
- 批量删除
257
- </Button>
287
+ <Space size="middle" wrap>
288
+ <Space size="small">
289
+ <StyledButton type="primary" onClick={createTemplate} $primary>
290
+ <i className="fa fa-plus" style={{ marginRight: '5px' }}></i>
291
+ 新增
292
+ </StyledButton>
293
+ <StyledInput
294
+ value={searchName}
295
+ placeholder="请输入名称搜索"
296
+ allowClear
297
+ onChange={(e) => setSearchName(e.target.value)}
298
+ onPressEnter={doSearch}
299
+ />
300
+ <StyledButton type="primary" onClick={doSearch} $primary>
301
+ <i className="fa fa-search" style={{ marginRight: '5px' }}></i>
302
+ 搜索
303
+ </StyledButton>
304
+ </Space>
305
+ <Space size="small">
306
+ <StyledButton onClick={exportTemplate} icon={<UploadOutlined rev={undefined} rotate={180} />} $export>
307
+ 导出
308
+ </StyledButton>
309
+ <Upload {...uploadProps}>
310
+ <StyledButton icon={<UploadOutlined rev={undefined} />} $import>
311
+ 导入
312
+ </StyledButton>
313
+ </Upload>
314
+ <StyledButton danger onClick={batchDeleteTemplate} $danger>
315
+ 批量删除
316
+ </StyledButton>
317
+ </Space>
258
318
  </Space>
259
319
  </SearchRow>
260
- <Table rowSelection={{
261
- type: 'checkbox',
262
- ...rowSelection,
263
- }} dataSource={dataSource} columns={columns} pagination={{
264
- total: totalCounts,
265
- pageSize: pageSize,
266
- onChange: (page, pageSize) => {
267
- console.log('onChange', page, pageSize)
268
- dispatch(searchTemplate(page - 1, pageSize, { name: handleXSS(searchName) }))
269
- }
270
- }} />
271
- <Modal title={(modalId == 0 ? "新增" : "修改") + " template"} open={isModalVisiable} onOk={handleOk} onCancel={handleCancel} okText="确认" cancelText="取消">
320
+ <StyledTable
321
+ rowSelection={{
322
+ type: 'checkbox',
323
+ ...rowSelection,
324
+ }}
325
+ dataSource={dataSource}
326
+ columns={columns}
327
+ bordered
328
+ rowClassName={(record, index) => index % 2 === 0 ? 'table-row-light' : 'table-row-dark'}
329
+ pagination={{
330
+ total: totalCounts,
331
+ pageSize: pageSize,
332
+ showSizeChanger: false,
333
+ showQuickJumper: true,
334
+ showTotal: (total) => `共 ${total} 条记录`,
335
+ onChange: (page, pageSize) => {
336
+ dispatch(searchTemplate(page - 1, pageSize, { name: handleXSS(searchName) }))
337
+ },
338
+ style: { marginTop: '16px', marginBottom: '16px' }
339
+ }}
340
+ />
341
+ <Modal
342
+ title={<ModalTitle>{(modalId == 0 ? "新增" : "修改") + " 模板"}</ModalTitle>}
343
+ open={isModalVisiable}
344
+ onOk={handleOk}
345
+ onCancel={handleCancel}
346
+ okText="确认"
347
+ cancelText="取消"
348
+ centered
349
+ maskClosable={false}
350
+ destroyOnClose
351
+ okButtonProps={{ style: { borderRadius: '4px' } }}
352
+ cancelButtonProps={{ style: { borderRadius: '4px' } }}
353
+ >
272
354
  <ModalContainer>
273
355
  <div className="line">
274
- <label>{keyTitles.name}</label>
275
- <Input value={modalName} placeholder="" onChange={(e) => setModalName(e.target.value)} />
356
+ <label>{keyTitles.name}:</label>
357
+ <ModalInput
358
+ value={modalName}
359
+ placeholder="请输入名称"
360
+ allowClear
361
+ autoFocus
362
+ onChange={(e) => setModalName(e.target.value)}
363
+ />
276
364
  </div>
277
365
  </ModalContainer>
278
366
  </Modal>
@@ -285,13 +373,10 @@ Page.getInitialProps = async () => {
285
373
  let template = null
286
374
 
287
375
  await getTemplateService(0, pageSize).then((res: any) => {
288
- console.log('res', res)
289
376
  const { data } = res
290
377
  template = data.template
291
378
  })
292
379
 
293
- console.log('template-getInitialProps', template)
294
-
295
380
  return {
296
381
  template
297
382
  }