import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Dropdown, Menu, Modal, notification } from 'antd'
|
import { FullscreenOutlined, FullscreenExitOutlined, FontSizeOutlined, FormatPainterOutlined, SwapOutlined } from '@ant-design/icons'
|
|
import {UnControlled as CodeMirror} from 'react-codemirror2'
|
import sqlFormatter from '@/utils/sqlFormatter.js'
|
import ReplaceForm from './replaceform'
|
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, // 可选,主题样式
|
func: PropTypes.any, // 编辑存储过程
|
onChange: PropTypes.func // 内容变化时回调
|
}
|
|
state = {
|
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,
|
visible: false
|
}
|
|
editor = null
|
|
UNSAFE_componentWillMount () {
|
const { func } = this.props
|
|
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({
|
style: func ? {fontSize: '14px', lineHeight: '25px'} : {fontSize: '18px', lineHeight: '32px'},
|
value: this.props.value || '',
|
defaultVal: this.props.value || '',
|
options
|
})
|
}
|
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
const { value } = this.state
|
|
if (value !== nextProps.value) {
|
this.setState({
|
value: nextProps.value || ''
|
})
|
|
if (this.editor && this.editor.setValue) {
|
this.editor.setValue(nextProps.value || '')
|
} else {
|
this.setState({
|
defaultVal: nextProps.value || ''
|
})
|
}
|
}
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS({...this.state, value: ''}), fromJS({...nextState, value: ''}))
|
}
|
|
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, defaultVal: this.state.value}, () => {
|
this.setState({display: true})
|
})
|
}
|
|
handleFormat = () => {
|
let _sql = this.state.value
|
|
if (!_sql) return
|
|
let getuuid = () => {
|
let uuid = []
|
let _options = '0123456789abcdefghigklmnopqrstuv'
|
for (let i = 0; i < 19; i++) {
|
uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
|
}
|
return '\'' + uuid.join('') + '\''
|
}
|
|
let arr = []
|
_sql = _sql.replace(/@[0-9a-zA-Z_]+@/g, (word) => {
|
let uuid = getuuid()
|
arr.push({id: uuid, value: word})
|
return uuid
|
})
|
|
_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')
|
// })
|
|
_sql = _sql.replace(/\$\s\*\s\//g, '$*/').replace(/\*\s\//g, '*/')
|
|
arr.forEach(item => {
|
_sql = _sql.replace(item.id, item.value)
|
})
|
|
_sql = _sql.replace(/,\n\s*/g, ',')
|
_sql = _sql.replace(/@db@\s+[a-zA-Z]/g, (w) => {
|
return w.replace(/\s+/g, '')
|
})
|
|
this.setState({display: false, defaultVal: _sql}, () => {
|
this.setState({display: true})
|
})
|
|
this.props.onChange(_sql)
|
}
|
|
submit = () => {
|
let _sql = this.state.value
|
|
this.ReplaceRef.handleConfirm().then(res => {
|
let reg = new RegExp(res.origin, 'ig')
|
let times = _sql.match(reg)
|
|
if (!times) {
|
notification.warning({
|
top: 92,
|
message: '未查询到字符《' + res.origin + '》。',
|
duration: 5
|
})
|
return
|
}
|
|
localStorage.setItem(window.GLOB.sysSign + 'sql_char', JSON.stringify([res.origin, res.value]))
|
|
_sql = _sql.replace(reg, res.value)
|
|
this.setState({display: false, defaultVal: _sql}, () => {
|
this.setState({display: true})
|
})
|
|
this.props.onChange(_sql)
|
|
notification.success({
|
top: 92,
|
message: `共替换${times.length}处字符《${res.origin}》。`,
|
duration: 5
|
})
|
this.setState({ visible: false })
|
})
|
}
|
|
render() {
|
const { mode, func } = this.props
|
const { defaultVal, options, fullScreen, style, display, visible } = 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 ' + (fullScreen ? 'mk-fullscreen' : '')} style={fullScreen || func ? style : null}>
|
{!mode ? <FormatPainterOutlined title="格式化" onClick={this.handleFormat}/> : null}
|
<FullscreenOutlined title="最大化" onClick={this.fullScreenChange}/>
|
<FullscreenExitOutlined title="向下还原" onClick={this.fullScreenChange}/>
|
{fullScreen || func ? <Dropdown overlayClassName="mk-mirror-font" overlay={menu} placement="bottomRight">
|
<FontSizeOutlined />
|
</Dropdown> : null}
|
{!mode ? <SwapOutlined title="字符替换" onClick={() => this.setState({visible: true})}/> : null}
|
{display ? <CodeMirror
|
className="code-mirror-area"
|
value={defaultVal}
|
options={options}
|
editorDidMount={editor => { this.editor = editor }}
|
onChange={(editor, data, value) => {
|
this.setState({value})
|
this.props.onChange(value)
|
}}
|
/> : null}
|
</div>
|
<Modal
|
title="字符替换"
|
visible={visible}
|
width={500}
|
maskClosable={false}
|
okText="替换"
|
onOk={this.submit}
|
onCancel={() => {this.setState({ visible: false })}}
|
destroyOnClose
|
>
|
<ReplaceForm inputSubmit={this.submit} wrappedComponentRef={(inst) => this.ReplaceRef = inst}/>
|
</Modal>
|
</>
|
)
|
}
|
}
|
|
export default CodeMirrorComponent
|