import React from 'react' import styles from './DraftEditor.less'; import { Modal, Form, Input, Button, message, Upload, Icon } from 'antd' import b from '../assets/专家经验系统切图/智能报告/B.png' import ii from '../assets/专家经验系统切图/智能报告/_.png' import it from '../assets/专家经验系统切图/智能报告/I.png' import h from '../assets/专家经验系统切图/智能报告/H.png' import blockQu from '../assets/专家经验系统切图/智能报告/blockQu.png' import code from '../assets/专家经验系统切图/智能报告/___.png' import prjectNum from '../assets/专家经验系统切图/智能报告/prjectNum.png' import num from '../assets/专家经验系统切图/智能报告/num.png' import t from '../assets/专家经验系统切图/智能报告/T.png' import lk from '../assets/专家经验系统切图/智能报告/lk.png' import pic from '../assets/专家经验系统切图/智能报告/pic.png' import video from '../assets/专家经验系统切图/智能报告/video.png' import sum from '../assets/专家经验系统切图/智能报告/sum.png' import attrs from '../assets/专家经验系统切图/智能报告/attr.png' import config from '../config/config' import MyBlockRender from './MyBlockRender' import { changeToDraftState2, changeFromDraftState2 } from '../utils/myutils' import { Editor, EditorState, AtomicBlockUtils, convertFromRaw, convertToRaw, CompositeDecorator, RichUtils } from 'draft-js'; const FormItem = Form.Item; function getBlockStyle(block) { switch (block.getType()) { case 'blockquote': return 'RichEditor-blockquote'; default: return null; } } const StyleControls = (props) => { const { editorState } = props; const selection = editorState.getSelection(); const blockType = editorState .getCurrentContent() .getBlockForKey(selection.getStartKey()) .getType(); var currentStyle = props.editorState.getCurrentInlineStyle(); return ( <div style={{ borderBottom: "1px solid gray", borderTop: "1px solid gray" ,height:50,paddingTop:10}}> {props.btns.map((fn) => <StyleButton key={fn.label} active={fn.type == "block" ? fn.style === blockType : currentStyle.has(fn.style)} label={fn} onToggle={fn.type == "block" ? props.toggleBlockType : props.toggleInlineStyle} style={fn.style} /> )} </div> ); }; export default class DraftEditor extends React.Component { constructor(props) { super(props); const value = props.value || {}; this.state = { editorState: value.editorState, modalVisible: false, fnKey: "", styleMap: { 'RED': { color: 'red', }, } }; this.setEditor = (editor) => { this.editor = editor; }; this.onChange = (editorState,callback) => { if (!('value' in this.props)) { this.setState({ editorState },()=>{ if(callback)callback() }); } this.triggerChange({ editorState },()=>{ if(callback)callback() }); } this.focusEditor = () => { if (this.editor) { this.editor.focus(); } }; this.triggerChange = (changedValue,callback) => { // Should provide an event to pass value to Form. const onChange = this.props.onChange; if (onChange) { onChange(Object.assign({}, this.state, changedValue)); } if(callback) callback(); }; } exchange=(data,editKey,callback)=>{ const blocks = changeFromDraftState2(this.state.editorState) const bs =blocks.blocks var b; for(var i=0;i<bs.length;i++){ if(bs[i].key==editKey){ b=bs[i]; break; } } const entityKey=b.entityRanges[0].key+""; blocks.entityMap[entityKey].data=data const editorState = changeToDraftState2(blocks) this.onChange(editorState,callback) } extends=(data,type,text,callback)=>{ var editorState = this.state.editorState const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity(type, 'IMMUTABLE', data); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); const xx = AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, text); if (!('value' in this.props)) { this.setState({ editorState: xx }, () => { callback() }); } this.triggerChange({ editorState: xx}, () => { callback() }); } urlChange(event) { const target = event.target; this.setState({ url: target.value }); } toggleBlockType = (blockType) => { this.onChange( RichUtils.toggleBlockType( this.state.editorState, blockType ) ); } toggleInlineStyle = (inlineStyle) => { this.onChange( RichUtils.toggleInlineStyle( this.state.editorState, inlineStyle ) ); } componentWillReceiveProps(nextProps) { // Should be a controlled component. if ('value' in nextProps) { const value = nextProps.value; this.setState(value); } } okHandle = () => { } insertAttr = () => { if (!('value' in this.props)) { this.setState({ modalVisible: true, fnKey: "attr" }); } this.triggerChange({ modalVisible: true, fnKey: "attr" }); } insertPic = () => { if (!('value' in this.props)) { this.setState({ modalVisible: true, fnKey: "image" }); } this.triggerChange({ modalVisible: true, fnKey: "image" }); } insertFormula = () => { if (!('value' in this.props)) { this.setState({ modalVisible: true, fnKey: "formula" }); } this.triggerChange({ modalVisible: true, fnKey: "formula" }); } insertVideo = () => { if (!('value' in this.props)) { this.setState({ modalVisible: true, fnKey: "video" }); } this.triggerChange({ modalVisible: true, fnKey: "video" }); } insertLink = () => { const x = this.state.editorState.getSelection().getStartOffset() const y = this.state.editorState.getSelection().getEndOffset() if ((y - x) == 0) { message.error("请选择需要插入链接的内容") return } if (!('value' in this.props)) { this.setState({ modalVisible: true, fnKey: "link" }); } this.triggerChange({ modalVisible: true, fnKey: "link" }); } callback3 = (content) => { const { editorState } = this.state const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity('video', 'IMMUTABLE', content); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); const xx = AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, '[视频]'); if (!('value' in this.props)) { this.setState({ editorState: xx, modalVisible: false, }); } this.triggerChange({ editorState: xx, modalVisible: false, }); } callback4 = (content) => { const { editorState } = this.state const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity('attr', 'IMMUTABLE', content); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); const xx = AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, '[附件]'); if (!('value' in this.props)) { this.setState({ editorState: xx, modalVisible: false, }); } this.triggerChange({ editorState: xx, modalVisible: false, }); } callback2 = (content) => { const { editorState } = this.state const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity('image', 'IMMUTABLE', content); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); const xx = AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, '[图片]'); if (!('value' in this.props)) { this.setState({ editorState: xx, modalVisible: false, }); } this.triggerChange({ editorState: xx, modalVisible: false, }); } callback = (content) => { const { editorState } = this.state; // 获取contentState const contentState = editorState.getCurrentContent(); // 在contentState上新建entity const contentStateWithEntity = contentState.createEntity( 'LINK', // 'MUTABLE', // 'IMMUTABLE', 'SEGMENTED', { url: content.url }, ); // 获取到刚才新建的entity const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); // 把带有entity的contentState设置到editorState上 const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); // 把entity和选中的内容对应 const xx = RichUtils.toggleLink( newEditorState, newEditorState.getSelection(), entityKey ) if (!('value' in this.props)) { this.setState({ editorState: xx, modalVisible: false}); } this.triggerChange({ editorState: xx, modalVisible: false}); } modelContent = () => { if (this.state.fnKey == "link") { return <LinkForm callback={this.callback} /> } else if (this.state.fnKey == "image") { return <PicForm callback={this.callback2} /> } else if (this.state.fnKey == "video") { return <VideoForm callback={this.callback3} /> } else if (this.state.fnKey == "formula") { return <FormulaForm callback={this.callback2} /> }else if (this.state.fnKey == "attr") { return <AttrForm callback={this.callback4} /> } } deleteBlock=(editKey)=>{ const oldObj = changeFromDraftState2(this.state.editorState) const blocks = JSON.parse(JSON.stringify(oldObj)); const bs =blocks.blocks var b; var j; for(var i=0;i<bs.length;i++){ if(bs[i].key==editKey){ b=bs[i]; j=i; break; } } const entityKey=b.entityRanges[0].key+""; delete blocks.entityMap[entityKey] blocks.blocks.splice(j,1) const editorState = changeToDraftState2(blocks) if (!('value' in this.props)) { this.setState({ editorState }); } this.triggerChange({ editorState }); } render() { const btns = [ { label: 'Bold', style: 'BOLD', type: "inline", icon: b }, { label: 'Italic', style: 'ITALIC', type: "inline", icon: it }, { label: 'line1', type: "line", icon: ii }, { label: 'H1', style: 'header-one', type: "block", icon: h }, /* {label: 'H2', style: 'header-two',type:"block",icon:b}, {label: 'H3', style: 'header-three',type:"block",icon:b}, {label: 'H4', style: 'header-four',type:"block",icon:b}, {label: 'H5', style: 'header-five',type:"block",icon:b}, {label: 'H6', style: 'header-six',type:"block",icon:b}, */ { label: 'Blockquote', style: 'blockquote', type: "block", icon: blockQu }, { label: 'Code Block', style: 'code-block', type: "block", icon: code }, { label: 'UL', style: 'unordered-list-item', type: "block", icon: prjectNum }, { label: 'OL', style: 'ordered-list-item', type: "block", icon: num }, { label: 'line2', type: "line", icon: ii }, { label: 'lk', type: "fn", icon: lk, fn: this.insertLink }, { label: 'pic', type: "fn", icon: pic, fn: this.insertPic }, { label: 'video', type: "fn", icon: video, fn: this.insertVideo }, /* { label: 'sum', type: "fn", icon: sum, fn: this.insertFormula }, */ { label: 'att', type: "fn", icon: attrs, fn: this.insertAttr }, { label: 'line3', type: "line", icon: ii }, { label: 'Del', style: 'STRIKETHROUGH', type: "inline", icon: t }, ]; const { modalVisible } = this.state return ( <div> <StyleControls btns={btns} editorState={this.state.editorState} toggleInlineStyle={this.toggleInlineStyle} toggleBlockType={this.toggleBlockType} /> <div className={styles.basic} style={{ height: '400px', overflowY: 'auto' }} onClick={this.focusEditor}> <Editor ref={this.setEditor} blockStyleFn={getBlockStyle} customStyleMap={this.state.styleMap} blockRendererFn={MyBlockRender.bind(this,false,null,this.props.editBlock,this.deleteBlock)} editorState={this.state.editorState} placeholder={this.props.placeholder || "请输入"} onChange={this.onChange} /> </div> <Modal width='700px' maskClosable={false} destroyOnClose title={"插入对象"} visible={modalVisible} footer={null} onCancel={() => this.setState({ modalVisible: false })}> {this.modelContent()} </Modal> </div> ) } } class StyleButton extends React.Component { constructor() { super(); this.onToggle = (e) => { e.preventDefault(); this.props.onToggle(this.props.style); }; } render() { const { label } = this.props return ( <a key={label.label} onMouseDown={this.onToggle} onClick={label.fn} style={{ marginLeft: 20 }}> <img src={label.icon} /> </a> ); } } @Form.create() class LinkForm extends React.Component { constructor(props) { super(props); } submit = (e) => { e.preventDefault(); const { form } = this.props; form.validateFields((err, fieldsValue) => { if (err) return; this.props.callback(fieldsValue) }) } render() { const { form } = this.props; return ( <Form onSubmit={this.submit}> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="链接地址"> {form.getFieldDecorator('url', { rules: [{ required: true, message: '请输入链接名称' }], })(<Input placeholder="请输入链接名称" />)} </FormItem> <Button type="primary" htmlType="submit"> 确定 </Button> </Form> ); } } @Form.create() class AttrForm extends React.Component { constructor(props) { super(props); this.state={ url:null, name:null, } } submit = (e) => { e.preventDefault(); const { form } = this.props; form.validateFields((err, fieldsValue) => { if (err) return; var src = fieldsValue.src; src = src[src.length - 1].response const params = { ...fieldsValue, src, name:this.state.name } this.props.callback(params) }) } normFile = (e) => { if (Array.isArray(e)) { return e; } return e && e.fileList; } onChange=(info) =>{ if (info.file.status === 'done') { message.success(`文件上传成功`); this.setState({url:info.file.response,name:info.file.name}) } else if (info.file.status === 'error') { message.error(`文件上传失败`); } }; render() { const { form } = this.props; const {url,name} =this.state return ( <Form onSubmit={this.submit}> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="上传附件"> {form.getFieldDecorator('src', { valuePropName: 'fileList', getValueFromEvent: this.normFile, })(<Upload.Dragger onChange={this.onChange} showUploadList={false} name="file" action={config.uploadUrl} onChangemultiple={false} style={{ padding: 0 }}> {url?<a href={config.httpServer + url} target="_blank" >{name}</a>:<p className="ant-upload-drag-icon" style={{ marginBottom: 0,height:400 }}> <Icon type="video" /> </p>} </Upload.Dragger>)} </FormItem> <Button type="primary" htmlType="submit"> 确定 </Button> </Form> ); } } @Form.create() class VideoForm extends React.Component { constructor(props) { super(props); this.state={ url:null } } submit = (e) => { e.preventDefault(); const { form } = this.props; form.validateFields((err, fieldsValue) => { if (err) return; var src = fieldsValue.src; src = src[src.length - 1].response const params = { ...fieldsValue, src } this.props.callback(params) }) } normFile = (e) => { if (Array.isArray(e)) { return e; } return e && e.fileList; } beforeUpload=(file)=> { const isJPG = file.type === 'video/mp4'; if (!isJPG) { message.error('请上传mp4格式文件!'); } return isJPG ; } onChange=(info) =>{ if (info.file.status === 'done') { message.success(`视频上传成功`); this.setState({url:info.file.response}) } else if (info.file.status === 'error') { message.error(`视频上传失败`); } }; render() { const { form } = this.props; const {url} =this.state return ( <Form onSubmit={this.submit}> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="上传视频"> {form.getFieldDecorator('src', { valuePropName: 'fileList', getValueFromEvent: this.normFile, })(<Upload.Dragger beforeUpload={this.beforeUpload} onChange={this.onChange} showUploadList={false} name="file" action={config.uploadUrl} onChangemultiple={false} style={{ padding: 0 }}> {url?<video src={config.httpServer + url} controls="controls">您的浏览器不支持 video 标签。 </video>:<p className="ant-upload-drag-icon" style={{ marginBottom: 0,height:400 }}> <Icon type="video" /> </p>} </Upload.Dragger>)} </FormItem> <Button type="primary" htmlType="submit"> 确定 </Button> </Form> ); } } @Form.create() class PicForm extends React.Component { constructor(props) { super(props); this.state={ url:null } } beforeUpload=(file)=> { const isJPG = (file.type === 'image/jpeg'||file.type === 'image/png') ; if (!isJPG) { message.error('请上传jpg或者png格式文件!'); } return isJPG ; } submit = (e) => { e.preventDefault(); const { form } = this.props; form.validateFields((err, fieldsValue) => { if (err) return; var src = fieldsValue.src; src = src[src.length - 1].response const params = { ...fieldsValue, src } this.props.callback(params) }) } normFile = (e) => { if (Array.isArray(e)) { return e; } return e && e.fileList; } onChange=(info) =>{ if (info.file.status === 'done') { message.success(`图片上传成功`); this.setState({url:info.file.response}) } else if (info.file.status === 'error') { message.error(`图片上传失败`); } }; render() { const { form } = this.props; const {url} =this.state return ( <Form onSubmit={this.submit}> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="上传图片"> {form.getFieldDecorator('src', { valuePropName: 'fileList', getValueFromEvent: this.normFile, })(<Upload.Dragger beforeUpload={this.beforeUpload} showUploadList={false} name="file" action={config.uploadUrl} onChange={this.onChange} multiple={false} style={{ padding: 0 }}> {url?<img src={config.httpServer+url} style={{height:400,width:"100%"}}/>:<p className="ant-upload-drag-icon" style={{ marginBottom: 0,height:400 }}> <Icon type="video" /> </p>} </Upload.Dragger>)} </FormItem> <Button type="primary" htmlType="submit"> 确定 </Button> </Form> ); } } @Form.create() class FormulaForm extends React.Component { constructor(props) { super(props); window.latex = (gs) => { this.props.callback({ src: '/latex?code=' + encodeURI(gs), description: "" }) } } render() { const { form } = this.props; return ( <iframe src="/gongshi/gongshi.html" style={{ width: '100%', minHeight: '400px', border: 'solid 1px #0062d5' }} ></iframe> ); } }