mirror of
				https://github.com/whyour/qinglong.git
				synced 2025-10-31 08:56:06 +08:00 
			
		
		
		
	手机端使用codemirror
This commit is contained in:
		
							parent
							
								
									38609feee9
								
							
						
					
					
						commit
						9728119101
					
				|  | @ -29,10 +29,15 @@ export default defineConfig({ | |||
|     react: 'window.React', | ||||
|     'react-dom': 'window.ReactDOM', | ||||
|     darkreader: 'window.DarkReader', | ||||
|     codemirror: 'window.CodeMirror', | ||||
|   }, | ||||
|   scripts: [ | ||||
|     'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js', | ||||
|     'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js', | ||||
|     'https://cdn.jsdelivr.net/npm/darkreader@4.9.34/darkreader.min.js', | ||||
|     'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/lib/codemirror.min.js', | ||||
|     'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/shell/shell.js', | ||||
|     'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/python/python.js', | ||||
|     'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/javascript/javascript.js', | ||||
|   ], | ||||
| }); | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ export default (app: Router) => { | |||
|               ); | ||||
|               return res.send({ | ||||
|                 code: 100, | ||||
|                 msg: '已初始化密码,请前往auth.json查看并重新登录', | ||||
|                 message: '已初始化密码,请前往auth.json查看并重新登录', | ||||
|               }); | ||||
|             } | ||||
|             if ( | ||||
|  | @ -57,10 +57,10 @@ export default (app: Router) => { | |||
|               ); | ||||
|               res.send({ code: 200, token }); | ||||
|             } else { | ||||
|               res.send({ code: 400, msg: config.authError }); | ||||
|               res.send({ code: 400, message: config.authError }); | ||||
|             } | ||||
|           } else { | ||||
|             res.send({ err: 400, msg: '请输入用户名密码!' }); | ||||
|             res.send({ err: 400, message: '请输入用户名密码!' }); | ||||
|           } | ||||
|         }); | ||||
|       } catch (e) { | ||||
|  | @ -101,7 +101,7 @@ export default (app: Router) => { | |||
|       try { | ||||
|         fs.writeFile(config.authConfigFile, JSON.stringify(req.body), (err) => { | ||||
|           if (err) console.log(err); | ||||
|           res.send({ code: 200, msg: '更新成功' }); | ||||
|           res.send({ code: 200, message: '更新成功' }); | ||||
|         }); | ||||
|       } catch (e) { | ||||
|         logger.error('🔥 error: %o', e); | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ export default (app: Router) => { | |||
|         const { name, content } = req.body; | ||||
|         const path = `${config.configPath}${name}`; | ||||
|         fs.writeFileSync(path, content); | ||||
|         res.send({ code: 200, msg: '保存成功' }); | ||||
|         res.send({ code: 200, message: '保存成功' }); | ||||
|       } catch (e) { | ||||
|         logger.error('🔥 error: %o', e); | ||||
|         return next(e); | ||||
|  |  | |||
|  | @ -48,4 +48,44 @@ export default (app: Router) => { | |||
|       } | ||||
|     }, | ||||
|   ); | ||||
| 
 | ||||
|   route.post( | ||||
|     '/scripts', | ||||
|     celebrate({ | ||||
|       body: Joi.object({ | ||||
|         filename: Joi.string().required(), | ||||
|         path: Joi.string().required(), | ||||
|         content: Joi.string().required(), | ||||
|       }), | ||||
|     }), | ||||
|     async (req: Request, res: Response, next: NextFunction) => { | ||||
|       const logger: Logger = Container.get('logger'); | ||||
|       try { | ||||
|         let { filename, path, content } = req.body as { | ||||
|           filename: string; | ||||
|           path: string; | ||||
|           content: string; | ||||
|         }; | ||||
|         if (!path.endsWith('/')) { | ||||
|           path += '/'; | ||||
|         } | ||||
|         if (config.writePathList.every((x) => !path.startsWith(x))) { | ||||
|           return res.send({ code: 400, data: '文件路径错误,可保存目录/ql/scripts、/ql/config、/ql/jbot、/ql/bak' }); | ||||
|         } | ||||
|         const filePath = `${path}${filename.replace(/\//g, '')}`; | ||||
|         const bakPath = '/ql/bak'; | ||||
|         if (fs.existsSync(filePath)) { | ||||
|           if (!fs.existsSync(bakPath)) { | ||||
|             fs.mkdirSync(bakPath); | ||||
|           } | ||||
|           fs.copyFileSync(filePath, bakPath); | ||||
|         } | ||||
|         fs.writeFileSync(filePath, content); | ||||
|         return res.send({ code: 200 }); | ||||
|       } catch (e) { | ||||
|         logger.error('🔥 error: %o', e); | ||||
|         return next(e); | ||||
|       } | ||||
|     }, | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -67,4 +67,10 @@ export default { | |||
|     'crontab.list', | ||||
|     'env.sh', | ||||
|   ], | ||||
|   writePathList: [ | ||||
|     '/ql/scripts/', | ||||
|     '/ql/config/', | ||||
|     '/ql/jbot/', | ||||
|     '/ql/bak/', | ||||
|   ], | ||||
| }; | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ | |||
|     ] | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@monaco-editor/react": "4.1.3", | ||||
|     "body-parser": "^1.19.0", | ||||
|     "celebrate": "^13.0.3", | ||||
|     "cors": "^2.8.5", | ||||
|  | @ -45,6 +44,7 @@ | |||
|   "devDependencies": { | ||||
|     "@ant-design/icons": "^4.6.2", | ||||
|     "@ant-design/pro-layout": "^6.5.0", | ||||
|     "@monaco-editor/react": "^4.2.1", | ||||
|     "@types/cors": "^2.8.10", | ||||
|     "@types/express": "^4.17.8", | ||||
|     "@types/express-jwt": "^6.0.1", | ||||
|  | @ -58,6 +58,7 @@ | |||
|     "@types/react-dom": "^17.0.0", | ||||
|     "@umijs/plugin-antd": "^0.9.1", | ||||
|     "@umijs/test": "^3.3.9", | ||||
|     "codemirror": "^5.62.2", | ||||
|     "compression-webpack-plugin": "6.1.1", | ||||
|     "darkreader": "^4.9.27", | ||||
|     "lint-staged": "^10.0.7", | ||||
|  | @ -65,6 +66,7 @@ | |||
|     "prettier": "^2.2.0", | ||||
|     "qrcode.react": "^1.0.1", | ||||
|     "react": "17.x", | ||||
|     "react-codemirror2": "^7.2.1", | ||||
|     "react-diff-viewer": "^3.1.1", | ||||
|     "react-dnd": "^14.0.2", | ||||
|     "react-dnd-html5-backend": "^14.0.0", | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ lockfileVersion: 5.3 | |||
| specifiers: | ||||
|   '@ant-design/icons': ^4.6.2 | ||||
|   '@ant-design/pro-layout': ^6.5.0 | ||||
|   '@monaco-editor/react': 4.1.3 | ||||
|   '@monaco-editor/react': ^4.2.1 | ||||
|   '@types/cors': ^2.8.10 | ||||
|   '@types/express': ^4.17.8 | ||||
|   '@types/express-jwt': ^6.0.1 | ||||
|  | @ -19,6 +19,7 @@ specifiers: | |||
|   '@umijs/test': ^3.3.9 | ||||
|   body-parser: ^1.19.0 | ||||
|   celebrate: ^13.0.3 | ||||
|   codemirror: ^5.62.2 | ||||
|   compression-webpack-plugin: 6.1.1 | ||||
|   cors: ^2.8.5 | ||||
|   cron-parser: ^3.5.0 | ||||
|  | @ -37,6 +38,7 @@ specifiers: | |||
|   prettier: ^2.2.0 | ||||
|   qrcode.react: ^1.0.1 | ||||
|   react: 17.x | ||||
|   react-codemirror2: ^7.2.1 | ||||
|   react-diff-viewer: ^3.1.1 | ||||
|   react-dnd: ^14.0.2 | ||||
|   react-dnd-html5-backend: ^14.0.0 | ||||
|  | @ -54,7 +56,6 @@ specifiers: | |||
|   yorkie: ^2.0.0 | ||||
| 
 | ||||
| dependencies: | ||||
|   '@monaco-editor/react': 4.1.3_react-dom@17.0.2+react@17.0.2 | ||||
|   body-parser: 1.19.0 | ||||
|   celebrate: 13.0.4 | ||||
|   cors: 2.8.5 | ||||
|  | @ -75,6 +76,7 @@ dependencies: | |||
| devDependencies: | ||||
|   '@ant-design/icons': 4.6.2_react-dom@17.0.2+react@17.0.2 | ||||
|   '@ant-design/pro-layout': 6.18.0_react-dom@17.0.2+react@17.0.2 | ||||
|   '@monaco-editor/react': 4.2.1_react-dom@17.0.2+react@17.0.2 | ||||
|   '@types/cors': 2.8.10 | ||||
|   '@types/express': 4.17.11 | ||||
|   '@types/express-jwt': 6.0.1 | ||||
|  | @ -88,6 +90,7 @@ devDependencies: | |||
|   '@types/react-dom': 17.0.5 | ||||
|   '@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75 | ||||
|   '@umijs/test': 3.4.20_ts-node@9.1.1 | ||||
|   codemirror: 5.62.2 | ||||
|   compression-webpack-plugin: 6.1.1_webpack@5.37.0 | ||||
|   darkreader: 4.9.32 | ||||
|   lint-staged: 10.5.4 | ||||
|  | @ -95,6 +98,7 @@ devDependencies: | |||
|   prettier: 2.3.0 | ||||
|   qrcode.react: 1.0.1_react@17.0.2 | ||||
|   react: 17.0.2 | ||||
|   react-codemirror2: 7.2.1_codemirror@5.62.2+react@17.0.2 | ||||
|   react-diff-viewer: 3.1.1_react-dom@17.0.2+react@17.0.2 | ||||
|   react-dnd: 14.0.2_695545ed68ea337339babea285839fc0 | ||||
|   react-dnd-html5-backend: 14.0.0 | ||||
|  | @ -872,12 +876,12 @@ packages: | |||
|       monaco-editor: '>= 0.21.0 < 1' | ||||
|     dependencies: | ||||
|       state-local: 1.0.7 | ||||
|     dev: false | ||||
|     dev: true | ||||
| 
 | ||||
|   /@monaco-editor/react/4.1.3_react-dom@17.0.2+react@17.0.2: | ||||
|     resolution: {integrity: sha512-kqcjVuoy6btcgALAk4RV/SlasveM+WTw5lzzlyq5FhKXjF8wu5tSe/2oCQ1uhLpcdtxcHfx3L0HrcAPWnejFnQ==} | ||||
|   /@monaco-editor/react/4.2.1_react-dom@17.0.2+react@17.0.2: | ||||
|     resolution: {integrity: sha512-yN8qVY0PyFIbqPjfrZ5TbR/wrcfeiwoys8+0QkmyfiOzG74vXxSBOPIUxk7Ly+qCj7qWHPq1uDJskzFGaIqaPA==} | ||||
|     peerDependencies: | ||||
|       monaco-editor: ^0.23.0 | ||||
|       monaco-editor: '>= 0.25.0 < 1' | ||||
|       react: ^16.8.0 || ^17.0.0 | ||||
|       react-dom: ^16.8.0 || ^17.0.0 | ||||
|     dependencies: | ||||
|  | @ -886,7 +890,7 @@ packages: | |||
|       react: 17.0.2 | ||||
|       react-dom: 17.0.2_react@17.0.2 | ||||
|       state-local: 1.0.7 | ||||
|     dev: false | ||||
|     dev: true | ||||
| 
 | ||||
|   /@npmcli/move-file/1.1.2: | ||||
|     resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} | ||||
|  | @ -2587,6 +2591,10 @@ packages: | |||
|     engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /codemirror/5.62.2: | ||||
|     resolution: {integrity: sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /collect-v8-coverage/1.0.1: | ||||
|     resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} | ||||
|     dev: true | ||||
|  | @ -5303,6 +5311,7 @@ packages: | |||
| 
 | ||||
|   /js-tokens/4.0.0: | ||||
|     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /js-yaml/3.14.1: | ||||
|     resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} | ||||
|  | @ -5715,6 +5724,7 @@ packages: | |||
|     hasBin: true | ||||
|     dependencies: | ||||
|       js-tokens: 4.0.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /lowercase-keys/1.0.1: | ||||
|     resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} | ||||
|  | @ -6954,6 +6964,7 @@ packages: | |||
|       loose-envify: 1.4.0 | ||||
|       object-assign: 4.1.1 | ||||
|       react-is: 16.13.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /proxy-addr/2.0.6: | ||||
|     resolution: {integrity: sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==} | ||||
|  | @ -7719,6 +7730,16 @@ packages: | |||
|       strip-json-comments: 2.0.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /react-codemirror2/7.2.1_codemirror@5.62.2+react@17.0.2: | ||||
|     resolution: {integrity: sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==} | ||||
|     peerDependencies: | ||||
|       codemirror: 5.x | ||||
|       react: '>=15.5 <=16.x' | ||||
|     dependencies: | ||||
|       codemirror: 5.62.2 | ||||
|       react: 17.0.2 | ||||
|     dev: true | ||||
| 
 | ||||
|   /react-diff-viewer/3.1.1_react-dom@17.0.2+react@17.0.2: | ||||
|     resolution: {integrity: sha512-rmvwNdcClp6ZWdS11m1m01UnBA4OwYaLG/li0dB781e/bQEzsGyj+qewVd6W5ztBwseQ72pO7nwaCcq5jnlzcw==} | ||||
|     engines: {node: '>= 8'} | ||||
|  | @ -7798,6 +7819,7 @@ packages: | |||
| 
 | ||||
|   /react-is/16.13.1: | ||||
|     resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /react-is/17.0.2: | ||||
|     resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} | ||||
|  | @ -8732,7 +8754,7 @@ packages: | |||
| 
 | ||||
|   /state-local/1.0.7: | ||||
|     resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} | ||||
|     dev: false | ||||
|     dev: true | ||||
| 
 | ||||
|   /static-extend/0.1.2: | ||||
|     resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=} | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| @import '~@/styles/variable.less'; | ||||
| @import '~codemirror/lib/codemirror.css'; | ||||
| 
 | ||||
| @font-face { | ||||
|   font-family: 'Source Code Pro'; | ||||
|  | @ -13,7 +14,7 @@ body { | |||
|   background-color: rgb(248, 248, 248); | ||||
| } | ||||
| 
 | ||||
| .ant-modal { | ||||
| .log-modal .ant-modal { | ||||
|   padding-bottom: 0 !important; | ||||
|   width: 580px !important; | ||||
| } | ||||
|  | @ -21,22 +22,14 @@ body { | |||
| .monaco-editor:not(.rename-box) { | ||||
|   height: calc(100vh - 128px) !important; | ||||
|   height: calc(100vh - var(--vh-offset, 0px) - 128px) !important; | ||||
|   .view-overlays .current-line{ | ||||
|   .view-overlays .current-line { | ||||
|     border-width: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .log-modal { | ||||
|   .monaco-editor:not(.rename-box) { | ||||
|     height: calc(100vh - 176px) !important; | ||||
|     height: calc(100vh - var(--vh-offset, 0px) - 176px) !important; | ||||
|     background-color: transparent !important; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .rename-box  { | ||||
| .rename-box { | ||||
|   height: 0; | ||||
|   .rename-input{ | ||||
|   .rename-input { | ||||
|     height: 0; | ||||
|     padding: 0 !important; | ||||
|   } | ||||
|  | @ -144,12 +137,17 @@ input:-webkit-autofill:active { | |||
|         height: calc(100vh - 184px); | ||||
|         height: calc(100vh - var(--vh-offset, 0px) - 184px); | ||||
|       } | ||||
|       .monaco-editor:not(.rename-box) { | ||||
|       .monaco-editor:not(.rename-box), | ||||
|       .CodeMirror { | ||||
|         height: calc(100vh - 216px) !important; | ||||
|         height: calc(100vh - var(--vh-offset, 0px) - 216px) !important; | ||||
|       } | ||||
|       .CodeMirror { | ||||
|         width: calc(100vw - 80px); | ||||
|       } | ||||
|     } | ||||
|     .monaco-editor:not(.rename-box) { | ||||
|     .monaco-editor:not(.rename-box), | ||||
|     .CodeMirror { | ||||
|       height: calc(100vh - 176px) !important; | ||||
|       height: calc(100vh - var(--vh-offset, 0px) - 176px) !important; | ||||
|     } | ||||
|  | @ -166,3 +164,59 @@ input:-webkit-autofill:active { | |||
|     min-height: calc(100vh - var(--vh-offset, 0px) - 72px); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .Resizer { | ||||
|   background: #000; | ||||
|   opacity: 0.2; | ||||
|   z-index: 1; | ||||
|   -moz-box-sizing: border-box; | ||||
|   -webkit-box-sizing: border-box; | ||||
|   box-sizing: border-box; | ||||
|   -moz-background-clip: padding; | ||||
|   -webkit-background-clip: padding; | ||||
|   background-clip: padding-box; | ||||
| } | ||||
| 
 | ||||
| .Resizer:hover { | ||||
|   -webkit-transition: all 2s ease; | ||||
|   transition: all 2s ease; | ||||
| } | ||||
| 
 | ||||
| .Resizer.horizontal { | ||||
|   height: 11px; | ||||
|   margin: -5px 0; | ||||
|   border-top: 5px solid rgba(255, 255, 255, 0); | ||||
|   border-bottom: 5px solid rgba(255, 255, 255, 0); | ||||
|   cursor: row-resize; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .Resizer.horizontal:hover { | ||||
|   border-top: 5px solid rgba(0, 0, 0, 0.5); | ||||
|   border-bottom: 5px solid rgba(0, 0, 0, 0.5); | ||||
| } | ||||
| 
 | ||||
| .Resizer.vertical { | ||||
|   width: 11px; | ||||
|   margin: 0 -5px; | ||||
|   border-left: 5px solid rgba(255, 255, 255, 0); | ||||
|   border-right: 5px solid rgba(255, 255, 255, 0); | ||||
|   cursor: col-resize; | ||||
| } | ||||
| 
 | ||||
| .Resizer.vertical:hover { | ||||
|   border-left: 5px solid rgba(0, 0, 0, 0.5); | ||||
|   border-right: 5px solid rgba(0, 0, 0, 0.5); | ||||
| } | ||||
| .Resizer.disabled { | ||||
|   cursor: not-allowed; | ||||
| } | ||||
| .Resizer.disabled:hover { | ||||
|   border-color: transparent; | ||||
| } | ||||
| 
 | ||||
| .edit-modal { | ||||
|   .ant-drawer-body { | ||||
|     padding: 0; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -65,6 +65,8 @@ export default function (props: any) { | |||
|   const isSafari = | ||||
|     navigator.userAgent.includes('Safari') && | ||||
|     !navigator.userAgent.includes('Chrome'); | ||||
|   const isQQBrowser = navigator.userAgent.includes('QQBrowser'); | ||||
| 
 | ||||
|   return ( | ||||
|     <ProLayout | ||||
|       selectedKeys={[props.location.pathname]} | ||||
|  | @ -76,8 +78,9 @@ export default function (props: any) { | |||
|               style={{ | ||||
|                 fontSize: isFirefox ? 9 : 12, | ||||
|                 color: '#666', | ||||
|                 marginLeft: 5, | ||||
|                 marginLeft: 2, | ||||
|                 zoom: isSafari ? 0.66 : 0.8, | ||||
|                 letterSpacing: isQQBrowser ? -2 : 0, | ||||
|               }} | ||||
|             > | ||||
|               {version} | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import config from '@/utils/config'; | |||
| import { PageContainer } from '@ant-design/pro-layout'; | ||||
| import { request } from '@/utils/http'; | ||||
| import Editor from '@monaco-editor/react'; | ||||
| import { Controlled as CodeMirror } from 'react-codemirror2'; | ||||
| 
 | ||||
| const Config = () => { | ||||
|   const [width, setWidth] = useState('100%'); | ||||
|  | @ -15,6 +16,7 @@ const Config = () => { | |||
|   const [select, setSelect] = useState('config.sh'); | ||||
|   const [data, setData] = useState<any[]>([]); | ||||
|   const [theme, setTheme] = useState<string>(''); | ||||
|   const [isPhone, setIsPhone] = useState(false); | ||||
| 
 | ||||
|   const getConfig = (name: string) => { | ||||
|     request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => { | ||||
|  | @ -38,7 +40,7 @@ const Config = () => { | |||
|         data: { content: value, name: select }, | ||||
|       }) | ||||
|       .then((data: any) => { | ||||
|         message.success(data.msg); | ||||
|         message.success(data.message); | ||||
|       }); | ||||
|   }; | ||||
| 
 | ||||
|  | @ -53,10 +55,12 @@ const Config = () => { | |||
|       setWidth('auto'); | ||||
|       setMarginLeft(0); | ||||
|       setMarginTop(0); | ||||
|       setIsPhone(true); | ||||
|     } else { | ||||
|       setWidth('100%'); | ||||
|       setMarginLeft(0); | ||||
|       setMarginTop(-72); | ||||
|       setIsPhone(false); | ||||
|     } | ||||
|     getFiles(); | ||||
|     getConfig('config.sh'); | ||||
|  | @ -111,21 +115,37 @@ const Config = () => { | |||
|         }, | ||||
|       }} | ||||
|     > | ||||
|       <Editor | ||||
|         defaultLanguage="shell" | ||||
|         value={value} | ||||
|         theme={theme} | ||||
|         options={{ | ||||
|           fontSize: 12, | ||||
|           minimap: { enabled: width === '100%' }, | ||||
|           lineNumbersMinChars: 3, | ||||
|           folding: false, | ||||
|           glyphMargin: false, | ||||
|         }} | ||||
|         onChange={(val) => { | ||||
|           setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|         }} | ||||
|       /> | ||||
|       {isPhone ? ( | ||||
|         <CodeMirror | ||||
|           value={value} | ||||
|           options={{ | ||||
|             lineNumbers: true, | ||||
|             styleActiveLine: true, | ||||
|             matchBrackets: true, | ||||
|             mode: 'shell', | ||||
|           }} | ||||
|           onBeforeChange={(editor, data, value) => { | ||||
|             setValue(value); | ||||
|           }} | ||||
|           onChange={(editor, data, value) => {}} | ||||
|         /> | ||||
|       ) : ( | ||||
|         <Editor | ||||
|           defaultLanguage="shell" | ||||
|           value={value} | ||||
|           theme={theme} | ||||
|           options={{ | ||||
|             fontSize: 12, | ||||
|             minimap: { enabled: width === '100%' }, | ||||
|             lineNumbersMinChars: 3, | ||||
|             folding: false, | ||||
|             glyphMargin: false, | ||||
|           }} | ||||
|           onChange={(val) => { | ||||
|             setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|           }} | ||||
|         /> | ||||
|       )} | ||||
|     </PageContainer> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -96,6 +96,7 @@ const CronLogModal = ({ | |||
|       title={titleElement()} | ||||
|       visible={visible} | ||||
|       centered | ||||
|       className="log-modal" | ||||
|       bodyStyle={{ | ||||
|         overflowY: 'auto', | ||||
|         maxHeight: 'calc(80vh - var(--vh-offset, 0px))', | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ import { Button, message, Modal } from 'antd'; | |||
| import config from '@/utils/config'; | ||||
| import { PageContainer } from '@ant-design/pro-layout'; | ||||
| import { request } from '@/utils/http'; | ||||
| import ReactDiffViewer from 'react-diff-viewer'; | ||||
| import './index.less'; | ||||
| import { DiffEditor } from "@monaco-editor/react"; | ||||
| import { DiffEditor } from '@monaco-editor/react'; | ||||
| import ReactDiffViewer from 'react-diff-viewer'; | ||||
| 
 | ||||
| const Crontab = () => { | ||||
|   const [width, setWidth] = useState('100%'); | ||||
|  | @ -15,6 +15,7 @@ const Crontab = () => { | |||
|   const [sample, setSample] = useState(''); | ||||
|   const [loading, setLoading] = useState(true); | ||||
|   const [theme, setTheme] = useState<string>(''); | ||||
|   const [isPhone, setIsPhone] = useState(false); | ||||
| 
 | ||||
|   const getConfig = () => { | ||||
|     request.get(`${config.apiPrefix}configs/config.sh`).then((data) => { | ||||
|  | @ -37,30 +38,33 @@ const Crontab = () => { | |||
|       setWidth('auto'); | ||||
|       setMarginLeft(0); | ||||
|       setMarginTop(0); | ||||
|       setIsPhone(true); | ||||
|     } else { | ||||
|       setWidth('100%'); | ||||
|       setMarginLeft(0); | ||||
|       setMarginTop(-72); | ||||
|       setIsPhone(false); | ||||
|     } | ||||
|     getConfig(); | ||||
|     getSample(); | ||||
|   }, []); | ||||
| 
 | ||||
|   useEffect(()=>{ | ||||
|   useEffect(() => { | ||||
|     const media = window.matchMedia('(prefers-color-scheme: dark)'); | ||||
|     const storageTheme = localStorage.getItem('qinglong_dark_theme'); | ||||
|     const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark'; | ||||
|     setTheme(isDark?'vs-dark':'vs'); | ||||
|     media.addEventListener('change',(e)=>{ | ||||
|       if(storageTheme === 'auto' || !storageTheme){ | ||||
|         if(e.matches){ | ||||
|           setTheme('vs-dark') | ||||
|         }else{ | ||||
|     const isDark = | ||||
|       (media.matches && storageTheme !== 'light') || storageTheme === 'dark'; | ||||
|     setTheme(isDark ? 'vs-dark' : 'vs'); | ||||
|     media.addEventListener('change', (e) => { | ||||
|       if (storageTheme === 'auto' || !storageTheme) { | ||||
|         if (e.matches) { | ||||
|           setTheme('vs-dark'); | ||||
|         } else { | ||||
|           setTheme('vs'); | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|   },[]) | ||||
|     }); | ||||
|   }, []); | ||||
| 
 | ||||
|   return ( | ||||
|     <PageContainer | ||||
|  | @ -80,22 +84,50 @@ const Crontab = () => { | |||
|         }, | ||||
|       }} | ||||
|     > | ||||
|       <DiffEditor | ||||
|         language={"shell"} | ||||
|         original={sample} | ||||
|         modified={value} | ||||
|         options={{ | ||||
|           readOnly: true, | ||||
|           fontSize: 12, | ||||
|           minimap: {enabled: width==='100%'}, | ||||
|           lineNumbersMinChars: 3, | ||||
|           folding: false, | ||||
|           glyphMargin: false, | ||||
|           renderSideBySide: width==='100%', | ||||
|           wordWrap: 'on' | ||||
|         }} | ||||
|         theme={theme} | ||||
|       /> | ||||
|       {isPhone ? ( | ||||
|         <ReactDiffViewer | ||||
|           styles={{ | ||||
|             diffContainer: { | ||||
|               overflowX: 'auto', | ||||
|               minWidth: 768, | ||||
|             }, | ||||
|             diffRemoved: { | ||||
|               overflowX: 'auto', | ||||
|               maxWidth: 300, | ||||
|             }, | ||||
|             diffAdded: { | ||||
|               overflowX: 'auto', | ||||
|               maxWidth: 300, | ||||
|             }, | ||||
|             line: { | ||||
|               wordBreak: 'break-word', | ||||
|             }, | ||||
|           }} | ||||
|           oldValue={value} | ||||
|           newValue={sample} | ||||
|           splitView={true} | ||||
|           leftTitle="config.sh" | ||||
|           rightTitle="config.sample.sh" | ||||
|           disableWordDiff={true} | ||||
|         /> | ||||
|       ) : ( | ||||
|         <DiffEditor | ||||
|           language={'shell'} | ||||
|           original={sample} | ||||
|           modified={value} | ||||
|           options={{ | ||||
|             readOnly: true, | ||||
|             fontSize: 12, | ||||
|             minimap: { enabled: width === '100%' }, | ||||
|             lineNumbersMinChars: 3, | ||||
|             folding: false, | ||||
|             glyphMargin: false, | ||||
|             renderSideBySide: width === '100%', | ||||
|             wordWrap: 'on', | ||||
|           }} | ||||
|           theme={theme} | ||||
|         /> | ||||
|       )} | ||||
|     </PageContainer> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -27,5 +27,9 @@ | |||
|     .ant-pro-grid-content.wide .ant-pro-page-container-children-content { | ||||
|       background-color: #f8f8f8; | ||||
|     } | ||||
| 
 | ||||
|     .CodeMirror { | ||||
|       width: calc(100% - 32px - @tree-width); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import { PageContainer } from '@ant-design/pro-layout'; | |||
| import Editor from '@monaco-editor/react'; | ||||
| import { request } from '@/utils/http'; | ||||
| import styles from './index.module.less'; | ||||
| import { Controlled as CodeMirror } from 'react-codemirror2'; | ||||
| 
 | ||||
| function getFilterData(keyword: string, data: any) { | ||||
|   const expandedKeys: string[] = []; | ||||
|  | @ -188,24 +189,41 @@ const Log = () => { | |||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|         <Editor | ||||
|           language="shell" | ||||
|           theme={theme} | ||||
|           value={value} | ||||
|           options={{ | ||||
|             readOnly: true, | ||||
|             fontSize: 12, | ||||
|             minimap: { enabled: width === '100%' }, | ||||
|             lineNumbersMinChars: 3, | ||||
|             fontFamily: 'Source Code Pro', | ||||
|             folding: false, | ||||
|             glyphMargin: false, | ||||
|             wordWrap: 'on', | ||||
|           }} | ||||
|           onChange={(val, ev) => { | ||||
|             setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|           }} | ||||
|         /> | ||||
|         {isPhone ? ( | ||||
|           <CodeMirror | ||||
|             value={value} | ||||
|             options={{ | ||||
|               lineNumbers: true, | ||||
|               lineWrapping: true, | ||||
|               styleActiveLine: true, | ||||
|               matchBrackets: true, | ||||
|               readOnly: true, | ||||
|             }} | ||||
|             onBeforeChange={(editor, data, value) => { | ||||
|               setValue(value); | ||||
|             }} | ||||
|             onChange={(editor, data, value) => {}} | ||||
|           /> | ||||
|         ) : ( | ||||
|           <Editor | ||||
|             language="shell" | ||||
|             theme={theme} | ||||
|             value={value} | ||||
|             options={{ | ||||
|               readOnly: true, | ||||
|               fontSize: 12, | ||||
|               minimap: { enabled: width === '100%' }, | ||||
|               lineNumbersMinChars: 3, | ||||
|               fontFamily: 'Source Code Pro', | ||||
|               folding: false, | ||||
|               glyphMargin: false, | ||||
|               wordWrap: 'on', | ||||
|             }} | ||||
|             onChange={(val, ev) => { | ||||
|               setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|             }} | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
|     </PageContainer> | ||||
|   ); | ||||
|  |  | |||
|  | @ -21,9 +21,9 @@ const Login = () => { | |||
|           localStorage.setItem(config.authKey, data.token); | ||||
|           history.push('/crontab'); | ||||
|         } else if (data.code === 100) { | ||||
|           message.warn(data.msg); | ||||
|           message.warn(data.message); | ||||
|         } else { | ||||
|           message.error(data.msg); | ||||
|           message.error(data.message); | ||||
|         } | ||||
|       }) | ||||
|       .catch(function (error) { | ||||
|  |  | |||
							
								
								
									
										209
									
								
								src/pages/script/editModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/pages/script/editModal.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| import React, { useEffect, useState } from 'react'; | ||||
| import { Drawer, Button, Tabs, Badge, Select, TreeSelect } from 'antd'; | ||||
| import { request } from '@/utils/http'; | ||||
| import config from '@/utils/config'; | ||||
| import SplitPane from 'react-split-pane'; | ||||
| import Editor from '@monaco-editor/react'; | ||||
| import SaveModal from './saveModal'; | ||||
| import SettingModal from './setting'; | ||||
| 
 | ||||
| const { Option } = Select; | ||||
| const LangMap: any = { | ||||
|   '.py': 'python', | ||||
|   '.js': 'javascript', | ||||
|   '.sh': 'shell', | ||||
|   '.ts': 'typescript', | ||||
| }; | ||||
| const prefixMap: any = { | ||||
|   python: '.py', | ||||
|   javascript: '.js', | ||||
|   shell: '.sh', | ||||
|   typescript: '.ts', | ||||
| }; | ||||
| 
 | ||||
| const EditModal = ({ | ||||
|   treeData, | ||||
|   currentFile, | ||||
|   content, | ||||
|   handleCancel, | ||||
|   visible, | ||||
| }: { | ||||
|   treeData?: any; | ||||
|   currentFile?: string; | ||||
|   content?: string; | ||||
|   visible: boolean; | ||||
|   handleCancel: () => void; | ||||
| }) => { | ||||
|   const [value, setValue] = useState(''); | ||||
|   const [theme, setTheme] = useState<string>(''); | ||||
|   const [language, setLanguage] = useState<string>('javascript'); | ||||
|   const [fileName, setFileName] = useState<string>(''); | ||||
|   const [saveModalVisible, setSaveModalVisible] = useState<boolean>(false); | ||||
|   const [settingModalVisible, setSettingModalVisible] = | ||||
|     useState<boolean>(false); | ||||
|   const [isNewFile, setIsNewFile] = useState<boolean>(false); | ||||
|   const [log, setLog] = useState<string>(''); | ||||
| 
 | ||||
|   const cancel = () => { | ||||
|     handleCancel(); | ||||
|   }; | ||||
| 
 | ||||
|   const onSelect = (value: any, node: any) => { | ||||
|     const newMode = LangMap[value.slice(-3)] || ''; | ||||
|     setFileName(value); | ||||
|     setLanguage(newMode); | ||||
|     setIsNewFile(false); | ||||
|     getDetail(node); | ||||
|   }; | ||||
| 
 | ||||
|   const getDetail = (node: any) => { | ||||
|     request.get(`${config.apiPrefix}scripts/${node.value}`).then((data) => { | ||||
|       setValue(data.data); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const createFile = () => { | ||||
|     setFileName(`未命名${prefixMap[language]}`); | ||||
|     setIsNewFile(true); | ||||
|     setValue(''); | ||||
|   }; | ||||
| 
 | ||||
|   const run = () => {}; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!currentFile) { | ||||
|       createFile(); | ||||
|     } else { | ||||
|       setFileName(currentFile); | ||||
|       setValue(content as string); | ||||
|     } | ||||
|     setIsNewFile(!currentFile); | ||||
|   }, []); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const media = window.matchMedia('(prefers-color-scheme: dark)'); | ||||
|     const storageTheme = localStorage.getItem('qinglong_dark_theme'); | ||||
|     const isDark = | ||||
|       (media.matches && storageTheme !== 'light') || storageTheme === 'dark'; | ||||
|     setTheme(isDark ? 'vs-dark' : 'vs'); | ||||
|     media.addEventListener('change', (e) => { | ||||
|       if (storageTheme === 'auto' || !storageTheme) { | ||||
|         if (e.matches) { | ||||
|           setTheme('vs-dark'); | ||||
|         } else { | ||||
|           setTheme('vs'); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   }, []); | ||||
| 
 | ||||
|   return ( | ||||
|     <Drawer | ||||
|       className="edit-modal" | ||||
|       title={ | ||||
|         <> | ||||
|           <span style={{ marginRight: 8 }}>{fileName}</span> | ||||
|           <TreeSelect | ||||
|             style={{ marginRight: 8, width: 120 }} | ||||
|             value={currentFile} | ||||
|             dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} | ||||
|             treeData={treeData} | ||||
|             placeholder="请选择脚本文件" | ||||
|             showSearch | ||||
|             key="value" | ||||
|             onSelect={onSelect} | ||||
|           /> | ||||
|           <Select | ||||
|             value={language} | ||||
|             style={{ width: 120, marginRight: 8 }} | ||||
|             onChange={(e) => { | ||||
|               setLanguage(e); | ||||
|             }} | ||||
|           > | ||||
|             <Option value="javascript">javascript</Option> | ||||
|             <Option value="typescript">typescript</Option> | ||||
|             <Option value="shell">shell</Option> | ||||
|             <Option value="python">python</Option> | ||||
|           </Select> | ||||
|           <Button type="primary" style={{ marginRight: 8 }} onClick={run}> | ||||
|             运行 | ||||
|           </Button> | ||||
|           <Button | ||||
|             type="primary" | ||||
|             style={{ marginRight: 8 }} | ||||
|             onClick={() => { | ||||
|               setLog(''); | ||||
|             }} | ||||
|           > | ||||
|             清空日志 | ||||
|           </Button> | ||||
|           <Button | ||||
|             type="primary" | ||||
|             style={{ marginRight: 8 }} | ||||
|             onClick={() => { | ||||
|               setSettingModalVisible(true); | ||||
|             }} | ||||
|           > | ||||
|             设置 | ||||
|           </Button> | ||||
|           <Button | ||||
|             type="primary" | ||||
|             style={{ marginRight: 8 }} | ||||
|             onClick={createFile} | ||||
|           > | ||||
|             新建文件 | ||||
|           </Button> | ||||
|           <Button | ||||
|             type="primary" | ||||
|             style={{ marginRight: 8 }} | ||||
|             onClick={() => { | ||||
|               setSaveModalVisible(true); | ||||
|             }} | ||||
|           > | ||||
|             保存 | ||||
|           </Button> | ||||
|         </> | ||||
|       } | ||||
|       width={'100%'} | ||||
|       headerStyle={{ padding: '11px 24px' }} | ||||
|       onClose={cancel} | ||||
|       visible={visible} | ||||
|     > | ||||
|       <SplitPane split="vertical" minSize={200} defaultSize="50%"> | ||||
|         <Editor | ||||
|           language={language} | ||||
|           value={value} | ||||
|           theme={theme} | ||||
|           options={{ | ||||
|             fontSize: 12, | ||||
|             minimap: { enabled: false }, | ||||
|             lineNumbersMinChars: 3, | ||||
|             glyphMargin: false, | ||||
|           }} | ||||
|           onChange={(val) => { | ||||
|             setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|           }} | ||||
|         /> | ||||
|         <div> | ||||
|           <pre>{log}</pre> | ||||
|         </div> | ||||
|       </SplitPane> | ||||
|       <SaveModal | ||||
|         visible={saveModalVisible} | ||||
|         handleCancel={() => { | ||||
|           setSaveModalVisible(false); | ||||
|         }} | ||||
|         isNewFile={isNewFile} | ||||
|         file={{ content: value, filename: fileName }} | ||||
|       /> | ||||
|       <SettingModal | ||||
|         visible={settingModalVisible} | ||||
|         handleCancel={() => { | ||||
|           setSettingModalVisible(false); | ||||
|         }} | ||||
|       /> | ||||
|     </Drawer> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default EditModal; | ||||
|  | @ -27,5 +27,9 @@ | |||
|     .ant-pro-grid-content.wide .ant-pro-page-container-children-content { | ||||
|       background-color: #f8f8f8; | ||||
|     } | ||||
| 
 | ||||
|     .CodeMirror { | ||||
|       width: calc(100% - 32px - @tree-width); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| import { useState, useEffect, useCallback, Key, useRef } from 'react'; | ||||
| import { TreeSelect, Tree, Input } from 'antd'; | ||||
| import { TreeSelect, Tree, Input, Button } from 'antd'; | ||||
| import config from '@/utils/config'; | ||||
| import { PageContainer } from '@ant-design/pro-layout'; | ||||
| import Editor from '@monaco-editor/react'; | ||||
| import { request } from '@/utils/http'; | ||||
| import styles from './index.module.less'; | ||||
| import EditModal from './editModal'; | ||||
| import { Controlled as CodeMirror } from 'react-codemirror2'; | ||||
| 
 | ||||
| function getFilterData(keyword: string, data: any) { | ||||
|   if (keyword) { | ||||
|  | @ -41,6 +43,7 @@ const Script = () => { | |||
|   const [height, setHeight] = useState<number>(); | ||||
|   const treeDom = useRef<any>(); | ||||
|   const [theme, setTheme] = useState<string>(''); | ||||
|   const [isLogModalVisible, setIsLogModalVisible] = useState(false); | ||||
| 
 | ||||
|   const getScripts = () => { | ||||
|     setLoading(true); | ||||
|  | @ -119,18 +122,29 @@ const Script = () => { | |||
|       title={title} | ||||
|       loading={loading} | ||||
|       extra={ | ||||
|         isPhone && [ | ||||
|           <TreeSelect | ||||
|             className="log-select" | ||||
|             value={select} | ||||
|             dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} | ||||
|             treeData={data} | ||||
|             placeholder="请选择脚本文件" | ||||
|             showSearch | ||||
|             key="value" | ||||
|             onSelect={onSelect} | ||||
|           />, | ||||
|         ] | ||||
|         isPhone | ||||
|           ? [ | ||||
|               <TreeSelect | ||||
|                 className="log-select" | ||||
|                 value={select} | ||||
|                 dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} | ||||
|                 treeData={data} | ||||
|                 placeholder="请选择脚本文件" | ||||
|                 showSearch | ||||
|                 key="value" | ||||
|                 onSelect={onSelect} | ||||
|               />, | ||||
|             ] | ||||
|           : [ | ||||
|               <Button | ||||
|                 type="primary" | ||||
|                 onClick={() => { | ||||
|                   setIsLogModalVisible(true); | ||||
|                 }} | ||||
|               > | ||||
|                 调试 | ||||
|               </Button>, | ||||
|             ] | ||||
|       } | ||||
|       header={{ | ||||
|         style: { | ||||
|  | @ -164,20 +178,47 @@ const Script = () => { | |||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|         <Editor | ||||
|           language={mode} | ||||
|           value={value} | ||||
|           theme={theme} | ||||
|           options={{ | ||||
|             readOnly: true, | ||||
|             fontSize: 12, | ||||
|             minimap: { enabled: width === '100%' }, | ||||
|             lineNumbersMinChars: 3, | ||||
|             folding: false, | ||||
|             glyphMargin: false, | ||||
|           }} | ||||
|           onChange={(val) => { | ||||
|             setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|         {isPhone ? ( | ||||
|           <CodeMirror | ||||
|             value={value} | ||||
|             options={{ | ||||
|               lineNumbers: true, | ||||
|               lineWrapping: true, | ||||
|               styleActiveLine: true, | ||||
|               matchBrackets: true, | ||||
|               mode, | ||||
|               readOnly: true, | ||||
|             }} | ||||
|             onBeforeChange={(editor, data, value) => { | ||||
|               setValue(value); | ||||
|             }} | ||||
|             onChange={(editor, data, value) => {}} | ||||
|           /> | ||||
|         ) : ( | ||||
|           <Editor | ||||
|             language={mode} | ||||
|             value={value} | ||||
|             theme={theme} | ||||
|             options={{ | ||||
|               readOnly: true, | ||||
|               fontSize: 12, | ||||
|               minimap: { enabled: width === '100%' }, | ||||
|               lineNumbersMinChars: 3, | ||||
|               folding: false, | ||||
|               glyphMargin: false, | ||||
|             }} | ||||
|             onChange={(val) => { | ||||
|               setValue((val as string).replace(/\r\n/g, '\n')); | ||||
|             }} | ||||
|           /> | ||||
|         )} | ||||
|         <EditModal | ||||
|           visible={isLogModalVisible} | ||||
|           treeData={data} | ||||
|           currentFile={select} | ||||
|           content={value} | ||||
|           handleCancel={() => { | ||||
|             setIsLogModalVisible(false); | ||||
|           }} | ||||
|         /> | ||||
|       </div> | ||||
|  |  | |||
							
								
								
									
										87
									
								
								src/pages/script/saveModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/pages/script/saveModal.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| import React, { useEffect, useState } from 'react'; | ||||
| import { Modal, message, Input, Form } from 'antd'; | ||||
| import { request } from '@/utils/http'; | ||||
| import config from '@/utils/config'; | ||||
| 
 | ||||
| const SaveModal = ({ | ||||
|   file, | ||||
|   handleCancel, | ||||
|   visible, | ||||
|   isNewFile, | ||||
| }: { | ||||
|   file?: any; | ||||
|   visible: boolean; | ||||
|   handleCancel: (cks?: any[]) => void; | ||||
|   isNewFile: boolean; | ||||
| }) => { | ||||
|   const [form] = Form.useForm(); | ||||
|   const [loading, setLoading] = useState(false); | ||||
| 
 | ||||
|   const handleOk = async (values: any) => { | ||||
|     console.log(file.filename); | ||||
|     setLoading(true); | ||||
|     const payload = { ...file, ...values }; | ||||
|     request | ||||
|       .post(`${config.apiPrefix}scripts`, { | ||||
|         data: payload, | ||||
|       }) | ||||
|       .then(({ code, data }) => { | ||||
|         if (code === 200) { | ||||
|           message.success('保存文件成功'); | ||||
|           handleCancel(data); | ||||
|         } else { | ||||
|           message.error(data); | ||||
|         } | ||||
|         setLoading(false); | ||||
|       }); | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     form.resetFields(); | ||||
|     setLoading(false); | ||||
|   }, [file, visible]); | ||||
| 
 | ||||
|   return ( | ||||
|     <Modal | ||||
|       title="保存文件" | ||||
|       visible={visible} | ||||
|       forceRender | ||||
|       onOk={() => { | ||||
|         form | ||||
|           .validateFields() | ||||
|           .then((values) => { | ||||
|             handleOk(values); | ||||
|           }) | ||||
|           .catch((info) => { | ||||
|             console.log('Validate Failed:', info); | ||||
|           }); | ||||
|       }} | ||||
|       onCancel={() => handleCancel()} | ||||
|       confirmLoading={loading} | ||||
|     > | ||||
|       <Form | ||||
|         form={form} | ||||
|         layout="vertical" | ||||
|         name="script_modal" | ||||
|         initialValues={file} | ||||
|       > | ||||
|         <Form.Item | ||||
|           name="filename" | ||||
|           label="文件名" | ||||
|           rules={[{ required: true, message: '请输入文件名' }]} | ||||
|         > | ||||
|           <Input placeholder="请输入文件名" /> | ||||
|         </Form.Item> | ||||
|         <Form.Item | ||||
|           name="path" | ||||
|           label="保存目录" | ||||
|           rules={[{ required: true, message: '请输入保存目录' }]} | ||||
|         > | ||||
|           <Input placeholder="请输入保存目录" /> | ||||
|         </Form.Item> | ||||
|       </Form> | ||||
|     </Modal> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default SaveModal; | ||||
							
								
								
									
										67
									
								
								src/pages/script/setting.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/pages/script/setting.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| import React, { useEffect, useState } from 'react'; | ||||
| import { Modal, message, Input, Form } from 'antd'; | ||||
| import { request } from '@/utils/http'; | ||||
| import config from '@/utils/config'; | ||||
| 
 | ||||
| const SettingModal = ({ | ||||
|   file, | ||||
|   handleCancel, | ||||
|   visible, | ||||
| }: { | ||||
|   file?: any; | ||||
|   visible: boolean; | ||||
|   handleCancel: (cks?: any[]) => void; | ||||
| }) => { | ||||
|   const [form] = Form.useForm(); | ||||
|   const [loading, setLoading] = useState(false); | ||||
| 
 | ||||
|   const handleOk = async (values: any) => { | ||||
|     console.log(file.filename); | ||||
|     setLoading(true); | ||||
|     const payload = { ...file, ...values }; | ||||
|     request | ||||
|       .post(`${config.apiPrefix}scripts`, { | ||||
|         data: payload, | ||||
|       }) | ||||
|       .then(({ code, data }) => { | ||||
|         if (code === 200) { | ||||
|           message.success('保存文件成功'); | ||||
|           handleCancel(data); | ||||
|         } else { | ||||
|           message.error(data); | ||||
|         } | ||||
|         setLoading(false); | ||||
|       }); | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     form.resetFields(); | ||||
|     setLoading(false); | ||||
|   }, [file, visible]); | ||||
| 
 | ||||
|   return ( | ||||
|     <Modal | ||||
|       title="运行设置" | ||||
|       visible={visible} | ||||
|       forceRender | ||||
|       onCancel={() => handleCancel()} | ||||
|     > | ||||
|       <Form | ||||
|         form={form} | ||||
|         layout="vertical" | ||||
|         name="setting_modal" | ||||
|         initialValues={file} | ||||
|       > | ||||
|         <Form.Item | ||||
|           name="filename" | ||||
|           label="待开发" | ||||
|           rules={[{ required: true, message: '待开发' }]} | ||||
|         > | ||||
|           <Input placeholder="待开发" /> | ||||
|         </Form.Item> | ||||
|       </Form> | ||||
|     </Modal> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default SettingModal; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 hanhh
						hanhh