import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Dropdown, Menu } from 'antd'
|
import { FullscreenOutlined, FullscreenExitOutlined, FontSizeOutlined, FormatPainterOutlined } from '@ant-design/icons'
|
|
import {UnControlled as CodeMirror} from 'react-codemirror2'
|
import sqlFormatter from '@/utils/sqlFormatter.js'
|
import 'codemirror/mode/javascript/javascript'
|
import 'codemirror/mode/sql/sql'
|
import 'codemirror/mode/xml/xml'
|
import 'codemirror/mode/css/css'
|
import 'codemirror/addon/display/fullscreen.js'
|
|
import 'codemirror/addon/display/fullscreen.css'
|
import 'codemirror/lib/codemirror.css'
|
import 'codemirror/theme/cobalt.css'
|
|
import './index.scss'
|
|
class CodeMirrorComponent extends Component {
|
static propTpyes = {
|
value: PropTypes.string, // 内容
|
mode: PropTypes.any, // 可选,语言模式,默认为sql
|
theme: PropTypes.any, // 可选,主题样式
|
onChange: PropTypes.func // 内容变化时回调
|
}
|
|
state = {
|
editor: null, // code对象
|
defaultVal: '', // 初始值
|
value: '', // 实时内容
|
options: null, // mode : text/xml, text/css, text/javascript、text/x-mysql ; theme : cobalt - 黑底
|
fullScreen: false,
|
style: {fontSize: '18px', lineHeight: '32px'},
|
display: true
|
}
|
|
UNSAFE_componentWillMount () {
|
let options = {
|
lineNumbers: true,
|
lineWrapping: true,
|
fullScreen: false
|
}
|
|
options.mode = this.props.mode || 'text/x-mysql'
|
if (this.props.theme) {
|
options.theme = this.props.theme
|
}
|
|
this.setState({
|
value: this.props.value || '',
|
defaultVal: this.props.value || '',
|
options
|
})
|
}
|
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
const { value, editor } = this.state
|
|
if (value !== nextProps.value) {
|
this.setState({
|
value: nextProps.value || ''
|
})
|
|
if (editor && editor.setValue) {
|
editor.setValue(nextProps.value || '')
|
} else {
|
this.setState({
|
defaultVal: nextProps.value || ''
|
})
|
}
|
}
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS({...this.state, value: '', editor: ''}), fromJS({...nextState, value: '', editor: ''}))
|
}
|
|
fullScreenChange = () => {
|
const { options, fullScreen } = this.state
|
|
this.setState({options: {...options, fullScreen: !fullScreen}, fullScreen: !fullScreen})
|
}
|
|
changeSize = (size) => {
|
let _style = null
|
if (size === 14) {
|
_style = {fontSize: '14px', lineHeight: '25px'}
|
} else if (size === 16) {
|
_style = {fontSize: '16px', lineHeight: '28px'}
|
} else if (size === 18) {
|
_style = {fontSize: '18px', lineHeight: '32px'}
|
} else if (size === 20) {
|
_style = {fontSize: '20px', lineHeight: '34px'}
|
} else if (size === 22) {
|
_style = {fontSize: '22px', lineHeight: '36px'}
|
} else if (size === 24) {
|
_style = {fontSize: '24px', lineHeight: '40px'}
|
}
|
|
// 切换字体大小,刷新编辑器窗口,解决调整后的选择区域错乱问题
|
this.setState({style: _style, display: false, editor: null, defaultVal: this.state.value}, () => {
|
this.setState({display: true})
|
})
|
}
|
|
handleFormat = () => {
|
let _sql = this.state.value
|
|
if (!_sql) return
|
|
_sql = sqlFormatter.format(_sql.replace(/\s{2,}/g, ' '))
|
// _sql = _sql.replace(/case\n\s+when\s/ig, (word) => {
|
// return word.replace(/case/, '').replace(/when/, 'case when')
|
// })
|
|
this.setState({display: false, editor: null, defaultVal: _sql}, () => {
|
this.setState({display: true})
|
})
|
|
this.props.onChange(_sql)
|
}
|
|
render() {
|
const { mode } = this.props
|
const { defaultVal, options, fullScreen, style, display } = this.state
|
const menu = (
|
<Menu>
|
<Menu.Item
|
style={!style || style.fontSize === '14px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(14)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>14px</span>
|
</Menu.Item>
|
<Menu.Item
|
style={style && style.fontSize === '16px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(16)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>16px</span>
|
</Menu.Item>
|
<Menu.Item
|
style={style && style.fontSize === '18px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(18)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>18px</span>
|
</Menu.Item>
|
<Menu.Item
|
style={style && style.fontSize === '20px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(20)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>20px</span>
|
</Menu.Item>
|
<Menu.Item
|
style={style && style.fontSize === '22px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(22)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>22px</span>
|
</Menu.Item>
|
<Menu.Item
|
style={style && style.fontSize === '24px' ? {backgroundColor: '#bae7ff'} : ''}
|
onClick={() => {this.changeSize(24)}}
|
>
|
<span style={{padding: '0 10px 0px 5px'}}>24px</span>
|
</Menu.Item>
|
</Menu>
|
)
|
|
return (
|
<div className="code-mirror-wrap" style={fullScreen ? style : null}>
|
{!mode && !fullScreen ? <FormatPainterOutlined onClick={this.handleFormat}/> : null}
|
{!fullScreen ? <FullscreenOutlined onClick={this.fullScreenChange}/> : null}
|
{fullScreen ? <FullscreenExitOutlined onClick={this.fullScreenChange}/> : null}
|
{fullScreen ? <Dropdown overlay={menu} placement="bottomRight">
|
<FontSizeOutlined />
|
</Dropdown> : null}
|
{display ? <CodeMirror
|
className="code-mirror-area"
|
value={defaultVal}
|
options={options}
|
onChange={(editor, data, value) => {
|
this.setState({editor, value})
|
this.props.onChange(value)
|
}}
|
/> : null}
|
</div>
|
)
|
}
|
}
|
|
export default CodeMirrorComponent
|