🆕 添加在线编程模块,添加nodejs服务代码

This commit is contained in:
xujiang 2020-09-13 11:31:24 +08:00
parent 06bc64cc7a
commit 8c2ed3a611
11 changed files with 308 additions and 10 deletions

View File

@ -30,6 +30,10 @@ export default defineConfig({
path: '/editor',
component: '../pages/editor',
},
{
path: '/ide',
component: '../pages/ide',
},
{
path: '/login',
component: '../pages/login',

View File

@ -17,11 +17,13 @@
"web visible"
],
"contributors": [
"徐小夕 <xujiang156@qq.com> (https://github.com/MrXujiang))",
"yehuozhili <yehuozhili@outlook.com> (https://github.com/yehuozhili))"
],
"scripts": {
"start": "umi dev",
"build": "umi build",
"server": "node server.js",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
@ -57,12 +59,14 @@
"axios": "^0.19.2",
"babel-plugin-import": "^1.13.0",
"chatbot-antd": "^0.6.0",
"codemirror": "^5.57.0",
"file-saver": "^2.0.2",
"lint-staged": "^10.0.7",
"node-sass": "^4.14.1",
"prettier": "^1.19.1",
"qrcode.react": "^1.0.0",
"react": "^16.12.0",
"react-codemirror2": "^7.2.1",
"react-color": "^2.18.1",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",

View File

@ -275,6 +275,11 @@ yarn install
yarn run start
```
## 更新日志
1. 添加在线编程模块在执行代码前先启动node服务 npm run server
2. 添加客服机器人模块[chatbot-antd](https://www.npmjs.com/package/chatbot-antd)
## 持续升级
正在升级1.3版本,敬请期待...

51
server.js Normal file
View File

@ -0,0 +1,51 @@
const Koa = require('koa');
const { resolve } = require('path');
const staticServer = require('koa-static');
const koaBody = require('koa-body');
const cors = require('koa2-cors');
const logger = require('koa-logger');
const fs = require('fs');
const app = new Koa();
app.use(staticServer(resolve(__dirname, './static')));
app.use(koaBody());
app.use(logger());
// 设置跨域
app.use(
cors({
origin: function(ctx) {
if (ctx.url.indexOf('/dooring') > -1) {
return '*'; // 允许来自所有域名请求
}
return '';
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'x-test-code'],
maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒
credentials: true,
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: [
'Content-Type',
'Authorization',
'Accept',
'x-requested-with',
'Content-Encoding',
],
}),
);
let htmlStr = '';
app.use(async (ctx, next) => {
console.log(ctx.url);
if (ctx.url === '/dooring/render') {
htmlStr = ctx.request.body;
ctx.body = 'success';
} else if (ctx.url.indexOf('/html') === 0) {
ctx.type = 'html';
ctx.body = htmlStr;
}
});
app.listen(80);

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -11,9 +11,24 @@ body {
--sk-color: #06c;
}
.ant-btn-primary {
color: #fff;
background: #2F54EB !important;
border-color: #2F54EB !important;
}
.ant-btn.ant-btn-link {
color: #2F54EB !important;
}
.ant-btn-background-ghost.ant-btn-primary {
color: #2F54EB !important;
border-color: #2F54EB !important;
}
@import '~react-grid-layout/css/styles.css';
@import '~react-resizable/css/styles.css';
@import '~zarm/dist/zarm.min.css';
.react-grid-item{
overflow: hidden;
}
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/material.css';

View File

@ -29,12 +29,13 @@
align-items: center;
flex-direction: column;
justify-content: center;
width: 260px;
height: 260px;
width: 220px;
height: 220px;
border-radius: 6px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0);
font-size: 60px;
font-size: 56px;
border: 1px solid #f0f0f0;
margin-right: 30px;
cursor: pointer;
&:hover {
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
@ -43,8 +44,8 @@
margin-top: 10px;
font-size: 20px;
}
&:first-child {
margin-right: 30px;
&:last-child {
margin-right: 0;
}
}
}

View File

@ -1,7 +1,12 @@
import React from 'react';
import { Tabs, message } from 'antd';
import { history } from 'umi';
import { MobileOutlined, ConsoleSqlOutlined, GithubOutlined } from '@ant-design/icons';
import {
MobileOutlined,
ConsoleSqlOutlined,
GithubOutlined,
CodeOutlined,
} from '@ant-design/icons';
import styles from './index.less';
const { TabPane } = Tabs;
@ -10,8 +15,10 @@ const Home = () => {
const handleGo = (type: string) => {
if (type === 'H5') {
history.push('/editor?tid=123456');
} else {
} else if (type === 'PC') {
message.error('该功能暂未开放, 敬请关注...');
} else {
history.push('/ide');
}
};
return (
@ -61,6 +68,10 @@ const Home = () => {
<MobileOutlined />
<div>H5页面</div>
</div>
<div className={styles.card} onClick={() => handleGo('online')}>
<CodeOutlined />
<div>线</div>
</div>
<div className={styles.card} onClick={() => handleGo('PC')}>
<ConsoleSqlOutlined />
<div></div>

59
src/pages/ide/index.less Normal file
View File

@ -0,0 +1,59 @@
.wrap {
display: flex;
flex-direction: column;
.header {
position: relative;
z-index: 10;
padding-left: 30px;
padding-right: 30px;
display: flex;
align-items: center;
height: 42px;
background: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.logoArea {
width: 300px;
font-size: 18px;
color: #2f54eb;
.backBtn {
display: inline-block;
padding: 12px 10px;
margin-right: 22px;
cursor: pointer;
}
.logo {
display: inline-block;
width: 38px;
overflow: hidden;
border-radius: 3px;
vertical-align: middle;
img {
width: 100%;
}
}
}
.operationBar {
margin-left: auto;
}
}
}
.contentWrap {
display: flex;
}
.codeWrap {
width: calc(100vw - 440px);
min-height: 560px;
}
:global(.cm-s-material.CodeMirror) {
height: 694px;
overflow: auto;
}
.previewWrap {
margin: 20px 30px;
width: 375px;
min-width: 375px;
min-height: 679px;
overflow: auto;
border: 12px solid #000;
border-radius: 20px;
}

138
src/pages/ide/index.tsx Normal file
View File

@ -0,0 +1,138 @@
import React from 'react';
import { UnControlled as CodeMirror } from 'react-codemirror2';
import { useState, useEffect } from 'react';
import { Button } from 'antd';
import { saveAs } from 'file-saver';
import { history } from 'umi';
import Logo from '@/assets/logo.png';
import styles from './index.less';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');
interface CursorData {
line: number;
ch: number;
[property: string]: number;
}
const isDev = process.env.NODE_ENV === 'development';
const serverUrl = isDev ? 'http://localhost:3000' : 'http://localhost:3000';
let timer: any = null;
let html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,body {
margin: 0;
padding: 0;
}
#root {
padding-top: 200px;
text-align: center;
}
p {
padding: 0 10px;
color: #06c;
line-height: 1.8;
font-size: 12px;
}
</style>
</head>
<body>
<div id="root">
<img src="http://io.nainor.com/uploads/logo_1747374040f.png" />
<p>
(H5编辑器)H5-Dooring是一款功能强大H5可视化页面配置解决方案
便H5落地页最佳实践
</p>
</div>
</body>
</html>
`;
export default function() {
const [isUpdate, setUpdate] = useState(false);
const [cursor, setCursor] = useState<CursorData>({ line: 1, ch: 1 });
const handleChange = (editor: any, data: any, value: string) => {
// codeStr = value
fetchPage(value);
};
const fetchPage = (v: string) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fetch(`${serverUrl}/dooring/render`, { method: 'POST', body: v }).then(res => {
html = v;
setUpdate(prev => !prev);
});
}, 1000);
};
const downLoadHtml = () => {
var file = new File([html], `${Date.now()}.html`, { type: 'text/html;charset=utf-8' });
saveAs(file);
};
const onCursorChange = (editor: any, data: CursorData) => {
const { line, ch } = data;
setCursor({ line, ch });
};
useEffect(() => {
fetch(`${serverUrl}/dooring/render`, { method: 'POST', body: html }).then(res => {
setUpdate(prev => !prev);
});
}, []);
return (
<div className={styles.wrap}>
<div className={styles.header}>
<div className={styles.logoArea}>
<div className={styles.logo} title="Dooring">
<a href="http://io.nainor.com/h5_visible">
<img src={Logo} alt="Dooring-强大的h5编辑器" />
</a>
</div>
&nbsp;&nbsp;| 线
</div>
<div className={styles.operationBar}>
<Button type="primary" onClick={downLoadHtml} style={{ marginRight: '10px' }}>
</Button>
<Button danger onClick={downLoadHtml}>
</Button>
</div>
</div>
<div className={styles.contentWrap}>
<div className={styles.codeWrap}>
<CodeMirror
className={styles.codeWrap}
value={html}
options={{
mode: 'xml',
theme: 'material',
lineNumbers: true,
}}
onChange={handleChange}
cursor={cursor}
onCursor={onCursorChange}
/>
</div>
<div className={styles.previewWrap}>
<iframe
src={`${serverUrl}/html?flag=${isUpdate}`}
style={{ width: '100%', height: '100%', margin: 0, padding: 0, border: 'none' }}
></iframe>
</div>
</div>
</div>
);
}

View File

@ -4453,6 +4453,11 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror@^5.57.0:
version "5.57.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.57.0.tgz#d26365b72f909f5d2dbb6b1209349ca1daeb2d50"
integrity sha512-WGc6UL7Hqt+8a6ZAsj/f1ApQl3NPvHY/UQSzG6fB6l4BjExgVdhFaxd7mRTw1UCiYe/6q86zHP+kfvBQcZGvUg==
collect-v8-coverage@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
@ -11585,6 +11590,11 @@ rc-virtual-list@^3.0.1, rc-virtual-list@^3.0.3:
rc-resize-observer "^0.2.3"
rc-util "^5.0.7"
react-codemirror2@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-7.2.1.tgz#38dab492fcbe5fb8ebf5630e5bb7922db8d3a10c"
integrity sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==
react-color@^2.18.1:
version "2.18.1"
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.18.1.tgz#2cda8cc8e06a9e2c52ad391a30ddad31972472f4"