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",
"query-string": "^6.13.7",
"react-color": "^2.18.1",
"react-json-view": "^1.19.1"
"react-json-view": "^1.19.1",
"react-split-pane": "^2.0.3"
},
"devDependencies": {
"@types/lodash.merge": "^4.6.6",

2
packages/core/rollup.config.js

@ -1,5 +1,6 @@
import pkg from './package.json';
import postcss from 'rollup-plugin-postcss';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript';
import rollupBaseConfig from '../../rollup.config';
@ -7,6 +8,7 @@ export default Object.assign(rollupBaseConfig, {
plugins: [
postcss(),
typescript(),
commonjs()
],
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;
flex-direction: row;
justify-content: space-between;
flex: 1;
}
}
.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%;
}
.left{
padding:10px;
flex:1;
border:1px solid #eee;
.ant-tabs-content-holder {
height: 100%;
}
.editor{
width:100%;
height:600px;
}
.ant-tabs-content {
height: 100%;
overflow: scroll;
}
.visualInput{
width:100%;
}
}
.sc-bdVaJa.cjjWdp {
opacity: 1;
background-color: #efefef;
.right{
height:100%;
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;
}
&:hover {
border-color: transparent;
}
}
}

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

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

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

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

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

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

Loading…
Cancel
Save