smallstonesk
4 years ago
15 changed files with 233 additions and 387 deletions
@ -0,0 +1,11 @@ |
|||||
|
.depsInfoModalContent { |
||||
|
padding-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.modal { |
||||
|
:global { |
||||
|
.ant-modal-body { |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,116 @@ |
|||||
|
import React, { |
||||
|
useState, |
||||
|
useEffect |
||||
|
} from 'react'; |
||||
|
|
||||
|
import 'antd/es/modal/style'; |
||||
|
import styles from './index.module.less'; |
||||
|
|
||||
|
import { Graph } from '@antv/x6'; |
||||
|
import { Modal, message } from 'antd'; |
||||
|
import JsonView from 'react-json-view'; |
||||
|
import { safeParse } from '../../../utils'; |
||||
|
import analyzeDeps from '../../../utils/analyzeDeps'; |
||||
|
import CodeEditor from '../../../components/codeEditor'; |
||||
|
|
||||
|
interface IProps { |
||||
|
title?: string; |
||||
|
flowChart: Graph; |
||||
|
} |
||||
|
|
||||
|
const CodeEditModal: React.FC<IProps> = (props) => { |
||||
|
|
||||
|
const { title = '编辑代码', flowChart } = props; |
||||
|
const [code, setCode] = useState<string>(''); |
||||
|
const [visible, setVisible] = useState<boolean>(false); |
||||
|
|
||||
|
const updateNodeCode = (code: string): void => { |
||||
|
const cell = flowChart.getSelectedCells()[0]; |
||||
|
const { dependencies } = cell.getData(); |
||||
|
cell.setData({ code }); |
||||
|
message.success('代码保存成功', 1); |
||||
|
const excludeDeps = safeParse(dependencies); |
||||
|
analyzeDeps(code, Object.keys(excludeDeps)).then((deps): void => { |
||||
|
if (Object.keys(deps).length > 0) { |
||||
|
Modal.info({ |
||||
|
title: '检测到您的代码有新依赖,已为您自动更新', |
||||
|
content: ( |
||||
|
<div className={styles.depsInfoModalContent}> |
||||
|
<JsonView |
||||
|
src={deps} |
||||
|
name={'dependencies'} |
||||
|
collapsed={false} |
||||
|
enableClipboard={false} |
||||
|
displayDataTypes={false} |
||||
|
displayObjectSize={false} |
||||
|
/> |
||||
|
</div> |
||||
|
), |
||||
|
onOk() { |
||||
|
const newDeps = { ...excludeDeps, ...deps }; |
||||
|
cell.setData({ |
||||
|
code, |
||||
|
dependencies: JSON.stringify(newDeps, null, 2) |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// life
|
||||
|
useEffect(() => { |
||||
|
const handler = () => setVisible(true); |
||||
|
flowChart.on('graph:editCode', handler); |
||||
|
return () => { |
||||
|
flowChart.off('graph:editCode', handler); |
||||
|
}; |
||||
|
}, []); |
||||
|
useEffect(() => { |
||||
|
if (visible) { |
||||
|
const cell = flowChart.getSelectedCells()[0]; |
||||
|
const { code } = cell.getData() || {}; |
||||
|
setCode(code); |
||||
|
} else { |
||||
|
setCode(''); |
||||
|
} |
||||
|
}, [visible]); |
||||
|
|
||||
|
// events
|
||||
|
const onOk = (): void => { |
||||
|
setVisible(false); |
||||
|
updateNodeCode(code); |
||||
|
}; |
||||
|
const onCancel = (): void => { |
||||
|
setVisible(false); |
||||
|
} |
||||
|
const onChangeCode = (ev: any, newCode: string | undefined = ''): void => { |
||||
|
setCode(newCode); |
||||
|
}; |
||||
|
const onSaveCode = (newCode: string) => { |
||||
|
updateNodeCode(newCode); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Modal |
||||
|
className={styles.modal} |
||||
|
width={1000} |
||||
|
title={title} |
||||
|
okText={'保存'} |
||||
|
visible={visible} |
||||
|
cancelText={'取消'} |
||||
|
onOk={onOk} |
||||
|
onCancel={onCancel} |
||||
|
> |
||||
|
<CodeEditor |
||||
|
value={code} |
||||
|
width={'100%'} |
||||
|
height={'600px'} |
||||
|
onChange={onChangeCode} |
||||
|
onSave={onSaveCode} |
||||
|
/> |
||||
|
</Modal> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default CodeEditModal; |
@ -0,0 +1,8 @@ |
|||||
|
.modal { |
||||
|
:global { |
||||
|
.ant-modal-body { |
||||
|
padding: 0; |
||||
|
height: 650px; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
import React, { |
||||
|
useState, |
||||
|
useEffect, |
||||
|
useCallback |
||||
|
} from 'react'; |
||||
|
|
||||
|
import styles from './index.module.less'; |
||||
|
|
||||
|
import { Modal } from 'antd'; |
||||
|
import { Graph } from '@antv/x6'; |
||||
|
import CodeRun from '../../../components/codeRun'; |
||||
|
|
||||
|
interface IEditModalProps { |
||||
|
title?: string; |
||||
|
flowChart: Graph; |
||||
|
} |
||||
|
|
||||
|
const CodeRunModal: React.FC<IEditModalProps> = (props): JSX.Element => { |
||||
|
|
||||
|
const { title = '执行代码', flowChart } = props; |
||||
|
const [visible, setVisible] = useState(false); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const handler = () => setVisible(true); |
||||
|
flowChart.on('graph:runCode', handler); |
||||
|
return () => { |
||||
|
flowChart.off('graph:runCode', handler); |
||||
|
}; |
||||
|
}, [flowChart]); |
||||
|
|
||||
|
// events
|
||||
|
const onClose = useCallback((): void => { |
||||
|
setVisible(false); |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<Modal |
||||
|
className={styles.modal} |
||||
|
width={1000} |
||||
|
title={title} |
||||
|
visible={visible} |
||||
|
footer={null} |
||||
|
onCancel={onClose} |
||||
|
> |
||||
|
<CodeRun flowChart={flowChart}/> |
||||
|
</Modal> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default CodeRunModal; |
@ -1,20 +0,0 @@ |
|||||
.container { |
|
||||
margin-top: 20px; |
|
||||
|
|
||||
.titleText { |
|
||||
font-size: 14px; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.btn { |
|
||||
margin-top: 5px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.editModal { |
|
||||
:global { |
|
||||
.ant-modal-body { |
|
||||
padding: 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,164 +0,0 @@ |
|||||
import React, { useState, useEffect } from 'react'; |
|
||||
|
|
||||
import 'antd/es/modal/style'; |
|
||||
import 'antd/es/button/style'; |
|
||||
import styles from './index.module.less'; |
|
||||
|
|
||||
import { Graph } from '@antv/x6'; |
|
||||
import { Button, Modal, message } from 'antd'; |
|
||||
import { monaco, EditorDidMount } from '@monaco-editor/react'; |
|
||||
import CodeEditor from '../../../../components/codeEditor'; |
|
||||
import CodeRun from '../../../../components/codeRun'; |
|
||||
interface IProps { |
|
||||
value: any; |
|
||||
name: string; |
|
||||
title: string; |
|
||||
flowChart: Graph; |
|
||||
onValueChange: (value: string) => void; |
|
||||
} |
|
||||
|
|
||||
// NOTE: avoid monaco init many times
|
|
||||
let KeyMod: any = {}; |
|
||||
let KeyCode: any = {}; |
|
||||
monaco.init().then(monaco => { |
|
||||
KeyMod = monaco.KeyMod; |
|
||||
KeyCode = monaco.KeyCode; |
|
||||
}); |
|
||||
|
|
||||
const Code: React.FC<IProps> = (props) => { |
|
||||
const { title, value, onValueChange, flowChart } = props; |
|
||||
const [visible, setVisible] = useState<boolean>(false); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
flowChart.on('settingBar:clickEditCode', onClickEdit); |
|
||||
return () => { |
|
||||
flowChart.off('settingBar:clickEditCode', onClickEdit); |
|
||||
}; |
|
||||
}, []); |
|
||||
|
|
||||
// events
|
|
||||
const onClickEdit = (): void => { |
|
||||
setVisible(true); |
|
||||
}; |
|
||||
const onClickCancel = (): void => { |
|
||||
setVisible(false); |
|
||||
}; |
|
||||
const onClickOk = (newCode: string): void => { |
|
||||
setVisible(false); |
|
||||
onValueChange(newCode); |
|
||||
}; |
|
||||
const onSave = (newCode: string): void => { |
|
||||
onValueChange(newCode); |
|
||||
}; |
|
||||
|
|
||||
return ( |
|
||||
<div className={styles.container}> |
|
||||
<p className={styles.titleText}>{title}</p> |
|
||||
<Button block={true} className={styles.btn} onClick={onClickEdit}> |
|
||||
编辑 |
|
||||
</Button> |
|
||||
<EditModal |
|
||||
title={title} |
|
||||
value={value} |
|
||||
visible={visible} |
|
||||
flowChart={flowChart} |
|
||||
onSave={onSave} |
|
||||
onOk={onClickOk} |
|
||||
onCancel={onClickCancel} |
|
||||
/> |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
interface IEditorModalProps { |
|
||||
visible: boolean; |
|
||||
title: string; |
|
||||
value: string; |
|
||||
flowChart: Graph; |
|
||||
onSave: (val: string) => void; |
|
||||
onOk: (val: string) => void; |
|
||||
onCancel: () => void; |
|
||||
} |
|
||||
|
|
||||
const EditModal: React.FC<IEditorModalProps> = (props) => { |
|
||||
const { visible, title, value, flowChart, onSave, onOk, onCancel } = props; |
|
||||
const [code, setCode] = useState<string>(value); |
|
||||
const [editorInst, setEditorInst] = useState<any>(); |
|
||||
const [codeRunVisible, setCodeRunVisible] = useState<boolean>(false); |
|
||||
|
|
||||
// life
|
|
||||
useEffect(() => { |
|
||||
// set value when opening modal
|
|
||||
// clear content when closing modal
|
|
||||
if (visible) { |
|
||||
setCode(value); |
|
||||
} else { |
|
||||
setCode(''); |
|
||||
} |
|
||||
}, [visible]); |
|
||||
useEffect(() => { |
|
||||
if (editorInst) { |
|
||||
// NOTE: how to add command(https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html#addcommand)
|
|
||||
editorInst.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, () => { |
|
||||
onSave(editorInst.getValue()); |
|
||||
message.success('保存成功', 1); |
|
||||
}); |
|
||||
} |
|
||||
}, [editorInst, onSave]); |
|
||||
|
|
||||
// events
|
|
||||
const onEditOk = (): void => { |
|
||||
// onOk(code);
|
|
||||
setCodeRunVisible(true) |
|
||||
}; |
|
||||
|
|
||||
const onRunOk = (): void => { |
|
||||
// setCodeRunVisible(false)
|
|
||||
} |
|
||||
const onRunCancel = (): void => { |
|
||||
setCodeRunVisible(false) |
|
||||
} |
|
||||
const onChangeCode = (ev: any, newCode: string | undefined = ''): void => { |
|
||||
if (newCode !== code) { |
|
||||
setCode(newCode); |
|
||||
} |
|
||||
}; |
|
||||
const onEditorDidMount: EditorDidMount = (getEditorValue, editor) => { |
|
||||
setEditorInst(editor); |
|
||||
}; |
|
||||
|
|
||||
return ( |
|
||||
<Modal |
|
||||
className={styles.editModal} |
|
||||
width={1000} |
|
||||
title={title} |
|
||||
okText={'执行代码'} |
|
||||
visible={visible} |
|
||||
cancelText={'取消'} |
|
||||
onOk={onEditOk} |
|
||||
onCancel={onCancel} |
|
||||
> |
|
||||
<CodeEditor |
|
||||
value={code} |
|
||||
width={'100%'} |
|
||||
height={'600px'} |
|
||||
onChange={onChangeCode} |
|
||||
editorDidMount={onEditorDidMount} |
|
||||
/> |
|
||||
<Modal |
|
||||
className={styles.runModal} |
|
||||
width={1000} |
|
||||
title={'执行代码'} |
|
||||
okText={'运行'} |
|
||||
visible={codeRunVisible} |
|
||||
cancelText={'取消'} |
|
||||
onOk={onRunOk} |
|
||||
onCancel={onRunCancel} |
|
||||
> |
|
||||
<CodeRun flowChart={flowChart}/> |
|
||||
</Modal> |
|
||||
</Modal> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default Code; |
|
@ -1,23 +0,0 @@ |
|||||
.container { |
|
||||
|
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
padding: 0 20px; |
|
||||
height: 100%; |
|
||||
|
|
||||
.empty { |
|
||||
margin-top: 100px; |
|
||||
} |
|
||||
|
|
||||
.footContainer { |
|
||||
|
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: flex-end; |
|
||||
margin-top: 20px; |
|
||||
|
|
||||
.saveBtn { |
|
||||
margin-left: 20px; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,123 +0,0 @@ |
|||||
import React, { useState, useEffect } from 'react'; |
|
||||
|
|
||||
import styles from './index.module.less'; |
|
||||
|
|
||||
import { Cell } from '@antv/x6'; |
|
||||
import { Button, Empty } from 'antd'; |
|
||||
import Input from '../../components/input'; |
|
||||
import Checkbox from '../../components/checkbox'; |
|
||||
import { safeParse } from '../../../../utils'; |
|
||||
|
|
||||
// FIXME
|
|
||||
// save original config in tempConfig when change to edit mode
|
|
||||
// and recover original config when click cancel button
|
|
||||
let tempConfig: any = null; |
|
||||
|
|
||||
enum Mode { |
|
||||
edit = 'edit', |
|
||||
readOnly = 'readOnly', |
|
||||
} |
|
||||
|
|
||||
interface IConfig { |
|
||||
[key: string]: any; |
|
||||
} |
|
||||
|
|
||||
interface IProps { |
|
||||
selectedCell: Cell; |
|
||||
} |
|
||||
|
|
||||
const Config: React.FC<IProps> = (props) => { |
|
||||
const { selectedCell } = props; |
|
||||
const [mode, setMode] = useState<Mode>(Mode.readOnly); |
|
||||
const [configData, setConfigData] = useState<IConfig>({}); |
|
||||
const [configSchema, setConfigSchema] = useState<IConfig>({}); |
|
||||
|
|
||||
// life
|
|
||||
useEffect(() => { |
|
||||
const { configSchema, configData = {} } = selectedCell.getData() || {}; |
|
||||
setConfigData(configData); |
|
||||
setConfigSchema(safeParse(configSchema)); |
|
||||
selectedCell.on('change:configSchema', (data: { configSchema: string }) => { |
|
||||
setConfigSchema(safeParse(data.configSchema)); |
|
||||
}); |
|
||||
return () => { |
|
||||
selectedCell.off('change:configSchema'); |
|
||||
}; |
|
||||
}, [selectedCell]); |
|
||||
|
|
||||
// events
|
|
||||
const onFieldValueChange = (key: string, value: any) => { |
|
||||
setConfigData(Object.assign({}, configData, { [key]: value })); |
|
||||
}; |
|
||||
const onClickEdit = (): void => { |
|
||||
setMode(Mode.edit); |
|
||||
tempConfig = configData; |
|
||||
}; |
|
||||
const onClickCancel = (): void => { |
|
||||
setMode(Mode.readOnly); |
|
||||
setConfigData(tempConfig); |
|
||||
}; |
|
||||
const onClickSave = (): void => { |
|
||||
setMode(Mode.readOnly); |
|
||||
selectedCell.setData({ configData }); |
|
||||
}; |
|
||||
|
|
||||
// no config schema
|
|
||||
if (!configSchema || Object.keys(configSchema).length === 0) { |
|
||||
return ( |
|
||||
<div className={styles.container}> |
|
||||
<Empty |
|
||||
className={styles.empty} |
|
||||
description={'请编辑投放配置schema'} |
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE} |
|
||||
/> |
|
||||
</div> |
|
||||
); |
|
||||
} else { |
|
||||
return ( |
|
||||
<div className={styles.container}> |
|
||||
{Object.keys(configSchema).map((key, idx) => { |
|
||||
const { title, description, type } = configSchema[key]; |
|
||||
const FieldComponent = Helper.getFieldComponent(type); |
|
||||
return ( |
|
||||
<FieldComponent |
|
||||
key={idx} |
|
||||
name={key} |
|
||||
title={title} |
|
||||
value={configData[key]} |
|
||||
description={description} |
|
||||
disabled={mode === Mode.readOnly} |
|
||||
onValueChange={(value: any) => onFieldValueChange(key, value)} |
|
||||
/> |
|
||||
); |
|
||||
})} |
|
||||
{mode === Mode.readOnly ? ( |
|
||||
<div className={styles.footContainer}> |
|
||||
<Button block onClick={onClickEdit}> |
|
||||
编辑 |
|
||||
</Button> |
|
||||
</div> |
|
||||
) : ( |
|
||||
<div className={styles.footContainer}> |
|
||||
<Button onClick={onClickCancel}>取消</Button> |
|
||||
<Button className={styles.saveBtn} type={'primary'} onClick={onClickSave}> |
|
||||
保存 |
|
||||
</Button> |
|
||||
</div> |
|
||||
)} |
|
||||
</div> |
|
||||
); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const Helper = { |
|
||||
getFieldComponent(type: string) { |
|
||||
if (type === 'boolean') { |
|
||||
return Checkbox; |
|
||||
} else { |
|
||||
return Input; |
|
||||
} |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
export default Config; |
|
Loading…
Reference in new issue