md/src/components/CodemirrorEditor.vue

248 lines
8.8 KiB
Vue
Raw Normal View History

2020-02-11 17:51:04 +08:00
<template>
2020-05-02 11:50:26 +08:00
<div id="app" class="container">
<el-container>
<el-header class="top editor__header">
<editor-header
2020-05-02 17:40:03 +08:00
@uploaded="uploaded"
2020-05-02 12:33:44 +08:00
@cssChanged="cssChanged"
2020-05-02 11:50:26 +08:00
@showBox="showBox = !showBox"
@showAboutDialog="aboutDialogVisible = true"
@showDialogForm="dialogFormVisible = true"
/>
</el-header>
<el-main class="main-body">
<el-row :gutter="10" class="main-section">
<el-col :span="12">
<textarea id="editor" type="textarea" placeholder="Your markdown text here." v-model="source">
</textarea>
</el-col>
<el-col :span="12" class="preview-wrapper" id="preview">
<section id="output-wrapper">
<div class="preview" contenteditable="true">
<section id="output" v-html="output">
</section>
</div>
</section>
</el-col>
<transition name="custom-classes-transition" enter-active-class="animated bounceInRight">
<el-col id="cssBox" :span="12" v-show="showBox">
<textarea id="cssEditor" type="textarea" placeholder="Your custom css here.">
</textarea>
</el-col>
</transition>
</el-row>
</el-main>
</el-container>
<about-dialog :aboutDialogVisible="aboutDialogVisible"
@close="aboutDialogVisible = false" />
<insert-form-dialog :dialogFormVisible="dialogFormVisible"
@close="dialogFormVisible = false" />
</div>
2020-02-11 17:51:04 +08:00
</template>
<script>
2020-05-02 12:33:44 +08:00
import CodeMirror from 'codemirror/lib/codemirror'
import 'codemirror/mode/css/css'
import 'codemirror/mode/markdown/markdown'
import 'codemirror/addon/edit/matchbrackets'
import 'codemirror/addon/selection/active-line'
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/css-hint.js'
import '../scripts/format.js'
import fileApi from '../api/file';
import editorHeader from './codeMirror/header';
import aboutDialog from './codeMirror/aboutDialog';
import insertFormDialog from './codeMirror/insertForm';
import {
setFontSize,
css2json,
customCssWithTemplate,
2020-05-01 21:30:25 +08:00
saveEditorContent,
2020-05-02 11:50:26 +08:00
isImageIllegal
2020-05-02 12:33:44 +08:00
} from '../scripts/util'
require('codemirror/mode/javascript/javascript')
import '../scripts/closebrackets'
import $ from 'jquery'
2020-05-03 10:26:26 +08:00
require('../scripts/google-code-prettify/prettify.js')
2020-05-02 12:33:44 +08:00
import config from '../scripts/config'
import {mapState, mapMutations} from 'vuex';
export default {
2020-05-01 21:30:25 +08:00
data() {
2020-05-02 11:50:26 +08:00
return {
config: config,
showBox: false,
aboutDialogVisible: false,
dialogFormVisible: false,
timeout: null,
2020-05-03 11:02:27 +08:00
changeTimer: null,
2020-05-02 11:50:26 +08:00
source: ''
}
2020-02-11 17:51:04 +08:00
},
2020-05-01 21:30:25 +08:00
components: {
2020-05-02 11:50:26 +08:00
editorHeader, aboutDialog, insertFormDialog
2020-02-11 17:51:04 +08:00
},
2020-05-01 21:30:25 +08:00
computed: {
...mapState({
wxRenderer: state=> state.wxRenderer,
output: state=> state.output,
editor: state=> state.editor,
cssEditor: state=> state.cssEditor,
2020-05-02 12:33:44 +08:00
currentSize: state=> state.currentSize,
currentColor: state=> state.currentColor,
2020-05-02 11:50:26 +08:00
html: state=> state.html
2020-02-11 17:51:04 +08:00
})
},
2020-05-01 21:30:25 +08:00
created() {
this.initEditorState()
this.$nextTick(() => {
this.initEditor()
this.initCssEditor()
this.editorRefresh()
2020-02-11 17:51:04 +08:00
})
},
2020-05-01 21:30:25 +08:00
methods: {
initEditor() {
this.initEditorEntity();
this.editor.on('change', (cm, e) => {
2020-05-03 11:02:27 +08:00
if (this.changeTimer) clearTimeout(this.changeTimer);
this.changeTimer = setTimeout(() => {
setTimeout(()=> PR.prettyPrint(), 0);
this.editorRefresh()
console.log('tick');
saveEditorContent(this.editor, '__editor_content')
2020-05-03 10:26:26 +08:00
}, 300);
2020-05-01 21:30:25 +08:00
});
2020-02-11 17:51:04 +08:00
2020-05-01 21:30:25 +08:00
// 粘贴上传图片并插入
this.editor.on('paste', (cm, e) => {
if (!(e.clipboardData && e.clipboardData.items)) {
return
}
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) {
let item = e.clipboardData.items[i]
if (item.kind === 'file') {
const pasteFile = item.getAsFile()
2020-05-02 11:50:26 +08:00
const checkImageResult = isImageIllegal(pasteFile);
2020-02-11 17:51:04 +08:00
2020-05-01 21:30:25 +08:00
if (checkImageResult) {
this.$message({
showClose: true,
message: checkImageResult,
type: 'error'
});
return;
}
let data = new FormData()
data.append('file', pasteFile)
fileApi.fileUpload(data).then(res => {
2020-05-02 16:28:00 +08:00
this.uploaded(res)
2020-05-01 21:30:25 +08:00
}).catch(err => {
console.log(err.message)
})
}
}
});
},
initCssEditor() {
this.initCssEditorEntity();
// 自动提示
this.cssEditor.on('keyup', (cm, e) => {
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
cm.showHint(e)
}
});
this.cssEditor.on('update', (instance) => {
this.cssChanged()
saveEditorContent(this.cssEditor, '__css_content')
})
},
2020-05-02 12:33:44 +08:00
cssChanged() {
let json = css2json(this.cssEditor.getValue(0))
let theme = setFontSize(this.currentSize.replace('px', ''))
theme = customCssWithTemplate(json, this.currentColor, theme)
this.setWxRendererOptions({
theme: theme
});
this.editorRefresh()
},
2020-05-03 10:26:26 +08:00
onTextareaChange() {
console.log('change');
},
2020-05-01 21:30:25 +08:00
// 图片上传结束
uploaded(response, file, fileList) {
2020-05-02 17:40:03 +08:00
if (response) {
if (response.success) {
// 上传成功,获取光标
const cursor = this.editor.getCursor()
const imageUrl = response.data
const markdownImage = `![](${imageUrl})`
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor)
this.$message({
showClose: true,
message: '图片插入成功',
type: 'success'
})
this.editorRefresh()
} else {
// 上传失败
this.$message({
showClose: true,
message: response.message,
type: 'error'
})
}
2020-05-01 21:30:25 +08:00
} else {
this.$message({
showClose: true,
2020-05-02 17:40:03 +08:00
message: '上传图片未知异常',
2020-05-01 21:30:25 +08:00
type: 'error'
})
}
},
// 左右栏同步滚动
leftAndRightScroll() {
$('div.CodeMirror-scroll, #preview').on('scroll', function callback() {
clearTimeout(this.timeout)
2020-02-13 21:50:27 +08:00
2020-05-01 21:30:25 +08:00
let source = $(this)
let target = $(source.is('#preview') ? 'div.CodeMirror-scroll' : '#preview')
2020-02-13 21:50:27 +08:00
2020-05-01 21:30:25 +08:00
target.off('scroll')
2020-02-13 21:50:27 +08:00
2020-05-01 21:30:25 +08:00
let source0 = source[0]
let target0 = target[0]
2020-02-13 21:50:27 +08:00
2020-05-01 21:30:25 +08:00
let percentage = source0.scrollTop / (source0.scrollHeight - source0.offsetHeight)
let height = percentage * (target0.scrollHeight - target0.offsetHeight)
target0.scrollTo(0, height)
2020-02-13 21:50:27 +08:00
2020-05-01 21:30:25 +08:00
this.timeout = setTimeout(() => {
target.on('scroll', callback)
}, 100)
})
},
2020-05-02 12:33:44 +08:00
...mapMutations(['initEditorState', 'initEditorEntity', 'setWxRendererOptions',
'editorRefresh', 'initCssEditorEntity'])
2020-05-01 21:30:25 +08:00
},
mounted() {
this.leftAndRightScroll()
2020-05-03 10:26:26 +08:00
setTimeout(() => {
PR.prettyPrint()
}, 300);
2020-02-11 17:51:04 +08:00
}
2020-05-02 11:50:26 +08:00
}
2020-05-01 21:30:25 +08:00
2020-02-11 17:51:04 +08:00
</script>
2020-05-01 22:35:39 +08:00
<style lang="less" scoped>
2020-05-03 10:26:26 +08:00
@import url('../scripts/google-code-prettify/prettify.css');
2020-05-02 11:50:26 +08:00
.main-body {
padding-top: 0;
2020-05-01 22:35:39 +08:00
}
2020-02-11 17:51:04 +08:00
</style>