Browse Source

feat: refactor codeRun UI

master
smallstonesk 4 years ago
parent
commit
7152cd9ef4
  1. 3
      packages/core/package.json
  2. 2
      packages/core/rollup.config.js
  3. 81
      packages/core/src/components/codeRun/index.module.less
  4. 192
      packages/core/src/components/codeRun/index.tsx
  5. 24
      packages/core/src/components/codeRun/inputPanel/index.module.less
  6. 166
      packages/core/src/components/codeRun/inputPanel/index.tsx
  7. 39
      packages/core/src/components/codeRun/json.ts
  8. 2
      packages/core/src/components/console/index.tsx
  9. 2
      packages/core/src/mods/settingBar/components/code/index.module.less
  10. 11
      packages/core/src/mods/settingBar/components/code/index.tsx
  11. 6
      packages/core/src/mods/settingBar/mods/testCase/index.tsx

3
packages/core/package.json

@ -48,7 +48,8 @@
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"query-string": "^6.13.7", "query-string": "^6.13.7",
"react-color": "^2.18.1", "react-color": "^2.18.1",
"react-json-view": "^1.19.1" "react-json-view": "^1.19.1",
"react-split-pane": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.merge": "^4.6.6", "@types/lodash.merge": "^4.6.6",

2
packages/core/rollup.config.js

@ -1,5 +1,6 @@
import pkg from './package.json'; import pkg from './package.json';
import postcss from 'rollup-plugin-postcss'; import postcss from 'rollup-plugin-postcss';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript'; import typescript from 'rollup-plugin-typescript';
import rollupBaseConfig from '../../rollup.config'; import rollupBaseConfig from '../../rollup.config';
@ -7,6 +8,7 @@ export default Object.assign(rollupBaseConfig, {
plugins: [ plugins: [
postcss(), postcss(),
typescript(), typescript(),
commonjs()
], ],
output: [ output: [
{ {

81
packages/core/src/components/codeRun/index.module.less

@ -1,44 +1,55 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
.container { .pane {
display: flex; display: flex;
flex-direction: row; flex: 1;
justify-content: space-between; }
}
.card {
display: flex;
flex-direction: column;
flex: 1;
background-color: #ffffff;
.cardTitleText {
margin: 10px 24px 0;
font-size: 16px;
font-weight: 500;
color: #000000;
}
.cardBody {
padding: 10px 24px;
height: 100%;
overflow: scroll;
}
}
:global {
.ant-tabs {
height: 100%; height: 100%;
}
.left{ .ant-tabs-content-holder {
padding:10px; height: 100%;
flex:1; }
border:1px solid #eee;
.editor{ .ant-tabs-content {
width:100%; height: 100%;
height:600px; overflow: scroll;
} }
.visualInput{ .sc-bdVaJa.cjjWdp {
width:100%; opacity: 1;
} background-color: #efefef;
}
.right{ &:hover {
height:100%; border-color: transparent;
flex:1;
display: flex;
flex-direction: column;
justify-content: space-between;
.rightTop{
height:50%;
padding:10px;
flex:1;
border:1px solid #eee;
overflow-y: auto;
}
.rightBottom{
height:300px;
flex:1;
border:1px solid #eee;
}
} }
} }
}

192
packages/core/src/components/codeRun/index.tsx

@ -1,135 +1,91 @@
import React from 'react'; import React, {
import JsonView from 'react-json-view'; useState,
import { Tabs, Form, Input, Button, Space } from 'antd'; useCallback,
const { TabPane } = Tabs; } from 'react';
import CodeEditor from '../codeEditor';
import Console from '../console';
import { inputJson, outputJson } from './json'
import styles from './index.module.less'; import styles from './index.module.less';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
interface MyFormItemProps {
type: string
}
const MyFormItem: React.FC<MyFormItemProps> = (props) => { import Console from '../console';
return ( import InputPanel from './inputPanel';
<> import JsonView from 'react-json-view';
<h3>{props.type}</h3>
<Form.List name={props.type}> // FIXME: https://github.com/tomkp/react-split-pane/issues/541
{(fields, { add, remove }) => ( // @ts-ignore
<> import SplitPane from 'react-split-pane/lib/SplitPane';
{fields.map(field => ( // @ts-ignore
<Space key={field.key} align="baseline"> import Pane from 'react-split-pane/lib/Pane';
<Form.Item
{...field}
label="key"
name={[field.name, 'key']}
fieldKey={[field.fieldKey, 'key']}
>
<Input />
</Form.Item>
<Form.Item
{...field}
label="value"
name={[field.name, 'value']}
fieldKey={[field.fieldKey, 'value']}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}></Button>
</Form.Item>
</>
)}
</Form.List>
</>
)
}
interface VisualInputProps { const defaultInput = {
onChange: (value: object) => void pipe: {},
context: {},
payload: {},
config: {}
};
interface ICardProps {
title: string;
} }
const VisualInput: React.FC<VisualInputProps> = (props) => { const Card: React.FC<ICardProps> = (props) => {
const [form] = Form.useForm() const {title} = props;
const PAYLOAD = 'payload', PIPE = 'pipe', CONTEXT = 'context', CONFIG = 'config'
const onChange = () => {
props.onChange(form.getFieldsValue())
}
return ( return (
<div className={styles.visualInput}> <div className={styles.card}>
<Form form={form} name="form" autoComplete="off" onValuesChange={onChange}> <div className={styles.cardTitleText}>{title}</div>
<MyFormItem type={PAYLOAD} /> <div className={styles.cardBody}>
<MyFormItem type={PIPE} /> {props.children}
<MyFormItem type={CONTEXT} /> </div>
<MyFormItem type={CONFIG} />
</Form>
</div> </div>
) );
} };
interface CodeRunProps {
onChange: (value: object) => void interface ICodeRunProps {
} }
const CodeRun: React.FC<CodeRunProps> = (props) => { const CodeRun: React.FC<ICodeRunProps> = (props) => {
const onFormChange = (value: any) => {
props.onChange(value) const [input, setInput] = useState(defaultInput);
} const [output, setOutput] = useState({});
const onJsonChange = (ev: any, newCode: any): void => {
const codeStr = newCode.slice(15) const onChangeInput = useCallback((val: any) => {
const formatCodeStr = codeStr.replace(/ |\/\/.*/g, '').replace(/\s/g, '').replace(/(\w+):/g, '"$1":').replace(/'/g, '"') setInput(val);
try { }, []);
props.onChange(JSON.parse(formatCodeStr))
} catch (err) {
props.onChange({})
}
};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.left}> <SplitPane split={'horizontal'}>
<Tabs <Pane initialSize={'380px'} minSize={'43px'}>
type="card" <SplitPane split={'vertical'}>
tabBarGutter={0} <Pane className={styles.pane} minSize={'360px'} maxSize={'660px'}>
defaultActiveKey={'basic'} <Card title={'输入'}>
tabBarStyle={{ display: 'flex', flex: 1, justifyContent: 'center', alignItems: 'center' }} <InputPanel
> data={input}
<TabPane className={styles.tabPane} tab={'可视化'} key={'visualTab'}> onChange={onChangeInput}
<VisualInput onChange={onFormChange} /> />
</TabPane> </Card>
<TabPane className={styles.tabPane} tab={'JSON输入'} key={'jsonTab'}> </Pane>
<CodeEditor <Pane className={styles.pane}>
value={inputJson} <Card title={'输出'}>
className={styles.editor} <JsonView
onChange={onJsonChange} name={null}
/> collapsed={false}
</TabPane> enableClipboard={false}
</Tabs> displayDataTypes={false}
</div> displayObjectSize={false}
src={{}}
<div className={styles.right}> />
<div className={styles.rightTop}> </Card>
<p></p> </Pane>
<JsonView </SplitPane>
name={null} </Pane>
collapsed={false} <Pane className={styles.pane} minSize={'150px'}>
enableClipboard={false} <Card title={'控制台'}>
displayDataTypes={false} <Console />
displayObjectSize={false} </Card>
src={outputJson} </Pane>
/> </SplitPane>
</div>
<div className={styles.rightBottom}>
<Console />
</div>
</div>
</div> </div>
);
)
} }
export default CodeRun; export default CodeRun;

24
packages/core/src/components/codeRun/inputPanel/index.module.less

@ -0,0 +1,24 @@
.itemHeader {
margin-bottom: 8px;
.itemTitleText {
font-size: 15px;
font-weight: 500;
}
.itemDescText {
margin-left: 5px;
font-size: 12px;
color: #999;
}
}
:global {
.ant-tabs-nav {
margin-bottom: 5px !important;
}
.ant-form-item {
margin-bottom: 8px;
}
}

166
packages/core/src/components/codeRun/inputPanel/index.tsx

@ -0,0 +1,166 @@
import React, {
useRef,
useEffect,
} from 'react';
import styles from './index.module.less';
import CodeEditor from '../../codeEditor';
import { Tabs, Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
const inputItems = [
{ type: 'pipe', desc: '上一节点返回值' },
{ type: 'context', desc: '当前逻辑流程公用数据' },
{ type: 'payload', desc: '当前逻辑流程通用数据' },
{ type: 'config', desc: '当前节点投放配置' },
];
interface IVisualFormItemProps {
type: string;
desc: string;
}
const VisualFormItem: React.FC<IVisualFormItemProps> = (props) => {
const { type, desc } = props;
return (
<React.Fragment>
<div className={styles.itemHeader}>
<span className={styles.itemTitleText}>{type}</span>
<span className={styles.itemDescText}>{desc}</span>
</div>
<Form.List name={type}>
{(fields, { add, remove }) => (
<React.Fragment>
{fields.map(field => (
<Space key={field.key} align={'baseline'}>
<Form.Item
{...field}
label={'key'}
name={[field.name, 'key']}
fieldKey={[field.fieldKey, 'key']}
>
<Input />
</Form.Item>
<Form.Item
{...field}
label={'value'}
name={[field.name, 'value']}
fieldKey={[field.fieldKey, 'value']}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button
block={true}
type={'dashed'}
icon={<PlusOutlined />}
onClick={() => add()}
>
</Button>
</Form.Item>
</React.Fragment>
)}
</Form.List>
</React.Fragment>
);
};
interface IVisualPanelProps {
data: any;
onChange: (value: object) => void
}
const VisualPanel: React.FC<IVisualPanelProps> = (props) => {
const { data, onChange } = props;
const [form] = Form.useForm();
useEffect(() => {
const filedsValue: { [key: string]: any } = {};
for (const { type } of inputItems) {
const mockValue = data[type] || {};
filedsValue[type] = Object.keys(mockValue).map(key => ({ key, value: mockValue[key] }));
}
form.setFieldsValue(filedsValue);
}, [data]);
const onFormChange = () => {
const input: { [key: string]: any } = {};
const filedsValue = form.getFieldsValue();
for (const { type } of inputItems) {
const mockValue = filedsValue[type] || [];
input[type] = mockValue.reduce((prev: any, cur: { key: string, value: any }) => {
const { key, value } = cur;
prev[key] = value;
return prev;
}, {});
}
onChange(input);
};
return (
<div className={styles.visualInput}>
<Form
form={form}
autoComplete={'on'}
onChange={onFormChange}
>
{inputItems.map(({ type, desc }, index) => (
<VisualFormItem
key={index}
type={type}
desc={desc}
/>
))}
</Form>
</div>
);
};
interface IInputPanelProps {
data: any;
onChange: (data: any) => void;
}
const InputPanel: React.FC<IInputPanelProps> = (props) => {
const cache: any = useRef({});
const { data, onChange } = props;
const onVisualChange = (newForm: any) => {
clearTimeout(cache.current.timer);
cache.current.timer = setTimeout(() => onChange(newForm), 1000);
};
const onEditorChange = (ev: any, newCode: string | undefined = '') => {
clearTimeout(cache.current.timer);
cache.current.timer = setTimeout(() => {
try {
onChange(JSON.parse(newCode));
} catch (err) {
console.log(err);
}
}, 1000);
};
return (
<Tabs type={'card'} defaultActiveKey={'visualTab'}>
<Tabs.TabPane className={styles.tabPane} tab={'可视化模式'} key={'visualTab'}>
<VisualPanel data={data} onChange={onVisualChange} />
</Tabs.TabPane>
<Tabs.TabPane className={styles.tabPane} tab={'源码模式'} key={'jsonTab'}>
<CodeEditor
height={'100%'}
language={'json'}
value={JSON.stringify(data, null, 2)}
onChange={onEditorChange}
/>
</Tabs.TabPane>
</Tabs>
);
};
export default InputPanel;

39
packages/core/src/components/codeRun/json.ts

@ -1,39 +0,0 @@
const inputJson = `export default {
// 上游数据
pipe: {
success: false,
message: '未登录'
},
// 公用数据
context: {
data: {
isLogin: false
}
},
// 传入载荷
payload: {
name:'xxx'
},
// 节点配置项
config: {
hasLog: true
}
}`;
const outputJson = {
pipe: {
success: false,
message: '未登录'
},
context: {
data: {
isLogin: false,
name: 'xxx'
}
}
}
export {
inputJson,
outputJson
}

2
packages/core/src/components/console/index.tsx

@ -69,7 +69,7 @@ const MyConsole: React.FC = () => {
} }
return ( return (
<div style={{ height: '100%', background: '#000', padding: 10 }}> <div style={{ height: '100%', background: 'rgb(39,40,35)', padding: 10 }}>
<div style={{ height: '100%', overflow: 'auto' }}>{renderLogList(logList)}</div> <div style={{ height: '100%', overflow: 'auto' }}>{renderLogList(logList)}</div>
</div> </div>
) )

2
packages/core/src/mods/settingBar/components/code/index.module.less

@ -1,5 +1,4 @@
.container { .container {
margin-top: 20px; margin-top: 20px;
.titleText { .titleText {
@ -13,7 +12,6 @@
} }
.editModal { .editModal {
:global { :global {
.ant-modal-body { .ant-modal-body {
padding: 0; padding: 0;

11
packages/core/src/mods/settingBar/components/code/index.tsx

@ -125,11 +125,6 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
setEditorInst(editor); setEditorInst(editor);
}; };
const onChange = (value: any) => {
// 运行节点代码时传入的json,包括payload、pipe、context、config
// console.log({ value })
}
return ( return (
<Modal <Modal
className={styles.editModal} className={styles.editModal}
@ -150,15 +145,15 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
/> />
<Modal <Modal
className={styles.runModal} className={styles.runModal}
width={1400} width={1000}
title="执行代码" title={'执行代码'}
okText={'运行'} okText={'运行'}
visible={codeRunVisible} visible={codeRunVisible}
cancelText={'取消'} cancelText={'取消'}
onOk={onRunOk} onOk={onRunOk}
onCancel={onRunCancel} onCancel={onRunCancel}
> >
<CodeRun onChange={onChange} /> <CodeRun />
</Modal> </Modal>
</Modal> </Modal>
); );

6
packages/core/src/mods/settingBar/mods/testCase/index.tsx

@ -59,9 +59,9 @@ const EditModal: React.FC<IEditModalProps> = (props): JSX.Element => {
return ( return (
<Modal <Modal
width={1400} width={1000}
centered={true} centered={true}
bodyStyle={{ height: 700, overflowY: 'auto' }} bodyStyle={{ padding: 0, height: 600 }}
title={'执行代码'} title={'执行代码'}
visible={visible} visible={visible}
okText={'运行'} okText={'运行'}
@ -69,7 +69,7 @@ const EditModal: React.FC<IEditModalProps> = (props): JSX.Element => {
onOk={onOk} onOk={onOk}
onCancel={onCancel} onCancel={onCancel}
> >
<CodeRun onChange={onChange} /> <CodeRun />
</Modal> </Modal>
); );
}; };

Loading…
Cancel
Save