mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-23 23:06:06 +08:00
手机端使用codemirror
This commit is contained in:
parent
38609feee9
commit
9728119101
|
@ -29,10 +29,15 @@ export default defineConfig({
|
||||||
react: 'window.React',
|
react: 'window.React',
|
||||||
'react-dom': 'window.ReactDOM',
|
'react-dom': 'window.ReactDOM',
|
||||||
darkreader: 'window.DarkReader',
|
darkreader: 'window.DarkReader',
|
||||||
|
codemirror: 'window.CodeMirror',
|
||||||
},
|
},
|
||||||
scripts: [
|
scripts: [
|
||||||
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
|
'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://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/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({
|
return res.send({
|
||||||
code: 100,
|
code: 100,
|
||||||
msg: '已初始化密码,请前往auth.json查看并重新登录',
|
message: '已初始化密码,请前往auth.json查看并重新登录',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@ -57,10 +57,10 @@ export default (app: Router) => {
|
||||||
);
|
);
|
||||||
res.send({ code: 200, token });
|
res.send({ code: 200, token });
|
||||||
} else {
|
} else {
|
||||||
res.send({ code: 400, msg: config.authError });
|
res.send({ code: 400, message: config.authError });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.send({ err: 400, msg: '请输入用户名密码!' });
|
res.send({ err: 400, message: '请输入用户名密码!' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -101,7 +101,7 @@ export default (app: Router) => {
|
||||||
try {
|
try {
|
||||||
fs.writeFile(config.authConfigFile, JSON.stringify(req.body), (err) => {
|
fs.writeFile(config.authConfigFile, JSON.stringify(req.body), (err) => {
|
||||||
if (err) console.log(err);
|
if (err) console.log(err);
|
||||||
res.send({ code: 200, msg: '更新成功' });
|
res.send({ code: 200, message: '更新成功' });
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('🔥 error: %o', e);
|
logger.error('🔥 error: %o', e);
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default (app: Router) => {
|
||||||
const { name, content } = req.body;
|
const { name, content } = req.body;
|
||||||
const path = `${config.configPath}${name}`;
|
const path = `${config.configPath}${name}`;
|
||||||
fs.writeFileSync(path, content);
|
fs.writeFileSync(path, content);
|
||||||
res.send({ code: 200, msg: '保存成功' });
|
res.send({ code: 200, message: '保存成功' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('🔥 error: %o', e);
|
logger.error('🔥 error: %o', e);
|
||||||
return next(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',
|
'crontab.list',
|
||||||
'env.sh',
|
'env.sh',
|
||||||
],
|
],
|
||||||
|
writePathList: [
|
||||||
|
'/ql/scripts/',
|
||||||
|
'/ql/config/',
|
||||||
|
'/ql/jbot/',
|
||||||
|
'/ql/bak/',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@monaco-editor/react": "4.1.3",
|
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"celebrate": "^13.0.3",
|
"celebrate": "^13.0.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
@ -45,6 +44,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/icons": "^4.6.2",
|
"@ant-design/icons": "^4.6.2",
|
||||||
"@ant-design/pro-layout": "^6.5.0",
|
"@ant-design/pro-layout": "^6.5.0",
|
||||||
|
"@monaco-editor/react": "^4.2.1",
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.10",
|
||||||
"@types/express": "^4.17.8",
|
"@types/express": "^4.17.8",
|
||||||
"@types/express-jwt": "^6.0.1",
|
"@types/express-jwt": "^6.0.1",
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
"@umijs/plugin-antd": "^0.9.1",
|
"@umijs/plugin-antd": "^0.9.1",
|
||||||
"@umijs/test": "^3.3.9",
|
"@umijs/test": "^3.3.9",
|
||||||
|
"codemirror": "^5.62.2",
|
||||||
"compression-webpack-plugin": "6.1.1",
|
"compression-webpack-plugin": "6.1.1",
|
||||||
"darkreader": "^4.9.27",
|
"darkreader": "^4.9.27",
|
||||||
"lint-staged": "^10.0.7",
|
"lint-staged": "^10.0.7",
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
"prettier": "^2.2.0",
|
"prettier": "^2.2.0",
|
||||||
"qrcode.react": "^1.0.1",
|
"qrcode.react": "^1.0.1",
|
||||||
"react": "17.x",
|
"react": "17.x",
|
||||||
|
"react-codemirror2": "^7.2.1",
|
||||||
"react-diff-viewer": "^3.1.1",
|
"react-diff-viewer": "^3.1.1",
|
||||||
"react-dnd": "^14.0.2",
|
"react-dnd": "^14.0.2",
|
||||||
"react-dnd-html5-backend": "^14.0.0",
|
"react-dnd-html5-backend": "^14.0.0",
|
||||||
|
|
|
@ -3,7 +3,7 @@ lockfileVersion: 5.3
|
||||||
specifiers:
|
specifiers:
|
||||||
'@ant-design/icons': ^4.6.2
|
'@ant-design/icons': ^4.6.2
|
||||||
'@ant-design/pro-layout': ^6.5.0
|
'@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/cors': ^2.8.10
|
||||||
'@types/express': ^4.17.8
|
'@types/express': ^4.17.8
|
||||||
'@types/express-jwt': ^6.0.1
|
'@types/express-jwt': ^6.0.1
|
||||||
|
@ -19,6 +19,7 @@ specifiers:
|
||||||
'@umijs/test': ^3.3.9
|
'@umijs/test': ^3.3.9
|
||||||
body-parser: ^1.19.0
|
body-parser: ^1.19.0
|
||||||
celebrate: ^13.0.3
|
celebrate: ^13.0.3
|
||||||
|
codemirror: ^5.62.2
|
||||||
compression-webpack-plugin: 6.1.1
|
compression-webpack-plugin: 6.1.1
|
||||||
cors: ^2.8.5
|
cors: ^2.8.5
|
||||||
cron-parser: ^3.5.0
|
cron-parser: ^3.5.0
|
||||||
|
@ -37,6 +38,7 @@ specifiers:
|
||||||
prettier: ^2.2.0
|
prettier: ^2.2.0
|
||||||
qrcode.react: ^1.0.1
|
qrcode.react: ^1.0.1
|
||||||
react: 17.x
|
react: 17.x
|
||||||
|
react-codemirror2: ^7.2.1
|
||||||
react-diff-viewer: ^3.1.1
|
react-diff-viewer: ^3.1.1
|
||||||
react-dnd: ^14.0.2
|
react-dnd: ^14.0.2
|
||||||
react-dnd-html5-backend: ^14.0.0
|
react-dnd-html5-backend: ^14.0.0
|
||||||
|
@ -54,7 +56,6 @@ specifiers:
|
||||||
yorkie: ^2.0.0
|
yorkie: ^2.0.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@monaco-editor/react': 4.1.3_react-dom@17.0.2+react@17.0.2
|
|
||||||
body-parser: 1.19.0
|
body-parser: 1.19.0
|
||||||
celebrate: 13.0.4
|
celebrate: 13.0.4
|
||||||
cors: 2.8.5
|
cors: 2.8.5
|
||||||
|
@ -75,6 +76,7 @@ dependencies:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@ant-design/icons': 4.6.2_react-dom@17.0.2+react@17.0.2
|
'@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
|
'@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/cors': 2.8.10
|
||||||
'@types/express': 4.17.11
|
'@types/express': 4.17.11
|
||||||
'@types/express-jwt': 6.0.1
|
'@types/express-jwt': 6.0.1
|
||||||
|
@ -88,6 +90,7 @@ devDependencies:
|
||||||
'@types/react-dom': 17.0.5
|
'@types/react-dom': 17.0.5
|
||||||
'@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75
|
'@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75
|
||||||
'@umijs/test': 3.4.20_ts-node@9.1.1
|
'@umijs/test': 3.4.20_ts-node@9.1.1
|
||||||
|
codemirror: 5.62.2
|
||||||
compression-webpack-plugin: 6.1.1_webpack@5.37.0
|
compression-webpack-plugin: 6.1.1_webpack@5.37.0
|
||||||
darkreader: 4.9.32
|
darkreader: 4.9.32
|
||||||
lint-staged: 10.5.4
|
lint-staged: 10.5.4
|
||||||
|
@ -95,6 +98,7 @@ devDependencies:
|
||||||
prettier: 2.3.0
|
prettier: 2.3.0
|
||||||
qrcode.react: 1.0.1_react@17.0.2
|
qrcode.react: 1.0.1_react@17.0.2
|
||||||
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-diff-viewer: 3.1.1_react-dom@17.0.2+react@17.0.2
|
||||||
react-dnd: 14.0.2_695545ed68ea337339babea285839fc0
|
react-dnd: 14.0.2_695545ed68ea337339babea285839fc0
|
||||||
react-dnd-html5-backend: 14.0.0
|
react-dnd-html5-backend: 14.0.0
|
||||||
|
@ -872,12 +876,12 @@ packages:
|
||||||
monaco-editor: '>= 0.21.0 < 1'
|
monaco-editor: '>= 0.21.0 < 1'
|
||||||
dependencies:
|
dependencies:
|
||||||
state-local: 1.0.7
|
state-local: 1.0.7
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@monaco-editor/react/4.1.3_react-dom@17.0.2+react@17.0.2:
|
/@monaco-editor/react/4.2.1_react-dom@17.0.2+react@17.0.2:
|
||||||
resolution: {integrity: sha512-kqcjVuoy6btcgALAk4RV/SlasveM+WTw5lzzlyq5FhKXjF8wu5tSe/2oCQ1uhLpcdtxcHfx3L0HrcAPWnejFnQ==}
|
resolution: {integrity: sha512-yN8qVY0PyFIbqPjfrZ5TbR/wrcfeiwoys8+0QkmyfiOzG74vXxSBOPIUxk7Ly+qCj7qWHPq1uDJskzFGaIqaPA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
monaco-editor: ^0.23.0
|
monaco-editor: '>= 0.25.0 < 1'
|
||||||
react: ^16.8.0 || ^17.0.0
|
react: ^16.8.0 || ^17.0.0
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
react-dom: ^16.8.0 || ^17.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -886,7 +890,7 @@ packages:
|
||||||
react: 17.0.2
|
react: 17.0.2
|
||||||
react-dom: 17.0.2_react@17.0.2
|
react-dom: 17.0.2_react@17.0.2
|
||||||
state-local: 1.0.7
|
state-local: 1.0.7
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@npmcli/move-file/1.1.2:
|
/@npmcli/move-file/1.1.2:
|
||||||
resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
|
resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
|
||||||
|
@ -2587,6 +2591,10 @@ packages:
|
||||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/codemirror/5.62.2:
|
||||||
|
resolution: {integrity: sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/collect-v8-coverage/1.0.1:
|
/collect-v8-coverage/1.0.1:
|
||||||
resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
|
resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -5303,6 +5311,7 @@ packages:
|
||||||
|
|
||||||
/js-tokens/4.0.0:
|
/js-tokens/4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/js-yaml/3.14.1:
|
/js-yaml/3.14.1:
|
||||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||||
|
@ -5715,6 +5724,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lowercase-keys/1.0.1:
|
/lowercase-keys/1.0.1:
|
||||||
resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==}
|
resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==}
|
||||||
|
@ -6954,6 +6964,7 @@ packages:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/proxy-addr/2.0.6:
|
/proxy-addr/2.0.6:
|
||||||
resolution: {integrity: sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==}
|
resolution: {integrity: sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==}
|
||||||
|
@ -7719,6 +7730,16 @@ packages:
|
||||||
strip-json-comments: 2.0.1
|
strip-json-comments: 2.0.1
|
||||||
dev: true
|
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:
|
/react-diff-viewer/3.1.1_react-dom@17.0.2+react@17.0.2:
|
||||||
resolution: {integrity: sha512-rmvwNdcClp6ZWdS11m1m01UnBA4OwYaLG/li0dB781e/bQEzsGyj+qewVd6W5ztBwseQ72pO7nwaCcq5jnlzcw==}
|
resolution: {integrity: sha512-rmvwNdcClp6ZWdS11m1m01UnBA4OwYaLG/li0dB781e/bQEzsGyj+qewVd6W5ztBwseQ72pO7nwaCcq5jnlzcw==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -7798,6 +7819,7 @@ packages:
|
||||||
|
|
||||||
/react-is/16.13.1:
|
/react-is/16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/react-is/17.0.2:
|
/react-is/17.0.2:
|
||||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||||
|
@ -8732,7 +8754,7 @@ packages:
|
||||||
|
|
||||||
/state-local/1.0.7:
|
/state-local/1.0.7:
|
||||||
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
|
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/static-extend/0.1.2:
|
/static-extend/0.1.2:
|
||||||
resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=}
|
resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import '~@/styles/variable.less';
|
@import '~@/styles/variable.less';
|
||||||
|
@import '~codemirror/lib/codemirror.css';
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
|
@ -13,7 +14,7 @@ body {
|
||||||
background-color: rgb(248, 248, 248);
|
background-color: rgb(248, 248, 248);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal {
|
.log-modal .ant-modal {
|
||||||
padding-bottom: 0 !important;
|
padding-bottom: 0 !important;
|
||||||
width: 580px !important;
|
width: 580px !important;
|
||||||
}
|
}
|
||||||
|
@ -21,22 +22,14 @@ body {
|
||||||
.monaco-editor:not(.rename-box) {
|
.monaco-editor:not(.rename-box) {
|
||||||
height: calc(100vh - 128px) !important;
|
height: calc(100vh - 128px) !important;
|
||||||
height: calc(100vh - var(--vh-offset, 0px) - 128px) !important;
|
height: calc(100vh - var(--vh-offset, 0px) - 128px) !important;
|
||||||
.view-overlays .current-line{
|
.view-overlays .current-line {
|
||||||
border-width: 0;
|
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;
|
height: 0;
|
||||||
.rename-input{
|
.rename-input {
|
||||||
height: 0;
|
height: 0;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
@ -144,12 +137,17 @@ input:-webkit-autofill:active {
|
||||||
height: calc(100vh - 184px);
|
height: calc(100vh - 184px);
|
||||||
height: calc(100vh - var(--vh-offset, 0px) - 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 - 216px) !important;
|
||||||
height: calc(100vh - var(--vh-offset, 0px) - 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 - 176px) !important;
|
||||||
height: calc(100vh - var(--vh-offset, 0px) - 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);
|
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 =
|
const isSafari =
|
||||||
navigator.userAgent.includes('Safari') &&
|
navigator.userAgent.includes('Safari') &&
|
||||||
!navigator.userAgent.includes('Chrome');
|
!navigator.userAgent.includes('Chrome');
|
||||||
|
const isQQBrowser = navigator.userAgent.includes('QQBrowser');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProLayout
|
<ProLayout
|
||||||
selectedKeys={[props.location.pathname]}
|
selectedKeys={[props.location.pathname]}
|
||||||
|
@ -76,8 +78,9 @@ export default function (props: any) {
|
||||||
style={{
|
style={{
|
||||||
fontSize: isFirefox ? 9 : 12,
|
fontSize: isFirefox ? 9 : 12,
|
||||||
color: '#666',
|
color: '#666',
|
||||||
marginLeft: 5,
|
marginLeft: 2,
|
||||||
zoom: isSafari ? 0.66 : 0.8,
|
zoom: isSafari ? 0.66 : 0.8,
|
||||||
|
letterSpacing: isQQBrowser ? -2 : 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{version}
|
{version}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import config from '@/utils/config';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
|
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||||
|
|
||||||
const Config = () => {
|
const Config = () => {
|
||||||
const [width, setWidth] = useState('100%');
|
const [width, setWidth] = useState('100%');
|
||||||
|
@ -15,6 +16,7 @@ const Config = () => {
|
||||||
const [select, setSelect] = useState('config.sh');
|
const [select, setSelect] = useState('config.sh');
|
||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
const [theme, setTheme] = useState<string>('');
|
const [theme, setTheme] = useState<string>('');
|
||||||
|
const [isPhone, setIsPhone] = useState(false);
|
||||||
|
|
||||||
const getConfig = (name: string) => {
|
const getConfig = (name: string) => {
|
||||||
request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => {
|
request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => {
|
||||||
|
@ -38,7 +40,7 @@ const Config = () => {
|
||||||
data: { content: value, name: select },
|
data: { content: value, name: select },
|
||||||
})
|
})
|
||||||
.then((data: any) => {
|
.then((data: any) => {
|
||||||
message.success(data.msg);
|
message.success(data.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,10 +55,12 @@ const Config = () => {
|
||||||
setWidth('auto');
|
setWidth('auto');
|
||||||
setMarginLeft(0);
|
setMarginLeft(0);
|
||||||
setMarginTop(0);
|
setMarginTop(0);
|
||||||
|
setIsPhone(true);
|
||||||
} else {
|
} else {
|
||||||
setWidth('100%');
|
setWidth('100%');
|
||||||
setMarginLeft(0);
|
setMarginLeft(0);
|
||||||
setMarginTop(-72);
|
setMarginTop(-72);
|
||||||
|
setIsPhone(false);
|
||||||
}
|
}
|
||||||
getFiles();
|
getFiles();
|
||||||
getConfig('config.sh');
|
getConfig('config.sh');
|
||||||
|
@ -111,6 +115,21 @@ const Config = () => {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{isPhone ? (
|
||||||
|
<CodeMirror
|
||||||
|
value={value}
|
||||||
|
options={{
|
||||||
|
lineNumbers: true,
|
||||||
|
styleActiveLine: true,
|
||||||
|
matchBrackets: true,
|
||||||
|
mode: 'shell',
|
||||||
|
}}
|
||||||
|
onBeforeChange={(editor, data, value) => {
|
||||||
|
setValue(value);
|
||||||
|
}}
|
||||||
|
onChange={(editor, data, value) => {}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<Editor
|
<Editor
|
||||||
defaultLanguage="shell"
|
defaultLanguage="shell"
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -126,6 +145,7 @@ const Config = () => {
|
||||||
setValue((val as string).replace(/\r\n/g, '\n'));
|
setValue((val as string).replace(/\r\n/g, '\n'));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -96,6 +96,7 @@ const CronLogModal = ({
|
||||||
title={titleElement()}
|
title={titleElement()}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
centered
|
centered
|
||||||
|
className="log-modal"
|
||||||
bodyStyle={{
|
bodyStyle={{
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
maxHeight: 'calc(80vh - var(--vh-offset, 0px))',
|
maxHeight: 'calc(80vh - var(--vh-offset, 0px))',
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { Button, message, Modal } from 'antd';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import ReactDiffViewer from 'react-diff-viewer';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import { DiffEditor } from "@monaco-editor/react";
|
import { DiffEditor } from '@monaco-editor/react';
|
||||||
|
import ReactDiffViewer from 'react-diff-viewer';
|
||||||
|
|
||||||
const Crontab = () => {
|
const Crontab = () => {
|
||||||
const [width, setWidth] = useState('100%');
|
const [width, setWidth] = useState('100%');
|
||||||
|
@ -15,6 +15,7 @@ const Crontab = () => {
|
||||||
const [sample, setSample] = useState('');
|
const [sample, setSample] = useState('');
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [theme, setTheme] = useState<string>('');
|
const [theme, setTheme] = useState<string>('');
|
||||||
|
const [isPhone, setIsPhone] = useState(false);
|
||||||
|
|
||||||
const getConfig = () => {
|
const getConfig = () => {
|
||||||
request.get(`${config.apiPrefix}configs/config.sh`).then((data) => {
|
request.get(`${config.apiPrefix}configs/config.sh`).then((data) => {
|
||||||
|
@ -37,30 +38,33 @@ const Crontab = () => {
|
||||||
setWidth('auto');
|
setWidth('auto');
|
||||||
setMarginLeft(0);
|
setMarginLeft(0);
|
||||||
setMarginTop(0);
|
setMarginTop(0);
|
||||||
|
setIsPhone(true);
|
||||||
} else {
|
} else {
|
||||||
setWidth('100%');
|
setWidth('100%');
|
||||||
setMarginLeft(0);
|
setMarginLeft(0);
|
||||||
setMarginTop(-72);
|
setMarginTop(-72);
|
||||||
|
setIsPhone(false);
|
||||||
}
|
}
|
||||||
getConfig();
|
getConfig();
|
||||||
getSample();
|
getSample();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(() => {
|
||||||
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
const storageTheme = localStorage.getItem('qinglong_dark_theme');
|
const storageTheme = localStorage.getItem('qinglong_dark_theme');
|
||||||
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
|
const isDark =
|
||||||
setTheme(isDark?'vs-dark':'vs');
|
(media.matches && storageTheme !== 'light') || storageTheme === 'dark';
|
||||||
media.addEventListener('change',(e)=>{
|
setTheme(isDark ? 'vs-dark' : 'vs');
|
||||||
if(storageTheme === 'auto' || !storageTheme){
|
media.addEventListener('change', (e) => {
|
||||||
if(e.matches){
|
if (storageTheme === 'auto' || !storageTheme) {
|
||||||
setTheme('vs-dark')
|
if (e.matches) {
|
||||||
}else{
|
setTheme('vs-dark');
|
||||||
|
} else {
|
||||||
setTheme('vs');
|
setTheme('vs');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},[])
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer
|
<PageContainer
|
||||||
|
@ -80,22 +84,50 @@ const Crontab = () => {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{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
|
<DiffEditor
|
||||||
language={"shell"}
|
language={'shell'}
|
||||||
original={sample}
|
original={sample}
|
||||||
modified={value}
|
modified={value}
|
||||||
options={{
|
options={{
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
minimap: {enabled: width==='100%'},
|
minimap: { enabled: width === '100%' },
|
||||||
lineNumbersMinChars: 3,
|
lineNumbersMinChars: 3,
|
||||||
folding: false,
|
folding: false,
|
||||||
glyphMargin: false,
|
glyphMargin: false,
|
||||||
renderSideBySide: width==='100%',
|
renderSideBySide: width === '100%',
|
||||||
wordWrap: 'on'
|
wordWrap: 'on',
|
||||||
}}
|
}}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,5 +27,9 @@
|
||||||
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
|
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
|
||||||
background-color: #f8f8f8;
|
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 Editor from '@monaco-editor/react';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import styles from './index.module.less';
|
import styles from './index.module.less';
|
||||||
|
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||||
|
|
||||||
function getFilterData(keyword: string, data: any) {
|
function getFilterData(keyword: string, data: any) {
|
||||||
const expandedKeys: string[] = [];
|
const expandedKeys: string[] = [];
|
||||||
|
@ -188,6 +189,22 @@ const Log = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{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
|
<Editor
|
||||||
language="shell"
|
language="shell"
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -206,6 +223,7 @@ const Log = () => {
|
||||||
setValue((val as string).replace(/\r\n/g, '\n'));
|
setValue((val as string).replace(/\r\n/g, '\n'));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,9 +21,9 @@ const Login = () => {
|
||||||
localStorage.setItem(config.authKey, data.token);
|
localStorage.setItem(config.authKey, data.token);
|
||||||
history.push('/crontab');
|
history.push('/crontab');
|
||||||
} else if (data.code === 100) {
|
} else if (data.code === 100) {
|
||||||
message.warn(data.msg);
|
message.warn(data.message);
|
||||||
} else {
|
} else {
|
||||||
message.error(data.msg);
|
message.error(data.message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.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 {
|
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
|
||||||
background-color: #f8f8f8;
|
background-color: #f8f8f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
width: calc(100% - 32px - @tree-width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useState, useEffect, useCallback, Key, useRef } from 'react';
|
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 config from '@/utils/config';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import styles from './index.module.less';
|
import styles from './index.module.less';
|
||||||
|
import EditModal from './editModal';
|
||||||
|
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||||
|
|
||||||
function getFilterData(keyword: string, data: any) {
|
function getFilterData(keyword: string, data: any) {
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
|
@ -41,6 +43,7 @@ const Script = () => {
|
||||||
const [height, setHeight] = useState<number>();
|
const [height, setHeight] = useState<number>();
|
||||||
const treeDom = useRef<any>();
|
const treeDom = useRef<any>();
|
||||||
const [theme, setTheme] = useState<string>('');
|
const [theme, setTheme] = useState<string>('');
|
||||||
|
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
|
||||||
|
|
||||||
const getScripts = () => {
|
const getScripts = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
@ -119,7 +122,8 @@ const Script = () => {
|
||||||
title={title}
|
title={title}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
extra={
|
extra={
|
||||||
isPhone && [
|
isPhone
|
||||||
|
? [
|
||||||
<TreeSelect
|
<TreeSelect
|
||||||
className="log-select"
|
className="log-select"
|
||||||
value={select}
|
value={select}
|
||||||
|
@ -131,6 +135,16 @@ const Script = () => {
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>,
|
/>,
|
||||||
]
|
]
|
||||||
|
: [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsLogModalVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
调试
|
||||||
|
</Button>,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
header={{
|
header={{
|
||||||
style: {
|
style: {
|
||||||
|
@ -164,6 +178,23 @@ const Script = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{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
|
<Editor
|
||||||
language={mode}
|
language={mode}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -180,6 +211,16 @@ const Script = () => {
|
||||||
setValue((val as string).replace(/\r\n/g, '\n'));
|
setValue((val as string).replace(/\r\n/g, '\n'));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<EditModal
|
||||||
|
visible={isLogModalVisible}
|
||||||
|
treeData={data}
|
||||||
|
currentFile={select}
|
||||||
|
content={value}
|
||||||
|
handleCancel={() => {
|
||||||
|
setIsLogModalVisible(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
|
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